feanor_math/reduce_lift/poly_eval.rs
1
2use std::fmt::Debug;
3
4use crate::algorithms::linsolve::LinSolveRing;
5use crate::algorithms::resultant::ComputeResultantRing;
6use crate::divisibility::{DivisibilityRing, Domain};
7use crate::homomorphism::*;
8use crate::pid::PrincipalIdealRing;
9use crate::ring::*;
10use crate::field::*;
11use crate::rings::finite::FiniteRing;
12use crate::specialization::FiniteRingSpecializable;
13
14///
15/// Trait for rings that can be temporarily replaced by an extension when we need more points,
16/// e.g. for interpolation.
17///
18/// Note that a trivial implementation is possible for every ring of characteristic 0, since
19/// these already have infinitely many points whose pairwise differences are non-zero-divisors.
20/// Such an implementation can be added to new types using the macro [`impl_interpolation_base_ring_char_zero!`].
21///
22#[stability::unstable(feature = "enable")]
23pub trait InterpolationBaseRing: DivisibilityRing {
24
25 ///
26 /// The type of the extension ring we can switch to to get more points.
27 ///
28 /// For the reason why there are so many quite specific trait bounds here:
29 /// See the doc of [`EvalPolyLocallyRing::LocalRingBase`].
30 ///
31 type ExtendedRingBase<'a>: ?Sized + PrincipalIdealRing + Domain + LinSolveRing + ComputeResultantRing + Debug
32 where Self: 'a;
33
34 type ExtendedRing<'a>: RingStore<Type = Self::ExtendedRingBase<'a>> + Clone
35 where Self: 'a;
36
37 fn in_base<'a, S>(&self, ext_ring: S, el: El<S>) -> Option<Self::Element>
38 where Self: 'a, S: RingStore<Type = Self::ExtendedRingBase<'a>>;
39
40 fn in_extension<'a, S>(&self, ext_ring: S, el: Self::Element) -> El<S>
41 where Self: 'a, S: RingStore<Type = Self::ExtendedRingBase<'a>>;
42
43 ///
44 /// Returns `count` points such that the difference between any two of them
45 /// is a non-zero-divisor.
46 ///
47 /// Any two calls must give elements in the same order.
48 ///
49 fn interpolation_points<'a>(&'a self, count: usize) -> (Self::ExtendedRing<'a>, Vec<El<Self::ExtendedRing<'a>>>);
50}
51
52///
53/// [`RingStore`] for [`InterpolationBaseRing`].
54///
55#[stability::unstable(feature = "enable")]
56pub trait InterpolationBaseRingStore: RingStore
57 where Self::Type: InterpolationBaseRing
58{}
59
60impl<R> InterpolationBaseRingStore for R
61 where R: RingStore, R::Type: InterpolationBaseRing
62{}
63
64///
65/// The inclusion map `R -> S` for a ring `R` and one of its extensions `S`
66/// as given by [`InterpolationBaseRing`].
67///
68#[stability::unstable(feature = "enable")]
69pub struct ToExtRingMap<'a, R>
70 where R: ?Sized + InterpolationBaseRing
71{
72 ring: RingRef<'a, R>,
73 ext_ring: R::ExtendedRing<'a>
74}
75
76impl<'a, R> ToExtRingMap<'a, R>
77 where R: ?Sized + InterpolationBaseRing
78{
79 #[stability::unstable(feature = "enable")]
80 pub fn for_interpolation(ring: &'a R, point_count: usize) -> (Self, Vec<El<R::ExtendedRing<'a>>>) {
81 let (ext_ring, points) = ring.interpolation_points(point_count);
82 return (Self { ring: RingRef::new(ring), ext_ring: ext_ring }, points);
83 }
84
85 #[stability::unstable(feature = "enable")]
86 pub fn as_base_ring_el(&self, el: El<R::ExtendedRing<'a>>) -> R::Element {
87 self.ring.get_ring().in_base(&self.ext_ring, el).unwrap()
88 }
89}
90
91impl<'a, R> Homomorphism<R, R::ExtendedRingBase<'a>> for ToExtRingMap<'a, R>
92 where R: ?Sized + InterpolationBaseRing
93{
94 type CodomainStore = R::ExtendedRing<'a>;
95 type DomainStore = RingRef<'a, R>;
96
97 fn codomain<'b>(&'b self) -> &'b Self::CodomainStore {
98 &self.ext_ring
99 }
100
101 fn domain<'b>(&'b self) -> &'b Self::DomainStore {
102 &self.ring
103 }
104
105 fn map(&self, x: <R as RingBase>::Element) -> <R::ExtendedRingBase<'a> as RingBase>::Element {
106 self.ring.get_ring().in_extension(&self.ext_ring, x)
107 }
108}
109
110///
111/// Trait for rings that support performing computations locally.
112///
113/// Note that here (and in `feanor-math` generally), the term "local" is used to refer to algorithms
114/// that work modulo prime ideals (or their powers), which is different from the mathematical concept
115/// of localization.
116///
117/// More concretely, a ring `R` implementing this trait should be endowed with a "pseudo norm"
118/// ```text
119/// |.|: R -> [0, ∞)
120/// ```
121/// i.e. a symmetric, sub-additive, sub-multiplicative map.
122/// Furthermore, for any bound `b`, the ring should be able to provide prime ideals
123/// `p1, ..., pk` together with the rings `Ri = R / pi`, such that the restricted
124/// reduction map
125/// ```text
126/// { x in R | |x| <= b } -> R1 x ... x Rk
127/// ```
128/// is injective.
129/// This means that a computation can be performed in the simpler ring `R1 x ... x Rk`,
130/// and - assuming the result is of pseudo-norm `<= b`, mapped back to `R`.
131///
132/// The standard use case is the evaluation of a multivariate polynomial `f(X1, ..., Xm)`
133/// over this ring. The trait is designed to enable the following approach:
134/// - Given ring elements `a1, ..., am`, compute an upper bound `B` on `|f(a1, ..., am)|`.
135/// The values `|ai|` are given by [`EvalPolyLocallyRing::pseudo_norm()`].
136/// - Get a sufficient number of prime ideals, using [`EvalPolyLocallyRing::local_computation()`]
137/// - Compute `f(a1 mod pi, ..., am mod pi) mod pi` for each prime `pi` within the ring given by
138/// [`EvalPolyLocallyRing::local_ring_at()`]. The reductions `ai mod pj` are given by
139/// [`EvalPolyLocallyRing::reduce()`].
140/// - Recombine the results to an element of `R` by using [`EvalPolyLocallyRing::lift_combine()`].
141///
142/// # Relationship with [`crate::reduce_lift::poly_factor_gcd::PolyGCDLocallyDomain`]
143///
144/// There are generally two ways of computing something via a reduce-modulo-primes-then-lift
145/// approach. Either one can take many different prime ideals, or one can take a large power
146/// of a single/a small amount of prime ideals.
147///
148/// This trait is for the former approach, which is especially suitable when the computation to
149/// perform can be written as a polynomial evaluation. In particular, this applicable to determinants,
150/// resultant, and (with some caveats) solving linear systems.
151///
152/// On the other hand, when factoring polynomials or computing their gcds, it is common to instead
153/// rely on Hensel lifting to compute the result modulo a large power of a single prime, or very
154/// few primes. This approach is formalized by [`crate::reduce_lift::poly_factor_gcd::PolyGCDLocallyDomain`].
155///
156/// # Type-level recursion in feanor-math
157///
158/// [`EvalPolyLocallyRing`] and [`PolyGCDLocallyDomain`] are the two traits in feanor-math which
159/// use type-level recursion with blanket implementations. The idea is simple: If our ring is an
160/// [`EvalPolyLocallyRing`], whose quotients are again [`EvalPolyLocallyRing`], whose quotients are
161/// again [`EvalPolyLocallyRing`] and so on, ending with a finite field. Then, for all kinds of
162/// operations which are actually just polynomial evaluations (resultants, determinants, unique-solution
163/// linear systems, ...) we already have all the information for an efficient algorithm. However,
164/// providing this through blanket implementations is not that simple. We now explain how it is
165/// done in feanor-math.
166///
167/// The proper solution:
168/// ```compile_fail,E0391
169/// #![feature(min_specialization)]
170/// // Some special property, in practice this might be LinSolveRing, ComputeResultantRing, FactorPolyRing, ...
171/// pub trait SpecialProperty {
172///
173/// fn foo<'a>(&'a self);
174/// }
175///
176/// impl<T> SpecialProperty for T
177/// where T: Recursive,
178/// for<'a> T::PrevCase<'a>: SpecialProperty
179/// {
180/// default fn foo<'a>(&'a self) {
181/// println!("recursion");
182/// <T::PrevCase<'a> as SpecialProperty>::foo(&self.map_down());
183/// }
184/// }
185///
186/// // Newtype to allow `&'a T: SpecialProperty` without conflicting with above blanket impl
187/// pub struct RefWrapper<'a, T>(&'a T);
188///
189/// impl<'a, T: Recursive> SpecialProperty for RefWrapper<'a, T> {
190///
191/// fn foo<'b>(&'b self) {
192/// self.0.foo()
193/// }
194/// }
195///
196/// // The main recursion type
197/// pub trait Recursive {
198///
199/// type PrevCase<'a> where Self: 'a;
200///
201/// fn map_down<'a>(&'a self) -> Self::PrevCase<'a>;
202/// }
203///
204/// #[derive(Clone, Copy)]
205/// pub struct BaseCase;
206///
207/// impl Recursive for BaseCase {
208///
209/// type PrevCase<'a> = Self where Self: 'a;
210///
211/// fn map_down<'a>(&'a self) -> Self::PrevCase<'a> { *self }
212/// }
213///
214/// impl SpecialProperty for BaseCase {
215///
216/// fn foo<'a>(&'a self) {
217/// println!("BaseCaseSpecial")
218/// }
219/// }
220///
221/// pub struct RecursiveCase<T: Recursive>(T);
222///
223/// impl<T: Recursive> Recursive for RecursiveCase<T> {
224///
225/// type PrevCase<'a> = RefWrapper<'a, T> where Self: 'a;
226///
227/// fn map_down<'a>(&'a self) -> Self::PrevCase<'a> {
228/// RefWrapper(&self.0)
229/// }
230/// }
231///
232/// fn run_type_recursion<'b, T>(t: &'b T)
233/// where T: 'b + Recursive,
234/// for<'a> T::PrevCase<'a>: SpecialProperty
235/// {
236/// t.foo()
237/// }
238///
239/// run_type_recursion(&BaseCase);
240/// run_type_recursion(&RecursiveCase(BaseCase));
241/// ```
242/// Unfortunately, this currently fails with an error
243/// ```text
244/// error[E0391]: cycle detected when computing whether impls specialize one another
245/// --> src/main.rs:9:1
246/// |
247/// 9 | / impl<T> SpecialProperty for T
248/// 10 | | where T: Recursive,
249/// 11 | | for<'a> T::PrevCase<'a>: SpecialProperty
250/// | |________________________________________________^
251/// |
252/// = note: ...which requires evaluating trait selection obligation `BaseCase: SpecialProperty`...
253/// = note: ...which again requires computing whether impls specialize one another, completing the cycle
254/// ```
255/// The only solution I found is to put the constraint by `SpecialProperty` directly into
256/// the declaration `type PrevCase<'a>: SpecialProperty where Self: 'a`. This works, but
257/// unfortunately makes things much less generic than we would want to. In particular,
258/// If we have a `NonSpecialBaseCase` in addition to `BaseCase`, and we want it to not
259/// implement `SpecialProperty`, then we cannot use `RecursiveCase` as well. On the other hand,
260/// if the compiler would accept the first variant, this would work out fine - the only effect
261/// of `NonSpecialBaseCase: !SpecialProperty` would then be that also
262/// `RecursiveCase<NonSpecialBaseCase>: !SpecialProperty`, which of course makes sense.
263///
264/// However, for now we stay with this workaround, which then looks as follows:
265/// ```
266/// #![feature(min_specialization)]
267/// // Some special property, in practice this might be LinSolveRing, ComputeResultantRing, FactorPolyRing, ...
268/// pub trait SpecialProperty {
269///
270/// fn foo<'a>(&'a self);
271/// }
272///
273/// impl<T> SpecialProperty for T
274/// where T: Recursive
275/// {
276/// default fn foo<'a>(&'a self) {
277/// println!("recursion");
278/// <T::PrevCase<'a> as SpecialProperty>::foo(&self.map_down());
279/// }
280/// }
281///
282/// // Newtype to allow `&'a T: SpecialProperty` without conflicting with above blanket impl
283/// pub struct RefWrapper<'a, T: SpecialProperty>(&'a T);
284///
285/// impl<'a, T: Recursive> SpecialProperty for RefWrapper<'a, T> {
286///
287/// fn foo<'b>(&'b self) {
288/// self.0.foo()
289/// }
290/// }
291///
292/// // The main recursion type
293/// pub trait Recursive {
294///
295/// type PrevCase<'a>: SpecialProperty where Self: 'a;
296///
297/// fn map_down<'a>(&'a self) -> Self::PrevCase<'a>;
298/// }
299///
300/// #[derive(Clone, Copy)]
301/// pub struct BaseCase;
302///
303/// impl Recursive for BaseCase {
304///
305/// type PrevCase<'a> = Self where Self: 'a;
306///
307/// fn map_down<'a>(&'a self) -> Self::PrevCase<'a> { *self }
308/// }
309///
310/// impl SpecialProperty for BaseCase {
311///
312/// fn foo<'a>(&'a self) {
313/// println!("BaseCaseSpecial")
314/// }
315/// }
316///
317/// pub struct RecursiveCase<T: Recursive + SpecialProperty>(T);
318///
319/// impl<T: Recursive> Recursive for RecursiveCase<T> {
320///
321/// type PrevCase<'a> = RefWrapper<'a, T> where Self: 'a;
322///
323/// fn map_down<'a>(&'a self) -> Self::PrevCase<'a> {
324/// RefWrapper(&self.0)
325/// }
326/// }
327///
328/// fn run_type_recursion<'b, T>(t: &'b T)
329/// where T: 'b + Recursive
330/// {
331/// t.foo()
332/// }
333///
334/// run_type_recursion(&BaseCase);
335/// run_type_recursion(&RecursiveCase(BaseCase));
336/// ```
337///
338/// Another advantage is that every function
339///
340#[stability::unstable(feature = "enable")]
341pub trait EvalPolyLocallyRing: RingBase + FiniteRingSpecializable {
342
343 ///
344 /// The type of the ring we get once quotienting by a prime ideal.
345 ///
346 /// The proper solution would be to just require `LocalRingBase<'ring>: ?Sized + RingBase`.
347 /// Algorithms which require this type to provide additional functionality (like being
348 /// a [`ComputeResultantRing`]) should then add a constraint
349 /// ```ignore
350 /// for<'ring> R::LocalRingBase<'ring>: ComputeResultantRing
351 /// ```
352 /// However, this causes problems, more concretely an error
353 /// "cycle detected when computing whether impls specialize one another".
354 /// More on that in the trait-level doc.
355 ///
356 /// Hence, we have to already bound `LocalRingBase` by all the traits that are reasonably
357 /// required by some algorithms. This clearly makes it a lot less generic than it should be,
358 /// but so far it worked out without too much trouble.
359 ///
360 type LocalRingBase<'ring>: ?Sized + PrincipalIdealRing + Domain + LinSolveRing + ComputeResultantRing + Debug
361 where Self: 'ring;
362
363 type LocalRing<'ring>: RingStore<Type = Self::LocalRingBase<'ring>>
364 where Self: 'ring;
365
366 ///
367 /// A collection of prime ideals of the ring, and additionally any data required to reconstruct
368 /// a small ring element from its projections onto each prime ideal.
369 ///
370 type LocalComputationData<'ring>
371 where Self: 'ring;
372
373 ///
374 /// Computes (an upper bound of) the natural logarithm of the pseudo norm of a ring element.
375 ///
376 /// The pseudo norm should be
377 /// - symmetric, i.e. `|-x| = |x|`,
378 /// - sub-additive, i.e. `|x + y| <= |x| + |y|`
379 /// - sub-multiplicative, i.e. `|xy| <= |x| |y|`
380 ///
381 /// and this function should give `ln|x|`
382 ///
383 fn ln_pseudo_norm(&self, el: &Self::Element) -> f64;
384
385 ///
386 /// Sets up the context for a new polynomial evaluation, whose output
387 /// should have pseudo norm less than the given bound.
388 ///
389 fn local_computation<'ring>(&'ring self, ln_pseudo_norm_bound: f64) -> Self::LocalComputationData<'ring>;
390
391 ///
392 /// Returns the number `k` of local rings that are required
393 /// to get the correct result of the given computation.
394 ///
395 fn local_ring_count<'ring>(&self, computation: &Self::LocalComputationData<'ring>) -> usize
396 where Self: 'ring;
397
398 ///
399 /// Returns the `i`-th local ring belonging to the given computation.
400 ///
401 fn local_ring_at<'ring>(&self, computation: &Self::LocalComputationData<'ring>, i: usize) -> Self::LocalRing<'ring>
402 where Self: 'ring;
403
404 ///
405 /// Computes the map `R -> R1 x ... x Rk`, i.e. maps the given element into each of
406 /// the local rings.
407 ///
408 fn reduce<'ring>(&self, computation: &Self::LocalComputationData<'ring>, el: &Self::Element) -> Vec<<Self::LocalRingBase<'ring> as RingBase>::Element>
409 where Self: 'ring;
410
411 ///
412 /// Computes a preimage under the map `R -> R1 x ... x Rk`, i.e. a ring element `x` that reduces
413 /// to each of the given local rings under the map [`EvalPolyLocallyRing::reduce()`].
414 ///
415 /// The result should have pseudo-norm bounded by the bound given when the computation
416 /// was initialized, via [`EvalPolyLocallyRing::local_computation()`].
417 ///
418 fn lift_combine<'ring>(&self, computation: &Self::LocalComputationData<'ring>, el: &[<Self::LocalRingBase<'ring> as RingBase>::Element]) -> Self::Element
419 where Self: 'ring;
420}
421
422impl<R> EvalPolyLocallyRing for R
423 where R: ?Sized + FiniteRing + Field + Debug + SelfIso
424{
425 type LocalComputationData<'ring> = RingRef<'ring, Self>
426 where Self: 'ring;
427
428 type LocalRing<'ring> = RingRef<'ring, Self>
429 where Self: 'ring;
430
431 type LocalRingBase<'ring> = Self
432 where Self: 'ring;
433
434 fn ln_pseudo_norm(&self, _el: &Self::Element) -> f64 {
435 0.
436 }
437
438 fn local_computation<'ring>(&'ring self, _ln_pseudo_norm_bound: f64) -> Self::LocalComputationData<'ring> {
439 RingRef::new(self)
440 }
441
442 fn local_ring_at<'ring>(&self, computation: &Self::LocalComputationData<'ring>, _i: usize) -> Self::LocalRing<'ring>
443 where Self: 'ring
444 {
445 *computation
446 }
447
448 fn local_ring_count<'ring>(&self, _computation: &Self::LocalComputationData<'ring>) -> usize
449 where Self: 'ring
450 {
451 1
452 }
453
454 fn reduce<'ring>(&self, _computation: &Self::LocalComputationData<'ring>, el: &Self::Element) -> Vec<<Self::LocalRingBase<'ring> as RingBase>::Element>
455 where Self: 'ring
456 {
457 vec![self.clone_el(el)]
458 }
459
460 fn lift_combine<'ring>(&self, _computation: &Self::LocalComputationData<'ring>, el: &[<Self::LocalRingBase<'ring> as RingBase>::Element]) -> Self::Element
461 where Self: 'ring
462 {
463 assert_eq!(1, el.len());
464 return self.clone_el(&el[0]);
465 }
466}
467
468///
469/// The map `R -> R/p` for a ring `R` and one of its local quotients `R/p` as
470/// given by [`EvalPolyLocallyRing`].
471///
472#[stability::unstable(feature = "enable")]
473pub struct EvaluatePolyLocallyReductionMap<'ring, 'data, R>
474 where R: 'ring + ?Sized + EvalPolyLocallyRing, 'ring: 'data
475{
476 ring: RingRef<'data, R>,
477 data: &'data R::LocalComputationData<'ring>,
478 local_ring: R::LocalRing<'ring>,
479 index: usize
480}
481
482impl<'ring, 'data, R> EvaluatePolyLocallyReductionMap<'ring, 'data, R>
483 where R: 'ring +?Sized + EvalPolyLocallyRing, 'ring: 'data
484{
485 #[stability::unstable(feature = "enable")]
486 pub fn new(ring: &'data R, data: &'data R::LocalComputationData<'ring>, index: usize) -> Self {
487 Self { ring: RingRef::new(ring), data: data, local_ring: ring.local_ring_at(data, index), index: index }
488 }
489}
490
491impl<'ring, 'data, R> Homomorphism<R, R::LocalRingBase<'ring>> for EvaluatePolyLocallyReductionMap<'ring, 'data, R>
492 where R: 'ring +?Sized + EvalPolyLocallyRing, 'ring: 'data
493{
494 type CodomainStore = R::LocalRing<'ring>;
495 type DomainStore = RingRef<'data, R>;
496
497 fn codomain<'b>(&'b self) -> &'b Self::CodomainStore {
498 &self.local_ring
499 }
500
501 fn domain<'b>(&'b self) -> &'b Self::DomainStore {
502 &self.ring
503 }
504
505 fn map(&self, x: <R as RingBase>::Element) -> <R::LocalRingBase<'ring> as RingBase>::Element {
506 let ring_ref: &'data R = self.ring.into();
507 let mut reductions: Vec<<R::LocalRingBase<'ring> as RingBase>::Element> = ring_ref.reduce(self.data, &x);
508 return reductions.swap_remove(self.index);
509 }
510}
511
512///
513/// Generates an implementation of [`crate::reduce_lift::poly_eval::InterpolationBaseRing`]
514/// for a ring of characteristic zero. Not that in this case, the only sensible implementation
515/// is trivial, since the ring itself has enough elements for any interpolation task.
516///
517/// # Example
518/// ```rust
519/// # use std::fmt::Debug;
520/// # use feanor_math::ring::*;
521/// # use feanor_math::delegate::*;
522/// # use feanor_math::reduce_lift::poly_eval::*;
523/// # use feanor_math::primitive_int::*;
524/// # use feanor_math::algorithms::resultant::*;
525/// # use feanor_math::divisibility::*;
526/// # use feanor_math::homomorphism::Homomorphism;
527/// # use feanor_math::rings::poly::*;
528/// # use feanor_math::rings::poly::dense_poly::DensePolyRing;
529/// # use feanor_math::pid::*;
530/// # use feanor_math::impl_interpolation_base_ring_char_zero;
531/// // we wrap a `RingBase` here for simplicity, but in practice a wrapper should
532/// // always store a `RingStore` instead
533/// #[derive(PartialEq, Debug)]
534/// struct MyRingWrapper<R: RingBase>(R);
535/// impl<R: RingBase> DelegateRing for MyRingWrapper<R> {
536/// type Element = R::Element;
537/// type Base = R;
538/// fn get_delegate(&self) -> &Self::Base { &self.0 }
539/// fn delegate(&self, x: R::Element) -> R::Element { x }
540/// fn rev_delegate(&self, x: R::Element) -> R::Element { x }
541/// fn delegate_ref<'a>(&self, x: &'a R::Element) -> &'a R::Element { x }
542/// fn delegate_mut<'a>(&self, x: &'a mut R::Element) -> &'a mut R::Element { x }
543/// }
544/// impl<R: RingBase> DelegateRingImplEuclideanRing for MyRingWrapper<R> {}
545/// impl<R: RingBase> DelegateRingImplFiniteRing for MyRingWrapper<R> {}
546/// impl<R: Domain> Domain for MyRingWrapper<R> {}
547/// impl_interpolation_base_ring_char_zero!{ <{ R }> InterpolationBaseRing for MyRingWrapper<R> where R: PrincipalIdealRing + Domain + ComputeResultantRing + Debug }
548///
549/// // now we can use `InterpolationBaseRing`-functionality
550/// let ring = MyRingWrapper(StaticRing::<i64>::RING.into());
551/// let (embedding, points) = ToExtRingMap::for_interpolation(&ring, 3);
552/// assert_eq!(0, points[0]);
553/// assert_eq!(1, points[1]);
554/// assert_eq!(2, points[2]);
555///
556/// // There is a problem here, described in EvalPolyLocallyRing::LocalRingBase.
557/// // Short version: we need to manually impl ComputeResultantRing
558/// impl<R: ComputeResultantRing> ComputeResultantRing for MyRingWrapper<R> {
559/// fn resultant<P>(poly_ring: P, f: El<P>, g: El<P>) -> Self::Element
560/// where P: RingStore + Copy,
561/// P::Type: PolyRing,
562/// <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>
563/// {
564/// let new_poly_ring = DensePolyRing::new(RingRef::new(poly_ring.base_ring().get_ring().get_delegate()), "X");
565/// let hom = new_poly_ring.lifted_hom(&poly_ring, UnwrapHom::new(poly_ring.base_ring().get_ring()));
566/// poly_ring.base_ring().get_ring().rev_delegate(R::resultant(&new_poly_ring, hom.map(f), hom.map(g)))
567/// }
568/// }
569/// ```
570///
571#[macro_export]
572macro_rules! impl_interpolation_base_ring_char_zero {
573 (<{$($gen_args:tt)*}> InterpolationBaseRing for $self_type:ty where $($constraints:tt)*) => {
574 impl<$($gen_args)*> $crate::reduce_lift::poly_eval::InterpolationBaseRing for $self_type where $($constraints)* {
575
576 type ExtendedRing<'a> = RingRef<'a, Self>
577 where Self: 'a;
578
579 type ExtendedRingBase<'a> = Self
580 where Self: 'a;
581
582 fn in_base<'a, S>(&self, _ext_ring: S, el: El<S>) -> Option<Self::Element>
583 where Self: 'a, S: RingStore<Type = Self::ExtendedRingBase<'a>>
584 {
585 Some(el)
586 }
587
588 fn in_extension<'a, S>(&self, _ext_ring: S, el: Self::Element) -> El<S>
589 where Self: 'a, S: RingStore<Type = Self::ExtendedRingBase<'a>>
590 {
591 el
592 }
593
594 fn interpolation_points<'a>(&'a self, count: usize) -> (Self::ExtendedRing<'a>, Vec<El<Self::ExtendedRing<'a>>>) {
595 let ZZbig = $crate::integer::BigIntRing::RING;
596 assert!(ZZbig.is_zero(&self.characteristic(&ZZbig).unwrap()));
597 let ring = $crate::ring::RingRef::new(self);
598 (ring, (0..count).map(|n| <_ as $crate::homomorphism::Homomorphism<_, _>>::map(&ring.int_hom(), n.try_into().unwrap())).collect())
599 }
600 }
601 };
602 (InterpolationBaseRing for $self_type:ty) => {
603 impl_interpolation_base_ring_char_zero!{ <{}> InterpolationBaseRing for $self_type where }
604 }
605}
606
607///
608/// Implements [`EvalPolyLocallyRing`] for an integer ring.
609///
610/// This uses a default implementation, where the prime ideals are given by the largest prime numbers
611/// such that the corresponding residue field can be implemented using [`crate::rings::zn::zn_64::Zn`].
612/// This should be suitable in almost all scenarios.
613///
614/// The syntax is the same as for other impl-macros, see e.g. [`crate::impl_interpolation_base_ring_char_zero!`].
615///
616#[macro_export]
617macro_rules! impl_eval_poly_locally_for_ZZ {
618 (EvalPolyLocallyRing for $int_ring_type:ty) => {
619 impl_eval_poly_locally_for_ZZ!{ <{}> EvalPolyLocallyRing for $int_ring_type where }
620 };
621 (<{$($gen_args:tt)*}> EvalPolyLocallyRing for $int_ring_type:ty where $($constraints:tt)*) => {
622
623 impl<$($gen_args)*> $crate::reduce_lift::poly_eval::EvalPolyLocallyRing for $int_ring_type
624 where $($constraints)*
625 {
626 type LocalComputationData<'ring> = $crate::rings::zn::zn_rns::Zn<$crate::rings::field::AsField<$crate::rings::zn::zn_64::Zn>, RingRef<'ring, Self>>
627 where Self: 'ring;
628
629 type LocalRing<'ring> = $crate::rings::field::AsField<$crate::rings::zn::zn_64::Zn>
630 where Self: 'ring;
631
632 type LocalRingBase<'ring> = $crate::rings::field::AsFieldBase<$crate::rings::zn::zn_64::Zn>
633 where Self: 'ring;
634
635 fn ln_pseudo_norm(&self, el: &Self::Element) -> f64 {
636 RingRef::new(self).abs_log2_ceil(el).unwrap_or(0) as f64 * 2f64.ln()
637 }
638
639 fn local_computation<'ring>(&'ring self, ln_pseudo_norm_bound: f64) -> Self::LocalComputationData<'ring> {
640 let mut primes = Vec::new();
641 let mut ln_current = 0.;
642 let mut prime_it = //$crate::reduce_lift::primelist::LARGE_PRIMES.iter().copied().chain
643 ((0..).scan((1 << 62) / 9, |current, _| {
644 *current = $crate::algorithms::miller_rabin::prev_prime(StaticRing::<i64>::RING, *current).unwrap();
645 if *current < (1 << 32) {
646 panic!("not enough primes");
647 }
648 return Some($crate::rings::zn::zn_64::Zn::new(*current as u64));
649 }));
650 while ln_current < ln_pseudo_norm_bound + 1. {
651 let Fp = prime_it.next().unwrap();
652 ln_current += (*$crate::rings::zn::ZnRingStore::modulus(&Fp) as f64).ln();
653 primes.push(Fp);
654 }
655 return $crate::rings::zn::zn_rns::Zn::new(
656 primes.into_iter().map(|Fp| $crate::rings::field::AsField::from($crate::rings::field::AsFieldBase::promise_is_perfect_field(Fp))).collect(),
657 RingRef::new(self)
658 );
659 }
660
661 fn local_ring_at<'ring>(&self, computation: &Self::LocalComputationData<'ring>, i: usize) -> Self::LocalRing<'ring>
662 where Self: 'ring
663 {
664 <_ as $crate::seq::VectorView<_>>::at(computation, i).clone()
665 }
666
667 fn local_ring_count<'ring>(&self, computation: &Self::LocalComputationData<'ring>) -> usize
668 where Self: 'ring
669 {
670 <_ as $crate::seq::VectorView<_>>::len(computation)
671 }
672
673 fn reduce<'ring>(&self, computation: &Self::LocalComputationData<'ring>, el: &Self::Element) -> Vec<<Self::LocalRingBase<'ring> as RingBase>::Element>
674 where Self: 'ring
675 {
676 <_ as $crate::seq::VectorView<_>>::as_iter(&computation.get_congruence(&computation.coerce(RingValue::from_ref(self), self.clone_el(el)))).map(|x| *x).collect()
677 }
678
679 fn lift_combine<'ring>(&self, computation: &Self::LocalComputationData<'ring>, el: &[<Self::LocalRingBase<'ring> as RingBase>::Element]) -> Self::Element
680 where Self: 'ring
681 {
682 <_ as $crate::rings::zn::ZnRingStore>::smallest_lift(computation, computation.from_congruence(el.iter().copied()))
683 }
684 }
685 };
686}