from z3 import *
# Create fixedpoint context
fp = Fixedpoint()
fp.set(engine='datalog')
# Declare relation: edge(node, node)
node = BitVecSort(3)
edge = Function('edge', node, node, BoolSort())
reachable = Function('reachable', node, node, BoolSort())
# Register relations
fp.register_relation(edge)
fp.register_relation(reachable)
# Declare variables
x, y, z = Consts('x y z', node)
fp.declare_var(x, y, z)
# Facts: graph edges
for (i, j) in [(0,1), (1,2), (2,3)]:
fp.fact(edge(BitVecVal(i, node), BitVecVal(j, node)))
# Rules: transitive closure
# reachable(x,y) :- edge(x,y)
fp.rule(reachable(x, y), edge(x, y))
# reachable(x,z) :- edge(x,y), reachable(y,z)
fp.rule(reachable(x, z), [edge(x, y), reachable(y, z)])
# Query: is node 0 reachable from node 3?
result = fp.query(reachable(BitVecVal(0, node), BitVecVal(3, node)))
if result == sat:
print("Reachable!")
print(fp.get_answer())
else:
print("Not reachable")