Merge pull request #48 from mir-protocol/target_field_extension

Clean extension code + Recursive interpolation gate
This commit is contained in:
wborgeaud 2021-05-24 22:04:25 +02:00 committed by GitHub
commit 5a261332d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 271 additions and 170 deletions

View File

@ -1,16 +1,43 @@
use crate::field::extension_field::quadratic::QuadraticFieldExtension;
use crate::field::extension_field::quartic::QuarticFieldExtension;
use crate::field::field::Field;
pub mod quadratic;
pub mod quartic;
pub mod target;
/// Optimal extension field trait.
/// A degree `d` field extension is optimal if there exists a base field element `W`,
/// such that the extension is `F[X]/(X^d-W)`.
#[allow(clippy::upper_case_acronyms)]
pub trait OEF<const D: usize>: FieldExtension<D> {
// Element W of BaseField, such that `X^d - W` is irreducible over BaseField.
const W: Self::BaseField;
/// Frobenius automorphisms: x -> x^p, where p is the order of BaseField.
fn frobenius(&self) -> Self {
let arr = self.to_basefield_array();
let k = (Self::BaseField::ORDER - 1) / (D as u64);
let z0 = Self::W.exp(k);
let mut z = Self::BaseField::ONE;
let mut res = [Self::BaseField::ZERO; D];
for i in 0..D {
res[i] = arr[i] * z;
z *= z0;
}
Self::from_basefield_array(res)
}
}
impl<F: Field> OEF<1> for F {
const W: Self::BaseField = F::ZERO;
}
pub trait Extendable<const D: usize>: Sized {
type Extension: Field + FieldExtension<D, BaseField = Self> + From<Self>;
type Extension: Field + OEF<D, BaseField = Self> + From<Self>;
}
impl<F: Field> Extendable<1> for F {
type Extension = Self;
type Extension = F;
}
pub trait FieldExtension<const D: usize>: Field {
@ -21,6 +48,10 @@ pub trait FieldExtension<const D: usize>: Field {
fn from_basefield_array(arr: [Self::BaseField; D]) -> Self;
fn from_basefield(x: Self::BaseField) -> Self;
fn is_in_basefield(&self) -> bool {
self.to_basefield_array()[1..].iter().all(|x| x.is_zero())
}
}
impl<F: Field> FieldExtension<1> for F {
@ -39,38 +70,6 @@ impl<F: Field> FieldExtension<1> for F {
}
}
impl<FE: QuadraticFieldExtension> FieldExtension<2> for FE {
type BaseField = FE::BaseField;
fn to_basefield_array(&self) -> [Self::BaseField; 2] {
self.to_canonical_representation()
}
fn from_basefield_array(arr: [Self::BaseField; 2]) -> Self {
Self::from_canonical_representation(arr)
}
fn from_basefield(x: Self::BaseField) -> Self {
x.into()
}
}
impl<FE: QuarticFieldExtension> FieldExtension<4> for FE {
type BaseField = FE::BaseField;
fn to_basefield_array(&self) -> [Self::BaseField; 4] {
self.to_canonical_representation()
}
fn from_basefield_array(arr: [Self::BaseField; 4]) -> Self {
Self::from_canonical_representation(arr)
}
fn from_basefield(x: Self::BaseField) -> Self {
x.into()
}
}
/// Flatten the slice by sending every extension field element to its D-sized canonical representation.
pub fn flatten<F: Field, const D: usize>(l: &[F::Extension]) -> Vec<F>
where

View File

@ -1,4 +1,5 @@
use crate::field::crandall_field::CrandallField;
use crate::field::extension_field::{FieldExtension, OEF};
use crate::field::field::Field;
use rand::Rng;
use std::fmt::{Debug, Display, Formatter};
@ -6,61 +7,41 @@ use std::hash::{Hash, Hasher};
use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
pub trait QuadraticFieldExtension:
Field + From<<Self as QuadraticFieldExtension>::BaseField>
{
type BaseField: Field;
// Element W of BaseField, such that `X^2 - W` is irreducible over BaseField.
const W: Self::BaseField;
fn to_canonical_representation(&self) -> [Self::BaseField; 2];
fn from_canonical_representation(v: [Self::BaseField; 2]) -> Self;
fn is_in_basefield(&self) -> bool {
self.to_canonical_representation()[1..]
.iter()
.all(|x| x.is_zero())
}
/// Frobenius automorphisms: x -> x^p, where p is the order of BaseField.
fn frobenius(&self) -> Self {
let [a0, a1] = self.to_canonical_representation();
let k = (Self::BaseField::ORDER - 1) / 2;
let z = Self::W.exp(k);
Self::from_canonical_representation([a0, a1 * z])
}
}
#[derive(Copy, Clone)]
pub struct QuadraticCrandallField([CrandallField; 2]);
impl QuadraticFieldExtension for QuadraticCrandallField {
type BaseField = CrandallField;
impl OEF<2> for QuadraticCrandallField {
// Verifiable in Sage with
// ``R.<x> = GF(p)[]; assert (x^2 -3).is_irreducible()`.
const W: Self::BaseField = CrandallField(3);
const W: CrandallField = CrandallField(3);
}
fn to_canonical_representation(&self) -> [Self::BaseField; 2] {
impl FieldExtension<2> for QuadraticCrandallField {
type BaseField = CrandallField;
fn to_basefield_array(&self) -> [Self::BaseField; 2] {
self.0
}
fn from_canonical_representation(v: [Self::BaseField; 2]) -> Self {
Self(v)
fn from_basefield_array(arr: [Self::BaseField; 2]) -> Self {
Self(arr)
}
fn from_basefield(x: Self::BaseField) -> Self {
x.into()
}
}
impl From<<Self as QuadraticFieldExtension>::BaseField> for QuadraticCrandallField {
fn from(x: <Self as QuadraticFieldExtension>::BaseField) -> Self {
Self([x, <Self as QuadraticFieldExtension>::BaseField::ZERO])
impl From<<Self as FieldExtension<2>>::BaseField> for QuadraticCrandallField {
fn from(x: <Self as FieldExtension<2>>::BaseField) -> Self {
Self([x, <Self as FieldExtension<2>>::BaseField::ZERO])
}
}
impl PartialEq for QuadraticCrandallField {
fn eq(&self, other: &Self) -> bool {
self.to_canonical_representation() == other.to_canonical_representation()
FieldExtension::<2>::to_basefield_array(self)
== FieldExtension::<2>::to_basefield_array(other)
}
}
@ -68,7 +49,7 @@ impl Eq for QuadraticCrandallField {}
impl Hash for QuadraticCrandallField {
fn hash<H: Hasher>(&self, state: &mut H) {
for l in &self.to_canonical_representation() {
for l in &FieldExtension::<2>::to_basefield_array(self) {
Hash::hash(l, state);
}
}
@ -99,9 +80,9 @@ impl Field for QuadraticCrandallField {
return None;
}
let a_pow_r_minus_1 = self.frobenius();
let a_pow_r_minus_1 = OEF::<2>::frobenius(self);
let a_pow_r = a_pow_r_minus_1 * *self;
debug_assert!(a_pow_r.is_in_basefield());
debug_assert!(FieldExtension::<2>::is_in_basefield(&a_pow_r));
Some(a_pow_r_minus_1 * a_pow_r.0[0].inverse().into())
}
@ -111,16 +92,13 @@ impl Field for QuadraticCrandallField {
}
fn from_canonical_u64(n: u64) -> Self {
Self([
<Self as QuadraticFieldExtension>::BaseField::from_canonical_u64(n),
<Self as QuadraticFieldExtension>::BaseField::ZERO,
])
<Self as FieldExtension<2>>::BaseField::from_canonical_u64(n).into()
}
fn rand_from_rng<R: Rng>(rng: &mut R) -> Self {
Self([
<Self as QuadraticFieldExtension>::BaseField::rand_from_rng(rng),
<Self as QuadraticFieldExtension>::BaseField::rand_from_rng(rng),
<Self as FieldExtension<2>>::BaseField::rand_from_rng(rng),
<Self as FieldExtension<2>>::BaseField::rand_from_rng(rng),
])
}
}
@ -191,7 +169,7 @@ impl Mul for QuadraticCrandallField {
let Self([a0, a1]) = self;
let Self([b0, b1]) = rhs;
let c0 = a0 * b0 + Self::W * a1 * b1;
let c0 = a0 * b0 + <Self as OEF<2>>::W * a1 * b1;
let c1 = a0 * b1 + a1 * b0;
Self([c0, c1])
@ -228,9 +206,8 @@ impl DivAssign for QuadraticCrandallField {
#[cfg(test)]
mod tests {
use crate::field::extension_field::quadratic::{
QuadraticCrandallField, QuadraticFieldExtension,
};
use crate::field::extension_field::quadratic::QuadraticCrandallField;
use crate::field::extension_field::{FieldExtension, OEF};
use crate::field::field::Field;
#[test]
@ -241,10 +218,7 @@ mod tests {
let z = F::rand();
assert_eq!(x + (-x), F::ZERO);
assert_eq!(-x, F::ZERO - x);
assert_eq!(
x + x,
x * <F as QuadraticFieldExtension>::BaseField::TWO.into()
);
assert_eq!(x + x, x * <F as FieldExtension<2>>::BaseField::TWO.into());
assert_eq!(x * (-x), -x.square());
assert_eq!(x + y, y + x);
assert_eq!(x * y, y * x);
@ -273,8 +247,8 @@ mod tests {
type F = QuadraticCrandallField;
let x = F::rand();
assert_eq!(
x.exp(<F as QuadraticFieldExtension>::BaseField::ORDER),
x.frobenius()
x.exp(<F as FieldExtension<2>>::BaseField::ORDER),
OEF::<2>::frobenius(&x)
);
}
@ -300,10 +274,9 @@ mod tests {
F::POWER_OF_TWO_GENERATOR
);
assert_eq!(
F::POWER_OF_TWO_GENERATOR.exp(
1 << (F::TWO_ADICITY - <F as QuadraticFieldExtension>::BaseField::TWO_ADICITY)
),
<F as QuadraticFieldExtension>::BaseField::POWER_OF_TWO_GENERATOR.into()
F::POWER_OF_TWO_GENERATOR
.exp(1 << (F::TWO_ADICITY - <F as FieldExtension<2>>::BaseField::TWO_ADICITY)),
<F as FieldExtension<2>>::BaseField::POWER_OF_TWO_GENERATOR.into()
);
}
}

View File

@ -1,4 +1,5 @@
use crate::field::crandall_field::CrandallField;
use crate::field::extension_field::{FieldExtension, OEF};
use crate::field::field::Field;
use rand::Rng;
use std::fmt::{Debug, Display, Formatter};
@ -6,72 +7,46 @@ use std::hash::{Hash, Hasher};
use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
pub trait QuarticFieldExtension: Field + From<<Self as QuarticFieldExtension>::BaseField> {
type BaseField: Field;
// Element W of BaseField, such that `X^4 - W` is irreducible over BaseField.
const W: Self::BaseField;
fn to_canonical_representation(&self) -> [Self::BaseField; 4];
fn from_canonical_representation(v: [Self::BaseField; 4]) -> Self;
fn is_in_basefield(&self) -> bool {
self.to_canonical_representation()[1..]
.iter()
.all(|x| x.is_zero())
}
/// Frobenius automorphisms: x -> x^p, where p is the order of BaseField.
fn frobenius(&self) -> Self {
let [a0, a1, a2, a3] = self.to_canonical_representation();
let k = (Self::BaseField::ORDER - 1) / 4;
let z0 = Self::W.exp(k);
let mut z = Self::BaseField::ONE;
let b0 = a0 * z;
z *= z0;
let b1 = a1 * z;
z *= z0;
let b2 = a2 * z;
z *= z0;
let b3 = a3 * z;
Self::from_canonical_representation([b0, b1, b2, b3])
}
}
#[derive(Copy, Clone)]
pub struct QuarticCrandallField([CrandallField; 4]);
impl QuarticFieldExtension for QuarticCrandallField {
type BaseField = CrandallField;
impl OEF<4> for QuarticCrandallField {
// Verifiable in Sage with
// ``R.<x> = GF(p)[]; assert (x^4 -3).is_irreducible()`.
const W: Self::BaseField = CrandallField(3);
const W: CrandallField = CrandallField(3);
}
fn to_canonical_representation(&self) -> [Self::BaseField; 4] {
impl FieldExtension<4> for QuarticCrandallField {
type BaseField = CrandallField;
fn to_basefield_array(&self) -> [Self::BaseField; 4] {
self.0
}
fn from_canonical_representation(v: [Self::BaseField; 4]) -> Self {
Self(v)
fn from_basefield_array(arr: [Self::BaseField; 4]) -> Self {
Self(arr)
}
fn from_basefield(x: Self::BaseField) -> Self {
x.into()
}
}
impl From<<Self as QuarticFieldExtension>::BaseField> for QuarticCrandallField {
fn from(x: <Self as QuarticFieldExtension>::BaseField) -> Self {
impl From<<Self as FieldExtension<4>>::BaseField> for QuarticCrandallField {
fn from(x: <Self as FieldExtension<4>>::BaseField) -> Self {
Self([
x,
<Self as QuarticFieldExtension>::BaseField::ZERO,
<Self as QuarticFieldExtension>::BaseField::ZERO,
<Self as QuarticFieldExtension>::BaseField::ZERO,
<Self as FieldExtension<4>>::BaseField::ZERO,
<Self as FieldExtension<4>>::BaseField::ZERO,
<Self as FieldExtension<4>>::BaseField::ZERO,
])
}
}
impl PartialEq for QuarticCrandallField {
fn eq(&self, other: &Self) -> bool {
self.to_canonical_representation() == other.to_canonical_representation()
FieldExtension::<4>::to_basefield_array(self)
== FieldExtension::<4>::to_basefield_array(other)
}
}
@ -79,7 +54,7 @@ impl Eq for QuarticCrandallField {}
impl Hash for QuarticCrandallField {
fn hash<H: Hasher>(&self, state: &mut H) {
for l in &self.to_canonical_representation() {
for l in &FieldExtension::<4>::to_basefield_array(self) {
Hash::hash(l, state);
}
}
@ -131,12 +106,12 @@ impl Field for QuarticCrandallField {
return None;
}
let a_pow_p = self.frobenius();
let a_pow_p = OEF::<4>::frobenius(self);
let a_pow_p_plus_1 = a_pow_p * *self;
let a_pow_p3_plus_p2 = a_pow_p_plus_1.frobenius().frobenius();
let a_pow_p3_plus_p2 = OEF::<4>::frobenius(&OEF::<4>::frobenius(&a_pow_p_plus_1));
let a_pow_r_minus_1 = a_pow_p3_plus_p2 * a_pow_p;
let a_pow_r = a_pow_r_minus_1 * *self;
debug_assert!(a_pow_r.is_in_basefield());
debug_assert!(FieldExtension::<4>::is_in_basefield(&a_pow_r));
Some(a_pow_r_minus_1 * a_pow_r.0[0].inverse().into())
}
@ -146,20 +121,15 @@ impl Field for QuarticCrandallField {
}
fn from_canonical_u64(n: u64) -> Self {
Self([
<Self as QuarticFieldExtension>::BaseField::from_canonical_u64(n),
<Self as QuarticFieldExtension>::BaseField::ZERO,
<Self as QuarticFieldExtension>::BaseField::ZERO,
<Self as QuarticFieldExtension>::BaseField::ZERO,
])
<Self as FieldExtension<4>>::BaseField::from_canonical_u64(n).into()
}
fn rand_from_rng<R: Rng>(rng: &mut R) -> Self {
Self([
<Self as QuarticFieldExtension>::BaseField::rand_from_rng(rng),
<Self as QuarticFieldExtension>::BaseField::rand_from_rng(rng),
<Self as QuarticFieldExtension>::BaseField::rand_from_rng(rng),
<Self as QuarticFieldExtension>::BaseField::rand_from_rng(rng),
<Self as FieldExtension<4>>::BaseField::rand_from_rng(rng),
<Self as FieldExtension<4>>::BaseField::rand_from_rng(rng),
<Self as FieldExtension<4>>::BaseField::rand_from_rng(rng),
<Self as FieldExtension<4>>::BaseField::rand_from_rng(rng),
])
}
}
@ -244,9 +214,9 @@ impl Mul for QuarticCrandallField {
let Self([a0, a1, a2, a3]) = self;
let Self([b0, b1, b2, b3]) = rhs;
let c0 = a0 * b0 + Self::W * (a1 * b3 + a2 * b2 + a3 * b1);
let c1 = a0 * b1 + a1 * b0 + Self::W * (a2 * b3 + a3 * b2);
let c2 = a0 * b2 + a1 * b1 + a2 * b0 + Self::W * a3 * b3;
let c0 = a0 * b0 + <Self as OEF<4>>::W * (a1 * b3 + a2 * b2 + a3 * b1);
let c1 = a0 * b1 + a1 * b0 + <Self as OEF<4>>::W * (a2 * b3 + a3 * b2);
let c2 = a0 * b2 + a1 * b1 + a2 * b0 + <Self as OEF<4>>::W * a3 * b3;
let c3 = a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0;
Self([c0, c1, c2, c3])
@ -283,7 +253,8 @@ impl DivAssign for QuarticCrandallField {
#[cfg(test)]
mod tests {
use crate::field::extension_field::quartic::{QuarticCrandallField, QuarticFieldExtension};
use crate::field::extension_field::quartic::QuarticCrandallField;
use crate::field::extension_field::{FieldExtension, OEF};
use crate::field::field::Field;
fn exp_naive<F: Field>(x: F, power: u128) -> F {
@ -307,10 +278,7 @@ mod tests {
let z = F::rand();
assert_eq!(x + (-x), F::ZERO);
assert_eq!(-x, F::ZERO - x);
assert_eq!(
x + x,
x * <F as QuarticFieldExtension>::BaseField::TWO.into()
);
assert_eq!(x + x, x * <F as FieldExtension<4>>::BaseField::TWO.into());
assert_eq!(x * (-x), -x.square());
assert_eq!(x + y, y + x);
assert_eq!(x * y, y * x);
@ -339,8 +307,8 @@ mod tests {
type F = QuarticCrandallField;
let x = F::rand();
assert_eq!(
exp_naive(x, <F as QuarticFieldExtension>::BaseField::ORDER as u128),
x.frobenius()
exp_naive(x, <F as FieldExtension<4>>::BaseField::ORDER as u128),
OEF::<4>::frobenius(&x)
);
}
@ -374,8 +342,8 @@ mod tests {
);
assert_eq!(
F::POWER_OF_TWO_GENERATOR
.exp(1 << (F::TWO_ADICITY - <F as QuarticFieldExtension>::BaseField::TWO_ADICITY)),
<F as QuarticFieldExtension>::BaseField::POWER_OF_TWO_GENERATOR.into()
.exp(1 << (F::TWO_ADICITY - <F as FieldExtension<4>>::BaseField::TWO_ADICITY)),
<F as FieldExtension<4>>::BaseField::POWER_OF_TWO_GENERATOR.into()
);
}
}

View File

@ -0,0 +1,88 @@
use crate::circuit_builder::CircuitBuilder;
use crate::field::extension_field::{Extendable, FieldExtension, OEF};
use crate::field::field::Field;
use crate::target::Target;
#[derive(Copy, Clone, Debug)]
pub struct ExtensionTarget<const D: usize>(pub [Target; D]);
impl<const D: usize> ExtensionTarget<D> {
pub fn to_target_array(&self) -> [Target; D] {
self.0
}
}
impl<F: Field> CircuitBuilder<F> {
pub fn zero_extension<const D: usize>(&mut self) -> ExtensionTarget<D>
where
F: Extendable<D>,
{
ExtensionTarget([self.zero(); D])
}
pub fn add_extension<const D: usize>(
&mut self,
mut a: ExtensionTarget<D>,
b: ExtensionTarget<D>,
) -> ExtensionTarget<D>
where
F: Extendable<D>,
{
for i in 0..D {
a.0[i] = self.add(a.0[i], b.0[i]);
}
a
}
pub fn sub_extension<const D: usize>(
&mut self,
mut a: ExtensionTarget<D>,
b: ExtensionTarget<D>,
) -> ExtensionTarget<D>
where
F: Extendable<D>,
{
for i in 0..D {
a.0[i] = self.sub(a.0[i], b.0[i]);
}
a
}
pub fn mul_extension<const D: usize>(
&mut self,
a: ExtensionTarget<D>,
b: ExtensionTarget<D>,
) -> ExtensionTarget<D>
where
F: Extendable<D>,
{
let w = self.constant(F::Extension::W);
let mut res = [self.zero(); D];
for i in 0..D {
for j in 0..D {
res[(i + j) % D] = if i + j < D {
self.mul_add(a.0[i], b.0[j], res[(i + j) % D])
} else {
let tmp = self.mul(a.0[i], b.0[j]);
self.mul_add(w, tmp, res[(i + j) % D])
}
}
}
ExtensionTarget(res)
}
/// Returns a*b where `b` is in the extension field and `a` is in the base field.
pub fn scalar_mul<const D: usize>(
&mut self,
a: Target,
mut b: ExtensionTarget<D>,
) -> ExtensionTarget<D>
where
F: Extendable<D>,
{
for i in 0..D {
b.0[i] = self.mul(a, b.0[i]);
}
b
}
}

View File

@ -1,3 +1,4 @@
pub mod arithmetic;
pub mod hash;
pub mod polynomial;
pub(crate) mod split_join;

35
src/gadgets/polynomial.rs Normal file
View File

@ -0,0 +1,35 @@
use crate::circuit_builder::CircuitBuilder;
use crate::field::extension_field::target::ExtensionTarget;
use crate::field::extension_field::Extendable;
use crate::field::field::Field;
use crate::target::Target;
pub struct PolynomialCoeffsTarget<const D: usize>(pub Vec<ExtensionTarget<D>>);
impl<const D: usize> PolynomialCoeffsTarget<D> {
pub fn eval_scalar<F: Field + Extendable<D>>(
&self,
builder: &mut CircuitBuilder<F>,
point: Target,
) -> ExtensionTarget<D> {
let mut acc = builder.zero_extension();
for &c in self.0.iter().rev() {
let tmp = builder.scalar_mul(point, acc);
acc = builder.add_extension(tmp, c);
}
acc
}
pub fn eval<F: Field + Extendable<D>>(
&self,
builder: &mut CircuitBuilder<F>,
point: ExtensionTarget<D>,
) -> ExtensionTarget<D> {
let mut acc = builder.zero_extension();
for &c in self.0.iter().rev() {
let tmp = builder.mul_extension(point, acc);
acc = builder.add_extension(tmp, c);
}
acc
}
}

View File

@ -6,6 +6,7 @@ use crate::circuit_builder::CircuitBuilder;
use crate::field::extension_field::{Extendable, FieldExtension};
use crate::field::field::Field;
use crate::field::lagrange::interpolant;
use crate::gadgets::polynomial::PolynomialCoeffsTarget;
use crate::gates::gate::{Gate, GateRef};
use crate::generator::{SimpleGenerator, WitnessGenerator};
use crate::polynomial::polynomial::PolynomialCoeffs;
@ -125,7 +126,34 @@ impl<F: Field + Extendable<D>, const D: usize> Gate<F> for InterpolationGate<F,
builder: &mut CircuitBuilder<F>,
vars: EvaluationTargets,
) -> Vec<Target> {
todo!()
let mut constraints = Vec::with_capacity(self.num_constraints());
let coeffs = (0..self.num_points)
.map(|i| vars.get_local_ext(self.wires_coeff(i)))
.collect();
let interpolant = PolynomialCoeffsTarget(coeffs);
for i in 0..self.num_points {
let point = vars.local_wires[self.wire_point(i)];
let value = vars.get_local_ext(self.wires_value(i));
let computed_value = interpolant.eval_scalar(builder, point);
constraints.extend(
&builder
.sub_extension(value, computed_value)
.to_target_array(),
);
}
let evaluation_point = vars.get_local_ext(self.wires_evaluation_point());
let evaluation_value = vars.get_local_ext(self.wires_evaluation_value());
let computed_evaluation_value = interpolant.eval(builder, evaluation_point);
constraints.extend(
&builder
.sub_extension(evaluation_value, computed_evaluation_value)
.to_target_array(),
);
constraints
}
fn generators(

View File

@ -1,6 +1,7 @@
use std::convert::TryInto;
use std::ops::Range;
use crate::field::extension_field::target::ExtensionTarget;
use crate::field::extension_field::{Extendable, FieldExtension};
use crate::field::field::Field;
use crate::target::Target;
@ -27,3 +28,11 @@ pub struct EvaluationTargets<'a> {
pub(crate) local_constants: &'a [Target],
pub(crate) local_wires: &'a [Target],
}
impl<'a> EvaluationTargets<'a> {
pub fn get_local_ext<const D: usize>(&self, wire_range: Range<usize>) -> ExtensionTarget<D> {
debug_assert_eq!(wire_range.len(), D);
let arr = self.local_wires[wire_range].try_into().unwrap();
ExtensionTarget(arr)
}
}