Skip to content

Commit

Permalink
Simplified slot data (#940)
Browse files Browse the repository at this point in the history
* simplified slot data

* added comments
  • Loading branch information
gabriel-barrett authored Dec 7, 2023
1 parent 212f295 commit f4ea425
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 91 deletions.
64 changes: 23 additions & 41 deletions src/lem/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,57 +235,39 @@ pub(crate) fn allocate_slot<F: LurkField, CS: ConstraintSystem<F>>(

// Allocate the preimage because the image depends on it
let mut preallocated_preimg = Vec::with_capacity(slot_type.preimg_size());

match slot_data {
SlotData::PtrVec(ptr_vec) => {
let mut component_idx = 0;
for ptr in ptr_vec {
slot_data
.vals
.iter()
.enumerate()
.for_each(|(component_idx, val)| match val {
Val::Pointer(ptr) => {
let z_ptr = store.hash_ptr(ptr);

// allocate pointer tag
preallocated_preimg.push(AllocatedNum::alloc_infallible(
cs.namespace(|| format!("component {component_idx} slot {slot}")),
cs.namespace(|| format!("component {component_idx} tag slot {slot}")),
|| z_ptr.tag_field(),
));

component_idx += 1;

// allocate pointer hash
preallocated_preimg.push(AllocatedNum::alloc_infallible(
cs.namespace(|| format!("component {component_idx} slot {slot}")),
cs.namespace(|| format!("component {component_idx} hash slot {slot}")),
|| *z_ptr.value(),
));

component_idx += 1;
}
}
SlotData::FPtr(f, ptr) => {
let f = store.expect_f(*f);
let z_ptr = store.hash_ptr(ptr);
// allocate first component
preallocated_preimg.push(AllocatedNum::alloc_infallible(
cs.namespace(|| format!("component 0 slot {slot}")),
|| *f,
));
// allocate second component
preallocated_preimg.push(AllocatedNum::alloc_infallible(
cs.namespace(|| format!("component 1 slot {slot}")),
|| z_ptr.tag_field(),
));
// allocate third component
preallocated_preimg.push(AllocatedNum::alloc_infallible(
cs.namespace(|| format!("component 2 slot {slot}")),
|| *z_ptr.value(),
));
}
SlotData::F(a) => {
let a = store.expect_f(*a);
preallocated_preimg.push(AllocatedNum::alloc_infallible(
cs.namespace(|| format!("component 0 slot {slot}")),
|| *a,
));
}
}
Val::Num(f) => {
let f = store.expect_f(*f);
preallocated_preimg.push(AllocatedNum::alloc_infallible(
cs.namespace(|| format!("component {component_idx} slot {slot}")),
|| *f,
));
}
Val::Boolean(b) => {
let f = if *b { F::ONE } else { F::ZERO };
preallocated_preimg.push(AllocatedNum::alloc_infallible(
cs.namespace(|| format!("component {component_idx} slot {slot}")),
|| f,
));
}
});

// Allocate the image by calling the arithmetic function according
// to the slot type
Expand Down
68 changes: 34 additions & 34 deletions src/lem/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ use anyhow::{anyhow, bail, Result};
use std::collections::VecDeque;

use super::{
path::Path, pointers::Ptr, slot::SlotData, store::Store, var_map::VarMap, Block, Ctrl, Func,
Op, Tag, Var,
path::Path,
pointers::Ptr,
slot::{SlotData, Val},
store::Store,
var_map::VarMap,
Block, Ctrl, Func, Op, Tag, Var,
};

use crate::{
Expand All @@ -15,12 +19,6 @@ use crate::{
tag::ExprTag::{Comm, Num, Sym},
};

#[derive(Clone)]
pub enum Val {
Pointer(Ptr),
Boolean(bool),
}

impl VarMap<Val> {
fn get_many_ptr(&self, args: &[Var]) -> Result<Vec<Ptr>> {
args.iter().map(|arg| self.get_ptr(arg)).collect()
Expand Down Expand Up @@ -301,15 +299,15 @@ impl Block {
let f = *store.expect_f(f_idx);
let g = *store.expect_f(g_idx);
let diff = f - g;
hints
.bit_decomp
.push(Some(SlotData::F(store.intern_f(f + f).0)));
hints
.bit_decomp
.push(Some(SlotData::F(store.intern_f(g + g).0)));
hints
.bit_decomp
.push(Some(SlotData::F(store.intern_f(diff + diff).0)));
hints.bit_decomp.push(Some(SlotData {
vals: vec![Val::Num(store.intern_f(f + f).0)],
}));
hints.bit_decomp.push(Some(SlotData {
vals: vec![Val::Num(store.intern_f(g + g).0)],
}));
hints.bit_decomp.push(Some(SlotData {
vals: vec![Val::Num(store.intern_f(diff + diff).0)],
}));
let f = BaseNum::Scalar(f);
let g = BaseNum::Scalar(g);
f < g
Expand All @@ -323,7 +321,9 @@ impl Block {
let a = bindings.get_ptr(a)?;
let c = if let Ptr::Atom(_, f_idx) = a {
let f = *store.expect_f(f_idx);
hints.bit_decomp.push(Some(SlotData::F(f_idx)));
hints.bit_decomp.push(Some(SlotData {
vals: vec![Val::Num(f_idx)],
}));
let b = if *n < 64 { (1 << *n) - 1 } else { u64::MAX };
store.intern_atom(Tag::Expr(Num), F::from_u64(f.to_u64_unchecked() & b))
} else {
Expand Down Expand Up @@ -360,14 +360,16 @@ impl Block {
let preimg_ptrs = bindings.get_many_ptr(preimg)?;
let tgt_ptr = store.intern_2_ptrs(*tag, preimg_ptrs[0], preimg_ptrs[1]);
bindings.insert_ptr(img.clone(), tgt_ptr);
hints.hash4.push(Some(SlotData::PtrVec(preimg_ptrs)));
let vals = preimg_ptrs.into_iter().map(Val::Pointer).collect();
hints.hash4.push(Some(SlotData { vals }));
}
Op::Cons3(img, tag, preimg) => {
let preimg_ptrs = bindings.get_many_ptr(preimg)?;
let tgt_ptr =
store.intern_3_ptrs(*tag, preimg_ptrs[0], preimg_ptrs[1], preimg_ptrs[2]);
bindings.insert_ptr(img.clone(), tgt_ptr);
hints.hash6.push(Some(SlotData::PtrVec(preimg_ptrs)));
let vals = preimg_ptrs.into_iter().map(Val::Pointer).collect();
hints.hash6.push(Some(SlotData { vals }));
}
Op::Cons4(img, tag, preimg) => {
let preimg_ptrs = bindings.get_many_ptr(preimg)?;
Expand All @@ -379,7 +381,8 @@ impl Block {
preimg_ptrs[3],
);
bindings.insert_ptr(img.clone(), tgt_ptr);
hints.hash8.push(Some(SlotData::PtrVec(preimg_ptrs)));
let vals = preimg_ptrs.into_iter().map(Val::Pointer).collect();
hints.hash8.push(Some(SlotData { vals }));
}
Op::Decons2(preimg, img) => {
let img_ptr = bindings.get_ptr(img)?;
Expand All @@ -393,9 +396,8 @@ impl Block {
for (var, ptr) in preimg.iter().zip(preimg_ptrs.iter()) {
bindings.insert_ptr(var.clone(), *ptr);
}
hints
.hash4
.push(Some(SlotData::PtrVec(preimg_ptrs.to_vec())));
let vals = preimg_ptrs.into_iter().map(Val::Pointer).collect();
hints.hash4.push(Some(SlotData { vals }));
}
Op::Decons3(preimg, img) => {
let img_ptr = bindings.get_ptr(img)?;
Expand All @@ -409,9 +411,8 @@ impl Block {
for (var, ptr) in preimg.iter().zip(preimg_ptrs.iter()) {
bindings.insert_ptr(var.clone(), *ptr);
}
hints
.hash6
.push(Some(SlotData::PtrVec(preimg_ptrs.to_vec())));
let vals = preimg_ptrs.into_iter().map(Val::Pointer).collect();
hints.hash6.push(Some(SlotData { vals }));
}
Op::Decons4(preimg, img) => {
let img_ptr = bindings.get_ptr(img)?;
Expand All @@ -425,9 +426,8 @@ impl Block {
for (var, ptr) in preimg.iter().zip(preimg_ptrs.iter()) {
bindings.insert_ptr(var.clone(), *ptr);
}
hints
.hash8
.push(Some(SlotData::PtrVec(preimg_ptrs.to_vec())));
let vals = preimg_ptrs.into_iter().map(Val::Pointer).collect();
hints.hash8.push(Some(SlotData { vals }));
}
Op::Hide(tgt, sec, src) => {
let src_ptr = bindings.get_ptr(src)?;
Expand All @@ -436,9 +436,8 @@ impl Block {
};
let secret = *store.expect_f(secret_idx);
let tgt_ptr = store.hide(secret, src_ptr);
hints
.commitment
.push(Some(SlotData::FPtr(secret_idx, src_ptr)));
let vals = vec![Val::Num(secret_idx), Val::Pointer(src_ptr)];
hints.commitment.push(Some(SlotData { vals }));
bindings.insert_ptr(tgt.clone(), tgt_ptr);
}
Op::Open(tgt_secret, tgt_ptr, comm) => {
Expand All @@ -455,7 +454,8 @@ impl Block {
store.intern_atom(Tag::Expr(Num), secret),
);
let secret_idx = store.intern_f(secret).0;
hints.commitment.push(Some(SlotData::FPtr(secret_idx, ptr)))
let vals = vec![Val::Num(secret_idx), Val::Pointer(ptr)];
hints.commitment.push(Some(SlotData { vals }));
}
Op::Unit(f) => f(),
}
Expand Down
41 changes: 25 additions & 16 deletions src/lem/slot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,16 +233,32 @@ impl Block {
}
}

#[derive(Clone, Debug)]
/// The values a variable can take. `Num`s are pure field elements, much like `Ptr::Atom`,
/// but missing the tag. `Boolean`s are also field elements, but they are guaranteed to be
/// constrained to take only 0 or 1 values.
pub enum Val {
Pointer(Ptr),
Num(usize),
Boolean(bool),
}

/// Holds data to feed the slots
#[derive(Clone, Debug)]
pub enum SlotData {
/// A sequence of pointers, holding hashing preimages
PtrVec(Vec<Ptr>),
/// An element of the finite field (cached in a `Store`) and a `Ptr` for
/// commitments
FPtr(usize, Ptr),
/// An element of the finite field (cached in a `Store`) for bit decompositions
F(usize),
pub struct SlotData {
pub vals: Vec<Val>,
}

impl SlotData {
/// Size of the slot data in number of field elements
pub(crate) fn size(&self) -> usize {
self.vals.iter().fold(0, |acc, val| match val {
// Pointers are tag/hash pairs
Val::Pointer(..) => acc + 2,
// Numbers and booleans are single field elements
Val::Num(..) | Val::Boolean(..) => acc + 1,
})
}
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
Expand All @@ -266,14 +282,7 @@ impl SlotType {
}

pub(crate) fn is_compatible(&self, slot_data: &SlotData) -> bool {
matches!(
(self, slot_data),
(Self::Hash4, SlotData::PtrVec(..))
| (Self::Hash6, SlotData::PtrVec(..))
| (Self::Hash8, SlotData::PtrVec(..))
| (Self::Commitment, SlotData::FPtr(..))
| (Self::BitDecomp, SlotData::F(..))
)
slot_data.size() == self.preimg_size()
}
}

Expand Down

1 comment on commit f4ea425

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmarks

Table of Contents

Overview

This benchmark report shows the Fibonacci GPU benchmark.
NVIDIA L4
Intel(R) Xeon(R) CPU @ 2.20GHz
125.78 GB RAM
Workflow run: https://github.com/lurk-lab/lurk-rs/actions/runs/7132318007

Benchmark Results

LEM Fibonacci Prove - rc = 100

fib-ref=212f295f846e31d752f2ca2e5d81200b651a514c fib-ref=f4ea42592ab4f2153e48b1b03533c2a8dc0bf3a8
num-100 3.86 s (✅ 1.00x) 3.85 s (✅ 1.00x faster)
num-200 7.77 s (✅ 1.00x) 7.76 s (✅ 1.00x faster)

LEM Fibonacci Prove - rc = 600

fib-ref=212f295f846e31d752f2ca2e5d81200b651a514c fib-ref=f4ea42592ab4f2153e48b1b03533c2a8dc0bf3a8
num-100 3.33 s (✅ 1.00x) 3.34 s (✅ 1.00x slower)
num-200 7.31 s (✅ 1.00x) 7.33 s (✅ 1.00x slower)

Made with criterion-table

Please sign in to comment.