facebook / winterfell

A STARK prover and verifier for arbitrary computations

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support custom implementations of the Constraint Evaluator

irakliyk opened this issue · comments

Currently, ConstraintEvaluator as a concrete struct. This struct contains functionality needed to evaluate constraints over the trace LDE and to combine all constraints having the same constraint divisor (i.e., constraints with the same vanishing polynomial).

While this structure works, it is not flexible enough to support such things are more optimized constraint evaluation or delegating constraint evaluation to hardware etc. To fix this, we could introduce a ConstraintEvaluator trait and then provide a default implementation for this trait. The trait could look as follows:

pub trait ConstraintEvaluator<'a, E: FieldElement> {
    type Air: Air<BaseField = E::BaseField>;
    type TraceLde: TraceLde<BaseField = E::BaseField>;

    fn evaluate(
        self,
        trace: &Self::TraceLde,
        domain: &'a StarkDomain<E::BaseField>,
    ) -> ConstraintEvaluationTable<'a, E>;
}

Creating an instances of ConstraintEvaluator would be handled by the prover. To do this, we'd need to add the following to the Prover trait:

pub trait Prover {
    ...
    type ConstraintEvaluator<'a, E>: ConstraintEvaluator<'a, E, Air = Self::Air, TraceLde = Self::TraceLde>
    where
        E: FieldElement<BaseField = Self::BaseField>;

    /// Returns a new constraint evaluator which can be used to evaluate transition and boundary
    /// constraints over the extended execution trace.
    fn new_evaluator<'a, E>(
        &self,
        air: &'a Self::Air,
        aux_rand_elements: AuxTraceRandElements<E>,
        composition_coefficients: ConstraintCompositionCoefficients<E>,
    ) -> Self::ConstraintEvaluator<'a, E>
    where
        E: FieldElement<BaseField = Self::BaseField>;
}

The above assumes that #180 has already been implemented.

@irakliyk Thanks for proposing this issue, this will be very useful for acceleration. I think it'll be good to make evaluate an async function. Web workers use message-based communication, so we end up dispatching a bunch of messages for workers and wait for them to come back, it's nice to have future support for that, see: https://github.com/starkoracles/Aero/blob/f9781dca9265727d53140e53e4d9b793fab5839a/sdk-api/miden-wasm/src/proving_worker.rs#L305.

It'll also be nice if we can implement the same thing for constraint commitment, it can also benefit from custom acceleration implementation, esp in environments that don't support Rayon (such as wasm).

I think it'll be good to make evaluate an async function.

Yes - I can see how this could be useful! I don't have too much experience working with async code. Would you suggestion mean that we'd define the interface like so:

pub trait ConstraintEvaluator<'a, E: FieldElement> {
    type Air: Air<BaseField = E::BaseField>;
    type TraceLde: TraceLde<BaseField = E::BaseField>;

    async fn evaluate(
        self,
        trace: &Self::TraceLde,
        domain: &'a StarkDomain<E::BaseField>,
    ) -> ConstraintEvaluationTable<'a, E>;
}

My understanding is that this currently works only on nightly, but there is a plan to stabilize it relatively soon.

One thing I'm not sure of: what kind of effect would it have in places where we don't need async functionality?

My understanding is that this currently works only on nightly, but there is a plan to stabilize it relatively soon.

Ah ok, I didn't realize it's not supported on stable yet. Maybe what we can do is have a sync fn evaluate by default and add the async version behind a feature so not all consumers are forced to use nightly.

One thing I'm not sure of: what kind of effect would it have in places where we don't need async functionality?

From what I can tell, defining sync code within async fn is ok, it'll just be executed normally without any context switching.