예제 #1
0
 def _verify(P, A):
     # eg. for dReal
     import z3
     from src.sympy_converter import sympy_converter
     from src.linear import f0, f1
     from src.common import OrNotZero
     eigvals = sp.Matrix(P).eigenvals()
     #log("eig %s" % eigvals)
     try:
         if all(sp.re(l) > 0 for (l, m) in eigvals.items()):  # eig P > 0
             return True
         else:
             return False
     except:
         pass
     x = sp.Matrix([sp.Symbol('x%s' % i) for i in range(P.shape[0])])
     x3 = [z3.Real('x%s' % i) for i in range(P.shape[0])]
     _, _, V = sympy_converter(f0(x, x.T, P))
     _, _, Vd = sympy_converter(f1(x, x.T, P, A))
     s = z3.Solver()
     s.add(z3.Not(z3.Implies(OrNotZero(x3), z3.And(V > 0, Vd < 0))))
     r = s.check()
     #if r == z3.sat:
         #log("_verify has model %s\n\t against V = %s ; Vd = %s" % (s.model(), V, Vd))
     return r == z3.unsat
예제 #2
0
 def _subs(self, e, e_sym, model):
     try:
         return model.evaluate(e)
     except:
         return convert.sympy_converter(e_sym.subs({
             self._xs_sym_map[str(k)]: v
             for k, v in izip(self.xs, model)
         }))[-1]
예제 #3
0
    def check(self, P):
        """

        :param P: matrix
        :return: unsat if there have been found no counterexamples, sat if there have been found and unknown otherwise
        """

        self._solver = Solver()
        self._set_timeout()

        log("Verifier got P %s" % P)

        V, Vd = self._V, self._Vd
        for i in range(len(self._P_sym)):
            V = V.subs({
                self._P_sym[i][r, c]: P[i][r, c]
                for r in range(self.n) for c in range(self.n)
            })
            Vd = Vd.subs({
                self._P_sym[i][r, c]: P[i][r, c]
                for r in range(self.n) for c in range(self.n)
            })

        _, _, V = sympy_converter(V.doit()[0])
        _, _, Vd = sympy_converter(Vd.doit()[0])

        if V == 0 or Vd == 0:
            log("error: P=0")
            return sat

        fml = Implies(OrNotZero(self._xs_z3), And(V >= 0, Vd <= 0))
        res, c0 = self._prove(fml, exp=0)

        if res == sat:
            self._add_counterexample(c0)

            self._solver.push()  # keep fml
            self._generate_counterexamples(c0 is not None)
            self._solver.pop()

        return res
예제 #4
0
    def _add_constraint(self, counterexample):
        for V_sym, Vd_sym in izip(self.V_list_sym, self.Vd_list_sym):
            V = V_sym.subs(
                {x: c
                 for x, c in izip(self._xs_sym, counterexample)})
            Vd = Vd_sym.subs(
                {x: c
                 for x, c in izip(self._xs_sym, counterexample)})

            _, __, V = convert.sympy_converter(V,
                                               var_map=self.P_map,
                                               target=convert.TARGET_SOLVER)
            _, __, Vd = convert.sympy_converter(Vd,
                                                var_map=self.P_map,
                                                target=convert.TARGET_SOLVER)
            try:
                self.model.addConstr(V >= consts.ZERO)
                self.model.addConstr(Vd <= -consts.ZERO)
                self.model.update()
            except Exception as e:
                log('Could not add counterexample: %s. (V:%s, Vd:%s). Exception: %s'
                    % (counterexample, V, Vd, e))
