1
0
Fork 0
mirror of synced 2025-09-24 04:40:05 +00:00

Merge pull request #322 from eupn/cs-variables-vec

Replace BTree with Vec in LinComb
This commit is contained in:
Thibaut Schaeffer 2019-04-18 14:32:45 +02:00 committed by GitHub
commit 726c4994a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 132 additions and 43 deletions

View file

@ -1,6 +1,6 @@
use crate::flat_absy::FlatVariable;
use num::Zero;
use std::collections::BTreeMap;
use std::collections::btree_map::{BTreeMap, Entry};
use std::fmt;
use std::ops::{Add, Div, Mul, Sub};
use zokrates_field::field::Field;
@ -20,13 +20,13 @@ impl<T: Field> QuadComb<T> {
// identify (k * ~ONE) * (lincomb) and return (k * lincomb)
match self.left.try_summand() {
Some((variable, coefficient)) if *variable == FlatVariable::one() => {
Some((ref variable, ref coefficient)) if *variable == FlatVariable::one() => {
return Some(self.right.clone() * &coefficient);
}
_ => {}
}
match self.right.try_summand() {
Some((variable, coefficient)) if *variable == FlatVariable::one() => {
Some((ref variable, ref coefficient)) if *variable == FlatVariable::one() => {
return Some(self.left.clone() * &coefficient);
}
_ => {}
@ -53,13 +53,22 @@ impl<T: Field> fmt::Display for QuadComb<T> {
}
}
#[derive(Eq, PartialOrd, Clone, Ord, Hash, Debug, Serialize, Deserialize)]
pub struct LinComb<T: Field>(pub Vec<(FlatVariable, T)>);
impl<T: Field> PartialEq for LinComb<T> {
fn eq(&self, other: &Self) -> bool {
self.as_canonical() == other.as_canonical()
}
}
#[derive(PartialEq, PartialOrd, Clone, Eq, Ord, Hash, Debug, Serialize, Deserialize)]
pub struct LinComb<T: Field>(pub BTreeMap<FlatVariable, T>);
pub struct CanonicalLinComb<T: Field>(BTreeMap<FlatVariable, T>);
impl<T: Field> LinComb<T> {
pub fn summand<U: Into<T>>(mult: U, var: FlatVariable) -> LinComb<T> {
let mut res = BTreeMap::new();
res.insert(var, mult.into());
let res = vec![(var, mult.into())];
LinComb(res)
}
@ -67,11 +76,61 @@ impl<T: Field> LinComb<T> {
Self::summand(1, FlatVariable::one())
}
pub fn try_summand(&self) -> Option<(&FlatVariable, &T)> {
if self.0.len() == 1 {
return self.0.iter().next();
pub fn try_summand(&self) -> Option<(FlatVariable, T)> {
match self.0.len() {
// if the lincomb is empty, it is not reduceable to a summand
0 => None,
_ => {
// take the first variable in the lincomb
let first = &self.0[0].0;
self.0
.iter()
.map(|element| {
// all terms must contain the same variable
if element.0 == *first {
// if they do, return the coefficient
Ok(&element.1)
} else {
// otherwise, stop
Err(())
}
})
// collect to a Result to short circuit when we hit an error
.collect::<Result<_, _>>()
// we didn't hit an error, do final processing. It's fine to clone here.
.map(|v: Vec<_>| (first.clone(), v.iter().fold(T::zero(), |acc, e| acc + *e)))
.ok()
}
}
None
}
fn as_canonical(&self) -> CanonicalLinComb<T> {
CanonicalLinComb(self.0.clone().into_iter().fold(
BTreeMap::new(),
|mut acc, (val, coeff)| {
// if we're adding 0 times some variable, we can ignore this term
if coeff != T::zero() {
match acc.entry(val) {
Entry::Occupied(o) => {
// if the new value is non zero, update, else remove the term entirely
if o.get().clone() + coeff.clone() != T::zero() {
*o.into_mut() = o.get().clone() + coeff;
} else {
o.remove();
}
}
Entry::Vacant(v) => {
// We checked earlier but let's make sure we're not creating zero-coeff terms
assert!(coeff != T::zero());
v.insert(coeff);
}
}
}
acc
},
))
}
}
@ -82,7 +141,8 @@ impl<T: Field> fmt::Display for LinComb<T> {
false => write!(
f,
"{}",
self.0
self.as_canonical()
.0
.iter()
.map(|(k, v)| format!("{} * {}", v.to_compact_dec_string(), k))
.collect::<Vec<_>>()
@ -94,8 +154,7 @@ impl<T: Field> fmt::Display for LinComb<T> {
impl<T: Field> From<FlatVariable> for LinComb<T> {
fn from(v: FlatVariable) -> LinComb<T> {
let mut r = BTreeMap::new();
r.insert(v, T::one());
let r = vec![(v, T::one())];
LinComb(r)
}
}
@ -104,16 +163,7 @@ impl<T: Field> Add<LinComb<T>> for LinComb<T> {
type Output = LinComb<T>;
fn add(self, other: LinComb<T>) -> LinComb<T> {
let mut res = self.0;
for (k, v) in other.0 {
let new_val = v + res.get(&k).unwrap_or(&T::zero());
if new_val == T::zero() {
res.remove(&k)
} else {
res.insert(k, new_val)
};
}
LinComb(res)
LinComb(self.0.into_iter().chain(other.0.into_iter()).collect())
}
}
@ -121,16 +171,18 @@ impl<T: Field> Sub<LinComb<T>> for LinComb<T> {
type Output = LinComb<T>;
fn sub(self, other: LinComb<T>) -> LinComb<T> {
let mut res = self.0;
for (k, v) in other.0 {
let new_val = T::zero() - v + res.get(&k).unwrap_or(&T::zero());
if new_val == T::zero() {
res.remove(&k)
} else {
res.insert(k, new_val)
};
}
LinComb(res)
// Concatenate with second vector that have negative coeffs
LinComb(
self.0
.into_iter()
.chain(
other
.0
.into_iter()
.map(|(var, coeff)| (var, T::zero() - coeff)),
)
.collect(),
)
}
}
@ -157,7 +209,7 @@ impl<T: Field> Div<&T> for LinComb<T> {
impl<T: Field> Zero for LinComb<T> {
fn zero() -> LinComb<T> {
LinComb(BTreeMap::new())
LinComb(Vec::new())
}
fn is_zero(&self) -> bool {
self.0.len() == 0
@ -184,16 +236,26 @@ mod tests {
let a: LinComb<FieldPrime> = FlatVariable::new(42).into();
let b: LinComb<FieldPrime> = FlatVariable::new(42).into();
let c = a + b.clone();
let mut expected_map = BTreeMap::new();
expected_map.insert(FlatVariable::new(42), FieldPrime::from(2));
assert_eq!(c, LinComb(expected_map));
let expected_vec = vec![
(FlatVariable::new(42), FieldPrime::from(1)),
(FlatVariable::new(42), FieldPrime::from(1)),
];
assert_eq!(c, LinComb(expected_vec));
}
#[test]
fn sub() {
let a: LinComb<FieldPrime> = FlatVariable::new(42).into();
let b: LinComb<FieldPrime> = FlatVariable::new(42).into();
let c = a - b.clone();
assert_eq!(c, LinComb::zero());
let expected_vec = vec![
(FlatVariable::new(42), FieldPrime::from(1)),
(FlatVariable::new(42), FieldPrime::from(-1)),
];
assert_eq!(c, LinComb(expected_vec));
}
#[test]
@ -244,4 +306,31 @@ mod tests {
assert_eq!(&a.to_string(), "(0) * (1 * _21)");
}
}
mod try {
use super::*;
#[test]
fn try_summand() {
let summand = LinComb(vec![
(FlatVariable::new(42), FieldPrime::from(1)),
(FlatVariable::new(42), FieldPrime::from(2)),
(FlatVariable::new(42), FieldPrime::from(3)),
]);
assert_eq!(
summand.try_summand(),
Some((FlatVariable::new(42), FieldPrime::from(6)))
);
let not_summand = LinComb(vec![
(FlatVariable::new(41), FieldPrime::from(1)),
(FlatVariable::new(42), FieldPrime::from(2)),
(FlatVariable::new(42), FieldPrime::from(3)),
]);
assert_eq!(not_summand.try_summand(), None);
let empty: LinComb<FieldPrime> = LinComb(vec![]);
assert_eq!(empty.try_summand(), None);
}
}
}

View file

@ -80,8 +80,8 @@ impl<T: Field> LinComb<T> {
fn is_assignee<U>(&self, witness: &BTreeMap<FlatVariable, U>) -> bool {
self.0.iter().count() == 1
&& self.0.iter().next().unwrap().1 == &T::from(1)
&& !witness.contains_key(self.0.iter().next().unwrap().0)
&& self.0.iter().next().unwrap().1 == T::from(1)
&& !witness.contains_key(&self.0.iter().next().unwrap().0)
}
}

View file

@ -47,11 +47,11 @@ impl<T: Field> Folder<T> for RedefinitionOptimizer<T> {
Some(l) => match lin.try_summand() {
// right side must be a single variable
Some((variable, coefficient)) => {
match variable == &FlatVariable::one() {
match variable == FlatVariable::one() {
// variable must not be ~ONE
false => match self.substitution.get(variable) {
false => match self.substitution.get(&variable) {
Some(_) => None,
None => Some((*variable, l / &coefficient)),
None => Some((variable, l / &coefficient)),
},
true => None,
}