Skip to content

Commit dfdad2b

Browse files
windericaPratyush
andauthored
Add convenient method for variable allocation with inferred mode (#143)
Co-authored-by: Pratyush Mishra <[email protected]>
1 parent f742abd commit dfdad2b

File tree

2 files changed

+49
-2
lines changed

2 files changed

+49
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
- Add `UInt::rotate_{left,right}_in_place`.
2424
- Add `{Boolean,UInt}::not_in_place`.
2525
- Add `UInt::{from_bytes_le, from_bytes_be, to_bytes_be}`.
26-
- [\#144](https://github.com/arkworks-rs/r1cs-std/pull/144)
27-
- Add `ToConstraintFieldGadget` bounds to `CurveVar` and `FieldVar`
26+
- [\#143](https://github.com/arkworks-rs/r1cs-std/pull/143) Add `AllocVar::new_variable_with_inferred_mode`.
27+
- [\#144](https://github.com/arkworks-rs/r1cs-std/pull/144) Add `ToConstraintFieldGadget` bounds to `CurveVar` and `FieldVar`
2828

2929
### Improvements
3030

src/alloc.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,53 @@ pub trait AllocVar<V: ?Sized, F: Field>: Sized {
7676
) -> Result<Self, SynthesisError> {
7777
Self::new_variable(cs, f, AllocationMode::Witness)
7878
}
79+
80+
/// Allocates a new constant or private witness of type `Self` in the
81+
/// `ConstraintSystem` `cs` with the allocation mode inferred from `cs`.
82+
/// A constant is allocated if `cs` is `None`, and a private witness is
83+
/// allocated otherwise.
84+
///
85+
/// A common use case is the creation of non-deterministic advice (a.k.a.
86+
/// hints) in the circuit, where this method can avoid boilerplate code
87+
/// while allowing optimization on circuit size.
88+
///
89+
/// For example, to compute `x_var / y_var` where `y_var` is a non-zero
90+
/// variable, one can write:
91+
/// ```
92+
/// use ark_ff::PrimeField;
93+
/// use ark_r1cs_std::{alloc::AllocVar, fields::{fp::FpVar, FieldVar}, R1CSVar};
94+
/// use ark_relations::r1cs::SynthesisError;
95+
///
96+
/// fn div<F: PrimeField>(x_var: &FpVar<F>, y_var: &FpVar<F>) -> Result<FpVar<F>, SynthesisError> {
97+
/// let cs = x_var.cs().or(y_var.cs());
98+
/// let z_var = FpVar::new_variable_with_inferred_mode(cs, || Ok(x_var.value()? / y_var.value()?))?;
99+
/// z_var.mul_equals(y_var, x_var)?;
100+
/// Ok(z_var)
101+
/// }
102+
/// ```
103+
/// In this example, if either `x_var` or `y_var` is a witness variable,
104+
/// then `z_var` is also a witness variable. On the other hand, `z_var`
105+
/// is a constant if both `x_var` and `y_var` are constants (i.e., `cs`
106+
/// is `None`), and future operations on `z_var` do not generate any
107+
/// constraints.
108+
///
109+
/// (Note that we use division as an example for simplicity. You may
110+
/// call `x_var.mul_by_inverse(y_var)?` directly, which internally works
111+
/// similarly to the above code.)
112+
#[tracing::instrument(target = "r1cs", skip(cs, f))]
113+
fn new_variable_with_inferred_mode<T: Borrow<V>>(
114+
cs: impl Into<Namespace<F>>,
115+
f: impl FnOnce() -> Result<T, SynthesisError>,
116+
) -> Result<Self, SynthesisError> {
117+
let ns: Namespace<F> = cs.into();
118+
let cs = ns.cs();
119+
let mode = if cs.is_none() {
120+
AllocationMode::Constant
121+
} else {
122+
AllocationMode::Witness
123+
};
124+
Self::new_variable(cs, f, mode)
125+
}
79126
}
80127

81128
/// This blanket implementation just allocates variables in `Self`

0 commit comments

Comments
 (0)