Skip to content

Commit cdfbb7e

Browse files
committed
signoff: Inner product of XFieldElements
1 parent 67441b6 commit cdfbb7e

File tree

1 file changed

+107
-184
lines changed

1 file changed

+107
-184
lines changed
Lines changed: 107 additions & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,28 @@
1+
use std::collections::HashMap;
2+
13
use triton_vm::prelude::*;
24

35
use crate::data_type::ArrayType;
46
use crate::prelude::*;
5-
7+
use crate::traits::basic_snippet::Reviewer;
8+
use crate::traits::basic_snippet::SignOffFingerprint;
9+
10+
/// Compute the inner product of two lists of [`XFieldElement`]s.
11+
///
12+
/// ### Behavior
13+
///
14+
/// ```text
15+
/// BEFORE: _ *a *b
16+
/// AFTER: _ [inner_product: XFieldElement]
17+
/// ```
18+
///
19+
/// ### Preconditions
20+
///
21+
/// None.
22+
///
23+
/// ### Postconditions
24+
///
25+
/// None.
626
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
727
pub struct InnerProductOfXfes {
828
pub length: usize,
@@ -12,20 +32,18 @@ impl InnerProductOfXfes {
1232
pub fn new(length: usize) -> Self {
1333
Self { length }
1434
}
15-
16-
fn argument_type(&self) -> DataType {
17-
DataType::Array(Box::new(ArrayType {
18-
element_type: DataType::Xfe,
19-
length: self.length,
20-
}))
21-
}
2235
}
2336

2437
impl BasicSnippet for InnerProductOfXfes {
2538
fn inputs(&self) -> Vec<(DataType, String)> {
39+
let argument_type = DataType::Array(Box::new(ArrayType {
40+
element_type: DataType::Xfe,
41+
length: self.length,
42+
}));
43+
2644
vec![
27-
(self.argument_type(), "*a".to_owned()),
28-
(self.argument_type(), "*b".to_owned()),
45+
(argument_type.clone(), "*a".to_owned()),
46+
(argument_type, "*b".to_owned()),
2947
]
3048
}
3149

@@ -37,210 +55,131 @@ impl BasicSnippet for InnerProductOfXfes {
3755
format!("tasmlib_array_inner_product_of_{}_xfes", self.length)
3856
}
3957

40-
fn code(&self, _library: &mut Library) -> Vec<LabelledInstruction> {
41-
let entrypoint = self.entrypoint();
42-
43-
let accumulate_all_indices = triton_asm![xx_dot_step; self.length];
44-
58+
fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
4559
triton_asm!(
46-
{entrypoint}:
47-
// _ *a *b
60+
// BEFORE: _ *a *b
61+
// AFTER: _ [inner_product: XFieldElement]
62+
{self.entrypoint()}:
4863

4964
push 0
5065
push 0
5166
push 0
52-
// _ *a *b 0 0 0
67+
// _ *a *b [0: XFE]
5368

5469
pick 4
5570
pick 4
56-
// _ 0 0 0 *a *b
71+
// _ [0: XFE] *a *b
5772

58-
{&accumulate_all_indices}
59-
// _ acc2 acc1 acc0 *garbage0 *garbage1
73+
{&triton_asm![xx_dot_step; self.length]}
74+
// _ [acc: XFE] *garbage0 *garbage1
6075

6176
pop 2
62-
// _ acc2 acc1 acc0
77+
// _ [acc: XFE]
6378

6479
return
6580
)
6681
}
82+
83+
fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
84+
let mut sign_offs = HashMap::new();
85+
86+
if self.length == 4 {
87+
sign_offs.insert(Reviewer("ferdinand"), 0x154bf4aa5a53bef7.into());
88+
}
89+
90+
sign_offs
91+
}
6792
}
6893

