def test_slice(): sig1 = signal(DATA1, start=0, end=4, tag='x') sig2 = sig1[-float('inf'):float('inf')][0:4] assert sig1 == sig2 assert sig1[1:2] == signal(DATA1, start=1, end=2, tag='x') assert len(sig1[1:2].values()) == 1
def test_make_signal(): sig1 = signal(DATA1, start=0, end=4, tag='x') sig2 = signal(DATA1, start=1, end=2, tag='x') assert sig1 | sig2 == sig1 assert set(fn.pluck(0, DATA1)) == set(sig1.times()) assert set(fn.pluck(0, DATA1)) > set(sig2.times()) assert len(sig2.times()) == 1 assert set(fn.pluck(1, DATA1)) == {v['x'] for v in sig1.values()}
def to_signal(ts_mapping) -> DiscreteSignal: if isinstance(ts_mapping, DiscreteSignal): return ts_mapping start = min(fn.pluck(0, fn.cat(ts_mapping.values()))) signals = (signal(v, start, OO, tag=k) for k, v in ts_mapping.items()) return reduce(op.or_, signals)
def test_eval_with_signal(): spec = mtl.parse('F(above_three)') raw_data = signal([(0, 1), (1, 2), (2, 3)], start=0, end=10, tag='a') processed = raw_data.map(lambda val: val['a'] > 3, tag="above_three") assert not spec(processed, quantitative=False) assert spec(processed, quantitative=True) == 0
def test_retag(): sig1 = signal(DATA1, start=0, end=4, tag='x') assert sig1 == sig1.retag({'w': 'n'}) sig2 = sig1.retag({'x': 'y'}) assert sig2.tags == {'y'} assert sig2.retag({'y': 'x'}) == sig1
def _eval(x): tmp = f(x) assert b >= a if b > a: if a < b < OO: if a != 0: return signal(_rolling(tmp, 0, b - a), tmp.start, tmp.end - b if b < tmp.end else tmp.end, tag=phi) << a else: return signal(_rolling(tmp, a, b), tmp.start, tmp.end - b if b < tmp.end else tmp.end, tag=phi) else: return signal(_rolling_inf(tmp), tmp.start, tmp.end - b if b < tmp.end else tmp.end, tag=phi) return tmp.rolling(a, b).map(_min, tag=phi) return tmp.retag({phi.arg: phi})
def test_rolling(): sig1 = signal(DATA1, start=0, end=4, tag='x') sig2 = sig1.rolling(0, 3) assert {v['x'] for v in sig1.values()} == set(sig2[0]['x']) sig3 = sig1.rolling(0, 2) assert {v['x'] for v in sig1[:2].values()} == set(sig3[0]['x']) sig4 = sig1.rolling(0, 1) assert {v['x'] for v in sig1[:1].values()} == set(sig4[0]['x']) sig5 = sig1.rolling(-1.1, 1.1) assert {v['x'] for v in sig1.values()} == set(sig5[1.1]['x']) assert {v['x'] for v in sig1[2:].values()} == set(sig5[3.1]['x'])
def test_par_compose(): sig1 = signal(DATA1, start=0, end=4, tag='x') assert sig1 | sig1 == sig1 sig2 = sig1 | sig1 >> 0.5 assert len(sig2.values()) == 2*len(sig1.values()) sig3 = sig1 | sig1 >> 1 assert len(sig3.values()) == len(sig1.values())+1 sig4 = sig1.map(lambda v: -v['x'], tag='y') sig5 = (sig1 | sig4).map(lambda v: sum(v.values()), tag='z') assert len(sig5.times()) == len(sig1.times()) assert all(v['z'] == 0 for v in sig5.values())
def test_append(): sig1 = signal(DATA1, start=0, end=4, tag='x') sig2 = sig1 @ sig1 assert len(sig2.values()) == 2*len(sig1.values()) assert sig1.start == sig2.start assert sig2.end == 2*sig1.end
def test_time_shifts(): sig1 = signal(DATA1, start=0, end=4, tag='x') assert (sig1 << 2) == (sig1 >> -2) assert (sig1 << 2) >> 2 == sig1
def TOP(self): return signal([(0, self.const_true)], start=-OO, end=OO, tag=ast.TOP)
def test_repr(): out = repr(signal(DATA1, start=0, end=4, tag='x')) assert out == "start, end: [0, 4)\n" \ "data: [(0, {'x': 1}), (1, {'x': 1.1}), (2, {'x': 3})]"
def test_transform(): sig1 = signal(DATA1, start=0, end=4, tag='x') assert sig1.transform(lambda v: v) == sig1 assert sig1.transform(lambda v: {'y': v['x']}) \ == sig1.retag({'x': 'y'})
def const_trace(x): return signal([(0, x)], start=0, end=oo)
def BOT(self): return signal([(0, self.const_false)], start=-OO, end=OO, tag=ast.BOT)
def test_filter(): sig1 = signal(DATA1, start=0, end=4, tag='x') sig2 = sig1.filter(lambda v: v['x'] > 2) assert len(sig2.items()) == 1 assert sig2[2]['x'] == 3
def _eval(x): sig = dense_compose(f1(x), f2(x), init=logic.const_false) sig = sig | interp_all(sig, x.start) data = apply_implies(phi.arg1, phi.arg2, sig, logic) return signal(data, x.start, x.end, tag=phi)
def to_signal(ts_mapping): start = min(fn.pluck(0, fn.cat(ts_mapping.values()))) assert start >= 0 signals = (signal(v, start, OO, tag=k) for k, v in ts_mapping.items()) return reduce(op.or_, signals)
def eval_mtl_constant_number(phi, dt, logic): return lambda _: signal([(0, phi)], start=-OO, end=OO, tag=phi)
def _eval(x): sig = dense_compose(f1(x), f2(x), init=-OO) data = apply_weak_until(phi.arg1, phi.arg2, sig) return signal(data, x.start, OO, tag=phi)
# TODO: figure out how to deduplicate this with robustness # - Abstract as working on distributive lattice import operator as op from collections import defaultdict from functools import reduce, singledispatch import funcy as fn from discrete_signals import signal, DiscreteSignal from mtl import ast OO = float('inf') CONST_FALSE = signal([(0, -1)], start=-OO, end=OO, tag=ast.BOT) CONST_TRUE = signal([(0, 1)], start=-OO, end=OO, tag=ast.TOP) def to_signal(ts_mapping) -> DiscreteSignal: if isinstance(ts_mapping, DiscreteSignal): return ts_mapping start = min(fn.pluck(0, fn.cat(ts_mapping.values()))) signals = (signal(v, start, OO, tag=k) for k, v in ts_mapping.items()) return reduce(op.or_, signals) def interp(sig, t, tag=None): # TODO: return function that interpolates the whole signal. sig = sig.project({tag}) idx = max(sig.data.bisect_right(t) - 1, 0) key = sig.data.keys()[idx]
def interp_all(sig, t, end=OO): v = fn.map(lambda u: signal([(t, interp(sig, t, u))], t, end, u), sig.tags) return reduce(op.__or__, v)
def eval_mtl(phi, dt, logic): return lambda _: signal([(0, phi)], start=-OO, end=OO, tag=phi)
def test_project(): sig1 = signal(DATA1, start=0, end=4, tag='x') sig2 = signal(DATA1, start=0, end=4, tag='y') assert (sig1 | sig2).project('x') == sig1
def _eval(x): sig = dense_compose(f1(x), f2(x), init=-OO) sig = sig | interp_all(sig, x.start, OO) # Force valuation at start data = apply_weak_until(phi.arg1, phi.arg2, sig) return signal(data, x.start, OO, tag=phi)