https://gitlab.com/tezos/tezos
Raw File
Tip revision: c4d535b1955cdc3f6e969b450ef49a8b308621bd authored by Arvid Jakobsson on 22 September 2023, 06:45:41 UTC
tmp disable warnings
Tip revision: c4d535b
test_coefficients.ml
(*****************************************************************************)
(*                                                                           *)
(* MIT License                                                               *)
(* Copyright (c) 2020-2021 Danny Willems <be.danny.willems@gmail.com>        *)
(* Copyright (c) 2022 Nomadic Labs <contact@nomadic-labs.com>                *)
(*                                                                           *)
(* Permission is hereby granted, free of charge, to any person obtaining a   *)
(* copy of this software and associated documentation files (the "Software"),*)
(* to deal in the Software without restriction, including without limitation *)
(* the rights to use, copy, modify, merge, publish, distribute, sublicense,  *)
(* and/or sell copies of the Software, and to permit persons to whom the     *)
(* Software is furnished to do so, subject to the following conditions:      *)
(*                                                                           *)
(* The above copyright notice and this permission notice shall be included   *)
(* in all copies or substantial portions of the Software.                    *)
(*                                                                           *)
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  *)
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL   *)
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING   *)
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER       *)
(* DEALINGS IN THE SOFTWARE.                                                 *)
(*                                                                           *)
(*****************************************************************************)

let rec non_null_int bound =
  let r = Random.int bound in
  if r = 0 then non_null_int bound else r

module Scalar = Bls12_381.Fr
module Domain = Octez_bls12_381_polynomial.Domain
module Poly = Octez_bls12_381_polynomial.Polynomial
module Evaluations = Octez_bls12_381_polynomial.Evaluations

let raises_invalid_arg f =
  try
    ignore @@ f () ;
    false
  with
  | Invalid_argument _ -> true
  | _ -> false

let test_equal () =
  let make a = Poly.of_dense (Array.map Scalar.of_int a) in
  let p1 = make [|0; 1; 0; 0|] in
  let p2 = make [|0; 1; 0|] in
  let p3 = make [|0; 1|] in
  assert (Poly.(equal p1 p2)) ;
  assert (Poly.(equal p2 p3)) ;
  assert (Poly.(equal zero (make [|0|]))) ;
  assert (Poly.(equal zero (make [|0; 0|])))

let test_copy () =
  let module C = Octez_bls12_381_polynomial.Internal_for_tests.Polynomial_unsafe
  in
  let make a = C.of_dense (Array.map Scalar.of_string a) in
  let p = make [|"0"; "1"; "2"; "0"|] in
  assert (C.equal (C.copy p) p) ;
  assert (C.equal (C.copy_carray ~offset:1 p) (make [|"1"; "2"; "0"|])) ;
  assert (C.equal (C.copy_carray ~offset:1 ~len:2 p) (make [|"1"; "2"|])) ;
  assert (C.equal (C.copy_carray ~len:2 p) (make [|"0"; "1"|])) ;
  assert (raises_invalid_arg (fun () -> C.copy_carray ~len:5 p)) ;
  assert (raises_invalid_arg (fun () -> C.copy_carray ~offset:(-2) p)) ;
  assert (raises_invalid_arg (fun () -> C.copy_carray ~offset:(C.length p) p)) ;
  assert (
    raises_invalid_arg (fun () ->
        C.copy_carray ~offset:(C.length p - 1) ~len:2 p))

let test_split_poly () =
  let make a = Poly.of_dense (Array.map Scalar.of_string a) in
  let p = make [|"0"; "1"; "2"; "3"; "4"; "0"|] in
  let res = Poly.split ~nb_chunks:3 2 p in
  assert (Poly.equal (List.nth res 0) (make [|"0"; "1"|])) ;
  assert (Poly.equal (List.nth res 1) (make [|"2"; "3"|])) ;
  assert (Poly.equal (List.nth res 2) (make [|"4"|])) ;
  let res = Poly.split ~nb_chunks:2 2 p in
  assert (Poly.equal (List.nth res 0) (make [|"0"; "1"|])) ;
  assert (Poly.equal (List.nth res 1) (make [|"2"; "3"; "4"|]))

