class Expression(Serializable): def __init__(self, ex: str) -> None: self.__string = str(ex) # type: str self.__expression = Parser().parse(ex.replace("**", "^")) # type: py_expression_eval.Expression @property def string(self) -> str: return self.__string def variables(self) -> Iterable[str]: return self.__expression.variables() def evaluate(self, **kwargs) -> float: if USE_NUMEXPR: return numexpr.evaluate(self.__string, global_dict={}, local_dict=kwargs) else: return self.__expression.evaluate(kwargs) def get_serialization_data(self, serializer: "Serializer") -> Dict[str, Any]: return dict(type="Expression", expression=self.__string) @staticmethod def deserialize(serializer: "Serializer", **kwargs) -> Serializable: return Expression(kwargs["expression"]) @property def identifier(self) -> Optional[str]: return None
class Expression(Serializable): def __init__(self, ex: str): ex = str(ex) self._string = ex self._expression = Parser().parse(ex.replace('**', '^')) @property def string(self): return self._string def variables(self): return self._expression.variables() def evaluate(self, parameters): if USE_NUMEXPR: return numexpr.evaluate(self._string, global_dict={}, local_dict=parameters) else: return self._expression.evaluate(parameters) def get_serialization_data(self, serializer: 'Serializer'): return dict(type='Expression', expression=self._string) @staticmethod def deserialize(serializer: 'Serializer', **kwargs): return Expression(kwargs['expression']) @property def identifier(self): return None
def set_conditions(self, conditions: str, action: Union[callable, int, float, str] = 0): self.conditions = None if conditions is not None: # TODO: add parsing exceptions here expr = Parser().parse(conditions) self.conditions = { "string": conditions, "expr": expr, "vars": expr.variables() } self.action = None
def __init__(self, expression, sample_points): """Initialize the Evaluator :param expression: string of a math expression :param sample_points: list of sample points (tuples) """ expression = Parser().parse(expression) variables = expression.variables() super().__init__(len(variables), 1) self.inputs = sample_points inputs_dicts = [ dict(zip(variables, sample_point)) for sample_point in sample_points ] self.outputs = [ expression.evaluate(input_dict) for input_dict in inputs_dicts ]
def generateNetwork(network_file_name): V = [] # vertices E = [] # edges F = {} # cost functions OD = [] # OD pairs lineid = 0 for line in open(network_file_name, 'r'): lineid += 1 # ignore \n line = line.rstrip() # ignore comments hash_pos = line.find('#') if hash_pos > -1: line = line[:hash_pos] # split the line taglist = line.split() if len(taglist) == 0: continue if taglist[0] == 'function': # process the params params = taglist[2][1:-1].split(',') if len(params) > 1: raise Exception('Cost functions with more than one parameter are not yet acceptable! (parameters defined: %s)' % str(params)[1:-1]) # process the function expr = taglist[3] function = Parser().parse(expr) # handle the case where the parameter is not in the formula # (this needs to be handled because py-expression-eval does # not allows simplifying all variables of an expression) if taglist[1] not in function.variables(): expr = '%s+%s-%s' % (taglist[3], params[0], params[0]) function = Parser().parse(expr) # process the constants constants = function.variables() if params[0] in constants: # the parameter must be ignored constants.remove(params[0]) # store the function F[taglist[1]] = [params[0], constants, expr] elif taglist[0] == 'node': V.append(Node(taglist[1])) elif taglist[0] == 'dedge' or taglist[0] == 'edge': # dedge is a directed edge # process the function func_tuple = F[taglist[4]] # get the corresponding function param_values = dict(zip(func_tuple[1], map(float, taglist[5:]))) # associate constants and values specified in the line (in order of occurrence) function = Parser().parse(func_tuple[2]) # create the function function = function.simplify(param_values) # replace constants # create the edge(s) E.append(Edge(taglist[1], taglist[2], taglist[3], function, func_tuple[0])) if taglist[0] == 'edge': E.append(Edge('%s-%s'%(taglist[3], taglist[2]), taglist[3], taglist[2], function, func_tuple[0])) elif taglist[0] == 'od': OD.append(taglist[1]) else: raise Exception('Network file does not comply with the specification! (line %d: "%s")' % (lineid, line)) return V, E, OD
def generateGraph(graph_file, flow=0.0): """ Adapted version from the KSP repository version 1.44. Original is available at: https://github.com/maslab-ufrgs/ksp/releases/tag/V1.44 Generates the graph from a text file following the specifications(available @ http://wiki.inf.ufrgs.br/network_files_specification). In: graph_file:String = Path to the network(graph) file. flow:Float = Value to sum the cost of the edges. Out: V:List = List of vertices or nodes of the graph. E:List = List of the edges of the graph. OD:List = List of the OD pairs in the network. """ V = [] # vertices E = [] # edges F = {} # cost functions OD = {} # OD pairs lineid = 0 for line in open(graph_file, 'r'): lineid += 1 # ignore \n line = line.rstrip() # ignore comments hash_pos = line.find('#') if hash_pos > -1: line = line[:hash_pos] # split the line taglist = line.split() if len(taglist) == 0: continue if taglist[0] == 'function': # process the params params = taglist[2][1:-1].split(',') if len(params) > 1: raise Exception('Cost functions with more than one parameter are not yet'\ 'acceptable! (parameters defined: %s)' % str(params)[1:-1]) # process the function function = Parser().parse(taglist[3]) # process the constants constants = function.variables() if params[0] in constants: # the parameter must be ignored constants.remove(params[0]) # store the function F[taglist[1]] = [params[0], constants, function] elif taglist[0] == 'node': V.append(Node(taglist[1])) elif taglist[0] == 'dedge' or taglist[ 0] == 'edge': # dedge is a directed edge # process the cost function = F[taglist[4]] # get the corresponding function # associate constants and values specified in the line (in order of occurrence) param_values = dict(zip(function[1], map(float, taglist[5:]))) param_values[function[ 0]] = flow # set the function's parameter with the flow value # create the edge(s) E.append( Edge(taglist[2], taglist[3], function, param_values, function[0])) if taglist[0] == 'edge': E.append( Edge(taglist[3], taglist[2], function, param_values, function[0])) elif taglist[0] == 'od': if taglist[2] != taglist[3]: OD[taglist[1]] = float(taglist[4]) else: raise Exception('Network file does not comply with the specification!'\ '(line %d: "%s")' % (lineid, line)) return V, E, OD
class VirtualMeter(Contract): """ Module that manages a virtual meter""" def __init__(self, private_key, abi_file, address, endpoint, formula): with open(abi_file) as f: abi = json.load(f)['abi'] super().__init__(private_key, address, abi, endpoint) # Check if meter is active active = self.contract.functions.isActive(self.account.address).call() if not active: raise NotAMeter('Check that the meter is enabled by the operator') # Get meter ID and connect to the monitoring server meter_id = self.contract.functions.getCurrentMeterData( self.account.address).call()[0] meter_id = meter_id.split(b'\0', 1)[0].decode('ascii') self.meter_id = getattr(MeterNames, meter_id) # Replace formula with ELT(x) calls. self.formula = Parser().parse(formula.replace('x', '*')) self.logger = configure_logging(self.meter_id) info = 'Initialized!' self.logger.info(green(info)) def start_pinging(self): (last_reading, reading) = (0, 0) while True: # Meter loops forever last_reading = reading (reading, timestamp) = \ self.calculate_reading() # Wait until we get a new reading # Perhaps make a call to monitoring server every 15 mins? while (reading == last_reading): self.logger.warning(red('Sleeping until new reading')) time.sleep(20) (reading, timestamp) = \ self.calculate_reading() self.ping(reading, timestamp) info = 'Pinged {} kWh at t={}'.format(reading, timestamp) self.logger.info(green(info)) def calculate_reading(self): """ Utilizes Abstract Syntax Trees to parse an equation and evaluate ELT readings or Coefficients github.com/Axiacore/py-expression-eval """ args = dict() coefficients = vars(Coefficients) meters = [m for m in self.formula.variables() if not m.startswith('F')] for var in self.formula.variables(): # If it's a known coefficient get it from the constants list if var in coefficients: args[var] = getattr(Coefficients, var) # If it's an ELT or KMZ, get it from the blockchain else: info = 'Fetching reading for => {}'.format(var) self.logger.debug(green(info)) addr = self.contract.functions.meterAddressById( normalize(var)).call() data = self.contract.functions.getCurrentMeterData(addr).call() reading = data[1] args[var] = reading timestamp = data[2] info = 'Arguments to be evaluated => {}'.format(args) self.logger.debug(yellow(info)) reading = self.formula.evaluate(args) return reading, timestamp def ping(self, reading, timestamp): """ Takes reading and timestamp and creates a raw transaction call to `ping` at the target contract """ args = [int(reading), int(timestamp)] self.sign_and_send(self.contract.functions.ping, args)
def generateGraph(graph_file): V = [] # vertices E = [] # edges F = {} # cost functions OD = [] # OD pairs lineid = 0 for line in open(graph_file, 'r'): lineid += 1 # ignore \n line = line.rstrip() # ignore comments hash_pos = line.find('#') if hash_pos > -1: line = line[:hash_pos] # split the line taglist = line.split() if len(taglist) == 0: continue if taglist[0] == 'function': # process the params params = taglist[2][1:-1].split(',') if len(params) > 1: raise Exception( 'Cost functions with more than one parameter are not yet acceptable! (parameters defined: %s)' % str(params)[1:-1]) # process the function function = Parser().parse(taglist[3]) # process the constants constants = function.variables() if params[0] in constants: # the parameter must be ignored constants.remove(params[0]) # store the function F[taglist[1]] = [params[0], constants, function] elif taglist[0] == 'node': V.append(Node(taglist[1])) elif taglist[0] == 'dedge' or taglist[ 0] == 'edge': # dedge is a directed edge # process the cost function = F[taglist[4]] # get the corresponding function param_values = dict( zip(function[1], map(float, taglist[5:])) ) # associate constants and values specified in the line (in order of occurrence) param_values[function[0]] = 0.0 # add the parameter with value 0 cost = function[2].evaluate(param_values) # calculate the cost # create the edge(s) E.append(Edge(taglist[1], taglist[2], taglist[3], cost)) if taglist[0] == 'edge': E.append(Edge(taglist[1], taglist[3], taglist[2], cost)) elif taglist[0] == 'od': OD.append(taglist[1]) else: raise Exception( 'Network file does not comply with the specification! (line %d: "%s")' % (lineid, line)) return V, E, OD
def generateNetwork(network_file_name): V = [] # vertices E = [] # edges F = {} # cost functions OD = [] # OD pairs lineid = 0 for line in open(network_file_name, 'r'): lineid += 1 # ignore \n line = line.rstrip() # ignore comments hash_pos = line.find('#') if hash_pos > -1: line = line[:hash_pos] # split the line taglist = line.split() if len(taglist) == 0: continue if taglist[0] == 'function': # process the params params = taglist[2][1:-1].split(',') if len(params) > 1: raise Exception( 'Cost functions with more than one parameter are not yet acceptable! (parameters defined: %s)' % str(params)[1:-1]) # process the function expr = taglist[3] function = Parser().parse(expr) # handle the case where the parameter is not in the formula # (this needs to be handled because py-expression-eval does # not allows simplifying all variables of an expression) if taglist[1] not in function.variables(): expr = '%s+%s-%s' % (taglist[3], params[0], params[0]) function = Parser().parse(expr) # process the constants constants = function.variables() if params[0] in constants: # the parameter must be ignored constants.remove(params[0]) # store the function F[taglist[1]] = [params[0], constants, expr] elif taglist[0] == 'node': V.append(Node(taglist[1])) elif taglist[0] == 'dedge' or taglist[ 0] == 'edge': # dedge is a directed edge # process the function func_tuple = F[taglist[4]] # get the corresponding function param_values = dict( zip(func_tuple[1], map(float, taglist[5:])) ) # associate constants and values specified in the line (in order of occurrence) function = Parser().parse(func_tuple[2]) # create the function function = function.simplify(param_values) # replace constants # create the edge(s) E.append( Edge(taglist[1], taglist[2], taglist[3], function, func_tuple[0])) if taglist[0] == 'edge': E.append( Edge('%s-%s' % (taglist[3], taglist[2]), taglist[3], taglist[2], function, func_tuple[0])) elif taglist[0] == 'od': OD.append(taglist[1]) else: raise Exception( 'Network file does not comply with the specification! (line %d: "%s")' % (lineid, line)) return V, E, OD