Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: docs and cleanup #123

Closed
wants to merge 7 commits into from
Closed
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
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ We recommend running the tests using the `--release` flag, as they are quite slo
cargo test --release
```

## Usage


## Building an AIR computation using Curta

Expand All @@ -31,4 +31,7 @@ cargo test --release


## Integrating into a Plonky2 circuit
Curta starks can be integrated into a [Plonky2](https://github.com/mir-protocol/plonky2) circuit
Curta starks can be integrated into a [Plonky2](https://github.com/mir-protocol/plonky2) circuit

# About the name
This library is named after the Curta mechanical calculator invented by Curt Herzstark. It is a small, hand-cranked device that can be used to perform addition, subtraction, multiplication, division, and, with more difficulty, square roots and other operations. Calculations can be preformed by entering a number, turning the crank, and reading the result from the display. Curta is able to perform calculations with up to 15 digits of precision in a hand-held device. The Curta calculator was used by the US Navy, NASA, and other organizations until the 1970s.
64 changes: 64 additions & 0 deletions curta/examples/fibonacci.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//! A Fibonacci STARK.

use curta::chip::register::element::ElementRegister;
use curta::math::goldilocks::cubic::GoldilocksCubicParameters;
use curta::plonky2::stark::config::CurtaPoseidonGoldilocksConfig;
use curta::prelude::*;
use plonky2::field::goldilocks_field::GoldilocksField;
use plonky2::util::timing::TimingTree;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Fibonacci;

impl AirParameters for Fibonacci {
type Field = GoldilocksField;
type CubicParams = GoldilocksCubicParameters;

type Instruction = EmptyInstruction<GoldilocksField>;

const NUM_ARITHMETIC_COLUMNS: usize = 0;
const NUM_FREE_COLUMNS: usize = 3;
const EXTENDED_COLUMNS: usize = 3;
}

fn main() {
type L = Fibonacci;
type F = GoldilocksField;
type C = CurtaPoseidonGoldilocksConfig;

env_logger::init();

let mut builder = StarkBuilder::<L>::new();

let x_0 = builder.alloc::<ElementRegister>();
let x_1 = builder.alloc::<ElementRegister>();

builder.set_first_row_const(&x_0, &F::ZERO);
builder.set_first_row_const(&x_1, &F::ONE);

builder.set_next(&x_0, &x_1);
builder.set_next_expression(&x_1, x_0.expr() + x_1.expr());

let num_rows = 1 << 5;
let stark = builder.build::<C, 2>(num_rows);

let mut writer_data = AirWriterData::new(&stark.air_data, num_rows);

let air_data = &stark.air_data;
air_data.write_global_instructions(&mut writer_data.public_writer());
writer_data.chunks(num_rows).for_each(|mut chunk| {
for i in 0..num_rows {
let mut writer = chunk.window_writer(i);
air_data.write_trace_instructions(&mut writer);
}
});

let (trace, public) = (writer_data.trace, writer_data.public);

let proof = stark
.prove(&trace, &public, &mut TimingTree::default())
.unwrap();

stark.verify(proof.clone(), &public).unwrap();
}
103 changes: 103 additions & 0 deletions curta/examples/fibonacci_simd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//! A STARK to compute several Fibonacci functions in a single stark.

use curta::chip::register::element::ElementRegister;
use curta::math::goldilocks::cubic::GoldilocksCubicParameters;
use curta::plonky2::stark::config::CurtaPoseidonGoldilocksConfig;
use curta::prelude::*;
use plonky2::field::goldilocks_field::GoldilocksField;
use plonky2::util::timing::TimingTree;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FibonacciSIMD;

impl AirParameters for FibonacciSIMD {
type Field = GoldilocksField;
type CubicParams = GoldilocksCubicParameters;

type Instruction = EmptyInstruction<GoldilocksField>;

const NUM_ARITHMETIC_COLUMNS: usize = 0;
const NUM_FREE_COLUMNS: usize = 11;
const EXTENDED_COLUMNS: usize = 21;
}

fn fibonacci(n: usize) -> u64 {
(0..n).fold((0, 1), |(x_0, x_1), _| (x_1, x_0 + x_1)).1
}

fn main() {
type L = FibonacciSIMD;
type F = GoldilocksField;
type C = CurtaPoseidonGoldilocksConfig;

env_logger::init();

let mut builder = StarkBuilder::<L>::new();

let fib_len = 32;
let num_ops = 1024;

// Initialze auxilary variable for SIMD control flow. These are explicit to allow the user to
// have maximum control, but this comes at a cost of some bioler-plate code.

// Make a cycle of length 32 which gives us access to a start bit and an end bit. We will only
// be using the end bit in this example.
let cycle_fib = builder.cycle(5);
// Using the end bit, we can get a process_id, which will give us a unique identifier for each
// computation thread in the AIR.
let idx = builder.process_id(fib_len, cycle_fib.end_bit);
let end_flag = builder.expression::<ElementRegister>(cycle_fib.end_bit.not_expr());

// Initialize slice pointers for the values of x_0 and x_1.
let x_0_slice = builder.uninit_slice();
let x_1_slice = builder.uninit_slice();

let x_0_init = builder.constant::<ElementRegister>(&F::ZERO);
let x_1_init = builder.constant::<ElementRegister>(&F::ONE);
let x_1_final = builder.constant::<ElementRegister>(&F::from_canonical_u64(fibonacci(fib_len)));

for i in 0..num_ops {
let index = i * fib_len;
builder.store(&x_0_slice.get(i), x_0_init, &Time::constant(index), None);

Check failure on line 62 in curta/examples/fibonacci_simd.rs

View workflow job for this annotation

GitHub Actions / Formatting and Clippy

this method takes 6 arguments but 4 arguments were supplied
builder.store(&x_1_slice.get(i), x_1_init, &Time::constant(index), None);

Check failure on line 63 in curta/examples/fibonacci_simd.rs

View workflow job for this annotation

GitHub Actions / Formatting and Clippy

this method takes 6 arguments but 4 arguments were supplied

builder.free(
&x_1_slice.get(i),
x_1_final,
&Time::constant(index + fib_len),
);
}

let clk = Time::from_element(builder.clk());
let x_0 = builder.load::<ElementRegister>(&x_0_slice.get_at(idx), &clk);

Check failure on line 73 in curta/examples/fibonacci_simd.rs

View workflow job for this annotation

GitHub Actions / Formatting and Clippy

this method takes 4 arguments but 2 arguments were supplied
let x_1 = builder.load::<ElementRegister>(&x_1_slice.get_at(idx), &clk);

Check failure on line 74 in curta/examples/fibonacci_simd.rs

View workflow job for this annotation

GitHub Actions / Formatting and Clippy

this method takes 4 arguments but 2 arguments were supplied

let sum = builder.add(x_0, x_1);
builder.store(&x_1_slice.get_at(idx), sum, &clk.advance(), None);

Check failure on line 77 in curta/examples/fibonacci_simd.rs

View workflow job for this annotation

GitHub Actions / Formatting and Clippy

this method takes 6 arguments but 4 arguments were supplied
builder.store(&x_0_slice.get_at(idx), x_1, &clk.advance(), Some(end_flag));

Check failure on line 78 in curta/examples/fibonacci_simd.rs

View workflow job for this annotation

GitHub Actions / Formatting and Clippy

this method takes 6 arguments but 4 arguments were supplied

let num_rows = num_ops * fib_len;
let stark = builder.build::<C, 2>(num_rows);

let mut writer_data = AirWriterData::new(&stark.air_data, num_rows);

// Write the initial values to the stark.
let mut public_writer = writer_data.public_writer();
let air_data = &stark.air_data;
air_data.write_global_instructions(&mut public_writer);
writer_data.chunks_par(fib_len).for_each(|mut chunk| {
for i in 0..fib_len {
let mut writer = chunk.window_writer(i);
air_data.write_trace_instructions(&mut writer);
}
});

let (trace, public) = (writer_data.trace, writer_data.public);

let proof = stark
.prove(&trace, &public, &mut TimingTree::default())
.unwrap();

stark.verify(proof.clone(), &public).unwrap();
}
8 changes: 0 additions & 8 deletions curta/src/air/curta_air.rs

This file was deleted.

9 changes: 9 additions & 0 deletions curta/src/air/extension/cubic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,33 @@ use crate::math::extension::cubic::element::CubicElement;
use crate::math::extension::cubic::extension::CubicExtension;
use crate::math::extension::cubic::parameters::CubicParameters;

/// Parser operations for the cubic extension field F[X]/(X^3 - X + 1).
///
/// All the methods are equipped with default implementations. Users can choose to override them
/// with their own implementations for better performance.
pub trait CubicParser<E: CubicParameters<Self::Field>>: AirParser {
/// Get an extension field element from a base field element.
fn element_from_base_field(&mut self, value: Self::Var) -> CubicElement<Self::Var> {
CubicElement([value, self.zero(), self.zero()])
}

/// Get an extension field element from a slice of base field elements.
fn element_from_base_slice(&self, values: &[Self::Var]) -> CubicElement<Self::Var> {
assert!(values.len() == 3);
CubicElement([values[0], values[1], values[2]])
}

/// Get the coefficients of an extension field element.
fn as_base_array(&self, value: CubicElement<Self::Var>) -> [Self::Var; 3] {
value.0
}

/// The multiplicative identity of the extension field.
fn one_extension(&mut self) -> CubicElement<Self::Var> {
CubicElement([self.one(), self.zero(), self.zero()])
}

/// The additive identity of the extension field.
fn zero_extension(&mut self) -> CubicElement<Self::Var> {
CubicElement([self.zero(), self.zero(), self.zero()])
}
Expand Down
27 changes: 22 additions & 5 deletions curta/src/air/fibonacci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::air::RoundDatum;
use crate::math::prelude::*;
use crate::trace::AirTrace;

/// An AIR for computing Fibonacci numbers, used for testing.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct FibonacciAir;

Expand All @@ -14,10 +15,12 @@ impl FibonacciAir {
Self {}
}

/// Compute the nth Fibonacci number.
pub fn fibonacci<F: Field>(n: usize, x0: F, x1: F) -> F {
(0..n).fold((x0, x1), |x, _| (x.1, x.0 + x.1)).1
}

/// Generate a computational trace of Fibonacci numbers.
pub fn generate_trace<F: Field>(x0: F, x1: F, num_rows: usize) -> AirTrace<F> {
let trace_rows = (0..num_rows)
.scan([x0, x1], |acc, _| {
Expand Down Expand Up @@ -59,27 +62,41 @@ impl RAirData for FibonacciAir {

impl<AP: AirParser> RAir<AP> for FibonacciAir {
fn eval(&self, parser: &mut AP) {
// Check public inputs.
// We encode the coputation of the fibonacci numbers in an AIR by keeping track of two
// variables, x0 and x1, and updating them according to the following rules:
// x0_next = x1
// x_1_next = x0 + x1.
// The first two values of the sequence are given as public inputs, and the last value is
// the output of the computation.

// First, we constrain the public inputs to be the first two values of the sequence in the
// first row.
let pis_constraints = [
parser.sub(parser.local_slice()[0], parser.public_slice()[0]),
parser.sub(parser.local_slice()[1], parser.public_slice()[1]),
// parser.sub(parser.local_slice()[1], parser.global_slice()[2]),
];
parser.constraint_first_row(pis_constraints[0]);
parser.constraint_first_row(pis_constraints[1]);
// parser.constraint_last_row(pis_constraints[2]);

// x0' <- x1
// We then encode the transition constraints. Using the `AirParser` trait, we can access the
// values of the variables in the current row and the next row. The constrain is then given
// by:
// x0_next - x1 = 0
// x1_next - x0 - x1 = 0

// First, we constrain x0_next - x1 = 0.
let first_col_constraint = parser.sub(parser.next_slice()[0], parser.local_slice()[1]);
parser.constraint_transition(first_col_constraint);
// x1' <- x0 + x1

// Then, we constrain x1_next - x0 - x1 = 0.
let second_col_constraint = {
let tmp = parser.sub(parser.next_slice()[1], parser.local_slice()[0]);
parser.sub(tmp, parser.local_slice()[1])
};
parser.constraint_transition(second_col_constraint);
}

/// We don't need to do anything for the global constraints, as there are none.
fn eval_global(&self, _parser: &mut AP) {}
}

Expand Down
1 change: 0 additions & 1 deletion curta/src/air/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
pub mod curta_air;
pub mod extension;
pub mod opening;
pub mod parser;
Expand Down
Loading
Loading