Skip to content

Commit 831c102

Browse files
authored
More explicit R1CS tests/examples (#358)
1 parent 7af2b44 commit 831c102

File tree

4 files changed

+123
-8
lines changed

4 files changed

+123
-8
lines changed

relations/src/r1cs/constraint_system.rs

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ impl<F: Field> ConstraintSystem<F> {
639639
self.lc_assignment_cache.borrow_mut().insert(idx, value);
640640
Some(value)
641641
}
642-
}
642+
},
643643
}
644644
}
645645
}
@@ -1053,4 +1053,119 @@ mod tests {
10531053
assert_eq!(matrices.c[2], vec![(two, 1), (two, 2)]);
10541054
Ok(())
10551055
}
1056+
1057+
#[test]
1058+
fn matrix_generation_outlined() -> crate::r1cs::Result<()> {
1059+
let cs = ConstraintSystem::<Fr>::new_ref();
1060+
cs.set_optimization_goal(OptimizationGoal::Weight);
1061+
let two = Fr::one() + Fr::one();
1062+
let a = cs.new_input_variable(|| Ok(Fr::one()))?;
1063+
let b = cs.new_witness_variable(|| Ok(Fr::one()))?;
1064+
let c = cs.new_witness_variable(|| Ok(two))?;
1065+
cs.enforce_constraint(lc!() + a, lc!() + (two, b), lc!() + c)?;
1066+
1067+
let d = cs.new_lc(lc!() + a + b)?;
1068+
cs.enforce_constraint(lc!() + a, lc!() + d, lc!() + d)?;
1069+
1070+
let e = cs.new_lc(lc!() + d + d)?;
1071+
cs.enforce_constraint(lc!() + Variable::One, lc!() + e, lc!() + e)?;
1072+
1073+
cs.finalize();
1074+
assert!(cs.is_satisfied().is_ok());
1075+
let matrices = cs.to_matrices().unwrap();
1076+
assert_eq!(matrices.a[0], vec![(Fr::one(), 1)]);
1077+
assert_eq!(matrices.b[0], vec![(two, 2)]);
1078+
assert_eq!(matrices.c[0], vec![(Fr::one(), 3)]);
1079+
1080+
assert_eq!(matrices.a[1], vec![(Fr::one(), 1)]);
1081+
// Notice here how the variable allocated for d is outlined
1082+
// compared to the example in previous test case.
1083+
// We are optimising for weight: there are less non-zero elements.
1084+
assert_eq!(matrices.b[1], vec![(Fr::one(), 4)]);
1085+
assert_eq!(matrices.c[1], vec![(Fr::one(), 4)]);
1086+
1087+
assert_eq!(matrices.a[2], vec![(Fr::one(), 0)]);
1088+
assert_eq!(matrices.b[2], vec![(two, 4)]);
1089+
assert_eq!(matrices.c[2], vec![(two, 4)]);
1090+
Ok(())
1091+
}
1092+
1093+
/// Example meant to follow as closely as possible the excellent R1CS
1094+
/// write-up by [Vitalik Buterin](https://vitalik.ca/general/2016/12/10/qap.html)
1095+
/// and demonstrate how to construct such matrices in arkworks.
1096+
#[test]
1097+
fn matrix_generation_example() -> crate::r1cs::Result<()> {
1098+
let cs = ConstraintSystem::<Fr>::new_ref();
1099+
// helper definitions
1100+
let three = Fr::from(3u8);
1101+
let five = Fr::from(5u8);
1102+
let nine = Fr::from(9u8);
1103+
// There will be six variables in the system, in the order governed by adding
1104+
// them to the constraint system (Note that the CS is initialised with
1105+
// `Variable::One` in the first position implicitly).
1106+
// Note also that the all public variables will always be placed before all witnesses
1107+
//
1108+
// Variable::One
1109+
// Variable::Instance(5)
1110+
// Variable::Witness(3) ( == x )
1111+
// Variable::Witness(9) ( == sym_1 )
1112+
// Variable::Witness(27) ( == y )
1113+
// Variable::Witness(30) ( == y )
1114+
1115+
// let one = Variable::One; // public input, implicitly defined
1116+
let out = cs.new_input_variable(|| Ok(nine * three + three + five))?; // public input
1117+
let x = cs.new_witness_variable(|| Ok(three))?; // explicit witness
1118+
let sym_1 = cs.new_witness_variable(|| Ok(nine))?; // intermediate witness variable
1119+
let y = cs.new_witness_variable(|| Ok(nine * three))?; // intermediate witness variable
1120+
let sym_2 = cs.new_witness_variable(|| Ok(nine * three + three))?; // intermediate witness variable
1121+
1122+
cs.enforce_constraint(lc!() + x, lc!() + x, lc!() + sym_1)?;
1123+
cs.enforce_constraint(lc!() + sym_1, lc!() + x, lc!() + y)?;
1124+
cs.enforce_constraint(lc!() + y + x, lc!() + Variable::One, lc!() + sym_2)?;
1125+
cs.enforce_constraint(
1126+
lc!() + sym_2 + (five, Variable::One),
1127+
lc!() + Variable::One,
1128+
lc!() + out,
1129+
)?;
1130+
1131+
cs.finalize();
1132+
assert!(cs.is_satisfied().is_ok());
1133+
let matrices = cs.to_matrices().unwrap();
1134+
// There are four gates(constraints), each generating a row.
1135+
// Resulting matrices:
1136+
// (Note how 2nd & 3rd columns are swapped compared to the online example.
1137+
// This results from an implementation detail of placing all Variable::Instances(_) first.
1138+
//
1139+
// A
1140+
// [0, 0, 1, 0, 0, 0]
1141+
// [0, 0, 0, 1, 0, 0]
1142+
// [0, 0, 1, 0, 1, 0]
1143+
// [5, 0, 0, 0, 0, 1]
1144+
// B
1145+
// [0, 0, 1, 0, 0, 0]
1146+
// [0, 0, 1, 0, 0, 0]
1147+
// [1, 0, 0, 0, 0, 0]
1148+
// [1, 0, 0, 0, 0, 0]
1149+
// C
1150+
// [0, 0, 0, 1, 0, 0]
1151+
// [0, 0, 0, 0, 1, 0]
1152+
// [0, 0, 0, 0, 0, 1]
1153+
// [0, 1, 0, 0, 0, 0]
1154+
assert_eq!(matrices.a[0], vec![(Fr::one(), 2)]);
1155+
assert_eq!(matrices.b[0], vec![(Fr::one(), 2)]);
1156+
assert_eq!(matrices.c[0], vec![(Fr::one(), 3)]);
1157+
1158+
assert_eq!(matrices.a[1], vec![(Fr::one(), 3)]);
1159+
assert_eq!(matrices.b[1], vec![(Fr::one(), 2)]);
1160+
assert_eq!(matrices.c[1], vec![(Fr::one(), 4)]);
1161+
1162+
assert_eq!(matrices.a[2], vec![(Fr::one(), 2), (Fr::one(), 4)]);
1163+
assert_eq!(matrices.b[2], vec![(Fr::one(), 0)]);
1164+
assert_eq!(matrices.c[2], vec![(Fr::one(), 5)]);
1165+
1166+
assert_eq!(matrices.a[3], vec![(five, 0), (Fr::one(), 5)]);
1167+
assert_eq!(matrices.b[3], vec![(Fr::one(), 0)]);
1168+
assert_eq!(matrices.c[3], vec![(Fr::one(), 1)]);
1169+
Ok(())
1170+
}
10561171
}

