plonky2/evm/src/cpu/simple_logic/eq_iszero.rs
Daniel Lubarov 3d83d63f0b Shared CPU columns
I was thinking we could have two sets of shared columns:
- First, a set of "core" columns which would contain instruction decoding registers during an execution cycle, or some counter data during a kernel bootloading cycle.
- Second, a set of "general" columns which would be more general-purpose. For now it could contain "looking" columns for most CTLs (Keccak, arithmetic and logic; NOT memory since memory can be used simultaneously with the others). It could potentially be reused for other things too, such as the registers used for `EQ` and `IS_ZERO` (but I know it's nontrivial to share those since we would need to use lower-degree constraints, so I wouldn't bother for now).

This PR implements just the latter. If it looks good I'll proceed with the former afterward.
2022-07-18 12:15:41 -07:00

154 lines
5.1 KiB
Rust

use plonky2::field::extension::Extendable;
use plonky2::field::packed::PackedField;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::ext_target::ExtensionTarget;
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
use crate::cpu::columns::CpuColumnsView;
const LIMB_SIZE: usize = 16;
pub fn generate<F: RichField>(lv: &mut CpuColumnsView<F>) {
let logic = lv.general.logic_mut();
let eq_filter = lv.is_eq.to_canonical_u64();
let iszero_filter = lv.is_iszero.to_canonical_u64();
assert!(eq_filter <= 1);
assert!(iszero_filter <= 1);
assert!(eq_filter + iszero_filter <= 1);
if eq_filter != 1 && iszero_filter != 1 {
return;
}
let diffs = if eq_filter == 1 {
logic
.input0
.into_iter()
.zip(logic.input1)
.map(|(in0, in1)| {
assert_eq!(in0.to_canonical_u64() >> LIMB_SIZE, 0);
assert_eq!(in1.to_canonical_u64() >> LIMB_SIZE, 0);
let diff = in0 - in1;
diff.square()
})
.sum()
} else if iszero_filter == 1 {
logic.input0.into_iter().sum()
} else {
panic!()
};
lv.simple_logic_diff = diffs;
lv.simple_logic_diff_inv = diffs.try_inverse().unwrap_or(F::ZERO);
logic.output[0] = F::from_bool(diffs == F::ZERO);
for out_limb_ref in logic.output[1..].iter_mut() {
*out_limb_ref = F::ZERO;
}
}
pub fn eval_packed<P: PackedField>(
lv: &CpuColumnsView<P>,
yield_constr: &mut ConstraintConsumer<P>,
) {
let logic = lv.general.logic();
let eq_filter = lv.is_eq;
let iszero_filter = lv.is_iszero;
let eq_or_iszero_filter = eq_filter + iszero_filter;
let ls_bit = logic.output[0];
// Handle EQ and ISZERO. Most limbs of the output are 0, but the least-significant one is
// either 0 or 1.
yield_constr.constraint(eq_or_iszero_filter * ls_bit * (ls_bit - P::ONES));
for &bit in &logic.output[1..] {
yield_constr.constraint(eq_or_iszero_filter * bit);
}
// Check SIMPLE_LOGIC_DIFF
let diffs = lv.simple_logic_diff;
let diffs_inv = lv.simple_logic_diff_inv;
{
let input0_sum: P = logic.input0.into_iter().sum();
yield_constr.constraint(iszero_filter * (diffs - input0_sum));
let sum_squared_diffs: P = logic
.input0
.into_iter()
.zip(logic.input1)
.map(|(in0, in1)| (in0 - in1).square())
.sum();
yield_constr.constraint(eq_filter * (diffs - sum_squared_diffs));
}
// diffs != 0 => ls_bit == 0
yield_constr.constraint(eq_or_iszero_filter * diffs * ls_bit);
// ls_bit == 0 => diffs != 0 (we provide a diffs_inv)
yield_constr.constraint(eq_or_iszero_filter * (diffs * diffs_inv + ls_bit - P::ONES));
}
pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
lv: &CpuColumnsView<ExtensionTarget<D>>,
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
) {
let logic = lv.general.logic();
let eq_filter = lv.is_eq;
let iszero_filter = lv.is_iszero;
let eq_or_iszero_filter = builder.add_extension(eq_filter, iszero_filter);
let ls_bit = logic.output[0];
// Handle EQ and ISZERO. Most limbs of the output are 0, but the least-significant one is
// either 0 or 1.
{
let constr = builder.mul_sub_extension(ls_bit, ls_bit, ls_bit);
let constr = builder.mul_extension(eq_or_iszero_filter, constr);
yield_constr.constraint(builder, constr);
}
for &bit in &logic.output[1..] {
let constr = builder.mul_extension(eq_or_iszero_filter, bit);
yield_constr.constraint(builder, constr);
}
// Check SIMPLE_LOGIC_DIFF
let diffs = lv.simple_logic_diff;
let diffs_inv = lv.simple_logic_diff_inv;
{
let input0_sum = builder.add_many_extension(logic.input0);
{
let constr = builder.sub_extension(diffs, input0_sum);
let constr = builder.mul_extension(iszero_filter, constr);
yield_constr.constraint(builder, constr);
}
let sum_squared_diffs = logic.input0.into_iter().zip(logic.input1).fold(
builder.zero_extension(),
|acc, (in0, in1)| {
let diff = builder.sub_extension(in0, in1);
builder.mul_add_extension(diff, diff, acc)
},
);
{
let constr = builder.sub_extension(diffs, sum_squared_diffs);
let constr = builder.mul_extension(eq_filter, constr);
yield_constr.constraint(builder, constr);
}
}
{
// diffs != 0 => ls_bit == 0
let constr = builder.mul_extension(diffs, ls_bit);
let constr = builder.mul_extension(eq_or_iszero_filter, constr);
yield_constr.constraint(builder, constr);
}
{
// ls_bit == 0 => diffs != 0 (we provide a diffs_inv)
let constr = builder.mul_add_extension(diffs, diffs_inv, ls_bit);
let constr = builder.mul_sub_extension(eq_or_iszero_filter, constr, eq_or_iszero_filter);
yield_constr.constraint(builder, constr);
}
}