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: 2026-03-02 11:31:58 Functions: 6 6 100.0 %
Branches: 0 0 -

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

Generated by: LCOV version 1.14