Skip to content

Commit 7877556

Browse files
committed
Horner Hax-CompPoly Poc
1 parent 27274ff commit 7877556

412 files changed

Lines changed: 5118 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README_Horner.md

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# Horner algorithm verification (WIP) using CompPoly
2+
3+
This folder documents an ongoing experiment toward an end-to-end verification workflow:
4+
5+
**Rust implementation → hax extraction → CompPoly specification → Lean proofs**
6+
7+
This repository follows a “one repo, multiple PoCs” approach. The Horner PoC currently lives in:
8+
- rust/horner_eval_rs/ — Rust implementation to be extracted with hax
9+
- lean-compoly/ — Lean project intended to host CompPoly specs and (eventually) proofs for the extracted code
10+
11+
Status note: this work is **in progress**. At the moment, the hax-extracted Horner code does not compile in the same Lean project as CompPoly due to Lean toolchain version pinning across libraries. The primary success so far is that we raised issues and coordinated with maintainers, who agreed on a unified Lean version. Once the upgrades land, the pipeline should become feasible without splitting projects.
12+
13+
14+
## Horner Evaluation
15+
16+
Many zk proof systems (including Plonky3-based stacks) rely heavily on polynomial arithmetic. A minimal but representative component is **univariate polynomial evaluation**.
17+
18+
Given coefficients [c0, c1, c2, …], Horner’s method computes:
19+
p(x)=c0+x(c1+x(c2+… ))
20+
21+
We aim to verify that the Rust implementation of Horner evaluation corresponds to a mathematical specification expressed using **CompPoly**.
22+
23+
**Reference (algorithmic origin)**
24+
25+
We use Plonky3-recursion as the [Plonky3-recursion](https://github.com/Plonky3/Plonky3-recursion/blob/d3ccaf73d5f707b4a2018ac65ece55d329fee934/recursion/src/pcs/fri/verifier.rs#L98).
26+
27+
28+
```Rust
29+
fn evaluate_polynomial<EF: Field>(
30+
builder: &mut CircuitBuilder<EF>,
31+
coefficients: &[Target],
32+
point: Target,
33+
) -> Target {
34+
let mut result = coefficients[coefficients.len() - 1];
35+
for &coeff in coefficients.iter().rev().skip(1) {
36+
result = builder.mul(result, point);
37+
result = builder.add(result, coeff);
38+
}
39+
result
40+
}
41+
```
42+
43+
This is Horner evaluation, but implemented over a circuit builder abstraction. For a first PoC we extract a simpler arithmetic-only version.
44+
45+
## Rust side (implementation + extraction target)
46+
47+
**Create an extraction-friendly Rust crate**
48+
49+
```bash
50+
cd zkvm-merkle-lean-verified/rust
51+
cargo new --lib horner_eval_rs
52+
cd horner_eval_rs
53+
cargo add --target 'cfg(hax)' --git https://github.com/hacspec/hax hax-lib
54+
55+
```
56+
57+
**Extraction-friendly Horner implementation**
58+
59+
Because the hax Lean backend is still evolving (and iterator support is limited), we avoid Rust iterators and write the loop using while. This keeps the algorithm identical but reduces extraction complexity.
60+
61+
```Rust
62+
pub fn horner_eval_i64(coeffs: &[i64], x: i64) -> i64 {
63+
// p(x) = a0 + a1*x + ... + an*x^n
64+
// Horner: (((an*x + a_{n-1})*x + ...)*x + a0)
65+
if coeffs.is_empty() {
66+
return 0;
67+
}
68+
69+
let mut i = coeffs.len();
70+
let mut acc = coeffs[i - 1];
71+
72+
while i > 1 {
73+
i -= 1;
74+
acc = acc * x + coeffs[i - 1];
75+
}
76+
acc
77+
}
78+
79+
#[cfg(test)]
80+
mod tests {
81+
use super::*;
82+
83+
#[test]
84+
fn horner_basic() {
85+
// p(x) = 3 + 2x + x^2, x=10 => 3 + 20 + 100 = 123
86+
let coeffs = [3i64, 2i64, 1i64];
87+
assert_eq!(horner_eval_i64(&coeffs, 10), 123);
88+
}
89+
90+
#[test]
91+
fn empty_is_zero() {
92+
let coeffs: [i64; 0] = [];
93+
assert_eq!(horner_eval_i64(&coeffs, 7), 0);
94+
}
95+
}
96+
```
97+
98+
**Run hax extraction**
99+
100+
We extract to:
101+
- lean-compoly/CompPolyEval/Extracted/Horner_eval_rs.lean
102+
103+
For convenience we added a script:
104+
- scripts/extract_horner_to_compoly.sh
105+
106+
## Lean side (CompPoly specification)
107+
108+
We created a separate Lean project:
109+
110+
- lean-compoly/
111+
112+
and added CompPoly as a dependency.
113+
114+
**Specification module**
115+
116+
We added:
117+
118+
- lean-compoly/CompPolyEval/Spec.lean
119+
120+
This defines a univariate polynomial model (one variable) over ℤ using CompPoly:
121+
122+
- P := CPoly.CMvPolynomial 1 ℤ
123+
- X0 as the variable X in one dimension
124+
- polyOfCoeffs : List ℤ → P building the polynomial from coefficients
125+
- evalAt : ℤ → P → ℤ using CPoly.CMvPolynomial.eval
126+
127+
This enables a clean mathematical statement of Horner correctness in Lean once the extracted code can be compiled in the same environment.
128+
129+
## Proof plan (WIP)
130+
131+
A direct equivalence proof between the extracted Rust function and the CompPoly spec over ℤ will require:
132+
133+
- Panic/exception freedom (or bounds assumptions) for the extracted i64 code, since the hax Lean model uses RustM and overflow-aware operators.
134+
- A semantic bridge from machine integers (i64) to mathematical integers (ℤ), typically under bounds that rule out overflow.
135+
- The main functional correctness theorem: extracted Horner evaluation equals evalAt of polyOfCoeffs.
136+
137+
## Current bottleneck: Lean toolchain version mismatch
138+
139+
At the time of writing, the blocker for a single-project end-to-end pipeline is Lean version pinning across libraries:
140+
141+
- hax Lean prelude was pinned to a single Lean version and is sensitive to Lean changes.
142+
- CompPoly and ArkLib were pinned to different Lean versions.
143+
144+
This prevented compiling the hax-extracted Horner module in the same Lean project as CompPoly.
145+
146+
### Coordination outcome (major progress)
147+
148+
As a result of issues raised during this work, maintainers of hax, CompPoly, and ArkLib agreed to converge on a unified Lean toolchain version:
149+
150+
**Lean 4.26.0**
151+
152+
Once these upgrades land, we expect the end-to-end pipeline:
153+
154+
**Rust → hax → CompPoly spec → proofs**
155+
156+
to become feasible without splitting across incompatible Lean versions.
157+
158+
Reference discussion: hax issue [#1873](https://github.com/cryspen/hax/issues/1873)
159+
160+
## Artifacts in this repository
161+
162+
- rust/horner_eval_rs/ — Rust Horner implementation (tests pass)
163+
- scripts/extract_horner_to_compoly.sh — extraction helper
164+
- lean-compoly/CompPolyEval/Spec.lean — CompPoly-based specification (typechecks)
165+
- lean-compoly/CompPolyEval/Extracted/Horner_eval_rs.lean — hax-extracted Lean code (generated; compilation depends on toolchain alignment)
166+
167+
## Next steps
168+
169+
- Update dependencies once hax/CompPoly/ArkLib are on Lean 4.26.0.
170+
- Compile extracted Horner code and write the first proof obligations:
171+
- panic-freedom / bounds,
172+
- correctness w.r.t. CompPoly eval.
173+
- Add additional normalization lemmas/tactics (simp/grind sets) as needed to support proof automation for Horner and future polynomial components.

0 commit comments

Comments
 (0)