Skip to content

Commit 729ba78

Browse files
committed
test: add evaluation unit test
1 parent 2d373e9 commit 729ba78

File tree

3 files changed

+132
-0
lines changed

3 files changed

+132
-0
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ cfg-if = "1.0.0"
4545
once_cell = "1.18.0"
4646
anyhow = "1.0.72"
4747
rand = "0.8.4"
48+
rand_xorshift = "0.3.0"
4849

4950
[target.'cfg(any(target_arch = "x86_64", target_arch = "aarch64"))'.dependencies]
5051
pasta-msm = { git="https://github.com/lurk-lab/pasta-msm", branch="dev", version = "0.1.4" }

ThirdPartyNotices.txt

+19
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,22 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
5252
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
5353
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
5454
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
55+
56+
------------------------------------------------------------
57+
https://github.com/AztecProtocol/aztec-packages/
58+
59+
Licensed under Apache 2.0
60+
61+
Copyright 2022 Aztec
62+
63+
Licensed under the Apache License, Version 2.0 (the "License");
64+
you may not use this file except in compliance with the License.
65+
You may obtain a copy of the License at
66+
67+
http://www.apache.org/licenses/LICENSE-2.0
68+
69+
Unless required by applicable law or agreed to in writing, software
70+
distributed under the License is distributed on an "AS IS" BASIS,
71+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
72+
See the License for the specific language governing permissions and
73+
limitations under the License.

src/spartan/polys/multilinear.rs

+112
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ mod tests {
213213

214214
use super::*;
215215
use pasta_curves::Fp;
216+
use rand_core::SeedableRng;
216217

217218
fn make_mlp<F: PrimeField>(len: usize, value: F) -> MultilinearPolynomial<F> {
218219
MultilinearPolynomial {
@@ -335,4 +336,115 @@ mod tests {
335336
test_evaluation_with::<provider::bn256_grumpkin::bn256::Scalar>();
336337
test_evaluation_with::<provider::secp_secq::secp256k1::Scalar>();
337338
}
339+
340+
pub fn partial_eval<F: PrimeField>(
341+
poly: &MultilinearPolynomial<F>,
342+
point: &[F],
343+
) -> MultilinearPolynomial<F> {
344+
// Get size of partial evaluation point u = (u_0,...,u_{m-1})
345+
let m = point.len();
346+
347+
// Assert that the size of the polynomial being evaluated is a power of 2 greater than (1 << m)
348+
assert!(poly.Z.len().is_power_of_two());
349+
assert!(poly.Z.len() >= 1 << m);
350+
let n = poly.Z.len().trailing_zeros() as usize;
351+
352+
// Partial evaluation is done in m rounds l = 0,...,m-1.
353+
let mut n_l = 1 << (n - 1);
354+
355+
// Temporary buffer of half the size of the polynomial
356+
let mut tmp = vec![F::ZERO; n_l];
357+
358+
let prev = &poly.Z;
359+
360+
// Evaluate variable X_{n-1} at u_{m-1}
361+
let u_l = point[m - 1];
362+
for i in 0..n_l {
363+
tmp[i] = prev[i] + u_l * (prev[i + n_l] - prev[i]);
364+
}
365+
366+
// Evaluate m-1 variables X_{n-l-1}, ..., X_{n-2} at m-1 remaining values u_0,...,u_{m-2})
367+
for l in 1..m {
368+
n_l = 1 << (n - l - 1);
369+
let u_l = point[m - l - 1];
370+
for i in 0..n_l {
371+
tmp[i] = tmp[i] + u_l * (tmp[i + n_l] - tmp[i]);
372+
}
373+
}
374+
tmp.truncate(1 << (poly.num_vars - m));
375+
376+
MultilinearPolynomial::new(tmp)
377+
}
378+
379+
fn make_rand_mlp<F: PrimeField, R: RngCore>(
380+
var_count: usize,
381+
mut rng: &mut R,
382+
) -> MultilinearPolynomial<F> {
383+
let eqpoly = EqPolynomial::new(
384+
std::iter::from_fn(|| Some(F::random(&mut rng)))
385+
.take(var_count)
386+
.collect::<Vec<_>>(),
387+
);
388+
MultilinearPolynomial::new(eqpoly.evals())
389+
}
390+
391+
fn partial_evaluate_mle_with<F: PrimeField>() {
392+
// Initialize a random polynomial
393+
let n = 5;
394+
let mut rng = rand_xorshift::XorShiftRng::from_seed([0u8; 16]);
395+
let poly = make_rand_mlp::<F, _>(n, &mut rng);
396+
397+
// Define a random multivariate evaluation point u = (u_0, u_1, u_2, u_3, u_4)
398+
let u_0 = F::random(&mut rng);
399+
let u_1 = F::random(&mut rng);
400+
let u_2 = F::random(&mut rng);
401+
let u_3 = F::random(&mut rng);
402+
let u_4 = F::random(&mut rng);
403+
let u_challenge = vec![u_4, u_3, u_2, u_1, u_0];
404+
405+
// Directly computing v = p(u_0,...,u_4) and comparing it with the result of
406+
// first computing the partial evaluation in the last 3 variables
407+
// g(X_0,X_1) = p(X_0,X_1,u_2,u_3,u_4), then v = g(u_0,u_1)
408+
409+
// Compute v = p(u_0,...,u_4)
410+
let v_expected = poly.evaluate(&u_challenge[..]);
411+
412+
// Compute g(X_0,X_1) = p(X_0,X_1,u_2,u_3,u_4), then v = g(u_0,u_1)
413+
let u_part_1 = vec![u_1, u_0]; // note the endianness difference
414+
let u_part_2 = vec![u_2, u_3, u_4];
415+
let partial_evaluated_poly = partial_eval(&poly, &u_part_2);
416+
let v_result = partial_evaluated_poly.evaluate(&u_part_1);
417+
418+
assert_eq!(v_result, v_expected);
419+
}
420+
421+
#[test]
422+
fn test_partial_evaluate_mle() {
423+
partial_evaluate_mle_with::<Fp>();
424+
partial_evaluate_mle_with::<bn256::Scalar>();
425+
partial_evaluate_mle_with::<secp256k1::Scalar>();
426+
}
427+
428+
fn partial_and_evaluate_with<F: PrimeField>() {
429+
for _i in 0..50 {
430+
// Initialize a random polynomial
431+
let n = 7;
432+
let mut rng = rand_xorshift::XorShiftRng::from_seed([0u8; 16]);
433+
let poly = make_rand_mlp::<F, _>(n, &mut rng);
434+
435+
// draw a random point
436+
let pt: Vec<_> = std::iter::from_fn(|| Some(F::random(&mut rng)))
437+
.take(n)
438+
.collect();
439+
let rev_pt: Vec<_> = pt.iter().cloned().rev().collect();
440+
assert_eq!(poly.evaluate(&pt), partial_eval(&poly, &rev_pt).Z[0])
441+
}
442+
}
443+
444+
#[test]
445+
fn test_partial_and_evaluate() {
446+
partial_and_evaluate_with::<Fp>();
447+
partial_and_evaluate_with::<bn256::Scalar>();
448+
partial_and_evaluate_with::<secp256k1::Scalar>();
449+
}
338450
}

0 commit comments

Comments
 (0)