Skip to content

Commit 2f6cf43

Browse files
committed
feat: enhance clamp lookup and polynomial commitment handling
1 parent f112d81 commit 2f6cf43

6 files changed

Lines changed: 40 additions & 12 deletions

File tree

jolt-atlas-core/src/onnx_proof/clamp_lookups/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ pub fn verify_clamp_lookup<F: JoltField, T: Transcript>(
410410
/// (empty for scalar nodes, which prove the clamp directly).
411411
pub fn clamp_committed_polys(node: &ComputationNode) -> Vec<CommittedPoly> {
412412
if is_scalar(node) {
413-
return vec![];
413+
return Vec::new();
414414
}
415415
let d = ClampEncoding::new(node).one_hot_params().instruction_d;
416416
(0..d)
@@ -427,7 +427,9 @@ pub fn verify_scalar_clamp<F: JoltField>(
427427
op: &str,
428428
) -> Result<(), ProofVerifyError> {
429429
let value = recover_small_int(combined).ok_or_else(|| {
430-
ProofVerifyError::InvalidOpeningProof(format!("{op} (scalar): value not in i32 range"))
430+
ProofVerifyError::InvalidOpeningProof(format!(
431+
"{op} (scalar): accumulation claim is not a small signed-integer encoding"
432+
))
431433
})?;
432434
let expected = F::from_i32(value.clamp(i32::MIN as i64, i32::MAX as i64) as i32);
433435
if output_claim != expected {

jolt-atlas-core/src/onnx_proof/prover.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ impl<F: JoltField, T: Transcript, PCS: CommitmentScheme<Field = F>> ONNXProof<F,
234234
}
235235

236236
#[tracing::instrument(skip_all)]
237-
fn commit_to_polynomials(
237+
pub(super) fn commit_to_polynomials(
238238
poly_map: &BTreeMap<CommittedPoly, MultilinearPolynomial<F>>,
239239
pcs: &PCS::ProverSetup,
240240
) -> Vec<PCS::Commitment> {

jolt-atlas-core/src/onnx_proof/zk.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1847,12 +1847,24 @@ pub fn prove_zk(
18471847
// public auxiliary vectors) toggle this flag off and back on.
18481848
prover.accumulator.zk_mode = true;
18491849

1850-
let (poly_map, commitments) = ONNXProof::<F, T, PCS>::commit_witness_polynomials(
1851-
pp.model(),
1852-
&prover.trace,
1853-
&pp.generators,
1854-
&mut prover.transcript,
1855-
);
1850+
// The ZK pipeline proves `Add`/`Sub`/`Sum` un-clamped (the saturating clamp
1851+
// lookup is not yet wired into `prove_zk`; per-node provers are the
1852+
// un-clamped `AddProver`/`SubProver`/`SumAxisProver`). Those provers never
1853+
// open the clamp one-hot decomposition (`ClampRaD`), so committing it would
1854+
// leave it without an opening-reduction sumcheck. Filter it out here so the
1855+
// committed set matches what the zk sumchecks actually open. (The non-ZK
1856+
// path commits and opens `ClampRaD` as usual.)
1857+
let poly_map: std::collections::BTreeMap<
1858+
common::CommittedPoly,
1859+
joltworks::poly::multilinear_polynomial::MultilinearPolynomial<F>,
1860+
> = ONNXProof::<F, T, PCS>::polynomial_map(pp.model(), &prover.trace)
1861+
.into_iter()
1862+
.filter(|(poly, _)| !matches!(poly, common::CommittedPoly::ClampRaD(..)))
1863+
.collect();
1864+
let commitments = ONNXProof::<F, T, PCS>::commit_to_polynomials(&poly_map, &pp.generators);
1865+
for commitment in &commitments {
1866+
prover.transcript.append_serializable(commitment);
1867+
}
18561868
// The output claim is a public scalar derived from IO; both prover and
18571869
// verifier (`verify_zk` does `transcript.append_scalar(&expected_output_claim)`)
18581870
// append it in the clear. Toggle prover zk_mode off so its append fires.

joltworks/src/lookup_tables/sat_clamp.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ impl<const XLEN: usize> PrefixSuffixDecompositionTrait<XLEN> for SatClampTable<X
8080
}
8181

8282
fn combine<F: JoltField>(&self, prefixes: &[PrefixEval<F>], suffixes: &[SuffixEval<F>]) -> F {
83+
// The prefix-suffix decomposition hard-codes the i32 split point and uses
84+
// `i32::MAX`, so it is only correct for a 64-bit address. (The MLE /
85+
// materialization paths additionally support XLEN = 16.)
86+
debug_assert_eq!(
87+
XLEN, 64,
88+
"SatClampTable prefix-suffix decomposition is only valid for XLEN = 64"
89+
);
8390
let [suffix_one, suffix_not_lower_msb_upper_eqz, suffix_not_lower_msb_upper_eqz_low, suffix_lower_msb_upper_eqo_low] =
8491
suffixes.try_into().unwrap();
8592
let [prefix_sat_val, prefix_upper_eqz, prefix_upper_eqo, prefix_lower_msb, prefix_not_lower_msb, prefix_lower_word_no_msb] =

joltworks/src/poly/signed_identity_poly.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,10 @@ impl<F: JoltField> PolynomialEvaluation<F> for SignedIdentityPoly<F> {
8686
// Weight of the variable being bound this round.
8787
let slope = F::from_u128(1 << self.num_bound_vars);
8888
// Two's-complement sign penalty: sign bit contributes -2^(n-1),
89-
// encoded here as (slope_at_sign_bit - 2^n).
90-
let sign_penalty = F::from_u64(self.num_vars.pow2() as u64);
89+
// encoded here as (slope_at_sign_bit - 2^n). Use a u128 shift so
90+
// this stays correct for `num_vars >= 64` (64-bit clamp lookups),
91+
// matching `evaluate`'s `from_u128(1 << num_vars)`.
92+
let sign_penalty = F::from_u128(1u128 << self.num_vars);
9193

9294
if self.sign_bit_pos() == self.num_bound_vars {
9395
// Binding the sign bit: p(0) = bound_value (sign=0, no penalty),

joltworks/src/utils/lookup_bits.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,12 @@ impl LookupBits {
6262
}
6363

6464
pub fn eqo(&self) -> bool {
65-
self.bits == (1 << self.len) - 1
65+
// `1u64 << 64` overflows; clamp lookups use 64-bit indices, so handle it.
66+
if self.len == 64 {
67+
self.bits == u64::MAX
68+
} else {
69+
self.bits == (1u64 << self.len) - 1
70+
}
6671
}
6772
}
6873

0 commit comments

Comments
 (0)