Source file evaluations_c.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
module Fr = Bls12_381.Fr
module Stubs = struct
type fr = Polynomial_c.Stubs.fr
type fr_array = Polynomial_c.Stubs.fr_array
external add : fr_array -> fr_array -> fr_array -> int -> int -> unit
= "caml_polynomial_evaluations_add_stubs"
[@@noalloc]
(** [add res a b size_a size_b] writes the result of polynomial addition
of [a] and [b] using the evaluation representation in [res], where
- [a] is evaluated on [domain_a] of size [size_a]
- [b] is evaluated on [domain_b] of size [size_b]
requires:
- [size a = size_a]
- [size b = size_b]
- [size res = min (size_a, size_b)]
- [res], [a] and [b] are either pairwise disjoint or equal
- [size_b mod size_a = 0] *)
external mul_arrays :
fr_array ->
(fr_array * int * int * Bytes.t * int) array ->
int ->
int ->
unit = "caml_polynomial_evaluations_mul_arrays_stubs"
[@@noalloc]
(** [mul_arrays res eval_evallen_comp_power_powlen size_res nb_evals] writes
the result of computing [p₁(gᶜ₁·x)ᵐ₁·p₂(gᶜ₂·x)ᵐ₂·…·pₖ(gᶜₖ·x)ᵐₖ] using
the evaluation representation in [res], where
- [eval_evallen_comp_power_powlen.[i] = (pᵢ, size_p_i, cᵢ, mᵢ, size_bits_m_i)]
- a polynomial [pᵢ] is evaluated on [domain_p_i] of size [size_p_i]
- [cᵢ] is a composition_gx; it computes [pᵢ(gᶜᵢ·x)] instead of [pᵢ(x)],
where [g] is a primitive [size_p_i]-th root of unity
- [mᵢ] is a power in [pᵢ(x)ᵐᵢ]
- [size_bits_m_i] is the *exact* number of bits in [mᵢ]
- [nb_evals] is a number of evaluations, i.e., [i = 1..nb_evals]
requires:
- [size res = size_res]
- [size eval_evallen_comp_power_powlen = nb_evals]
- [size p_i = size_p_i]
- [size_bits m_i = size_bits_m_i]
- [size_p_i mod size_res = 0]
- [res] and [p_i] are either pairwise disjoint or equal when [cᵢ = 0] *)
external linear_arrays :
fr_array -> (fr_array * int * fr * int) array -> fr -> int -> int -> unit
= "caml_polynomial_evaluations_linear_arrays_stubs"
[@@noalloc]
(** [linear_arrays res eval_evallen_coeff_comp add_constant size_res nb_evals]
writes the result of computing
[λ₁·p₁(gᶜ₁·x) + λ₂·p₂(gᶜ₂·x) + … + λₖ·pₖ(gᶜₖ·x) + add_constant] using
the evaluation representation in [res], where
- [eval_evallen_coeff_comp.[i] = (pᵢ, size_p_i, λᵢ, cᵢ)]
- a polynomial [pᵢ] is evaluated on [domain_p_i] of size [size_p_i]
- [cᵢ] is a composition_gx; it computes [pᵢ(gᶜᵢ·x)] instead of [pᵢ(x)],
where [g] is a primitive [size_p_i]-th root of unity
- [λᵢ] is a coefficient in [λᵢ·pᵢ(x)]
- [nb_evals] is a number of evaluations, i.e., [i = 1..nb_evals]
requires:
- [size res = size_res]
- [size eval_evallen_coeff_comp = nb_evals]
- [size p_i = size_p_i]
- [size_p_i mod size_res = 0]
- [res] and [p_i] are disjoint *)
external fft_inplace :
fr_array ->
domain:fr_array ->
log:int ->
degree:int ->
log_degree:int ->
unit = "caml_polynomial_fft_inplace_on_stubs"
[@@noalloc]
(** [fft_inplace p domain log] computes Fast Fourier Transform.
It converts the coefficient representation of a polynomial [p] to
the evaluation representation
requires:
- [size p = size domain]
- [size domain = 2^log]
- [domain = [one; g; ..; g^{n-1}]] where [g] is a primitive
[n]-th root of unity and [n = 2^log] (as done by {!compute_domain}) *)
external ifft_inplace : fr_array -> domain:fr_array -> log:int -> unit
= "caml_polynomial_ifft_inplace_on_stubs"
[@@noalloc]
(** [ifft_inplace p domain log] computes Inverse Fast Fourier Transform.
It converts the evaluation representation of a polynomial [p] to
the coefficient representation
requires:
- [size p = size domain]
- [size domain = 2^log]
- [domain = [one; g; ..; g^{n-1}]] where [g] is a primitive
[n]-th root of unity and [n = 2^log] (as done by {!compute_domain}) *)
end
module type Evaluations_c_sig = sig
type scalar
type polynomial
type t
val make_evaluation : int * scalar array -> t
(** [make_evaluation (d, e)] creates a value of type [t] from the evaluation
representation of a polynomial [e] of degree [d], i.e, it converts an OCaml
array to a C array *)
val string_of_eval : t -> string
(** [string_of_eval e] returns the string representation of evaluation [e] *)
type domain
val of_domain : domain -> t
(** [of_domain d] converts [d] from type {!domain} to type {!t}.
Note: [of_domain d] doesn't create a copy of [d] *)
val to_domain : t -> domain
(** [to_domain d] converts [d] from type {!t} to type {!domain}.
Note: [to_domain d] doesn't create a copy of [d] *)
val zero : t
(** [zero] returns the evaluation representation of the zero polynomial *)
val degree : t -> int
(** [degree] returns the degree of a polynomial. Returns [-1] for the zero polynomial *)
val length : t -> int
(** [length e] returns the size of domain where a polynomial is evaluated, or equally,
the size of a C array where evaluation [e] is stored *)
val create : int -> t
(** [create len] returns the evaluation representation of a zero polynomial of size [len] *)
val copy : ?res:t -> t -> t
(** [copy ?res a] returns a copy of evaluation [a]. The function writes the result in [res]
if [res] has the correct size and allocates a new array for the result otherwise *)
val get : t -> int -> scalar
(** [get p i] returns the [i]-th element of a given array [p] *)
val mul_by_scalar : scalar -> t -> t
(** [mul_by_scalar] computes muliplication of a polynomial by a blst_fr element *)
val mul_c :
?res:t ->
evaluations:t list ->
?composition_gx:int list * int ->
?powers:int list ->
unit ->
t
(** [mul_c] computes [p₁(gᶜ₁·x)ᵐ₁·p₂(gᶜ₂·x)ᵐ₂·…·pₖ(gᶜₖ·x)ᵐₖ], where
- [pᵢ = List.nth evaluations i]
- [mᵢ = List.nth powers i]
- [cᵢ = List.nth (fst composition_gx) i]
- [n = snd composition_gx] is the order of generator, i.e., [gⁿ = 1]
The function writes the result in [res] if [res] has the correct size (= min (size pᵢ))
and allocates a new array for the result otherwise
Note: [res] can be equal to [pᵢ] when [cᵢ = 0] *)
val linear_c :
?res:t ->
evaluations:t list ->
?linear_coeffs:scalar list ->
?composition_gx:int list * int ->
?add_constant:scalar ->
unit ->
t
(** [linear_c] computes [λ₁·p₁(gᶜ₁·x) + λ₂·p₂(gᶜ₂·x) + … + λₖ·pₖ(gᶜₖ·x) + add_constant], where
- [pᵢ = List.nth evaluations i]
- [λᵢ = List.nth linear_coeffs i]
- [cᵢ = List.nth (fst composition_gx) i]
- [n = snd composition_gx] is the order of generator, i.e., [gⁿ = 1]
The function writes the result in [res] if [res] has the correct size (= min (size pᵢ))
and allocates a new array for the result otherwise
Note: [res] and [pᵢ] are disjoint *)
val add : ?res:t -> t -> t -> t
(** [add ?res a b] computes polynomial addition of [a] and [b]. The function writes
the result in [res] if [res] has the correct size (= min (size (a, b))) and allocates
a new array for the result otherwise
Note: [res] can be equal to either [a] or [b] *)
val equal : t -> t -> bool
(** [equal a b] checks whether a polynomial [a] is equal to a polynomial [b]
Note: [equal] is defined as restrictive equality, i.e., the same polynomial
evaluated on different domains are said to be different *)
val evaluation_fft : domain -> polynomial -> t
(** [evaluation_fft domain p] converts the coefficient representation of
a polynomial [p] to the evaluation representation. [domain] can be obtained
using {!Domain.build}
Note:
- size of domain must be a power of two
- size of a polynomial must be less than or equal to size of domain *)
val interpolation_fft : domain -> t -> polynomial
(** [interpolation_fft domain p] converts the evaluation representation of
a polynomial [p] to the coefficient representation. [domain] can be obtained
using {!Domain.build}
Note:
- size of domain must be a power of two
- size of a polynomial must be equal to size of domain *)
val evaluation_fft2 : domain -> polynomial -> scalar array
val interpolation_fft2 : domain -> scalar array -> polynomial
end
module Evaluations_c_impl = struct
module Domain_c = Domain.Stubs
module Domain = Domain.Domain_unsafe
module Polynomial = Polynomial_c.Polynomial_unsafe
type scalar = Fr.t
type polynomial = Polynomial.t
type t = int * Stubs.fr_array * int
type domain = Domain.t
let make_evaluation (d, p) =
if d < 0 then
raise @@ Invalid_argument "make_evaluation: degree must be non negative";
if Array.length p <= d then
raise
@@ Invalid_argument "make_evaluation: array must be longer than degree";
let res, len = Carray.of_array p in
(d, res, len)
let string_of_eval (d, e, l) =
Printf.sprintf "%d : [%s]" d (Carray.to_string (e, l))
let of_domain domain =
let d, l = Domain.to_carray domain in
(1, d, l)
let to_domain (_, eval, l) = Domain.of_carray (eval, l)
let zero = (-1, Carray.allocate 1, 1)
let degree (d, _, _) = d
let length (_, _, l) = l
let create length = (-1, Carray.allocate length, length)
let allocate_for_res res length_result =
match res with
| Some (_, e, l) when l = length_result -> e
| _ -> Carray.allocate length_result
let copy ?res (d, e, l) =
let res = allocate_for_res res l in
Carray.Stubs.copy res e 0 l;
(d, res, l)
let get (_, eval, l) i = Carray.get (eval, l) i
let mul_by_scalar lambda (deg, eval, len) =
let res = Carray.allocate len in
Polynomial_c.Stubs.mul_by_scalar res lambda eval len;
(deg, res, len)
let mul_c ?res ~evaluations
?(composition_gx = (List.init (List.length evaluations) (fun _ -> 0), 1))
?(powers = List.init (List.length evaluations) (fun _ -> 1)) () =
let domain_len = snd composition_gx in
assert (domain_len != 0);
let deg_result, list_array, is_zero, min_length_eval =
let rec fold_left3 f acc l1 l2 l3 =
match (l1, l2, l3) with
| [], [], [] -> acc
| a1 :: l1, a2 :: l2, a3 :: l3 -> fold_left3 f (f acc a1 a2 a3) l1 l2 l3
| _ ->
raise
(Invalid_argument "fold_left3 : lists don’t have the same size.")
in
fold_left3
(fun (acc_degree, list, is_zero, min_length_eval) x composition pow ->
let d, eval, l = x in
let is_zero = if d = -1 then true else is_zero in
let new_min_length_eval =
if l < min_length_eval then l else min_length_eval
in
let deg_to_add = if d = -1 then 0 else d * pow in
let pow = Z.of_int pow in
let pow_bytes = Z.to_bits pow |> Bytes.unsafe_of_string in
let pow_len = Z.numbits pow in
let rescale_composition = composition * l / domain_len in
( acc_degree + deg_to_add,
(eval, l, rescale_composition, pow_bytes, pow_len) :: list,
is_zero,
new_min_length_eval ))
(0, [], false, Int.max_int)
evaluations (fst composition_gx) powers
in
if is_zero then zero
else
let log = Z.(log2up (of_int deg_result)) in
let length_degree = Z.(to_int (one lsl log)) in
if length_degree > min_length_eval then
raise
(Invalid_argument
(Printf.sprintf
"Utils.mul_evaluations : evaluation is too short (length=%d) \
for expected result size %d"
min_length_eval length_degree))
else
let nb_evals = List.length evaluations in
let res = allocate_for_res res min_length_eval in
Stubs.mul_arrays res (Array.of_list list_array) min_length_eval nb_evals;
(deg_result, res, min_length_eval)
let linear_c ?res ~evaluations
?(linear_coeffs = List.init (List.length evaluations) (fun _ -> Fr.one))
?(composition_gx = (List.init (List.length evaluations) (fun _ -> 0), 1))
?(add_constant = Fr.zero) () =
let domain_len = snd composition_gx in
assert (domain_len != 0);
let list_eval_coeff_composition =
List.map2
(fun (eval, coeff) composition ->
let rescale_composition = composition * length eval / domain_len in
(eval, coeff, rescale_composition))
(List.combine evaluations linear_coeffs)
(fst composition_gx)
|> List.filter (fun ((d, _eval, _length), _, _) -> d != -1)
in
let l =
List.sort
(fun ((_, _, length_1), _, _) ((_, _, length_2), _, _) ->
Int.compare length_1 length_2)
list_eval_coeff_composition
in
let length_result =
if List.compare_length_with l 0 = 0 then 0
else
let (_, _, res), _, _ = List.hd l in
res
in
if length_result = 0 then zero
else
let l =
List.sort
(fun ((d1, _, _), _, _) ((d2, _, _), _, _) -> Int.compare d2 d1)
list_eval_coeff_composition
in
let degree_result =
if List.compare_length_with l 0 = 0 then -1
else
let (res, _, _), _, _ = List.hd l in
res
in
let nb_evals = List.length list_eval_coeff_composition in
let res = allocate_for_res res length_result in
let list_eval_coeff_composition =
List.map
(fun ((_, eval, eval_len), linear_coeff, composition) ->
(eval, eval_len, linear_coeff, composition))
list_eval_coeff_composition
in
Stubs.linear_arrays res
(Array.of_list list_eval_coeff_composition)
add_constant length_result nb_evals;
(degree_result, res, length_result)
let add ?res (d1, eval1, l1) (d2, eval2, l2) =
if d1 = -1 then
let res = allocate_for_res res l2 in
copy ~res:(d2, res, l2) (d2, eval2, l2)
else if d2 = -1 then
let res = allocate_for_res res l1 in
copy ~res:(d1, res, l1) (d1, eval1, l1)
else
let deg_result = max d1 d2 in
let length_result = min l1 l2 in
let res = allocate_for_res res length_result in
Stubs.add res eval1 eval2 l1 l2;
(deg_result, res, length_result)
let equal (deg1, eval1, l1) (deg2, eval2, l2) =
if deg1 <> deg2 || l1 <> l2 then false
else Carray.Stubs.eq eval1 eval2 l1 l2
let evaluation_fft_internal : Domain.t -> polynomial -> Carray.t =
fun domain poly ->
let degree = Polynomial.degree poly in
let log_degree = Z.log2up (Z.of_int (degree + 1)) in
let domain, n_domain = Domain.to_carray domain in
let poly, size_poly = Polynomial.to_carray poly in
let log = Z.(log2up @@ of_int n_domain) in
if not Z.(log = log2 @@ of_int n_domain) then
raise @@ Invalid_argument "Size of domain should be a power of 2.";
if not (size_poly <= n_domain) then
raise @@ Invalid_argument "Size of poly should be smaller than domain.";
let dense_evaluation = Carray.allocate n_domain in
Carray.Stubs.copy dense_evaluation poly 0 size_poly;
Stubs.fft_inplace dense_evaluation ~domain ~log ~degree ~log_degree;
(dense_evaluation, n_domain)
let evaluation_fft : domain -> polynomial -> t =
fun domain poly ->
let d = Polynomial.degree poly in
let domain_length = Domain.length domain in
if d = -1 then (d, Carray.allocate domain_length, domain_length)
else
let res, len = evaluation_fft_internal domain poly in
let d = Polynomial.degree poly in
(d, res, len)
let evaluation_fft2 : Domain.t -> polynomial -> scalar array =
fun domain p ->
let d = Polynomial.degree p in
let domain_length = Domain.length domain in
if d = -1 then Array.init domain_length (fun _ -> Fr.(copy zero))
else Carray.to_array (evaluation_fft_internal domain p)
let interpolation_fft_internal : Domain.t -> Carray.t -> polynomial =
fun domain (coefficients, n_coefficients) ->
let domain, n_domain = Domain.to_carray domain in
let log = Z.(log2up @@ of_int n_domain) in
if not Z.(log = log2 @@ of_int n_domain) then
raise @@ Invalid_argument "Size of domain should be a power of 2.";
if not (n_coefficients = n_domain) then
raise @@ Invalid_argument "Size of coefficients should be same as domain.";
Stubs.ifft_inplace coefficients ~domain ~log;
Polynomial.of_carray (coefficients, n_coefficients)
let interpolation_fft : domain -> t -> polynomial =
fun domain (d, evaluation, l) ->
if d = -1 then Polynomial.zero
else
let length_res = Domain.length domain in
let rescaled_eval = Carray.allocate length_res in
Domain_c.rescale rescaled_eval evaluation length_res l;
interpolation_fft_internal domain (rescaled_eval, length_res)
let interpolation_fft2 : Domain.t -> scalar array -> polynomial =
fun domain coefficients ->
interpolation_fft_internal domain (Carray.of_array coefficients)
end
include (
Evaluations_c_impl :
Evaluations_c_sig
with type scalar = Bls12_381.Fr.t
and type domain = Domain.t
and type polynomial = Polynomial_c.t)