mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-02-12 01:43:10 +00:00
* First draft of 256-bit addition. * Update comment. * cargo fmt * Rename addition evaluation file. * Port ALU logic from SZ. * Give a name to some magic numbers. * `addition.rs` -> `add.rs`; fix carry propagation in add; impl sub. * Clippy. * Combine hi and lo parts of the output. * Implement MUL. * Suppress Clippy's attempt to make my code even harder to read. * Next draft of MUL. * Make all limbs (i.e. input and output) 16-bits. * Tidying. * Use iterators instead of building arrays. * Documentation. * Clippy is wrong; also cargo fmt. * Un-refactor equality checking, since it was wrong for sub. * Daniel comments. * Daniel comments. * Rename folder 'alu' -> 'arithmetic'. * Rename file. * Finish changing name ALU -> Arithmetic Unit. * Finish removing dependency on array_zip feature. * Remove operations that will be handled elsewhere. * Rename var; tidy up. * Clean up columns; mark places where range-checks need to be done. * Import all names in 'columns' to reduce verbiage. * cargo fmt * Fix aux_in calculation in mul. * Remove redundant 'allow's; more precise range-check size. * Document functions. * Document MUL instruction verification technique. * Initial tests for ADD. * Minor test fixes; add test for SUB. * Fix bugs in generate functions. * Fix SUB verification; refactor equality verification. * cargo fmt * Add test for MUL and fix some bugs. * Update doc. * Quiet incorrect clippy error. * Initial implementation of ADDMOD and MOD. * Fixes to addmod. * Update doc. * Do 1000 random tests instead of just 1. * Documentation fix. * Working version of ADDMOD. * Working version of MOD. * Name magic number; do multiple MUL tests. * Add code and test for special case; add some docs. * Fix spelling mistake. * Simplify asserts. * Tidy comment. * Remove unused module. * cargo fmt * Check that output is reduced. * Add conversion of canonical `i64` to a `Field64`. * Handle zero modulus within degree constraint. * cargo fmt * Fix some comments. * Check that the top half of the product is zero! * Start of refactor. * Refactoring. * Remove zero and reduction handling from addmod. * Refactoring; renaming; bug fixes. * Reuse intermediate calculations across all modular operations; don't negate quot poly unnecessarily. * Fix bug where last elt of q*m wasn't checked. * Refactoring. * Move circuit poly functions to utils.rs. * Rename ADDMOD stuff to MODULAR. * Rename module addmod -> modular. * Handle zero modulus. * Verify that output is reduced. * Implement recursive version of modular circuits. * clippy * Tidy up i64 -> Field conversion following Jacqui's comments. * cargo fmt * Improved documentation. * Address Jacqui's comments. * Save some gates by using builder.arithmetic_extension().
157 lines
5.4 KiB
Rust
157 lines
5.4 KiB
Rust
use itertools::izip;
|
|
use plonky2::field::extension::Extendable;
|
|
use plonky2::field::packed::PackedField;
|
|
use plonky2::hash::hash_types::RichField;
|
|
use plonky2::iop::ext_target::ExtensionTarget;
|
|
|
|
use crate::arithmetic::add::{eval_ext_circuit_are_equal, eval_packed_generic_are_equal};
|
|
use crate::arithmetic::columns::*;
|
|
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
|
use crate::range_check_error;
|
|
|
|
pub(crate) fn u256_sub_br(input0: [u64; N_LIMBS], input1: [u64; N_LIMBS]) -> ([u64; N_LIMBS], u64) {
|
|
const LIMB_BOUNDARY: u64 = 1 << LIMB_BITS;
|
|
const MASK: u64 = LIMB_BOUNDARY - 1u64;
|
|
|
|
let mut output = [0u64; N_LIMBS];
|
|
let mut br = 0u64;
|
|
for (i, a, b) in izip!(0.., input0, input1) {
|
|
let d = LIMB_BOUNDARY + a - b - br;
|
|
// if a < b, then d < 2^16 so br = 1
|
|
// if a >= b, then d >= 2^16 so br = 0
|
|
br = 1u64 - (d >> LIMB_BITS);
|
|
assert!(br <= 1u64, "input limbs were larger than 16 bits");
|
|
output[i] = d & MASK;
|
|
}
|
|
|
|
(output, br)
|
|
}
|
|
|
|
pub fn generate<F: RichField>(lv: &mut [F; NUM_ARITH_COLUMNS]) {
|
|
let input0_limbs = SUB_INPUT_0.map(|c| lv[c].to_canonical_u64());
|
|
let input1_limbs = SUB_INPUT_1.map(|c| lv[c].to_canonical_u64());
|
|
|
|
let (output_limbs, _) = u256_sub_br(input0_limbs, input1_limbs);
|
|
|
|
for (&c, output_limb) in SUB_OUTPUT.iter().zip(output_limbs) {
|
|
lv[c] = F::from_canonical_u64(output_limb);
|
|
}
|
|
}
|
|
|
|
pub fn eval_packed_generic<P: PackedField>(
|
|
lv: &[P; NUM_ARITH_COLUMNS],
|
|
yield_constr: &mut ConstraintConsumer<P>,
|
|
) {
|
|
range_check_error!(SUB_INPUT_0, 16);
|
|
range_check_error!(SUB_INPUT_1, 16);
|
|
range_check_error!(SUB_OUTPUT, 16);
|
|
|
|
let is_sub = lv[IS_SUB];
|
|
let input0_limbs = SUB_INPUT_0.iter().map(|&c| lv[c]);
|
|
let input1_limbs = SUB_INPUT_1.iter().map(|&c| lv[c]);
|
|
let output_limbs = SUB_OUTPUT.iter().map(|&c| lv[c]);
|
|
|
|
let output_computed = input0_limbs.zip(input1_limbs).map(|(a, b)| a - b);
|
|
|
|
eval_packed_generic_are_equal(yield_constr, is_sub, output_limbs, output_computed);
|
|
}
|
|
|
|
#[allow(clippy::needless_collect)]
|
|
pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
|
|
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
|
|
lv: &[ExtensionTarget<D>; NUM_ARITH_COLUMNS],
|
|
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
|
) {
|
|
let is_sub = lv[IS_SUB];
|
|
let input0_limbs = SUB_INPUT_0.iter().map(|&c| lv[c]);
|
|
let input1_limbs = SUB_INPUT_1.iter().map(|&c| lv[c]);
|
|
let output_limbs = SUB_OUTPUT.iter().map(|&c| lv[c]);
|
|
|
|
// Since `map` is lazy and the closure passed to it borrows
|
|
// `builder`, we can't then borrow builder again below in the call
|
|
// to `eval_ext_circuit_are_equal`. The solution is to force
|
|
// evaluation with `collect`.
|
|
let output_computed = input0_limbs
|
|
.zip(input1_limbs)
|
|
.map(|(a, b)| builder.sub_extension(a, b))
|
|
.collect::<Vec<ExtensionTarget<D>>>();
|
|
|
|
eval_ext_circuit_are_equal(
|
|
builder,
|
|
yield_constr,
|
|
is_sub,
|
|
output_limbs,
|
|
output_computed.into_iter(),
|
|
);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use plonky2::field::goldilocks_field::GoldilocksField;
|
|
use plonky2::field::types::Field;
|
|
use rand::{Rng, SeedableRng};
|
|
use rand_chacha::ChaCha8Rng;
|
|
|
|
use super::*;
|
|
use crate::arithmetic::columns::NUM_ARITH_COLUMNS;
|
|
use crate::constraint_consumer::ConstraintConsumer;
|
|
|
|
const N_RND_TESTS: usize = 1000;
|
|
|
|
// TODO: Should be able to refactor this test to apply to all operations.
|
|
#[test]
|
|
fn generate_eval_consistency_not_sub() {
|
|
type F = GoldilocksField;
|
|
|
|
let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25);
|
|
let mut lv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::rand_from_rng(&mut rng));
|
|
|
|
// if `IS_SUB == 0`, then the constraints should be met even
|
|
// if all values are garbage.
|
|
lv[IS_SUB] = F::ZERO;
|
|
|
|
let mut constraint_consumer = ConstraintConsumer::new(
|
|
vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)],
|
|
GoldilocksField::ONE,
|
|
GoldilocksField::ONE,
|
|
GoldilocksField::ONE,
|
|
);
|
|
eval_packed_generic(&lv, &mut constraint_consumer);
|
|
for &acc in &constraint_consumer.constraint_accs {
|
|
assert_eq!(acc, GoldilocksField::ZERO);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn generate_eval_consistency_sub() {
|
|
type F = GoldilocksField;
|
|
|
|
let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25);
|
|
let mut lv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::rand_from_rng(&mut rng));
|
|
|
|
// set `IS_SUB == 1` and ensure all constraints are satisfied.
|
|
lv[IS_SUB] = F::ONE;
|
|
|
|
for _ in 0..N_RND_TESTS {
|
|
// set inputs to random values
|
|
for (&ai, bi) in SUB_INPUT_0.iter().zip(SUB_INPUT_1) {
|
|
lv[ai] = F::from_canonical_u16(rng.gen());
|
|
lv[bi] = F::from_canonical_u16(rng.gen());
|
|
}
|
|
|
|
generate(&mut lv);
|
|
|
|
let mut constraint_consumer = ConstraintConsumer::new(
|
|
vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)],
|
|
GoldilocksField::ONE,
|
|
GoldilocksField::ONE,
|
|
GoldilocksField::ONE,
|
|
);
|
|
eval_packed_generic(&lv, &mut constraint_consumer);
|
|
for &acc in &constraint_consumer.constraint_accs {
|
|
assert_eq!(acc, GoldilocksField::ZERO);
|
|
}
|
|
}
|
|
}
|
|
}
|