def test_chull_one_step_inputs(): 'test convex hull with one-step lpi for a system with inputs (bug where current vars was not set correctly)' mode = HybridAutomaton().new_mode('mode_name') step_size = math.pi/4 a_mat = np.array([[0, 1], [-1, 0]], dtype=float) b_mat = [[1], [0]] b_constraints = [[1], [-1]] b_rhs = [0.2, 0.2] mode.set_dynamics(a_mat) mode.set_inputs(b_mat, b_constraints, b_rhs) mode.init_time_elapse(step_size) box = [[-5, -4], [0.0, 1.0]] lpi = lputil.from_box(box, mode) lpi_one_step = lpi.clone() bm, ie_mat = mode.time_elapse.get_basis_matrix(1) lputil.set_basis_matrix(lpi_one_step, bm) lputil.add_input_effects_matrix(lpi_one_step, ie_mat, mode) lpi_list = [lpi, lpi_one_step] chull_lpi = lputil.aggregate_chull(lpi_list, mode) # 2 current vars and 2 total input effect vars, so expected to be 4 from the end assert chull_lpi.cur_vars_offset == chull_lpi.get_num_cols() - 4, "cur_vars in wrong place"
def test_reject_constant_inputs(): 'tests the detection of B matrix + constraints where an input is fixed to a constant' # x' = Ax + Bu # x: [[1, 0], [0, 1]] mode = HybridAutomaton().new_mode('mode_name') mode.set_dynamics(np.identity(2)) b_mat = np.identity(2) b_con = [[1, 0], [-1, 0], [0, -1], [0, -1]] b_rhs = [1, 0, 2, -2] try: mode.set_inputs(b_mat, b_con, b_rhs) assert False, "expected fixed inputs to be rejected" except AssertionError: pass b_rhs = [1, 0, 2, -1] mode.set_inputs(b_mat, b_con, b_rhs) # should be okay b_mat = [[1, 1], [1, 1]] b_rhs = [1, 0, 2, -2] mode.set_inputs(b_mat, b_con, b_rhs) # should be okay (b_mat is not identity) b_mat = np.identity(2) b_con = [[1, 1], [-1, -1], [1, -1], [-1, 1]] b_rhs = [1, 0, 2, -2] mode.set_inputs(b_mat, b_con, b_rhs) # should be okay b_mat = np.identity(2) b_con = [[1, 1], [-1, -1], [1, -1], [-1, 1]] b_rhs = [1, -2, 2, -2] try: mode.set_inputs(b_mat, b_con, b_rhs) assert False, "expected unsat inputs to be rejected" except AssertionError: pass
def test_step_slow(): 'tests slow-step with non-one step size' mode = HybridAutomaton().new_mode('mode_name') mode.set_dynamics(np.identity(2)) mode.set_inputs([[1, 0], [0, 2]], [[1, 0], [-1, 0], [0, 1], [0, -1]], [10, -1, 10, -1]) mode.init_time_elapse(0.5) # do step 1 _, _ = mode.time_elapse.get_basis_matrix(1) # do step 2 basis_mat, input_mat = mode.time_elapse.get_basis_matrix(2) # do step 3 _, _ = mode.time_elapse.get_basis_matrix(3) # go back to step 2 (slow step) and make sure it matches slow_basis_mat, slow_input_mat = mode.time_elapse.get_basis_matrix(2) assert np.allclose(basis_mat, slow_basis_mat) assert np.allclose(input_mat, slow_input_mat)
def test_approx_lgg_inputs(): 'test lgg approximation model with inputs' # simple dynamics, x' = 1, y' = 0 + u, a' = 0, u in [0.1, 0.2] # step size (tau) 0.02 # after one step, the input effect size should by tau*V \oplus beta*B # we'll manually assign beta to be 0.02, in order to be able to check that the constraints are correct # A norm is 1 tau = 0.05 a_matrix = [[0, 0, 1], [0, 0, 0], [0, 0, 0]] b_mat = [[0], [1], [0]] b_constraints = [[1], [-1]] b_rhs = [0.2, -0.1] mode = HybridAutomaton().new_mode('mode') mode.set_dynamics(a_matrix) mode.set_inputs(b_mat, b_constraints, b_rhs) init_lpi = lputil.from_box([[0, 0], [0, 0], [1, 1]], mode) assert lputil.compute_radius_inf(init_lpi) == 1 ss = StateSet(init_lpi, mode) mode.init_time_elapse(tau) assert_verts_equals(lpplot.get_verts(ss.lpi), [(0, 0)]) ss.apply_approx_model(HylaaSettings.APPROX_LGG) assert np.linalg.norm(a_matrix, ord=np.inf) == 1.0 v_set = lputil.from_input_constraints(mode.b_csr, mode.u_constraints_csc, mode.u_constraints_rhs, mode) assert lputil.compute_radius_inf(v_set) == 0.2 alpha = (math.exp(tau) - 1 - tau) * (1 + 0.2) assert_verts_equals(lpplot.get_verts(ss.lpi), \ [(0, 0), (tau-alpha, 0.2*tau + alpha), (tau+alpha, 0.2*tau+alpha), (tau+alpha, 0.1*tau-alpha)]) # note: c gets bloated by alpha as well assert (ss.lpi.minimize( [0, 0, -1])[ss.lpi.cur_vars_offset + 2]) - (1 + alpha) < 1e-9 assert (ss.lpi.minimize( [0, 0, 1])[ss.lpi.cur_vars_offset + 2]) - (1 - alpha) < 1e-9 # c is actually growing, starting at (1,1) at x=0 and going to [1-alpha, 1+alpha] at x=tau assert_verts_equals(lpplot.get_verts(ss.lpi, xdim=0, ydim=2), \ [(0, 1), (tau-alpha, 1+alpha), (tau+alpha, 1+alpha), (tau+alpha, 1-alpha), (tau-alpha, 1-alpha)]) # ready to start ss.step() beta = (math.exp(tau) - 1 - tau) * 0.2 # note: c gets bloated as well! so now it's [1-epsilon, 1+epsilon], where epsilon=alpha # so x will grow by [tau * (1 - alpha), tau * (1 + alpha)] expected = [(tau + beta, -beta + tau * 0.1), \ (tau - beta, -beta + tau * 0.1), \ (tau - beta, beta + tau * 0.2), \ ((tau - alpha) + tau * (1 - alpha) - beta, 2*0.2*tau + alpha + beta), \ ((tau + alpha) + tau * (1 + alpha) + beta, 2*0.2*tau+alpha + beta), \ ((tau + alpha) + tau * (1 + alpha) + beta, 2*0.1*tau-alpha - beta)] #xs, ys = zip(*expected) #plt.plot([x for x in xs] + [xs[0]], [y for y in ys] + [ys[0]], 'r-') # expected is red verts = lpplot.get_verts(ss.lpi) #xs, ys = zip(*verts) #plt.plot(xs, ys, 'k-+') # computed is black #plt.show() assert_verts_equals(verts, expected) # one more step should work without errors ss.step()
def test_box_inputs(): 'tests from_box with a simple input effects matrix' # x' = Ax + Bu # A = 0 # B = [[1, 0], [0, 2]] # u1 and u2 are bounded between [1, 10] # (init) step 0: [0, 1] x [0, 1] # step 1: [1, 11] x [2, 21] # step 2: [2, 21] x [4, 41] mode = HybridAutomaton().new_mode('mode_name') mode.set_dynamics(np.zeros((2, 2))) mode.set_inputs([[1, 0], [0, 2]], [[1, 0], [-1, 0], [0, 1], [0, -1]], [10, -1, 10, -1]) init_box = [[0, 1], [0, 1]] lpi = lputil.from_box(init_box, mode) assert lpi.basis_mat_pos == (0, 0) assert lpi.dims == 2 assert lpi.cur_vars_offset == 2 assert lpi.input_effects_offsets == ( 6, 4) # row 6, column 4 for total input effects offsets # step 0 mat = lpi.get_full_constraints() types = lpi.get_types() rhs = lpi.get_rhs() names = lpi.get_names() expected_mat = np.array([\ [1, 0, -1, 0, 1, 0], \ [0, 1, 0, -1, 0, 1], \ [-1, 0, 0, 0, 0, 0], \ [1, 0, 0, 0, 0, 0], \ [0, -1, 0, 0, 0, 0], \ [0, 1, 0, 0, 0, 0], \ [0, 0, 0, 0, -1, 0], \ [0, 0, 0, 0, 0, -1]], dtype=float) expected_vec = np.array([0, 0, 0, 1, 0, 1, 0, 0], dtype=float) fx = glpk.GLP_FX up = glpk.GLP_UP expected_types = [fx, fx, up, up, up, up, fx, fx] expected_names = ["m0_i0", "m0_i1", "m0_c0", "m0_c1", "m0_ti0", "m0_ti1"] assert np.allclose(rhs, expected_vec) assert types == expected_types assert np.allclose(mat.toarray(), expected_mat) assert names == expected_names verts = lpplot.get_verts(lpi) assert_verts_is_box(verts, init_box) # do step 1 mode.init_time_elapse(1.0) basis_mat, input_mat = mode.time_elapse.get_basis_matrix(1) lputil.set_basis_matrix(lpi, basis_mat) lputil.add_input_effects_matrix(lpi, input_mat, mode) mat = lpi.get_full_constraints() types = lpi.get_types() rhs = lpi.get_rhs() names = lpi.get_names() expected_mat = np.array([\ [1, 0, -1, 0, 1, 0, 0, 0], \ [0, 1, 0, -1, 0, 1, 0, 0], \ [-1, 0, 0, 0, 0, 0, 0, 0], \ [1, 0, 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, -1, 0, 1, 0], \ [0, 0, 0, 0, 0, -1, 0, 2], \ [0, 0, 0, 0, 0, 0, 1, 0], \ [0, 0, 0, 0, 0, 0, -1, 0], \ [0, 0, 0, 0, 0, 0, 0, 1], \ [0, 0, 0, 0, 0, 0, 0, -1]], dtype=float) expected_vec = np.array([0, 0, 0, 1, 0, 1, 0, 0, 10, -1, 10, -1], dtype=float) fx = glpk.GLP_FX up = glpk.GLP_UP expected_types = [fx, fx, up, up, up, up, fx, fx, up, up, up, up] expected_names = [ "m0_i0", "m0_i1", "m0_c0", "m0_c1", "m0_ti0", "m0_ti1", "m0_I0", "m0_I1" ] assert np.allclose(rhs, expected_vec) assert types == expected_types assert np.allclose(mat.toarray(), expected_mat) assert names == expected_names verts = lpplot.get_verts(lpi) assert_verts_is_box(verts, [(1, 11), (2, 21)]) # do step 2 basis_mat, input_mat = mode.time_elapse.get_basis_matrix(2) lputil.set_basis_matrix(lpi, basis_mat) lputil.add_input_effects_matrix(lpi, input_mat, mode) verts = lpplot.get_verts(lpi) assert_verts_is_box(verts, [(2, 21), (4, 41)])