let test_shift () =
  let p = Poly.generate_biased_random_polynomial 10 in
  let shift = 5 in
  let res = Poly.(of_coefficients [(Scalar.one, shift)] * p) in
  let p_a = Poly.to_dense_coefficients p in
  let res_a = Poly.to_dense_coefficients res in
  Array.iteri
    (fun i e ->
      if i < shift then assert (Scalar.(eq e zero))
      else assert (Scalar.eq e p_a.(i - shift)))
    res_a

let test_split_poly2 () =
  let expected_poly = Poly.generate_biased_random_polynomial 34 in
  let res = Poly.split ~nb_chunks:5 10 expected_poly in
  let reconstructed_poly, _ =
    List.fold_left
      (fun (poly, i) poly_part ->
        ( Poly.(
            poly + (of_coefficients [(Scalar.one, Int.mul 10 i)] * poly_part)),
          i - 1 ))
      (Poly.zero, List.length res - 1)
      (List.rev res)
  in
  assert (Poly.equal reconstructed_poly expected_poly)

let test_degree_zero_is_infinity () = assert (Poly.degree Poly.zero = -1)

let test_degree_of_constants_is_one () =
  assert (Poly.(degree (constant (Scalar.random ()))) = 0)

let test_degree_int_test_vectors () =
  let vectors =
    [
      (Poly.zero, -1);
      (Poly.of_coefficients [(Scalar.one, 0)], 0);
      (Poly.of_coefficients [(Scalar.one, 10)], 10);
    ]
  in
  List.iter
    (fun (p, expected_result) -> assert (Poly.degree p = expected_result))
    vectors

let test_eval_random_point_zero_polynomial () =
  assert (Scalar.is_zero (Poly.evaluate Poly.zero (Scalar.random ())))

let test_eval_at_zero_of_zero_polynomial () =
  assert (Scalar.is_zero (Poly.evaluate Poly.zero Scalar.zero))

let test_eval_at_zero_point_of_random_constant_polynomial () =
  let constant = Scalar.random () in
  assert (
    Scalar.eq (Poly.evaluate (Poly.constant constant) Scalar.zero) constant)

let test_eval_random_point_constant_polynomial () =
  let constant = Scalar.random () in
  assert (
    Scalar.eq
      (Poly.evaluate (Poly.constant constant) (Scalar.random ()))
      constant)

let test_eval_x_to_random_point () =
  let p = Scalar.random () in
  assert (Scalar.eq (Poly.evaluate (Poly.of_coefficients [(Scalar.one, 1)]) p) p)

let test_of_coeff_to_dense_vectors () =
  let rec generate_non_null () =
    let r = Scalar.random () in
    if Scalar.is_zero r then generate_non_null () else r
  in
  let x = generate_non_null () in
  let z = Scalar.zero in
  let vectors =
    [
      (Poly.zero, [z]);
      (Poly.constant x, [x]);
      (Poly.of_coefficients [(z, 4); (x, 2)], [z; z; x]);
      (Poly.of_coefficients [(x, 1)], [z; x]);
      (Poly.of_coefficients [(x, 3); (x, 1)], [z; x; z; x]);
      (Poly.of_coefficients [(x, 4); (x, 1)], [z; x; z; z; x]);
      ( Poly.of_coefficients [(x, 17); (x, 14); (x, 3); (x, 1); (x, 0)],
        [x; x; z; x; z; z; z; z; z; z; z; z; z; z; x; z; z; x] );
    ]
  in
  List.iter
    (fun (v, expected) ->
      let array = Poly.to_dense_coefficients v |> Array.to_list in
      assert (List.for_all2 Scalar.eq expected array) ;
      assert (Poly.equal v (Poly.of_dense (Array.of_list expected))))
    vectors

let test_multiply_by_zero_is_zero () =
  let r = Poly.generate_biased_random_polynomial (Random.int 1000) in
  assert (Poly.equal (Poly.mul r Poly.zero) Poly.zero) ;
  assert (Poly.equal (Poly.mul Poly.zero r) Poly.zero)

