binius_math/
multilinear.rs

1// Copyright 2023-2025 Irreducible Inc.
2
3use std::fmt::Debug;
4
5use binius_field::PackedField;
6use either::Either;
7
8use crate::{Error, MultilinearExtension, MultilinearQueryRef};
9
10/// Represents a multilinear polynomial.
11///
12/// This interface includes no generic methods, in order to support the creation of trait objects.
13#[auto_impl::auto_impl(&, &mut, Box, Rc, Arc)]
14pub trait MultilinearPoly<P: PackedField>: Debug {
15	/// Number of variables.
16	fn n_vars(&self) -> usize;
17
18	/// The number of coefficients required to specify the polynomial.
19	fn size(&self) -> usize {
20		1 << self.n_vars()
21	}
22
23	/// Binary logarithm of the extension degree (always exists because we only support power-of-two extension degrees)
24	fn log_extension_degree(&self) -> usize;
25
26	/// Get the evaluations of the polynomial at a vertex of the hypercube.
27	///
28	/// # Arguments
29	///
30	/// * `index` - The index of the point, in lexicographic order
31	fn evaluate_on_hypercube(&self, index: usize) -> Result<P::Scalar, Error>;
32
33	/// Get the evaluations of the polynomial at a vertex of the hypercube and scale the value.
34	///
35	/// This can be more efficient than calling `evaluate_on_hypercube` followed by a
36	/// multiplication when the trait implementation can use a subfield multiplication.
37	///
38	/// # Arguments
39	///
40	/// * `index` - The index of the point, in lexicographic order
41	/// * `scalar` - The scaling coefficient
42	fn evaluate_on_hypercube_and_scale(
43		&self,
44		index: usize,
45		scalar: P::Scalar,
46	) -> Result<P::Scalar, Error>;
47
48	fn evaluate(&self, query: MultilinearQueryRef<P>) -> Result<P::Scalar, Error>;
49
50	fn evaluate_partial_low(
51		&self,
52		query: MultilinearQueryRef<P>,
53	) -> Result<MultilinearExtension<P>, Error>;
54
55	fn evaluate_partial_high(
56		&self,
57		query: MultilinearQueryRef<P>,
58	) -> Result<MultilinearExtension<P>, Error>;
59
60	fn evaluate_partial(
61		&self,
62		query: MultilinearQueryRef<P>,
63		start_index: usize,
64	) -> Result<MultilinearExtension<P>, Error>;
65
66	/// Get a subcube of the boolean hypercube after `evaluate_partial_low`.
67	///
68	/// Equivalent computation is `evaluate_partial_low(query)` followed by a `subcube_evals`
69	/// on a result. This method is more efficient due to handling it as a special case.
70	fn subcube_partial_low_evals(
71		&self,
72		query: MultilinearQueryRef<P>,
73		subcube_vars: usize,
74		subcube_index: usize,
75		partial_low_evals: &mut [P],
76	) -> Result<(), Error>;
77
78	/// Get a subcube of the boolean hypercube after `evaluate_partial_high`.
79	///
80	/// Equivalent computation is `evaluate_partial_high(query)` followed by a `subcube_evals`
81	/// on a result. This method is more efficient due to handling it as a special case.
82	fn subcube_partial_high_evals(
83		&self,
84		query: MultilinearQueryRef<P>,
85		subcube_vars: usize,
86		subcube_index: usize,
87		partial_high_evals: &mut [P],
88	) -> Result<(), Error>;
89
90	/// Get a subcube of the boolean hypercube of a given size.
91	///
92	/// Subcube of a multilinear is a set of evaluations $M(\beta_i\Vert x_j)$ , where
93	/// $\beta_i \in \mathcal{B}_k$ iterates over `subcube_vars`-sized hypercube and $x_j$ is a binary
94	/// representation of the `subcube_index`.
95	///
96	/// The result slice `evals` holds subcube evaluations in lexicographic order of $\beta_i$, with the
97	/// fastest stride corresponding to the first variable. Each scalar of the packed field `P` is assumed
98	/// to be a `2^log_embedding_degree` extension field, where subcube evaluations are assigned to bases
99	/// in lexicographic order of the lowest `log_embedding_degree` variables.
100	///
101	/// Note that too large `log_embedding_degree` values may cause this method to fail.
102	fn subcube_evals(
103		&self,
104		subcube_vars: usize,
105		subcube_index: usize,
106		log_embedding_degree: usize,
107		evals: &mut [P],
108	) -> Result<(), Error>;
109
110	/// Returns the hypercube evaluations, embedded into packed extension field elements, if the
111	/// data is already available.
112	///
113	/// This method is primarily used to access the raw evaluation data underlying a
114	/// [`MultilinearExtension`] that is type-erased as a [`MultilinearPoly`] trait object. The
115	/// evaluation data is useful for cases where the caller needs to dynamically re-interpret it
116	/// as subfield coefficients while avoiding copying, like for the small-field polynomial
117	/// commitment scheme or to provide directly to a hardware accelerator.
118	///
119	/// If the data is not available, this method returns `None`. If the data is available, it
120	/// should be interpreted not actually as a list of evaluations points given by iterating the
121	/// packed slice, but rather by iterating coefficients from a subfield with an embedding degree
122	/// given by [`Self::log_extension_degree`].
123	///
124	/// The data returned, if `Some`, should be the same as the data that is written by
125	/// [`Self::subcube_evals`].
126	fn packed_evals(&self) -> Option<&[P]>;
127}
128
129impl<P, L, R> MultilinearPoly<P> for Either<L, R>
130where
131	P: PackedField,
132	L: MultilinearPoly<P>,
133	R: MultilinearPoly<P>,
134{
135	fn n_vars(&self) -> usize {
136		either::for_both!(self, inner => inner.n_vars())
137	}
138
139	fn size(&self) -> usize {
140		either::for_both!(self, inner => inner.size())
141	}
142
143	fn log_extension_degree(&self) -> usize {
144		either::for_both!(self, inner => inner.log_extension_degree())
145	}
146
147	fn evaluate_on_hypercube(&self, index: usize) -> Result<P::Scalar, Error> {
148		either::for_both!(self, inner => inner.evaluate_on_hypercube(index))
149	}
150
151	fn evaluate_on_hypercube_and_scale(
152		&self,
153		index: usize,
154		scalar: P::Scalar,
155	) -> Result<P::Scalar, Error> {
156		either::for_both!(self, inner => inner.evaluate_on_hypercube_and_scale(index, scalar))
157	}
158
159	fn evaluate(&self, query: MultilinearQueryRef<P>) -> Result<P::Scalar, Error> {
160		either::for_both!(self, inner => inner.evaluate(query))
161	}
162
163	fn evaluate_partial(
164		&self,
165		query: MultilinearQueryRef<P>,
166		start_index: usize,
167	) -> Result<MultilinearExtension<P>, Error> {
168		either::for_both!(self, inner => inner.evaluate_partial(query, start_index))
169	}
170
171	fn evaluate_partial_low(
172		&self,
173		query: MultilinearQueryRef<P>,
174	) -> Result<MultilinearExtension<P>, Error> {
175		either::for_both!(self, inner => inner.evaluate_partial_low(query))
176	}
177
178	fn evaluate_partial_high(
179		&self,
180		query: MultilinearQueryRef<P>,
181	) -> Result<MultilinearExtension<P>, Error> {
182		either::for_both!(self, inner => inner.evaluate_partial_high(query))
183	}
184
185	fn subcube_partial_low_evals(
186		&self,
187		query: MultilinearQueryRef<P>,
188		subcube_vars: usize,
189		subcube_index: usize,
190		partial_low_evals: &mut [P],
191	) -> Result<(), Error> {
192		either::for_both!(
193			self,
194			inner => {
195				inner.subcube_partial_low_evals(query, subcube_vars, subcube_index, partial_low_evals)
196			}
197		)
198	}
199
200	fn subcube_partial_high_evals(
201		&self,
202		query: MultilinearQueryRef<P>,
203		subcube_vars: usize,
204		subcube_index: usize,
205		partial_high_evals: &mut [P],
206	) -> Result<(), Error> {
207		either::for_both!(
208			self,
209			inner => {
210				inner.subcube_partial_high_evals(query, subcube_vars, subcube_index, partial_high_evals)
211			}
212		)
213	}
214
215	fn subcube_evals(
216		&self,
217		subcube_vars: usize,
218		subcube_index: usize,
219		log_embedding_degree: usize,
220		evals: &mut [P],
221	) -> Result<(), Error> {
222		either::for_both!(
223			self,
224			inner => inner.subcube_evals(subcube_vars, subcube_index, log_embedding_degree, evals)
225		)
226	}
227
228	fn packed_evals(&self) -> Option<&[P]> {
229		either::for_both!(self, inner => inner.packed_evals())
230	}
231}