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