relations/src/r1cs/error.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,17 @@ impl fmt::Display for SynthesisError {
3131
SynthesisError::MissingCS => write!(f, "the constraint system was `None`"),
3232
SynthesisError::AssignmentMissing => {
3333
write!(f, "an assignment for a variable could not be computed")
34-
}
34+
},
3535
SynthesisError::DivisionByZero => write!(f, "division by zero"),
3636
SynthesisError::Unsatisfiable => write!(f, "unsatisfiable constraint system"),
3737
SynthesisError::PolynomialDegreeTooLarge => write!(f, "polynomial degree is too large"),
3838
SynthesisError::UnexpectedIdentity => {
3939
write!(f, "encountered an identity element in the CRS")
40-
}
40+
},
4141
SynthesisError::MalformedVerifyingKey => write!(f, "malformed verifying key"),
4242
SynthesisError::UnconstrainedVariable => {
4343
write!(f, "auxiliary variable was unconstrained")
44-
}
44+
},
4545
}
4646
}
4747
}

relations/src/r1cs/impl_lc.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,16 +228,16 @@ where
228228
Ordering::Greater => {
229229
new_vec.push((push_fn(other[j].0), other[j].1));
230230
j += 1;
231-
}
231+
},
232232
Ordering::Less => {
233233
new_vec.push(*self_cur);
234234
i += 1;
235-
}
235+
},
236236
Ordering::Equal => {
237237
new_vec.push((combine_fn(self_cur.0, other_cur.0), self_cur.1));
238238
i += 1;
239239
j += 1;
240-
}
240+
},
241241
};
242242
}
243243
new_vec.extend_from_slice(&cur[i..]);

relations/src/r1cs/trace.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ where
6868
id if id == TypeId::of::<Self>() => Some(self as *const _ as *const ()),
6969
id if id == TypeId::of::<WithContext>() => {
7070
Some(&self.get_context as *const _ as *const ())
71-
}
71+
},
7272
_ => None,
7373
}
7474
}

0 commit comments

Comments
 (0)