@logos.define is how you introduce functions into the logos proof system. The function body runs in expression-tree mode — operators build trees instead of evaluating — and the result is a Function object you can use in proofs.
Basic Usage
import logosimport logos.tactics as t@logos.definedef double(x: int):return x + x# double is now a Function objectprint(type(double)) # <class 'logos.define.Function'>print(double.name) # doubleprint(double.params) # [Var('x', int)]
The proof trace is: 1. intro("x") — introduce x from the Forall 2. unfold(double) — replaces double(x) with x + x in the goal 3. ring() — proves x + x == 2 * x by polynomial normalization
For functions logos can’t inspect — C extensions, external APIs, database queries — declare them symbolically with logos.extern and axiomatize their properties:
# Declare an external sorting functionsorted_fn = logos.extern("my_sorted", [list], list)xs = logos.var("xs", list)# State what we know about it@logos.axiomdef sorted_returns_same_type(xs: list):return sorted_fn(xs) == sorted_fn(xs)# Now we can use sorted_fn in propositions and proofsfrom logos.expr import Appresult = sorted_fn(xs)print(type(result).__name__) # Appprint(result.func_name) # my_sorted
App
my_sorted
Note
The extern approach is how logos reasons about library functions you can’t redefine. You state the properties you need as axioms, then build proofs on top of them.
---title: "Defining Functions"---`@logos.define` is how you introduce functions into the logos proof system. The function body runs in **expression-tree mode** — operators build trees instead of evaluating — and the result is a `Function` object you can use in proofs.## Basic Usage```{python}import logosimport logos.tactics as t@logos.definedef double(x: int):return x + x# double is now a Function objectprint(type(double)) # <class 'logos.define.Function'>print(double.name) # doubleprint(double.params) # [Var('x', int)]```## Dual ModeThe same function works as a normal Python function and as a symbolic expression builder:```{python}x = logos.var("x", int)from logos.expr import App# Python mode — called with a concrete valueprint(double(5)) # 10 (= 5 + 5)print(double(7)) # 14# Symbolic mode — called with a Varresult = double(x)print(type(result).__name__) # Appprint(result.func_name) # doubleprint(result.args) # [Var("x", int)]```## Proving About Defined FunctionsUse `t.unfold(double)` (the Function object) to replace `double(x)` with its definition body:```{python}@logos.theoremdef double_is_2x(x: int):return double(x) == logos.lit(2) * xlogos.check(double_is_2x.proof(t.intro("x"), t.unfold(double), t.ring()))print("Proved: double(x) = 2x")```The proof trace is:1. `intro("x")` — introduce `x` from the `Forall`2. `unfold(double)` — replaces `double(x)` with `x + x` in the goal3. `ring()` — proves `x + x == 2 * x` by polynomial normalization## Composing DefinitionsDefined functions can call each other:```{python}@logos.definedef square(x: int):return x * x@logos.definedef sum_of_squares(x: int, y: int):return square(x) + square(y)@logos.theoremdef sos_symmetric(x: int, y: int):return sum_of_squares(x, y) == sum_of_squares(y, x)logos.check(sos_symmetric.proof( t.intro("x", "y"), t.unfold(sum_of_squares), t.unfold(square), t.ring(),))print("Proved: sum_of_squares is symmetric")```## Case ExpressionsUse `logos.cases()` for conditional definitions. Each branch is a `(condition, value)` pair:```{python}@logos.definedef abs_val(x: int):return logos.cases( (x >= logos.lit(0), x), (x < logos.lit(0), -x), )# Python mode still worksprint(abs_val(-5)) # 5print(abs_val(3)) # 3```## External (Black-Box) FunctionsFor functions logos can't inspect — C extensions, external APIs, database queries — declare them symbolically with `logos.extern` and axiomatize their properties:```{python}# Declare an external sorting functionsorted_fn = logos.extern("my_sorted", [list], list)xs = logos.var("xs", list)# State what we know about it@logos.axiomdef sorted_returns_same_type(xs: list):return sorted_fn(xs) == sorted_fn(xs)# Now we can use sorted_fn in propositions and proofsfrom logos.expr import Appresult = sorted_fn(xs)print(type(result).__name__) # Appprint(result.func_name) # my_sorted```::: {.callout-note}The extern approach is how logos reasons about library functions you can't redefine. You state the properties you need as axioms, then build proofs on top of them.:::## Multiple ParametersDefinitions can take any number of parameters:```{python}@logos.definedef clamp(x: int, lo: int, hi: int):return logos.cases( (x < lo, lo), (x > hi, hi), (logos.lit(True), x), # default/else case )print(clamp(-5, 0, 10)) # 0print(clamp(50, 0, 10)) # 10print(clamp(5, 0, 10)) # 5```