Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AllocVar implementation for Vec<T> does not respect the setup mode #17

Open
weikengchen opened this issue Nov 17, 2020 · 5 comments
Open
Labels
D-easy Difficulty: easy P-high Priority: high T-bug Type: bug T-design Type: discuss API design and/or research

Comments

@weikengchen
Copy link
Member

weikengchen commented Nov 17, 2020

In alloc.rs, we have the following implementation for Vec<T>:

/// This blanket implementation just allocates variables in `Self`
/// element by element.
impl<I, F: Field, A: AllocVar<I, F>> AllocVar<[I], F> for Vec<A> {
    fn new_variable<T: Borrow<[I]>>(
        cs: impl Into<Namespace<F>>,
        f: impl FnOnce() -> Result<T, SynthesisError>,
        mode: AllocationMode,
    ) -> Result<Self, SynthesisError> {
        let ns = cs.into();
        let cs = ns.cs();
        let mut vec = Vec::new();
        for value in f()?.borrow().iter() {
            vec.push(A::new_variable(cs.clone(), || Ok(value), mode)?);
        }
        Ok(vec)
    }
}

However, in the setup mode, assignments are missing, and we expect all executions of f to be wrapped, layer by layer, inside the closures of new_variable. But the current implementation does not respect the setup mode. Instead, it runs f and unwraps its result in the setup mode.

There seems no easy solution. One may want to unwrap f() in each A::new_variable, but f is FnOnce, so calling f()? inside the closure of each value in the Vec does not work.

Now let me describe some thinking on how to solve it.

First, we can let new_variable break the "fourth wall" and check whether the ConstraintSystem being referenced is in the setup mode. This would allow it to treat the case in the setup mode differently.

However, this is not enough, because it also needs to learn the size of the Vec. If f() cannot be run and unwrapped, there is no way for it to learn the size.

So, this becomes a tangled situation. Going further, did we use this Vec<T> blanket implementation anywhere?

@Pratyush
Copy link
Member

Hmm it seems that this needs to be fixed by also requiring the length to be passed in somehow. One way is to change [I] to be [I; n] for some common values of n. Another way is to have a wrapper struct

pub struct SliceWrapper<'a, I> { size: usize, slice: Option<&'a [I]> }

impl<...> SliceWrapper<...> {
	fn empty(size: usize) -> Self {
		Self {
			size,
			slice: None,
		}
	}

	fn new(slice: &[I]) -> Self {
		Self {
			size: slice.len(),
			slice: Some(slice),
		}
	}
}

This latter always has has a defined length, and so we should always be able to unwrap it inside AllocVar methods

@weikengchen
Copy link
Member Author

For the solution [I; n], it requires a Rust feature that would be stable after Feb 11 next year.

The feature is min_const_generics.
rust-lang/rust#79135

Should we do the second solution or wait for min_const_generics?

The second one would be more convenient because you can still use Vec. The first solution, [I; n], may create an additional barrier for one who has a Vec on hand.

@Pratyush
Copy link
Member

We can do both solutions, and for the array based one we can just implement it for common sizes (say, up to 32)

@Pratyush
Copy link
Member

I think we should take the approach of the wrapper struct for now, and remove the impl for Vec<T>, because that just encourages errors.

@weikengchen
Copy link
Member Author

Yes. Especially because the first one, [I; n], would make using Vec difficult.

Pratyush pushed a commit that referenced this issue Aug 8, 2021
* Fix pre_eq_reduce

The pre_eq_reduce should directly reduce the value to the normal form rather than checking it.

* fmt
@Pratyush Pratyush added T-design Type: discuss API design and/or research P-high Priority: high and removed help wanted labels Sep 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
D-easy Difficulty: easy P-high Priority: high T-bug Type: bug T-design Type: discuss API design and/or research
Projects
None yet
Development

No branches or pull requests

2 participants