import logos
import logos.tactics as t
from logos.goal import Goal
from logos.runner import run
from logos.kernel import infer
from logos.helpers import structural_eqStructural Tactics
Structural tactics manipulate the shape of the goal — introducing variables, splitting conjunctions, pattern-matching on disjunctions.
intro(*names)
Strips leading Forall and Implies from the goal, adding the introduced items to the context.
x, y = logos.vars("x y", int)
# Introduce universally quantified variables
logos.check(logos.prove(
logos.forall(int, int, lambda x, y: x + y == y + x),
[
t.intro("x", "y"), # strips Forall(x, Forall(y, ...)), adds x,y to context
t.ring(),
],
))
print("Proved with intro")
# Introduce hypothesis from implication
logos.check(logos.prove(
logos.forall(int, lambda x: (x > logos.lit(0)) >> (x > logos.lit(0))),
[
t.intro("x", "h"), # x from Forall, h: x>0 from Implies
t.assumption(), # close with h
],
))
print("Proved implication with intro")Proved with intro
Proved implication with intro
assumption()
Closes the goal if it appears verbatim in the context.
x = logos.var("x", int)
hyp = x > logos.lit(0)
goal = Goal({"h": hyp}, hyp)
proof = run(goal, [t.assumption()])
print("Closed by assumption:", type(proof).__name__) # HypRefClosed by assumption: HypRef
exact(term)
Closes the goal with an explicit proof term. For when you know exactly what kernel term to supply.
from logos.kernel import Refl
x = logos.var("x", int)
goal = Goal({}, x == x)
proof = run(goal, [t.exact(Refl(x))])
print("Closed by exact:", type(proof).__name__) # ReflClosed by exact: Refl
split()
For conjunction goals A & B — splits into two subgoals, one for A and one for B.
x, y = logos.vars("x y", int)
logos.check(logos.prove(
logos.forall(int, int, lambda x, y:
(x > logos.lit(0)) >> ((y > logos.lit(0)) >> ((x > logos.lit(0)) & (y > logos.lit(0))))
),
[
t.intro("x", "y", "hx", "hy"),
t.split(),
[t.assumption()], # prove x > 0
[t.assumption()], # prove y > 0
],
))
print("Proved conjunction")Proved conjunction
left(right_type) / right(left_type)
For disjunction goals A | B — choose which side to prove.
x = logos.var("x", int)
# Prove "x > 0 or x > 0" by proving the left side
logos.check(logos.prove(
logos.forall(int, lambda x:
(x > logos.lit(0)) >> ((x > logos.lit(0)) | (x > logos.lit(0)))
),
[
t.intro("x", "h"),
t.left(x > logos.lit(0)), # choose to prove left side; right_type = x > 0
t.assumption(),
],
))
print("Proved disjunction via left")Proved disjunction via left
witness(val)
For existential goals Exists(x, P(x)) — provide the witness value, reducing to P[x:=val].
logos.check(logos.prove(
logos.exists(int, lambda x: x > logos.lit(5)),
[
t.witness(logos.lit(10)), # instantiate x := 10
t.decide(), # verify 10 > 5
],
))
print("Proved: ∃x > 5 (witness: 10)")Proved: ∃x > 5 (witness: 10)
contradiction()
Closes the goal if the context contains both P and ~P for some proposition P.
from logos.expr import Not
x = logos.var("x", int)
p = x > logos.lit(0)
# Context has both h: x > 0 and hn: ~(x > 0) — contradiction!
goal = Goal({"h": p, "hn": Not(p)}, x == logos.lit(99))
proof = run(goal, [t.contradiction()])
print("Closed by contradiction:", type(proof).__name__) # ExFalsoClosed by contradiction: ExFalso
apply(ref)
Applies an Axiom/Theorem object (or a local hypothesis string) whose statement matches the current goal.
x, y = logos.vars("x y", int)
# Declare and prove a theorem
@logos.theorem
def x_eq_x(x: int):
return x == x
logos.check(x_eq_x.proof(t.intro("x"), t.refl()))
# Apply it inside another proof using the object
x_inst = logos.var("x", int)
goal = Goal({}, x_inst == x_inst)
proof = run(goal, [t.apply(x_eq_x)])
print("Applied theorem:", type(proof).__name__)Applied theorem: ForallElim
You can also apply local hypotheses by name:
x = logos.var("x", int)
hyp = x > logos.lit(0)
goal = Goal({"h": hyp}, hyp)
proof = run(goal, [t.apply("h")])
print("Applied hypothesis:", type(proof).__name__)Applied hypothesis: HypRef