Skip to content

Commit fe68394

Browse files
authored
Simplifier refactoring (#107)
* Refactor simplifier * Fix detekt test * Fix bvNot simplification * Refactor bitvector mul no underflow/overflow simplification
1 parent e1d18a8 commit fe68394

19 files changed

Lines changed: 5825 additions & 3316 deletions
Lines changed: 27 additions & 309 deletions
Original file line numberDiff line numberDiff line change
@@ -1,336 +1,54 @@
11
package io.ksmt.expr.rewrite.simplify
22

33
import io.ksmt.KContext
4-
import io.ksmt.expr.KAddArithExpr
54
import io.ksmt.expr.KExpr
6-
import io.ksmt.expr.KIntNumExpr
7-
import io.ksmt.expr.KMulArithExpr
8-
import io.ksmt.expr.KRealNumExpr
9-
import io.ksmt.expr.KToRealIntExpr
10-
import io.ksmt.expr.KUnaryMinusArithExpr
115
import io.ksmt.sort.KArithSort
126
import io.ksmt.sort.KBoolSort
137
import io.ksmt.sort.KIntSort
148
import io.ksmt.sort.KRealSort
15-
import io.ksmt.utils.ArithUtils.RealValue
16-
import io.ksmt.utils.ArithUtils.bigIntegerValue
17-
import io.ksmt.utils.ArithUtils.compareTo
18-
import io.ksmt.utils.ArithUtils.numericValue
19-
import io.ksmt.utils.ArithUtils.toRealValue
20-
import io.ksmt.utils.uncheckedCast
21-
import java.math.BigInteger
22-
import kotlin.math.absoluteValue
239

24-
fun <T : KArithSort> KContext.simplifyArithUnaryMinus(arg: KExpr<T>): KExpr<T> {
25-
if (arg is KIntNumExpr) {
26-
return mkIntNum(-arg.bigIntegerValue).uncheckedCast()
27-
}
10+
fun <T : KArithSort> KContext.simplifyArithUnaryMinus(arg: KExpr<T>): KExpr<T> =
11+
simplifyArithUnaryMinusLight(arg, ::mkArithUnaryMinusNoSimplify)
2812

29-
if (arg is KRealNumExpr) {
30-
return mkRealNum(
31-
mkIntNum(-arg.numerator.bigIntegerValue),
32-
arg.denominator
33-
).uncheckedCast()
34-
}
35-
36-
if (arg is KUnaryMinusArithExpr<T>) {
37-
return arg.arg
38-
}
39-
40-
return mkArithUnaryMinusNoSimplify(arg)
41-
}
42-
43-
fun <T : KArithSort> KContext.simplifyArithAdd(args: List<KExpr<T>>): KExpr<T> {
44-
require(args.isNotEmpty()) {
45-
"Arith add requires at least a single argument"
46-
}
47-
48-
val simplifiedArgs = ArrayList<KExpr<T>>(args.size)
49-
var constantTerm = RealValue.zero
50-
51-
for (arg in args) {
52-
// flat one level
53-
if (arg is KAddArithExpr<T>) {
54-
for (flatArg in arg.args) {
55-
constantTerm = addArithTerm(constantTerm, flatArg, simplifiedArgs)
56-
}
57-
} else {
58-
constantTerm = addArithTerm(constantTerm, arg, simplifiedArgs)
59-
}
60-
}
61-
62-
if (simplifiedArgs.isEmpty()) {
63-
return numericValue(constantTerm, args.first().sort)
64-
}
65-
66-
if (!constantTerm.isZero()) {
67-
// prefer constant to be the first argument
68-
val firstArg = simplifiedArgs.first()
69-
simplifiedArgs[0] = numericValue(constantTerm, firstArg.sort)
70-
simplifiedArgs.add(firstArg)
71-
}
72-
73-
if (simplifiedArgs.size == 1) {
74-
return simplifiedArgs.single()
75-
}
76-
77-
return mkArithAddNoSimplify(simplifiedArgs)
78-
}
13+
fun <T : KArithSort> KContext.simplifyArithAdd(args: List<KExpr<T>>): KExpr<T> =
14+
simplifyArithAddLight(args, ::mkArithAddNoSimplify)
7915

8016
fun <T : KArithSort> KContext.simplifyArithSub(args: List<KExpr<T>>): KExpr<T> =
81-
if (args.size == 1) {
82-
args.single()
83-
} else {
84-
require(args.isNotEmpty()) {
85-
"Arith sub requires at least a single argument"
86-
}
87-
88-
val simplifiedArgs = arrayListOf(args.first())
89-
for (arg in args.drop(1)) {
90-
simplifiedArgs += simplifyArithUnaryMinus(arg)
91-
}
92-
simplifyArithAdd(simplifiedArgs)
93-
}
94-
95-
fun <T : KArithSort> KContext.simplifyArithMul(args: List<KExpr<T>>): KExpr<T> {
96-
require(args.isNotEmpty()) {
97-
"Arith mul requires at least a single argument"
98-
}
99-
100-
val simplifiedArgs = ArrayList<KExpr<T>>(args.size)
101-
var constantTerm = RealValue.one
102-
103-
for (arg in args) {
104-
// flat one level
105-
if (arg is KMulArithExpr<T>) {
106-
for (flatArg in arg.args) {
107-
constantTerm = mulArithTerm(constantTerm, flatArg, simplifiedArgs)
108-
}
109-
} else {
110-
constantTerm = mulArithTerm(constantTerm, arg, simplifiedArgs)
111-
}
112-
}
113-
114-
if (simplifiedArgs.isEmpty() || constantTerm.isZero()) {
115-
return numericValue(constantTerm, args.first().sort)
116-
}
117-
118-
if (constantTerm != RealValue.one) {
119-
// prefer constant to be the first argument
120-
val firstArg = simplifiedArgs.first()
121-
simplifiedArgs[0] = numericValue(constantTerm, firstArg.sort)
122-
simplifiedArgs.add(firstArg)
123-
}
124-
125-
if (simplifiedArgs.size == 1) {
126-
return simplifiedArgs.single()
127-
}
128-
129-
return mkArithMulNoSimplify(simplifiedArgs)
130-
}
131-
132-
fun <T : KArithSort> KContext.simplifyArithDiv(lhs: KExpr<T>, rhs: KExpr<T>): KExpr<T> {
133-
val rValue = rhs.toRealValue()
134-
135-
if (rValue != null && !rValue.isZero()) {
136-
if (rValue == RealValue.one) {
137-
return lhs
138-
}
139-
140-
if (rValue == RealValue.minusOne) {
141-
return simplifyArithUnaryMinus(lhs)
142-
}
143-
144-
if (lhs is KIntNumExpr) {
145-
return mkIntNum(lhs.bigIntegerValue / rValue.numerator).uncheckedCast()
146-
}
147-
148-
if (lhs is KRealNumExpr) {
149-
val value = lhs.toRealValue().div(rValue)
150-
return numericValue(value, lhs.sort).uncheckedCast()
151-
}
152-
}
153-
return mkArithDivNoSimplify(lhs, rhs)
154-
}
17+
rewriteArithSub(args, KContext::simplifyArithAdd, KContext::simplifyArithUnaryMinus)
15518

156-
fun <T : KArithSort> KContext.simplifyArithPower(lhs: KExpr<T>, rhs: KExpr<T>): KExpr<T> {
157-
val lValue = lhs.toRealValue()
158-
val rValue = rhs.toRealValue()
19+
fun <T : KArithSort> KContext.simplifyArithMul(args: List<KExpr<T>>): KExpr<T> =
20+
simplifyArithMulLight(args, ::mkArithMulNoSimplify)
15921

160-
if (lValue != null && rValue != null) {
161-
tryEvalArithPower(lValue, rValue)?.let { return castRealValue(it, lhs.sort) }
162-
}
22+
fun <T : KArithSort> KContext.simplifyArithDiv(lhs: KExpr<T>, rhs: KExpr<T>): KExpr<T> =
23+
simplifyArithDivLight(lhs, rhs, KContext::simplifyArithUnaryMinus, ::mkArithDivNoSimplify)
16324

164-
if (lValue == RealValue.one) {
165-
return lhs
166-
}
25+
fun <T : KArithSort> KContext.simplifyArithPower(lhs: KExpr<T>, rhs: KExpr<T>): KExpr<T> =
26+
simplifyArithPowerLight(lhs, rhs, ::mkArithPowerNoSimplify)
16727

168-
if (rValue == RealValue.one) {
169-
return lhs
170-
}
28+
fun <T : KArithSort> KContext.simplifyArithLe(lhs: KExpr<T>, rhs: KExpr<T>): KExpr<KBoolSort> =
29+
simplifyArithLeLight(lhs, rhs, ::mkArithLeNoSimplify)
17130

172-
return mkArithPowerNoSimplify(lhs, rhs)
173-
}
174-
175-
private fun tryEvalArithPower(base: RealValue, power: RealValue): RealValue? = when {
176-
base.isZero() && power.isZero() -> null
177-
power.isZero() -> RealValue.one
178-
base.isZero() -> RealValue.zero
179-
base == RealValue.one -> RealValue.one
180-
power == RealValue.one -> base
181-
power == RealValue.minusOne -> base.inverse()
182-
else -> power.smallIntValue()?.let { powerIntValue ->
183-
if (powerIntValue >= 0) {
184-
base.pow(powerIntValue)
185-
} else {
186-
base.inverse().pow(powerIntValue.absoluteValue)
187-
}
188-
}
189-
}
190-
191-
private fun <T : KArithSort> KContext.castRealValue(value: RealValue, sort: T): KExpr<T> = when (sort) {
192-
realSort -> numericValue(value, sort)
193-
intSort -> mkIntNum(value.numerator / value.denominator).uncheckedCast()
194-
else -> error("Unexpected arith sort: $sort")
195-
}
196-
197-
fun <T : KArithSort> KContext.simplifyArithLe(lhs: KExpr<T>, rhs: KExpr<T>): KExpr<KBoolSort> {
198-
if (lhs is KIntNumExpr && rhs is KIntNumExpr) {
199-
return (lhs <= rhs).expr
200-
}
201-
if (lhs is KRealNumExpr && rhs is KRealNumExpr) {
202-
return (lhs.toRealValue() <= rhs.toRealValue()).expr
203-
}
204-
return mkArithLeNoSimplify(lhs, rhs)
205-
}
206-
207-
fun <T : KArithSort> KContext.simplifyArithLt(lhs: KExpr<T>, rhs: KExpr<T>): KExpr<KBoolSort> {
208-
if (lhs is KIntNumExpr && rhs is KIntNumExpr) {
209-
return (lhs < rhs).expr
210-
}
211-
if (lhs is KRealNumExpr && rhs is KRealNumExpr) {
212-
return (lhs.toRealValue() < rhs.toRealValue()).expr
213-
}
214-
return mkArithLtNoSimplify(lhs, rhs)
215-
}
31+
fun <T : KArithSort> KContext.simplifyArithLt(lhs: KExpr<T>, rhs: KExpr<T>): KExpr<KBoolSort> =
32+
simplifyArithLtLight(lhs, rhs, ::mkArithLtNoSimplify)
21633

21734
fun <T : KArithSort> KContext.simplifyArithGe(lhs: KExpr<T>, rhs: KExpr<T>): KExpr<KBoolSort> =
218-
simplifyArithLe(rhs, lhs)
35+
rewriteArithGe(lhs, rhs, KContext::simplifyArithLe)
21936

22037
fun <T : KArithSort> KContext.simplifyArithGt(lhs: KExpr<T>, rhs: KExpr<T>): KExpr<KBoolSort> =
221-
simplifyArithLt(rhs, lhs)
222-
223-
224-
fun KContext.simplifyIntMod(lhs: KExpr<KIntSort>, rhs: KExpr<KIntSort>): KExpr<KIntSort> {
225-
if (rhs is KIntNumExpr) {
226-
val rValue = rhs.bigIntegerValue
227-
228-
if (rValue == BigInteger.ONE || rValue == -BigInteger.ONE) {
229-
return mkIntNum(0)
230-
}
231-
232-
if (rValue != BigInteger.ZERO && lhs is KIntNumExpr) {
233-
return mkIntNum(evalIntMod(lhs.bigIntegerValue, rValue))
234-
}
235-
}
236-
return mkIntModNoSimplify(lhs, rhs)
237-
}
238-
239-
/**
240-
* Eval integer mod wrt Int theory rules.
241-
* */
242-
private fun evalIntMod(a: BigInteger, b: BigInteger): BigInteger {
243-
val remainder = a.rem(b)
244-
if (remainder >= BigInteger.ZERO) return remainder
245-
return if (b >= BigInteger.ZERO) remainder + b else remainder - b
246-
}
247-
248-
fun KContext.simplifyIntRem(lhs: KExpr<KIntSort>, rhs: KExpr<KIntSort>): KExpr<KIntSort> {
249-
if (rhs is KIntNumExpr) {
250-
val rValue = rhs.bigIntegerValue
251-
252-
if (rValue == BigInteger.ONE || rValue == -BigInteger.ONE) {
253-
return mkIntNum(0)
254-
}
255-
256-
if (rValue != BigInteger.ZERO && lhs is KIntNumExpr) {
257-
return mkIntNum(evalIntRem(lhs.bigIntegerValue, rValue))
258-
}
259-
}
260-
return mkIntRemNoSimplify(lhs, rhs)
261-
}
262-
263-
/**
264-
* Eval integer rem wrt Int theory rules.
265-
* */
266-
private fun evalIntRem(a: BigInteger, b: BigInteger): BigInteger {
267-
val mod = evalIntMod(a, b)
268-
return if (b >= BigInteger.ZERO) mod else -mod
269-
}
270-
271-
fun KContext.simplifyIntToReal(arg: KExpr<KIntSort>): KExpr<KRealSort> {
272-
if (arg is KIntNumExpr) {
273-
return mkRealNum(arg)
274-
}
275-
return mkIntToRealNoSimplify(arg)
276-
}
277-
278-
fun KContext.simplifyRealIsInt(arg: KExpr<KRealSort>): KExpr<KBoolSort> {
279-
if (arg is KRealNumExpr) {
280-
return (arg.toRealValue().denominator == BigInteger.ONE).expr
281-
}
282-
283-
// (isInt (int2real x)) ==> true
284-
if (arg is KToRealIntExpr) {
285-
return trueExpr
286-
}
287-
288-
return mkRealIsIntNoSimplify(arg)
289-
}
290-
291-
fun KContext.simplifyRealToInt(arg: KExpr<KRealSort>): KExpr<KIntSort> {
292-
if (arg is KRealNumExpr) {
293-
val realValue = arg.toRealValue()
294-
return mkIntNum(realValue.numerator / realValue.denominator)
295-
}
296-
297-
// (real2int (int2real x)) ==> x
298-
if (arg is KToRealIntExpr) {
299-
return arg.arg
300-
}
301-
302-
return mkRealToIntNoSimplify(arg)
303-
}
304-
305-
306-
private fun <T : KArithSort> addArithTerm(value: RealValue, term: KExpr<T>, terms: MutableList<KExpr<T>>): RealValue {
307-
if (term is KIntNumExpr) {
308-
return value.add(term.toRealValue())
309-
}
38+
rewriteArithGt(lhs, rhs, KContext::simplifyArithLt)
31039

311-
if (term is KRealNumExpr) {
312-
return value.add(term.toRealValue())
313-
}
40+
fun KContext.simplifyIntMod(lhs: KExpr<KIntSort>, rhs: KExpr<KIntSort>): KExpr<KIntSort> =
41+
simplifyIntModLight(lhs, rhs, ::mkIntModNoSimplify)
31442

315-
terms += term
316-
return value
317-
}
43+
fun KContext.simplifyIntRem(lhs: KExpr<KIntSort>, rhs: KExpr<KIntSort>): KExpr<KIntSort> =
44+
simplifyIntRemLight(lhs, rhs, ::mkIntRemNoSimplify)
31845

319-
private fun <T : KArithSort> mulArithTerm(value: RealValue, term: KExpr<T>, terms: MutableList<KExpr<T>>): RealValue {
320-
if (term is KIntNumExpr) {
321-
return value.mul(term.toRealValue())
322-
}
46+
fun KContext.simplifyIntToReal(arg: KExpr<KIntSort>): KExpr<KRealSort> =
47+
simplifyIntToRealLight(arg, ::mkIntToRealNoSimplify)
32348

324-
if (term is KRealNumExpr) {
325-
return value.mul(term.toRealValue())
326-
}
49+
fun KContext.simplifyRealIsInt(arg: KExpr<KRealSort>): KExpr<KBoolSort> =
50+
simplifyRealIsIntLight(arg, ::mkRealIsIntNoSimplify)
32751

328-
terms += term
329-
return value
330-
}
52+
fun KContext.simplifyRealToInt(arg: KExpr<KRealSort>): KExpr<KIntSort> =
53+
simplifyRealToIntLight(arg, ::mkRealToIntNoSimplify)
33154

332-
private fun <T : KArithSort> KExpr<T>.toRealValue(): RealValue? = when (this) {
333-
is KIntNumExpr -> toRealValue()
334-
is KRealNumExpr -> toRealValue()
335-
else -> null
336-
}

0 commit comments

Comments
 (0)