forked from Accelergy-Project/accelergy-table-based-plug-ins
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsets_of_tables.py
230 lines (206 loc) · 12 KB
/
sets_of_tables.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# MIT License
#
# Copyright (c) 2019 Yannan (Nellie) Wu
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os, yaml, sys, csv
from accelergy.plug_in_interface.interface import *
class SetsOfTables(AccelergyPlugIn):
def __init__(self):
super().__init__() # Initializes our logger
self.sets_of_tables = self.summarize_sets_of_tables()
self.holder = [] # holds the results retrieved by primitive_action_supported/primitive_area_supported
def get_name(self) -> str:
return 'table-based-plug-ins'
#
# ERT interface functions
#
# (1) primitive_action_supported(interface)
# (2) estimate_energy(interface)
def primitive_action_supported(self, query: AccelergyQuery) -> AccuracyEstimation:
class_name = query.class_name
attributes = query.class_attrs
action_name = query.action_name
arguments = query.action_args
# Legacy interface dictionary has keys class_name, attributes, action_name, and arguments
interface = query.to_legacy_interface_dict()
technology = interface['attributes']['technology'] \
if 'technology' in interface['attributes'] else None
if technology is None:
raise Exception(
f'"{self.get_name()}" requires "technology" attribute to locate '
f'the correct set of tables')
# print(self.estimator_name, ': ',
# '"technology" attribute needs to be provided to locate the correct set of tables')
# return 0
max_accuracy, estimated_energy = self.select_best_set(interface)
# record the retrieved result to avoid potential repetitive search
if max_accuracy:
self.holder = [] # reset energy holder
self.holder.append(interface)
self.holder.append(estimated_energy)
assert False
return AccuracyEstimation(max_accuracy)
def estimate_energy(self, query: AccelergyQuery) -> Estimation:
class_name = query.class_name
attributes = query.class_attrs
action_name = query.action_name
arguments = query.action_args
# Legacy interface dictionary has keys class_name, attributes, action_name, and arguments
interface = query.to_legacy_interface_dict()
if len(self.holder) == 0 or not self.holder[0] == interface:
self.logger.error('There is no energy held in energy holder or held energy incorrect')
self.logger.error(f'Received Request: {interface}')
self.logger.error(f'Energy Holder Data: {self.holder}')
assert False
return Estimation(self.holder[1], 'p') # units are pJ
#
# ART interface functions
#
# (1) primitive_area_supported(interface)
# (2) estimate_area(interface)
def primitive_area_supported(self, query: AccelergyQuery) -> AccuracyEstimation:
class_name = query.class_name
attributes = query.class_attrs
action_name = query.action_name
arguments = query.action_args
# Legacy interface dictionary has keys class_name, attributes, action_name, and arguments
interface = query.to_legacy_interface_dict()
technology = interface['attributes']['technology'] \
if 'technology' in interface['attributes'] else None
if technology is None:
raise Exception(
f'"{self.get_name()}" requires "technology" attribute to locate '
f'the correct set of tables')
# print(self.estimator_name, ': ',
# '"technology" attribute needs to be provided to locate the correct set of tables')
# return 0
max_accuracy, estimated_area = self.select_best_set(interface)
# record the retrieved result to avoid potential repetitive search
if max_accuracy:
self.holder = [] # reset energy holder
self.holder.append(interface)
self.holder.append(estimated_area)
return AccuracyEstimation(max_accuracy)
def estimate_area(self, query: AccelergyQuery) -> Estimation:
class_name = query.class_name
attributes = query.class_attrs
action_name = query.action_name
arguments = query.action_args
# Legacy interface dictionary has keys class_name, attributes, action_name, and arguments
interface = query.to_legacy_interface_dict()
if len(self.holder) == 0 or not self.holder[0] == interface:
self.logger.error('There is no energy held in energy holder or held energy incorrect')
self.logger.error(f'Received Request: {interface}')
self.logger.error(f'Area Holder Data: {self.holder}')
assert False
return Estimation(self.holder[1], 'u^2') # units are um^2
# -------- Utility functions -------#
def select_best_set(self, interface):
""" Select the best set of tables according to the recorded identifiers """
max_accuracy = 0
estimated_result = None
primitive_class_name = interface['class_name']
for set_name, set_identifier in self.sets_of_tables.items():
if str(interface['attributes']['technology']) == str(set_identifier['technology']) and \
set_identifier['accuracy'] > max_accuracy and \
primitive_class_name in set_identifier['supported_primitive_classes']:
# check if there are matching attributes( and actions)
area_query = False if 'action_name' in interface else True
supported, estimated_result = self.walk_csv(interface, set_identifier['path_to_data_dir'], area_query)
if supported:
max_accuracy = set_identifier['accuracy']
return max_accuracy, estimated_result
def walk_csv(self, interface, data_dir_path, area_query = False):
""" Check if there is corresponding entry for the requested attributes (and actions) """
supported = False
result = None
csv_file_path = os.path.join(data_dir_path, interface['class_name'] + '.csv')
with open(csv_file_path) as csv_file:
reader = csv.DictReader(csv_file)
for row in reader:
if supported:
break
attr_matched = True
# check if hardware attributes are present in the csv file
for attr_name, attr_val in interface['attributes'].items():
if attr_name in row and not row[attr_name] == str(attr_val):
# only check if the attributes in the provided csv match the interface,
# ignore the ones that are in the interface but not in csv
# (the users do not care the ones that are not specified in the csv)
attr_matched = False
break # attributes not matched, next row
# check if action name (and arguments) are present in the csv file (given attributes are there)
if attr_matched and not area_query:
# action query requires action and action argument check
if row['action'] == interface['action_name']: # if action name match
arg_matched = True
if interface['arguments'] is not None:
for arg_name, arg_val in interface['arguments'].items():
if not arg_name in row or not row[arg_name] == str(arg_val):
arg_matched = False
break # arg not matched, next row
if arg_matched:
supported = True
result = float(row['energy'])
if attr_matched and area_query:
# area query does not require action and action argument check
supported = True
result = float(row['area'])
return supported, result
def summarize_sets_of_tables(self):
""" Collect the information stored in identifier YAML files for all sets of tables"""
sets_of_tables_info = {}
file_dir = os.path.dirname(__file__)
accelergy_config_file = os.path.join(os.path.expanduser('~'),'.config/accelergy/accelergy_config.yaml')
config_file_content = yaml.load(open(accelergy_config_file), Loader=yaml.SafeLoader)
if 'table_plug_ins' not in config_file_content:
self.logger.error('Cannot find the listed roots for the sets of tables')
self.logger.error('Please initialize by running: accelergyTables')
self.logger.error('A pointer to the default set of tables will be created in ~/.config/accelergy/accelergy_config.yaml')
table_roots = config_file_content['table_plug_ins']['roots']
for table_root in table_roots:
for root, directories, filenames in os.walk(table_root):
for filename in filenames:
if 'table.yaml' in filename: # locate a set of tables
identifier_path = root + os.sep + filename
identifier = yaml.load(open(identifier_path), Loader=yaml.SafeLoader)
set_name = identifier['name']
# Check the required keys
if 'accuracy' not in identifier or 'technology' not in identifier \
or 'name' not in identifier or 'path_to_data_dir' not in identifier:
self.logger.error('ERROR-- ', identifier_path,
'Identifier YAML file for each set of tables must contain the following keys: \n'
'"name", "technology", "accuracy", "path_to_data_dir"')
if not os.path.isabs(identifier['path_to_data_dir']):
abs_path = os.path.abspath(os.path.join(os.path.join(root), identifier['path_to_data_dir']))
if not os.path.exists(abs_path):
self.logger.warn('The sepcified data directory cannot be located...')
self.logger.warn(f'Intepreted absolute path to the specified data directory: {abs_path}')
identifier['path_to_data_dir'] = abs_path
supported_primitive_classes = []
for data_root, data_directories, data_filenames in os.walk(identifier['path_to_data_dir']):
for data_filename in data_filenames:
if '.csv' in data_filename:
supported_primitive_classes.append(data_filename[0:-4])
identifier['supported_primitive_classes'] = supported_primitive_classes
set_name in sets_of_tables_info and self.logger.warn(f'Repeated table set name: {set_name}')
sets_of_tables_info[set_name] = identifier
self.logger.info(f'Identified a set of tables named: {set_name}')
return sets_of_tables_info