def test_zero_dynamics(): 'test a system with zero dynamic (only should process one frame)' ha = HybridAutomaton() # with time and affine variable mode = ha.new_mode('mode') mode.set_dynamics([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]) # initial set init_lpi = lputil.from_box([(-5, -5), (0, 1), (0, 0), (1, 1)], mode) init_list = [StateSet(init_lpi, mode)] # settings settings = HylaaSettings(math.pi/4, 20*math.pi) settings.stdout = HylaaSettings.STDOUT_VERBOSE settings.plot.plot_mode = PlotSettings.PLOT_NONE core = Core(ha, settings) core.setup(init_list) core.do_step() # pop core.do_step() # propagate and remove assert core.aggdag.get_cur_state() is None, "cur state should be none, since mode dynamics were zero"
def test_tt_with_invstr(): 'test time-triggered transitions combined with invariant strengthening' ha = HybridAutomaton() # mode one: x' = 1, a' = 0 m1 = ha.new_mode('m1') m1.set_dynamics([[0, 1], [0, 0]]) m1.set_invariant([[1, 0]], [2.0]) # invariant: x <= 2.0 # mode two: x' = 1, a' = 0 m2 = ha.new_mode('m2') m2.set_dynamics([[0, 1], [0, 0]]) m2.set_invariant([[1, 1]], [4.0]) # x + a <= 4.0 # guard: x >= 2.0 trans1 = ha.new_transition(m1, m2, 'trans1') trans1.set_guard([[-1, 0]], [-2.0]) # error x >= 4.0 error = ha.new_mode('error') trans2 = ha.new_transition(m2, error, "to_error") trans2.set_guard([[-1, 0]], [-4.0]) # initial set has x0 = [0, 1] init_lpi = lputil.from_box([(0, 1), (0, 1)], m1) init_list = [StateSet(init_lpi, m1)] # settings, step size = 0.1 settings = HylaaSettings(0.1, 5.0) settings.stdout = HylaaSettings.STDOUT_VERBOSE settings.plot.plot_mode = PlotSettings.PLOT_NONE # run setup() only and check the result core = Core(ha, settings) core.setup(init_list) assert trans1.time_triggered assert not trans2.time_triggered # not time-triggered because invariant of m2 is True
def test_redundant_inv_transition(): 'test removing of redundant invariants with a transition' ha = HybridAutomaton() mode1 = ha.new_mode('mode1') # dynamics: x' = 1, y' = 1, a' = 0 mode1.set_dynamics([[0, 0, 1], [0, 0, 1], [0, 0, 0]]) # invariant: x <= 2.5 mode1.set_invariant([[1, 0, 0]], [2.5]) mode2 = ha.new_mode('mode2') mode2.set_dynamics([[0, 0, 0], [0, 0, 0], [0, 0, 0]]) ha.new_transition(mode1, mode2).set_guard([[-1, 0, 0]], [-2.5]) # x >= 2.5 # initial set has x0 = [0, 1] init_lpi = lputil.from_box([(0, 1), (0, 1), (1, 1)], mode1) init_list = [StateSet(init_lpi, mode1)] # settings, step size = 0.1 settings = HylaaSettings(0.1, 5.0) settings.stdout = HylaaSettings.STDOUT_DEBUG settings.plot.plot_mode = PlotSettings.PLOT_NONE core = Core(ha, settings) core.setup(init_list) for _ in range(20): core.do_step() assert core.result.last_cur_state.lpi.get_num_rows() == 3 + 2*3 + 1 # 3 for basis matrix, 2*3 for init constraints assert len(core.aggdag.waiting_list) > 2 core.plotman.run_to_completion()
def test_agg_ha(): 'test aggregation with the harmonic oscillator dynamics' ha = HybridAutomaton('Deaggregation Example') m1 = ha.new_mode('green') m1.set_dynamics([[0, 1], [-1, 0]]) m2 = ha.new_mode('cyan') m2.set_dynamics([[0, 0, 0], [0, 0, -2], [0, 0, 0]]) t1 = ha.new_transition(m1, m2) t1.set_guard_true() reset_mat = [[1, 0], [0, 1], [0, 0]] t1.set_reset(reset_mat, [[0], [0], [1]], [[1], [-1]], [1, -1]) # create 3rd variable with a0 = 1 mode = ha.modes['green'] init_lpi = lputil.from_box([(-5, -4), (-0.5, 0.5)], mode) init_list = [StateSet(init_lpi, mode)] step = math.pi/4 settings = HylaaSettings(step, 2*step) settings.process_urgent_guards = True settings.plot.plot_mode = PlotSettings.PLOT_NONE settings.stdout = HylaaSettings.STDOUT_DEBUG core = Core(ha, settings) core.setup(init_list) core.do_step() # pop #xs, ys = zip(*core.cur_state.verts(core.plotman)) #plt.plot(xs, ys, 'k-') core.do_step() # 0 #xs, ys = zip(*core.cur_state.verts(core.plotman)) #plt.plot(xs, ys, 'k-') core.do_step() # 1 #xs, ys = zip(*core.cur_state.verts(core.plotman)) #plt.plot(xs, ys, 'k-') core.do_step() # 2 assert len(core.aggdag.waiting_list) > 1 #for state in core.waiting_list: # xs, ys = zip(*state.verts(core.plotman)) # plt.plot(xs, ys, 'k-') core.do_step() # pop assert not core.aggdag.waiting_list lpi = core.aggdag.get_cur_state().lpi # 3 constraints from basis matrix # 2 aggregation directions from premode arnoldi, +1 from null space # + 2 more aggregation directions from box (3rd is omited since it's exactly the same as null space direction) #print(lpi) #xs, ys = zip(*core.cur_state.verts(core.plotman)) #plt.plot(xs, ys, 'r--') #plt.show() assert lpi.get_num_rows() == 3 + 2 * (5) assert lputil.is_point_in_lpi((-5, 2, 1), lpi)
def test_inputs_reset(): 'test a system with both inputs and a reset' # 2-d system with one input # x' = x, y' = u, u \in [1, 1] # x0 = 1, y0 = 0 # inv1: y <= 2.5 # guard: y >= 2.5 # reset: x := 1, y += 2 [should go from (e^3, 3.0) -> (1, 5.0)] # mode2: # x' = 2x, y' = Bu, u \in [1, 2], B = 2 # (1, 5.0) -> (e^2, [7, 9]) -> (e^4, [9, 13]) # mode2 -> error y >= 13 ha = HybridAutomaton() m1 = ha.new_mode('m1') m1.set_dynamics([[1, 0], [0, 0]]) m1.set_inputs([[0], [1]], [[1], [-1]], [1, -1], allow_constants=True) m1.set_invariant([[0, 1]], [2.5]) m2 = ha.new_mode('m2') m2.set_dynamics([[2, 0], [0, 0]]) m2.set_inputs([[0], [2]], [[1], [-1]], [2, -1]) error = ha.new_mode('error') t1 = ha.new_transition(m1, m2) t1.set_guard([[0, -1]], [-2.5]) # y >= 2.5 reset_mat = [[0, 0], [0, 1]] min_mat = np.identity(2) min_cons = [[1, 0], [-1, 0], [0, 1], [0, -1]] min_rhs = [1, -1, 2, -2] t1.set_reset(reset_mat, min_mat, min_cons, min_rhs) t2 = ha.new_transition(m2, error) t2.set_guard([0, -1], [-13]) # y >= 13 init_box = [[1, 1], [0, 0]] lpi = lputil.from_box(init_box, m1) settings = HylaaSettings(1.0, 10.0) settings.stdout = HylaaSettings.STDOUT_VERBOSE settings.plot.store_plot_result = True settings.plot.plot_mode = PlotSettings.PLOT_NONE core = Core(ha, settings) init_list = [StateSet(lpi, m1)] core.setup(init_list) core.do_step() # pop core.do_step() # continuous_post() to time 1 lpi = core.result.last_cur_state.lpi assert lpi.get_names() == ['m0_i0', 'm0_i1', 'm0_c0', 'm0_c1', 'm0_ti0', 'm0_ti1', 'm0_I0'] assert_verts_is_box(lpplot.get_verts(lpi), [[math.exp(1), math.exp(1)], [1, 1]]) core.do_step() # continuous_post() to time 2 assert_verts_is_box(lpplot.get_verts(core.result.last_cur_state.lpi), [[math.exp(2), math.exp(2)], [2, 2]]) core.do_step() # continuous_post() to time 3 assert_verts_is_box(lpplot.get_verts(core.result.last_cur_state.lpi), [[math.exp(3), math.exp(3)], [3, 3]]) core.do_step() # trim to invariant assert core.aggdag.get_cur_state() is None assert len(core.aggdag.waiting_list) == 1 core.run_to_completion() result = core.result # reset: x := 1, y += 2 [should go from (e^3, 3.0) -> (1, 5.0)] # (1, 5.0) -> (e^2, [7, 9]) -> (e^4, [9, 13]) polys2 = [obj[0] for obj in result.plot_data.mode_to_obj_list[0]['m2']] assert_verts_is_box(polys2[0], [[1, 1], [5, 5]]) assert_verts_is_box(polys2[1], [[math.exp(2), math.exp(2)], [7, 9]]) assert_verts_is_box(polys2[2], [[math.exp(4), math.exp(4)], [9, 13]]) assert len(polys2) == 3 # check counterexamples assert len(result.counterexample) == 2 c1 = result.counterexample[0] assert c1.mode == m1 assert c1.outgoing_transition == t1 assert np.allclose(c1.start, [1, 0]) assert np.allclose(c1.end, [math.exp(3), 3]) assert len(c1.reset_minkowski_vars) == 2 assert abs(c1.reset_minkowski_vars[0] - 1) < 1e-9 assert abs(c1.reset_minkowski_vars[1] - 2) < 1e-9 assert len(c1.inputs) == 3 for i in c1.inputs: assert len(i) == 1 assert abs(i[0] - 1) < 1e-9 c2 = result.counterexample[1] assert c2.mode == m2 assert c2.outgoing_transition == t2 assert np.allclose(c2.start, [1, 5]) assert np.allclose(c2.end, [math.exp(4), 13]) assert not c2.reset_minkowski_vars assert len(c2.inputs) == 2 for i in c2.inputs: assert len(i) == 1 assert abs(i[0] - 2) < 1e-9
def fail_deagg_counterexample(): 'test that aggregation with a counterexample' # init: x0, y0 \in [0, 1], step = 1.0 # # m1 dynamics: x' == 1, y' == 0, # m1 invariant: x <= 3 # m1 -> m2 guard: True # m2 dynamics: x' == 0, y' == 1 # m2 -> error: y >= 3 ha = HybridAutomaton() # mode one: x' = 1, y' = 0, a' = 0 m1 = ha.new_mode('m1') m1.set_dynamics([[0, 0, 1], [0, 0, 0], [0, 0, 0]]) # mode two: x' = 0, y' = 1, a' = 0 m2 = ha.new_mode('m2') m2.set_dynamics([[0, 0, 0], [0, 0, 1], [0, 0, 0]]) # invariant: x <= 3.0 m1.set_invariant([[1, 0, 0]], [3.0]) # guard: True trans1 = ha.new_transition(m1, m2, 'trans1') trans1.set_guard_true() error = ha.new_mode('error') trans2 = ha.new_transition(m2, error, 'trans2') trans2.set_guard([[0, -1, 0]], [-3]) # y >= 3 # initial set has x0 = [0, 1], t = [0, 1], a = 1 init_lpi = lputil.from_box([(0, 1), (0, 1), (1, 1)], m1) init_list = [StateSet(init_lpi, m1)] # settings, step size = 1.0 settings = HylaaSettings(1.0, 10.0) settings.stdout = HylaaSettings.STDOUT_VERBOSE settings.plot.plot_mode = PlotSettings.PLOT_NONE settings.aggregation.deaggregation = True core = Core(ha, settings) core.setup(init_list) core.do_step() # pop assert core.aggdag.cur_node is not None for _ in range(5): # 4 + 1 step to leave invariant core.do_step() # continuous post in m1 core.do_step() # pop # at this point, the state should be aggregated, but we should maintain one concrete state that's feasible cur_node = core.aggdag.cur_node assert cur_node.aggregated_state == core.aggdag.get_cur_state() assert cur_node.concrete_state is not None assert len(core.aggdag.roots) == 1 root = core.aggdag.roots[0] assert root.op_list assert isinstance( root.op_list[0], OpTransition) and root.op_list[0].step == 1 # transition from x=[1, 2] assert isinstance( root.op_list[1], OpTransition) and root.op_list[1].step == 2 # transition from x=[2, 3] assert isinstance( root.op_list[2], OpTransition) and root.op_list[2].step == 3 # transition from x=[3, 4] assert isinstance( root.op_list[3], OpTransition) and root.op_list[3].step == 4 # transition from x=[4, 5] for s in range(4): assert root.op_list[s].transition == trans1 and root.op_list[ s].poststate.is_concrete assert isinstance(root.op_list[4], OpInvIntersect) op4 = root.op_list[4] assert op4.step == 5 and op4.node == root and op4.i_index == 0 and not op4.is_stronger assert isinstance( root.op_list[5], OpTransition) and root.op_list[5].step == 5 # transition from x=[4, 4] assert isinstance(root.op_list[6], OpInvIntersect) op6 = root.op_list[6] assert op6.step == 6 and op6.node == root and op6.i_index == 0 and op6.is_stronger assert len(root.op_list) == 7 core.run_to_completion() assert root.op_list[0].child_node == cur_node assert root.op_list[3].child_node == cur_node