Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "main"
type = "bin"
authors = [""]

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
x = 2
y = 2410
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main(x: Field, y: Field) -> pub u16 {
(x as u16) + (y as u16)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "main"
type = "bin"
authors = [""]

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
x = 2222
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn main(x: u16) { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "main"
type = "bin"
authors = [""]

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
x = 2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn main(x: u8) { }
122 changes: 79 additions & 43 deletions noir-r1cs-logup/src/digits.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
use acir::{AcirField, FieldElement};
use crate::{compiler::R1CS, solver::WitnessBuilder};
use {
crate::{compiler::R1CS, solver::WitnessBuilder},
acir::{AcirField, FieldElement},
serde::{Deserialize, Serialize},
};

/// Allocates witnesses for the digital decomposition of the given witnesses into its digits in the
/// given bases. A log base is specified for each digit (permitting mixed base decompositions). The
/// order of bases is little-endian. Witnesses are grouped by digital place, in the order of the bases,
/// where each group of witnesses is in 1:1 correspondence with witnesses_to_decompose.
#[derive(Debug, Clone)]
/// Allocates witnesses for the digital decomposition of the given witnesses
/// into its digits in the given bases. A log base is specified for each digit
/// (permitting mixed base decompositions). The order of bases is little-endian.
/// Witnesses are grouped by digital place, in the order of the bases,
/// where each group of witnesses is in 1:1 correspondence with
/// witnesses_to_decompose.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct DigitalDecompositionWitnesses {
/// The log base of each digit (in little-endian order)
pub log_bases: Vec<usize>,
pub log_bases: Vec<usize>,
/// The number of witnesses to decompose
pub num_witnesses_to_decompose: usize,
/// Witness indices of the values to be decomposed
pub witnesses_to_decompose: Vec<usize>,
pub witnesses_to_decompose: Vec<usize>,
/// The index of the first witness written to
pub first_witness_idx: usize,
pub first_witness_idx: usize,
/// The number of witnesses written to
pub num_witnesses: usize,
pub num_witnesses: usize,
}

impl DigitalDecompositionWitnesses {
pub fn new(next_witness_idx: usize, log_bases: Vec<usize>, witnesses_to_decompose: Vec<usize>) -> Self {
pub fn new(
next_witness_idx: usize,
log_bases: Vec<usize>,
witnesses_to_decompose: Vec<usize>,
) -> Self {
let num_witnesses_to_decompose = witnesses_to_decompose.len();
let digital_decomp_length = log_bases.len();
Self {
Expand All @@ -32,9 +41,10 @@ impl DigitalDecompositionWitnesses {
}
}

/// Returns the witness index of the `value_offset`-th witness of the `digit_place`-th digit.
/// Note that `value_offset` is the index of the witness in the original list of witnesses (not
/// itself a witness index).
/// Returns the witness index of the `value_offset`-th witness of the
/// `digit_place`-th digit. Note that `value_offset` is the index of the
/// witness in the original list of witnesses (not itself a witness
/// index).
pub fn get_digit_witness_index(&self, digit_place: usize, value_offset: usize) -> usize {
debug_assert!(digit_place < self.log_bases.len());
debug_assert!(value_offset < self.num_witnesses_to_decompose);
Expand All @@ -43,51 +53,72 @@ impl DigitalDecompositionWitnesses {

/// Solve for the witness values allocated to the digital decomposition.
pub fn solve(&self, witness: &mut [FieldElement]) {
self.witnesses_to_decompose.iter().enumerate().for_each(|(i, value_witness_idx)| {
let value = witness[*value_witness_idx];
let digits = decompose_into_digits(value, &self.log_bases);
digits.iter().enumerate().for_each(|(digit_place, digit_value)| {
witness[self.first_witness_idx + digit_place * self.witnesses_to_decompose.len() + i] = *digit_value;
self.witnesses_to_decompose
.iter()
.enumerate()
.for_each(|(i, value_witness_idx)| {
let value = witness[*value_witness_idx];
let digits = decompose_into_digits(value, &self.log_bases);
digits
.iter()
.enumerate()
.for_each(|(digit_place, digit_value)| {
witness[self.first_witness_idx
+ digit_place * self.witnesses_to_decompose.len()
+ i] = *digit_value;
});
});
});
}
}

/// Adds the witnesses and constraints for the digital decomposition of the given witnesses in a
/// mixed base decomposition (see [DigitalDecompositionWitnesses]). Does NOT add constraints for
/// range checking the digits - this is left to the caller (as sometimes these range checks are
/// obviated e.g. by later lookups, as in the case of the binop codes).
/// Adds the witnesses and constraints for the digital decomposition of the
/// given witnesses in a mixed base decomposition (see
/// [DigitalDecompositionWitnesses]). Does NOT add constraints for
/// range checking the digits - this is left to the caller (as sometimes these
/// range checks are obviated e.g. by later lookups, as in the case of the binop
/// codes).
pub(crate) fn add_digital_decomposition(
r1cs: &mut R1CS,
log_bases: Vec<usize>,
witnesses_to_decompose: Vec<usize>,
) -> DigitalDecompositionWitnesses {
let next_witness_idx = r1cs.num_witnesses();
let dd_struct = DigitalDecompositionWitnesses::new(next_witness_idx, log_bases.clone(), witnesses_to_decompose);
let dd_struct = DigitalDecompositionWitnesses::new(
next_witness_idx,
log_bases.clone(),
witnesses_to_decompose,
);
r1cs.add_witness_builder(WitnessBuilder::DigitalDecomposition(dd_struct.clone()));
// Add the constraints for the digital recomposition
let mut digit_multipliers = vec![FieldElement::one()];
for log_base in log_bases[..log_bases.len() - 1].iter() {
let multiplier = *digit_multipliers.last().unwrap() * FieldElement::from(1u64 << *log_base);
digit_multipliers.push(multiplier);
}
dd_struct.witnesses_to_decompose.iter().enumerate().for_each(|(i, value)| {
let mut recomp_summands = vec![];
digit_multipliers.iter().enumerate().for_each(|(digit_place, digit_multiplier)| {
let digit_witness = dd_struct.get_digit_witness_index(digit_place, i);
recomp_summands.push((FieldElement::from(*digit_multiplier), digit_witness));
dd_struct
.witnesses_to_decompose
.iter()
.enumerate()
.for_each(|(i, value)| {
let mut recomp_summands = vec![];
digit_multipliers
.iter()
.enumerate()
.for_each(|(digit_place, digit_multiplier)| {
let digit_witness = dd_struct.get_digit_witness_index(digit_place, i);
recomp_summands.push((FieldElement::from(*digit_multiplier), digit_witness));
});
r1cs.matrices.add_constraint(
&[(FieldElement::one(), r1cs.witness_one())],
&[(FieldElement::one(), *value)],
&recomp_summands,
);
});
r1cs.matrices.add_constraint(
&[(FieldElement::one(), r1cs.witness_one())],
&[(FieldElement::one(), *value)],
&recomp_summands,
);
});
dd_struct
}

/// Compute a mixed-base decomposition of a field element into its digits, using the given log bases.
/// Decomposition is little-endian.
/// Compute a mixed-base decomposition of a field element into its digits, using
/// the given log bases. Decomposition is little-endian.
/// Panics if the value provided can not be represented in the given bases.
pub(crate) fn decompose_into_digits(
value: FieldElement,
Expand All @@ -96,7 +127,8 @@ pub(crate) fn decompose_into_digits(
let num_digits = log_bases.len();
let mut digits = vec![FieldElement::zero(); num_digits];
let value_bits = field_to_le_bits(value);
// Grab the bits of the element that we need for each digit, and turn them back into field elements.
// Grab the bits of the element that we need for each digit, and turn them back
// into field elements.
let mut start_bit = 0;
for digit_idx in 0..num_digits {
let log_base = log_bases[digit_idx];
Expand All @@ -106,7 +138,10 @@ pub(crate) fn decompose_into_digits(
start_bit += log_base;
}
let remaining_bits = &value_bits[start_bit..];
assert!(remaining_bits.iter().all(|&bit| !bit), "Higher order bits are not zero");
assert!(
remaining_bits.iter().all(|&bit| !bit),
"Higher order bits are not zero"
);
digits
}

Expand All @@ -120,8 +155,9 @@ pub(crate) fn field_to_le_bits(value: FieldElement) -> Vec<bool> {
.collect()
}

/// Given the binary representation of a field element in little-endian order, convert it to a field
/// element. The input is padded to the next multiple of 8 bits.
/// Given the binary representation of a field element in little-endian order,
/// convert it to a field element. The input is padded to the next multiple of 8
/// bits.
pub(crate) fn le_bits_to_field(bits: &[bool]) -> FieldElement {
let next_multiple_of_8 = bits.len().div_ceil(8) * 8;
let padding_amt = next_multiple_of_8 - bits.len();
Expand Down
Loading
Loading