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
use core::slice::Chunks;
use anyhow::{anyhow, Result};
use ark_ff::{vec::Vec, PrimeField};
pub trait MatrixOperations<F> {
/// Create a new matrix
fn new(n_rows: usize, n_cols: usize, elements: Vec<F>) -> Self;
/// Access elements as a vector
fn elements(&self) -> &Vec<F>;
/// Get element[i,j]
fn get_element(&self, i: usize, j: usize) -> F;
/// Set element[i,j]
fn set_element(&mut self, i: usize, j: usize, val: F);
/// Get rows
fn rows(&self) -> Vec<&[F]>;
/// Get rows in chunks
fn iter_rows(&self) -> Chunks<F> {
self.elements().chunks(self.n_cols())
}
/// Number of rows
fn n_rows(&self) -> usize;
/// Number of columns
fn n_cols(&self) -> usize;
/// Take transpose of the matrix
fn transpose(&self) -> Self;
/// Compute Hadamard (element-wise) product
fn hadamard_product(&self, rhs: &Self) -> Result<Self>
where
Self: Sized;
}
/// Multiply two matrices
pub fn mat_mul<F: PrimeField, M: MatrixOperations<F>>(lhs: &M, rhs: &M) -> Result<M> {
if lhs.n_cols() != rhs.n_rows() {
return Err(anyhow!(
"matrix dimensions do not allow matrix multiplication"
));
}
let rhs_T = rhs.transpose();
Ok(M::new(
lhs.n_rows(),
rhs.n_cols(),
lhs.iter_rows()
.flat_map(|row| {
// Rows of the transposed matrix are the columns of the original matrix
rhs_T
.iter_rows()
.map(|column| dot_product(row, column))
.collect::<Vec<F>>()
})
.collect(),
))
}
/// Compute vector dot product
pub fn dot_product<F: PrimeField>(a: &[F], b: &[F]) -> F {
if a.len() != b.len() {
panic!("vecs not same len")
}
a.iter().zip(b.iter()).map(|(x, y)| *x * *y).sum()
}
pub struct Polynomial<F> {
/// The coefficients of the polynomial a_0, ..., a_i
pub coeffs: Vec<F>,
}
impl<F: PrimeField> Polynomial<F> {
/// Construct a new polynomial
pub fn new(coeffs: Vec<F>) -> Self {
Self { coeffs }
}
/// Degree of the polynomial
pub fn max_degree(&self) -> usize {
self.coeffs.len() - 1
}
/// Evaluate the polynomial at a given point
pub fn evaluate(&self, x: F) -> F {
self.coeffs
.iter()
.rev()
.fold(F::zero(), |acc, coeff| acc * x + coeff)
}
/// Check if the polynomial is irreducible using Perron's irreducibility criterion.
pub fn is_irreducible(&self) -> bool {
// We first need to check the polynomial is monic.
if self.coeffs.last() != Some(&F::one()) {
unimplemented!("polynomial is not monic, not sure how to check irreducibility")
} else {
// The polynomial is monic, so we can apply Perron's criterion.
// See https://en.wikipedia.org/wiki/Perron%27s_irreducibility_criterion
// for more details.
let n = self.max_degree();
let mut sum = F::one();
for i in 0..n - 1 {
sum += self.coeffs[i];
}
match self.coeffs[n - 1] {
// Condition 1:
// $\abs{a_{n-1}} > 1 + \abs{a_{n-2}} + ... + \abs{a_0}$
coeff if coeff > sum => true,
// Condition 2:
// $\abs{a_{n-1}} = 1 + \abs{a_{n-2}} + ... + \abs{a_0}$
// AND
// f(1) != 0 AND f(-1) != 0
coeff if coeff == sum => {
let f_of_1 = self.evaluate(F::one());
let f_of_neg_1 = self.evaluate(-F::one());
f_of_1 != F::zero() && f_of_neg_1 != F::zero()
}
_ => false,
}
}
}
}
/// Matrix operations that are defined on square matrices.
pub trait SquareMatrixOperations<F> {
/// Compute the matrix inverse, if it exists
fn inverse(&self) -> Result<Self>
where
Self: Sized;
/// Construct an n x n identity matrix
fn identity(n: usize) -> Self;
/// Compute the matrix of minors
fn minors(&self) -> Self;
/// Compute the matrix of cofactors
fn cofactors(&self) -> Self;
/// Compute the matrix determinant
fn determinant(&self) -> F;
}
#[cfg(test)]
mod tests {
use super::*;
use ark_ed_on_bls12_377::Fq;
#[test]
fn poly_evaluate() {
// f(x) = 1 + 2x + 3x^2
let poly = Polynomial::new(vec![Fq::from(1), Fq::from(2), Fq::from(3)]);
assert_eq!(poly.max_degree(), 2);
assert_eq!(poly.evaluate(Fq::from(2)), Fq::from(17));
}
}