1 /********************* */
2 /*! \file sygus_simple_sym.cpp
4 ** Top contributors (to current version):
5 ** Andrew Reynolds, Haniel Barbosa, 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 simple symmetry breaking for sygus
15 #include "theory/datatypes/sygus_simple_sym.h"
17 #include "theory/quantifiers_engine.h"
20 using namespace CVC4::kind
;
26 SygusSimpleSymBreak::SygusSimpleSymBreak(QuantifiersEngine
* qe
)
27 : d_tds(qe
->getTermDatabaseSygus()), d_tutil(qe
->getTermUtil())
33 * This class is used to make queries about sygus grammars, including what
34 * constructors they contain, and their types.
36 * As a simple example, consider the trie:
43 * This trie is satisfied by sygus types that have a constructor whose builtin
44 * kind is PLUS and whose argument types are both A.
49 ReqTrie() : d_req_kind(UNDEFINED_KIND
) {}
50 /** the children of this node */
51 std::map
<unsigned, ReqTrie
> d_children
;
52 /** the (builtin) kind required by this node */
54 /** the type that this node is required to be */
56 /** the constant required by this node */
58 /** print this trie */
59 void print(const char* c
, int indent
= 0)
61 if (d_req_kind
!= UNDEFINED_KIND
)
63 Trace(c
) << d_req_kind
<< " ";
65 else if (!d_req_type
.isNull())
67 Trace(c
) << d_req_type
;
69 else if (!d_req_const
.isNull())
71 Trace(c
) << d_req_const
;
77 Trace(c
) << std::endl
;
78 for (std::map
<unsigned, ReqTrie
>::iterator it
= d_children
.begin();
79 it
!= d_children
.end();
82 for (int i
= 0; i
<= indent
; i
++)
86 Trace(c
) << it
->first
<< " : ";
87 it
->second
.print(c
, indent
+ 1);
91 * Are the requirements of this trie satisfied by sygus type tn?
92 * tdb is a reference to the sygus term database.
94 bool satisfiedBy(quantifiers::TermDbSygus
* tdb
, TypeNode tn
)
96 if (!d_req_const
.isNull())
98 quantifiers::SygusTypeInfo
& sti
= tdb
->getTypeInfo(tn
);
99 if (!sti
.hasConst(d_req_const
))
104 if (!d_req_type
.isNull())
106 Trace("sygus-sb-debug")
107 << "- check if " << tn
<< " is type " << d_req_type
<< std::endl
;
108 if (tn
!= d_req_type
)
113 if (d_req_kind
!= UNDEFINED_KIND
)
115 Trace("sygus-sb-debug")
116 << "- check if " << tn
<< " has " << d_req_kind
<< std::endl
;
117 std::vector
<TypeNode
> argts
;
118 if (tdb
->canConstructKind(tn
, d_req_kind
, argts
))
120 for (std::map
<unsigned, ReqTrie
>::iterator it
= d_children
.begin();
121 it
!= d_children
.end();
124 if (it
->first
< argts
.size())
126 TypeNode tnc
= argts
[it
->first
];
127 if (!it
->second
.satisfiedBy(tdb
, tnc
))
145 /** is this the empty (trivially satisfied) trie? */
148 return d_req_kind
== UNDEFINED_KIND
&& d_req_const
.isNull()
149 && d_req_type
.isNull() && d_children
.empty();
153 bool SygusSimpleSymBreak::considerArgKind(
154 TypeNode tn
, TypeNode tnp
, Kind k
, Kind pk
, int arg
)
156 const DType
& pdt
= tnp
.getDType();
157 const DType
& dt
= tn
.getDType();
158 quantifiers::SygusTypeInfo
& ti
= d_tds
->getTypeInfo(tn
);
159 quantifiers::SygusTypeInfo
& pti
= d_tds
->getTypeInfo(tnp
);
160 Assert(ti
.hasKind(k
));
161 Assert(pti
.hasKind(pk
));
162 Trace("sygus-sb-debug") << "Consider sygus arg kind " << k
<< ", pk = " << pk
163 << ", arg = " << arg
<< " in " << tnp
<< "?"
165 int c
= ti
.getKindConsNum(k
);
166 int pc
= pti
.getKindConsNum(pk
);
167 // check for associativity
168 if (k
== pk
&& quantifiers::TermUtil::isAssoc(k
))
170 // if the operator is associative, then a repeated occurrence should only
171 // occur in the leftmost argument position
172 int firstArg
= getFirstArgOccurrence(pdt
[pc
], tn
);
173 Assert(firstArg
!= -1);
178 // the argument types of the child must be the parent's type
179 for (unsigned i
= 0, nargs
= dt
[c
].getNumArgs(); i
< nargs
; i
++)
181 TypeNode type
= dt
[c
].getArgType(i
);
187 Trace("sygus-sb-simple")
188 << " sb-simple : do not consider " << k
<< " at child arg " << arg
190 << " since it is associative, with first arg = " << firstArg
194 // describes the shape of an alternate term to construct
195 // we check whether this term is in the sygus grammar below
199 // construct rt by cases
200 if (pk
== NOT
|| pk
== BITVECTOR_NOT
|| pk
== UMINUS
|| pk
== BITVECTOR_NEG
)
202 // negation normal form
205 rt
.d_req_type
= dt
[c
].getArgType(0);
209 Kind reqk
= UNDEFINED_KIND
; // required kind for all children
210 std::map
<unsigned, Kind
> reqkc
; // required kind for some children
229 rt
.d_req_kind
= EQUAL
;
236 rt
.d_children
[0].d_req_type
= dt
[c
].getArgType(0);
238 else if (k
== LEQ
|| k
== GT
)
240 // (not (~ x y)) -----> (~ (+ y 1) x)
242 rt
.d_children
[0].d_req_kind
= PLUS
;
243 rt
.d_children
[0].d_children
[0].d_req_type
= dt
[c
].getArgType(1);
244 rt
.d_children
[0].d_children
[1].d_req_const
=
245 NodeManager::currentNM()->mkConst(Rational(1));
246 rt
.d_children
[1].d_req_type
= dt
[c
].getArgType(0);
248 else if (k
== LT
|| k
== GEQ
)
250 // (not (~ x y)) -----> (~ y (+ x 1))
252 rt
.d_children
[0].d_req_type
= dt
[c
].getArgType(1);
253 rt
.d_children
[1].d_req_kind
= PLUS
;
254 rt
.d_children
[1].d_children
[0].d_req_type
= dt
[c
].getArgType(0);
255 rt
.d_children
[1].d_children
[1].d_req_const
=
256 NodeManager::currentNM()->mkConst(Rational(1));
259 else if (pk
== BITVECTOR_NOT
)
261 if (k
== BITVECTOR_AND
)
263 rt
.d_req_kind
= BITVECTOR_OR
;
264 reqk
= BITVECTOR_NOT
;
266 else if (k
== BITVECTOR_OR
)
268 rt
.d_req_kind
= BITVECTOR_AND
;
269 reqk
= BITVECTOR_NOT
;
271 else if (k
== BITVECTOR_XNOR
)
273 rt
.d_req_kind
= BITVECTOR_XOR
;
275 else if (k
== BITVECTOR_XOR
)
277 rt
.d_req_kind
= BITVECTOR_XNOR
;
280 else if (pk
== UMINUS
)
284 rt
.d_req_kind
= PLUS
;
288 else if (pk
== BITVECTOR_NEG
)
292 rt
.d_req_kind
= PLUS
;
293 reqk
= BITVECTOR_NEG
;
296 if (!rt
.empty() && (reqk
!= UNDEFINED_KIND
|| !reqkc
.empty()))
298 int pcr
= pti
.getKindConsNum(rt
.d_req_kind
);
301 Assert(pcr
< static_cast<int>(pdt
.getNumConstructors()));
302 // must have same number of arguments
303 if (pdt
[pcr
].getNumArgs() == dt
[c
].getNumArgs())
305 for (unsigned i
= 0, nargs
= pdt
[pcr
].getNumArgs(); i
< nargs
; i
++)
308 if (reqk
== UNDEFINED_KIND
)
310 std::map
<unsigned, Kind
>::iterator itr
= reqkc
.find(i
);
311 if (itr
!= reqkc
.end())
316 if (rk
!= UNDEFINED_KIND
)
318 rt
.d_children
[i
].d_req_kind
= rk
;
319 rt
.d_children
[i
].d_children
[0].d_req_type
= dt
[c
].getArgType(i
);
327 else if (k
== MINUS
|| k
== BITVECTOR_SUB
)
329 if (pk
== EQUAL
|| pk
== MINUS
|| pk
== BITVECTOR_SUB
|| pk
== LEQ
330 || pk
== LT
|| pk
== GEQ
|| pk
== GT
)
332 int oarg
= arg
== 0 ? 1 : 0;
333 // (~ x (- y z)) ----> (~ (+ x z) y)
334 // (~ (- y z) x) ----> (~ y (+ x z))
336 rt
.d_children
[arg
].d_req_type
= dt
[c
].getArgType(0);
337 rt
.d_children
[oarg
].d_req_kind
= k
== MINUS
? PLUS
: BITVECTOR_PLUS
;
338 rt
.d_children
[oarg
].d_children
[0].d_req_type
= pdt
[pc
].getArgType(oarg
);
339 rt
.d_children
[oarg
].d_children
[1].d_req_type
= dt
[c
].getArgType(1);
341 else if (pk
== PLUS
|| pk
== BITVECTOR_PLUS
)
343 // (+ x (- y z)) -----> (- (+ x y) z)
344 // (+ (- y z) x) -----> (- (+ x y) z)
345 rt
.d_req_kind
= pk
== PLUS
? MINUS
: BITVECTOR_SUB
;
346 int oarg
= arg
== 0 ? 1 : 0;
347 rt
.d_children
[0].d_req_kind
= pk
;
348 rt
.d_children
[0].d_children
[0].d_req_type
= pdt
[pc
].getArgType(oarg
);
349 rt
.d_children
[0].d_children
[1].d_req_type
= dt
[c
].getArgType(0);
350 rt
.d_children
[1].d_req_type
= dt
[c
].getArgType(1);
357 // (o X (ite y z w) X') -----> (ite y (o X z X') (o X w X'))
359 rt
.d_children
[0].d_req_type
= dt
[c
].getArgType(0);
360 unsigned n_args
= pdt
[pc
].getNumArgs();
361 for (unsigned r
= 1; r
<= 2; r
++)
363 rt
.d_children
[r
].d_req_kind
= pk
;
364 for (unsigned q
= 0; q
< n_args
; q
++)
368 rt
.d_children
[r
].d_children
[q
].d_req_type
= dt
[c
].getArgType(r
);
372 rt
.d_children
[r
].d_children
[q
].d_req_type
= pdt
[pc
].getArgType(q
);
376 // this increases term size but is probably a good idea
383 // (ite (not y) z w) -----> (ite y w z)
385 rt
.d_children
[0].d_req_type
= dt
[c
].getArgType(0);
386 rt
.d_children
[1].d_req_type
= pdt
[pc
].getArgType(2);
387 rt
.d_children
[2].d_req_type
= pdt
[pc
].getArgType(1);
390 Trace("sygus-sb-debug") << "Consider sygus arg kind " << k
<< ", pk = " << pk
391 << ", arg = " << arg
<< "?" << std::endl
;
394 rt
.print("sygus-sb-debug");
395 // check if it meets the requirements
396 if (rt
.satisfiedBy(d_tds
, tnp
))
398 Trace("sygus-sb-debug") << "...success!" << std::endl
;
399 Trace("sygus-sb-simple")
400 << " sb-simple : do not consider " << k
<< " as arg " << arg
401 << " of " << pk
<< std::endl
;
402 // do not need to consider the kind in the search since there are ways to
403 // construct equivalent terms
408 Trace("sygus-sb-debug") << "...failed." << std::endl
;
410 Trace("sygus-sb-debug") << std::endl
;
412 // must consider this kind in the search
416 bool SygusSimpleSymBreak::considerConst(
417 TypeNode tn
, TypeNode tnp
, Node c
, Kind pk
, int arg
)
419 const DType
& pdt
= tnp
.getDType();
420 // child grammar-independent
421 if (!considerConst(pdt
, tnp
, c
, pk
, arg
))
425 // this can probably be made child grammar independent
426 quantifiers::SygusTypeInfo
& ti
= d_tds
->getTypeInfo(tn
);
427 quantifiers::SygusTypeInfo
& pti
= d_tds
->getTypeInfo(tnp
);
428 int pc
= pti
.getKindConsNum(pk
);
429 if (pdt
[pc
].getNumArgs() == 2)
433 if (d_tutil
->hasOffsetArg(pk
, arg
, offset
, ok
))
435 Trace("sygus-sb-simple-debug")
436 << pk
<< " has offset arg " << ok
<< " " << offset
<< std::endl
;
437 int ok_arg
= pti
.getKindConsNum(ok
);
440 Trace("sygus-sb-simple-debug")
441 << "...at argument " << ok_arg
<< std::endl
;
442 // other operator be the same type
443 if (d_tds
->isTypeMatch(pdt
[ok_arg
], pdt
[arg
]))
446 Node co
= d_tutil
->getTypeValueOffset(c
.getType(), c
, offset
, status
);
447 Trace("sygus-sb-simple-debug")
448 << c
<< " with offset " << offset
<< " is " << co
449 << ", status=" << status
<< std::endl
;
450 if (status
== 0 && !co
.isNull())
454 Trace("sygus-sb-simple")
455 << " sb-simple : by offset reasoning, do not consider const "
457 Trace("sygus-sb-simple")
458 << " as arg " << arg
<< " of " << pk
<< " since we can use "
459 << co
<< " under " << ok
<< " " << std::endl
;
470 bool SygusSimpleSymBreak::considerConst(
471 const DType
& pdt
, TypeNode tnp
, Node c
, Kind pk
, int arg
)
473 quantifiers::SygusTypeInfo
& pti
= d_tds
->getTypeInfo(tnp
);
474 Assert(pti
.hasKind(pk
));
475 int pc
= pti
.getKindConsNum(pk
);
477 Trace("sygus-sb-debug") << "Consider sygus const " << c
<< ", parent = " << pk
478 << ", arg = " << arg
<< "?" << std::endl
;
479 if (d_tutil
->isIdempotentArg(c
, pk
, arg
))
481 if (pdt
[pc
].getNumArgs() == 2)
483 int oarg
= arg
== 0 ? 1 : 0;
484 TypeNode otn
= pdt
[pc
].getArgType(oarg
);
487 Trace("sygus-sb-simple")
488 << " sb-simple : " << c
<< " is idempotent arg " << arg
<< " of "
489 << pk
<< "..." << std::endl
;
496 Node sc
= d_tutil
->isSingularArg(c
, pk
, arg
);
499 if (pti
.hasConst(sc
))
501 Trace("sygus-sb-simple")
502 << " sb-simple : " << c
<< " is singular arg " << arg
<< " of "
503 << pk
<< ", evaluating to " << sc
<< "..." << std::endl
;
512 Node max_c
= d_tutil
->getTypeMaxValue(c
.getType());
513 Node zero_c
= d_tutil
->getTypeValue(c
.getType(), 0);
514 Node one_c
= d_tutil
->getTypeValue(c
.getType(), 1);
515 if (pk
== XOR
|| pk
== BITVECTOR_XOR
)
519 rt
.d_req_kind
= pk
== XOR
? NOT
: BITVECTOR_NOT
;
528 rt
.d_children
[1].d_req_type
= tnp
;
530 else if (c
== zero_c
)
532 rt
.d_children
[2].d_req_type
= tnp
;
536 else if (pk
== STRING_SUBSTR
)
538 if (c
== one_c
&& arg
== 2)
540 rt
.d_req_kind
= STRING_CHARAT
;
541 rt
.d_children
[0].d_req_type
= pdt
[pc
].getArgType(0);
542 rt
.d_children
[1].d_req_type
= pdt
[pc
].getArgType(1);
547 // check if satisfied
548 if (rt
.satisfiedBy(d_tds
, tnp
))
550 Trace("sygus-sb-simple") << " sb-simple : do not consider const " << c
551 << " as arg " << arg
<< " of " << pk
;
552 Trace("sygus-sb-simple") << " in " << pdt
.getName() << std::endl
;
553 // do not need to consider the constant in the search since there are
554 // ways to construct equivalent terms
562 int SygusSimpleSymBreak::solveForArgument(TypeNode tn
,
566 // we currently do not solve for arguments
570 int SygusSimpleSymBreak::getFirstArgOccurrence(const DTypeConstructor
& c
,
573 for (unsigned i
= 0, nargs
= c
.getNumArgs(); i
< nargs
; i
++)
575 if (c
.getArgType(i
) == tn
)
583 } // namespace datatypes
584 } // namespace theory