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
27 changes: 24 additions & 3 deletions noir-r1cs/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,10 @@ impl R1CS {
match opcode {
Opcode::AssertZero(expr) => self.add_assert_zero(expr),

// TODO: Brillig is a VM used to generate witness values. It does not produce
// constraints.
// Brillig instructions are used by the ACVM to solve for ACIR witness values.
// Corresponding ACIR constraints are by Noir as AssertZeros, and we map all ACIR
// witness values to R1CS witness values, so we can safely ignore
// Opcode::BrilligCall.
Opcode::BrilligCall { .. } => {
println!("BrilligCall {:?}", opcode)
}
Expand Down Expand Up @@ -160,7 +162,6 @@ impl R1CS {
}
}
}
println!("self.constraints: {:?}", self.constraints);
}

/// Index of the constant one witness
Expand Down Expand Up @@ -268,3 +269,23 @@ impl R1CS {
self.add_constraint(&a, &b, &linear);
}
}

/// Print the R1CS matrices and the ACIR -> R1CS witness map, useful for debugging.
impl std::fmt::Display for R1CS {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if std::cmp::max(self.constraints, self.witnesses) > 15 {
println!("Matrices too large to print");
return Ok(());
}
writeln!(f, "ACIR witness <-> R1CS witness mapping:")?;
for (k, v) in &self.remap {
writeln!(f, "{k} <-> {v}")?;
}
writeln!(f, "Matrix A:")?;
write!(f, "{}", self.a)?;
writeln!(f, "Matrix B:")?;
write!(f, "{}", self.b)?;
writeln!(f, "Matrix C:")?;
write!(f, "{}", self.c)
}
}
70 changes: 20 additions & 50 deletions noir-r1cs/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use {
argh::FromArgs,
noirc_artifacts::program::ProgramArtifact,
rand::Rng,
std::{fs::File, iter::zip, path::PathBuf, vec},
std::{fs::File, path::PathBuf, vec},
tracing::{info, level_filters::LevelFilter},
tracing_subscriber::{self, fmt::format::FmtSpan, EnvFilter},
utils::{file_io::deserialize_witness_stack, PrintAbi},
Expand Down Expand Up @@ -72,9 +72,11 @@ fn noir(args: NoirCmd) -> AnyResult<()> {
"Program must have one entry point."
);
let main = &program.bytecode.functions[0];
let num_public_parameters = main.public_parameters.0.len();
let num_acir_witnesses = main.current_witness_index as usize;
info!(
"ACIR: {} witnesses, {} opcodes.",
main.current_witness_index,
num_acir_witnesses,
main.opcodes.len()
);

Expand All @@ -85,65 +87,30 @@ fn noir(args: NoirCmd) -> AnyResult<()> {

Comment thread
benjaminwilson marked this conversation as resolved.
let witness_stack = witness_stack.pop().unwrap().witness;

if num_acir_witnesses < 15 {
println!("ACIR witness values:");
(0..num_acir_witnesses).for_each(|i| {
println!("{}: {:?}", i, witness_stack[&Witness(i as u32)]);
});
}

// Create the R1CS relation
let mut r1cs = R1CS::new();
r1cs.add_circuit(main);

// just checking the private inputs for now
let mut private_inputs_original_witnesses = vec![];
let mut public_inputs_original_witnesses = vec![];

// Collect inputs and outputs
let public_inputs = main
.public_parameters
.0
.iter()
.map(|w| {
public_inputs_original_witnesses.push(w);
r1cs.map_witness(*w)
})
.collect::<Vec<_>>();
let public_outputs = main
.return_values
.0
.iter()
.map(|w| r1cs.map_witness(*w))
.collect::<Vec<_>>();
let private_inputs = main
.private_parameters
.iter()
.map(|w| {
private_inputs_original_witnesses.push(w);
r1cs.map_witness(*w)
})
.collect::<Vec<_>>();
print!("{}", r1cs);

info!(
"R1CS: {} witnesses, {} constraints",
r1cs.witnesses, r1cs.constraints
);
// dbg!(&r1cs);
dbg!(&public_inputs);
dbg!(&public_outputs);
dbg!(&private_inputs);

// Compute a satisfying witness
let mut witness = vec![None; r1cs.witnesses];
witness[0] = Some(FieldElement::one()); // Constant

// Inputs
for (witness_idx, original_witness_idx) in private_inputs
.iter()
.zip(private_inputs_original_witnesses.iter())
{
witness[*witness_idx] = Some(witness_stack[original_witness_idx])
}

for (witness_idx, original_witness_idx) in public_inputs
.iter()
.zip(public_inputs_original_witnesses.iter())
{
witness[*witness_idx] = Some(witness_stack[original_witness_idx])
// Fill in R1CS witness values with the pre-computed ACIR witness values
for (acir_witness_idx, witness_idx) in &r1cs.remap {
witness[*witness_idx] = Some(witness_stack[&Witness(*acir_witness_idx as u32)]);
}

// Solve constraints (this is how Noir expects it to be done, judging from ACVM)
Expand All @@ -158,7 +125,10 @@ fn noir(args: NoirCmd) -> AnyResult<()> {
(Some(a), Some(b), None) => (a * b, &r1cs.c),
(Some(a), None, Some(c)) => (c / a, &r1cs.b),
(None, Some(b), Some(c)) => (c / b, &r1cs.a),
_ => panic!("Can not solve constraint {row}."),
_ => {
dbg!(a, b, c);
panic!("Can not solve constraint {row}.")
},
};
let Some((col, val)) = solve_dot(mat.iter_row(row), &witness, val) else {
panic!("Could not solve constraint {row}.")
Expand Down Expand Up @@ -208,7 +178,7 @@ fn noir(args: NoirCmd) -> AnyResult<()> {
// dbg!(&b);
// dbg!(&c);

r1cs.write_json_to_file(public_inputs.len(), &witness, "r1cs.json")?;
r1cs.write_json_to_file(num_public_parameters, &witness, "r1cs.json")?;

Ok(())
}
Expand Down
30 changes: 28 additions & 2 deletions noir-r1cs/src/sparse_matrix.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::{
collections::BTreeMap,
ops::{Add, AddAssign, Index, IndexMut, Mul},
collections::BTreeMap, fmt::{Debug, Display, Formatter}, ops::{Add, AddAssign, Index, IndexMut, Mul}
};

/// A sparse matrix with elements of type `F`.
Expand Down Expand Up @@ -43,6 +42,33 @@ impl<F> SparseMatrix<F> {
assert!(col < self.cols, "column index out of bounds");
self.entries.insert((row, col), value);
}

/// Return a dense representation of the matrix.
/// (This is a helper method for debugging.)
fn to_dense(&self) -> Vec<Vec<F>>
Comment thread
benjaminwilson marked this conversation as resolved.
where
F: Clone,
{
let mut result = vec![vec![self.default.clone(); self.cols]; self.rows];
for ((i, j), value) in self.entries.iter() {
result[*i][*j] = value.clone();
}
result
}
}

/// Print a dense representation of the matrix, for debugging.
impl<F: Debug + Clone> Display for SparseMatrix<F> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let dense = self.to_dense();
for row in dense.iter() {
for value in row.iter() {
write!(f, "{:?}\t", value)?;
}
writeln!(f)?;
}
Ok(())
}
}

impl<F: PartialEq> SparseMatrix<F> {
Expand Down
Loading