let test_communitativity () =
  let p = Poly.generate_biased_random_polynomial (Random.int 30) in
  let q = Poly.generate_biased_random_polynomial (Random.int 30) in
  assert (Poly.equal (Poly.mul p q) (Poly.mul q p))

let test_distributivity () =
  let a = Scalar.random () in
  let b = Scalar.random () in
  let p = Poly.generate_biased_random_polynomial (Random.int 30) in
  let q = Poly.generate_biased_random_polynomial (Random.int 30) in
  assert (
    Poly.equal
      (Poly.mul (Poly.mul_by_scalar a p) (Poly.mul_by_scalar b q))
      (Poly.mul (Poly.mul_by_scalar a q) (Poly.mul_by_scalar b p))) ;
  assert (
    Poly.equal
      (Poly.mul (Poly.mul_by_scalar Scalar.(a * b) p) q)
      (Poly.mul (Poly.mul_by_scalar Scalar.(a * b) q) p)) ;
  assert (
    Poly.equal
      (Poly.mul p (Poly.mul_by_scalar Scalar.(a * b) q))
      (Poly.mul q (Poly.mul_by_scalar Scalar.(a * b) p))) ;
  assert (
    Poly.equal
      (Poly.mul (Poly.mul_by_scalar a p) (Poly.mul_by_scalar b q))
      Poly.(mul_by_scalar Scalar.(a * b) (mul p q)))

let test_interpolation_fft_with_only_roots ~power () =
  let domain = Domain.build_power_of_two Z.(log2up (of_int power)) in
  (* only roots *)
  let evaluation_points = Array.init power (fun _i -> Scalar.zero) in
  let results = Evaluations.interpolation_fft2 domain evaluation_points in
  assert (Poly.is_zero results)

let test_evaluation_fft_zero ~power () =
  let domain = Domain.build_power_of_two Z.(log2up (of_int power)) in
  let polynomial = Poly.zero in
  let results = Evaluations.(evaluation_fft domain polynomial |> to_array) in
  let expected_results = Array.init power (fun _ -> Scalar.zero) in
  assert (Array.for_all2 Scalar.eq results expected_results)

let test_evaluation_fft_constant ~power () =
  let domain = Domain.build_power_of_two Z.(log2up (of_int power)) in
  let s = Scalar.random () in
  let polynomial = Poly.constant s in
  let results = Evaluations.(evaluation_fft domain polynomial |> to_array) in
  let expected_results = Array.init power (fun _ -> s) in
  assert (Array.for_all2 Scalar.eq results expected_results)

(*   let test_evaluation_fft_random_values_with_larger_polynomial ~generator ~power *)
(*       () = *)
(*     let domain = *)
(*       Polynomial.generate_evaluation_domain (module Scalar) power generator *)
(*     in *)
(*     let polynomial = Poly.generate_biased_random_polynomial (power + Random.int 100) in *)
(*     let expected_results = *)
(*       List.map (fun x -> Poly.evaluate polynomial x) (Array.to_list domain) *)
(*     in *)
(*     let results = Poly.evaluation_fft domain polynomial in *)
(*     assert (List.for_all2 Scalar.eq results expected_results) *)

let test_evaluation_fft_random_values_with_smaller_polynomial ~power () =
  let domain = Domain.build_power_of_two Z.(log2up (of_int power)) in
  let polynomial =
    Poly.generate_biased_random_polynomial (Random.int (power - 1))
  in
  let expected_results =
    Array.map (Poly.evaluate polynomial) (Domain.to_array domain)
  in
  let results = Evaluations.(evaluation_fft domain polynomial |> to_array) in
  assert (Array.for_all2 Scalar.eq results expected_results)

let test_multiply_constant_by_scalar_zero_is_zero () =
  let p1 = Poly.constant (Scalar.random ()) in
  assert (Poly.is_zero (Poly.mul_by_scalar Scalar.zero p1))

