pub mod polynomial_constraint;
use super::{Constraint, ConstraintSystem, Matrix};
use crate::{gr1cs::Variable, utils::error::SynthesisError::ArityMismatch};
use ark_ff::Field;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, SerializationError};
use ark_std::{io::Write, vec::Vec};
use polynomial_constraint::PolynomialPredicate;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum Predicate<F: Field> {
Polynomial(PolynomialPredicate<F>),
}
impl<F: Field> ark_serialize::Valid for Predicate<F> {
fn check(&self) -> Result<(), SerializationError> {
match self {
Predicate::Polynomial(p) => p.check(),
}
}
}
impl<F: Field> CanonicalDeserialize for Predicate<F> {
fn deserialize_with_mode<R: ark_serialize::Read>(
reader: R,
compress: Compress,
should_validate: ark_serialize::Validate,
) -> Result<Self, SerializationError> {
let predicate_type =
PolynomialPredicate::<F>::deserialize_with_mode(reader, compress, should_validate)?;
Ok(Predicate::Polynomial(predicate_type))
}
}
impl<F: Field> CanonicalSerialize for Predicate<F> {
fn serialize_with_mode<W: Write>(
&self,
writer: W,
compress: Compress,
) -> Result<(), SerializationError> {
match self {
Predicate::Polynomial(p) => p.serialize_with_mode(writer, compress),
}
}
fn serialized_size(&self, compress: Compress) -> usize {
match self {
Predicate::Polynomial(p) => p.serialized_size(compress),
}
}
}
impl<F: Field> Predicate<F> {
fn is_satisfied(&self, variables: &[F]) -> bool {
match self {
Predicate::Polynomial(p) => p.is_satisfied(variables),
}
}
fn arity(&self) -> usize {
match self {
Predicate::Polynomial(p) => p.arity(),
}
}
}
#[derive(Debug, Clone)]
pub struct PredicateConstraintSystem<F: Field> {
argument_lcs: Vec<Vec<Variable>>,
num_constraints: usize,
predicate: Predicate<F>,
}
impl<F: Field> PredicateConstraintSystem<F> {
fn new(predicate: Predicate<F>) -> Self {
Self {
argument_lcs: vec![Vec::new(); predicate.arity()],
predicate,
num_constraints: 0,
}
}
pub fn new_polynomial_predicate_cs(arity: usize, terms: Vec<(F, Vec<(usize, usize)>)>) -> Self {
Self::new(Predicate::Polynomial(PolynomialPredicate::new(
arity, terms,
)))
}
pub fn new_r1cs() -> crate::utils::Result<Self> {
Ok(Self::new_polynomial_predicate_cs(
3,
vec![(F::ONE, vec![(0, 1), (1, 1)]), (-F::ONE, vec![(2, 1)])],
))
}
pub fn new_sr1cs_predicate() -> crate::utils::Result<Self> {
Ok(Self::new_polynomial_predicate_cs(
2,
vec![(F::ONE, vec![(0, 2)]), (-F::ONE, vec![(1, 1)])],
))
}
pub fn get_arity(&self) -> usize {
self.predicate.arity()
}
pub fn num_constraints(&self) -> usize {
self.num_constraints
}
pub fn get_constraints(&self) -> &Vec<Constraint> {
&self.argument_lcs
}
pub fn get_predicate(&self) -> &Predicate<F> {
&self.predicate
}
pub fn enforce_constraint(
&mut self,
constraint: impl IntoIterator<Item = Variable>,
) -> crate::utils::Result<()> {
let mut arity = 0;
constraint
.into_iter()
.zip(&mut self.argument_lcs)
.for_each(|(lc_index, arg_lc)| {
arity += 1;
arg_lc.push(lc_index);
});
if arity != self.get_arity() {
return Err(ArityMismatch);
}
self.num_constraints += 1;
Ok(())
}
fn iter_constraints(&self) -> impl Iterator<Item = Constraint> + '_ {
let num_constraints = self.num_constraints;
(0..num_constraints).map(move |i| self.argument_lcs.iter().map(|lc_s| lc_s[i]).collect())
}
pub fn which_constraint_is_unsatisfied(&self, cs: &ConstraintSystem<F>) -> Option<usize> {
let panic_msg = |v| panic!("Variable {v:?} is not assigned; did you run `cs.finalize()`?");
for (i, constraint) in self.iter_constraints().enumerate() {
let variables: Vec<F> = constraint
.into_iter()
.map(|v| {
cs.assigned_value(v).unwrap_or_else(|| {
cs.get_lc(v)
.iter()
.map(|&(c, v)| c * cs.assigned_value(v).unwrap_or_else(|| panic_msg(v)))
.sum()
})
})
.collect();
if !self.predicate.is_satisfied(&variables) {
return Some(i);
}
}
None
}
pub fn to_matrices(&self, cs: &ConstraintSystem<F>) -> Vec<Matrix<F>> {
let mut matrices: Vec<Matrix<F>> = vec![Vec::new(); self.get_arity()];
for constraint in self.iter_constraints() {
for (matrix_ind, lc_index) in constraint.iter().enumerate() {
let lc = cs.get_lc(*lc_index);
let row = cs.make_row(lc);
matrices[matrix_ind].push(row);
}
}
matrices
}
}