diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 93fe2bdc..ece6509b 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -15,6 +15,7 @@ binius_hash = { path = "../crates/hash" } binius_macros = { path = "../crates/macros" } binius_math = { path = "../crates/math" } binius_utils = { path = "../crates/utils", default-features = false } +bytemuck.workspace = true bumpalo.workspace = true bytesize.workspace = true clap = { version = "4.5.20", features = ["derive"] } @@ -120,6 +121,30 @@ path = "acc-disjoint-product.rs" name = "acc-eq-ind-partial-eval" path = "acc-eq-ind-partial-eval.rs" +[[example]] +name = "acc-multilinear-extension-transparent" +path = "acc-multilinear-extension-transparent.rs" + +[[example]] +name = "acc-select-row" +path = "acc-select-row.rs" + +[[example]] +name = "acc-shift-ind-partial-eq" +path = "acc-shift-ind-partial-eq.rs" + +[[example]] +name = "acc-step-down" +path = "acc-step-down.rs" + +[[example]] +name = "acc-step-up" +path = "acc-step-up.rs" + +[[example]] +name = "acc-tower-basis" +path = "acc-tower-basis.rs" + [lints.clippy] needless_range_loop = "allow" diff --git a/examples/acc-multilinear-extension-transparent.rs b/examples/acc-multilinear-extension-transparent.rs new file mode 100644 index 00000000..f9dd4570 --- /dev/null +++ b/examples/acc-multilinear-extension-transparent.rs @@ -0,0 +1,95 @@ +use binius_circuits::builder::ConstraintSystemBuilder; +use binius_core::{ + constraint_system::validate::validate_witness, transparent::MultilinearExtensionTransparent, +}; +use binius_field::{ + arch::OptimalUnderlier, as_packed_field::PackedType, underlier::WithUnderlier, BinaryField128b, + BinaryField1b, PackedField, +}; +use binius_utils::checked_arithmetics::log2_ceil_usize; +use bytemuck::{pod_collect_to_vec, Pod}; + +type U = OptimalUnderlier; +type F128 = BinaryField128b; +type F1 = BinaryField1b; + +// From a perspective of circuits creation, MultilinearExtensionTransparent can be used naturally for decomposing integers to bits +fn decompose_transparent_u64(builder: &mut ConstraintSystemBuilder, x: u64) { + builder.push_namespace("decompose_transparent_u64"); + + let log_bits = log2_ceil_usize(64); + + let broadcasted = vec![x; 1 << (PackedType::::LOG_WIDTH.saturating_sub(log_bits))]; + + let broadcasted_decomposed = into_packed_vec::>(&broadcasted); + + let transparent_id = builder + .add_transparent( + "transparent", + MultilinearExtensionTransparent::<_, PackedType, _>::from_values( + broadcasted_decomposed, + ) + .unwrap(), + ) + .unwrap(); + + if let Some(witness) = builder.witness() { + let mut transparent_witness = witness.new_column::(transparent_id); + let values = transparent_witness.as_mut_slice::(); + values.fill(x); + } + + builder.pop_namespace(); +} + +fn decompose_transparent_u32(builder: &mut ConstraintSystemBuilder, x: u32) { + builder.push_namespace("decompose_transparent_u32"); + + let log_bits = log2_ceil_usize(32); + + let broadcasted = vec![x; 1 << (PackedType::::LOG_WIDTH.saturating_sub(log_bits))]; + + let broadcasted_decomposed = into_packed_vec::>(&broadcasted); + + let transparent_id = builder + .add_transparent( + "transparent", + MultilinearExtensionTransparent::<_, PackedType, _>::from_values( + broadcasted_decomposed, + ) + .unwrap(), + ) + .unwrap(); + + if let Some(witness) = builder.witness() { + let mut transparent_witness = witness.new_column::(transparent_id); + let values = transparent_witness.as_mut_slice::(); + values.fill(x); + } + + builder.pop_namespace(); +} + +fn main() { + let allocator = bumpalo::Bump::new(); + let mut builder = ConstraintSystemBuilder::::new_with_witness(&allocator); + + decompose_transparent_u64(&mut builder, 0xff00ff00ff00ff00); + decompose_transparent_u32(&mut builder, 0x00ff00ff); + + let witness = builder.take_witness().unwrap(); + let constraints_system = builder.build().unwrap(); + + validate_witness(&constraints_system, &[], &witness).unwrap(); +} + +fn into_packed_vec

