2022-06-27 07:18:21 -07:00
|
|
|
use plonky2::field::extension::Extendable;
|
2022-06-27 15:07:52 -07:00
|
|
|
use plonky2::field::packed::PackedField;
|
2022-06-27 12:24:09 -07:00
|
|
|
use plonky2::field::types::{Field, PrimeField64};
|
2022-02-10 12:05:04 -08:00
|
|
|
use plonky2::hash::hash_types::RichField;
|
|
|
|
|
use plonky2::iop::ext_target::ExtensionTarget;
|
|
|
|
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
2022-05-17 11:04:35 +02:00
|
|
|
use plonky2::plonk::plonk_common::reduce_with_powers_ext_circuit;
|
2022-02-10 12:05:04 -08:00
|
|
|
use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
|
|
|
|
|
2022-02-19 18:32:11 -07:00
|
|
|
use crate::registers::alu::*;
|
2022-02-10 12:05:04 -08:00
|
|
|
use crate::registers::NUM_COLUMNS;
|
|
|
|
|
|
2022-02-14 13:47:33 -08:00
|
|
|
pub(crate) fn generate_addition<F: PrimeField64>(values: &mut [F; NUM_COLUMNS]) {
|
Implement a mul-add circuit in the ALU (#495)
* Implement a mul-add circuit in the ALU
The inputs are assumed to be `u32`s, while the output is encoded as four `u16 limbs`. Each output limb is range-checked.
So, our basic mul-add constraint looks like
out_0 + 2^16 out_1 + 2^32 out_2 + 2^48 out_3 = in_1 * in_2 + in_3
The right hand side will never overflow, since `u32::MAX * u32::MAX + u32::MAX < |F|`. However, the left hand side could overflow, even though we know each limb is less than `2^16`.
For example, an operation like `0 * 0 + 0` could have two possible outputs, 0 and `|F|`, both of which would satisfy the constraint above. To prevent these non-canonical outputs, we need a comparison to enforce that `out < |F|`.
Thankfully, `F::MAX` has all zeros in its low 32 bits, so `x <= F::MAX` is equivalent to `x_lo == 0 || x_hi != u32::MAX`. `x_hi != u32::MAX` can be checked by showing that `u32::MAX - x_hi` has an inverse. If `x_hi != u32::MAX`, the prover provides this (purported) inverse in an advice column.
See @bobbinth's [post](https://hackmd.io/NC-yRmmtRQSvToTHb96e8Q#Checking-element-validity) for details. That post calls the purported inverse column `m`; I named it `canonical_inv` in this code.
* fix
* PR feedback
* naming
2022-02-21 00:39:04 -08:00
|
|
|
let in_1 = values[COL_ADD_INPUT_0].to_canonical_u64();
|
|
|
|
|
let in_2 = values[COL_ADD_INPUT_1].to_canonical_u64();
|
|
|
|
|
let in_3 = values[COL_ADD_INPUT_2].to_canonical_u64();
|
2022-02-10 12:05:04 -08:00
|
|
|
let output = in_1 + in_2 + in_3;
|
|
|
|
|
|
Implement a mul-add circuit in the ALU (#495)
* Implement a mul-add circuit in the ALU
The inputs are assumed to be `u32`s, while the output is encoded as four `u16 limbs`. Each output limb is range-checked.
So, our basic mul-add constraint looks like
out_0 + 2^16 out_1 + 2^32 out_2 + 2^48 out_3 = in_1 * in_2 + in_3
The right hand side will never overflow, since `u32::MAX * u32::MAX + u32::MAX < |F|`. However, the left hand side could overflow, even though we know each limb is less than `2^16`.
For example, an operation like `0 * 0 + 0` could have two possible outputs, 0 and `|F|`, both of which would satisfy the constraint above. To prevent these non-canonical outputs, we need a comparison to enforce that `out < |F|`.
Thankfully, `F::MAX` has all zeros in its low 32 bits, so `x <= F::MAX` is equivalent to `x_lo == 0 || x_hi != u32::MAX`. `x_hi != u32::MAX` can be checked by showing that `u32::MAX - x_hi` has an inverse. If `x_hi != u32::MAX`, the prover provides this (purported) inverse in an advice column.
See @bobbinth's [post](https://hackmd.io/NC-yRmmtRQSvToTHb96e8Q#Checking-element-validity) for details. That post calls the purported inverse column `m`; I named it `canonical_inv` in this code.
* fix
* PR feedback
* naming
2022-02-21 00:39:04 -08:00
|
|
|
values[COL_ADD_OUTPUT_0] = F::from_canonical_u16(output as u16);
|
|
|
|
|
values[COL_ADD_OUTPUT_1] = F::from_canonical_u16((output >> 16) as u16);
|
|
|
|
|
values[COL_ADD_OUTPUT_2] = F::from_canonical_u16((output >> 32) as u16);
|
2022-02-10 12:05:04 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn eval_addition<F: Field, P: PackedField<Scalar = F>>(
|
|
|
|
|
local_values: &[P; NUM_COLUMNS],
|
|
|
|
|
yield_constr: &mut ConstraintConsumer<P>,
|
|
|
|
|
) {
|
|
|
|
|
let is_add = local_values[IS_ADD];
|
Implement a mul-add circuit in the ALU (#495)
* Implement a mul-add circuit in the ALU
The inputs are assumed to be `u32`s, while the output is encoded as four `u16 limbs`. Each output limb is range-checked.
So, our basic mul-add constraint looks like
out_0 + 2^16 out_1 + 2^32 out_2 + 2^48 out_3 = in_1 * in_2 + in_3
The right hand side will never overflow, since `u32::MAX * u32::MAX + u32::MAX < |F|`. However, the left hand side could overflow, even though we know each limb is less than `2^16`.
For example, an operation like `0 * 0 + 0` could have two possible outputs, 0 and `|F|`, both of which would satisfy the constraint above. To prevent these non-canonical outputs, we need a comparison to enforce that `out < |F|`.
Thankfully, `F::MAX` has all zeros in its low 32 bits, so `x <= F::MAX` is equivalent to `x_lo == 0 || x_hi != u32::MAX`. `x_hi != u32::MAX` can be checked by showing that `u32::MAX - x_hi` has an inverse. If `x_hi != u32::MAX`, the prover provides this (purported) inverse in an advice column.
See @bobbinth's [post](https://hackmd.io/NC-yRmmtRQSvToTHb96e8Q#Checking-element-validity) for details. That post calls the purported inverse column `m`; I named it `canonical_inv` in this code.
* fix
* PR feedback
* naming
2022-02-21 00:39:04 -08:00
|
|
|
let in_1 = local_values[COL_ADD_INPUT_0];
|
|
|
|
|
let in_2 = local_values[COL_ADD_INPUT_1];
|
|
|
|
|
let in_3 = local_values[COL_ADD_INPUT_2];
|
|
|
|
|
let out_1 = local_values[COL_ADD_OUTPUT_0];
|
|
|
|
|
let out_2 = local_values[COL_ADD_OUTPUT_1];
|
|
|
|
|
let out_3 = local_values[COL_ADD_OUTPUT_2];
|
2022-02-10 12:05:04 -08:00
|
|
|
|
|
|
|
|
let weight_2 = F::from_canonical_u64(1 << 16);
|
|
|
|
|
let weight_3 = F::from_canonical_u64(1 << 32);
|
|
|
|
|
// Note that this can't overflow. Since each output limb has been range checked as 16-bits,
|
|
|
|
|
// this sum can be around 48 bits at most.
|
|
|
|
|
let out = out_1 + out_2 * weight_2 + out_3 * weight_3;
|
|
|
|
|
|
|
|
|
|
let computed_out = in_1 + in_2 + in_3;
|
|
|
|
|
|
2022-02-20 17:48:31 -07:00
|
|
|
yield_constr.constraint(is_add * (out - computed_out));
|
2022-02-10 12:05:04 -08:00
|
|
|
}
|
|
|
|
|
|
2022-05-17 11:15:53 +02:00
|
|
|
pub(crate) fn eval_addition_circuit<F: RichField + Extendable<D>, const D: usize>(
|
2022-02-10 12:05:04 -08:00
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
local_values: &[ExtensionTarget<D>; NUM_COLUMNS],
|
|
|
|
|
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
|
|
|
|
) {
|
|
|
|
|
let is_add = local_values[IS_ADD];
|
Implement a mul-add circuit in the ALU (#495)
* Implement a mul-add circuit in the ALU
The inputs are assumed to be `u32`s, while the output is encoded as four `u16 limbs`. Each output limb is range-checked.
So, our basic mul-add constraint looks like
out_0 + 2^16 out_1 + 2^32 out_2 + 2^48 out_3 = in_1 * in_2 + in_3
The right hand side will never overflow, since `u32::MAX * u32::MAX + u32::MAX < |F|`. However, the left hand side could overflow, even though we know each limb is less than `2^16`.
For example, an operation like `0 * 0 + 0` could have two possible outputs, 0 and `|F|`, both of which would satisfy the constraint above. To prevent these non-canonical outputs, we need a comparison to enforce that `out < |F|`.
Thankfully, `F::MAX` has all zeros in its low 32 bits, so `x <= F::MAX` is equivalent to `x_lo == 0 || x_hi != u32::MAX`. `x_hi != u32::MAX` can be checked by showing that `u32::MAX - x_hi` has an inverse. If `x_hi != u32::MAX`, the prover provides this (purported) inverse in an advice column.
See @bobbinth's [post](https://hackmd.io/NC-yRmmtRQSvToTHb96e8Q#Checking-element-validity) for details. That post calls the purported inverse column `m`; I named it `canonical_inv` in this code.
* fix
* PR feedback
* naming
2022-02-21 00:39:04 -08:00
|
|
|
let in_1 = local_values[COL_ADD_INPUT_0];
|
|
|
|
|
let in_2 = local_values[COL_ADD_INPUT_1];
|
|
|
|
|
let in_3 = local_values[COL_ADD_INPUT_2];
|
|
|
|
|
let out_1 = local_values[COL_ADD_OUTPUT_0];
|
|
|
|
|
let out_2 = local_values[COL_ADD_OUTPUT_1];
|
|
|
|
|
let out_3 = local_values[COL_ADD_OUTPUT_2];
|
2022-02-10 12:05:04 -08:00
|
|
|
|
|
|
|
|
let limb_base = builder.constant(F::from_canonical_u64(1 << 16));
|
|
|
|
|
// Note that this can't overflow. Since each output limb has been range checked as 16-bits,
|
|
|
|
|
// this sum can be around 48 bits at most.
|
2022-05-17 11:04:35 +02:00
|
|
|
let out = reduce_with_powers_ext_circuit(builder, &[out_1, out_2, out_3], limb_base);
|
2022-02-10 12:05:04 -08:00
|
|
|
|
2022-06-03 18:06:14 +02:00
|
|
|
let computed_out = builder.add_many_extension([in_1, in_2, in_3]);
|
2022-02-10 12:05:04 -08:00
|
|
|
|
|
|
|
|
let diff = builder.sub_extension(out, computed_out);
|
|
|
|
|
let filtered_diff = builder.mul_extension(is_add, diff);
|
2022-02-20 17:48:31 -07:00
|
|
|
yield_constr.constraint(builder, filtered_diff);
|
2022-02-10 12:05:04 -08:00
|
|
|
}
|