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
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
use super::ColMatrix;
use math::{polynom, FieldElement};
use utils::{collections::Vec, uninit_vector};
// COMPOSITION POLYNOMIAL
// ================================================================================================
/// Represents a composition polynomial split into columns with each column being of length equal
/// to trace_length. Thus, for example, if the composition polynomial has degree 2N - 1, where N
/// is the trace length, it will be stored as two columns of size N (each of degree N - 1).
pub struct CompositionPoly<E: FieldElement> {
data: ColMatrix<E>,
}
impl<E: FieldElement> CompositionPoly<E> {
/// Returns a new composition polynomial.
pub fn new(coefficients: Vec<E>, trace_length: usize) -> Self {
assert!(
coefficients.len().is_power_of_two(),
"size of composition polynomial must be a power of 2, but was {}",
coefficients.len(),
);
assert!(
trace_length.is_power_of_two(),
"trace length must be a power of 2, but was {trace_length}"
);
assert!(
trace_length < coefficients.len(),
"trace length must be smaller than size of composition polynomial"
);
assert!(
coefficients[coefficients.len() - 1] != E::ZERO,
"expected composition polynomial of degree {}, but was {}",
coefficients.len() - 1,
polynom::degree_of(&coefficients)
);
let num_columns = coefficients.len() / trace_length;
let polys = transpose(coefficients, num_columns);
CompositionPoly {
data: ColMatrix::new(polys),
}
}
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------
/// Returns the number of individual column polynomials used to describe this composition
/// polynomial.
pub fn num_columns(&self) -> usize {
self.data.num_cols()
}
/// Returns the length of individual column polynomials; this is guaranteed to be a power of 2.
pub fn column_len(&self) -> usize {
self.data.num_rows()
}
/// Returns the degree of individual column polynomial.
#[allow(unused)]
pub fn column_degree(&self) -> usize {
self.column_len() - 1
}
/// Returns evaluations of all composition polynomial columns at point z^m, where m is
/// the number of column polynomials.
pub fn evaluate_at(&self, z: E) -> Vec<E> {
let z_m = z.exp((self.num_columns() as u32).into());
self.data.evaluate_columns_at(z_m)
}
/// Returns a reference to the matrix of individual column polynomials.
pub fn data(&self) -> &ColMatrix<E> {
&self.data
}
/// Transforms this composition polynomial into a vector of individual column polynomials.
pub fn into_columns(self) -> Vec<Vec<E>> {
self.data.into_columns()
}
}
// HELPER FUNCTIONS
// ================================================================================================
/// Splits polynomial coefficients into the specified number of columns. The coefficients are split
/// in such a way that each resulting column has the same degree. For example, a polynomial
/// a * x^3 + b * x^2 + c * x + d, can be rewritten as: (b * x^2 + d) + x * (a * x^2 + c), and then
/// the two columns will be: (b * x^2 + d) and (a * x^2 + c).
fn transpose<E: FieldElement>(coefficients: Vec<E>, num_columns: usize) -> Vec<Vec<E>> {
let column_len = coefficients.len() / num_columns;
let mut result = unsafe {
(0..num_columns)
.map(|_| uninit_vector(column_len))
.collect::<Vec<_>>()
};
// TODO: implement multi-threaded version
for (i, coeff) in coefficients.into_iter().enumerate() {
let row_idx = i / num_columns;
let col_idx = i % num_columns;
result[col_idx][row_idx] = coeff;
}
result
}
// TESTS
// ================================================================================================
#[cfg(test)]
mod tests {
use math::fields::f128::BaseElement;
use utils::collections::Vec;
#[test]
fn transpose() {
let values = (0u128..16).map(BaseElement::new).collect::<Vec<_>>();
let actual = super::transpose(values, 4);
#[rustfmt::skip]
let expected = vec![
vec![BaseElement::new(0), BaseElement::new(4), BaseElement::new(8), BaseElement::new(12)],
vec![BaseElement::new(1), BaseElement::new(5), BaseElement::new(9), BaseElement::new(13)],
vec![BaseElement::new(2), BaseElement::new(6), BaseElement::new(10), BaseElement::new(14)],
vec![BaseElement::new(3), BaseElement::new(7), BaseElement::new(11), BaseElement::new(15)],
];
assert_eq!(expected, actual)
}
}