knuckledragger 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kdrag/__init__.py +191 -0
- kdrag/all.py +17 -0
- kdrag/config.py +19 -0
- kdrag/contrib/__init__.py +0 -0
- kdrag/contrib/absint/knownbit.py +138 -0
- kdrag/contrib/data_modulo/__init__.py +6 -0
- kdrag/contrib/expr/__init__.py +113 -0
- kdrag/contrib/fast/.gitignore +1 -0
- kdrag/contrib/fast/Makefile +3 -0
- kdrag/contrib/fast/__init__.py +74 -0
- kdrag/contrib/fast/fast.c +76 -0
- kdrag/contrib/fast/fast.h +3 -0
- kdrag/contrib/fast/tests.c +47 -0
- kdrag/contrib/hoare.py +183 -0
- kdrag/contrib/junk_drawer/algebra.py +0 -0
- kdrag/contrib/junk_drawer/generic.py +266 -0
- kdrag/contrib/junk_drawer/list.py +40 -0
- kdrag/contrib/pcode/README.md +49 -0
- kdrag/contrib/pcode/__init__.py +726 -0
- kdrag/contrib/pcode/__main__.py +113 -0
- kdrag/contrib/pcode/asmspec.py +793 -0
- kdrag/contrib/yosys/__init__.py +354 -0
- kdrag/datatype.py +610 -0
- kdrag/hypothesis.py +305 -0
- kdrag/kernel.py +693 -0
- kdrag/modal.py +123 -0
- kdrag/notation.py +549 -0
- kdrag/parsers/__init__.py +0 -0
- kdrag/parsers/microlean.py +261 -0
- kdrag/parsers/sexp.py +121 -0
- kdrag/parsers/smtlib.py +62 -0
- kdrag/parsers/tptp.py +96 -0
- kdrag/parsers/trs.py +45 -0
- kdrag/printers/__init__.py +5 -0
- kdrag/printers/c.py +241 -0
- kdrag/printers/lean.py +182 -0
- kdrag/printers/llvm.py +0 -0
- kdrag/printers/rust.py +75 -0
- kdrag/printers/verilog.py +124 -0
- kdrag/property.py +102 -0
- kdrag/reflect.py +775 -0
- kdrag/rewrite.py +767 -0
- kdrag/smt.py +578 -0
- kdrag/smt.pyi +8316 -0
- kdrag/solvers/.gitignore +19 -0
- kdrag/solvers/__init__.py +925 -0
- kdrag/solvers/__main__.py +50 -0
- kdrag/solvers/aprove.py +41 -0
- kdrag/solvers/datalog.py +109 -0
- kdrag/solvers/egglog.py +77 -0
- kdrag/solvers/egraph.py +371 -0
- kdrag/solvers/install.sh +82 -0
- kdrag/solvers/kb/__init__.py +173 -0
- kdrag/solvers/kb/multiset.py +199 -0
- kdrag/solvers/kb/string.py +170 -0
- kdrag/solvers/ljt.py +126 -0
- kdrag/solvers/prolog.py +193 -0
- kdrag/solvers/sympy.py +403 -0
- kdrag/tactics.py +1646 -0
- kdrag/tele.py +407 -0
- kdrag/theories/__init__.py +3 -0
- kdrag/theories/algebra/__init__.py +73 -0
- kdrag/theories/algebra/category.py +22 -0
- kdrag/theories/algebra/filter.py +52 -0
- kdrag/theories/algebra/group.py +157 -0
- kdrag/theories/algebra/kleene.py +0 -0
- kdrag/theories/algebra/lattice.py +34 -0
- kdrag/theories/algebra/ordering.py +33 -0
- kdrag/theories/algebra/sqmatrix.py +68 -0
- kdrag/theories/algebra/topology.py +65 -0
- kdrag/theories/algebra/vector.py +144 -0
- kdrag/theories/bitvec.py +360 -0
- kdrag/theories/bool.py +87 -0
- kdrag/theories/fixed.py +27 -0
- kdrag/theories/float.py +237 -0
- kdrag/theories/fun.py +31 -0
- kdrag/theories/int.py +180 -0
- kdrag/theories/list.py +69 -0
- kdrag/theories/logic/GAT.py +0 -0
- kdrag/theories/logic/__init__.py +0 -0
- kdrag/theories/logic/axioms.py +288 -0
- kdrag/theories/logic/computable.py +45 -0
- kdrag/theories/logic/ideas.md +8 -0
- kdrag/theories/logic/intuitionistic.py +134 -0
- kdrag/theories/logic/nominal.py +31 -0
- kdrag/theories/logic/ordinal.py +0 -0
- kdrag/theories/logic/peano.py +35 -0
- kdrag/theories/logic/robinson.py +36 -0
- kdrag/theories/logic/sear.py +0 -0
- kdrag/theories/logic/sep.py +135 -0
- kdrag/theories/logic/temporal.py +327 -0
- kdrag/theories/logic/zf.py +664 -0
- kdrag/theories/nat.py +115 -0
- kdrag/theories/option.py +50 -0
- kdrag/theories/real/__init__.py +425 -0
- kdrag/theories/real/analysis.py +50 -0
- kdrag/theories/real/arb.py +280 -0
- kdrag/theories/real/complex.py +97 -0
- kdrag/theories/real/extended.py +85 -0
- kdrag/theories/real/geometry.py +22 -0
- kdrag/theories/real/interval.py +89 -0
- kdrag/theories/real/ndarray.py +32 -0
- kdrag/theories/real/seq.py +239 -0
- kdrag/theories/real/vec.py +148 -0
- kdrag/theories/regex.py +107 -0
- kdrag/theories/seq.py +189 -0
- kdrag/theories/set.py +397 -0
- kdrag/theories/sexp.py +112 -0
- kdrag/utils.py +1294 -0
- knuckledragger-0.1.3.dist-info/METADATA +267 -0
- knuckledragger-0.1.3.dist-info/RECORD +112 -0
- knuckledragger-0.1.3.dist-info/WHEEL +4 -0
kdrag/__init__.py
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Knuckledragger is an attempt at creating a down to earth,
|
|
3
|
+
highly automated interactive proof assistant in python.
|
|
4
|
+
It is not attempting to be the most interesting, expressive,
|
|
5
|
+
or flexible logic in the world.
|
|
6
|
+
|
|
7
|
+
The goal is to support applications like software/hardware verification,
|
|
8
|
+
calculus, equational reasoning, and numerical bounds.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from . import smt
|
|
12
|
+
from . import kernel
|
|
13
|
+
from . import notation
|
|
14
|
+
from . import utils
|
|
15
|
+
from . import datatype
|
|
16
|
+
from . import rewrite
|
|
17
|
+
from . import tactics
|
|
18
|
+
import functools
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
Proof = kernel.Proof
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
prove = tactics.prove
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
axiom = kernel.axiom
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
define = kernel.define
|
|
31
|
+
|
|
32
|
+
FreshVar = kernel.FreshVar
|
|
33
|
+
|
|
34
|
+
FreshVars = tactics.FreshVars
|
|
35
|
+
|
|
36
|
+
QImplies = notation.QImplies
|
|
37
|
+
|
|
38
|
+
QForAll = notation.QForAll
|
|
39
|
+
|
|
40
|
+
QExists = notation.QExists
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
cond = notation.cond
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
Inductive = kernel.Inductive
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
Struct = datatype.Struct
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
NewType = datatype.NewType
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
InductiveRel = datatype.InductiveRel
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
Enum = datatype.Enum
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
Calc = tactics.Calc
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
Lemma = tactics.Lemma
|
|
65
|
+
Theorem = tactics.Theorem
|
|
66
|
+
PTheorem = tactics.PTheorem
|
|
67
|
+
|
|
68
|
+
simp = rewrite.simp
|
|
69
|
+
|
|
70
|
+
search = utils.search
|
|
71
|
+
|
|
72
|
+
# TODO: Remove this
|
|
73
|
+
R = smt.RealSort()
|
|
74
|
+
Z = smt.IntSort()
|
|
75
|
+
RSeq = Z >> R
|
|
76
|
+
RFun = R >> R
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def seq(*args):
|
|
80
|
+
"""
|
|
81
|
+
Helper to construct sequences.
|
|
82
|
+
>>> seq(1, 2, 3)
|
|
83
|
+
Concat(Unit(1), Concat(Unit(2), Unit(3)))
|
|
84
|
+
>>> seq(1)
|
|
85
|
+
Unit(1)
|
|
86
|
+
"""
|
|
87
|
+
if len(args) == 0:
|
|
88
|
+
raise ValueError(
|
|
89
|
+
"seq() requires at least one argument. use smt.Empty(sort) instead."
|
|
90
|
+
)
|
|
91
|
+
elif len(args) == 1:
|
|
92
|
+
return smt.Unit(smt._py2expr(args[0]))
|
|
93
|
+
else:
|
|
94
|
+
return smt.Concat(*[smt.Unit(smt._py2expr(a)) for a in args])
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
Nat = Inductive("Nat")
|
|
98
|
+
Nat.declare("Z")
|
|
99
|
+
Nat.declare("S", ("pred", Nat))
|
|
100
|
+
Nat = Nat.create()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def NatSort() -> smt.DatatypeSortRef:
|
|
104
|
+
global Nat
|
|
105
|
+
assert isinstance(Nat, smt.DatatypeSortRef)
|
|
106
|
+
return Nat
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@functools.cache
|
|
110
|
+
def ListSort(Elt: smt.SortRef) -> smt.DatatypeSortRef:
|
|
111
|
+
"""
|
|
112
|
+
>>> ListSort(smt.IntSort())
|
|
113
|
+
List_Int...
|
|
114
|
+
"""
|
|
115
|
+
T = Inductive("List_" + Elt.name())
|
|
116
|
+
T.declare("Nil")
|
|
117
|
+
T.declare("Cons", ("head", Elt), ("tail", T))
|
|
118
|
+
return T.create()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def list(*args: smt.ExprRef) -> smt.DatatypeRef:
|
|
122
|
+
"""
|
|
123
|
+
Helper to construct List values
|
|
124
|
+
>>> list(1, 2, 3)
|
|
125
|
+
Cons(1, Cons(2, Cons(3, Nil)))
|
|
126
|
+
"""
|
|
127
|
+
if len(args) == 0:
|
|
128
|
+
raise ValueError("list() requires at least one argument")
|
|
129
|
+
LT = ListSort(smt._py2expr(args[0]).sort())
|
|
130
|
+
acc = LT.Nil
|
|
131
|
+
for a in reversed(args):
|
|
132
|
+
acc = LT.Cons(a, acc)
|
|
133
|
+
return acc
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@functools.cache
|
|
137
|
+
def OptionSort(T: smt.SortRef) -> smt.DatatypeSortRef:
|
|
138
|
+
"""
|
|
139
|
+
Define an Option type for a given type T
|
|
140
|
+
>>> OInt = OptionSort(smt.IntSort())
|
|
141
|
+
>>> OInt.Some(1)
|
|
142
|
+
Some(1)
|
|
143
|
+
>>> OInt.None_
|
|
144
|
+
None_
|
|
145
|
+
>>> OInt.Some(1).val
|
|
146
|
+
val(Some(1))
|
|
147
|
+
"""
|
|
148
|
+
Option = Inductive("Option_" + T.name())
|
|
149
|
+
Option.declare("None_")
|
|
150
|
+
Option.declare("Some", ("val", T))
|
|
151
|
+
return Option.create()
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# I guess I could make this a SortDispatch for regularity. I just don't see why I'd need to overload in any way but the default
|
|
155
|
+
def Some(x: smt.ExprRef) -> smt.DatatypeRef:
|
|
156
|
+
"""
|
|
157
|
+
Helper to create Option values
|
|
158
|
+
>>> Some(42)
|
|
159
|
+
Some(42)
|
|
160
|
+
>>> Some(42).sort()
|
|
161
|
+
Option_Int
|
|
162
|
+
"""
|
|
163
|
+
x = smt._py2expr(x)
|
|
164
|
+
return OptionSort(x.sort()).Some(x)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
__all__ = [
|
|
168
|
+
"prove",
|
|
169
|
+
"axiom",
|
|
170
|
+
"define",
|
|
171
|
+
"Proof",
|
|
172
|
+
"FreshVar",
|
|
173
|
+
"FreshVars",
|
|
174
|
+
"QImplies",
|
|
175
|
+
"QForAll",
|
|
176
|
+
"QExists",
|
|
177
|
+
"cond",
|
|
178
|
+
"Struct",
|
|
179
|
+
"NewType",
|
|
180
|
+
"Inductive",
|
|
181
|
+
"Calc",
|
|
182
|
+
"Lemma",
|
|
183
|
+
"Theorem",
|
|
184
|
+
"PTheorem",
|
|
185
|
+
"R",
|
|
186
|
+
"Z",
|
|
187
|
+
"RSeq",
|
|
188
|
+
"RFun",
|
|
189
|
+
"simp",
|
|
190
|
+
"search",
|
|
191
|
+
]
|
kdrag/all.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A convenience module to import commonly needed other modules as shorthands
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import kdrag as kd
|
|
6
|
+
import kdrag.smt as smt
|
|
7
|
+
|
|
8
|
+
# import kdrag.theories.real as real
|
|
9
|
+
import kdrag.solvers as solvers
|
|
10
|
+
import kdrag.theories as thy
|
|
11
|
+
import kdrag.rewrite as rw
|
|
12
|
+
import kdrag.tele as tele
|
|
13
|
+
|
|
14
|
+
import kdrag.theories.option as option
|
|
15
|
+
import kdrag.theories.list as list_
|
|
16
|
+
import kdrag.theories.set as set_
|
|
17
|
+
import kdrag.theories.seq as seq
|
kdrag/config.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Global configuration of Knuckledragger
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
solver = None
|
|
6
|
+
admit_enabled = True
|
|
7
|
+
# timeout = 1000
|
|
8
|
+
admit_level = 50
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# TODO: Someday, when it is annoyingly slow to check built in theories, we can add a flag to disable them
|
|
12
|
+
# check_lib = True
|
|
13
|
+
timing = False
|
|
14
|
+
perf_log = []
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def perf_event(tag, data, time):
|
|
18
|
+
if timing:
|
|
19
|
+
perf_log.append((tag, data, time))
|
|
File without changes
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Known Bits Abstract Domain
|
|
3
|
+
|
|
4
|
+
https://pypy.org/posts/2024/08/toy-knownbits.html
|
|
5
|
+
https://arxiv.org/pdf/2105.05398 Sound, Precise, and Fast Abstract Interpretation with Tristate Numbers
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import kdrag as kd
|
|
9
|
+
import kdrag.smt as smt
|
|
10
|
+
import functools
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def Knowns(x):
|
|
14
|
+
return ~x.unknowns
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def Zeros(x):
|
|
18
|
+
return ~x.unknowns & ~x.ones
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def Concat(a, b):
|
|
22
|
+
"""
|
|
23
|
+
>>> a = KB1.const(1) # 1
|
|
24
|
+
>>> b = KB1.const(0) # 0
|
|
25
|
+
>>> Concat(a, b)
|
|
26
|
+
KnownBits_2(Concat(ones(const(1)), ones(const(0))),
|
|
27
|
+
Concat(unknowns(const(1)), unknowns(const(0))))
|
|
28
|
+
"""
|
|
29
|
+
sa = a.ones.size()
|
|
30
|
+
sb = b.ones.size()
|
|
31
|
+
return KB(sa + sb).T.mk(
|
|
32
|
+
smt.Concat(a.ones, b.ones), smt.Concat(a.unknowns, b.unknowns)
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class KnownBits:
|
|
37
|
+
def __init__(self, W):
|
|
38
|
+
self.W = W
|
|
39
|
+
KnownBits = kd.Struct(
|
|
40
|
+
f"KnownBits_{W}",
|
|
41
|
+
("ones", smt.BitVecSort(W)),
|
|
42
|
+
("unknowns", smt.BitVecSort(W)),
|
|
43
|
+
)
|
|
44
|
+
self.T = KnownBits
|
|
45
|
+
x, y, z = smt.Consts("x y z", KnownBits)
|
|
46
|
+
a, b, c = smt.Consts("a b c", smt.BitVecSort(W))
|
|
47
|
+
|
|
48
|
+
setof = kd.define(
|
|
49
|
+
"setof",
|
|
50
|
+
[x],
|
|
51
|
+
smt.Lambda([a], (~x.unknowns & a) == x.ones),
|
|
52
|
+
)
|
|
53
|
+
self.setof = setof
|
|
54
|
+
self.getitem = kd.notation.getitem.register(
|
|
55
|
+
KnownBits, lambda a, idx: setof(a)[idx]
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
kd.prove(KnownBits.mk(0, 0)[0], unfold=1)
|
|
59
|
+
|
|
60
|
+
self.wf = kd.notation.wf.define([x], (x.ones & x.unknowns) == 0)
|
|
61
|
+
|
|
62
|
+
const = kd.define("const", [a], KnownBits.mk(a, 0))
|
|
63
|
+
self.const = const
|
|
64
|
+
is_const = kd.define("is_const", [x], x.unknowns == 0)
|
|
65
|
+
self.is_const = is_const
|
|
66
|
+
|
|
67
|
+
self.set_const = kd.prove(smt.ForAll([a], const(a)[a]), unfold=1)
|
|
68
|
+
|
|
69
|
+
self.is_const_unique = kd.prove(
|
|
70
|
+
kd.QForAll(
|
|
71
|
+
[x], is_const(x), smt.Exists([a], kd.QForAll([b], x[b], a == b))
|
|
72
|
+
),
|
|
73
|
+
unfold=1,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
top = KnownBits.mk(0, -1)
|
|
77
|
+
self.set_top = kd.prove(smt.ForAll([a], top[a]), unfold=1)
|
|
78
|
+
|
|
79
|
+
zeros = kd.define("zeros", [x], Zeros(x))
|
|
80
|
+
self.zeros = zeros
|
|
81
|
+
|
|
82
|
+
invert = kd.notation.invert.define([x], KnownBits.mk(Zeros(x), x.unknowns))
|
|
83
|
+
self.invert = invert
|
|
84
|
+
|
|
85
|
+
self.invert_const = kd.prove(smt.ForAll([a], ~const(a) == const(~a)), unfold=1)
|
|
86
|
+
self.invert_top = kd.prove(~top == top, unfold=1)
|
|
87
|
+
self.invert_invol = kd.prove(kd.QForAll([x], invert(invert(x)) == x), unfold=1)
|
|
88
|
+
self.set_invert = kd.prove(kd.QForAll([x, a], x[a], (~x)[~a]), unfold=1)
|
|
89
|
+
|
|
90
|
+
_ones = x.ones & y.ones
|
|
91
|
+
_knowns = Zeros(x) | Zeros(y) | _ones
|
|
92
|
+
self.and_ = kd.notation.and_.define([x, y], KnownBits.mk(_ones, ~_knowns))
|
|
93
|
+
|
|
94
|
+
self.and_const = kd.prove(
|
|
95
|
+
kd.QForAll([a, b], const(a) & const(b) == const(a & b)), unfold=1
|
|
96
|
+
)
|
|
97
|
+
# and_top = kd.prove(kd.QForAll([x], x & top == ?), unfold=1)
|
|
98
|
+
self.and_comm = kd.prove(kd.QForAll([x, y], x & y == y & x), unfold=1)
|
|
99
|
+
self.and_assoc = kd.prove(
|
|
100
|
+
kd.QForAll([x, y, z], (x & y) & z == x & (y & z)), unfold=1
|
|
101
|
+
)
|
|
102
|
+
self.set_and = kd.prove(
|
|
103
|
+
kd.QForAll([x, y, a, b], x[a], y[b], (x & y)[a & b]), unfold=1
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
_ones = x.ones | y.ones
|
|
107
|
+
_zeros = Zeros(x) & Zeros(y)
|
|
108
|
+
_knowns = _ones | _zeros
|
|
109
|
+
self.or_ = kd.notation.or_.define([x, y], KnownBits.mk(_ones, ~_knowns))
|
|
110
|
+
|
|
111
|
+
self.or_const = kd.prove(
|
|
112
|
+
kd.QForAll([a, b], const(a) | const(b) == const(a | b)), unfold=1
|
|
113
|
+
)
|
|
114
|
+
# or_top = kd.prove(kd.QForAll([x], x | top == ?), unfold=1)
|
|
115
|
+
self.or_comm = kd.prove(kd.QForAll([x, y], x | y == y | x), unfold=1)
|
|
116
|
+
self.or_assoc = kd.prove(
|
|
117
|
+
kd.QForAll([x, y, z], (x | y) | z == x | (y | z)), unfold=1
|
|
118
|
+
)
|
|
119
|
+
self.set_or = kd.prove(
|
|
120
|
+
kd.QForAll([x, y, a, b], x[a], y[b], (x | y)[a | b]), unfold=1
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@functools.cache
|
|
125
|
+
def KB(W):
|
|
126
|
+
return KnownBits(W)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
KB1 = KB(1)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test():
|
|
133
|
+
"""
|
|
134
|
+
>>> True
|
|
135
|
+
True
|
|
136
|
+
>>> KB8 = KnownBits(8)
|
|
137
|
+
>>> KB1 = KnownBits(1)
|
|
138
|
+
"""
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@dataclass
|
|
5
|
+
class Context:
|
|
6
|
+
counter = 0
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
_main_ctx = Context()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main_ctx():
|
|
13
|
+
return _main_ctx
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class AstRef:
|
|
18
|
+
ctx: Context
|
|
19
|
+
|
|
20
|
+
def eq(self, other):
|
|
21
|
+
return self is other
|
|
22
|
+
|
|
23
|
+
def get_id(self):
|
|
24
|
+
return id(self)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class SortRef(AstRef):
|
|
29
|
+
_kind: object
|
|
30
|
+
_name: str
|
|
31
|
+
|
|
32
|
+
def kind(self):
|
|
33
|
+
return self._kind
|
|
34
|
+
|
|
35
|
+
def name(self):
|
|
36
|
+
return self._name
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class TypeVarRef(AstRef):
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class FuncDeclRef(AstRef):
|
|
46
|
+
_name: str
|
|
47
|
+
_domain: tuple[SortRef, ...]
|
|
48
|
+
_range: SortRef
|
|
49
|
+
|
|
50
|
+
def name(self):
|
|
51
|
+
return self._name
|
|
52
|
+
|
|
53
|
+
def arity(self):
|
|
54
|
+
return len(self._domain)
|
|
55
|
+
|
|
56
|
+
def domain(self, i):
|
|
57
|
+
return self._domain[i]
|
|
58
|
+
|
|
59
|
+
def range(self):
|
|
60
|
+
return self._range
|
|
61
|
+
|
|
62
|
+
def __call__(self, *args):
|
|
63
|
+
assert len(args) == len(self._domain)
|
|
64
|
+
return ExprRef(ctx=self.ctx, _decl=self, _args=args)
|
|
65
|
+
|
|
66
|
+
# params. huh
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def Function(name, *sig, ctx=None):
|
|
70
|
+
assert len(sig) > 0
|
|
71
|
+
if ctx is None:
|
|
72
|
+
ctx = main_ctx()
|
|
73
|
+
return FuncDeclRef(ctx, name, sig[:-1], sig[-1])
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass
|
|
77
|
+
class ExprRef(AstRef):
|
|
78
|
+
_decl: FuncDeclRef
|
|
79
|
+
_args: tuple[AstRef, ...]
|
|
80
|
+
|
|
81
|
+
def arg(self, i):
|
|
82
|
+
return self._args[i]
|
|
83
|
+
|
|
84
|
+
def children(self):
|
|
85
|
+
return list(self._args)
|
|
86
|
+
|
|
87
|
+
def decl(self):
|
|
88
|
+
return self._decl
|
|
89
|
+
|
|
90
|
+
def num_args(self):
|
|
91
|
+
return len(self._args)
|
|
92
|
+
|
|
93
|
+
def _sort(self):
|
|
94
|
+
return self._decl.range()
|
|
95
|
+
|
|
96
|
+
def __eq__(self, other):
|
|
97
|
+
raise NotImplementedError
|
|
98
|
+
|
|
99
|
+
def __ne__(self, other):
|
|
100
|
+
raise NotImplementedError
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def Const(name: str, sort: SortRef, ctx=None) -> ExprRef:
|
|
104
|
+
""" """
|
|
105
|
+
return Function(name, sort, ctx=ctx)()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def Consts(names: str, sort: SortRef) -> list[ExprRef]:
|
|
109
|
+
return [Const(name, sort, ctx=sort.ctx) for name in names.split()]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def FreshConst(sort: SortRef, prefix="c"):
|
|
113
|
+
return Const(prefix + "!" + str(sort.ctx.counter), sort)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
test
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from cffi import FFI
|
|
2
|
+
import tempfile
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import z3
|
|
7
|
+
|
|
8
|
+
ffibuilder = FFI()
|
|
9
|
+
|
|
10
|
+
ffibuilder.cdef("""
|
|
11
|
+
int myadd(int a, int b);
|
|
12
|
+
const char* z3_version();
|
|
13
|
+
typedef struct z3_config *Z3_config;
|
|
14
|
+
typedef struct z3_context *Z3_context;
|
|
15
|
+
typedef void *Z3_ast;
|
|
16
|
+
typedef void *Z3_ast_vector;
|
|
17
|
+
Z3_ast my_mk_true(Z3_context ctx);
|
|
18
|
+
Z3_ast my_mk_and(Z3_context ctx, Z3_ast a, Z3_ast b);
|
|
19
|
+
Z3_ast_vector KDRAG_get_consts(Z3_context ctx, Z3_ast t);
|
|
20
|
+
""")
|
|
21
|
+
z3lib = Path(z3.__file__).parent.absolute() / "lib"
|
|
22
|
+
z3_include = Path(z3.__file__).parent.absolute() / "include"
|
|
23
|
+
ffibuilder.set_source(
|
|
24
|
+
"_fast",
|
|
25
|
+
"",
|
|
26
|
+
sources=[str(Path(__file__).parent.absolute() / "fast.c")],
|
|
27
|
+
library_dirs=[str(z3lib)],
|
|
28
|
+
include_dirs=[str(z3_include)],
|
|
29
|
+
libraries=["z3"],
|
|
30
|
+
)
|
|
31
|
+
# shenangians to build in temp directory
|
|
32
|
+
dir0 = os.getcwd()
|
|
33
|
+
tmpdir = tempfile.mkdtemp()
|
|
34
|
+
os.chdir(tmpdir)
|
|
35
|
+
ffibuilder.compile(verbose=True)
|
|
36
|
+
sys.path.insert(1, tmpdir)
|
|
37
|
+
os.chdir(dir0)
|
|
38
|
+
|
|
39
|
+
from _fast.lib import * # noqa: E402, F403 # type: ignore
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def ctxptr(ctx: z3.Context):
|
|
43
|
+
return ffibuilder.cast("void *", ctx.ctx.value) # type: ignore
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def astptr(ast: z3.AstRef):
|
|
47
|
+
return ffibuilder.cast("void *", ast.as_ast().value) # type: ignore
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def intify(ptr):
|
|
51
|
+
return int(ffibuilder.cast("uintptr_t", ptr))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test():
|
|
55
|
+
"""
|
|
56
|
+
>>> myadd(2, 3)
|
|
57
|
+
5
|
|
58
|
+
>>> ctx = ffibuilder.cast("void *", z3.main_ctx().ctx.value)
|
|
59
|
+
>>> ast = my_mk_true(ctx)
|
|
60
|
+
>>> value = intify(ast)
|
|
61
|
+
>>> z3.BoolRef(value)
|
|
62
|
+
True
|
|
63
|
+
>>> z3.BoolRef(intify(my_mk_and(ctx, ast, ast)))
|
|
64
|
+
And(True, True)
|
|
65
|
+
>>> z3.AstVector(intify(KDRAG_get_consts(ctx, ast)), z3.main_ctx())
|
|
66
|
+
[True]
|
|
67
|
+
>>> x = z3.Int("x")
|
|
68
|
+
>>> z3.AstVector(intify(KDRAG_get_consts(ctx, astptr(x + x + x))), z3.main_ctx())
|
|
69
|
+
[x]
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
# >>> intify(my_mk_and(ctx, ast, ast))
|
|
73
|
+
# >>> FFI().string(z3_version())
|
|
74
|
+
# >>> my_mk_true(ffibuilder.cast("Z3_context", z3.main_ctx().ctx.value))
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#include "z3.h"
|
|
2
|
+
#include <stdio.h>
|
|
3
|
+
#include <stdlib.h>
|
|
4
|
+
#include <string.h>
|
|
5
|
+
int myadd(int a, int b) { return a + b; }
|
|
6
|
+
|
|
7
|
+
const char *hello = "hello from fast.c";
|
|
8
|
+
const char *z3_version() {
|
|
9
|
+
puts(Z3_get_full_version());
|
|
10
|
+
char *test = (char *)malloc(sizeof(char) * 10);
|
|
11
|
+
if (!test)
|
|
12
|
+
return NULL;
|
|
13
|
+
test[0] = 'h';
|
|
14
|
+
test[1] = 'e';
|
|
15
|
+
test[2] = 'l';
|
|
16
|
+
test[3] = 'l';
|
|
17
|
+
test[4] = 'o';
|
|
18
|
+
test[5] = '\0';
|
|
19
|
+
strncpy(test, Z3_get_full_version(), 9);
|
|
20
|
+
return test; //
|
|
21
|
+
// return "hello"; // Z3_get_full_version();//hello;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
Z3_ast my_mk_true(Z3_context ctx) { return Z3_mk_true(ctx); }
|
|
25
|
+
|
|
26
|
+
Z3_ast my_mk_and(Z3_context ctx, Z3_ast a, Z3_ast b) {
|
|
27
|
+
Z3_ast args[2] = {a, b};
|
|
28
|
+
return Z3_mk_and(ctx, 2, args);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// https://stackoverflow.com/questions/27200320/maintenance-of-reference-counting-in-z3
|
|
32
|
+
// https://github.com/Z3Prover/z3/blob/master/examples/c/test_capi.c
|
|
33
|
+
Z3_ast_vector KDRAG_get_consts(Z3_context ctx, Z3_ast t) {
|
|
34
|
+
Z3_ast_vector res = Z3_mk_ast_vector(ctx);
|
|
35
|
+
Z3_ast_vector_inc_ref(ctx, res);
|
|
36
|
+
Z3_ast_vector todo = Z3_mk_ast_vector(ctx);
|
|
37
|
+
Z3_ast_vector_inc_ref(ctx, todo);
|
|
38
|
+
// todo: seen set
|
|
39
|
+
// Z3_ast_map seen = Z3_mk_ast_map(ctx);
|
|
40
|
+
// Z3_ast_map_inc_ref(ctx, seen);
|
|
41
|
+
|
|
42
|
+
Z3_ast_vector_push(ctx, todo, t);
|
|
43
|
+
Z3_inc_ref(ctx, t);
|
|
44
|
+
unsigned visited = 0;
|
|
45
|
+
while (visited < Z3_ast_vector_size(ctx, todo)) {
|
|
46
|
+
Z3_ast current = Z3_ast_vector_get(ctx, todo, visited);
|
|
47
|
+
if (Z3_is_app(ctx, current)) {
|
|
48
|
+
Z3_app app = Z3_to_app(ctx, current);
|
|
49
|
+
unsigned num_args = Z3_get_app_num_args(ctx, app);
|
|
50
|
+
if (num_args == 0) { // possible constant
|
|
51
|
+
bool found = false; // already in res?
|
|
52
|
+
for (unsigned i = 0; i < Z3_ast_vector_size(ctx, res); i++) {
|
|
53
|
+
Z3_ast existing = Z3_ast_vector_get(ctx, res, i);
|
|
54
|
+
if (existing == current) {
|
|
55
|
+
found = true;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (!found) {
|
|
60
|
+
Z3_inc_ref(ctx, current);
|
|
61
|
+
Z3_ast_vector_push(ctx, res, current);
|
|
62
|
+
}
|
|
63
|
+
} else { // push args to todo
|
|
64
|
+
for (unsigned j = 0; j < num_args; j++) {
|
|
65
|
+
Z3_ast arg = Z3_get_app_arg(ctx, app, j);
|
|
66
|
+
Z3_inc_ref(ctx, arg);
|
|
67
|
+
Z3_ast_vector_push(ctx, todo, arg);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
Z3_dec_ref(ctx, current);
|
|
72
|
+
visited++;
|
|
73
|
+
}
|
|
74
|
+
Z3_ast_vector_dec_ref(ctx, todo);
|
|
75
|
+
return res;
|
|
76
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#include "fast.h"
|
|
2
|
+
#include "z3.h"
|
|
3
|
+
#include <assert.h>
|
|
4
|
+
int main() {
|
|
5
|
+
Z3_config cfg = Z3_mk_config();
|
|
6
|
+
Z3_context ctx = Z3_mk_context_rc(cfg);
|
|
7
|
+
Z3_sort sort = Z3_mk_bool_sort(ctx);
|
|
8
|
+
Z3_symbol sym = Z3_mk_string_symbol(ctx, "x");
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
Z3_ast t = Z3_mk_const(ctx, sym, sort);
|
|
12
|
+
Z3_inc_ref(ctx, t);
|
|
13
|
+
|
|
14
|
+
{
|
|
15
|
+
Z3_ast_vector vec = KDRAG_get_consts(ctx, t);
|
|
16
|
+
unsigned size = Z3_ast_vector_size(ctx, vec);
|
|
17
|
+
assert(size == 1 && "there should be one constant");
|
|
18
|
+
for (unsigned i = 0; i < size; i++) {
|
|
19
|
+
Z3_ast ast = Z3_ast_vector_get(ctx, vec, i);
|
|
20
|
+
Z3_dec_ref(ctx, ast);
|
|
21
|
+
}
|
|
22
|
+
Z3_ast_vector_dec_ref(ctx, vec);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
{
|
|
26
|
+
Z3_ast args[] = {t, t};
|
|
27
|
+
Z3_ast myand = Z3_mk_and(ctx, 2, args);
|
|
28
|
+
// Z3_inc_ref(ctx, myand);
|
|
29
|
+
Z3_ast_vector vec = KDRAG_get_consts(ctx, myand);
|
|
30
|
+
unsigned size = Z3_ast_vector_size(ctx, vec);
|
|
31
|
+
assert(size == 2 && "there should be two constants");
|
|
32
|
+
for (unsigned i = 0; i < size; i++) {
|
|
33
|
+
Z3_ast ast = Z3_ast_vector_get(ctx, vec, i);
|
|
34
|
+
assert(ast == t && "the constant should be t");
|
|
35
|
+
Z3_dec_ref(ctx, ast);
|
|
36
|
+
}
|
|
37
|
+
Z3_dec_ref(ctx, myand);
|
|
38
|
+
Z3_ast_vector_dec_ref(ctx, vec);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Z3_dec_ref(ctx, t);
|
|
42
|
+
}
|
|
43
|
+
Z3_del_context(ctx);
|
|
44
|
+
Z3_del_config(cfg);
|
|
45
|
+
|
|
46
|
+
return 0;
|
|
47
|
+
}
|