def test_monitor_with_delay_model(self): m = Monitor("test/test_py-monitor-delay-simple.pl", 'x') # delayed step function (x1 is slower than a1) i0 = Itoms([Itom('a1', 0, variable='a'), Itom('x1', 0, variable='x')]) i1 = Itoms([Itom('a1', 1, variable='a'), Itom('x1', 0, variable='x')]) i2 = Itoms([Itom('a1', 1, variable='a'), Itom('x1', 1, variable='x')]) failed = m.monitor(i0) self.assertEqual(failed, None) failed = m.monitor(i0) self.assertEqual(failed, None) failed = m.monitor(i1) self.assertEqual(failed, None) # x1 (value is still 0) doesn't follow a1 (v=1) failed = m.monitor(i1) self.assertNotEqual(failed, None) failed = m.monitor(i2) self.assertEqual(failed, None) # delay = 2 timesteps m = Monitor("test/test_py-monitor-delay-general.pl", 'x') failed = m.monitor(i0) self.assertEqual(failed, None) failed = m.monitor(i0) self.assertEqual(failed, None) failed = m.monitor(i1) self.assertEqual(failed, None) failed = m.monitor(i1) self.assertEqual(failed, None) failed = m.monitor(i1) self.assertNotEqual(failed, None) failed = m.monitor(i2) self.assertEqual(failed, None)
def test_execute(self): f = Function('a', ['b', 'c'], "a.v = b.v + c.v", name="add") a = Itom('a', 0) b = Itom('b', 1) c = Itom('c', 2) itoms = Itoms([a, b, c]) v = f.execute(itoms) self.assertAlmostEqual(v['a'].v, 3) b.v = 4 c.v = 2 v = f.execute(itoms) self.assertAlmostEqual(v['a'].v, 6) a.v = 0 b.v = 5 c.v = 5 v = f.execute(itoms) self.assertAlmostEqual(v['a'].v, 10) with self.assertRaises(Exception): f.execute({a.name: a}) with self.assertRaises(Exception): f.execute({b.name: b}) # test execution without wrapping the code in a function f = Function('a', ['b', 'c'], "a.v = b.v + c.v", name="add", wrap=False) b.v = 1 c.v = 2 v = f.execute(itoms) self.assertAlmostEqual(v['a'].v, 3) # function setting timestamp f = Function('a', ['b', 'c'], "a.v = b.v + c.v; a.t = max(b.t, c.t)", name="add") b.t = 0 c.t = 1 v = f.execute(itoms) self.assertEqual(v['a'].t, 1) # test execution with non-Python-identifiers as variables b = Itom('0b', 1) c = Itom('1c', 2) f = Function('a', [b.name, c.name], "a.v = {}.v + {}.v".format(b.codename, c.codename)) v = f.execute(Itoms([b, c])) self.assertAlmostEqual(v['a'].v, 3) # function with lists (e.g., ROS itoms representing point clouds) sonar = Itom('/p2os/sonar', [1, 3, 4]) f = Function('dmin', [sonar.name], "dmin.v = min({}.v)".format(sonar.codename)) v = f.execute(Itoms([sonar])) self.assertAlmostEqual(v['dmin'].v, 1)
def execute(self, itoms): """Executes the function given the itoms. itoms -- 'Itoms' object, list or dictionary of itoms """ # make sure its an Itoms-object itoms = Itoms(itoms) # verify inputs if not set([v.name for v in self.__vin]).issubset( set([v.name for v in itoms.values()])): raise RuntimeError("Missing itoms to execute the function.") # generate code code = self.__enclosed_code() # execute code itom_vars = {i.codename: i for i in itoms.values()} self.__locals.update(itom_vars) exec(code, { 'Itom': Itom, 'copy': copy, 'interval': interval }, self.__locals) # local_vars may include utils and functions # -> keep only inputs and output itoms[self.vout.name] = self.__locals[self.vout.name] return itoms
def __collect_substitutions(self, itoms=[]): """Find relations from variables (given itoms) to domain.""" program = "\n" if "itomsOf" not in self.__pli.program and len(itoms) > 0: # be sure itoms is of the right type 'Itoms' itoms = Itoms(itoms) # append available itoms to program with "itomsOf(variable,[itom1,..])" for variable, il in itoms.availability.items(): assert variable is not None and variable != "" names = [i.name for i in il] program += "itomsOf({},[{}]).\n".format(variable, ','.join(names)) program += "\n" if len(itoms) > 0: assert "itomsOf" in program or "itomsOf" in self.__pli.program # get all valid substitutions for the domain # -> query problog knowledge base program += "query(substitution({},S)).".format(self.__domain) result = self.__pli.evaluate(program) S = [] for r in result.keys(): s = self.__pli.parse_substitution(str(r)) S.append(s) if len(itoms) == 0: # set itoms used (default value) for s in S: for v in s.vin: self.__itoms[v.name] = Itom(v.name, 0.0, variable=v.name) return S
def __recollect(self, itoms): itoms = Itoms(itoms) recollected = False if self.__substitutions is None \ or set(itoms.keys()) != set(self.__itoms.keys()): self.__substitutions = self.__collect_substitutions(itoms) recollected = True return recollected
def test_execute_with_interval(self): f = Function('a', ['b', 'c'], "a.v = b.v + c.v", name="add") b = Itom('b', interval([0.5, 1.5])) c = Itom('c', interval([1, 3])) o = f.execute(Itoms([b, c])) self.assertEqual(o['a'].v, interval([1.5, 4.5])) # mix interval arithmetic with scalars b = Itom('b', 1) o = f.execute(Itoms([b, c])) self.assertEqual(o['a'].v, interval([2, 4])) # timestamps are intervals code = "a.v = b.v + c.v" + "\n" + "a.t = b.t & c.t" f = Function('a', ['b', 'c'], code, name="add") b = Itom('b', 1, interval([0.1, 0.2])) c = Itom('c', 2, interval([0.15, 0.25])) o = f.execute(Itoms([b, c])) self.assertEqual(o['a'].v, 3) self.assertEqual(o['a'].t, interval([0.15, 0.2]))
def test_implementation_multiline(self): self.__pli.reset() self.__pli.load("test/test_py-implementation-multiline.pl") result = self.__pli.evaluate("query(implementation(r1, X)).") relation, code = self.__pli.parse_implementation(list(result)[0]) self.assertEqual(relation, "r1") # create function to execute f = Function('a', 'b', code, name='r1') itoms = f.execute(Itoms([Itom('b', 1, timestamp=0, variable='b')])) self.assertEqual(itoms['a'].v, 2) self.assertEqual(itoms['a'].t, 0) # execute through substitution S = self.__get_substitutions("query(substitution(a,S)).") itoms = Itoms([Itom('b1', 1, timestamp=0, variable='b')]) outputs = self.__execute_substitutions(S, itoms) s = list(S)[0] self.assertEqual(outputs[s]['a'].v, 2) self.assertEqual(outputs[s]['a'].t, 0)
def test_availability(self): a1 = Itom('a1', 0, variable='a') a2 = Itom('a2', 1, variable='a') b1 = Itom('b1', 2.1, variable='b') itoms = Itoms([a1, a2, b1]) av = itoms.availability self.assertEqual(len(av.keys()), 2) self.assertEqual(len(av['a']), 2) self.assertTrue('a1' in [itom.name for itom in av['a']]) self.assertEqual(len(av['b']), 1)
def test_execute(self): s = Substitution([self.__f_add, self.__f_mult]) b = Itom('b', 1) c = Itom('c', 2) itoms = Itoms(list=[b, c]) variables = s.execute(itoms) self.assertEqual(variables['d'].v, 6) # function names not unique s = Substitution([self.__f_add, self.__f_add2]) variables = s.execute([b, c]) self.assertEqual(variables['a'].v, 3)
def setUp(self): self.__itoms1 = Itoms([ Itom('x1', 10, variable='x'), Itom('x2', 10.01, variable='x'), Itom('a1', 5, variable='a'), Itom('b1', 5.1, variable='b'), Itom('c1', 4.95, variable='c'), Itom('d1', 19, variable='d'), ]) # no error (all intervals intersect in the common domain) self.__itoms2 = Itoms([ Itom('x1', interval([9, 11]), variable='x'), Itom('a1', interval([4.9, 5.1]), variable='a'), Itom('d1', interval([19.5, 20.5]), variable='d'), ]) # error (d1) self.__itoms2_err = Itoms([ Itom('x1', interval([9, 11]), variable='x'), Itom('a1', interval([4.9, 5.1]), variable='a'), Itom('d1', interval([23, 25]), variable='d'), # error because: 9..11 does not inersect with 11.5..12.5 ])
def __init__(self, model, domain, itoms=[], librarypaths=["./model/"], recollect=True): """Initialize the monitor. model -- SHSA knowledge base collecting the relations between variables. domain -- Common domain (a variable in the knowledge base) where the itoms will be compared to each other. itoms -- Itoms-object holding inputs to the monitor. librarypaths -- Set paths of problog libraries used in model. filter_window_size -- Set the size of the window for the median filter. """ self.__pli = ProblogInterface(librarypaths=librarypaths) self.__pli.load(model) """SHSA knowledge base.""" self.__domain = domain """Variable domain where the itoms shall be compared.""" self.__recollect_enabled = recollect """Indicates that a change in the itoms should trigger a re-query of the substitutions.""" self.__itoms = Itoms(itoms) """Available itoms or last itoms monitored. Used to identify a change in the itoms.""" self.__substitutions = None """List of substitutions used to bring the itoms into the common domain.""" # workaround: for ROS monitor (subscribe based on model) try: self.__substitutions = self.__collect_substitutions(itoms) # workaround: triggers reset on first monitor (necessary to fully initialize) self.__itoms = Itoms(itoms) except problog.engine.UnknownClause as e: # no itomsOf in the problog model (needed to find substitutions) # we will try later (monitor) pass # debugging self._debug_callback = None """Called at the end of monitor(.) to debug the monitor step."""
def test_time_uncertainty(self): m = Monitor("test/test_py-monitor-simple.pl", 'x') # itoms with uncertainties in time iok = Itoms([ Itom('x1', interval([9, 11]), timestamp=interval([0.1, 0.2]), variable='x'), Itom('a1', interval([5, 6]), timestamp=interval([0.15, 0.25]), variable='a'), Itom('b1', interval([4, 8]), timestamp=interval([0.1, 0.3]), variable='b'), ]) failed = m.monitor(iok) self.assertEqual(failed, None) # some itoms timestamps do not overlap ilate = Itoms([ Itom('x1', interval([9, 11]), timestamp=interval([0.0, 0.09]), variable='x'), Itom('a1', interval([5, 6]), timestamp=interval([0.15, 0.25]), variable='a'), Itom('b1', interval([4, 8]), timestamp=interval([0.1, 0.3]), variable='b'), ]) failed = m.monitor(ilate) self.assertEqual(failed, None)
def test_timestamp_model(self): m = Monitor("test/test_py-monitor-timestamp-simple.pl", 'c') self.assertEqual(m.buffer_size, 1) a1 = Itom('a1', 1, interval([0.11, 0.21]), variable='a') b1 = Itom('b1', 2, interval([0.12, 0.22]), variable='b') c1 = Itom('c1', 3, interval([0.05, 0.15]), variable='c') c2 = Itom('c2', 3, interval([0.14, 0.24]), variable='c') failed = m.monitor(Itoms([a1, b1, c1, c2])) self.assertEqual(failed, None) # wrong value, however time stamp do not overlap -> won't be used for comparison c1_late = Itom('c1', 0, interval([0.0, 0.04]), variable='c') failed = m.monitor(Itoms([a1, b1, c1_late, c2])) self.assertEqual(failed, None) # monitor shall compensate late itoms -> increase buffer size m = Monitor("test/test_py-monitor-timestamp-simple.pl", 'c', buffer_size=2) self.assertEqual(m.buffer_size, 2) failed = m.monitor(Itoms([c1, c2])) failed = m.monitor(Itoms([c1_late, c2])) # another late value but wrong itom arrives c2_wrong = Itom('c2', 1, interval([0.0, 0.03]), variable='c') failed = m.monitor(Itoms([c1, c2_wrong])) self.assertNotEqual(failed, None)
def test_implementation_simple(self): self.__pli.load("test/test_py-implementation-simple.pl") S = self.__get_substitutions("query(substitution(x,S)).") # create some dummy data x1 = Itom('x1', 2, variable='x') x2 = Itom('x2', 2, variable='x') a1 = Itom('a1', 1, variable='a') b1 = Itom('b1', 1, variable='b') c1 = Itom('c1', 1, variable='c') d1 = Itom('d1', 4, variable='d') itoms = Itoms([x1, x2, a1, b1, c1, d1]) # execute substitutions outputs = self.__execute_substitutions(S, itoms) # check for output_itoms in outputs.values(): self.assertAlmostEqual(output_itoms['x'].v, 2)
def bring_to_common_domain(S, itoms): """Apply substitutions to itoms. Returns values. Values is an ordered dictionary of substitution->itom (Itoms with key=substitution). """ output = Itoms() for i, s in enumerate(S): try: result = s.execute(itoms) output[s] = result[variable] except Exception as e: print("Execution failed - value ignored. {}".format(e)) raise e return output
def execute(self, itoms): """Execute the substitution and returns all variables. itoms -- dictionary of itom-name->'Itom'-object. These will be passed as local variables to the functions. Passes itoms through the functions. Output variables of functions may be appended (variable-name->'Itom'-object). However, the last variable assigned is 'this.vout' which is the result of the substitution given the inputs 'itoms'. The value (and possibly a timestamp) of 'this.vout' can be retrieved from the returned variables ('this.vout.v'). """ # make sure its an Itoms-object itoms = Itoms(itoms) # verify inputs if not set([v.name for v in self.vin]).issubset( set([v.name for v in itoms.values()])): raise RuntimeError("Missing itoms to execute the substitution.") # execute function after function for f in self: itoms = f.execute(itoms) return itoms
def test_init_itoms(self): empty = Itoms() a = Itom('a', 0) b = Itom('b', 1, variable='b') c = Itom('c', 2.1) l = Itoms(list=[a, b, c]) self.assertEqual(len(l), 3) self.assertEqual(l['a'].v, 0) l = Itoms([a, b, c]) self.assertEqual(len(l), 3) self.assertEqual(l['b'].v, 1) # try different initial iterables and keys l = Itoms({1: a, 2: b, 3: c}) self.assertEqual(len(l), 3) self.assertEqual(l[1], a) l = Itoms(set([a, b, c])) self.assertEqual(len(l), 3) self.assertTrue(c in l.values()) # start from empty dict l = Itoms() l['av'] = a self.assertEqual(len(l), 1) self.assertEqual(l['av'], a)
def __execute_substitutions(self, S, itoms): output = Itoms() for i, s in enumerate(S): output[s] = s.execute(itoms) return output
'b1': 1, 'c1': 1, 'd1': 1, } # # get the available itoms # try: data = pd.read_csv(args.csv) except Exception as e: raise SystemExit("Failed to parse csv. {}".format(e)) # get variable/itoms available from column name (header) 'variable:itom' itoms = Itoms() try: for head in data.columns.values: head = head.strip().split(':') v = head[0] i = head[1] # empty value and timestamp for this itom describing the header only itom = Itom(i, None, variable=v, timestamp=None) itoms[itom.name] = itom except Exception as e: raise SystemExit( "Failed to parse column name 'variable:itom'. {}".format(e)) # problog knows the mapping now, so we can change the column names to itoms only # substitution only needs itom names data.columns = itoms.keys()