@@ -213,6 +213,7 @@ mod tests {
213
213
214
214
use super :: * ;
215
215
use pasta_curves:: Fp ;
216
+ use rand_core:: SeedableRng ;
216
217
217
218
fn make_mlp < F : PrimeField > ( len : usize , value : F ) -> MultilinearPolynomial < F > {
218
219
MultilinearPolynomial {
@@ -335,4 +336,115 @@ mod tests {
335
336
test_evaluation_with :: < provider:: bn256_grumpkin:: bn256:: Scalar > ( ) ;
336
337
test_evaluation_with :: < provider:: secp_secq:: secp256k1:: Scalar > ( ) ;
337
338
}
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
+ }
338
450
}
0 commit comments