Skip to content

Commit bc66762

Browse files
committed
feat(ConstraintSystem): universal CS abstraction with Hom and BehavioralContract
Introduces a small theory of constraint systems that unifies the indexed relations used across ArkLib (R1CS, Plonkish, lookups, memory checking, AIR, CCS, and DSL-level systems such as Clean's FormalCircuit). ArkLib/ProofSystem/ConstraintSystem/Basic.lean (new): - `ConstraintSystem` bundling Index, Stmt, OStmt, Wit, satisfies. - Smart constructors `ofRelation` (no oracle slot) and `ofWitnessFree`. - `ConstraintSystem.Hom` — completeness-preserving morphism with `index`, `stmt`, `oStmt`, `wit`, and `preserves`. Name follows Mathlib convention (RelHom, RingHom, LinearMap). - `Hom.id`, `Hom.comp`, and the category laws `id_comp`, `comp_id`, `comp_assoc` (all rfl). - `Hom.isSatisfiable_map` lifting satisfiability along a morphism. - `BehavioralContract` — per-index contract with `Assumptions`, `Spec`, soundness, and completeness, mirroring Clean's `FormalCircuit` pattern. ArkLib/ProofSystem/ConstraintSystem/Examples.lean (new): - `R1CS.toConstraintSystem` and `Plonk.toConstraintSystem` (+ `Plonk.Shape`) wiring the existing concrete relations into the universal abstraction, with `Iff.rfl` sanity lemmas. ArkLib/ProofSystem/ConstraintSystem/Plonk.lean: - `Plonk.ConstraintSystem` marked `protected` so bare `ConstraintSystem` inside namespace `Plonk` resolves to the universal one. Internal uses qualified to `Plonk.ConstraintSystem`. ArkLib.lean regenerated via scripts/update-lib.sh. Made-with: Cursor
1 parent cbee170 commit bc66762

4 files changed

Lines changed: 316 additions & 6 deletions

File tree

ArkLib.lean

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ import ArkLib.ProofSystem.Component.RandomQuery
145145
import ArkLib.ProofSystem.Component.ReduceClaim
146146
import ArkLib.ProofSystem.Component.SendClaim
147147
import ArkLib.ProofSystem.Component.SendWitness
148+
import ArkLib.ProofSystem.ConstraintSystem.Basic
149+
import ArkLib.ProofSystem.ConstraintSystem.Examples
148150
import ArkLib.ProofSystem.ConstraintSystem.Lookup
149151
import ArkLib.ProofSystem.ConstraintSystem.MemoryChecking
150152
import ArkLib.ProofSystem.ConstraintSystem.Plonk
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/-
2+
Copyright (c) 2025 ArkLib Contributors. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Quang Dao
5+
-/
6+
7+
import Mathlib.Data.Set.Defs
8+
9+
/-!
10+
# Universal constraint system abstraction
11+
12+
This file introduces a small theory of constraint systems that provides a uniform interface
13+
for the various concrete systems used across ArkLib (R1CS, Plonkish, lookups, memory
14+
checking, AIR, CCS, and DSL-level systems such as Clean's `FormalCircuit`).
15+
16+
The goals are:
17+
18+
1. **Unify**: capture the common shape `(index, statement, oracle statement, witness,
19+
satisfies)` so that protocols and compilers can be written once and instantiated for any
20+
concrete CS.
21+
2. **Compose**: introduce morphisms `ConstraintSystem.Hom` that transport satisfiability
22+
along index/statement/witness maps, modelling reductions between constraint systems
23+
(e.g. Clean gadget → R1CS row block, Plonkish → CCS, Plain R1CS → padded R1CS).
24+
3. **Extend with behaviour**: add a `BehavioralContract` layer that pairs a CS with
25+
user-facing I/O contracts (assumptions + spec), in the style of Clean's `FormalCircuit`.
26+
27+
## Design notes
28+
29+
- The structure is **indexed**: a single `ConstraintSystem` value packages an entire family
30+
of concrete relations (one per `Index`), so that, e.g., R1CS of every size is a single
31+
constraint system rather than one per `(m, n, n_w)`.
32+
- The statement is split into an **in-the-clear** part (`Stmt`) and a **committed/oracle**
33+
part (`OStmt`). Use `fun _ => PUnit` for `OStmt` if the system is purely non-oracle. If
34+
multiple oracle slots are needed (as in R1CS with three matrices), bundle them via a
35+
dependent function type inside `OStmt`.
36+
- `satisfies` is a `Prop`, not a `Set`, for ergonomic use inside proofs and reductions.
37+
- Morphisms are one-way (completeness-preserving). Soundness-reflecting variants
38+
(extractors, embeddings, isos) are future work.
39+
-/
40+
41+
universe u v w
42+
43+
/-- A **constraint system** packages a family of indexed relations into a single bundle.
44+
45+
For each `i : Index` there is a `Stmt i` (the in-the-clear statement), an `OStmt i` (the
46+
committed/oracle-accessible statement data; use `PUnit` if absent), and a `Wit i` (the
47+
private witness). The predicate `satisfies i s o w` asserts that the triple `(s, o, w)` is
48+
a valid instance at index `i`. -/
49+
structure ConstraintSystem : Type (max (u + 1) (v + 1) (w + 1)) where
50+
/-- Index type parametrising the family of relations (sizes, shape parameters, etc.). -/
51+
Index : Type u
52+
/-- In-the-clear part of the statement at each index. -/
53+
Stmt : Index → Type v
54+
/-- Committed/oracle-accessible part of the statement. Use `fun _ => PUnit` for a
55+
purely non-oracle constraint system. Multiple oracle slots can be bundled via a
56+
dependent function type (e.g. `MatrixIdx → Matrix _ _ R` for R1CS). -/
57+
OStmt : Index → Type v
58+
/-- Private witness. -/
59+
Wit : Index → Type w
60+
/-- The satisfiability predicate at each index. -/
61+
satisfies : (i : Index) → Stmt i → OStmt i → Wit i → Prop
62+
63+
namespace ConstraintSystem
64+
65+
variable (C : ConstraintSystem.{u, v, w})
66+
67+
/-- The underlying set-theoretic relation at a given index. -/
68+
def relation (i : C.Index) : Set (C.Stmt i × C.OStmt i × C.Wit i) :=
69+
{ t | C.satisfies i t.1 t.2.1 t.2.2 }
70+
71+
/-- Existence of valid oracle data and witness for a given in-the-clear statement. -/
72+
def IsSatisfiable (i : C.Index) (s : C.Stmt i) : Prop :=
73+
∃ o w, C.satisfies i s o w
74+
75+
/-- Build a constraint system from a plain indexed relation (no oracle statement slot). -/
76+
def ofRelation
77+
{Idx : Type u} {S : Idx → Type v} {W : Idx → Type w}
78+
(rel : (i : Idx) → S i → W i → Prop) : ConstraintSystem.{u, v, w} where
79+
Index := Idx
80+
Stmt := S
81+
OStmt := fun _ => PUnit
82+
Wit := W
83+
satisfies := fun i s _ w => rel i s w
84+
85+
/-- Build a witness-free constraint system (assertions purely over the public and oracle
86+
parts of the statement). -/
87+
def ofWitnessFree
88+
{Idx : Type u} {S O : Idx → Type v}
89+
(rel : (i : Idx) → S i → O i → Prop) : ConstraintSystem.{u, v, 0} where
90+
Index := Idx
91+
Stmt := S
92+
OStmt := O
93+
Wit := fun _ => PUnit
94+
satisfies := fun i s o _ => rel i s o
95+
96+
end ConstraintSystem
97+
98+
/-!
99+
## Morphisms between constraint systems
100+
101+
A `ConstraintSystem.Hom` transports valid instances of one constraint system to valid
102+
instances of another. It consists of:
103+
104+
- `index` — a map of shape indices (e.g. scale the size of an R1CS instance);
105+
- `stmt`, `oStmt`, `wit` — compatible maps of statement, oracle-statement, and witness
106+
data that in general may depend on the input in-the-clear statement;
107+
- `preserves` — the core axiom that maps satisfying triples to satisfying triples.
108+
109+
Intuitively, `Hom C D` is a *completeness-preserving* reduction from `C` to `D`: if you can
110+
satisfy `C` at some index, you can satisfy `D` at the image index, with an explicit
111+
construction of the needed data. Soundness-reflecting morphisms (with extractors) are a
112+
strict strengthening and are future work.
113+
114+
The name `Hom` follows Mathlib convention (`RelHom`, `RingHom`, `LinearMap`). A future
115+
extension can add `ConstraintSystem.Hom.Embedding` or `ConstraintSystem.Iso` without
116+
renaming anything.
117+
-/
118+
119+
namespace ConstraintSystem
120+
121+
/-- A completeness-preserving morphism between constraint systems. -/
122+
@[ext]
123+
structure Hom (C D : ConstraintSystem.{u, v, w}) where
124+
/-- Map on index types. -/
125+
index : C.Index → D.Index
126+
/-- Map on in-the-clear statements. -/
127+
stmt : (i : C.Index) → C.Stmt i → D.Stmt (index i)
128+
/-- Map on oracle/committed statement data. It may depend on the in-the-clear statement
129+
as well, which is needed e.g. when the index encodes a global shape and the statement
130+
fixes public inputs before committed data is computed. -/
131+
oStmt : (i : C.Index) → (s : C.Stmt i) → C.OStmt i → D.OStmt (index i)
132+
/-- Map on witnesses, likewise allowed to depend on the input statement. -/
133+
wit : (i : C.Index) → (s : C.Stmt i) → C.Wit i → D.Wit (index i)
134+
/-- Preservation property: valid instances map to valid instances. -/
135+
preserves : ∀ i s o w,
136+
C.satisfies i s o w → D.satisfies (index i) (stmt i s) (oStmt i s o) (wit i s w)
137+
138+
namespace Hom
139+
140+
/-- The identity morphism on a constraint system. -/
141+
def id (C : ConstraintSystem.{u, v, w}) : Hom C C where
142+
index := _root_.id
143+
stmt := fun _ => _root_.id
144+
oStmt := fun _ _ => _root_.id
145+
wit := fun _ _ => _root_.id
146+
preserves := fun _ _ _ _ h => h
147+
148+
/-- Composition of two morphisms. Reads right-to-left as usual. -/
149+
def comp {C D E : ConstraintSystem.{u, v, w}} (g : Hom D E) (f : Hom C D) : Hom C E where
150+
index := g.index ∘ f.index
151+
stmt := fun i s => g.stmt (f.index i) (f.stmt i s)
152+
oStmt := fun i s o => g.oStmt (f.index i) (f.stmt i s) (f.oStmt i s o)
153+
wit := fun i s w => g.wit (f.index i) (f.stmt i s) (f.wit i s w)
154+
preserves := fun i s o w h =>
155+
g.preserves (f.index i) (f.stmt i s) (f.oStmt i s o) (f.wit i s w) (f.preserves i s o w h)
156+
157+
variable {C D E F : ConstraintSystem.{u, v, w}}
158+
159+
@[simp] theorem id_comp (f : Hom C D) : (id D).comp f = f := rfl
160+
161+
@[simp] theorem comp_id (f : Hom C D) : f.comp (id C) = f := rfl
162+
163+
theorem comp_assoc (h : Hom E F) (g : Hom D E) (f : Hom C D) :
164+
(h.comp g).comp f = h.comp (g.comp f) := rfl
165+
166+
end Hom
167+
168+
/-- Morphisms preserve satisfiability. -/
169+
theorem Hom.isSatisfiable_map {C D : ConstraintSystem.{u, v, w}} (f : Hom C D)
170+
{i : C.Index} {s : C.Stmt i} (hs : C.IsSatisfiable i s) :
171+
D.IsSatisfiable (f.index i) (f.stmt i s) := by
172+
obtain ⟨o, w, h⟩ := hs
173+
exact ⟨f.oStmt i s o, f.wit i s w, f.preserves i s o w h⟩
174+
175+
end ConstraintSystem
176+
177+
/-!
178+
## Behavioural contracts
179+
180+
In DSL-level systems like Clean, a circuit is bundled with a user-facing contract:
181+
preconditions (`Assumptions`) on the statement and a high-level specification (`Spec`)
182+
relating statement and witness. A `BehavioralContract` lifts this pattern onto an
183+
arbitrary constraint system: at a fixed index, one provides such a contract together with
184+
proofs that the underlying constraints *imply* the spec under the assumptions (soundness
185+
of the gadget) and that a satisfying witness *exists* under the assumptions (completeness).
186+
187+
This gives us a clean target for migrating Clean's `FormalCircuit` into ArkLib without a
188+
Clean-specific protocol bridge.
189+
-/
190+
191+
namespace ConstraintSystem
192+
193+
/-- A behavioural contract for a constraint system at a fixed index, consisting of an
194+
`Assumptions` precondition on the in-the-clear statement and a `Spec` postcondition
195+
relating statement and witness, together with soundness and completeness proofs tying the
196+
contract to the underlying satisfiability relation. -/
197+
structure BehavioralContract (C : ConstraintSystem.{u, v, w}) (i : C.Index) where
198+
/-- Precondition on the in-the-clear statement under which the contract applies. -/
199+
Assumptions : C.Stmt i → Prop
200+
/-- High-level specification relating the in-the-clear statement and the witness. -/
201+
Spec : C.Stmt i → C.Wit i → Prop
202+
/-- Soundness: if the assumptions hold and the underlying constraints are satisfied,
203+
the spec holds for the recovered witness. -/
204+
soundness : ∀ s o w, Assumptions s → C.satisfies i s o w → Spec s w
205+
/-- Completeness: if the assumptions hold, there exist oracle and witness data making
206+
the underlying constraints satisfied *and* the spec true. -/
207+
completeness : ∀ s, Assumptions s → ∃ o w, C.satisfies i s o w ∧ Spec s w
208+
209+
namespace BehavioralContract
210+
211+
variable {C : ConstraintSystem.{u, v, w}} {i : C.Index} (B : BehavioralContract C i)
212+
213+
/-- Under the assumptions, the constraint system is satisfiable at `i` for every statement.
214+
This is the most immediately useful corollary of `completeness`. -/
215+
theorem isSatisfiable_of_assumptions {s : C.Stmt i} (hs : B.Assumptions s) :
216+
C.IsSatisfiable i s := by
217+
obtain ⟨o, w, hsat, _⟩ := B.completeness s hs
218+
exact ⟨o, w, hsat⟩
219+
220+
end BehavioralContract
221+
222+
end ConstraintSystem
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/-
2+
Copyright (c) 2025 ArkLib Contributors. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Quang Dao
5+
-/
6+
7+
import ArkLib.ProofSystem.ConstraintSystem.Basic
8+
import ArkLib.ProofSystem.ConstraintSystem.R1CS
9+
import ArkLib.ProofSystem.ConstraintSystem.Plonk
10+
11+
/-!
12+
# Concrete instances of the universal `ConstraintSystem`
13+
14+
This file wires the existing R1CS and Plonkish relations into the universal
15+
`ConstraintSystem` abstraction as a smoke test that the interface captures their shapes.
16+
17+
- `R1CS.toConstraintSystem R` packages R1CS of every size `(m, n, n_w)` over a commutative
18+
semiring `R` as a single constraint system. The three matrices `A, B, C` live in the
19+
oracle-statement slot.
20+
- `Plonk.toConstraintSystem R` packages the gate-based Plonkish relation of every shape
21+
`(numWires, numGates, ℓ)` over a commutative ring `R`. The gate layout itself lives in
22+
the oracle-statement slot.
23+
24+
Each instance is accompanied by a trivial verification lemma showing the `satisfies`
25+
predicate agrees with the original relation by definitional equality.
26+
-/
27+
28+
/-- R1CS of every size over a fixed commutative semiring, packaged as a universal
29+
`ConstraintSystem`. The in-the-clear statement holds the public inputs, the oracle
30+
statement holds the three constraint matrices `A, B, C`, and the witness holds the
31+
private witness variables. -/
32+
def R1CS.toConstraintSystem (R : Type*) [CommSemiring R] : ConstraintSystem where
33+
Index := R1CS.Size
34+
Stmt := fun sz => Fin sz.n_x → R
35+
OStmt := fun sz => R1CS.MatrixIdx → Matrix (Fin sz.m) (Fin sz.n) R
36+
Wit := fun sz => Fin sz.n_w → R
37+
satisfies := R1CS.relation R
38+
39+
@[simp] theorem R1CS.toConstraintSystem_satisfies
40+
(R : Type*) [CommSemiring R] (sz : R1CS.Size)
41+
(stmt : Fin sz.n_x → R)
42+
(mats : R1CS.MatrixIdx → Matrix (Fin sz.m) (Fin sz.n) R)
43+
(wit : Fin sz.n_w → R) :
44+
(R1CS.toConstraintSystem R).satisfies sz stmt mats wit ↔
45+
R1CS.relation R sz stmt mats wit :=
46+
Iff.rfl
47+
48+
/-- The index data for the Plonkish constraint system: the number of wires, the number
49+
of gates, and the number of public inputs `ℓ ≤ numWires`. -/
50+
structure Plonk.Shape where
51+
/-- Number of wires in the circuit. -/
52+
numWires : ℕ
53+
/-- Number of gates in the circuit. -/
54+
numGates : ℕ
55+
/-- Number of public inputs, bounded by `numWires`. -/
56+
ℓ : ℕ
57+
/-- Proof that `ℓ ≤ numWires`. -/
58+
hℓ : ℓ ≤ numWires
59+
60+
instance : Inhabited Plonk.Shape where
61+
default := ⟨0, 0, 0, Nat.le_refl _⟩
62+
63+
/-- The Plonkish relation of every shape over a fixed commutative ring, packaged as a
64+
universal `ConstraintSystem`. The in-the-clear statement holds the public inputs, the
65+
oracle statement holds the full gate layout (a `Plonk.ConstraintSystem` value), and the
66+
witness holds the private wire values. -/
67+
def Plonk.toConstraintSystem (𝓡 : Type) [CommRing 𝓡] : ConstraintSystem where
68+
Index := Plonk.Shape
69+
Stmt := fun s => Fin s.ℓ → 𝓡
70+
OStmt := fun s => Plonk.ConstraintSystem 𝓡 s.numWires s.numGates
71+
Wit := fun s => Fin (s.numWires - s.ℓ) → 𝓡
72+
satisfies := fun s stmt cs wit => Plonk.relation cs s.ℓ s.hℓ stmt wit
73+
74+
@[simp] theorem Plonk.toConstraintSystem_satisfies
75+
(𝓡 : Type) [CommRing 𝓡] (s : Plonk.Shape)
76+
(stmt : Fin s.ℓ → 𝓡)
77+
(cs : Plonk.ConstraintSystem 𝓡 s.numWires s.numGates)
78+
(wit : Fin (s.numWires - s.ℓ) → 𝓡) :
79+
(Plonk.toConstraintSystem 𝓡).satisfies s stmt cs wit ↔
80+
Plonk.relation cs s.ℓ s.hℓ stmt wit :=
81+
Iff.rfl

ArkLib/ProofSystem/ConstraintSystem/Plonk.lean

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,12 @@ end Gate
107107

108108
/-- A Plonk constraint system is a vector of `numGates` gates, each parametrized by the underlying
109109
ring `𝓡` and `numWires`, the number of wires.
110+
111+
Marked `protected` so that bare `ConstraintSystem` inside `namespace Plonk` refers to the
112+
universal `ConstraintSystem` in `ArkLib.ProofSystem.ConstraintSystem.Basic`. Users access
113+
this definition as `Plonk.ConstraintSystem`.
110114
-/
111-
def ConstraintSystem (𝓡 : Type) (numWires numGates : ℕ) := Fin numGates → Gate 𝓡 numWires
115+
protected def ConstraintSystem (𝓡 : Type) (numWires numGates : ℕ) := Fin numGates → Gate 𝓡 numWires
112116

113117
variable {𝓡 : Type} [CommRing 𝓡] {numWires numGates : ℕ}
114118

@@ -122,15 +126,15 @@ namespace ConstraintSystem
122126

123127
/-- A constraint system accepts an input vector `x` if all of its gates accept `x`. -/
124128
def accepts (x : Fin numWires → 𝓡)
125-
(cs : ConstraintSystem 𝓡 numWires numGates) : Prop :=
129+
(cs : Plonk.ConstraintSystem 𝓡 numWires numGates) : Prop :=
126130
∀ i : Fin numGates, (cs i).accepts x
127131

128132
/-- The partition induced by a constraint system as defined in the Plonk paper.
129133
130134
For `i ∈ [numWires]`, let `T_i ⊆ [3*numGates]` be the set of indices `j` such that `V_j = i`,
131135
where `V` is the flattened vector of all wire indices `(a,b,c)` from all gates.
132136
This creates a partition of `[3 * numGates]` based on which gates use each wire index. -/
133-
def partition (cs : ConstraintSystem 𝓡 numWires numGates) :
137+
def partition (cs : Plonk.ConstraintSystem 𝓡 numWires numGates) :
134138
Fin numWires → Finset (Fin (3 * numGates)) :=
135139
-- We first cast via the equivalence `Fin (3 * numGates) ≃ Fin 3 × Fin numGates`,
136140
-- then filter by matching on the first coordinate `j.1`, which determines which wire we are
@@ -142,12 +146,13 @@ def partition (cs : ConstraintSystem 𝓡 numWires numGates) :
142146
(Finset.univ : Finset (Fin 3 × Fin numGates)))
143147

144148
/-- The permutation corresponding to the partition induced by a constraint system. -/
145-
def perm (cs : ConstraintSystem 𝓡 numWires numGates) : Equiv.Perm (Fin (3 * numGates)) := sorry
149+
def perm (cs : Plonk.ConstraintSystem 𝓡 numWires numGates) : Equiv.Perm (Fin (3 * numGates)) :=
150+
sorry
146151

147152
/-- A constraint system is prepared for `ℓ` public inputs, for some `ℓ ≤ numGates, numWires`,
148153
if for all `i ∈ [ℓ]`, the `i`-th gate constrains the `i`-th wire to be some public value. -/
149154
def isPreparedFor (ℓ : ℕ) (hℓ : ℓ ≤ numGates) (hℓ' : ℓ ≤ numWires)
150-
(cs : ConstraintSystem 𝓡 numWires numGates) : Prop :=
155+
(cs : Plonk.ConstraintSystem 𝓡 numWires numGates) : Prop :=
151156
∀ i : Fin ℓ, ∃ c, cs (Fin.castLE hℓ i) = Gate.eq (Fin.castLE hℓ' i) c
152157

153158
end ConstraintSystem
@@ -158,7 +163,7 @@ end ConstraintSystem
158163
- A natural number `ℓ ≤ m` representing the number of public inputs
159164
- A subset `ℐ ⊂ [m]` of "public inputs" (assumed to be `{1,...,ℓ}` without loss of generality)
160165
-/
161-
def relation (cs : ConstraintSystem 𝓡 numWires numGates) (ℓ : ℕ) (hℓ : ℓ ≤ numWires) :
166+
def relation (cs : Plonk.ConstraintSystem 𝓡 numWires numGates) (ℓ : ℕ) (hℓ : ℓ ≤ numWires) :
162167
(publicInputs : Fin ℓ → 𝓡) → (privateWitness : Fin (numWires - ℓ) → 𝓡) → Prop :=
163168
fun (x : Fin ℓ → 𝓡) (ω : Fin (numWires - ℓ) → 𝓡) =>
164169
let combined := Fin.append x ω ∘ Fin.cast (by omega)

0 commit comments

Comments
 (0)