6994
#[cfg(test)]
7095
mod tests {
71-
use num::Zero;
72-
use num_traits::ConstZero;
73-
use twenty_first::math::x_field_element::EXTENSION_DEGREE;
74-
7596
use super::*;
97+
use crate::rust_shadowing_helper_functions::array::array_from_memory;
7698
use crate::rust_shadowing_helper_functions::array::insert_as_array;
7799
use crate::rust_shadowing_helper_functions::array::insert_random_array;
78100
use crate::test_prelude::*;
79101

80-
impl Function for InnerProductOfXfes {
102+
impl Accessor for InnerProductOfXfes {
81103
fn rust_shadow(
82104
&self,
83105
stack: &mut Vec<BFieldElement>,
84-
memory: &mut HashMap<BFieldElement, BFieldElement>,
106+
memory: &HashMap<BFieldElement, BFieldElement>,
85107
) {
86-
fn read_xfe(
87-
memory: &HashMap<BFieldElement, BFieldElement>,
88-
address: BFieldElement,
89-
) -> XFieldElement {
90-
let coefficients = [
91-
memory.get(&address),
92-
memory.get(&(address + bfe!(1))),
93-
memory.get(&(address + bfe!(2))),
94-
]
95-
.map(|b| b.copied().unwrap_or(BFieldElement::ZERO));
96-
xfe!(coefficients)
97-
}
98-
99-
let mut array_pointer_b = stack.pop().unwrap();
100-
let mut array_pointer_a = stack.pop().unwrap();
101-
let mut acc = XFieldElement::zero();
102-
for _ in 0..self.length {
103-
let element_a = read_xfe(memory, array_pointer_a);
104-
let element_b = read_xfe(memory, array_pointer_b);
105-
acc += element_a * element_b;
106-
array_pointer_b += bfe!(3);
107-
array_pointer_a += bfe!(3);
108-
}
109-
110-
for word in acc.coefficients.into_iter().rev() {
111-
stack.push(word);
112-
}
108+
let b = array_from_memory::<XFieldElement>(stack.pop().unwrap(), self.length, memory);
109+
let a = array_from_memory::<XFieldElement>(stack.pop().unwrap(), self.length, memory);
110+
let inner_product: XFieldElement = a.into_iter().zip(b).map(|(a, b)| a * b).sum();
111+
112+
push_encodable(stack, &inner_product);
113113
}
114114

115115
fn pseudorandom_initial_state(
116116
&self,
117117
seed: [u8; 32],
118-
_bench_case: Option<BenchmarkCase>,
119-
) -> FunctionInitialState {
118+
_: Option<BenchmarkCase>,
119+
) -> AccessorInitialState {
120120
let mut rng = StdRng::from_seed(seed);
121-
let array_pointer_a = BFieldElement::new(rng.gen());
122-
let mut array_pointer_b = BFieldElement::new(rng.gen());
123-
while array_pointer_a.value().abs_diff(array_pointer_b.value())
124-
< EXTENSION_DEGREE as u64 * self.length as u64
125-
{
126-
array_pointer_b = BFieldElement::new(rng.gen());
127-
}
128-
129-
self.prepare_state(array_pointer_a, array_pointer_b)
121+
let pointer_a = rng.gen();
122+
let pointer_b_offset = rng.gen_range(self.length..usize::MAX - self.length);
123+
let pointer_b = pointer_a + bfe!(pointer_b_offset);
124+
125+
let mut memory = HashMap::default();
126+
insert_random_array(&DataType::Xfe, pointer_a, self.length, &mut memory);
127+
insert_random_array(&DataType::Xfe, pointer_b, self.length, &mut memory);
128+
129+
let mut stack = self.init_stack_for_isolated_run();
130+
stack.push(pointer_a);
131+
stack.push(pointer_b);
132+
133+
AccessorInitialState { stack, memory }
130134
}
131135

132-
fn corner_case_initial_states(&self) -> Vec<FunctionInitialState> {
133-
let all_zeros = {
134-
let init_stack = [
135-
self.init_stack_for_isolated_run(),
136-
vec![BFieldElement::new(0), BFieldElement::new(1 << 40)],
137-
]
138-
.concat();
139-
FunctionInitialState {
140-
stack: init_stack,
141-
memory: HashMap::default(),
142-
}
136+
fn corner_case_initial_states(&self) -> Vec<AccessorInitialState> {
137+
let all_zeros = AccessorInitialState {
138+
stack: [self.init_stack_for_isolated_run(), bfe_vec![0, 1_u64 << 40]].concat(),
139+
memory: HashMap::default(),
143140
};
144141

145142
vec![all_zeros]
146143
}
147144
}
148145

149-
impl InnerProductOfXfes {
150-
fn prepare_state(
151-
&self,
152-
array_pointer_a: BFieldElement,
153-
array_pointer_b: BFieldElement,
154-
) -> FunctionInitialState {
155-
let mut memory = HashMap::default();
156-
insert_random_array(&DataType::Xfe, array_pointer_a, self.length, &mut memory);
157-
insert_random_array(&DataType::Xfe, array_pointer_b, self.length, &mut memory);
158-
159-
let mut init_stack = self.init_stack_for_isolated_run();
160-
init_stack.push(array_pointer_a);
161-
init_stack.push(array_pointer_b);
162-
FunctionInitialState {
163-
stack: init_stack,
164-
memory,
165-
}
166-
}
167-
}
168-
169146
#[test]
170147
fn inner_product_of_xfes_pbt() {
171-
let snippets = (0..20)
172-
.chain(100..110)
173-
.map(|x| InnerProductOfXfes { length: x });
174-
for test_case in snippets {
175-
ShadowedFunction::new(test_case).test()
148+
for test_case in (0..20).chain(100..110).map(InnerProductOfXfes::new) {
149+
ShadowedAccessor::new(test_case).test()
176150
}
177151
}
178152

179153
#[test]
180154
fn inner_product_unit_test() {
181-
let a = vec![
182-
XFieldElement::new([
183-
BFieldElement::new(3),
184-
BFieldElement::zero(),
185-
BFieldElement::zero(),
186-
]),
187-
XFieldElement::new([
188-
BFieldElement::new(5),
189-
BFieldElement::zero(),
190-
BFieldElement::zero(),
191-
]),
192-
];
193-
let b = vec![
194-
XFieldElement::new([
195-
BFieldElement::new(501),
196-
BFieldElement::zero(),
197-
BFieldElement::zero(),
198-
]),
199-
XFieldElement::new([
200-
BFieldElement::new(1003),
201-
BFieldElement::zero(),
202-
BFieldElement::zero(),
203-
]),
204-
];
205-
206-
let expected_inner_product = XFieldElement::new([
207-
BFieldElement::new(3 * 501 + 5 * 1003),
208-
BFieldElement::zero(),
209-
BFieldElement::zero(),
210-
]);
211-
assert_eq!(
212-
expected_inner_product,
213-
a.iter()
214-
.cloned()
215-
.zip(b.iter().cloned())
216-
.map(|(a, b)| a * b)
217-
.sum::<XFieldElement>()
218-
);
155+
let a = xfe_vec![[3, 0, 0], [5, 0, 0]];
156+
let b = xfe_vec![[501, 0, 0], [1003, 0, 0]];
157+
let inner_product = xfe!([3 * 501 + 5 * 1003, 0, 0]);
158+
159+
let rust_inner_product = a
160+
.iter()
161+
.zip(&b)
162+
.map(|(&a, &b)| a * b)
163+
.sum::<XFieldElement>();
164+
debug_assert_eq!(inner_product, rust_inner_product);
219165

220166
let mut memory = HashMap::default();
221-
let array_pointer_a = BFieldElement::new(1u64 << 44);
222-
insert_as_array(array_pointer_a, &mut memory, a);
223-
let array_pointer_b = BFieldElement::new(1u64 << 45);
224-
insert_as_array(array_pointer_b, &mut memory, b);
225-
226-
let snippet = InnerProductOfXfes { length: 2 };
227-
let expected_final_stack = [
228-
snippet.init_stack_for_isolated_run(),
229-
expected_inner_product
230-
.coefficients
231-
.into_iter()
232-
.rev()
233-
.collect_vec(),
234-
]
235-
.concat();
236-
let init_stack = [
237-
snippet.init_stack_for_isolated_run(),
238-
vec![array_pointer_a, array_pointer_b],
239-
]
240-
.concat();
167+
let pointer_a = bfe!(1_u64 << 44);
168+
let pointer_b = bfe!(1_u64 << 45);
169+
insert_as_array(pointer_a, &mut memory, a);
170+
insert_as_array(pointer_b, &mut memory, b);
171+
172+
let snippet = InnerProductOfXfes::new(2);
173+
let mut initial_stack = snippet.init_stack_for_isolated_run();
174+
initial_stack.push(pointer_a);
175+
initial_stack.push(pointer_b);
176+
177+
let mut expected_final_stack = snippet.init_stack_for_isolated_run();
178+
push_encodable(&mut expected_final_stack, &inner_product);
179+
241180
test_rust_equivalence_given_complete_state(
242-
&ShadowedFunction::new(snippet),
243-
&init_stack,
181+
&ShadowedAccessor::new(snippet),
182+
&initial_stack,
244183
&[],
245184
&NonDeterminism::default().with_ram(memory),
246185
&None,
@@ -259,28 +198,12 @@ mod benches {
259198
use crate::test_prelude::*;
260199

261200
#[test]
262-
fn inner_product_xfes_bench_100() {
263-
ShadowedFunction::new(InnerProductOfXfes { length: 100 }).bench();
264-
}
265-
266-
#[test]
267-
fn inner_product_xfes_bench_200() {
268-
ShadowedFunction::new(InnerProductOfXfes { length: 200 }).bench();
269-
}
201+
fn benchmark() {
202+
ShadowedAccessor::new(InnerProductOfXfes::new(100)).bench();
203+
ShadowedAccessor::new(InnerProductOfXfes::new(200)).bench();
270204

271-
#[test]
272-
fn inner_product_xfes_bench_num_columns_current_tvm() {
273-
ShadowedFunction::new(InnerProductOfXfes {
274-
length: MasterMainTable::NUM_COLUMNS + MasterAuxTable::NUM_COLUMNS,
275-
})
276-
.bench();
277-
}
278-
279-
#[test]
280-
fn inner_product_xfes_bench_num_constraints_current_tvm() {
281-
ShadowedFunction::new(InnerProductOfXfes {
282-
length: MasterAuxTable::NUM_CONSTRAINTS,
283-
})
284-
.bench();
205+
let num_columns = MasterMainTable::NUM_COLUMNS + MasterAuxTable::NUM_COLUMNS;
206+
ShadowedAccessor::new(InnerProductOfXfes::new(num_columns)).bench();
207+
ShadowedAccessor::new(InnerProductOfXfes::new(MasterAuxTable::NUM_CONSTRAINTS)).bench();
285208
}
286209
}

0 commit comments

Comments
 (0)