예제 #5
0
    def __init__(self, params):
        assert is_valid_matrix_A(params.A)
        self.A = params.A
        self.n = params.A.shape[0]
        self.iter = -1
        self.solution = None
        self.xs = params.xs
        self.P_sym = params.P
        self.P_z3 = []  # flat array of P Z3's Real

        self._solver = Solver()

        self.vars_map = {}

        self._V_sym = params.V.doit()[0]
        self._Vd_sym = params.Vd.doit()[0]
        self._Vd_diag_sym = params.Vd_diag.doit()[0]
        # self._Vd_list = params.Vd_list
        self._Vd_diag_list = params.Vd_diag_list

        # the following lines impose a diagonal P if diag = True
        # or a full P if diag = False
        diag = True # is_diagonal(params.A)
        for P in params.P:
            for r in range(self.n):
                for c in range(self.n):
                    name = str(P[r, c])
                    p = Real(name)
                    if diag and r != c:
                        # self.P_sym[r, c] = 0
                        self._solver.add(p == 0)
                        self._V_sym = self._V_sym.subs(P[r, c], 0)
                        self._Vd_sym = self._Vd_sym.subs(P[r, c], 0)
                        self._Vd_diag_sym = self._Vd_diag_sym.subs(P[r, c], 0)

                    if name not in self.vars_map and (not diag or r == c):
                        self.vars_map[name] = p
                        self.P_z3.append(p)

        # this prevents a whole P_i matrix to be null
        if len(self.P_sym) > 1:
            self._P_not_zero = True
            n = len(self.A[0,:])
            for l in range(len(self.P_sym)):
                c = Or( *( self.P_z3[l*n+i] != 0  for i in range(n) ) )
                self._P_not_zero = And(c, self._P_not_zero)
        else:
            self._P_not_zero = Or(*(p != 0 for p in self.P_z3))

        self._xs_sym_map = {str(_x): _x for _x in self.xs}

        _, __, self.V_z3expr = convert.sympy_converter(self._V_sym, var_map=self.vars_map)
        _, __, self.Vd_z3expr = convert.sympy_converter(self._Vd_sym, var_map=self.vars_map)
        _, __, self.Vd_diag_z3expr = convert.sympy_converter(self._Vd_diag_sym, var_map=self.vars_map)

        #self.Vd_list_z3expr = []
        self.Vd_diag_list_z3expr = []

        for l in range(len(self._Vd_diag_list)):
            # _, __, temp = convert.sympy_converter(self._Vd_list[l][0], var_map=self.vars_map)
            # self.Vd_list_z3expr += [temp]
            _, __, temp2 = convert.sympy_converter(self._Vd_diag_list[l][0], var_map=self.vars_map)
            self.Vd_diag_list_z3expr += [temp2]

        self._multiply_by_inv_min = False
        self._close_diagonal_by = 0
        self._lb, self._ub = None, None
        self._min_sum, self._max_sum = None, None

        self._timeout = float('inf')
        self._has_timedout = False
        self._error = False

        self._set_solver()
예제 #6
0
    def solve(self):
        S = []
        for idx in range(self.batch_size):
            s = torch.normal(0, self.outer / 3,
                             size=torch.Size([self.n])).double()
            # if inner_radius < torch.norm(s) < outer_radius:
            S.append(s)
        Sdot = list(map(torch.tensor, map(self.f, S)))

        S, Sdot = torch.stack(S), torch.stack(Sdot)

        if self.learner_type == LearnerType.NN:
            self.optimizer = torch.optim.AdamW(self.learner.parameters(),
                                               lr=self.learning_rate)

        stats = {}
        # the CEGIS loop
        iters = 0
        stop, found = False, False
        start = timeit.default_timer()
        #
        while not stop:

            print_section('Learning', iters)
            if self.learner_type == LearnerType.NN:
                learned = self.learner.learn(self.optimizer, S, Sdot, self.lf)

                # to disable rounded numbers, set rounding=-1
                x_sp = [sp.Symbol('x%d' % i) for i in range(len(self.x))]
                V_s, Vdot_s = get_symbolic_formula(self.learner,
                                                   self.x,
                                                   self.f(x_sp),
                                                   self.eq,
                                                   rounding=3,
                                                   lf=self.lf)
                V_s, Vdot_s = sp.simplify(V_s), sp.simplify(Vdot_s)
                V = sympy_converter(V_s,
                                    var_map=self.x_map,
                                    target=type(self.verifier))
                Vdot = sympy_converter(Vdot_s,
                                       var_map=self.x_map,
                                       target=type(self.verifier))
                if self.verifier == Z3Verifier:
                    V, Vdot = z3.simplify(V), z3.simplify(Vdot)
            else:
                P = self.learner.learn(S.numpy().T, Sdot.numpy().T)
                # might modify get_symbolic_formula to work with x*P*x Lyapunov candidate...
                V, Vdot = self.learner.get_poly_formula(self.x, self.xdot, P)

            print_section('Candidate', iters)
            print(f'V: {V_s}')
            print(f'Vdot: {Vdot_s}')

            print_section('Verification', iters)
            found, ces = self.verifier.verify(V, Vdot)

            if self.max_cegis_iter == iters:
                print('Out of Cegis loops')
                stop = True

            if found:
                print('Found a Lyapunov function, baby!')
                stop = True
            else:
                iters += 1
                if len(ces) > 0:
                    S, Sdot = self.add_ces_to_data(S, Sdot, ces)
                    # the original ctx is in the last row of ces
                    trajectory = self.trajectoriser(ces[-1])
                    S, Sdot = self.add_ces_to_data(S, Sdot, trajectory)

        print('Learner times: {}'.format(self.learner.get_timer()))
        print('Verifier times: {}'.format(self.verifier.get_timer()))
        return self.learner, found, iters