LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/quantifiers/sygus - transition_inference.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 8 8 100.0 %
Date: 2025-03-19 10:36:02 Functions: 6 6 100.0 %
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * Top contributors (to current version):
       3                 :            :  *   Andrew Reynolds, Daniel Larraz, Aina Niemetz
       4                 :            :  *
       5                 :            :  * This file is part of the cvc5 project.
       6                 :            :  *
       7                 :            :  * Copyright (c) 2009-2025 by the authors listed in the file AUTHORS
       8                 :            :  * in the top-level source directory and their institutional affiliations.
       9                 :            :  * All rights reserved.  See the file COPYING in the top-level source
      10                 :            :  * directory for licensing information.
      11                 :            :  * ****************************************************************************
      12                 :            :  *
      13                 :            :  * Utility for inferring whether a synthesis conjecture encodes a
      14                 :            :  * transition system.
      15                 :            :  */
      16                 :            : 
      17                 :            : #include "cvc5_private.h"
      18                 :            : 
      19                 :            : #ifndef CVC5__THEORY__QUANTIFIERS__TRANSITION_INFERENCE_H
      20                 :            : #define CVC5__THEORY__QUANTIFIERS__TRANSITION_INFERENCE_H
      21                 :            : 
      22                 :            : #include <map>
      23                 :            : #include <vector>
      24                 :            : 
      25                 :            : #include "expr/node.h"
      26                 :            : #include "smt/env_obj.h"
      27                 :            : #include "theory/quantifiers/cegqi/inst_strategy_cegqi.h"
      28                 :            : #include "theory/quantifiers/inst_match_trie.h"
      29                 :            : #include "theory/quantifiers/single_inv_partition.h"
      30                 :            : 
      31                 :            : namespace cvc5::internal {
      32                 :            : namespace theory {
      33                 :            : namespace quantifiers {
      34                 :            : 
      35                 :            : /**
      36                 :            :  * Utility for storing a deterministic trace of a transition system. A trace
      37                 :            :  * is stored as a collection of vectors of values that have been taken by
      38                 :            :  * the variables of transition system. For example, say we have a transition
      39                 :            :  * system with variables x,y,z. Say the precondition constrains these variables
      40                 :            :  * to x=1,y=2,z=3, and say that the transition relation admits the single
      41                 :            :  * trace: [1,2,3], [2,3,4], [3,4,5]. We store these three vectors of variables
      42                 :            :  * in the trie within this class.
      43                 :            :  *
      44                 :            :  * This utility is used for determining whether a transition system has a
      45                 :            :  * deterministic terminating trace and hence a trivial invariant.
      46                 :            :  */
      47                 :            : class DetTrace
      48                 :            : {
      49                 :            :  public:
      50                 :            :   /** The current value of the trace */
      51                 :            :   std::vector<Node> d_curr;
      52                 :            :   /**
      53                 :            :    * Increment the trace: index the current values, if successful (they are
      54                 :            :    * not a duplicate of a previous value), update the current values to vals.
      55                 :            :    * Returns true if the increment was successful.
      56                 :            :    */
      57                 :            :   bool increment(Node loc, std::vector<Node>& vals);
      58                 :            :   /**
      59                 :            :    * Construct the formula that this trace represents with respect to variables
      60                 :            :    * in vars. For details, see DetTraceTrie::constructFormula below.
      61                 :            :    */
      62                 :            :   Node constructFormula(NodeManager* nm, const std::vector<Node>& vars);
      63                 :            :   /** Debug print this trace on trace message c */
      64                 :            :   void print(const char* c) const;
      65                 :            : 
      66                 :            :  private:
      67                 :            :   /**
      68                 :            :    * A trie of value vectors for the variables of a transition system. Nodes
      69                 :            :    * are stored as data in tries with no children at the leaves of this trie.
      70                 :            :    */
      71                 :            :   class DetTraceTrie
      72                 :            :   {
      73                 :            :    public:
      74                 :            :     /** the children of this trie */
      75                 :            :     std::map<Node, DetTraceTrie> d_children;
      76                 :            :     /** Add data loc to this trie, indexed by val. */
      77                 :            :     bool add(Node loc, const std::vector<Node>& val);
      78                 :            :     /** clear the trie */
      79                 :        630 :     void clear() { d_children.clear(); }
      80                 :            :     /**
      81                 :            :      * Construct the formula corresponding to this trie with respect to
      82                 :            :      * variables in vars. For example, if we have indexed [1,2,3] and [2,3,4]
      83                 :            :      * and vars is [x,y,z], then this method returns:
      84                 :            :      *   ( x=1 ^ y=2 ^ z=3 ) V ( x=2 ^ y=3 ^ z=4 ).
      85                 :            :      */
      86                 :            :     Node constructFormula(NodeManager* nm,
      87                 :            :                           const std::vector<Node>& vars,
      88                 :            :                           unsigned index = 0);
      89                 :            :   };
      90                 :            :   /** The above trie data structure for this class */
      91                 :            :   DetTraceTrie d_trie;
      92                 :            : };
      93                 :            : 
      94                 :            : /**
      95                 :            :  * Trace increment status, used for incrementTrace below.
      96                 :            :  */
      97                 :            : enum TraceIncStatus
      98                 :            : {
      99                 :            :   // the trace was successfully incremented to a new value
     100                 :            :   TRACE_INC_SUCCESS,
     101                 :            :   // the trace terminated
     102                 :            :   TRACE_INC_TERMINATE,
     103                 :            :   // the trace encountered a bad state (violating the post-condition)
     104                 :            :   TRACE_INC_CEX,
     105                 :            :   // the trace was invalid
     106                 :            :   TRACE_INC_INVALID
     107                 :            : };
     108                 :            : 
     109                 :            : /**
     110                 :            :  * This class is used for inferring that an arbitrary synthesis conjecture
     111                 :            :  * corresponds to an invariant synthesis problem for some predicate (d_func).
     112                 :            :  *
     113                 :            :  * The invariant-to-synthesize can either be explicitly given, via a call
     114                 :            :  * to initialize( f, vars ), or otherwise inferred if this method is not called.
     115                 :            :  */
     116                 :            : class TransitionInference : protected EnvObj
     117                 :            : {
     118                 :            :  public:
     119                 :       9087 :   TransitionInference(Env& env) : EnvObj(env), d_complete(false) {}
     120                 :            :   /** Process the conjecture n
     121                 :            :    *
     122                 :            :    * This initializes this class with information related to viewing it as a
     123                 :            :    * transition system. This means we infer a function, the state variables,
     124                 :            :    * the pre/post condition and transition relation.
     125                 :            :    *
     126                 :            :    * The node n should be the inner body of the negated synthesis conjecture,
     127                 :            :    * prior to generating the deep embedding. That is, given:
     128                 :            :    *   forall f. ~forall x. P( f, x ),
     129                 :            :    * this method expects n to be P( f, x ), and the argument f to be the
     130                 :            :    * function f above.
     131                 :            :    */
     132                 :            :   void process(Node n, Node f);
     133                 :            :   /**
     134                 :            :    * Same as above, without specifying f. This will try to infer a function f
     135                 :            :    * based on the body of n.
     136                 :            :    */
     137                 :            :   void process(Node n);
     138                 :            :   /**
     139                 :            :    * Get the function that is the subject of the synthesis problem we are
     140                 :            :    * analyzing.
     141                 :            :    */
     142                 :            :   Node getFunction() const;
     143                 :            :   /**
     144                 :            :    * Get the variables that the function is applied to. These are the free
     145                 :            :    * variables of the pre/post condition, and transition relation. These are
     146                 :            :    * fresh (Skolem) variables allocated by this class.
     147                 :            :    */
     148                 :            :   void getVariables(std::vector<Node>& vars) const;
     149                 :            :   /**
     150                 :            :    * Get the pre/post condition, or transition relation that was inferred by
     151                 :            :    * this class.
     152                 :            :    */
     153                 :            :   Node getPreCondition() const;
     154                 :            :   Node getPostCondition() const;
     155                 :            :   Node getTransitionRelation() const;
     156                 :            :   /**
     157                 :            :    * Was the analysis of the conjecture complete?
     158                 :            :    *
     159                 :            :    * If this is false, then the system we have inferred does not take into
     160                 :            :    * account all of the synthesis conjecture. This is the case when process(...)
     161                 :            :    * was called on formula that does not have the shape of a transition system.
     162                 :            :    */
     163                 :       2001 :   bool isComplete() const { return d_complete; }
     164                 :            :   /**
     165                 :            :    * Was the analysis of the conjecture trivial? This is true if the function
     166                 :            :    * did not occur in the conjecture.
     167                 :            :    */
     168                 :       1135 :   bool isTrivial() const { return d_trivial; }
     169                 :            : 
     170                 :            :   /**
     171                 :            :    * The following two functions are used for computing whether this transition
     172                 :            :    * relation is deterministic and terminating.
     173                 :            :    *
     174                 :            :    * The argument fwd is whether we are going in the forward direction of the
     175                 :            :    * transition system (starting from the precondition).
     176                 :            :    *
     177                 :            :    * If fwd is true, the initializeTrace method returns TRACE_INC_SUCCESS if the
     178                 :            :    * precondition consists of a single conjunct of the form
     179                 :            :    *   ( x1 = t1 ^ ... ^ xn = tn )
     180                 :            :    * where x1...xn are the state variables of the transition system. Otherwise
     181                 :            :    * it returns TRACE_INC_INVALID.
     182                 :            :    */
     183                 :            :   TraceIncStatus initializeTrace(DetTrace& dt, bool fwd = true);
     184                 :            :   /**
     185                 :            :    * Increment the trace dt in direction fwd.
     186                 :            :    *
     187                 :            :    * If fwd is true, the incrementTrace method returns TRACE_INC_INVALID if the
     188                 :            :    * transition relation is not of the form
     189                 :            :    *   ( x1' = t1[X] ^ ... ^ xn' = tn[X] ^ Q[X] ^ P(x1...xn) ) => P( x1'...xn' )
     190                 :            :    * Otherwise, it returns TRACE_INC_TERMINATE if the values of
     191                 :            :    * x1' = t1[dt.d_curr] ^ ... ^ xn' = tn[dt.d_curr] have already been executed
     192                 :            :    * on trace dt (the trace has looped), or if
     193                 :            :    * x1' = t1[dt.d_curr] ^ ... ^ xn' = tn[dt.d_curr] ^ Q[dt.d_curr] is unsat
     194                 :            :    * (the trace has terminated). It returns TRACE_INC_CEX if the postcondition
     195                 :            :    * is false for the values t1[dt.d_curr] ^ ... ^ tn[dt.d_curr]. Otherwise,
     196                 :            :    * it returns TRACE_INC_SUCCESS.
     197                 :            :    */
     198                 :            :   TraceIncStatus incrementTrace(DetTrace& dt, bool fwd = true);
     199                 :            :   /**
     200                 :            :    * Constructs the formula corresponding to trace dt with respect to the
     201                 :            :    * variables of this class.
     202                 :            :    */
     203                 :            :   Node constructFormulaTrace(DetTrace& dt) const;
     204                 :            : 
     205                 :            :  private:
     206                 :            :   /**
     207                 :            :    * The function (predicate) that is the subject of the invariant synthesis
     208                 :            :    * problem we are inferring.
     209                 :            :    */
     210                 :            :   Node d_func;
     211                 :            :   /** The variables that the function is applied to */
     212                 :            :   std::vector<Node> d_vars;
     213                 :            :   /**
     214                 :            :    * The variables that the function is applied to in the next state of the
     215                 :            :    * inferred transition relation.
     216                 :            :    */
     217                 :            :   std::vector<Node> d_prime_vars;
     218                 :            :   /**
     219                 :            :    * Was the analysis of the synthesis conjecture passed to the process method
     220                 :            :    * of this class complete?
     221                 :            :    */
     222                 :            :   bool d_complete;
     223                 :            :   /** Was the analyis trivial? */
     224                 :            :   bool d_trivial;
     225                 :            : 
     226                 :            :   /** process disjunct
     227                 :            :    *
     228                 :            :    * The purpose of this function is to infer pre/post/transition conditions
     229                 :            :    * for a (possibly unknown) invariant-to-synthesis, given a conjunct from
     230                 :            :    * an arbitrary synthesis conjecture.
     231                 :            :    *
     232                 :            :    * Assume our negated synthesis conjecture is of the form:
     233                 :            :    *    forall f. exists x. (and (or F11 ... F1{m_1}) ... (or Fn1 ... Fn{m_n}))
     234                 :            :    * This method is called on each (or Fi1 ... Fi{m_i}), where topLevel is true
     235                 :            :    * for each of Fi1...F1{m_i} and false otherwise. It adds each of Fi1..Fi{m_i}
     236                 :            :    * to disjuncts.
     237                 :            :    *
     238                 :            :    * If this method returns true, then (1) all applications of free function
     239                 :            :    * symbols have operator d_func. Note this function may set d_func to a
     240                 :            :    * function symbol in n if d_func was null prior to this call. In other words,
     241                 :            :    * this method may infer the subject of the invariant synthesis problem;
     242                 :            :    * (2) all occurrences of d_func are "top-level", that is, each Fij may be
     243                 :            :    * of the form (not) <d_func>( tj ), but otherwise d_func does not occur in
     244                 :            :    * (or Fi1 ... Fi{m_i}); (3) there exists at most one occurrence of
     245                 :            :    * <d_func>( tj ), and (not <d_func>( tk )).
     246                 :            :    *
     247                 :            :    * If the above conditions are met, then terms[true] is set to <d_func>( tj )
     248                 :            :    * if Fij is <d_func>( tj ) for some j, and likewise terms[false]
     249                 :            :    * is set to <d_func>( tk ) if Fik is (not <d_func>( tk )) for some k.
     250                 :            :    *
     251                 :            :    * The argument visited caches the results of this function for (topLevel, n).
     252                 :            :    */
     253                 :            :   bool processDisjunct(Node n,
     254                 :            :                        std::map<bool, Node>& terms,
     255                 :            :                        std::vector<Node>& disjuncts,
     256                 :            :                        std::map<bool, std::map<Node, bool> >& visited,
     257                 :            :                        bool topLevel);
     258                 :            :   /**
     259                 :            :    * This method infers if the conjunction of disjuncts is equivalent to a
     260                 :            :    * conjunction of the form
     261                 :            :    *   (~) const_var[1] = const_subs[1] ... (~) const_var[n] = const_subs[n]
     262                 :            :    * where the above equalities are negated iff reqPol is false, and
     263                 :            :    *   const_var[1] ... const_var[n]
     264                 :            :    * are distinct members of vars
     265                 :            :    */
     266                 :            :   void getConstantSubstitution(const std::vector<Node>& vars,
     267                 :            :                                const std::vector<Node>& disjuncts,
     268                 :            :                                std::vector<Node>& const_var,
     269                 :            :                                std::vector<Node>& const_subs,
     270                 :            :                                bool reqPol);
     271                 :            :   /** get normalized substitution
     272                 :            :    *
     273                 :            :    * This method takes as input a node curr of the form I( t1, ..., tn ) and
     274                 :            :    * a vector of variables pvars, where pvars.size()=n. For each ti that is
     275                 :            :    * a variable, it adds ti to vars, and pvars[i] to subs. For each ti that is
     276                 :            :    * not a variable, it adds the disequality ti != pvars[i] to disjuncts.
     277                 :            :    *
     278                 :            :    * This function is used for instance to normalize an arbitrary application of
     279                 :            :    * I so that is over arguments pvars. For instance if curr is I(3,5,y) and
     280                 :            :    * pvars = { x1,x2,x3 }, then the formula:
     281                 :            :    *   I(3,5,y) ^ P(y)
     282                 :            :    * is equivalent to:
     283                 :            :    *   x1 != 3 V x2 != 5 V I(x1,x2,x3) V P( y ) { y -> x3 }
     284                 :            :    * Here, we add y and x3 to vars and subs respectively, and x1!=3 and x2!=5
     285                 :            :    * to disjuncts.
     286                 :            :    */
     287                 :            :   void getNormalizedSubstitution(Node curr,
     288                 :            :                                  const std::vector<Node>& pvars,
     289                 :            :                                  std::vector<Node>& vars,
     290                 :            :                                  std::vector<Node>& subs,
     291                 :            :                                  std::vector<Node>& disjuncts);
     292                 :            :   /**
     293                 :            :    * Stores one of the components of the inferred form of the synthesis
     294                 :            :    * conjecture (precondition, postcondition, or transition relation).
     295                 :            :    */
     296                 :            :   class Component
     297                 :            :   {
     298                 :            :    public:
     299                 :      27261 :     Component() {}
     300                 :            :     /** The formula that was inferred for this component */
     301                 :            :     Node d_this;
     302                 :            :     /** The list of conjuncts of the above formula */
     303                 :            :     std::vector<Node> d_conjuncts;
     304                 :            :     /**
     305                 :            :      * Maps formulas to the constant equality substitution that it entails.
     306                 :            :      * For example, the formula (x=4 ^ y=x+5) may map to { x -> 4, y -> 9 }.
     307                 :            :      */
     308                 :            :     std::map<Node, std::map<Node, Node> > d_const_eq;
     309                 :            :     /** Does this component have conjunct c? */
     310                 :        666 :     bool has(Node c) const
     311                 :            :     {
     312                 :        666 :       return std::find(d_conjuncts.begin(), d_conjuncts.end(), c)
     313                 :       1332 :              != d_conjuncts.end();
     314                 :            :     }
     315                 :            :   };
     316                 :            :   /** Components for the pre/post condition and transition relation. */
     317                 :            :   Component d_pre;
     318                 :            :   Component d_post;
     319                 :            :   Component d_trans;
     320                 :            :   /**
     321                 :            :    * Initialize trace dt, loc is a node to identify the trace, fwd is whether
     322                 :            :    * we are going in the forward direction of the transition system (starting
     323                 :            :    * from the precondition).
     324                 :            :    *
     325                 :            :    * The argument loc is a conjunct of transition relation that entails that the
     326                 :            :    * trace dt has executed in its last step to its current value. For example,
     327                 :            :    * if the transition relation is ( x'=x+1 ^ P( x ) ) => P(x'), and our trace's
     328                 :            :    * current value was last updated [x:=1] -> [x:=2] based on x'=x+1, then loc
     329                 :            :    * is the node x'=x+1.
     330                 :            :    */
     331                 :            :   TraceIncStatus initializeTrace(DetTrace& dt, Node loc, bool fwd = true);
     332                 :            :   /** Same as above, for incrementing the trace dt */
     333                 :            :   TraceIncStatus incrementTrace(DetTrace& dt, Node loc, bool fwd = true);
     334                 :            : };
     335                 :            : 
     336                 :            : }  // namespace quantifiers
     337                 :            : }  // namespace theory
     338                 :            : }  // namespace cvc5::internal
     339                 :            : 
     340                 :            : #endif

Generated by: LCOV version 1.14