forked from IrreducibleOSS/binius
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
examples: Transparent columns usage (part 1) (#8)
* feat: Add example of Transparent (Constant) column usage * example: Add example of Transparent (Powers) column usage * example: Add example of Transparent (DisjointProduct) column usage * example: Add example of Transparent (EqIndPartialEval) column usage
- Loading branch information
Showing
5 changed files
with
326 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
use binius_circuits::{builder::ConstraintSystemBuilder, sha256::u32const_repeating}; | ||
use binius_core::{ | ||
constraint_system::validate::validate_witness, oracle::OracleId, | ||
transparent::constant::Constant, | ||
}; | ||
use binius_field::{arch::OptimalUnderlier, BinaryField128b, BinaryField1b, BinaryField32b}; | ||
|
||
type U = OptimalUnderlier; | ||
type F128 = BinaryField128b; | ||
type F32 = BinaryField32b; | ||
type F1 = BinaryField1b; | ||
|
||
const LOG_SIZE: usize = 4; | ||
|
||
// FIXME: Following gadgets are unconstrained. Only for demonstrative purpose, don't use in production | ||
|
||
fn constants_gadget( | ||
name: impl ToString, | ||
log_size: usize, | ||
builder: &mut ConstraintSystemBuilder<U, F128>, | ||
constant_value: u32, | ||
) -> OracleId { | ||
builder.push_namespace(name); | ||
|
||
let c = Constant::new(log_size, F32::new(constant_value)); | ||
|
||
let oracle = builder.add_transparent("constant", c).unwrap(); | ||
|
||
if let Some(witness) = builder.witness() { | ||
let mut oracle_witness = witness.new_column::<F32>(oracle); | ||
let values = oracle_witness.as_mut_slice::<u32>(); | ||
for v in values { | ||
*v = constant_value; | ||
} | ||
} | ||
|
||
builder.pop_namespace(); | ||
|
||
oracle | ||
} | ||
|
||
// Transparent column can also naturally be used for storing some constants (also available for verifier). | ||
// For example there is a 'u32const_repeating' function (in sha256 gadget) that does exactly this | ||
// using Transparent + Repeated columns. Alternatively one can use Constant abstraction to create equivalent | ||
// Transparent column. | ||
fn main() { | ||
let allocator = bumpalo::Bump::new(); | ||
let mut builder = ConstraintSystemBuilder::<U, F128>::new_with_witness(&allocator); | ||
|
||
pub const SHA256_INIT: [u32; 8] = [ | ||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, | ||
0x5be0cd19, | ||
]; | ||
|
||
let oracles: [OracleId; 8] = | ||
SHA256_INIT.map(|c| u32const_repeating(LOG_SIZE, &mut builder, c, "INIT").unwrap()); | ||
if let Some(witness) = builder.witness() { | ||
for (index, oracle) in oracles.into_iter().enumerate() { | ||
let values = witness.get::<F1>(oracle).unwrap().as_slice::<u32>(); | ||
|
||
// every value in the column should match the expected one | ||
for value in values { | ||
assert_eq!(*value, SHA256_INIT[index]); | ||
} | ||
} | ||
} | ||
|
||
let oracles: [OracleId; 8] = | ||
SHA256_INIT.map(|c| constants_gadget("constants_gadget", LOG_SIZE, &mut builder, c)); | ||
if let Some(witness) = builder.witness() { | ||
for (index, oracle) in oracles.into_iter().enumerate() { | ||
// The difference is here. With Constant we have to operate over F32, while | ||
// with Transparent + Repeated approach as in 'u32const_repeating' we operate over F1, | ||
// which can be more convenient in the bit-oriented computations | ||
let values = witness.get::<F32>(oracle).unwrap().as_slice::<u32>(); | ||
|
||
// every value in the column should match the expected one | ||
for value in values { | ||
assert_eq!(*value, SHA256_INIT[index]); | ||
} | ||
} | ||
} | ||
|
||
let witness = builder.take_witness().unwrap(); | ||
let constraints_system = builder.build().unwrap(); | ||
|
||
validate_witness(&constraints_system, &[], &witness).unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
use binius_circuits::builder::ConstraintSystemBuilder; | ||
use binius_core::{ | ||
constraint_system::validate::validate_witness, | ||
transparent::{constant::Constant, disjoint_product::DisjointProduct, powers::Powers}, | ||
}; | ||
use binius_field::{ | ||
arch::OptimalUnderlier, BinaryField, BinaryField128b, BinaryField8b, PackedField, | ||
}; | ||
|
||
type U = OptimalUnderlier; | ||
type F128 = BinaryField128b; | ||
type F8 = BinaryField8b; | ||
|
||
const LOG_SIZE: usize = 4; | ||
|
||
// FIXME: Following gadgets are unconstrained. Only for demonstrative purpose, don't use in production | ||
|
||
// DisjointProduct can be used for creating some more elaborated regularities over public data. | ||
// In the following example we have a Transparent column with DisjointProduct instantiated over Powers | ||
// and Constant. In this regularity, the DisjointProduct would be represented as a following expression: | ||
// | ||
// [ c * F8(x)^0, c * F8(x)^1, c * F8(x)^2, ... c * F8(x)^(2^LOG_SIZE) ], | ||
// | ||
// where | ||
// 'x' is a multiplicative generator - a public value that exists for every BinaryField, | ||
// 'c' is some (F8) constant. | ||
// | ||
// Also note, that DisjointProduct makes eventual Transparent column to have height (n_vars) which is sum | ||
// of heights (n_vars) of Powers and Constant, so actual data could be repeated multiple times | ||
fn main() { | ||
let allocator = bumpalo::Bump::new(); | ||
let mut builder = ConstraintSystemBuilder::<U, F128>::new_with_witness(&allocator); | ||
|
||
let generator = F8::MULTIPLICATIVE_GENERATOR; | ||
let powers = Powers::new(LOG_SIZE, generator.into()); | ||
|
||
let constant_value = F8::new(0xf0); | ||
let constant = Constant::new(LOG_SIZE, constant_value); | ||
|
||
let disjoint_product = DisjointProduct(powers, constant); | ||
let disjoint_product_id = builder | ||
.add_transparent("disjoint_product", disjoint_product) | ||
.unwrap(); | ||
|
||
if let Some(witness) = builder.witness() { | ||
let mut disjoint_product_witness = witness.new_column::<F8>(disjoint_product_id); | ||
|
||
let values = disjoint_product_witness.as_mut_slice::<F8>(); | ||
|
||
let mut exponent = 0u64; | ||
for val in values.iter_mut() { | ||
if exponent == 2u64.pow(LOG_SIZE as u32) { | ||
exponent = 0; | ||
} | ||
*val = generator.pow(exponent) * constant_value; | ||
exponent += 1; | ||
} | ||
} | ||
|
||
let witness = builder.take_witness().unwrap(); | ||
let constraints_system = builder.build().unwrap(); | ||
|
||
validate_witness(&constraints_system, &[], &witness).unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
use binius_circuits::builder::ConstraintSystemBuilder; | ||
use binius_core::{ | ||
constraint_system::validate::validate_witness, transparent::eq_ind::EqIndPartialEval, | ||
}; | ||
use binius_field::{arch::OptimalUnderlier, BinaryField128b, PackedField}; | ||
|
||
type U = OptimalUnderlier; | ||
type F128 = BinaryField128b; | ||
|
||
const LOG_SIZE: usize = 3; | ||
|
||
// FIXME: Following gadgets are unconstrained. Only for demonstrative purpose, don't use in production | ||
|
||
// Currently, it is hard for me to imagine some real world use-cases where Transparent column specified by | ||
// EqIndPartialEval could be useful. The program can use some of its data as challenges and the Transparent | ||
// column with EqIndPartialEval will expect witness values defined as following: | ||
// | ||
// x_i * y_i + (1 - x_i) * (1 - y_i) | ||
// | ||
// where 'x_i' is an element from a particular row of basis matrix, and y_i is a given challenge. | ||
// | ||
fn main() { | ||
let allocator = bumpalo::Bump::new(); | ||
let mut builder = ConstraintSystemBuilder::<U, F128>::new_with_witness(&allocator); | ||
|
||
// A truth table [000, 001, 010, 011 ... 111] where each row is in reversed order | ||
let rev_basis = [ | ||
vec![0, 0, 0], | ||
vec![1, 0, 0], | ||
vec![0, 1, 0], | ||
vec![1, 1, 0], | ||
vec![0, 0, 1], | ||
vec![1, 0, 1], | ||
vec![0, 1, 1], | ||
vec![1, 1, 1], | ||
]; | ||
|
||
// rev_basis size correlates with LOG_SIZE | ||
assert_eq!(1 << LOG_SIZE, rev_basis.len()); | ||
|
||
// let's choose some random challenges (each not greater than 1 << LOG_SIZE bits for this example) | ||
let challenges = vec![F128::from(110), F128::from(190), F128::from(200)]; | ||
|
||
// challenges size correlates with LOG_SIZE | ||
assert_eq!(challenges.len(), LOG_SIZE); | ||
|
||
let eq_ind_partial_eval = EqIndPartialEval::new(LOG_SIZE, challenges.clone()).unwrap(); | ||
|
||
let id = builder | ||
.add_transparent("eq_ind_partial_eval", eq_ind_partial_eval) | ||
.unwrap(); | ||
|
||
if let Some(witness) = builder.witness() { | ||
let mut eq_witness = witness.new_column::<F128>(id); | ||
|
||
let column_values = eq_witness.as_mut_slice::<F128>(); | ||
assert_eq!(column_values.len(), 1 << LOG_SIZE); | ||
|
||
let one = F128::one(); | ||
|
||
for (inv_basis_item, val) in rev_basis.iter().zip(column_values.iter_mut()) { | ||
let mut value = F128::one(); | ||
inv_basis_item | ||
.iter() | ||
.zip(challenges.iter()) | ||
.for_each(|(x, y)| { | ||
let x = F128::new(*x); | ||
let y = *y; | ||
|
||
// following expression is defined in the EqIndPartialEval implementation | ||
value *= x * y + (one - x) * (one - y); | ||
}); | ||
*val = value; | ||
} | ||
} | ||
|
||
let witness = builder.take_witness().unwrap(); | ||
let constraints_system = builder.build().unwrap(); | ||
|
||
validate_witness(&constraints_system, &[], &witness).unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
use binius_circuits::builder::ConstraintSystemBuilder; | ||
use binius_core::constraint_system::validate::validate_witness; | ||
use binius_field::{ | ||
arch::OptimalUnderlier, BinaryField, BinaryField128b, BinaryField16b, BinaryField32b, | ||
PackedField, | ||
}; | ||
|
||
type U = OptimalUnderlier; | ||
type F128 = BinaryField128b; | ||
type F32 = BinaryField32b; | ||
type F16 = BinaryField16b; | ||
|
||
const LOG_SIZE: usize = 3; | ||
|
||
// FIXME: Following gadgets are unconstrained. Only for demonstrative purpose, don't use in production | ||
|
||
// Values for the Transparent columns are known to verifier, so they can be used for storing non-private data | ||
// (like constants for example). The following gadget demonstrates how to use Powers abstraction to build a | ||
// Transparent column that keeps following values (we write them during witness population): | ||
// | ||
// [ F32(x)^0, F32(x)^1 , F32(x)^2, ... F32(x)^(2^LOG_SIZE) ], | ||
|
||
// where 'x' is a multiplicative generator - a public value that exists for every BinaryField | ||
// | ||
fn powers_gadget_f32(builder: &mut ConstraintSystemBuilder<U, F128>, name: impl ToString) { | ||
builder.push_namespace(name); | ||
|
||
let generator = F32::MULTIPLICATIVE_GENERATOR; | ||
let powers = binius_core::transparent::powers::Powers::new(LOG_SIZE, generator.into()); | ||
let transparent = builder | ||
.add_transparent("Powers of F32 gen", powers) | ||
.unwrap(); | ||
|
||
if let Some(witness) = builder.witness() { | ||
let mut transparent_witness = witness.new_column::<F32>(transparent); | ||
let transparent_values = transparent_witness.as_mut_slice::<F32>(); | ||
for (exp, val) in transparent_values.iter_mut().enumerate() { | ||
*val = generator.pow(exp as u64); | ||
} | ||
} | ||
|
||
builder.pop_namespace(); | ||
} | ||
|
||
// Only Field is being changed | ||
fn powers_gadget_f16(builder: &mut ConstraintSystemBuilder<U, F128>, name: impl ToString) { | ||
builder.push_namespace(name); | ||
|
||
let generator = F16::MULTIPLICATIVE_GENERATOR; | ||
let powers = binius_core::transparent::powers::Powers::new(LOG_SIZE, generator.into()); | ||
let transparent = builder | ||
.add_transparent("Powers of F16 gen", powers) | ||
.unwrap(); | ||
|
||
if let Some(witness) = builder.witness() { | ||
let mut transparent_witness = witness.new_column::<F16>(transparent); | ||
let transparent_values = transparent_witness.as_mut_slice::<F16>(); | ||
for (exp, val) in transparent_values.iter_mut().enumerate() { | ||
*val = generator.pow(exp as u64); | ||
} | ||
} | ||
|
||
builder.pop_namespace(); | ||
} | ||
|
||
fn main() { | ||
let allocator = bumpalo::Bump::new(); | ||
let mut builder = ConstraintSystemBuilder::<U, F128>::new_with_witness(&allocator); | ||
|
||
powers_gadget_f16(&mut builder, "f16"); | ||
powers_gadget_f32(&mut builder, "f32"); | ||
|
||
let witness = builder.take_witness().unwrap(); | ||
let constraints_system = builder.build().unwrap(); | ||
|
||
validate_witness(&constraints_system, &[], &witness).unwrap(); | ||
} |