Skip to content

Commit

Permalink
Merge pull request #190 from noir-lang/gd/ssa_arrays
Browse files Browse the repository at this point in the history
Gd/ssa arrays
  • Loading branch information
jfecher authored Mar 21, 2022
2 parents 9c3b96e + 79addc9 commit 81576d3
Show file tree
Hide file tree
Showing 29 changed files with 1,562 additions and 418 deletions.
71 changes: 70 additions & 1 deletion crates/acir/src/circuit/gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub struct XorGate {
pub num_bits: u32,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
// XXX: Gate does not capture what this is anymore. I think IR/OPCODE would be a better name
pub enum Gate {
Arithmetic(Arithmetic),
Expand All @@ -42,6 +42,75 @@ impl Gate {
}
}

impl std::fmt::Debug for Gate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut result = String::new();
match self {
Gate::Arithmetic(a) => {
for i in &a.mul_terms {
result += &format!(
"{:?}x{}*x{} + ",
i.0,
i.1.witness_index(),
i.2.witness_index()
);
}
for i in &a.linear_combinations {
result += &format!("{:?}x{} + ", i.0, i.1.witness_index());
}
result += &format!("{:?} = 0", a.q_c);
}
Gate::Range(w, s) => {
result = format!("x{} is {} bits", w.witness_index(), s);
}
Gate::Directive(Directive::Invert { x, result: r }) => {
result = format!("1/{}={}, or 0", x.witness_index(), r.witness_index());
}
Gate::Directive(Directive::Truncate {
a,
b,
c: _c,
bit_size,
}) => {
result = format!(
"Truncate: x{} is x{} truncated to {} bits",
b.witness_index(),
a.witness_index(),
bit_size
);
}
Gate::Directive(Directive::Quotient { a, b, q, r }) => {
result = format!(
"Euclidian division: x{} = x{}*x{} + x{}",
a.witness_index(),
q.witness_index(),
b.witness_index(),
r.witness_index()
);
}
Gate::Directive(Directive::Oddrange { a, b, r, bit_size }) => {
result = format!(
"Oddrange: x{} = x{}*2^{} + x{}",
a.witness_index(),
b.witness_index(),
bit_size,
r.witness_index()
);
}
Gate::And(g) => {
dbg!(&g);
}
Gate::Xor(g) => {
dbg!(&g);
}
Gate::GadgetCall(g) => {
dbg!(&g);
}
}
write!(f, "{}", result)
}
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
/// Directives do not apply any constraints.
pub enum Directive {
Expand Down
17 changes: 15 additions & 2 deletions crates/acir/src/optimiser/csat_optimiser.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::cmp::Ordering;

use crate::native_types::{Arithmetic, Witness};
use indexmap::IndexMap;
use noir_field::FieldElement;
Expand Down Expand Up @@ -144,8 +146,19 @@ impl Optimiser {
intermediate_gate.linear_combinations.push(left_wire_term);
intermediate_gate.linear_combinations.push(right_wire_term);
// Remove the left and right wires so we do not re-add them
gate.linear_combinations.remove(x);
gate.linear_combinations.remove(y);
match x.cmp(&y) {
Ordering::Greater => {
gate.linear_combinations.remove(x);
gate.linear_combinations.remove(y);
}
Ordering::Less => {
gate.linear_combinations.remove(y);
gate.linear_combinations.remove(x);
}
Ordering::Equal => {
gate.linear_combinations.remove(x);
}
}

// Now we have used up 2 spaces in our arithmetic gate. The width now dictates, how many more we can add
let remaining_space = self.width - 2 - 1; // We minus 1 because we need an extra space to contain the intermediate variable
Expand Down
2 changes: 1 addition & 1 deletion crates/nargo/src/cli/contract_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub(crate) fn run(args: ArgMatches) -> Result<(), CliError> {
let driver = Resolver::resolve_root_config(&package_dir)?;

let backend = crate::backends::ConcreteBackend;
let compiled_program = driver.into_compiled_program(backend.np_language());
let compiled_program = driver.into_compiled_program(backend.np_language(), false);

let smart_contract_string = backend.eth_contract_from_cs(compiled_program.circuit);

Expand Down
8 changes: 7 additions & 1 deletion crates/nargo/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ pub fn start_cli() {
Arg::with_name("proof_name")
.help("The name of the proof")
.required(true),
)
.arg(
Arg::with_name("interactive")
.help("pause execution")
.required(false),
),
)
.get_matches();
Expand Down Expand Up @@ -103,6 +108,7 @@ fn write_to_file(bytes: &[u8], path: &Path) -> String {
// helper function which tests noir programs by trying to generate a proof and verify it
pub fn prove_and_verify(proof_name: &str, prg_dir: &Path) -> bool {
let tmp_dir = TempDir::new("p_and_v_tests").unwrap();
let proof_path = prove_cmd::prove_with_path(proof_name, prg_dir, &tmp_dir.into_path()).unwrap();
let proof_path =
prove_cmd::prove_with_path(proof_name, prg_dir, &tmp_dir.into_path(), false).unwrap();
verify_cmd::verify_with_path(prg_dir, &proof_path).unwrap()
}
20 changes: 15 additions & 5 deletions crates/nargo/src/cli/prove_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,28 @@ pub(crate) fn run(args: ArgMatches) -> Result<(), CliError> {
.unwrap()
.value_of("proof_name")
.unwrap();

prove(proof_name)
let interactive = args
.subcommand_matches("prove")
.unwrap()
.value_of("interactive");
let mut is_interactive = false;
if let Some(int) = interactive {
if int == "i" {
is_interactive = true;
}
}
prove(proof_name, is_interactive)
}

/// In Barretenberg, the proof system adds a zero witness in the first index,
/// So when we add witness values, their index start from 1.
const WITNESS_OFFSET: u32 = 1;

fn prove(proof_name: &str) -> Result<(), CliError> {
fn prove(proof_name: &str, interactive: bool) -> Result<(), CliError> {
let curr_dir = std::env::current_dir().unwrap();
let mut proof_path = PathBuf::new();
proof_path.push(PROOFS_DIR);
let result = prove_with_path(proof_name, curr_dir, proof_path);
let result = prove_with_path(proof_name, curr_dir, proof_path, interactive);
match result {
Ok(_) => Ok(()),
Err(e) => Err(e),
Expand Down Expand Up @@ -90,10 +99,11 @@ pub fn prove_with_path<P: AsRef<Path>>(
proof_name: &str,
program_dir: P,
proof_dir: P,
interactive: bool,
) -> Result<PathBuf, CliError> {
let driver = Resolver::resolve_root_config(program_dir.as_ref())?;
let backend = crate::backends::ConcreteBackend;
let compiled_program = driver.into_compiled_program(backend.np_language());
let compiled_program = driver.into_compiled_program(backend.np_language(), interactive);

// Parse the initial witness values
let witness_map = noirc_abi::input_parser::Format::Toml.parse(program_dir, PROVER_INPUT_FILE);
Expand Down
2 changes: 1 addition & 1 deletion crates/nargo/src/cli/verify_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ pub fn verify_with_path<P: AsRef<Path>>(program_dir: P, proof_path: P) -> Result
let driver = Resolver::resolve_root_config(program_dir.as_ref())?;
let backend = crate::backends::ConcreteBackend;

let compiled_program = driver.into_compiled_program(backend.np_language());
let compiled_program = driver.into_compiled_program(backend.np_language(), false);

let mut public_abi = compiled_program.abi.clone().unwrap().public_abi();
add_dummy_setpub_arr(&mut public_abi);
Expand Down
3 changes: 1 addition & 2 deletions crates/nargo/tests/run_examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
#[test]
fn run_examples() {
let examples_dir = format!("{}/../../examples", env!("CARGO_MANIFEST_DIR"));

let paths = std::fs::read_dir(&examples_dir)
.expect(&format!("Could not read from directory {examples_dir}"));
.expect(&format!("Could not read from directory {}", examples_dir));

for path in paths {
let path = path.unwrap().path();
Expand Down
1 change: 0 additions & 1 deletion crates/nargo/tests/test_data/5_over/src/main.nr
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// Test unsafe integer arithmetic
// minimal failing test ...to debug!

fn main(x : u32, y : u32) {
x = x * x;
Expand Down
5 changes: 5 additions & 0 deletions crates/nargo/tests/test_data/6_array/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
authors = [""]
compiler_version = "0.1"

[dependencies]
9 changes: 9 additions & 0 deletions crates/nargo/tests/test_data/6_array/Prover.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
x = [104, 101, 108, 108, 111]
y = [10, 81, 18, 48, 0]
z = "59"
t = "10"

#7128
#15309
#16349

1 change: 1 addition & 0 deletions crates/nargo/tests/test_data/6_array/Verifier.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
setpub = []
44 changes: 44 additions & 0 deletions crates/nargo/tests/test_data/6_array/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//Basic tests for arrays

fn main(x : [5]u32 , y : [5]u32 , z : u32, t : u32) {
let c = 2301 as u32;
//t= t+x[0]-x[0];
z=y[4];
//Test 1:
for i in 0..5 {
c = z*z*y[i];
z = z - c;
};
constrain (z==0); //y[4]=0, so c and z are always 0

//Test 2:
c = 2301 as u32;
for i in 0..5 {
c = t+2;
c = z*z*x[i];
z = z + x[i]*y[i] - c;
};
constrain (z==3814912846);

//Test 3:
c = 2300001 as u32;
z=y[4];
for i in 0..5 {
z = z + x[i]*y[i];
for _i in 0..3 {
c = i as u32 - 2 as u32;
z=c*z;
};
};
constrain (z==41472);

//Test 4:
z=y[4];
for i in 0..3 {
z = z + x[i]*y[i];
for j in 0..2 {
z = z + x[i+j]-y[i+j];
};
};
constrain (z ==11539);
}
2 changes: 1 addition & 1 deletion crates/nargo/tests/test_data/config.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

# List of tests to be excluded (i.e not run), as their directory name in test_data
exclude = []
exclude = ["6_array"]

# List of tests (as their directory name in test_data) expecting to fail: if the test pass, we report an error.
fail = []
3 changes: 3 additions & 0 deletions crates/noir_field/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ blake2 = "0.9.1"
cfg-if = "1.0.0"
serde = { version = "1.0.136", features = ["derive"] }

num-bigint = "0.4"
num-traits = "0.2.8"

[dev-dependencies]
ark-bn254 = { version = "^0.3.0", features = ["curve"] }

Expand Down
52 changes: 50 additions & 2 deletions crates/noir_field/src/generic_ark.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,62 @@
use ark_ff::to_bytes;
use ark_ff::FpParameters;
use ark_ff::One;
use ark_ff::PrimeField;

use ark_ff::Zero;
use num_bigint::BigUint;
use serde::{Deserialize, Serialize};

// XXX: Switch out for a trait and proper implementations
// This implementation is in-efficient, can definitely remove hex usage and Iterator instances for trivial functionality
#[derive(Clone, Copy, Debug, Eq, PartialOrd, Ord)]
#[derive(Clone, Copy, Eq, PartialOrd, Ord)]
pub struct FieldElement<F: PrimeField>(F);

impl<F: PrimeField> std::fmt::Display for FieldElement<F> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let big_f = BigUint::from_bytes_be(&self.to_bytes());
let s = big_f.bits();
let big_s = BigUint::one() << s;
if big_s == big_f {
return write!(f, "2^{}", s);
}
if big_f == BigUint::zero() {
return write!(f, "0");
}
let big_minus = BigUint::from_bytes_be(&(self.neg()).to_bytes());
if big_minus.to_string().len() < big_f.to_string().len() {
return write!(f, "-{}", big_minus);
}
write!(f, "{}", big_f)
}
}

impl<F: PrimeField> std::fmt::Debug for FieldElement<F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if *self == -FieldElement::one() {
return write!(f, "-");
}
if *self == FieldElement::one() {
return write!(f, "");
}

let big_f = BigUint::from_bytes_be(&self.to_bytes());
if big_f == BigUint::zero() {
return write!(f, "0");
}

let s = big_f.bits();
let big_s = BigUint::one() << s;
if big_s == big_f {
return write!(f, "2^{}", s);
}
if big_f.clone() % BigUint::from(2_u128).pow(32) == BigUint::zero() {
return write!(f, "2^32*{}", big_f / BigUint::from(2_u128).pow(32));
}

write!(f, "{}", self)
}
}

impl<F: PrimeField> std::hash::Hash for FieldElement<F> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
state.write(&self.to_bytes())
Expand Down
10 changes: 7 additions & 3 deletions crates/noirc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl Driver {
pub fn compile_file(root_file: PathBuf, np_language: acvm::Language) -> CompiledProgram {
let mut driver = Driver::new();
driver.create_local_crate(root_file, CrateType::Binary);
driver.into_compiled_program(np_language)
driver.into_compiled_program(np_language, false)
}

/// Compiles a file and returns true if compilation was successful
Expand Down Expand Up @@ -154,7 +154,11 @@ impl Driver {
Some(abi)
}

pub fn into_compiled_program(mut self, np_language: acvm::Language) -> CompiledProgram {
pub fn into_compiled_program(
mut self,
np_language: acvm::Language,
interactive: bool,
) -> CompiledProgram {
self.build();
// First find the local crate
// There is always a local crate
Expand All @@ -181,7 +185,7 @@ impl Driver {
let evaluator = Evaluator::new(main_function, &self.context);

// Compile Program
let circuit = match evaluator.compile(np_language) {
let circuit = match evaluator.compile(np_language, interactive) {
Ok(circuit) => circuit,
Err(err) => {
// The FileId here will be the file id of the file with the main file
Expand Down
Loading

0 comments on commit 81576d3

Please sign in to comment.