1 /********************* */
2 /*! \file ho_extension.cpp
4 ** Top contributors (to current version):
5 ** Andrew Reynolds, Mathias Preiner
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2020 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.\endverbatim
12 ** \brief Implementation of the higher-order extension of TheoryUF.
16 #include "theory/uf/ho_extension.h"
18 #include "expr/node_algorithm.h"
19 #include "options/uf_options.h"
20 #include "theory/theory_model.h"
21 #include "theory/uf/theory_uf_rewriter.h"
24 using namespace CVC4::kind
;
30 HoExtension::HoExtension(TheoryState
& state
, TheoryInferenceManager
& im
)
33 d_extensionality(state
.getUserContext()),
34 d_uf_std_skolem(state
.getUserContext())
36 d_true
= NodeManager::currentNM()->mkConst(true);
39 Node
HoExtension::ppRewrite(Node node
)
41 // convert HO_APPLY to APPLY_UF if fully applied
42 if (node
[0].getType().getNumChildren() == 2)
44 Trace("uf-ho") << "uf-ho : expanding definition : " << node
<< std::endl
;
45 Node ret
= getApplyUfForHoApply(node
);
46 Trace("uf-ho") << "uf-ho : ppRewrite : " << node
<< " to " << ret
53 Node
HoExtension::getExtensionalityDeq(TNode deq
, bool isCached
)
55 Assert(deq
.getKind() == NOT
&& deq
[0].getKind() == EQUAL
);
56 Assert(deq
[0][0].getType().isFunction());
59 std::map
<Node
, Node
>::iterator it
= d_extensionality_deq
.find(deq
);
60 if (it
!= d_extensionality_deq
.end())
65 TypeNode tn
= deq
[0][0].getType();
66 std::vector
<TypeNode
> argTypes
= tn
.getArgTypes();
67 std::vector
<Node
> skolems
;
68 NodeManager
* nm
= NodeManager::currentNM();
69 for (unsigned i
= 0, nargs
= argTypes
.size(); i
< nargs
; i
++)
72 nm
->mkSkolem("k", argTypes
[i
], "skolem created for extensionality.");
76 for (unsigned i
= 0; i
< 2; i
++)
78 std::vector
<Node
> children
;
79 Node curr
= deq
[0][i
];
80 while (curr
.getKind() == HO_APPLY
)
82 children
.push_back(curr
[1]);
85 children
.push_back(curr
);
86 std::reverse(children
.begin(), children
.end());
87 children
.insert(children
.end(), skolems
.begin(), skolems
.end());
88 t
[i
] = nm
->mkNode(APPLY_UF
, children
);
90 Node conc
= t
[0].eqNode(t
[1]).negate();
93 d_extensionality_deq
[deq
] = conc
;
98 unsigned HoExtension::applyExtensionality(TNode deq
)
100 Assert(deq
.getKind() == NOT
&& deq
[0].getKind() == EQUAL
);
101 Assert(deq
[0][0].getType().isFunction());
102 // apply extensionality
103 if (d_extensionality
.find(deq
) == d_extensionality
.end())
105 d_extensionality
.insert(deq
);
106 Node conc
= getExtensionalityDeq(deq
);
107 Node lem
= NodeManager::currentNM()->mkNode(OR
, deq
[0], conc
);
108 Trace("uf-ho-lemma") << "uf-ho-lemma : extensionality : " << lem
116 Node
HoExtension::getApplyUfForHoApply(Node node
)
118 Assert(node
[0].getType().getNumChildren() == 2);
119 std::vector
<TNode
> args
;
120 Node f
= TheoryUfRewriter::decomposeHoApply(node
, args
, true);
122 NodeManager
* nm
= NodeManager::currentNM();
123 if (!TheoryUfRewriter::canUseAsApplyUfOperator(f
))
125 NodeNodeMap::const_iterator itus
= d_uf_std_skolem
.find(f
);
126 if (itus
== d_uf_std_skolem
.end())
128 std::unordered_set
<Node
, NodeHashFunction
> fvs
;
129 expr::getFreeVariables(f
, fvs
);
133 std::vector
<TypeNode
> newTypes
;
134 std::vector
<Node
> vs
;
135 std::vector
<Node
> nvs
;
136 for (const Node
& v
: fvs
)
138 TypeNode vt
= v
.getType();
139 newTypes
.push_back(vt
);
140 Node nv
= nm
->mkBoundVar(vt
);
144 TypeNode ft
= f
.getType();
145 std::vector
<TypeNode
> argTypes
= ft
.getArgTypes();
146 TypeNode rangeType
= ft
.getRangeType();
148 newTypes
.insert(newTypes
.end(), argTypes
.begin(), argTypes
.end());
149 TypeNode nft
= nm
->mkFunctionType(newTypes
, rangeType
);
150 new_f
= nm
->mkSkolem("app_uf", nft
);
151 for (const Node
& v
: vs
)
153 new_f
= nm
->mkNode(HO_APPLY
, new_f
, v
);
155 Assert(new_f
.getType() == f
.getType());
156 Node eq
= new_f
.eqNode(f
);
157 Node seq
= eq
.substitute(vs
.begin(), vs
.end(), nvs
.begin(), nvs
.end());
159 FORALL
, nm
->mkNode(BOUND_VAR_LIST
, nvs
), seq
);
163 // introduce skolem to make a standard APPLY_UF
164 new_f
= nm
->mkSkolem("app_uf", f
.getType());
165 lem
= new_f
.eqNode(f
);
168 << "uf-ho-lemma : Skolem definition for apply-conversion : " << lem
171 d_uf_std_skolem
[f
] = new_f
;
175 new_f
= (*itus
).second
;
177 // unroll the HO_APPLY, adding to the first argument position
178 // Note arguments in the vector args begin at position 1.
179 while (new_f
.getKind() == HO_APPLY
)
181 args
.insert(args
.begin() + 1, new_f
[1]);
185 Assert(TheoryUfRewriter::canUseAsApplyUfOperator(new_f
));
187 Node ret
= nm
->mkNode(APPLY_UF
, args
);
188 Assert(ret
.getType() == node
.getType());
192 unsigned HoExtension::checkExtensionality(TheoryModel
* m
)
194 eq::EqualityEngine
* ee
= d_state
.getEqualityEngine();
195 NodeManager
* nm
= NodeManager::currentNM();
196 unsigned num_lemmas
= 0;
197 bool isCollectModel
= (m
!= nullptr);
198 Trace("uf-ho") << "HoExtension::checkExtensionality, collectModel="
199 << isCollectModel
<< "..." << std::endl
;
200 std::map
<TypeNode
, std::vector
<Node
> > func_eqcs
;
201 eq::EqClassesIterator eqcs_i
= eq::EqClassesIterator(ee
);
202 while (!eqcs_i
.isFinished())
204 Node eqc
= (*eqcs_i
);
205 TypeNode tn
= eqc
.getType();
208 // if during collect model, must have an infinite type
209 // if not during collect model, must have a finite type
210 if (tn
.isInterpretedFinite() != isCollectModel
)
212 func_eqcs
[tn
].push_back(eqc
);
214 << " func eqc : " << tn
<< " : " << eqc
<< std::endl
;
220 for (std::map
<TypeNode
, std::vector
<Node
> >::iterator itf
= func_eqcs
.begin();
221 itf
!= func_eqcs
.end();
224 for (unsigned j
= 0, sizej
= itf
->second
.size(); j
< sizej
; j
++)
226 for (unsigned k
= (j
+ 1), sizek
= itf
->second
.size(); k
< sizek
; k
++)
228 // if these equivalence classes are not explicitly disequal, do
229 // extensionality to ensure distinctness
230 if (!ee
->areDisequal(itf
->second
[j
], itf
->second
[k
], false))
233 Rewriter::rewrite(itf
->second
[j
].eqNode(itf
->second
[k
]).negate());
234 // either add to model, or add lemma
237 // Add extentionality disequality to the model.
238 // It is important that we construct new (unconstrained) variables
239 // k here, so that we do not generate any inconsistencies.
240 Node edeq
= getExtensionalityDeq(deq
, false);
241 Assert(edeq
.getKind() == NOT
&& edeq
[0].getKind() == EQUAL
);
242 // introducing terms, must add required constraints, e.g. to
243 // force equalities between APPLY_UF and HO_APPLY terms
244 for (unsigned r
= 0; r
< 2; r
++)
246 if (!collectModelInfoHoTerm(edeq
[0][r
], m
))
252 << "Add extensionality deq to model : " << edeq
<< std::endl
;
253 if (!m
->assertEquality(edeq
[0][0], edeq
[0][1], false))
255 Node eq
= edeq
[0][0].eqNode(edeq
[0][1]);
256 Node lem
= nm
->mkNode(OR
, deq
.negate(), eq
);
257 Trace("uf-ho") << "HoExtension: cmi extensionality lemma " << lem
265 // apply extensionality lemma
266 num_lemmas
+= applyExtensionality(deq
);
275 unsigned HoExtension::applyAppCompletion(TNode n
)
277 Assert(n
.getKind() == APPLY_UF
);
279 eq::EqualityEngine
* ee
= d_state
.getEqualityEngine();
280 // must expand into APPLY_HO version if not there already
281 Node ret
= TheoryUfRewriter::getHoApplyForApplyUf(n
);
282 if (!ee
->hasTerm(ret
) || !ee
->areEqual(ret
, n
))
284 Node eq
= n
.eqNode(ret
);
285 Trace("uf-ho-lemma") << "uf-ho-lemma : infer, by apply-expand : " << eq
287 d_im
.assertInternalFact(eq
, true, PfRule::HO_APP_ENCODE
, {}, {n
});
290 Trace("uf-ho-debug") << " ...already have " << ret
<< " == " << n
<< "."
295 unsigned HoExtension::checkAppCompletion()
297 Trace("uf-ho") << "HoExtension::checkApplyCompletion..." << std::endl
;
298 // compute the operators that are relevant (those for which an HO_APPLY exist)
299 std::set
<TNode
> rlvOp
;
300 eq::EqualityEngine
* ee
= d_state
.getEqualityEngine();
301 eq::EqClassesIterator eqcs_i
= eq::EqClassesIterator(ee
);
302 std::map
<TNode
, std::vector
<Node
> > apply_uf
;
303 while (!eqcs_i
.isFinished())
305 Node eqc
= (*eqcs_i
);
306 Trace("uf-ho-debug") << " apply completion : visit eqc " << eqc
308 eq::EqClassIterator eqc_i
= eq::EqClassIterator(eqc
, ee
);
309 while (!eqc_i
.isFinished())
312 if (n
.getKind() == APPLY_UF
|| n
.getKind() == HO_APPLY
)
315 std::map
<TNode
, bool> curr_rops
;
316 if (n
.getKind() == APPLY_UF
)
318 TNode rop
= ee
->getRepresentative(n
.getOperator());
319 if (rlvOp
.find(rop
) != rlvOp
.end())
321 // try if its operator is relevant
322 curr_sum
= applyAppCompletion(n
);
330 // add to pending list
331 apply_uf
[rop
].push_back(n
);
333 // Arguments are also relevant operators.
334 // It might be possible include fewer terms here, see #1115.
335 for (unsigned k
= 0; k
< n
.getNumChildren(); k
++)
337 if (n
[k
].getType().isFunction())
339 TNode rop2
= ee
->getRepresentative(n
[k
]);
340 curr_rops
[rop2
] = true;
346 Assert(n
.getKind() == HO_APPLY
);
347 TNode rop
= ee
->getRepresentative(n
[0]);
348 curr_rops
[rop
] = true;
350 for (std::map
<TNode
, bool>::iterator itc
= curr_rops
.begin();
351 itc
!= curr_rops
.end();
354 TNode rop
= itc
->first
;
355 if (rlvOp
.find(rop
) == rlvOp
.end())
358 // now, try each pending APPLY_UF for this operator
359 std::map
<TNode
, std::vector
<Node
> >::iterator itu
=
361 if (itu
!= apply_uf
.end())
363 for (unsigned j
= 0, size
= itu
->second
.size(); j
< size
; j
++)
365 curr_sum
= applyAppCompletion(itu
->second
[j
]);
382 unsigned HoExtension::check()
384 Trace("uf-ho") << "HoExtension::checkHigherOrder..." << std::endl
;
386 // infer new facts based on apply completion until fixed point
390 num_facts
= checkAppCompletion();
391 if (d_state
.isInConflict())
393 Trace("uf-ho") << "...conflict during app-completion." << std::endl
;
396 } while (num_facts
> 0);
398 if (options::ufHoExt())
400 unsigned num_lemmas
= 0;
402 num_lemmas
= checkExtensionality();
405 Trace("uf-ho") << "...extensionality returned " << num_lemmas
406 << " lemmas." << std::endl
;
411 Trace("uf-ho") << "...finished check higher order." << std::endl
;
416 bool HoExtension::collectModelInfoHo(TheoryModel
* m
,
417 const std::set
<Node
>& termSet
)
419 for (std::set
<Node
>::iterator it
= termSet
.begin(); it
!= termSet
.end(); ++it
)
422 // For model-building with ufHo, we require that APPLY_UF is always
423 // expanded to HO_APPLY. That is, we always expand to a fully applicative
424 // encoding during model construction.
425 if (!collectModelInfoHoTerm(n
, m
))
430 int addedLemmas
= checkExtensionality(m
);
431 return addedLemmas
== 0;
434 bool HoExtension::collectModelInfoHoTerm(Node n
, TheoryModel
* m
)
436 if (n
.getKind() == APPLY_UF
)
438 Node hn
= TheoryUfRewriter::getHoApplyForApplyUf(n
);
439 if (!m
->assertEquality(n
, hn
, true))
441 Node eq
= n
.eqNode(hn
);
442 Trace("uf-ho") << "HoExtension: cmi app completion lemma " << eq
452 } // namespace theory