GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/sygus/sygus_interpol.cpp Lines: 152 180 84.4 %
Date: 2021-11-07 Branches: 271 674 40.2 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Ying Sheng, Abdalrhman Mohamed, Andrew Reynolds
4
 *
5
 * This file is part of the cvc5 project.
6
 *
7
 * Copyright (c) 2009-2021 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
 * Implementation of sygus interpolation utility, which transforms an input of
14
 * axioms and conjecture into an interpolation problem, and solve it.
15
 */
16
17
#include "theory/quantifiers/sygus/sygus_interpol.h"
18
19
#include <sstream>
20
21
#include "base/modal_exception.h"
22
#include "expr/dtype.h"
23
#include "expr/node_algorithm.h"
24
#include "options/smt_options.h"
25
#include "smt/env.h"
26
#include "theory/datatypes/sygus_datatype_utils.h"
27
#include "theory/quantifiers/quantifiers_attributes.h"
28
#include "theory/quantifiers/sygus/sygus_grammar_cons.h"
29
#include "theory/smt_engine_subsolver.h"
30
31
namespace cvc5 {
32
namespace theory {
33
namespace quantifiers {
34
35
10
SygusInterpol::SygusInterpol(Env& env) : EnvObj(env) {}
36
37
10
void SygusInterpol::collectSymbols(const std::vector<Node>& axioms,
38
                                   const Node& conj)
39
{
40
10
  Trace("sygus-interpol-debug") << "Collect symbols..." << std::endl;
41
20
  std::unordered_set<Node> symSetAxioms;
42
20
  std::unordered_set<Node> symSetConj;
43
35
  for (size_t i = 0, size = axioms.size(); i < size; i++)
44
  {
45
25
    expr::getSymbols(axioms[i], symSetAxioms);
46
  }
47
10
  expr::getSymbols(conj, symSetConj);
48
10
  d_syms.insert(d_syms.end(), symSetAxioms.begin(), symSetAxioms.end());
49
10
  d_syms.insert(d_syms.end(), symSetConj.begin(), symSetConj.end());
50
36
  for (const Node& elem : symSetConj)
51
  {
52
26
    if (symSetAxioms.find(elem) != symSetAxioms.end())
53
    {
54
16
      d_symSetShared.insert(elem);
55
    }
56
  }
57
20
  Trace("sygus-interpol-debug")
58
20
      << "...finish, got " << d_syms.size() << " symbols in total. And "
59
10
      << d_symSetShared.size() << " shared symbols." << std::endl;
60
10
}
61
62
10
void SygusInterpol::createVariables(bool needsShared)
63
{
64
10
  NodeManager* nm = NodeManager::currentNM();
65
113
  for (const Node& s : d_syms)
66
  {
67
206
    TypeNode tn = s.getType();
68
103
    if (tn.isConstructor() || tn.isSelector() || tn.isTester())
69
    {
70
      // datatype symbols should be considered interpreted symbols here, not
71
      // (higher-order) variables.
72
      continue;
73
    }
74
    // Notice that we allow for non-first class (e.g. function) variables here.
75
206
    std::stringstream ss;
76
103
    ss << s;
77
206
    Node var = nm->mkBoundVar(tn);
78
103
    d_vars.push_back(var);
79
206
    Node vlv = nm->mkBoundVar(ss.str(), tn);
80
    // set that this variable encodes the term s
81
    SygusVarToTermAttribute sta;
82
103
    vlv.setAttribute(sta, s);
83
103
    d_vlvs.push_back(vlv);
84
103
    if (!needsShared || d_symSetShared.find(s) != d_symSetShared.end())
85
    {
86
34
      d_varsShared.push_back(var);
87
34
      d_vlvsShared.push_back(vlv);
88
34
      d_varTypesShared.push_back(tn);
89
    }
90
  }
91
  // make the sygus variable list
92
10
  d_ibvlShared = nm->mkNode(kind::BOUND_VAR_LIST, d_vlvsShared);
93
10
  Trace("sygus-interpol-debug") << "...finish" << std::endl;
94
10
}
95
96
9
void SygusInterpol::getIncludeCons(
97
    const std::vector<Node>& axioms,
98
    const Node& conj,
99
    std::map<TypeNode, std::unordered_set<Node>>& result)
100
{
101
9
  NodeManager* nm = NodeManager::currentNM();
102
9
  Assert(options().smt.produceInterpols != options::ProduceInterpols::NONE);
103
  // ASSUMPTIONS
104
9
  if (options().smt.produceInterpols == options::ProduceInterpols::ASSUMPTIONS)
105
  {
106
    Node tmpAssumptions =
107
        (axioms.size() == 1 ? axioms[0] : nm->mkNode(kind::AND, axioms));
108
    expr::getOperatorsMap(tmpAssumptions, result);
109
  }
110
  // CONJECTURE
111
18
  else if (options().smt.produceInterpols
112
9
           == options::ProduceInterpols::CONJECTURE)
113
  {
114
1
    expr::getOperatorsMap(conj, result);
115
  }
116
  // SHARED
117
8
  else if (options().smt.produceInterpols == options::ProduceInterpols::SHARED)
118
  {
119
    // Get operators from axioms
120
    std::map<TypeNode, std::unordered_set<Node>> include_cons_axioms;
121
    Node tmpAssumptions =
122
        (axioms.size() == 1 ? axioms[0] : nm->mkNode(kind::AND, axioms));
123
    expr::getOperatorsMap(tmpAssumptions, include_cons_axioms);
124
125
    // Get operators from conj
126
    std::map<TypeNode, std::unordered_set<Node>> include_cons_conj;
127
    expr::getOperatorsMap(conj, include_cons_conj);
128
129
    // Compute intersection
130
    for (std::map<TypeNode, std::unordered_set<Node>>::iterator it =
131
             include_cons_axioms.begin();
132
         it != include_cons_axioms.end();
133
         it++)
134
    {
135
      TypeNode tn = it->first;
136
      std::unordered_set<Node> axiomsOps = it->second;
137
      std::map<TypeNode, std::unordered_set<Node>>::iterator concIter =
138
          include_cons_conj.find(tn);
139
      if (concIter != include_cons_conj.end())
140
      {
141
        std::unordered_set<Node> conjOps = concIter->second;
142
        for (const Node& n : axiomsOps)
143
        {
144
          if (conjOps.find(n) != conjOps.end())
145
          {
146
            if (result.find(tn) == result.end())
147
            {
148
              result[tn] = std::unordered_set<Node>();
149
            }
150
            result[tn].insert(n);
151
          }
152
        }
153
      }
154
    }
155
  }
156
  // ALL
157
8
  else if (options().smt.produceInterpols == options::ProduceInterpols::ALL)
158
  {
159
    Node tmpAssumptions =
160
        (axioms.size() == 1 ? axioms[0] : nm->mkNode(kind::AND, axioms));
161
    Node tmpAll = nm->mkNode(kind::AND, tmpAssumptions, conj);
162
    expr::getOperatorsMap(tmpAll, result);
163
  }
164
9
}
165
166
10
TypeNode SygusInterpol::setSynthGrammar(const TypeNode& itpGType,
167
                                        const std::vector<Node>& axioms,
168
                                        const Node& conj)
169
{
170
10
  Trace("sygus-interpol-debug") << "Setup grammar..." << std::endl;
171
10
  TypeNode itpGTypeS;
172
10
  if (!itpGType.isNull())
173
  {
174
    // set user-defined grammar
175
1
    Assert(itpGType.isDatatype() && itpGType.getDType().isSygus());
176
1
    itpGTypeS = datatypes::utils::substituteAndGeneralizeSygusType(
177
        itpGType, d_syms, d_vlvs);
178
1
    Assert(itpGTypeS.isDatatype() && itpGTypeS.getDType().isSygus());
179
    // TODO(Ying Sheng) check if the vars in user-defined grammar, are
180
    // consistent with the shared vars
181
  }
182
  else
183
  {
184
    // set default grammar
185
18
    std::map<TypeNode, std::unordered_set<Node>> extra_cons;
186
18
    std::map<TypeNode, std::unordered_set<Node>> exclude_cons;
187
18
    std::map<TypeNode, std::unordered_set<Node>> include_cons;
188
9
    getIncludeCons(axioms, conj, include_cons);
189
18
    std::unordered_set<Node> terms_irrelevant;
190
27
    itpGTypeS = CegGrammarConstructor::mkSygusDefaultType(
191
18
        NodeManager::currentNM()->booleanType(),
192
        d_ibvlShared,
193
        "interpolation_grammar",
194
        extra_cons,
195
        exclude_cons,
196
        include_cons,
197
        terms_irrelevant);
198
  }
199
10
  Trace("sygus-interpol-debug") << "...finish setting up grammar" << std::endl;
200
10
  return itpGTypeS;
201
}
202
203
10
Node SygusInterpol::mkPredicate(const std::string& name)
204
{
205
10
  NodeManager* nm = NodeManager::currentNM();
206
  // make the interpolation predicate to synthesize
207
20
  Trace("sygus-interpol-debug")
208
10
      << "Make interpolation predicate..." << std::endl;
209
10
  TypeNode itpType = d_varTypesShared.empty()
210
                         ? nm->booleanType()
211
20
                         : nm->mkPredicateType(d_varTypesShared);
212
10
  Node itp = nm->mkBoundVar(name.c_str(), itpType);
213
10
  Trace("sygus-interpol-debug") << "...finish" << std::endl;
214
20
  return itp;
215
}
216
217
10
void SygusInterpol::mkSygusConjecture(Node itp,
218
                                      const std::vector<Node>& axioms,
219
                                      const Node& conj)
220
{
221
10
  NodeManager* nm = NodeManager::currentNM();
222
  // make the interpolation application to synthesize
223
20
  Trace("sygus-interpol-debug")
224
10
      << "Make interpolation predicate app..." << std::endl;
225
20
  std::vector<Node> ichildren;
226
10
  ichildren.push_back(itp);
227
10
  ichildren.insert(ichildren.end(), d_varsShared.begin(), d_varsShared.end());
228
  Node itpApp =
229
20
      d_varsShared.empty() ? itp : nm->mkNode(kind::APPLY_UF, ichildren);
230
20
  Trace("sygus-interpol-debug") << "itpApp: " << itpApp << std::endl
231
10
                                << std::endl;
232
10
  Trace("sygus-interpol-debug") << "...finish" << std::endl;
233
234
  // set the sygus bound variable list
235
10
  Trace("sygus-interpol-debug") << "Set attributes..." << std::endl;
236
10
  itp.setAttribute(SygusSynthFunVarListAttribute(), d_ibvlShared);
237
10
  Trace("sygus-interpol-debug") << "...finish" << std::endl;
238
239
  // Fa( x )
240
10
  Trace("sygus-interpol-debug") << "Make conjecture body..." << std::endl;
241
20
  Node Fa = axioms.size() == 1 ? axioms[0] : nm->mkNode(kind::AND, axioms);
242
  // Fa( x ) => A( x )
243
20
  Node firstImplication = nm->mkNode(kind::IMPLIES, Fa, itpApp);
244
20
  Trace("sygus-interpol-debug")
245
10
      << "first implication: " << firstImplication << std::endl
246
10
      << std::endl;
247
  // A( x ) => Fc( x )
248
20
  Node Fc = conj;
249
20
  Node secondImplication = nm->mkNode(kind::IMPLIES, itpApp, Fc);
250
20
  Trace("sygus-interpol-debug")
251
10
      << "second implication: " << secondImplication << std::endl
252
10
      << std::endl;
253
  // Fa( x ) => A( x ) ^ A( x ) => Fc( x )
254
20
  Node constraint = nm->mkNode(kind::AND, firstImplication, secondImplication);
255
10
  constraint = constraint.substitute(
256
      d_syms.begin(), d_syms.end(), d_vars.begin(), d_vars.end());
257
10
  Trace("sygus-interpol-debug") << constraint << "...finish" << std::endl;
258
10
  constraint = rewrite(constraint);
259
260
10
  d_sygusConj = constraint;
261
10
  Trace("sygus-interpol") << "Generate: " << d_sygusConj << std::endl;
262
10
}
263
264
10
bool SygusInterpol::findInterpol(SolverEngine* subSolver,
265
                                 Node& interpol,
266
                                 Node itp)
267
{
268
  // get the synthesis solution
269
20
  std::map<Node, Node> sols;
270
10
  subSolver->getSynthSolutions(sols);
271
10
  Assert(sols.size() == 1);
272
10
  std::map<Node, Node>::iterator its = sols.find(itp);
273
10
  if (its == sols.end())
274
  {
275
    Trace("sygus-interpol")
276
        << "SolverEngine::getInterpol: could not find solution!" << std::endl;
277
    throw RecoverableModalException(
278
        "Could not find solution for get-interpol.");
279
    return false;
280
  }
281
20
  Trace("sygus-interpol") << "SolverEngine::getInterpol: solution is "
282
10
                          << its->second << std::endl;
283
10
  interpol = its->second;
284
  // replace back the created variables to original symbols.
285
10
  if (interpol.getKind() == kind::LAMBDA)
286
  {
287
10
    interpol = interpol[1];
288
  }
289
290
  // get the grammar type for the interpolant
291
20
  Node igdtbv = itp.getAttribute(SygusSynthFunVarListAttribute());
292
10
  Assert(!igdtbv.isNull());
293
10
  Assert(igdtbv.getKind() == kind::BOUND_VAR_LIST);
294
  // convert back to original
295
  // must replace formal arguments of itp with the free variables in the
296
  // input problem that they correspond to.
297
20
  std::vector<Node> vars;
298
20
  std::vector<Node> syms;
299
  SygusVarToTermAttribute sta;
300
44
  for (const Node& bv : igdtbv)
301
  {
302
34
    vars.push_back(bv);
303
34
    syms.push_back(bv.hasAttribute(sta) ? bv.getAttribute(sta) : bv);
304
  }
305
10
  interpol =
306
20
      interpol.substitute(vars.begin(), vars.end(), syms.begin(), syms.end());
307
308
10
  return true;
309
}
310
311
10
bool SygusInterpol::solveInterpolation(const std::string& name,
312
                                       const std::vector<Node>& axioms,
313
                                       const Node& conj,
314
                                       const TypeNode& itpGType,
315
                                       Node& interpol)
316
{
317
  // Some instructions in setSynthGrammar and mkSygusConjecture need a fully
318
  // initialized solver to work properly. Notice, however, that the sub-solver
319
  // created below is not fully initialized by the time those two methods are
320
  // needed. Therefore, we call them while the current parent solver is in scope
321
  // (i.e., before creating the sub-solver).
322
10
  collectSymbols(axioms, conj);
323
10
  createVariables(itpGType.isNull());
324
20
  TypeNode grammarType = setSynthGrammar(itpGType, axioms, conj);
325
326
20
  Node itp = mkPredicate(name);
327
10
  mkSygusConjecture(itp, axioms, conj);
328
329
20
  std::unique_ptr<SolverEngine> subSolver;
330
10
  initializeSubsolver(subSolver, d_env);
331
  // get the logic
332
20
  LogicInfo l = subSolver->getLogicInfo().getUnlockedCopy();
333
  // enable everything needed for sygus
334
10
  l.enableSygus();
335
10
  subSolver->setLogic(l);
336
337
113
  for (const Node& var : d_vars)
338
  {
339
103
    subSolver->declareSygusVar(var);
340
  }
341
20
  std::vector<Node> vars_empty;
342
10
  subSolver->declareSynthFun(itp, grammarType, false, vars_empty);
343
20
  Trace("sygus-interpol") << "SolverEngine::getInterpol: made conjecture : "
344
10
                          << d_sygusConj << ", solving for "
345
10
                          << d_sygusConj[0][0] << std::endl;
346
10
  subSolver->assertSygusConstraint(d_sygusConj);
347
348
20
  Trace("sygus-interpol") << "  SolverEngine::getInterpol check sat..."
349
10
                          << std::endl;
350
20
  Result r = subSolver->checkSynth();
351
20
  Trace("sygus-interpol") << "  SolverEngine::getInterpol result: " << r
352
10
                          << std::endl;
353
10
  if (r.asSatisfiabilityResult().isSat() == Result::UNSAT)
354
  {
355
10
    return findInterpol(subSolver.get(), interpol, itp);
356
  }
357
  return false;
358
}
359
360
}  // namespace quantifiers
361
}  // namespace theory
362
31137
}  // namespace cvc5