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_redundant_invariants(): 'test removing of redundant invariants' ha = HybridAutomaton() mode = ha.new_mode('mode') # dynamics: x' = 1, y' = 1, a' = 0 mode.set_dynamics([[0, 0, 1], [0, 0, 1], [0, 0, 0]]) # invariant: x <= 2.5 mode.set_invariant([[1, 0, 0]], [2.5]) # initial set has x0 = [0, 1] init_lpi = lputil.from_box([(0, 1), (0, 1), (1, 1)], mode) init_list = [StateSet(init_lpi, mode)] # settings, step size = 0.1 settings = HylaaSettings(0.1, 5.0) settings.stdout = HylaaSettings.STDOUT_NONE settings.plot.plot_mode = PlotSettings.PLOT_NONE result = Core(ha, settings).run(init_list) # check last cur_state to ensure redundant constraints were not added assert result.last_cur_state.lpi.get_num_rows() == 3 + 2*3 + 1 # 3 for basis matrix, 2*3 for initial constraints
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 define_settings(): '''get the hylaa settings object see hylaa/settings.py for a complete list of reachability settings''' # step_size = 0.001, max_time = 0.75 settings = HylaaSettings(0.001, 0.15) settings.plot.plot_mode = PlotSettings.PLOT_IMAGE # try PLOT_VIDEO (takes 10 minutes) settings.plot.xdim_dir = 2 settings.plot.ydim_dir = 0 settings.plot.label.title = "Fuzzy PD Controller" settings.plot.label.axes_limits = (-0.01, 0.3, -1.1, 1.1) settings.stdout = HylaaSettings.STDOUT_VERBOSE #settings.aggregation.require_same_path=False #settings.aggregation.pop_strategy=AggregationSettings.POP_LARGEST_MAXTIME #self.aggstrat = aggstrat.Aggregated() # custom settings for video export def make_video_writer(): 'returns the Writer to create a video for export' writer_class = animation.writers['ffmpeg'] return writer_class(fps=50, metadata=dict(artist='Me'), bitrate=1800) settings.plot.make_video_writer_func = make_video_writer return settings
def define_settings(): '''get the hylaa settings object see hylaa/settings.py for a complete list of reachability settings''' # step_size = 5.0E-4, max_time = 2.0 settings = HylaaSettings(5.0E-3, 2.0) settings.stdout = HylaaSettings.STDOUT_VERBOSE settings.plot.plot_mode = PlotSettings.PLOT_IMAGE settings.plot.xdim_dir = 0 # [0, None, None] #x0_dir = np.array([0, 0, 0, 0, 0, 0, 0.0833333333333333, 0, -1, 0, 0, 0, 0], dtype=float) #settings.plot.ydim_dir = [2, 6, 8] settings.plot.ydim_dir = 2 #settings.stop_on_error = False #settings.plot.draw_stride = 5 #settings.plot.num_angles = 4096 * 128 # required for convex hull to show up correctly #settings.aggregation.agg_mode = AggregationSettings.AGG_NONE #def custom_pop_func(waiting_list): # 'custom pop function for aggregation' # mode = waiting_list[0].mode # state_list = [state for state in waiting_list if state.mode == mode] # num = 2 # performing clustering with this number of items # return heapq.nsmallest(num, state_list, lambda s: s.cur_steps_since_start) #settings.aggregation.custom_pop_func = custom_pop_func settings.aggstrat.agg_type = Aggregated.AGG_CONVEX_HULL return settings
def define_settings(unsafe_box): 'get the hylaa settings object' step = math.pi / 6 max_time = 3 * math.pi settings = HylaaSettings(step, max_time) settings.process_urgent_guards = True settings.aggstrat.deaggregate = True # use deaggregation settings.aggstrat.deagg_preference = Aggregated.DEAGG_LEAVES_FIRST settings.aggstrat.agg_type = Aggregated.AGG_CONVEX_HULL plot_settings = settings.plot plot_settings.plot_mode = PlotSettings.PLOT_IMAGE plot_settings.xdim_dir = 0 plot_settings.ydim_dir = 1 plot_settings.label.x_label = '$x$' plot_settings.label.y_label = '$y$' cols = [] line = [(-10, -3), (10, -3)] cols.append( collections.LineCollection([line], animated=True, colors=('gray'), linewidths=(2), linestyle='dashed')) line = [] line.append((unsafe_box[0][0], unsafe_box[1][0])) line.append((unsafe_box[0][1], unsafe_box[1][0])) line.append((unsafe_box[0][1], unsafe_box[1][1])) line.append((unsafe_box[0][0], unsafe_box[1][1])) line.append((unsafe_box[0][0], unsafe_box[1][0])) cols.append( collections.LineCollection([line], animated=True, colors=('red'), linewidths=(2), linestyle='dashed')) settings.plot.extra_collections = cols plot_settings.plot_mode = PlotSettings.PLOT_VIDEO plot_settings.filename = 'ha_deagg.mp4' plot_settings.video_fps = 4 plot_settings.video_extra_frames = 12 # extra frames at the end of a video so it doesn't end so abruptly plot_settings.video_pause_frames = 2 # frames to render in video whenever a 'pause' occurs plot_settings.label.axes_limits = [-6, 6, -4, 6] plot_settings.label.y_label = '$y$' plot_settings.label.x_label = '$x$' plot_settings.label.title = 'Deaggregation Demo' return settings
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 make_settings(): 'make the reachability settings object' # see hylaa.settings for a list of reachability settings settings = HylaaSettings(1.0, 10.0) # step size = 1.0, time bound 10.0 settings.plot.plot_mode = PlotSettings.PLOT_IMAGE settings.stdout = HylaaSettings.STDOUT_VERBOSE settings.plot.filename = "demo_reset.png" return settings
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 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_settings(): 'make the reachability settings object' # see hylaa.settings for a list of reachability settings settings = HylaaSettings(math.pi / 8, math.pi) # step size = pi/8, time bound pi settings.plot.plot_mode = PlotSettings.PLOT_IMAGE settings.stdout = HylaaSettings.STDOUT_NORMAL settings.plot.filename = "demo_inputs_reset.png" return settings
def make_settings(): 'make the reachability settings object' # see hylaa.settings for a list of reachability settings settings = HylaaSettings(math.pi / 4, math.pi) # step size = pi/4, time bound pi settings.plot.plot_mode = PlotSettings.PLOT_IMAGE settings.stdout = HylaaSettings.STDOUT_NORMAL settings.plot.filename = "hylaa.png" settings.plot.label.title_size = 18 settings.plot.plot_size = (6, 6) return settings
def make_settings(): 'make the reachability settings object' # see hylaa.settings for a list of reachability settings settings = HylaaSettings(0.1, 20.0) # step size = 0.1, time bound 20.0 settings.plot.plot_mode = PlotSettings.PLOT_NONE settings.stdout = HylaaSettings.STDOUT_VERBOSE settings.plot.filename = "space_station.png" settings.plot.xdim_dir = None # x dimension will be time dynamics = loadmat('iss.mat') y3 = dynamics['C'][2] settings.plot.ydim_dir = y3.toarray()[0] # use y3 for the y plot direction return settings
def define_settings(): 'get the hylaa settings object' step = math.pi/16 max_time = 2*math.pi settings = HylaaSettings(step, max_time) settings.stdout = stdout = HylaaSettings.STDOUT_VERBOSE plot_settings = settings.plot plot_settings.plot_mode = PlotSettings.PLOT_IMAGE plot_settings.xdim_dir = 0 plot_settings.ydim_dir = 1 #plot_settings.plot_mode = PlotSettings.PLOT_VIDEO #plot_settings.filename = 'ha.mp4' #plot_settings.video_fps = 2 #plot_settings.video_extra_frames = 10 # extra frames at the end of a video so it doesn't end so abruptly #plot_settings.video_pause_frames = 5 # frames to render in video whenever a 'pause' occurs plot_settings.label.y_label = '$y$' plot_settings.label.x_label = '$x$' plot_settings.label.title = 'Harmonic Oscillator' return settings
def define_settings(): 'get the hylaa settings object' step = 1 max_time = 28 # max_time = 22 settings = HylaaSettings(step, max_time) settings.stdout = stdout = HylaaSettings.STDOUT_VERBOSE plot_settings = settings.plot plot_settings.plot_mode = PlotSettings.PLOT_IMAGE plot_settings.xdim_dir = 0 plot_settings.ydim_dir = 1 # plot_settings.plot_mode = PlotSettings.PLOT_VIDEO # plot_settings.filename = 'tmpc.mp4' plot_settings.video_fps = 1 plot_settings.video_extra_frames = 2 # extra frames at the end of a video so it doesn't end so abruptly plot_settings.video_pause_frames = 2 # frames to render in video whenever a 'pause' occurs plot_settings.label.y_label = '$y$' plot_settings.label.x_label = '$x$' plot_settings.label.title = 'TMPC mode 1 X vs Y' return settings
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_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 define_settings(ha, limit): 'get the hylaa settings object' step = 0.0025 max_time = 1.0 settings = HylaaSettings(step, max_time) #settings.interval_guard_optimization = False #settings.time_elapse.scipy_sim.max_step = 0.001 #settings.time_elapse.scipy_sim.rtol = 1e-9 #settings.time_elapse.scipy_sim.atol = 1e-12 settings.stdout = stdout = HylaaSettings.STDOUT_VERBOSE plot_settings = settings.plot plot_settings.plot_mode = PlotSettings.PLOT_IMAGE #plot_settings.plot_mode = PlotSettings.PLOT_VIDEO #plot_settings.filename = 'building.mp4' plot_settings.xdim_dir = None plot_settings.ydim_dir = ha.transitions[0].guard_csr[0].toarray()[0] plot_settings.label.y_label = '$y_{1}$' plot_settings.label.x_label = 'Time' plot_settings.label.title = 'Building (Uncertain Inputs)' #plot_settings.label.axes_limits = (0.4, 0.6, -0.0002, -0.0001) plot_settings.plot_size = (12, 8) plot_settings.label.big(size=36) settings.stop_on_concrete_error = False settings.make_counterexample = False line = [(0.0, -limit), (max_time, -limit)] lc = collections.LineCollection([line], animated=True, colors=('red'), linewidths=(1), linestyle='dashed') plot_settings.extra_collections = [lc] return settings
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_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_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_plot_over_time(): 'test doing a plot over time' ha = HybridAutomaton() mode = ha.new_mode('mode') mode.set_dynamics([[0, 1], [-1, 0]]) # initial set init_lpi = lputil.from_box([(-5, -4), (0, 1)], mode) init_list = [StateSet(init_lpi, mode)] # settings settings = HylaaSettings(math.pi/4, math.pi) settings.stdout = HylaaSettings.STDOUT_VERBOSE settings.plot.store_plot_result = True settings.plot.plot_mode = PlotSettings.PLOT_NONE settings.plot.ydim_dir = None # y dimension will be time result = Core(ha, settings).run(init_list) assert not result.has_aggregated_error and not result.has_concrete_error # check the reachable state # we would expect at the end that x = [4, 5], t = pi obj_list = result.plot_data.mode_to_obj_list[0][mode.name] for vert in obj_list[0][0]: x, y = vert assert abs(y) < 1e-6, "initial poly time is wrong" assert abs(-5 - x) < 1e-6 or abs(-4 - x) < 1e-6 for vert in obj_list[-1][0]: x, y = vert assert abs(math.pi - y) < 1e-6, "final poly time is wrong" assert abs(5 - x) < 1e-6 or abs(4 - x) < 1e-6
def test_init_unsat(): 'initial region unsat with multiple invariant conditions' ha = HybridAutomaton() mode = ha.new_mode('A') mode.set_dynamics(np.identity(2)) mode.set_invariant([[1, 0], [1, 0]], [2, 3]) # x <= 2 and x <= 3 # initial set lpi1 = lputil.from_box([(10, 11), (0, 1)], mode) lpi2 = lputil.from_box([(0, 1), (0, 1)], mode) init_list = [StateSet(lpi1, mode), StateSet(lpi2, mode)] # settings settings = HylaaSettings(1, 5) settings.stdout = HylaaSettings.STDOUT_VERBOSE settings.plot.plot_mode = PlotSettings.PLOT_NONE core = Core(ha, settings) core.run(init_list) # expect no exception during running
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_multiple_init_states(): 'test with multiple initial states in the same mode (should NOT do aggregation)' ha = HybridAutomaton() # with time and affine variable mode = ha.new_mode('mode') mode.set_dynamics([[0, 0], [0, 0]]) # initial set init_lpi = lputil.from_box([(-5, -4), (0, 1)], mode) init_lpi2 = lputil.from_box([(-5, -5), (2, 3)], mode) init_list = [StateSet(init_lpi, mode), StateSet(init_lpi2, mode)] # settings settings = HylaaSettings(math.pi/4, math.pi) settings.stdout = HylaaSettings.STDOUT_NONE settings.plot.plot_mode = PlotSettings.PLOT_NONE core = Core(ha, settings) core.run(init_list)
def test_invariants(): 'test invariant trimming' ha = HybridAutomaton() mode = ha.new_mode('mode') # dynamics: x' = 1, y' = 1, a' = 0 mode.set_dynamics([[0, 0, 1], [0, 0, 1], [0, 0, 0]]) # invariant: x <= 2.5 mode.set_invariant([[1, 0, 0]], [2.5]) # initial set has x0 = [0, 1] init_lpi = lputil.from_box([(0, 1), (0, 1), (1, 1)], mode) init_list = [StateSet(init_lpi, mode)] # settings, step size = 1.0 settings = HylaaSettings(1.0, 5.0) settings.stdout = HylaaSettings.STDOUT_VERBOSE settings.plot.store_plot_result = True result = Core(ha, settings).run(init_list) # check the reachable state polys = [obj[0] for obj in result.plot_data.mode_to_obj_list[0][mode.name]] # 4 steps because invariant is allowed to be false for the final step assert len(polys) == 4, "expected invariant to become false after 4 steps" assert_verts_is_box(polys[0], [[0, 1], [0, 1]]) assert_verts_is_box(polys[1], [[1, 2], [1, 2]]) assert_verts_is_box(polys[2], [[2, 3], [2, 3]]) assert_verts_is_box(polys[3], [[3, 3.5], [3, 4]])
def test_stateset_bad_init(): 'test constructing a stateset with a basis matrix that is not the identity (should raise error)' # this is from an issue reported by Mojtaba Zarei ha = HybridAutomaton() mode = ha.new_mode('mode') mode.set_dynamics([[0, 1], [-1, 0]]) # initial set init_lpi = lputil.from_box([(-5, -5), (0, 1)], mode) init_list = [StateSet(init_lpi, mode)] # settings settings = HylaaSettings(math.pi/4, math.pi) settings.stdout = HylaaSettings.STDOUT_NONE settings.plot.store_plot_result = True settings.plot.plot_mode = PlotSettings.PLOT_NONE core = Core(ha, settings) result = core.run(init_list) # use last result stateset = result.last_cur_state mode = stateset.mode lpi = stateset.lpi try: init_states = [StateSet(lpi, mode)] settings = HylaaSettings(0.1, 0.1) core = Core(ha, settings) result = core.run(init_states) assert False, "assertion should be raised if init basis matrix is not identity" except RuntimeError: pass
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_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 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)"