(src: &[impl Pod]) -> Vec

+where + P: PackedField + WithUnderlier, + P::Underlier: Pod, +{ + pod_collect_to_vec::<_, P::Underlier>(src) + .into_iter() + .map(P::from_underlier) + .collect() +} diff --git a/examples/acc-select-row.rs b/examples/acc-select-row.rs new file mode 100644 index 00000000..4337540b --- /dev/null +++ b/examples/acc-select-row.rs @@ -0,0 +1,35 @@ +use binius_circuits::builder::ConstraintSystemBuilder; +use binius_core::{ + constraint_system::validate::validate_witness, transparent::select_row::SelectRow, +}; +use binius_field::{arch::OptimalUnderlier, BinaryField128b, BinaryField8b}; + +type U = OptimalUnderlier; +type F128 = BinaryField128b; +type F8 = BinaryField8b; + +const LOG_SIZE: usize = 8; + +// SelectRow expects exactly one witness value at particular index to be set. +fn main() { + let allocator = bumpalo::Bump::new(); + let mut builder = ConstraintSystemBuilder::::new_with_witness(&allocator); + + let index = 58; + assert!(index < 1 << LOG_SIZE); + + let select_row = SelectRow::new(LOG_SIZE, index).unwrap(); + let transparent = builder.add_transparent("select_row", select_row).unwrap(); + + if let Some(witness) = builder.witness() { + let mut transparent_witness = witness.new_column::(transparent); + let values = transparent_witness.as_mut_slice::(); + + values[index] = 0x01; + } + + let witness = builder.take_witness().unwrap(); + let constraints_system = builder.build().unwrap(); + + validate_witness(&constraints_system, &[], &witness).unwrap(); +} diff --git a/examples/acc-shift-ind-partial-eq.rs b/examples/acc-shift-ind-partial-eq.rs new file mode 100644 index 00000000..74fa2bae --- /dev/null +++ b/examples/acc-shift-ind-partial-eq.rs @@ -0,0 +1,93 @@ +use binius_circuits::builder::ConstraintSystemBuilder; +use binius_core::{ + constraint_system::validate::validate_witness, oracle::ShiftVariant, + transparent::shift_ind::ShiftIndPartialEval, +}; +use binius_field::{arch::OptimalUnderlier, util::eq, BinaryField128b, Field}; + +type U = OptimalUnderlier; +type F128 = BinaryField128b; + +// ShiftIndPartialEval is a more elaborated version of EqIndPartialEval. Same idea with challenges, but a bit more +// elaborated evaluation algorithm is used +fn main() { + let allocator = bumpalo::Bump::new(); + let mut builder = ConstraintSystemBuilder::::new_with_witness(&allocator); + + let block_size = 3; + let shift_offset = 4; + // Challenges have to be F128, but actual values in the witness could be of smaller field + let challenges = vec![ + F128::new(0xff00ff00ff00ff00ff00ff00ff00ff00), + F128::new(0x1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f), + F128::new(0x2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f), + ]; + let shift_variant = ShiftVariant::LogicalLeft; + + assert_eq!(block_size, challenges.len()); + + let shift_ind = + ShiftIndPartialEval::new(block_size, shift_offset, shift_variant, challenges.clone()) + .unwrap(); + + let transparent = builder.add_transparent("shift_ind", shift_ind).unwrap(); + + if let Some(witness) = builder.witness() { + let mut transparent_witness = witness.new_column::(transparent); + let values = transparent_witness.as_mut_slice::(); + + let lexicographical_order_x = [ + vec![F128::new(0), F128::new(0), F128::new(0)], + vec![F128::new(1), F128::new(0), F128::new(0)], + vec![F128::new(0), F128::new(1), F128::new(0)], + vec![F128::new(1), F128::new(1), F128::new(0)], + vec![F128::new(0), F128::new(0), F128::new(1)], + vec![F128::new(1), F128::new(0), F128::new(1)], + vec![F128::new(0), F128::new(1), F128::new(1)], + vec![F128::new(1), F128::new(1), F128::new(1)], + ]; + + assert_eq!(lexicographical_order_x.len(), 1 << block_size); + + for (val, x) in values.iter_mut().zip(lexicographical_order_x.into_iter()) { + *val = compute(block_size, shift_offset, shift_variant, x, challenges.clone()).val(); + } + } + + let witness = builder.take_witness().unwrap(); + let constraints_system = builder.build().unwrap(); + + validate_witness(&constraints_system, &[], &witness).unwrap(); +} + +// Evaluation logic taken from ShiftIndPartialEval implementation +fn compute( + block_size: usize, + shift_offset: usize, + shift_variant: ShiftVariant, + x: Vec, + y: Vec, +) -> F128 { + let (mut s_ind_p, mut s_ind_pp) = (F128::ONE, F128::ZERO); + let (mut temp_p, mut temp_pp) = (F128::default(), F128::default()); + (0..block_size).for_each(|k| { + let o_k = shift_offset >> k; + let product = x[k] * y[k]; + if o_k % 2 == 1 { + temp_p = (y[k] - product) * s_ind_p; + temp_pp = (x[k] - product) * s_ind_p + eq(x[k], y[k]) * s_ind_pp; + } else { + temp_p = eq(x[k], y[k]) * s_ind_p + (y[k] - product) * s_ind_pp; + temp_pp = (x[k] - product) * s_ind_pp; + } + // roll over results + s_ind_p = temp_p; + s_ind_pp = temp_pp; + }); + + match shift_variant { + ShiftVariant::CircularLeft => s_ind_p + s_ind_pp, + ShiftVariant::LogicalLeft => s_ind_p, + ShiftVariant::LogicalRight => s_ind_pp, + } +} diff --git a/examples/acc-step-down.rs b/examples/acc-step-down.rs new file mode 100644 index 00000000..cbe3cd6e --- /dev/null +++ b/examples/acc-step-down.rs @@ -0,0 +1,34 @@ +use binius_circuits::builder::ConstraintSystemBuilder; +use binius_core::{ + constraint_system::validate::validate_witness, transparent::step_down::StepDown, +}; +use binius_field::{arch::OptimalUnderlier, BinaryField128b, BinaryField8b}; + +const LOG_SIZE: usize = 8; + +type U = OptimalUnderlier; +type F128 = BinaryField128b; +type F8 = BinaryField8b; + +// StepDown expects all bytes to be set before particular index specified as input +fn main() { + let allocator = bumpalo::Bump::new(); + let mut builder = ConstraintSystemBuilder::::new_with_witness(&allocator); + + let index = 10; + + let step_down = StepDown::new(LOG_SIZE, index).unwrap(); + let transparent = builder.add_transparent("step_down", step_down).unwrap(); + + if let Some(witness) = builder.witness() { + let mut transparent_witness = witness.new_column::(transparent); + let values = transparent_witness.as_mut_slice::(); + + values[0..index].fill(0x01); + } + + let witness = builder.take_witness().unwrap(); + let constraints_system = builder.build().unwrap(); + + validate_witness(&constraints_system, &[], &witness).unwrap(); +} diff --git a/examples/acc-step-up.rs b/examples/acc-step-up.rs new file mode 100644 index 00000000..e659352e --- /dev/null +++ b/examples/acc-step-up.rs @@ -0,0 +1,32 @@ +use binius_circuits::builder::ConstraintSystemBuilder; +use binius_core::{constraint_system::validate::validate_witness, transparent::step_up::StepUp}; +use binius_field::{arch::OptimalUnderlier, BinaryField128b, BinaryField8b}; + +type U = OptimalUnderlier; +type F128 = BinaryField128b; +type F8 = BinaryField8b; + +const LOG_SIZE: usize = 8; + +// StepUp expects all bytes to be unset before particular index specified as input (opposite to StepDown) +fn main() { + let allocator = bumpalo::Bump::new(); + let mut builder = ConstraintSystemBuilder::::new_with_witness(&allocator); + + let index = 10; + + let step_up = StepUp::new(LOG_SIZE, index).unwrap(); + let transparent = builder.add_transparent("step_up", step_up).unwrap(); + + if let Some(witness) = builder.witness() { + let mut transparent_witness = witness.new_column::(transparent); + let values = transparent_witness.as_mut_slice::(); + + values[index..].fill(0x01); + } + + let witness = builder.take_witness().unwrap(); + let constraints_system = builder.build().unwrap(); + + validate_witness(&constraints_system, &[], &witness).unwrap(); +} diff --git a/examples/acc-tower-basis.rs b/examples/acc-tower-basis.rs new file mode 100644 index 00000000..e99e6790 --- /dev/null +++ b/examples/acc-tower-basis.rs @@ -0,0 +1,60 @@ +use binius_circuits::builder::ConstraintSystemBuilder; +use binius_core::{ + constraint_system::validate::validate_witness, transparent::tower_basis::TowerBasis, +}; +use binius_field::{arch::OptimalUnderlier, BinaryField128b, Field, TowerField}; + +type U = OptimalUnderlier; +type F128 = BinaryField128b; + +// TowerBasis expects actually basis vectors written to the witness. +// The form of basis could vary depending on 'iota' and 'k' parameters +fn main() { + let allocator = bumpalo::Bump::new(); + let mut builder = ConstraintSystemBuilder::::new_with_witness(&allocator); + + let k = 3usize; + let iota = 4usize; + + assert!(k + iota < 8); + + let tower_basis = TowerBasis::new(k, iota).unwrap(); + let transparent = builder.add_transparent("tower_basis", tower_basis).unwrap(); + + if let Some(witness) = builder.witness() { + let mut transparent_witness = witness.new_column::(transparent); + let values = transparent_witness.as_mut_slice::(); + + let lexicographic_query = [ + vec![F128::new(0), F128::new(0), F128::new(0)], + vec![F128::new(1), F128::new(0), F128::new(0)], + vec![F128::new(0), F128::new(1), F128::new(0)], + vec![F128::new(1), F128::new(1), F128::new(0)], + vec![F128::new(0), F128::new(0), F128::new(1)], + vec![F128::new(1), F128::new(0), F128::new(1)], + vec![F128::new(0), F128::new(1), F128::new(1)], + vec![F128::new(1), F128::new(1), F128::new(1)], + ]; + + assert_eq!(lexicographic_query.len(), 1 << k); + + for (val, query) in values.iter_mut().zip(lexicographic_query.into_iter()) { + *val = compute(iota, query).val(); + } + } + + let witness = builder.take_witness().unwrap(); + let constraints_system = builder.build().unwrap(); + + validate_witness(&constraints_system, &[], &witness).unwrap(); +} + +fn compute(iota: usize, query: Vec) -> F128 { + let mut result = F128::ONE; + for (i, query_i) in query.iter().enumerate() { + let r_comp = F128::ONE - query_i; + let basis_elt = ::basis(iota + i, 1).unwrap(); + result *= r_comp + *query_i * basis_elt; + } + result +}