let test_multiply_degree_one_by_scalar_zero_is_zero () =
  let p1 = Poly.of_coefficients [(Scalar.of_string "1", 1)] in
  assert (Poly.is_zero (Poly.mul_by_scalar Scalar.zero p1))

let test_property_of_twice_opposite () =
  let p1 = Poly.of_coefficients [(Scalar.of_string "10", 1)] in
  assert (Poly.equal (Poly.opposite (Poly.opposite p1)) p1)

let test_property_opposite_of_constant () =
  let random = Scalar.random () in
  assert (
    Poly.equal
      (Poly.opposite (Poly.constant random))
      (Poly.constant (Scalar.negate random)))

let test_property_opposite_of_zero () =
  assert (Poly.(Poly.opposite Poly.zero = Poly.zero))

let tests =
  Alcotest.
    [
      test_case "equal" `Quick test_equal;
      test_case "copy" `Quick test_copy;
      test_case "shift" `Quick test_shift;
      test_case "split poly" `Quick test_split_poly;
      test_case "split poly2" `Quick test_split_poly2;
      test_case "degree of zero is infinity" `Quick test_degree_zero_is_infinity;
      test_case "degree of constant is one" `Quick test_degree_zero_is_infinity;
      test_case "degree int test vectors" `Quick test_degree_int_test_vectors;
      test_case
        "evaluation at any point of the zero polynomial"
        `Quick
        (Helpers.repeat 100 test_eval_random_point_zero_polynomial);
      test_case
        "evaluation at any point of a random constant polynomial"
        `Quick
        (Helpers.repeat 100 test_eval_random_point_constant_polynomial);
      test_case
        "evaluation at zero of a random constant polynomial"
        `Quick
        (Helpers.repeat
           100
           test_eval_at_zero_point_of_random_constant_polynomial);
      test_case
        "evaluation at zero of the zero polynomial"
        `Quick
        (Helpers.repeat 100 test_eval_at_zero_of_zero_polynomial);
      test_case
        "evaluation at any point of the polynomial X"
        `Quick
        (Helpers.repeat 100 test_eval_x_to_random_point);
      test_case
        "of_coeff_to_dense_vectors"
        `Quick
        test_of_coeff_to_dense_vectors;
      test_case
        "test properties nullifier 0 * P = P * 0 = 0"
        `Quick
        (Helpers.repeat 10 test_multiply_by_zero_is_zero);
      test_case
        "test properties commutativity p * q = p * q"
        `Quick
        (Helpers.repeat 10 test_communitativity);
      test_case
        "test properties distributivity and communtativity a p * b q = (a * b) \
         (p * q) = (b p) * (a q) = p * (a * b) q"
        `Quick
        (Helpers.repeat 10 test_distributivity);
      test_case
        "test interpolation with only roots"
        `Quick
        (Helpers.repeat 10 (test_interpolation_fft_with_only_roots ~power:32));
      test_case
        "test evaluation with zero polynomial"
        `Quick
        (test_evaluation_fft_zero ~power:1024);
      test_case
        "test evaluation with smaller polynomial"
        `Quick
        (Helpers.repeat
           10
           (test_evaluation_fft_random_values_with_smaller_polynomial
              ~power:1024));
      (*       test_case *)
      (*         "test evaluation with larger polynomial" *)
      (*         `Quick *)
      (*         (Helpers.repeat *)
      (*            10 *)
      (*            (test_evaluation_fft_random_values_with_larger_polynomial *)
      (*               ~generator *)
      (*               ~power)); *)
      test_case
        "test multiply constant by scalar zero is zero"
        `Quick
        test_multiply_constant_by_scalar_zero_is_zero;
      test_case
        "test multiply degree one by scalar zero is zero"
        `Quick
        test_multiply_degree_one_by_scalar_zero_is_zero;
      test_case
        "test property opposite twice"
        `Quick
        test_property_of_twice_opposite;
      test_case
        "test property opposite of constant"
        `Quick
        test_property_opposite_of_constant;
      test_case
        "test property opposite of zero"
        `Quick
        test_property_opposite_of_zero;
    ]
back to top