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