Everything Everywhere All at Once
Why should we use abstractions when analyzing code?
What is the advantage of Galois connections?
Which cases can the Sign abstraction not catch?
Breath-first!
is the abstractor
A partially ordered set
Integers in both directions
Sets
Signed
class Poset(ABC):
@abstractmethod
def __eq__(self, other: Self) -> bool: ...
@abstractmethod
def __lt__(self, other: Self) -> bool: ...
def __le__(self, other: Self) -> bool:
return self == other or self < other
@abstractmethod
def __gt__(self, other: Self) -> bool: ...
def __ge__(self, other: Self) -> bool:
return self == other or self > otherdef is_reflexive(a: Poset):
assert a == a
def is_antisymmetric(a: Poset, b: Poset):
if a != b:
assert not (a <= b <= a)
def is_transitive(a: Poset, b: Poset, c: Poset):
if a <= b <= c:
assert a <= c
@given(st_signset(), st_signset(), st_signset())
def test_signset_is_poset(a: ab.SignSet, b: ab.SignSet, c: ab.SignSet):
...
is_transitive(a, b, c)Partial orders ,
meet ,
join ,
and has a smallest and biggest element.
A Hasse diagram over the poset . By I, KSmrq, CC BY-SA 3.0 (with edits)
, , and

(A Galois Connection is a connection between two ordered sets, with a concretion and an abstraction function.)
A mathematical definition of one-sided information loss. If my abstraction finds a bug, is there a bug in the original domain?
A weak equality.
# Most of the time
parse(pretty(a)) == a
# Always
pretty(parse(pretty(a))) == pretty(a)
# Bonus for all strings s:
parse(pretty(parse(s))) == parse(s)def to_sign(value: jvms.StackValue) -> Sign:
match value:
case jvms.StackInt(v):
return (v > 0) - (v < 0)
case a:
raise NotImplementedError(f"Unsupported value {value!r}")
class SignSet:
...
@classmethod
def abstract(cls, items : Iterable[jvms.StackValue]):
signs = set()
for value in values:
signs.add(to_sign(value))
return cls(frozenset(signs))def __contains__(self, value : jvms.StackValue):
return to_sign(value) in self.signssee solutions/static/tests/test_abstractions.py
match opr:
case jvm.BinaryOpr.Add:
output = set()
if 1 in self.signs:
output.add(1)
if -1 in other.signs:
output.update([0, -1])
if -1 in self.signs:
output.add(-1)
if 1 in other.signs:
output.update([0, 1])
if 0 in self.signs:
output.update(other.signs)
return (SignSet(output), set())if , then
Abstract Your Interpreter
Emit multiple states.
Start Small, Just One frame.
Why should we use abstractions when analyzing code?
What is the advantage of Galois connections?
Which cases can the Sign abstraction not catch?