def define_ha(unsafe_box): '''make the hybrid automaton''' ha = HybridAutomaton() # dynamics: x' = y, y' = -x, t' == a a_mat = [[0, 1, 0, 0], [-1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 0]] one = ha.new_mode('one') one.set_dynamics(a_mat) one.set_invariant([[0, 0, 1, 0]], [math.pi - 1e-6]) # t <= pi two = ha.new_mode('two') two.set_dynamics([[0, 0, 0, 0], [0, 0, 0, -1], [0, 0, 0, 1], [0, 0, 0, 0]]) two.set_invariant([[0, -1, 0, 0]], [3]) # y >= -3 t = ha.new_transition(one, two) t.set_guard_true() error = ha.new_mode('error') t = ha.new_transition(two, error) unsafe_rhs = [ -unsafe_box[0][0], unsafe_box[0][1], -unsafe_box[1][0], unsafe_box[1][1] ] t.set_guard([[-1, 0, 0, 0], [1, 0, 0, 0], [0, -1, 0, 0], [0, 1, 0, 0]], unsafe_rhs) return ha
def test_guard_strengthening(): 'simple 2-mode, 2-guard, 2d system with 1st guard A->B is x <= 2, 2nd guard A->B is y <= 2, and inv(B) is y <= 2' ha = HybridAutomaton() mode_a = ha.new_mode('A') mode_a.set_dynamics(np.identity(2)) mode_b = ha.new_mode('B') mode_b.set_dynamics(np.identity(2)) mode_b.set_invariant([[0, 1]], [2]) trans1 = ha.new_transition(mode_a, mode_b, 'first') trans1.set_guard([[1, 0]], [2]) trans2 = ha.new_transition(mode_a, mode_b, 'second') trans2.set_guard([[0, 1]], [2]) ha.do_guard_strengthening() # trans1 should now have 2 conditions assert (trans1.guard_csr.toarray() == np.array([[1, 0], [0, 1]], dtype=float)).all() assert (trans1.guard_rhs == np.array([2, 2], dtype=float)).all() # trans2 should still have 1 condition since invariant was redundant assert (trans2.guard_csr.toarray() == np.array([[0, 1]], dtype=float)).all()
def test_transition(): 'test a discrete transition' ha = HybridAutomaton() # mode one: x' = 1, t' = 1, a' = 0 m1 = ha.new_mode('m1') m1.set_dynamics([[0, 0, 1], [0, 0, 1], [0, 0, 0]]) # mode two: x' = -1, t' = 1, a' = 0 m2 = ha.new_mode('m2') m2.set_dynamics([[0, 0, -1], [0, 0, 1], [0, 0, 0]]) # invariant: t <= 2.5 m1.set_invariant([[0, 1, 0]], [2.5]) # guard: t >= 2.5 trans1 = ha.new_transition(m1, m2, 'trans1') trans1.set_guard([[0, -1, 0]], [-2.5]) # error t >= 4.5 error = ha.new_mode('error') trans2 = ha.new_transition(m2, error, "to_error") trans2.set_guard([[0, -1, 0]], [-4.5]) # initial set has x0 = [0, 1], t = [0, 0.2], a = 1 init_lpi = lputil.from_box([(0, 1), (0, 0.2), (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.plot.store_plot_result = True result = Core(ha, settings).run(init_list) ce = result.counterexample assert len(ce) == 2 assert ce[0].mode.name == 'm1' assert ce[0].outgoing_transition.name == 'trans1' assert ce[1].mode.name == 'm2' assert ce[1].outgoing_transition.name == 'to_error' assert ce[1].start[0] + 1e-9 >= 3.0 assert ce[1].end[0] - 1e-9 <= 2.0 polys = [obj[0] for obj in result.plot_data.mode_to_obj_list[0]['m1']] assert len(polys) == 4 polys = [obj[0] for obj in result.plot_data.mode_to_obj_list[0]['m2']] assert len(polys) == 3 assert result.last_cur_state.cur_steps_since_start[0] == 5
def define_ha(limit): '''make the hybrid automaton and return it''' ha = HybridAutomaton() mode = ha.new_mode('mode') dynamics = loadmat('build.mat') a_matrix = dynamics['A'] b_matrix = csc_matrix(dynamics['B']) mode.set_dynamics(csr_matrix(a_matrix)) # 0.8 <= u1 <= 1.0 u_mat = [[1.0], [-1.0]] u_rhs = [1.0, -0.8] mode.set_inputs(b_matrix, u_mat, u_rhs) error = ha.new_mode('error') y1 = dynamics['C'][0] mat = csr_matrix(y1, dtype=float) trans1 = ha.new_transition(mode, error) rhs = np.array([-limit], dtype=float) # safe trans1.set_guard(mat, rhs) # y3 >= limit return ha
def make_automaton(): 'make the hybrid automaton' ha = HybridAutomaton() # mode one: x' = y + u1, y' = -x + u2, c' = 1, a' = 0 m1 = ha.new_mode('m1') m1.set_dynamics([[0, 1, 0, 0], [-1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 0]]) b_mat = [[1, 0], [0, 1], [0, 0], [0, 0]] b_constraints = [[1, 0], [-1, 0], [0, 1], [0, -1]] b_rhs = [0.5, 0.5, 0.5, 0.5] m1.set_inputs(b_mat, b_constraints, b_rhs) # mode two: x' = -y, y' = x, a' = 0 m2 = ha.new_mode('m2') m2.set_dynamics([[0, -1], [1, 0]]) # m1 invariant: c <= pi/2 m1.set_invariant([[0, 0, 1, 0]], [math.pi / 2]) # guard: c >= pi/2 trans = ha.new_transition(m1, m2) trans.set_guard([[0, 0, -1, 0]], [-math.pi / 2]) # Assign the reset to the transition # y *= -1, also the reset is what is used to change the number of system variables (m1 has four vars, m2 has two) reset_csr = [[1, 0, 0, 0], [0, -1, 0, 0]] # no minkowski sum terms trans.set_reset(reset_csr) return ha
def test_init_outside_invariant(): 'test when initial state is outside of the mode invariant' ha = HybridAutomaton() mode = ha.new_mode('mode') mode.set_dynamics([[0, 0, 1], [0, 0, 1], [0, 0, 0]]) # x' = 1, y' = 1, a' = 0 # x <= 2.5 mode.set_invariant([[1, 0, 0]], [2.5]) # initial set, x = [3, 4] init_lpi = lputil.from_box([(3, 4), (0, 1), (1, 1)], mode) init_list = [StateSet(init_lpi, mode)] # transition to error if x >= 10 error = ha.new_mode('error') trans = ha.new_transition(mode, error) trans.set_guard([[-1., 0, 0],], [-10]) # settings settings = HylaaSettings(1.0, 5.0) settings.stdout = HylaaSettings.STDOUT_VERBOSE try: Core(ha, settings).run(init_list) assert False, "running with initial state outside of invariant did not raise RuntimeError" except RuntimeError: pass
def test_agg_with_reset(): 'test the aggregation of states with a reset' # m1 dynamics: x' == 1, y' == 0, x0: [-3, -2], y0: [0, 1], step: 1.0 # m1 invariant: x + y <= 0 # m1 -> m2 guard: x + y >= 0 and y <= 0.5, reset = [[0, -1, 0], [1, 0, 0]] (x' = -y, y' = x, remove a) # m2 dynamics: x' == 0, y' == 0 # time bound: 4 # expected result: last state is line (not box!) from (0, 0) to (-0.5, -0.5) 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 m2 = ha.new_mode('m2') m2.set_dynamics([[0, 0], [0, 0]]) # invariant: x + y <= 0 m1.set_invariant([[1, 1, 0]], [0]) # guard: x + y == 0 & y <= 0.5 trans1 = ha.new_transition(m1, m2, 'trans1') trans1.set_guard([[-1, -1, 0], [1, 1, 0], [0, 1, 0]], [0, 0, 0.5]) #trans1.set_reset(np.identity(3)[:2]) trans1.set_reset(np.array([[0, -1, 0], [1, 0, 0]], dtype=float)) # initial set has x0 = [-3, -2], y = [0, 1], a = 1 init_lpi = lputil.from_box([(-3, -2), (0, 1), (1, 1)], m1) init_list = [StateSet(init_lpi, m1)] # settings, step size = 1.0 settings = HylaaSettings(1.0, 4.0) settings.stdout = HylaaSettings.STDOUT_NONE settings.plot.plot_mode = PlotSettings.PLOT_NONE # use agg_box settings.aggstrat.agg_type = Aggregated.AGG_BOX core = Core(ha, settings) result = core.run(init_list) lpi = result.last_cur_state.lpi # 2 basis matrix rows, 4 init constraints rows, 6 rows from guard conditions (2 from each) assert lpi.get_num_rows() == 2 + 4 + 6 verts = result.last_cur_state.verts(core.plotman) assert len(verts) == 3 assert np.allclose(verts[0], verts[-1]) assert pair_almost_in((0, 0), verts) assert pair_almost_in((-0.5, -0.5), verts)
def test_unaggregation(): 'test an unaggregated discrete transition' ha = HybridAutomaton() # mode one: x' = 1, t' = 1, a' = 0 m1 = ha.new_mode('m1') m1.set_dynamics([[0, 0, 1], [0, 0, 1], [0, 0, 0]]) # mode two: x' = -1, t' = 1, a' = 0 m2 = ha.new_mode('m2') m2.set_dynamics([[0, 0, -1], [0, 0, 1], [0, 0, 0]]) # invariant: t <= 2.5 m1.set_invariant([[0, 1, 0]], [2.5]) # guard: t >= 0.5 trans1 = ha.new_transition(m1, m2, 'trans1') trans1.set_guard([[0, -1, 0]], [-0.5]) # error x >= 4.5 error = ha.new_mode('error') trans2 = ha.new_transition(m2, error, "to_error") trans2.set_guard([[-1, 0, 0]], [-4.5]) # initial set has x0 = [0, 0.2], t = [0, 0.2], a = 1 init_lpi = lputil.from_box([(0, 0.2), (0, 0.2), (1, 1)], m1) init_list = [StateSet(init_lpi, m1)] # settings, step size = 1.0 settings = HylaaSettings(1.0, 10.0) settings.stdout = HylaaSettings.STDOUT_DEBUG settings.plot.store_plot_result = True settings.plot.plot_mode = PlotSettings.PLOT_NONE settings.aggstrat = aggstrat.Unaggregated() result = Core(ha, settings).run(init_list) # expected no exception # m2 should be reachable polys = [obj[0] for obj in result.plot_data.mode_to_obj_list[0]['m2']] assert len(polys) > 15
def test_agg_to_more_vars(): 'test the aggregation of states with a reset to a mode with new variables' ha = HybridAutomaton() # mode one: x' = 1, a' = 0 m1 = ha.new_mode('m1') m1.set_dynamics([[0, 1], [0, 0]]) # mode two: x' = 0, a' = 0, y' == 1 m2 = ha.new_mode('m2') m2.set_dynamics([[0, 0, 0], [0, 0, 0], [0, 1, 0]]) # invariant: x <= 3.0 m1.set_invariant([[1, 0]], [3.0]) # guard: True trans1 = ha.new_transition(m1, m2, 'trans1') trans1.set_guard_true() reset_mat = [[1, 0], [0, 1], [0, 0]] reset_minkowski = [[0], [0], [1]] reset_minkowski_constraints = [[1], [-1]] reset_minkowski_rhs = [3, -3] # y0 == 3 trans1.set_reset(reset_mat, reset_minkowski, reset_minkowski_constraints, reset_minkowski_rhs) # initial set has x0 = [0, 1], a = 1 init_lpi = lputil.from_box([(0, 1), (1, 1)], m1) init_list = [StateSet(init_lpi, m1)] # settings, step size = 1.0 settings = HylaaSettings(1.0, 4.0) settings.stdout = HylaaSettings.STDOUT_DEBUG settings.plot.plot_mode = PlotSettings.PLOT_NONE settings.plot.store_plot_result = True settings.plot.xdim_dir = 0 settings.plot.ydim_dir = {'m1': 1, 'm2': 2} result = Core(ha, settings).run(init_list) polys = [obj[0] for obj in result.plot_data.mode_to_obj_list[0]['m1']] # 4 steps because invariant is allowed to be false for the final step assert 4 <= len(polys) <= 5, "expected invariant to become false after 4/5 steps" assert_verts_is_box(polys[0], [[0, 1], [1, 1]]) assert_verts_is_box(polys[1], [[1, 2], [1, 1]]) assert_verts_is_box(polys[2], [[2, 3], [1, 1]]) assert_verts_is_box(polys[3], [[3, 4], [1, 1]]) polys = [obj[0] for obj in result.plot_data.mode_to_obj_list[0]['m2']] assert_verts_is_box(polys[0], [[1, 4], [3, 3]]) assert_verts_is_box(polys[1], [[1, 4], [4, 4]])
def make_automaton(unsafe_box): 'make the hybrid automaton' ha = HybridAutomaton('Deaggregation Example') # x' = 2 m1 = ha.new_mode('mode0_right') m1.set_dynamics([[0, 0, 2], [0, 0, 0], [0, 0, 0]]) m1.set_invariant([[1, 0, 0]], [3.5]) # x <= 3.5 # y' == 2 m2 = ha.new_mode('mode1_up') m2.set_dynamics([[0, 0, 0], [0, 0, 2], [0, 0, 0]]) m2.set_invariant([0., 1., 0], [3.5]) # y <= 3.5 # x' == 2 m3 = ha.new_mode('mode2_right') m3.set_dynamics([[0, 0, 2], [0, 0, -0], [0, 0, 0]]) m3.set_invariant([1., 0, 0], [7]) # x <= 7 t = ha.new_transition(m1, m2) t.set_guard_true() t = ha.new_transition(m2, m3) t.set_guard_true() error = ha.new_mode('error') t = ha.new_transition(m3, error) unsafe_rhs = [ -unsafe_box[0][0], unsafe_box[0][1], -unsafe_box[1][0], unsafe_box[1][1] ] # x >= 1.1 x <= 1.9, y >= 2.7, y <= 4.3 t.set_guard([[-1, 0, 0], [1, 0, 0], [0, -1, 0], [0, 1, 0]], unsafe_rhs) t = ha.new_transition(m2, error) # x >= 1.1 x <= 1.9, y >= 2.7, y <= 4.3 t.set_guard([[-1, 0, 0], [1, 0, 0], [0, -1, 0], [0, 1, 0]], unsafe_rhs) return ha
def test_agg_no_counterexample(): 'test that aggregation to error does not create a counterexample' # m1 dynamics: x' == 1, y' == 0, x0, y0: [0, 1], step: 1.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_DEBUG settings.plot.plot_mode = PlotSettings.PLOT_NONE result = Core(ha, settings).run(init_list) assert not result.counterexample
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 make_automaton(): 'make the hybrid automaton' ha = HybridAutomaton() mode = ha.new_mode('mode') dynamics = loadmat('iss.mat') a_matrix = dynamics['A'] b_matrix = dynamics['B'] mode.set_dynamics(a_matrix) # input bounds # 0 <= u1 <= 0.1 # 0.8 <= u2 <= 1.0 # 0.9 <= u3 <= 1.0 bounds_mat = [[1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]] bounds_rhs = [0.1, 0, 1.0, -0.8, 1.0, -0.9] mode.set_inputs(b_matrix, bounds_mat, bounds_rhs) error = ha.new_mode('error') # the third output defines the unsafe condition y3 = dynamics['C'][2] limit = 0.0005 #limit = 0.0007 # Error condition: y3 * x <= -limit OR y3 >= limit trans1 = ha.new_transition(mode, error) trans1.set_guard(y3, [-limit]) trans2 = ha.new_transition(mode, error) trans2.set_guard(-1 * y3, [-limit]) return ha
def make_automaton(): 'make the hybrid automaton' ha = HybridAutomaton() # mode one: x' = 2, y' = 1, a' = 0 m1 = ha.new_mode('m1') m1.set_dynamics([[0, 0, 2], [0, 0, 1], [0, 0, 0]]) # mode two: x' = 1, y' = 1, a' = 0 m2 = ha.new_mode('m2') m2.set_dynamics([[0, 0, 1], [0, 0, 1], [0, 0, 0]]) # invariant: x <= 9.9 m1.set_invariant([[1, 0, 0]], [9.9]) # guard: x >= 9.9 trans = ha.new_transition(m1, m2, 'transition_name') trans.set_guard([[-1, 0, 0]], [-9.9]) # Assign the reset to the transition: # # def set_reset(self, reset_csr=None, reset_minkowski_csr=None, reset_minkowski_constraints_csr=None, # reset_minkowski_constraints_rhs=None): # '''resets are of the form x' = Rx + My, Cy <= rhs, where y are fresh variables # the reset_minowski variables can be None if no new variables are needed. If unassigned, the identity # reset is assumed # # x' are the new variables # x are the old variables # reset_csr is R # reset_minkowski_csr is M # reset_minkowski_constraints_csr is C # reset_minkowski_constraints_rhs is rhs # ''' # we want the reset to set x' := [0, 1], y' := y - 10 reset_csr = [[0, 0, 0], [0, 1, 0], [0, 0, 1]] # two new minkowski variables, y0 = [0, 1], y1 = [-10, -10] minkowski_csr = [[1, 0], [0, 1], [0, 0]] constraints_csr = [[1, 0], [-1, 0], [0, 1], [0, -1]] constraints_rhs = [1, 0, -10, 10] trans.set_reset(reset_csr, minkowski_csr, constraints_csr, constraints_rhs) return ha
def test_ha(): 'test for the harmonic oscillator example with line initial set (from ARCH 2018 paper)' ha = HybridAutomaton() # with time and affine variable mode = ha.new_mode('mode') mode.set_dynamics([[0, 1, 0, 0], [-1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 0]]) error = ha.new_mode('error') trans1 = ha.new_transition(mode, error) trans1.set_guard([[1., 0, 0, 0], [-1., 0, 0, 0]], [4.0, -4.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, 2*math.pi) settings.stdout = HylaaSettings.STDOUT_VERBOSE settings.plot.store_plot_result = True settings.plot.plot_mode = PlotSettings.PLOT_NONE core = Core(ha, settings) result = core.run(init_list) assert result.has_concrete_error ce = result.counterexample[0] # [-5.0, 0.6568542494923828, 0.0, 1.0] -> [4.0, 3.0710678118654737, 2.356194490192345, 1.0] assert ce.mode == mode assert np.allclose(ce.start, np.array([-5, 0.65685, 0, 1], dtype=float)) assert np.allclose(ce.end, np.array([4, 3.07106, 2.35619, 1], dtype=float)) # check the reachable state (should always have x <= 3.5) obj_list = result.plot_data.mode_to_obj_list[0][mode.name] for obj in obj_list: verts = obj[0] for vert in verts: x, _ = vert assert x <= 4.9
def test_over_time_range(): 'test plotting over time with aggergation (time range)' ha = HybridAutomaton() mode_a = ha.new_mode('A') mode_b = ha.new_mode('B') # dynamics: x' = a, a' = 0 mode_a.set_dynamics([[0, 1], [0, 0]]) mode_b.set_dynamics([[0, 1], [0, 0]]) # invariant: x <= 2.5 mode_a.set_invariant([[1, 0]], [2.5]) trans1 = ha.new_transition(mode_a, mode_b, 'first') trans1.set_guard_true() # initial set has x0 = [0, 0] init_lpi = lputil.from_box([(0, 0), (1, 1)], mode_a) init_list = [StateSet(init_lpi, mode_a)] # settings, step size = 1.0 settings = HylaaSettings(1.0, 4.0) settings.stdout = HylaaSettings.STDOUT_DEBUG settings.process_urgent_guards = True settings.plot.plot_mode = PlotSettings.PLOT_NONE settings.plot.store_plot_result = True settings.plot.xdim_dir = None settings.plot.ydim_dir = 0 result = Core(ha, settings).run(init_list) polys = [obj[0] for obj in result.plot_data.mode_to_obj_list[0][mode_b.name]] # expected with aggegregation: [0, 2.5] -> [1, 3.5] -> [2, 4.5] -> [3, 5.5] -> [4, 6.5] # 4 steps because invariant is allowed to be false for the final step assert len(polys) == 5, "expected invariant to become false after 5 steps" for i in range(5): assert_verts_is_box(polys[i], [[i, i + 3.0], [i, i + 3.0]])
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 define_ha(): '''make the hybrid automaton and return it''' ha = HybridAutomaton('Drivetrain') # dynamics variable order: [x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, t, affine] negAngle = ha.new_mode('negAngle') a_matrix = [ \ [0, 0, 0, 0, 0, 0, 0.0833333333333333, 0, -1, 0, 0, 0, 0], \ [13828.8888888889, -26.6666666666667, 60, 60, 0, 0, -5, -60, 0, 0, 0, 0, 716.666666666667], \ [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5], \ [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], \ [0, 0, 0, 0, -714.285714285714, -0.04, 0, 0, 0, 714.285714285714, 0, 0, 0], \ [-2777.77777777778, 3.33333333333333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -83.3333333333333], \ [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], \ [100, 0, 0, 0, 0, 0, 0, -1000, -0.01, 1000, 0, 0, 3], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], \ [0, 0, 0, 0, 1000, 0, 0, 1000, 0, -2000, -0.01, 0, 0], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ ] negAngle.set_dynamics(a_matrix) # x1 <= -0.03 negAngle.set_invariant([ [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ], [ -0.03, ]) deadzone = ha.new_mode('deadzone') a_matrix = [ \ [0, 0, 0, 0, 0, 0, 0.0833333333333333, 0, -1, 0, 0, 0, 0], \ [-60, -26.6666666666667, 60, 60, 0, 0, -5, -60, 0, 0, 0, 0, 300], \ [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5], \ [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], \ [0, 0, 0, 0, -714.285714285714, -0.04, 0, 0, 0, 714.285714285714, 0, 0, 0], \ [0, 3.33333333333333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], \ [0, 0, 0, 0, 0, 0, 0, -1000, -0.01, 1000, 0, 0, 0], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], \ [0, 0, 0, 0, 1000, 0, 0, 1000, 0, -2000, -0.01, 0, 0], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ ] deadzone.set_dynamics(a_matrix) # -0.03 <= x1 & x1 <= 0.03 deadzone.set_invariant([ [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ], [ 0.03, 0.03, ]) posAngle = ha.new_mode('posAngle') a_matrix = [ \ [0, 0, 0, 0, 0, 0, 0.0833333333333333, 0, -1, 0, 0, 0, 0], \ [13828.8888888889, -26.6666666666667, 60, 60, 0, 0, -5, -60, 0, 0, 0, 0, -116.666666666667], \ [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5], \ [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], \ [0, 0, 0, 0, -714.285714285714, -0.04, 0, 0, 0, 714.285714285714, 0, 0, 0], \ [-2777.77777777778, 3.33333333333333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83.3333333333333], \ [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], \ [100, 0, 0, 0, 0, 0, 0, -1000, -0.01, 1000, 0, 0, -3], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], \ [0, 0, 0, 0, 1000, 0, 0, 1000, 0, -2000, -0.01, 0, 0], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ ] posAngle.set_dynamics(a_matrix) # 0.03 <= x1 posAngle.set_invariant([ [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ], [ -0.03, ]) negAngleInit = ha.new_mode('negAngleInit') a_matrix = [ \ [0, 0, 0, 0, 0, 0, 0.0833333333333333, 0, -1, 0, 0, 0, 0], \ [13828.8888888889, -26.6666666666667, 60, 60, 0, 0, -5, -60, 0, 0, 0, 0, 116.666666666667], \ [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -5], \ [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], \ [0, 0, 0, 0, -714.285714285714, -0.04, 0, 0, 0, 714.285714285714, 0, 0, 0], \ [-2777.77777777778, 3.33333333333333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -83.3333333333333], \ [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], \ [100, 0, 0, 0, 0, 0, 0, -1000, -0.01, 1000, 0, 0, 3], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], \ [0, 0, 0, 0, 1000, 0, 0, 1000, 0, -2000, -0.01, 0, 0], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], \ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \ ] negAngleInit.set_dynamics(a_matrix) # t <= 0.2 negAngleInit.set_invariant([ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], ], [ 0.2, ]) error = ha.new_mode('error') trans = ha.new_transition(negAngleInit, negAngle) # t >= 0.2 trans.set_guard([ [-0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -0], ], [ -0.2, ]) trans = ha.new_transition(negAngle, deadzone) # x1 >= -0.03 trans.set_guard([ [-1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0], ], [ 0.03, ]) trans = ha.new_transition(deadzone, posAngle) # x1 >= 0.03 trans.set_guard([ [-1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0], ], [ -0.03, ]) trans = ha.new_transition(deadzone, error) # x1 <= -0.03 trans.set_guard([ [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ], [ -0.03, ]) trans = ha.new_transition(posAngle, error) # x1 <= 0.03 trans.set_guard([ [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ], [ 0.03, ]) return ha
def make_automaton(theta_deg, maxi=20): 'make the hybrid automaton' ha = HybridAutomaton('Gearbox') # variables variables = ["px", "py", "vx", "vy", "I"] derivatives = ["vx", "vy", "Fs/ms", "-Rs*Tf/Jg2", "0"] constant_dict = { "zeta": 0.9, "ms": 3.4, "mg2": 18.1, "Jg2": 0.7, "Rs": 0.08, "theta": theta_deg * math.pi / 180, #0.628318530717959, "deltap": -0.003, "Fs": 70, "Tf": 1 } ############## Modes ############## move_free = ha.new_mode('move_free') # a_mat = symbolic.make_dynamics_mat(variables, derivatives, constant_dict, has_affine_variable=True) move_free.set_dynamics(a_mat) invariant = f"px<=deltap & py<=-px*0.726542528005361 & py>=px*0.726542528005361 & I <= {maxi}" mat, rhs = symbolic.make_condition(variables, invariant.split('&'), constant_dict, has_affine_variable=True) move_free.set_invariant(mat, rhs) meshed = ha.new_mode('meshed') a_mat = np.zeros((6, 6)) meshed.set_dynamics(a_mat) # error mode error = ha.new_mode('error') ############## Cyclic Transitions ############## # t1 t1 = ha.new_transition(move_free, move_free, "t1") t1_guard = "py>=-px*0.726542528005361 & vx*0.587785252292473+vy*0.809016994374947>=0 & px <= deltap" mat, rhs = symbolic.make_condition(variables, t1_guard.split('&'), constant_dict, has_affine_variable=True) t1.set_guard(mat, rhs) # projection of a point onto a plane # if <x, y> is the point and <nx, ny> is the normal to the plane with mag 1 # proj = <x, y> - (<x, y> dot <nx, ny>) * <nx, ny> nx = math.cos(math.pi / 2 - constant_dict['theta']) ny = math.sin(math.pi / 2 - constant_dict['theta']) i_reset = "I+(vx*0.587785252292473+vy*0.809016994374947)*(zeta+1)*ms*mg2/(ms*(0.809016994374947*0.809016994374947)+mg2*(0.587785252292473*0.587785252292473))" resets = [ f"px - (px*{nx} + py*{ny}) * {nx}", f"py - (px*{nx} + py*{ny}) * {ny}", "(vx*(ms*0.809016994374947*0.809016994374947-mg2*zeta*0.587785252292473*0.587785252292473)+vy*(-(zeta+1)*mg2*0.587785252292473*0.809016994374947))/(ms*(0.809016994374947*0.809016994374947)+mg2*(0.587785252292473*0.587785252292473))", "(vx*(-(zeta+1)*ms*0.587785252292473*0.809016994374947)+vy*(mg2*0.587785252292473*0.587785252292473-ms*zeta*0.809016994374947*0.809016994374947))/(ms*(0.809016994374947*0.809016994374947)+mg2*(0.587785252292473*0.587785252292473))", i_reset ] reset_mat = symbolic.make_reset_mat(variables, resets, constant_dict, has_affine_variable=True) t1.set_reset(reset_mat) # T1 to error mode t1_to_error = ha.new_transition(move_free, error, "t1_to_error") guard = f"{t1_guard} & {i_reset}>={maxi}" mat, rhs = symbolic.make_condition(variables, guard.split('&'), constant_dict, has_affine_variable=True) t1_to_error.set_guard(mat, rhs) t1_to_error.set_reset(reset_mat) # t2 t2 = ha.new_transition(move_free, move_free, "t2") t2_guard = "py<=px*0.726542528005361 & vx*0.587785252292473-vy*0.809016994374947>=0 & px <= deltap" mat, rhs = symbolic.make_condition(variables, t2_guard.split('&'), constant_dict, has_affine_variable=True) t2.set_guard(mat, rhs) ny = -ny i_reset = "I+(vx*0.587785252292473-vy*0.809016994374947)*(zeta+1)*ms*mg2/(ms*(0.809016994374947*0.809016994374947)+mg2*(0.587785252292473*0.587785252292473))" resets = [ f"px - (px*{nx} + py*{ny}) * {nx}", f"py - (px*{nx} + py*{ny}) * {ny}", "(vx*(ms*0.809016994374947*0.809016994374947-mg2*zeta*0.587785252292473*0.587785252292473)+vy*((zeta+1)*mg2*0.587785252292473*0.809016994374947))/(ms*(0.809016994374947*0.809016994374947)+mg2*(0.587785252292473*0.587785252292473))", "(vx*((zeta+1)*ms*0.587785252292473*0.809016994374947)+vy*(mg2*0.587785252292473*0.587785252292473-ms*zeta*0.809016994374947*0.809016994374947))/(ms*(0.809016994374947*0.809016994374947)+mg2*(0.587785252292473*0.587785252292473))", i_reset ] reset_mat = symbolic.make_reset_mat(variables, resets, constant_dict, has_affine_variable=True) t2.set_reset(reset_mat) # T2 to error mode t2_to_error = ha.new_transition(move_free, error, "t2_to_error") guard = f"{t2_guard} & {i_reset}>={maxi}" mat, rhs = symbolic.make_condition(variables, guard.split('&'), constant_dict, has_affine_variable=True) t2_to_error.set_guard(mat, rhs) t2_to_error.set_reset(reset_mat) #### transitions to meshed guards = [ "px >= deltap & vx >= 0 & vy >= 0", "px >= deltap & vx >= 0 & vy <= 0", "px >= deltap & vx <= 0 & vy >= 0", "px >= deltap & vx <= 0 & vy <= 0" ] i_resets = [ "I+ms*vx+ms*vy", "I+ms*vx-ms*vy", "I-ms*vx+ms*vy", "I-ms*vx-ms*vy" ] for i, (guard, i_reset) in enumerate(zip(guards, i_resets)): t3 = ha.new_transition(move_free, meshed, f"meshed_{i}") t3_guard = guard + f" & I <= {maxi}" mat, rhs = symbolic.make_condition(variables, t3_guard.split('&'), constant_dict, has_affine_variable=True) t3.set_guard(mat, rhs) resets = [f"px", f"py", "0", "0", i_reset] reset_mat = symbolic.make_reset_mat(variables, resets, constant_dict, has_affine_variable=True) t3.set_reset(reset_mat) # T3 to error mode t3_to_error = ha.new_transition(move_free, error, f"t3_to_error_{i}") t3_err_guard = guard + f" & I >= {maxi}" mat, rhs = symbolic.make_condition(variables, t3_err_guard.split('&'), constant_dict, has_affine_variable=True) t3_to_error.set_guard(mat, rhs) return ha
def test_plain(): 'test plain aggregation of states across discrete transitions' # m1 dynamics: x' == 1, y' == 0, x0, y0: [0, 1], step: 1.0 # m1 invariant: x <= 3 # m1 -> m2 guard: True # m2 dynamics: x' == 0, y' == 1 # time bound: 4 # excepted final states to be: x: [0, 4], y: [4,5] # x is [1, 4] because no transitions are allowed at step 0 (simulation-equiv semantics) and a transition is # allowed one step after the invariant becomes false # y is [4,5] because after aggregation, the time elapsed for the aggregated set will be 0.0, the minimum 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(csr_matrix((0, 0)), []) # 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, 4.0) settings.stdout = HylaaSettings.STDOUT_DEBUG settings.plot.plot_mode = PlotSettings.PLOT_NONE settings.plot.store_plot_result = True core = Core(ha, settings) result = core.run(init_list) # check history state = result.last_cur_state assert state.mode == m2 assert len(state.aggdag_op_list) > 1 op0 = state.aggdag_op_list[0] op1 = state.aggdag_op_list[1] assert isinstance(op0, OpTransition) assert len(core.aggdag.roots) == 1 assert op0.child_node.stateset.mode is m2 assert op0.transition == trans1 assert op0.parent_node == core.aggdag.roots[0] assert isinstance(op0.poststate, StateSet) assert op0.step == 1 assert isinstance(op0.child_node, AggDagNode) assert op0.child_node == op1.child_node assert op0.child_node not in core.aggdag.roots assert len(op0.parent_node.stateset.aggdag_op_list) == 1 assert op0.parent_node.stateset.aggdag_op_list[0] is None # check polygons in m2 polys2 = [obj[0] for obj in result.plot_data.mode_to_obj_list[0]['m2']] assert 4 <= len(polys2) <= 5 assert_verts_is_box(polys2[0], [[1, 4], [0, 1]]) assert_verts_is_box(polys2[1], [[1, 4], [1, 2]]) assert_verts_is_box(polys2[2], [[1, 4], [2, 3]]) assert_verts_is_box(polys2[3], [[1, 4], [3, 4]])
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
def test_tt_09(): 'test time-triggered transition at 0.9 bug' # this test is from an issue reported by Mojtaba Zarei tt_time = 0.9 ha = HybridAutomaton() # the test seems to be sensitive to the a_matrix... my guess is the LP is barely feasible at the tt_time a_matrix = np.array( [[6.037291088, -4.007840286, 2.870370645, 43.12729646, 10.06751155, 23.26084098, -0.001965587832, 0, 0], [3.896645707, -0.03417905392, -9.564966476, 15.25894014, -21.57196438, 16.60548055, 0.03473846441, 0, 0], [22.72995871, 14.12055097, -0.9315267908, 136.9851951, -71.66383111, 109.7143863, 0.1169799769, 0, 0], [-38.16694597, 3.349061908, -9.10171149, -185.1866526, 9.210877185, -165.8086527, -0.06858712649, 0, 0], [46.78596597, 27.7996521, 17.18120319, 285.4632424, -135.289626, 235.9427441, 0.228154713, 0, 0], [-8.31135303, 3.243945466, -4.523811735, -39.26067436, -9.385678542, -36.63193931, -0.0008874747046, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=float) mode1 = ha.new_mode('mode') mode1.set_dynamics(a_matrix) # time-triggered invariant: t <= tt_time mat = np.array([[0, 0, 0, 0, 0, 0, 0, 1, 0]], dtype=float) rhs = [tt_time] mode1.set_invariant(mat, rhs) mode2 = ha.new_mode('mode2') mode2.set_dynamics(a_matrix) # transition, guard: x >= -2 & y > 4 & t >= tt_time # transition, guard: t >= 0.9 mat = np.array([[0, 0, 0, 0, 0, 0, 0, -1, 0]], dtype=float) rhs = [-tt_time] t = ha.new_transition(mode1, mode2) t.set_guard(mat, rhs) # initial set init_box = np.array([[-0.1584, -0.1000], [-0.0124, 0.0698], [-0.3128, 0.0434], [-0.0208, 0.0998], [-0.4895, 0.1964], [-0.0027, 0.0262], [42.40, 42.5], [0, 0], # t(0) = 0 [1, 1]]) # affine(0) = 1 init_lpi = lputil.from_box(init_box, mode1) init_list = [StateSet(init_lpi, mode1)] # settings settings = HylaaSettings(0.05, 1.0) settings.stdout = HylaaSettings.STDOUT_DEBUG settings.plot.store_plot_result = True settings.plot.plot_mode = PlotSettings.PLOT_NONE #INTERACTIVE #settings.plot.xdim_dir = 7 #None #settings.plot.ydim_dir = 0 core = Core(ha, settings) result = core.run(init_list) mode2_list = result.plot_data.mode_to_obj_list[0]['mode2'] assert len(mode2_list) == 3, f"mode2_list len was {len(mode2_list)}, expected 3 (0.9, 0.95, 1.0)"
class F1Hylaa: def __init__(self): # ------------------ Semi-Static Configuration ---------------------- self.settings = None #hylaa settings object self.dt = None #dt from MPC metdata self.ttime = None self.input_uncertainty = [] self.state_uncertainty = [] self.model = None #dynamics abstraction object #TODO automatic or otherwise validated variability # ------------------ Per-run Configuration ---------------------- self.ha = None self.predictions = None #predictions from MPC 3d array : [ dt[ state[x,y,psi], inputs[d,v] ] ] self.modeList = [] self.initialState = None def set_model_params(self, state_uncertainty, input_uncertainty, dynamics_module="kinematics_model"): model_gen = importlib.import_module(dynamics_module) self.modeList = [] self.state_uncertainty = state_uncertainty self.input_uncertainty = input_uncertainty self.model = model_gen.getModel() self.model.setInputUncertainty(input_uncertainty) return 1 def run_hylaa(self, predictions): self.ha = None self.predictions = None self.modeList = [] self.initialState = None self.ha = HybridAutomaton() self.predictions = predictions self.graphPredictions() self.make_automaton() initialBox = self.make_init(self.predictions[0][0]) core = Core(self.ha, self.settings) profile.runctx( 'resultprof = self.run_hylaa_profile(initialBox, core)', globals(), locals(), filename= "/home/nvidia/f1racing/f110_ws/src/ppcm/reachability/scripts/profiler/prof/out_tmp.prof" ) result = locals()['resultprof'] #result = core.run(initialBox) reachsets = [ result.plot_data.get_verts_list(mode)[0] for mode in self.modeList ] return reachsets def run_hylaa_profile(self, initialBox, core): return core.run(initialBox) def make_settings(self, dt, total, displayType, verbosity, fileName="hylaa_reach.png"): #rospy.loginfo("SETTINGS CREATED WITH dt={}, total={}".format(dt, total)) self.dt = dt self.ttime = total 'make the reachability settings object' # see hylaa.settings for a list of reachability settings settings = HylaaSettings(dt, total) settings.optimize_tt_transitions = True settings.plot.filename = fileName #wwe want to store the reach set cuz that is the whole point of this settings.plot.store_plot_result = True settings.plot.plot_mode = PlotSettings.PLOT_IMAGE settings.stdout = HylaaSettings.STDOUT_VERBOSE if displayType == "NONE": settings.plot.plot_mode = PlotSettings.PLOT_NONE elif displayType == "IMAGE": settings.plot.plot_mode = PlotSettings.PLOT_IMAGE elif displayType == "VIDEO": settings.plot.plot_mode = PlotSettings.PLOT_VIDEO else: #rospy.logwarn("Invalid hylaa plot type specified, default=NONE used") settings.plot.plot_mode = PlotSettings.PLOT_NONE #rospy.loginfo("Using HYLAA setting: plot_mode = {}".format(displayType)) if verbosity == "NONE": settings.stdout = HylaaSettings.STDOUT_NONE elif verbosity == "VERBOSE": settings.stdout = HylaaSettings.STDOUT_VERBOSE else: #rospy.logwarn("WARNING: Invalid hylaa output verbosity specified, default=VERBOSE used") settings.stdout = HylaaSettings.STDOUT_VERBOSE #rospy.loginfo("Using HYLAA setting: stdout = {}".format(verbosity)) if displayType != "NONE": settings.plot.xdim_dir = [0, 6] settings.plot.ydim_dir = [1, 2] settings.plot.label = [LabelSettings(), LabelSettings()] xyplot = settings.plot.label[0] ytplot = settings.plot.label[1] #xy plot xyplot.big(size=26) xyplot.title = "Linearized Kinematics" xyplot.x_label = "X Position" xyplot.y_label = "Y Position" #yaw time plot ytplot.big(size=26) ytplot.title = "Yaw Vs Time" ytplot.x_label = "Time" ytplot.y_label = "Yaw" #plot the predicted trajectory (MPC output) nl_Circles = None self.settings = settings return 1 def graphPredictions(self): if True: #self.graph_predictions: nl_Centers = [(state[0], state[1]) for state, _ in self.predictions] nl_Patches = [Ellipse(center, .2, .2) for center in nl_Centers] nl_Circles = collections.PatchCollection(nl_Patches, facecolors='red', edgecolors='black', zorder=1000) nl_yaw_centers = [(self.dt * pidx, self.predictions[pidx][0][2]) for pidx in range(len(self.predictions))] nl_yaw_patches = [ Ellipse(center, self.dt / 8, 3.14 / 32) for center in nl_yaw_centers ] nl_yaw_circles = collections.PatchCollection(nl_yaw_patches, facecolors='red', edgecolors='black', zorder=-1) self.settings.plot.extra_collections = [[nl_Circles], [nl_yaw_circles]] def make_init(self, initialState): 'make the initial states' # initial set has every variable as [-0.1, 0.1] mode = self.ha.modes['m0'] dims = mode.a_csr.shape[0] without_time_dim = dims - 2 pure_state_dim = without_time_dim // 2 init_box = [(0, 0)] * (pure_state_dim) lin_box = [(1, 1)] * (pure_state_dim) time_init = [(0.0, 0.0), (1.0, 1.0)] #initial state = state uncertainty + linear var uncertainty + time uncertainty(0) #we assume the uncertainty is symettrical for x, y, yaw for s in range(len(init_box)): init_box[s] = (initialState[s] - self.state_uncertainty[s], initialState[s] + self.state_uncertainty[s]) init_box += lin_box + time_init #print(init_box) init_lpi = lputil.from_box(init_box, mode) init_list = [StateSet(init_lpi, mode)] return init_list #generated modes IN PLACE on given ha, filling with given inputs def make_automaton(self): modeIncrement = 0 lastMode = None for state, inputs in self.predictions[:int(self.ttime / self.dt)]: #get the dynamics linearized around this state/input set dynamics = self.model.linearized_dynamics(state[:3], inputs, self.dt) modeName = 'm{}'.format(modeIncrement) mode = self.ha.new_mode(modeName) self.modeList.append(modeName) a_matrix = dynamics['A'] b_matrix = dynamics['B'] mode.set_dynamics(a_matrix) bounds_mat = dynamics["bounds_mat"] bounds_rhs = dynamics["bounds_rhs"] mode.set_inputs(b_matrix, bounds_mat, bounds_rhs, allow_constants=True) offsetTime = 1e-4 criticalTime = self.dt * modeIncrement #using time offset frome rendevous example invariant_mat = dynamics["invariant_mat"] #TODO add configurable ciritical time variance invariant_rhs = [criticalTime + offsetTime] mode.set_invariant(invariant_mat, invariant_rhs) if lastMode: t = self.ha.new_transition(lastMode, mode) guard_mat = dynamics["guard_mat"] guard_rhs = [-criticalTime + offsetTime ] #time from last state is bound t.set_guard(guard_mat, guard_rhs) lastMode = mode modeIncrement += 1
def test_tt_split(): 'tests time-triggered dynamics where the state is split (based on state) after some amount of time elapses' ha = HybridAutomaton() # dynamics variable order: [x0, x1, x2, x3, u, t, affine] pole = ha.new_mode('pole') a_matrix = [ \ [0, 1, 0, 0, 0, 0, 0], \ [0, 0, 0.7164, 0, 0.9755, 0, 0], \ [0, 0, 0, 1, 0, 0, 0], \ [0, 0, 0.76, 0, 0.46, 0, 0], \ [0, 0, 0, 0, 0, 0, 0], \ [0, 0, 0, 0, 0, 0, 1], \ [0, 0, 0, 0, 0, 0, 0], \ ] pole.set_dynamics(a_matrix) # 0.0 <= t & t <= 0.1 pole.set_invariant([[0, 0, 0, 0, 0, -1, 0], [0, 0, 0, 0, 0, 1, 0], ], [0, 0.1, ]) trans = ha.new_transition(pole, pole, 'b2') # x3 <= 1.0229164510965 & x2 <= 2.0244571492076 & x3 > -10.0172335505486 & x2 <= 1.0329331979156 & t >= 0.1 trans.set_guard([[0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0], [-0, -0, -0, -1, -0, -0, -0], [0, 0, 1, 0, 0, 0, 0], [-0, -0, -0, -0, -0, -1, -0], ], [1.0229164510965, 2.0244571492076, 10.0172335505486, 1.0329331979156, -0.1, ]) # Reset: # t := 0.0 # u := 0.2 reset_mat = [ \ [1, 0, 0, 0, 0, 0, 0, ], \ [0, 1, 0, 0, 0, 0, 0, ], \ [0, 0, 1, 0, 0, 0, 0, ], \ [0, 0, 0, 1, 0, 0, 0, ], \ [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, ], \ [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, ], \ [0, 0, 0, 0, 0, 0, 1, ], \ ] reset_minkowski = [ \ [0, ], \ [0, ], \ [0, ], \ [0, ], \ [1, ], \ [0, ], \ [0, ], \ ] minkowski_constraints = [ \ [1, ], \ [-1, ], \ ] minkowski_rhs = [0.2, -0.2] trans.set_reset(reset_mat, reset_minkowski, minkowski_constraints, minkowski_rhs) # manually run ha.detect_tt_transitions() and check the result def print_none(str): 'suppress printing' pass settings = HylaaSettings(0.05, 0.15) settings.plot.plot_mode = PlotSettings.PLOT_IMAGE settings.plot.xdim_dir = 0 settings.plot.ydim_dir = 3 settings.stdout = HylaaSettings.STDOUT_DEBUG init_list = [] mode = ha.modes['pole'] mat = [[1, 0, 0, 0, 0, 0, 0], \ [-1, -0, -0, -0, -0, -0, -0], \ [0, 1, 0, 0, 0, 0, 0], \ [-0, -1, -0, -0, -0, -0, -0], \ [0, 0, 1, 0, 0, 0, 0], \ [-0, -0, -1, -0, -0, -0, -0], \ [0, 0, 0, -1, 0, 0, 0], \ [0, 0, 0, 1, 0, 0, 0], \ [0, 0, 0, 0, 1, 0, 0], \ [-0, -0, -0, -0, -1, -0, -0], \ [0, 0, 0, 0, 0, 1, 0], \ [-0, -0, -0, -0, -0, -1, -0], \ [0, 0, 0, 0, 0, 0, 1], \ [-0, -0, -0, -0, -0, -0, -1], ] rhs = [0, -0, 0, -0, 0, -0, 1.3, -1.3, 0, -0, 0, -0, 1, -1, ] init_list.append(StateSet(lputil.from_constraints(mat, rhs, mode), mode)) core = Core(ha, settings) result = core.run(init_list) # expect no exception during running assert result.last_cur_state.cur_steps_since_start[0] == 3 assert result.last_cur_state.cur_steps_since_start[1] == 3
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 make_automaton(abortmin, abortmax): 'make the hybrid automaton' safe = True ha = HybridAutomaton('Spacecraft Rendezvous with Abort') rad = 5.0 passive_min_time = abortmin passive_max_time = abortmax ############## Modes ############## p2 = ha.new_mode('Far') a_mat = [\ [0.0, 0.0, 1.0, 0.0, 0.0, 0], \ [0.0, 0.0, 0.0, 1.0, 0.0, 0], \ [-0.057599765881773, 0.000200959896519766, -2.89995083970656, 0.00877200894463775, 0.0, 0], \ [-0.000174031357370456, -0.0665123984901026, -0.00875351105536225, -2.90300269286856, 0.0, 0.0], \ [0.0, 0.0, 0.0, 0.0, 0.0, 1.0], \ [0, 0, 0, 0, 0, 0]] inv_mat = [[0.0, 0.0, 0.0, 0.0, 1.0, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0]] inv_rhs = [125.0, -100.0] p2.set_dynamics(a_mat) p2.set_invariant(inv_mat, inv_rhs) p3 = ha.new_mode('Approaching') a_mat = [\ [0.0, 0.0, 1.0, 0.0, 0.0, 0], \ [0.0, 0.0, 0.0, 1.0, 0.0, 0], \ [-0.575999943070835, 0.000262486079431672, -19.2299795908647, 0.00876275931760007, 0.0, 0], \ [-0.000262486080737868, -0.575999940191886, -0.00876276068239993, -19.2299765959399, 0.0, 0], \ [0.0, 0.0, 0.0, 0.0, 0.0, 1.],\ [0, 0, 0, 0, 0, 0]] inv_mat = [\ [-1.0, 0., 0., 0., 0., 0], \ [1.0, 0., 0., 0., 0., 0], \ [0., -1.0, 0., 0., 0., 0], \ [0., 1.0, 0., 0., 0., 0], \ [-1.0, -1.0, 0.0, 0.0, 0.0, 0], \ [1.0, 1.0, 0.0, 0.0, 0.0, 0], \ [-1.0, 1.0, 0., 0., 0., 0], \ [1.0, -1.0, 0., 0., 0., 0], \ [0., 0., 0., 0., 1., 0]] inv_rhs = [ 100, 100, 100, 100, 141.1, 141.1, 141.1, 141.1, passive_max_time ] p3.set_dynamics(a_mat) p3.set_invariant(inv_mat, inv_rhs) passive = ha.new_mode('Abort') a_mat = [\ [0, 0, 1, 0, 0, 0], \ [0, 0, 0, 1, 0, 0], \ [0.0000575894721132000, 0, 0, 0.00876276, 0, 0], \ [0, 0, -0.00876276, 0, 0, 0], \ [0, 0, 0, 0, 0, 1.], \ [0, 0, 0, 0, 0, 0]] passive.set_dynamics(a_mat) error = ha.new_mode('Error') ############## Normal Transitions ############## t1 = ha.new_transition(p2, p3) guard_mat = [\ [-1.0, 0., 0., 0., 0., 0], \ [1.0, 0., 0., 0., 0., 0], \ [0., -1.0, 0., 0., 0., 0], \ [0., 1.0, 0., 0., 0., 0], \ [-1.0, -1.0, 0.0, 0.0, 0.0, 0], \ [1.0, 1.0, 0.0, 0.0, 0.0, 0], \ [-1.0, 1.0, 0., 0., 0., 0], \ [1.0, -1.0, 0., 0., 0., 0]] guard_rhs = [100, 100, 100, 100, 141.1, 141.1, 141.1, 141.1] t1.set_guard(guard_mat, guard_rhs) ha.new_transition(p2, passive).set_guard([[0.0, 0.0, 0.0, 0.0, -1.0, 0]], [-passive_min_time]) ha.new_transition(p3, passive).set_guard([[0.0, 0.0, 0.0, 0.0, -1.0, 0]], [-passive_min_time]) ############## Error Transitions ############## # In the aborting mode, the vehicle must avoid the target, which is modeled as a box B with # 0.2m edge length and the center placed as the origin # unsafe rad: 1.0 #rad = 1.0 t = ha.new_transition(passive, error) guard_mat = [ \ [1, 0, 0., 0., 0., 0], \ [-1, 0, 0., 0., 0., 0], \ [0, 1., 0., 0., 0., 0], \ [0, -1., 0., 0., 0., 0]] guard_rhs = [rad, rad, rad, rad] t.set_guard(guard_mat, guard_rhs) #In the rendezvous attempt the spacecraft must remain within the lineof-sight #cone L = {[x, y]^T | (x >= -100m) AND (y >= x*tan(30)) AND (-y >= x*tan(30))} #ha.new_transition(p3, error).set_guard([[1, 0, 0., 0., 0., 0]], [-100]) #ha.new_transition(p3, error).set_guard([[-0.57735, 1, 0, 0., 0., 0]], [0]) #ha.new_transition(p3, error).set_guard([[-0.57735, -1, 0., 0., 0., 0]], [0]) # sqrt(vx^2 + vy^2) should stay below 0.055 m/SECOND (time in model is in MINUTES) # to make the model unsafe, try changing this to 0.05 meters_per_sec_limit = 0.055 if safe else 0.05 meters_per_min_limit = meters_per_sec_limit * 60 h = meters_per_min_limit * math.cos(math.pi / 8.0) w = meters_per_min_limit * math.sin(math.pi / 8.0) #ha.new_transition(p3, error).set_guard([[0, 0, 1., 0., 0., 0]], [-h]) #ha.new_transition(p3, error).set_guard([[0, 0, -1., 0., 0., 0]], [-h]) #ha.new_transition(p3, error).set_guard([[0, 0, 0., 1., 0., 0]], [-h]) #ha.new_transition(p3, error).set_guard([[0, 0, 0., -1., 0., 0]], [-h]) #ha.new_transition(p3, error).set_guard([[0, 0, 1., 1., 0., 0]], [-(w + h)]) #ha.new_transition(p3, error).set_guard([[0, 0, -1., 1., 0., 0]], [-(w + h)]) #ha.new_transition(p3, error).set_guard([[0, 0, -1., -1., 0., 0]], [-(w + h)]) #ha.new_transition(p3, error).set_guard([[0, 0, 1., -1., 0., 0]], [-(w + h)]) return ha
def define_ha(): '''make the hybrid automaton and return it''' ha = HybridAutomaton() # dynamics variable order: [x1, x2, t, affine] loc2 = ha.new_mode('loc2') a_matrix = [ \ [0, 1, 0, 0], \ [18300, -20, 0, -9562.5], \ [0, 0, 0, 1], \ [0, 0, 0, 0], \ ] loc2.set_dynamics(a_matrix) # -x1 > -0.51 & -x1 <= -0.5 loc2.set_invariant([[1, -0, -0, -0], [-1, 0, 0, 0], ], [0.51, -0.5, ]) loc3 = ha.new_mode('loc3') a_matrix = [ \ [0, 1, 0, 0], \ [-825, -20, 0, 0], \ [0, 0, 0, 1], \ [0, 0, 0, 0], \ ] loc3.set_dynamics(a_matrix) # -x1 > -0.5 & -x1 <= -0.09 loc3.set_invariant([[1, -0, -0, -0], [-1, 0, 0, 0], ], [0.5, -0.09, ]) loc4 = ha.new_mode('loc4') a_matrix = [ \ [0, 1, 0, 0], \ [30675, -695, 0, -2835], \ [0, 0, 0, 1], \ [0, 0, 0, 0], \ ] loc4.set_dynamics(a_matrix) # -x1 > -0.09 & -x1 <= -0.08 loc4.set_invariant([[1, -0, -0, -0], [-1, 0, 0, 0], ], [0.09, -0.08, ]) loc1 = ha.new_mode('loc1') a_matrix = [ \ [0, 1, 0, 0], \ [-450, -20, 0, 0], \ [0, 0, 0, 1], \ [0, 0, 0, 0], \ ] loc1.set_dynamics(a_matrix) # -x1 <= -0.51 loc1.set_invariant([[-1, 0, 0, 0], ], [-0.51, ]) loc5 = ha.new_mode('loc5') a_matrix = [ \ [0, 1, 0, 0], \ [-4762.5, -95, 0, 0], \ [0, 0, 0, 1], \ [0, 0, 0, 0], \ ] loc5.set_dynamics(a_matrix) # -x1 > -0.08 & -x1 < 0.08 loc5.set_invariant([[1, -0, -0, -0], [-1, 0, 0, 0], ], [0.08, 0.08, ]) loc6 = ha.new_mode('loc6') a_matrix = [ \ [0, 1, 0, 0], \ [30675, -695, 0, 2835], \ [0, 0, 0, 1], \ [0, 0, 0, 0], \ ] loc6.set_dynamics(a_matrix) # -x1 >= 0.08 & -x1 < 0.09 loc6.set_invariant([[1, -0, -0, -0], [-1, 0, 0, 0], ], [-0.08, 0.09, ]) loc7 = ha.new_mode('loc7') a_matrix = [ \ [0, 1, 0, 0], \ [-825, -20, 0, 0], \ [0, 0, 0, 1], \ [0, 0, 0, 0], \ ] loc7.set_dynamics(a_matrix) # -x1 >= 0.09 & -x1 < 0.5 loc7.set_invariant([[1, -0, -0, -0], [-1, 0, 0, 0], ], [-0.09, 0.5, ]) loc8 = ha.new_mode('loc8') a_matrix = [ \ [0, 1, 0, 0], \ [18300, -20, 0, 9562.5], \ [0, 0, 0, 1], \ [0, 0, 0, 0], \ ] loc8.set_dynamics(a_matrix) # -x1 >= 0.5 & -x1 < 0.51 loc8.set_invariant([[1, -0, -0, -0], [-1, 0, 0, 0], ], [-0.5, 0.51, ]) loc9 = ha.new_mode('loc9') a_matrix = [ \ [0, 1, 0, 0], \ [-450, -20, 0, 0], \ [0, 0, 0, 1], \ [0, 0, 0, 0], \ ] loc9.set_dynamics(a_matrix) # -x1 >= 0.51 loc9.set_invariant([[1, -0, -0, -0], ], [-0.51, ]) trans = ha.new_transition(loc2, loc1) # -x1 <= -0.51 trans.set_guard([[-1, 0, 0, 0], ], [-0.51, ]) trans = ha.new_transition(loc2, loc3) # -x1 > -0.5 trans.set_guard([[1, -0, -0, -0], ], [0.5, ]) trans = ha.new_transition(loc3, loc4) # -x1 > -0.09 trans.set_guard([[1, -0, -0, -0], ], [0.09, ]) trans = ha.new_transition(loc3, loc2) # -x1 <= -0.5 trans.set_guard([[-1, 0, 0, 0], ], [-0.5, ]) trans = ha.new_transition(loc4, loc5) # -x1 > -0.08 trans.set_guard([[1, -0, -0, -0], ], [0.08, ]) trans = ha.new_transition(loc4, loc3) # -x1 <= -0.09 trans.set_guard([[-1, 0, 0, 0], ], [-0.09, ]) trans = ha.new_transition(loc1, loc2) # -x1 > -0.51 trans.set_guard([[1, -0, -0, -0], ], [0.51, ]) trans = ha.new_transition(loc5, loc6) # -x1 >= 0.0801 trans.set_guard([[1, -0, -0, -0], ], [-0.0801, ]) trans = ha.new_transition(loc5, loc4) # -x1 <= -0.0801 trans.set_guard([[-1, 0, 0, 0], ], [-0.0801, ]) trans = ha.new_transition(loc6, loc7) # -x1 >= 0.09 trans.set_guard([[1, -0, -0, -0], ], [-0.09, ]) trans = ha.new_transition(loc6, loc5) # -x1 < 0.08 trans.set_guard([[-1, 0, 0, 0], ], [0.08, ]) trans = ha.new_transition(loc7, loc8) # -x1 >= 0.5 trans.set_guard([[1, -0, -0, -0], ], [-0.5, ]) trans = ha.new_transition(loc7, loc6) # -x1 < 0.09 trans.set_guard([[-1, 0, 0, 0], ], [0.09, ]) trans = ha.new_transition(loc8, loc9) # -x1 >= 0.51 trans.set_guard([[1, -0, -0, -0], ], [-0.51, ]) trans = ha.new_transition(loc8, loc7) # -x1 < 0.5 trans.set_guard([[-1, 0, 0, 0], ], [0.5, ]) trans = ha.new_transition(loc9, loc8) # -x1 < 0.51 trans.set_guard([[-1, 0, 0, 0], ], [0.51, ]) return ha
def test_time_triggered(): 'test to make sure exact time-triggered guards only have a single sucessor state' ha = HybridAutomaton() # mode one: x' = 1, a' = 0 m1 = ha.new_mode('m1') m1.set_dynamics([[0, 1], [0, 0]]) # mode two: x' = 1, a' = 0 m2 = ha.new_mode('m2') m2.set_dynamics([[0, 1], [0, 0]]) # invariant: x <= 2.0 m1.set_invariant([[1, 0]], [2.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]) # manually run ha.detect_tt_transitions() and check the result ha.detect_tt_transitions() assert trans1.time_triggered assert not trans2.time_triggered # not time-triggered because invariant of m2 is True # initial set has x = 0, a = 1 init_lpi = lputil.from_box([(0, 0), (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.plot.store_plot_result = True result = Core(ha, settings).run(init_list) ce = result.counterexample assert len(ce) == 2 assert ce[0].mode.name == 'm1' assert ce[0].outgoing_transition.name == 'trans1' assert ce[1].mode.name == 'm2' assert ce[1].outgoing_transition.name == 'to_error' assert abs(ce[0].start[0] - 0.0) < 1e-5 assert abs(ce[0].end[0] - 2.0) < 1e-5 assert abs(ce[1].start[0] - 2.0) < 1e-5 assert abs(ce[1].end[0] - 4.0) < 1e-5 polys = [obj[0] for obj in result.plot_data.mode_to_obj_list[0]['m1']] assert len(polys) == 3 # time 0, 1, 2 polys = [obj[0] for obj in result.plot_data.mode_to_obj_list[0]['m2']] assert len(polys) == 3 # times 2, 3, 4