1use core::fmt;
6use core::ops::{Add, Mul, Neg, Sub};
7use std::any::Any;
8use std::sync::Arc;
9
10use serde::ser::SerializeStruct;
11use serde::{Deserialize, Deserializer, Serialize, Serializer};
12
13use crate::numthy::FiniteField;
14use crate::{
15 CoefficientMap, CommutativeRing, CompositeCoefficientMap, ContextMismatchError, Field, Integer,
16 NoetherError, Ring, SemanticType, TypeDescription, VerificationError,
17};
18
19use super::groebner::{quotient_normal_form, unit_ideal_multipliers};
20use super::{
21 from_kernel_basis, from_prime_kernel_polynomial, to_kernel_order, to_kernel_polynomial,
22 to_prime_kernel_polynomial, Degree, FiniteQuotientAlgebra, FreeAbelianGrading, GroebnerError,
23 HilbertSeries, Ideal, NoetherFieldLane, Polynomial, PolynomialError, PolynomialRing,
24};
25
26static QUOTIENT_RING_DESCRIPTION: TypeDescription = TypeDescription {
27 semantic_id: "noether/commalg/QuotientRing@1",
28 rust_path: "noether::commalg::QuotientRing",
29 python_path: Some("noether.commalg.QuotientRing"),
30};
31
32static QUOTIENT_ELEMENT_DESCRIPTION: TypeDescription = TypeDescription {
33 semantic_id: "noether/commalg/QuotientElement@1",
34 rust_path: "noether::commalg::QuotientElement",
35 python_path: Some("noether.commalg.QuotientElement"),
36};
37
38static QUOTIENT_RING_MAP_DESCRIPTION: TypeDescription = TypeDescription {
39 semantic_id: "noether/commalg/QuotientRingMap@1",
40 rust_path: "noether::commalg::QuotientRingMap",
41 python_path: Some("noether.commalg.QuotientRingMap"),
42};
43const QUOTIENT_RING_MAP_TYPE_ID: &str = "noether/commalg/QuotientRingMap@1";
44
45const PREPARED_PRIME_MIN_BASIS_POLYNOMIALS: usize = 8;
49const PREPARED_PRIME_MIN_BASIS_TERMS: usize = 32;
50
51#[derive(Clone, Debug, PartialEq, Eq)]
53pub enum QuotientError {
54 ContextMismatch(ContextMismatchError),
56 Polynomial(PolynomialError),
58 Groebner(GroebnerError),
60 NotGraded,
62 NotAUnit,
64 InvalidUnitCertificate,
66}
67
68impl fmt::Display for QuotientError {
69 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
70 match self {
71 Self::ContextMismatch(error) => error.fmt(formatter),
72 Self::Polynomial(error) => error.fmt(formatter),
73 Self::Groebner(error) => error.fmt(formatter),
74 Self::NotGraded => {
75 formatter.write_str("quotient grading requires a multihomogeneous defining ideal")
76 }
77 Self::NotAUnit => formatter.write_str("quotient element is not a unit"),
78 Self::InvalidUnitCertificate => {
79 formatter.write_str("quotient unit certificate failed exact replay")
80 }
81 }
82 }
83}
84
85impl std::error::Error for QuotientError {
86 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
87 match self {
88 Self::ContextMismatch(error) => Some(error),
89 Self::Polynomial(error) => Some(error),
90 Self::Groebner(error) => Some(error),
91 Self::NotGraded | Self::NotAUnit | Self::InvalidUnitCertificate => None,
92 }
93 }
94}
95
96impl From<ContextMismatchError> for QuotientError {
97 fn from(error: ContextMismatchError) -> Self {
98 Self::ContextMismatch(error)
99 }
100}
101
102impl From<PolynomialError> for QuotientError {
103 fn from(error: PolynomialError) -> Self {
104 Self::Polynomial(error)
105 }
106}
107
108impl From<GroebnerError> for QuotientError {
109 fn from(error: GroebnerError) -> Self {
110 Self::Groebner(error)
111 }
112}
113
114#[derive(Clone, Debug, PartialEq, Eq)]
116pub enum QuotientRingMapError {
117 ContextMismatch(ContextMismatchError),
119 BaseMap(String),
121 RelationDoesNotVanish {
123 generator: usize,
125 },
126}
127
128impl fmt::Display for QuotientRingMapError {
129 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
130 match self {
131 Self::ContextMismatch(error) => error.fmt(formatter),
132 Self::BaseMap(detail) => write!(formatter, "quotient ambient map failed: {detail}"),
133 Self::RelationDoesNotVanish { generator } => write!(
134 formatter,
135 "source quotient relation {generator} has nonzero image in the target quotient"
136 ),
137 }
138 }
139}
140
141impl std::error::Error for QuotientRingMapError {
142 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
143 match self {
144 Self::ContextMismatch(error) => Some(error),
145 Self::BaseMap(_) | Self::RelationDoesNotVanish { .. } => None,
146 }
147 }
148}
149
150impl From<ContextMismatchError> for QuotientRingMapError {
151 fn from(error: ContextMismatchError) -> Self {
152 Self::ContextMismatch(error)
153 }
154}
155
156struct QuotientContext<S: CommutativeRing> {
157 ideal: Ideal<S>,
158 is_graded: bool,
159 reducer: Option<Arc<dyn QuotientReducer<S>>>,
160}
161
162type PreparedQuotientElement = Arc<dyn Any + Send + Sync>;
163
164struct ReducedQuotientElement<E> {
165 value: E,
166 prepared: Option<PreparedQuotientElement>,
167}
168
169trait QuotientReducer<S: CommutativeRing>: Send + Sync {
170 fn normal_form(&self, value: &S::Element) -> Option<ReducedQuotientElement<S::Element>>;
171
172 fn product_normal_form(
173 &self,
174 _left: &S::Element,
175 _right: &S::Element,
176 _left_prepared: Option<&(dyn Any + Send + Sync)>,
177 _right_prepared: Option<&(dyn Any + Send + Sync)>,
178 ) -> Option<ReducedQuotientElement<S::Element>> {
179 None
180 }
181}
182
183struct PreparedFieldPolynomialReducer<R: Field> {
184 ring: PolynomialRing<R>,
185 reducer: groebner_kernel::PreparedPolynomialReducer<R::Element>,
186}
187
188struct PreparedPrimePolynomialReducer<R: Field> {
189 ring: PolynomialRing<R>,
190 field: FiniteField,
191 prime: groebner_kernel::PrimeField32,
192 reducer: groebner_kernel::PreparedPolynomialReducer<u32>,
193}
194
195impl<R: Field> PreparedPrimePolynomialReducer<R> {
196 fn new(ideal: &Ideal<PolynomialRing<R>>) -> Option<Self> {
197 let basis_terms = ideal
198 .generators()
199 .iter()
200 .map(Polynomial::nonzero_term_count)
201 .sum::<usize>();
202 if ideal.generators().len() < PREPARED_PRIME_MIN_BASIS_POLYNOMIALS
203 || basis_terms < PREPARED_PRIME_MIN_BASIS_TERMS
204 {
205 return None;
206 }
207 let field = (ideal.ring().coefficient_ring() as &dyn Any).downcast_ref::<FiniteField>()?;
208 if field.degree() != 1 {
209 return None;
210 }
211 let characteristic = field
212 .characteristic()
213 .to_u64_checked()
214 .and_then(|value| u32::try_from(value).ok())
215 .filter(|value| *value < (1_u32 << 31))?;
216 let prime = groebner_kernel::PrimeField32::new(characteristic).ok()?;
217 let order = to_kernel_order(&ideal.ring().term_order())?;
218 let basis = ideal
219 .generators()
220 .iter()
221 .map(|polynomial| to_prime_kernel_polynomial(polynomial, &prime))
222 .collect::<Option<Vec<_>>>()?;
223 let reducer = groebner_kernel::PreparedPolynomialReducer::new(
224 &prime,
225 ideal.ring().variables().len(),
226 order,
227 &basis,
228 )
229 .ok()?;
230 Some(Self {
231 ring: ideal.ring().clone(),
232 field: field.clone(),
233 prime,
234 reducer,
235 })
236 }
237}
238
239impl<R: Field> QuotientReducer<PolynomialRing<R>> for PreparedPrimePolynomialReducer<R> {
240 fn normal_form(&self, value: &Polynomial<R>) -> Option<ReducedQuotientElement<Polynomial<R>>> {
241 let value = to_prime_kernel_polynomial(value, &self.prime)?;
242 let mut values = self
243 .reducer
244 .normal_forms(
245 &self.prime,
246 &[value],
247 groebner_kernel::GroebnerOptions::default().max_reduction_steps,
248 )
249 .ok()?;
250 let remainder = values.pop()?;
251 let value =
252 from_prime_kernel_polynomial(&self.ring, remainder.clone(), &self.field).ok()?;
253 Some(ReducedQuotientElement {
254 value,
255 prepared: Some(Arc::new(remainder)),
256 })
257 }
258
259 fn product_normal_form(
260 &self,
261 left: &Polynomial<R>,
262 right: &Polynomial<R>,
263 left_prepared: Option<&(dyn Any + Send + Sync)>,
264 right_prepared: Option<&(dyn Any + Send + Sync)>,
265 ) -> Option<ReducedQuotientElement<Polynomial<R>>> {
266 let left_prepared = left_prepared
267 .and_then(|value| value.downcast_ref::<poly_core::SparsePolynomial<u32>>());
268 let right_prepared = right_prepared
269 .and_then(|value| value.downcast_ref::<poly_core::SparsePolynomial<u32>>());
270 let left_owned = left_prepared
271 .is_none()
272 .then(|| to_prime_kernel_polynomial(left, &self.prime))
273 .flatten();
274 let right_owned = right_prepared
275 .is_none()
276 .then(|| to_prime_kernel_polynomial(right, &self.prime))
277 .flatten();
278 let left = left_prepared.or(left_owned.as_ref())?;
279 let right = right_prepared.or(right_owned.as_ref())?;
280 let product = left.multiply(right, &self.prime, None);
281 let mut values = self
282 .reducer
283 .normal_forms(
284 &self.prime,
285 &[product],
286 groebner_kernel::GroebnerOptions::default().max_reduction_steps,
287 )
288 .ok()?;
289 let remainder = values.pop()?;
290 let value =
291 from_prime_kernel_polynomial(&self.ring, remainder.clone(), &self.field).ok()?;
292 Some(ReducedQuotientElement {
293 value,
294 prepared: Some(Arc::new(remainder)),
295 })
296 }
297}
298
299impl<R: Field> PreparedFieldPolynomialReducer<R> {
300 fn new(ideal: &Ideal<PolynomialRing<R>>) -> Option<Self> {
301 if (ideal.ring().coefficient_ring() as &dyn Any).is::<crate::numthy::FiniteField>() {
305 return None;
306 }
307 let order = to_kernel_order(&ideal.ring().term_order())?;
308 let basis = ideal
309 .generators()
310 .iter()
311 .map(to_kernel_polynomial)
312 .collect::<Option<Vec<_>>>()?;
313 let field = NoetherFieldLane {
314 field: ideal.ring().coefficient_ring(),
315 };
316 let reducer = groebner_kernel::PreparedPolynomialReducer::new(
317 &field,
318 ideal.ring().variables().len(),
319 order,
320 &basis,
321 )
322 .ok()?;
323 Some(Self {
324 ring: ideal.ring().clone(),
325 reducer,
326 })
327 }
328}
329
330impl<R: Field> QuotientReducer<PolynomialRing<R>> for PreparedFieldPolynomialReducer<R> {
331 fn normal_form(&self, value: &Polynomial<R>) -> Option<ReducedQuotientElement<Polynomial<R>>> {
332 let value = to_kernel_polynomial(value)?;
333 let field = NoetherFieldLane {
334 field: self.ring.coefficient_ring(),
335 };
336 let mut values = self
337 .reducer
338 .normal_forms(
339 &field,
340 &[value],
341 groebner_kernel::GroebnerOptions::default().max_reduction_steps,
342 )
343 .ok()?;
344 let remainder = values.pop()?;
345 Some(ReducedQuotientElement {
346 value: from_kernel_basis(&self.ring, vec![remainder]).ok()?.pop()?,
347 prepared: None,
348 })
349 }
350}
351
352#[derive(Clone)]
359pub struct QuotientRing<S: CommutativeRing> {
360 context: Arc<QuotientContext<S>>,
361}
362
363#[derive(Clone)]
368pub struct QuotientElement<S: CommutativeRing> {
369 ring: QuotientRing<S>,
370 lift: S::Element,
371 prepared: Option<PreparedQuotientElement>,
372}
373
374#[derive(Clone, PartialEq, Eq)]
380pub struct QuotientRingMap<S, T, M>
381where
382 S: CommutativeRing,
383 T: CommutativeRing,
384 M: CoefficientMap<Source = S, Target = T>,
385{
386 source: QuotientRing<S>,
387 target: QuotientRing<T>,
388 base_map: M,
389}
390
391impl<S: CommutativeRing> QuotientRing<S> {
392 pub fn base_ring(&self) -> &S {
394 self.context.ideal.ring()
395 }
396
397 pub fn defining_ideal(&self) -> &Ideal<S> {
399 &self.context.ideal
400 }
401
402 fn from_parts(
403 ideal: Ideal<S>,
404 is_graded: bool,
405 reducer: Option<Arc<dyn QuotientReducer<S>>>,
406 ) -> Self {
407 Self {
408 context: Arc::new(QuotientContext {
409 ideal,
410 is_graded,
411 reducer,
412 }),
413 }
414 }
415}
416
417impl<R: Field> PolynomialRing<R> {
418 pub fn quotient(
420 &self,
421 ideal: &Ideal<Self>,
422 ) -> Result<QuotientRing<Self>, ContextMismatchError> {
423 if ideal.ring() != self {
424 return Err(ContextMismatchError {
425 expected: self.to_string(),
426 actual: ideal.ring().to_string(),
427 });
428 }
429 let is_graded = ideal
430 .generators()
431 .iter()
432 .all(|generator| generator.multihomogeneous_degree().ok().flatten().is_some());
433 let reducer = PreparedPrimePolynomialReducer::new(ideal)
434 .map(|reducer| Arc::new(reducer) as Arc<dyn QuotientReducer<Self>>)
435 .or_else(|| {
436 PreparedFieldPolynomialReducer::new(ideal)
437 .map(|reducer| Arc::new(reducer) as Arc<dyn QuotientReducer<Self>>)
438 });
439 Ok(QuotientRing::from_parts(ideal.clone(), is_graded, reducer))
440 }
441}
442
443impl<R: Field> QuotientRing<PolynomialRing<R>> {
444 fn is_zero_ring_context(&self) -> bool {
445 self.defining_ideal()
446 .generators()
447 .iter()
448 .any(Polynomial::is_one)
449 }
450
451 fn monomial_product(
452 &self,
453 left: &QuotientElement<PolynomialRing<R>>,
454 right: &QuotientElement<PolynomialRing<R>>,
455 ) -> Option<Result<QuotientElement<PolynomialRing<R>>, ContextMismatchError>> {
456 if left.lift.nonzero_term_count() != 1 || right.lift.nonzero_term_count() != 1 {
457 return None;
458 }
459 let (left_exponent, _) = left.lift.terms().next()?;
460 let (right_exponent, _) = right.lift.terms().next()?;
461 let product_exponent = left_exponent
462 .iter()
463 .zip(right_exponent)
464 .map(|(left, right)| left + right)
465 .collect::<Vec<_>>();
466 for relation in self.defining_ideal().generators() {
467 if relation.nonzero_term_count() != 1 {
468 return None;
469 }
470 let (relation_exponent, _) = relation.terms().next()?;
471 if relation_exponent
472 .iter()
473 .zip(&product_exponent)
474 .all(|(relation, product)| relation <= product)
475 {
476 return Some(Ok(self.zero()));
477 }
478 }
479 Some(
480 self.base_ring()
481 .mul(&left.lift, &right.lift)
482 .and_then(|value| self.element_from_normal_form(value)),
483 )
484 }
485
486 pub fn element(
488 &self,
489 value: &Polynomial<R>,
490 ) -> Result<QuotientElement<PolynomialRing<R>>, ContextMismatchError> {
491 if !self.base_ring().contains(value) {
492 return Err(ContextMismatchError {
493 expected: self.base_ring().to_string(),
494 actual: value.ring().to_string(),
495 });
496 }
497 let reduced = self
498 .context
499 .reducer
500 .as_ref()
501 .and_then(|reducer| reducer.normal_form(value))
502 .map(Ok)
503 .unwrap_or_else(|| {
504 quotient_normal_form(self.defining_ideal(), value).map(|value| {
505 ReducedQuotientElement {
506 value,
507 prepared: None,
508 }
509 })
510 })?;
511 Ok(QuotientElement {
512 ring: self.clone(),
513 lift: reduced.value,
514 prepared: reduced.prepared,
515 })
516 }
517
518 pub(crate) fn element_from_normal_form(
524 &self,
525 value: Polynomial<R>,
526 ) -> Result<QuotientElement<PolynomialRing<R>>, ContextMismatchError> {
527 if !self.base_ring().contains(&value) {
528 return Err(ContextMismatchError {
529 expected: self.base_ring().to_string(),
530 actual: value.ring().to_string(),
531 });
532 }
533 Ok(QuotientElement {
534 ring: self.clone(),
535 lift: value,
536 prepared: None,
537 })
538 }
539
540 fn require(
541 &self,
542 value: &QuotientElement<PolynomialRing<R>>,
543 ) -> Result<(), ContextMismatchError> {
544 if self.contains(value) {
545 Ok(())
546 } else {
547 Err(ContextMismatchError {
548 expected: self.to_string(),
549 actual: value.ring.to_string(),
550 })
551 }
552 }
553
554 pub fn is_graded(&self) -> bool {
556 self.context.is_graded
557 }
558
559 pub fn grading(&self) -> Option<&FreeAbelianGrading> {
561 self.is_graded().then(|| self.base_ring().grading())
562 }
563
564 pub fn hilbert_series(&self) -> Result<HilbertSeries, GroebnerError> {
566 self.defining_ideal().hilbert_series()
567 }
568
569 pub fn hilbert_numerator(&self) -> Result<Vec<Integer>, GroebnerError> {
571 self.defining_ideal().hilbert_numerator()
572 }
573
574 pub fn dimension(&self) -> Result<i64, GroebnerError> {
576 self.defining_ideal().dimension()
577 }
578
579 pub fn degree(&self) -> Result<Integer, GroebnerError> {
581 self.defining_ideal().degree()
582 }
583
584 pub fn hilbert_polynomial(&self) -> Result<Polynomial<crate::RationalField>, GroebnerError> {
586 self.defining_ideal().hilbert_polynomial()
587 }
588
589 pub fn finite_algebra(&self) -> Result<FiniteQuotientAlgebra<R>, GroebnerError> {
591 self.defining_ideal().finite_quotient_algebra()
592 }
593
594 pub fn induced_map<Q, M>(
599 &self,
600 target: &QuotientRing<PolynomialRing<Q>>,
601 base_map: M,
602 ) -> Result<QuotientRingMap<PolynomialRing<R>, PolynomialRing<Q>, M>, QuotientRingMapError>
603 where
604 Q: Field,
605 M: CoefficientMap<Source = PolynomialRing<R>, Target = PolynomialRing<Q>>,
606 {
607 QuotientRingMap::new(self.clone(), target.clone(), base_map)
608 }
609
610 fn inverse_element(
611 &self,
612 value: &QuotientElement<PolynomialRing<R>>,
613 ) -> Result<QuotientElement<PolynomialRing<R>>, QuotientError> {
614 self.require(value)?;
615 let generators = self
616 .defining_ideal()
617 .generators()
618 .iter()
619 .cloned()
620 .chain(std::iter::once(value.lift.clone()))
621 .collect::<Vec<_>>();
622 let Some(mut multipliers) = unit_ideal_multipliers(self.base_ring(), &generators)? else {
623 return Err(QuotientError::NotAUnit);
624 };
625 let inverse_lift = multipliers
626 .pop()
627 .ok_or(QuotientError::InvalidUnitCertificate)?;
628 let inverse = self.element(&inverse_lift)?;
629 if self.mul(value, &inverse)? != self.one() {
630 return Err(QuotientError::InvalidUnitCertificate);
631 }
632 Ok(inverse)
633 }
634
635 pub fn to_json(&self) -> Result<String, NoetherError>
637 where
638 R: Serialize,
639 R::Element: Serialize,
640 {
641 serde_json::to_string(self).map_err(|error| NoetherError::Serialization(error.to_string()))
642 }
643
644 pub fn from_json(value: &str) -> Result<Self, NoetherError>
646 where
647 R: for<'de> Deserialize<'de>,
648 R::Element: for<'de> Deserialize<'de>,
649 {
650 serde_json::from_str(value).map_err(|error| NoetherError::Serialization(error.to_string()))
651 }
652}
653
654impl<R: Field> QuotientElement<PolynomialRing<R>> {
655 pub fn ring(&self) -> &QuotientRing<PolynomialRing<R>> {
657 &self.ring
658 }
659
660 pub fn lift(&self) -> &Polynomial<R> {
662 &self.lift
663 }
664
665 pub fn is_zero(&self) -> bool {
667 self.lift.is_zero()
668 }
669
670 pub fn is_one(&self) -> bool {
672 self.lift.is_one() || (self.lift.is_zero() && self.ring.is_zero_ring_context())
673 }
674
675 pub fn multihomogeneous_degree(&self) -> Result<Option<Degree>, QuotientError> {
677 if !self.ring.is_graded() {
678 return Err(QuotientError::NotGraded);
679 }
680 self.lift.multihomogeneous_degree().map_err(Into::into)
681 }
682
683 pub fn is_unit(&self) -> Result<bool, QuotientError> {
685 match self.ring.inverse_element(self) {
686 Ok(_) => Ok(true),
687 Err(QuotientError::NotAUnit) => Ok(false),
688 Err(error) => Err(error),
689 }
690 }
691
692 pub fn inverse(&self) -> Result<Self, QuotientError> {
694 self.ring.inverse_element(self)
695 }
696
697 pub fn pow(&self, mut exponent: u64) -> Self {
699 let mut result = self.ring.one();
700 let mut base = self.clone();
701 while exponent != 0 {
702 if exponent & 1 == 1 {
703 result = self
704 .ring
705 .mul(&result, &base)
706 .expect("powers retain one quotient context");
707 }
708 exponent >>= 1;
709 if exponent != 0 {
710 base = self
711 .ring
712 .mul(&base, &base)
713 .expect("powers retain one quotient context");
714 }
715 }
716 result
717 }
718
719 pub fn to_json(&self) -> Result<String, NoetherError>
721 where
722 R: Serialize,
723 R::Element: Serialize,
724 {
725 serde_json::to_string(self).map_err(|error| NoetherError::Serialization(error.to_string()))
726 }
727
728 pub fn from_json(value: &str) -> Result<Self, NoetherError>
730 where
731 R: for<'de> Deserialize<'de>,
732 R::Element: for<'de> Deserialize<'de>,
733 {
734 serde_json::from_str(value).map_err(|error| NoetherError::Serialization(error.to_string()))
735 }
736}
737
738impl<R, Q, M> QuotientRingMap<PolynomialRing<R>, PolynomialRing<Q>, M>
739where
740 R: Field,
741 Q: Field,
742 M: CoefficientMap<Source = PolynomialRing<R>, Target = PolynomialRing<Q>>,
743{
744 pub fn new(
746 source: QuotientRing<PolynomialRing<R>>,
747 target: QuotientRing<PolynomialRing<Q>>,
748 base_map: M,
749 ) -> Result<Self, QuotientRingMapError> {
750 if source.base_ring() != base_map.source() {
751 return Err(ContextMismatchError {
752 expected: source.base_ring().to_string(),
753 actual: base_map.source().to_string(),
754 }
755 .into());
756 }
757 if target.base_ring() != base_map.target() {
758 return Err(ContextMismatchError {
759 expected: target.base_ring().to_string(),
760 actual: base_map.target().to_string(),
761 }
762 .into());
763 }
764 let candidate = Self {
765 source,
766 target,
767 base_map,
768 };
769 candidate.check_relations()?;
770 Ok(candidate)
771 }
772
773 pub fn source(&self) -> &QuotientRing<PolynomialRing<R>> {
775 &self.source
776 }
777
778 pub fn target(&self) -> &QuotientRing<PolynomialRing<Q>> {
780 &self.target
781 }
782
783 pub fn base_map(&self) -> &M {
785 &self.base_map
786 }
787
788 pub fn apply(
790 &self,
791 value: &QuotientElement<PolynomialRing<R>>,
792 ) -> Result<QuotientElement<PolynomialRing<Q>>, QuotientRingMapError> {
793 if !self.source.contains(value) {
794 return Err(ContextMismatchError {
795 expected: self.source.to_string(),
796 actual: value.ring().to_string(),
797 }
798 .into());
799 }
800 let image = self
801 .base_map
802 .apply(value.lift())
803 .map_err(|error| QuotientRingMapError::BaseMap(error.to_string()))?;
804 self.target.element(&image).map_err(Into::into)
805 }
806
807 pub fn verify(&self) -> Result<(), VerificationError> {
809 if self.source.base_ring() != self.base_map.source() {
810 return Err(VerificationError::InvalidWitness {
811 claim: "quotient map ambient source equals the source quotient's base ring",
812 });
813 }
814 if self.target.base_ring() != self.base_map.target() {
815 return Err(VerificationError::InvalidWitness {
816 claim: "quotient map ambient target equals the target quotient's base ring",
817 });
818 }
819 self.check_relations()
820 .map_err(|_| VerificationError::InvalidWitness {
821 claim: "quotient map sends every source defining relation to zero",
822 })
823 }
824
825 fn check_relations(&self) -> Result<(), QuotientRingMapError> {
826 for (generator, relation) in self.source.defining_ideal().generators().iter().enumerate() {
827 let image = self
828 .base_map
829 .apply(relation)
830 .map_err(|error| QuotientRingMapError::BaseMap(error.to_string()))?;
831 if !self.target.element(&image)?.is_zero() {
832 return Err(QuotientRingMapError::RelationDoesNotVanish { generator });
833 }
834 }
835 Ok(())
836 }
837
838 pub fn to_json(&self) -> Result<String, NoetherError>
840 where
841 R: Serialize,
842 R::Element: Serialize,
843 Q: Serialize,
844 Q::Element: Serialize,
845 M: Serialize,
846 {
847 serde_json::to_string(self).map_err(|error| NoetherError::Serialization(error.to_string()))
848 }
849
850 pub fn from_json(value: &str) -> Result<Self, NoetherError>
852 where
853 R: for<'de> Deserialize<'de>,
854 R::Element: for<'de> Deserialize<'de>,
855 Q: for<'de> Deserialize<'de>,
856 Q::Element: for<'de> Deserialize<'de>,
857 M: for<'de> Deserialize<'de>,
858 {
859 serde_json::from_str(value).map_err(|error| NoetherError::Serialization(error.to_string()))
860 }
861}
862
863impl<B, C, N> QuotientRingMap<PolynomialRing<B>, PolynomialRing<C>, N>
864where
865 B: Field,
866 C: Field,
867 N: CoefficientMap<Source = PolynomialRing<B>, Target = PolynomialRing<C>> + Clone,
868{
869 #[allow(clippy::type_complexity)]
871 pub fn compose<A, M>(
872 &self,
873 inner: &QuotientRingMap<PolynomialRing<A>, PolynomialRing<B>, M>,
874 ) -> Result<
875 QuotientRingMap<PolynomialRing<A>, PolynomialRing<C>, CompositeCoefficientMap<M, N>>,
876 QuotientRingMapError,
877 >
878 where
879 A: Field,
880 M: CoefficientMap<Source = PolynomialRing<A>, Target = PolynomialRing<B>> + Clone,
881 {
882 if inner.target() != self.source() {
883 return Err(ContextMismatchError {
884 expected: self.source().to_string(),
885 actual: inner.target().to_string(),
886 }
887 .into());
888 }
889 let base_map =
890 CompositeCoefficientMap::new(inner.base_map().clone(), self.base_map().clone())?;
891 QuotientRingMap::new(inner.source().clone(), self.target().clone(), base_map)
892 }
893}
894
895impl<R, Q, M> CoefficientMap for QuotientRingMap<PolynomialRing<R>, PolynomialRing<Q>, M>
896where
897 R: Field,
898 Q: Field,
899 M: CoefficientMap<Source = PolynomialRing<R>, Target = PolynomialRing<Q>>,
900{
901 type Source = QuotientRing<PolynomialRing<R>>;
902 type Target = QuotientRing<PolynomialRing<Q>>;
903 type Error = QuotientRingMapError;
904
905 fn source(&self) -> &Self::Source {
906 &self.source
907 }
908
909 fn target(&self) -> &Self::Target {
910 &self.target
911 }
912
913 fn apply(
914 &self,
915 value: &QuotientElement<PolynomialRing<R>>,
916 ) -> Result<QuotientElement<PolynomialRing<Q>>, Self::Error> {
917 QuotientRingMap::apply(self, value)
918 }
919}
920
921impl<R: Field> Ring for QuotientRing<PolynomialRing<R>> {
922 type Element = QuotientElement<PolynomialRing<R>>;
923
924 fn contains(&self, value: &Self::Element) -> bool {
925 self == &value.ring && self.base_ring().contains(&value.lift)
926 }
927
928 fn zero(&self) -> Self::Element {
929 QuotientElement {
930 ring: self.clone(),
931 lift: self.base_ring().zero(),
932 prepared: None,
933 }
934 }
935
936 fn is_zero(&self, value: &Self::Element) -> bool {
937 self.contains(value) && value.is_zero()
938 }
939
940 fn one(&self) -> Self::Element {
941 let lift = if self.is_zero_ring_context() {
946 self.base_ring().zero()
947 } else {
948 self.base_ring().one()
949 };
950 QuotientElement {
951 ring: self.clone(),
952 lift,
953 prepared: None,
954 }
955 }
956
957 fn add(
958 &self,
959 left: &Self::Element,
960 right: &Self::Element,
961 ) -> Result<Self::Element, ContextMismatchError> {
962 self.require(left)?;
963 self.require(right)?;
964 let lift = self.base_ring().add(&left.lift, &right.lift)?;
965 Ok(QuotientElement {
966 ring: self.clone(),
967 lift,
968 prepared: None,
969 })
970 }
971
972 fn sub(
973 &self,
974 left: &Self::Element,
975 right: &Self::Element,
976 ) -> Result<Self::Element, ContextMismatchError> {
977 self.require(left)?;
978 self.require(right)?;
979 let lift = self.base_ring().sub(&left.lift, &right.lift)?;
980 Ok(QuotientElement {
981 ring: self.clone(),
982 lift,
983 prepared: None,
984 })
985 }
986
987 fn mul(
988 &self,
989 left: &Self::Element,
990 right: &Self::Element,
991 ) -> Result<Self::Element, ContextMismatchError> {
992 self.require(left)?;
993 self.require(right)?;
994 if left.lift.is_zero() || right.lift.is_zero() {
995 return Ok(self.zero());
996 }
997 if left.lift.is_one() {
998 return Ok(right.clone());
999 }
1000 if right.lift.is_one() {
1001 return Ok(left.clone());
1002 }
1003 if let Some(product) = self.monomial_product(left, right) {
1004 return product;
1005 }
1006 if let Some(reduced) = self.context.reducer.as_ref().and_then(|reducer| {
1007 reducer.product_normal_form(
1008 &left.lift,
1009 &right.lift,
1010 left.prepared.as_deref(),
1011 right.prepared.as_deref(),
1012 )
1013 }) {
1014 return Ok(QuotientElement {
1015 ring: self.clone(),
1016 lift: reduced.value,
1017 prepared: reduced.prepared,
1018 });
1019 }
1020 self.element(&self.base_ring().mul(&left.lift, &right.lift)?)
1021 }
1022
1023 fn sum_products(
1024 &self,
1025 products: &[(&Self::Element, &Self::Element)],
1026 ) -> Result<Self::Element, ContextMismatchError> {
1027 for &(left, right) in products {
1028 self.require(left)?;
1029 self.require(right)?;
1030 }
1031 let nonzero = products
1032 .iter()
1033 .copied()
1034 .filter(|(left, right)| !left.lift.is_zero() && !right.lift.is_zero())
1035 .collect::<Vec<_>>();
1036 if nonzero.is_empty() {
1037 return Ok(self.zero());
1038 }
1039 if nonzero.len() == 1 {
1040 let (left, right) = nonzero[0];
1041 return self.mul(left, right);
1042 }
1043 let ambient = nonzero
1044 .iter()
1045 .map(|&(left, right)| (&left.lift, &right.lift))
1046 .collect::<Vec<_>>();
1047 self.element(&self.base_ring().sum_products(&ambient)?)
1048 }
1049
1050 fn neg(&self, value: &Self::Element) -> Result<Self::Element, ContextMismatchError> {
1051 self.require(value)?;
1052 Ok(QuotientElement {
1053 ring: self.clone(),
1054 lift: self.base_ring().neg(&value.lift)?,
1055 prepared: None,
1056 })
1057 }
1058}
1059
1060impl<R: Field> CommutativeRing for QuotientRing<PolynomialRing<R>> {}
1061
1062impl<S: CommutativeRing> PartialEq for QuotientRing<S> {
1063 fn eq(&self, other: &Self) -> bool {
1064 Arc::ptr_eq(&self.context, &other.context) || self.context.ideal == other.context.ideal
1065 }
1066}
1067
1068impl<S: CommutativeRing> Eq for QuotientRing<S> {}
1069
1070impl<S: CommutativeRing> fmt::Debug for QuotientRing<S> {
1071 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1072 formatter
1073 .debug_struct("QuotientRing")
1074 .field("base_ring", &S::TYPE_ID)
1075 .field(
1076 "defining_generators",
1077 &self.defining_ideal().generators().len(),
1078 )
1079 .finish()
1080 }
1081}
1082
1083impl<S: CommutativeRing> fmt::Display for QuotientRing<S> {
1084 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1085 write!(
1086 formatter,
1087 "QuotientRing(base={}, generators={})",
1088 S::TYPE_ID,
1089 self.defining_ideal().generators().len()
1090 )
1091 }
1092}
1093
1094impl<S: CommutativeRing> SemanticType for QuotientRing<S> {
1095 const TYPE_ID: &'static str = "noether/commalg/QuotientRing@1";
1096
1097 fn canonical_bytes(&self) -> Result<Vec<u8>, NoetherError> {
1098 self.defining_ideal().canonical_bytes()
1099 }
1100
1101 fn describe() -> &'static TypeDescription {
1102 "IENT_RING_DESCRIPTION
1103 }
1104}
1105
1106impl<S: CommutativeRing> PartialEq for QuotientElement<S> {
1107 fn eq(&self, other: &Self) -> bool {
1108 self.ring == other.ring && self.lift == other.lift
1109 }
1110}
1111
1112impl<S: CommutativeRing> Eq for QuotientElement<S> {}
1113
1114impl<S: CommutativeRing> fmt::Debug for QuotientElement<S> {
1115 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1116 formatter
1117 .debug_struct("QuotientElement")
1118 .field("base_ring", &S::TYPE_ID)
1119 .finish()
1120 }
1121}
1122
1123impl<S: CommutativeRing> fmt::Display for QuotientElement<S> {
1124 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1125 write!(formatter, "QuotientElement(base={})", S::TYPE_ID)
1126 }
1127}
1128
1129impl<S: CommutativeRing> SemanticType for QuotientElement<S> {
1130 const TYPE_ID: &'static str = "noether/commalg/QuotientElement@1";
1131
1132 fn canonical_bytes(&self) -> Result<Vec<u8>, NoetherError> {
1133 let ring = self.ring.canonical_bytes()?;
1134 let lift = self.lift.canonical_bytes()?;
1135 let mut output = Vec::with_capacity(16 + ring.len() + lift.len());
1136 for component in [ring, lift] {
1137 let length = u64::try_from(component.len()).map_err(|_| {
1138 NoetherError::Serialization(
1139 "quotient-element component is too large for canonical framing".to_owned(),
1140 )
1141 })?;
1142 output.extend_from_slice(&length.to_be_bytes());
1143 output.extend_from_slice(&component);
1144 }
1145 Ok(output)
1146 }
1147
1148 fn describe() -> &'static TypeDescription {
1149 "IENT_ELEMENT_DESCRIPTION
1150 }
1151}
1152
1153impl<R: Field> Add<&QuotientElement<PolynomialRing<R>>> for &QuotientElement<PolynomialRing<R>> {
1154 type Output = Result<QuotientElement<PolynomialRing<R>>, ContextMismatchError>;
1155
1156 fn add(self, rhs: &QuotientElement<PolynomialRing<R>>) -> Self::Output {
1157 self.ring.add(self, rhs)
1158 }
1159}
1160
1161impl<R: Field> Sub<&QuotientElement<PolynomialRing<R>>> for &QuotientElement<PolynomialRing<R>> {
1162 type Output = Result<QuotientElement<PolynomialRing<R>>, ContextMismatchError>;
1163
1164 fn sub(self, rhs: &QuotientElement<PolynomialRing<R>>) -> Self::Output {
1165 self.ring.sub(self, rhs)
1166 }
1167}
1168
1169impl<R: Field> Mul<&QuotientElement<PolynomialRing<R>>> for &QuotientElement<PolynomialRing<R>> {
1170 type Output = Result<QuotientElement<PolynomialRing<R>>, ContextMismatchError>;
1171
1172 fn mul(self, rhs: &QuotientElement<PolynomialRing<R>>) -> Self::Output {
1173 self.ring.mul(self, rhs)
1174 }
1175}
1176
1177impl<R: Field> Neg for &QuotientElement<PolynomialRing<R>> {
1178 type Output = Result<QuotientElement<PolynomialRing<R>>, ContextMismatchError>;
1179
1180 fn neg(self) -> Self::Output {
1181 self.ring.neg(self)
1182 }
1183}
1184
1185impl<R, Q, M> SemanticType for QuotientRingMap<PolynomialRing<R>, PolynomialRing<Q>, M>
1186where
1187 R: Field,
1188 Q: Field,
1189 M: CoefficientMap<Source = PolynomialRing<R>, Target = PolynomialRing<Q>>
1190 + SemanticType
1191 + PartialEq
1192 + Eq,
1193{
1194 const TYPE_ID: &'static str = QUOTIENT_RING_MAP_TYPE_ID;
1195
1196 fn canonical_bytes(&self) -> Result<Vec<u8>, NoetherError> {
1197 let mut output = Vec::new();
1198 for component in [
1199 self.source.canonical_bytes()?,
1200 self.target.canonical_bytes()?,
1201 self.base_map.canonical_bytes()?,
1202 ] {
1203 let length = u64::try_from(component.len()).map_err(|_| {
1204 NoetherError::Serialization(
1205 "quotient-ring-map component is too large for canonical framing".to_owned(),
1206 )
1207 })?;
1208 output.extend_from_slice(&length.to_be_bytes());
1209 output.extend_from_slice(&component);
1210 }
1211 Ok(output)
1212 }
1213
1214 fn describe() -> &'static TypeDescription {
1215 "IENT_RING_MAP_DESCRIPTION
1216 }
1217}
1218
1219impl<R, Q, M> fmt::Debug for QuotientRingMap<PolynomialRing<R>, PolynomialRing<Q>, M>
1220where
1221 R: Field,
1222 Q: Field,
1223 M: CoefficientMap<Source = PolynomialRing<R>, Target = PolynomialRing<Q>>,
1224{
1225 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1226 formatter
1227 .debug_struct("QuotientRingMap")
1228 .field("source", &self.source)
1229 .field("target", &self.target)
1230 .finish_non_exhaustive()
1231 }
1232}
1233
1234impl<R, Q, M> fmt::Display for QuotientRingMap<PolynomialRing<R>, PolynomialRing<Q>, M>
1235where
1236 R: Field,
1237 Q: Field,
1238 M: CoefficientMap<Source = PolynomialRing<R>, Target = PolynomialRing<Q>>,
1239{
1240 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1241 write!(formatter, "{} -> {}", self.source, self.target)
1242 }
1243}
1244
1245#[derive(Deserialize)]
1246#[serde(
1247 deny_unknown_fields,
1248 bound(
1249 deserialize = "R: Field + Deserialize<'de>, R::Element: Deserialize<'de>, Q: Field + Deserialize<'de>, Q::Element: Deserialize<'de>, M: CoefficientMap<Source = PolynomialRing<R>, Target = PolynomialRing<Q>> + Deserialize<'de>"
1250 )
1251)]
1252struct QuotientRingMapWire<R, Q, M>
1253where
1254 R: Field,
1255 Q: Field,
1256 M: CoefficientMap<Source = PolynomialRing<R>, Target = PolynomialRing<Q>>,
1257{
1258 #[serde(rename = "type")]
1259 type_id: String,
1260 source: QuotientRing<PolynomialRing<R>>,
1261 target: QuotientRing<PolynomialRing<Q>>,
1262 base_map: M,
1263}
1264
1265impl<R, Q, M> Serialize for QuotientRingMap<PolynomialRing<R>, PolynomialRing<Q>, M>
1266where
1267 R: Field + Serialize,
1268 R::Element: Serialize,
1269 Q: Field + Serialize,
1270 Q::Element: Serialize,
1271 M: CoefficientMap<Source = PolynomialRing<R>, Target = PolynomialRing<Q>> + Serialize,
1272{
1273 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1274 where
1275 S: Serializer,
1276 {
1277 let mut state = serializer.serialize_struct("QuotientRingMap", 4)?;
1278 state.serialize_field("type", QUOTIENT_RING_MAP_TYPE_ID)?;
1279 state.serialize_field("source", &self.source)?;
1280 state.serialize_field("target", &self.target)?;
1281 state.serialize_field("base_map", &self.base_map)?;
1282 state.end()
1283 }
1284}
1285
1286impl<'de, R, Q, M> Deserialize<'de> for QuotientRingMap<PolynomialRing<R>, PolynomialRing<Q>, M>
1287where
1288 R: Field + Deserialize<'de>,
1289 R::Element: Deserialize<'de>,
1290 Q: Field + Deserialize<'de>,
1291 Q::Element: Deserialize<'de>,
1292 M: CoefficientMap<Source = PolynomialRing<R>, Target = PolynomialRing<Q>> + Deserialize<'de>,
1293{
1294 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1295 where
1296 D: Deserializer<'de>,
1297 {
1298 let wire = QuotientRingMapWire::<R, Q, M>::deserialize(deserializer)?;
1299 if wire.type_id != QUOTIENT_RING_MAP_TYPE_ID {
1300 return Err(serde::de::Error::custom(format!(
1301 "expected type {QUOTIENT_RING_MAP_TYPE_ID}, received {}",
1302 wire.type_id
1303 )));
1304 }
1305 Self::new(wire.source, wire.target, wire.base_map).map_err(serde::de::Error::custom)
1306 }
1307}
1308
1309#[derive(Deserialize)]
1310#[serde(
1311 deny_unknown_fields,
1312 bound(deserialize = "R: Field + Deserialize<'de>, R::Element: Deserialize<'de>")
1313)]
1314struct QuotientRingWire<R: Field> {
1315 #[serde(rename = "type")]
1316 type_id: String,
1317 defining_ideal: Ideal<PolynomialRing<R>>,
1318}
1319
1320impl<R> Serialize for QuotientRing<PolynomialRing<R>>
1321where
1322 R: Field + Serialize,
1323 R::Element: Serialize,
1324{
1325 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1326 where
1327 S: Serializer,
1328 {
1329 let mut state = serializer.serialize_struct("QuotientRing", 2)?;
1330 state.serialize_field("type", Self::TYPE_ID)?;
1331 state.serialize_field("defining_ideal", self.defining_ideal())?;
1332 state.end()
1333 }
1334}
1335
1336impl<'de, R> Deserialize<'de> for QuotientRing<PolynomialRing<R>>
1337where
1338 R: Field + Deserialize<'de>,
1339 R::Element: Deserialize<'de>,
1340{
1341 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1342 where
1343 D: Deserializer<'de>,
1344 {
1345 let wire = QuotientRingWire::<R>::deserialize(deserializer)?;
1346 if wire.type_id != Self::TYPE_ID {
1347 return Err(serde::de::Error::custom(format!(
1348 "expected type {}, received {}",
1349 Self::TYPE_ID,
1350 wire.type_id
1351 )));
1352 }
1353 wire.defining_ideal
1354 .ring()
1355 .quotient(&wire.defining_ideal)
1356 .map_err(serde::de::Error::custom)
1357 }
1358}
1359
1360#[derive(Deserialize)]
1361#[serde(
1362 deny_unknown_fields,
1363 bound(deserialize = "R: Field + Deserialize<'de>, R::Element: Deserialize<'de>")
1364)]
1365struct QuotientElementWire<R: Field> {
1366 #[serde(rename = "type")]
1367 type_id: String,
1368 ring: QuotientRing<PolynomialRing<R>>,
1369 lift: Polynomial<R>,
1370}
1371
1372impl<R> Serialize for QuotientElement<PolynomialRing<R>>
1373where
1374 R: Field + Serialize,
1375 R::Element: Serialize,
1376{
1377 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1378 where
1379 S: Serializer,
1380 {
1381 let mut state = serializer.serialize_struct("QuotientElement", 3)?;
1382 state.serialize_field("type", Self::TYPE_ID)?;
1383 state.serialize_field("ring", &self.ring)?;
1384 state.serialize_field("lift", &self.lift)?;
1385 state.end()
1386 }
1387}
1388
1389impl<'de, R> Deserialize<'de> for QuotientElement<PolynomialRing<R>>
1390where
1391 R: Field + Deserialize<'de>,
1392 R::Element: Deserialize<'de>,
1393{
1394 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1395 where
1396 D: Deserializer<'de>,
1397 {
1398 let wire = QuotientElementWire::<R>::deserialize(deserializer)?;
1399 if wire.type_id != Self::TYPE_ID {
1400 return Err(serde::de::Error::custom(format!(
1401 "expected type {}, received {}",
1402 Self::TYPE_ID,
1403 wire.type_id
1404 )));
1405 }
1406 let value = wire
1407 .ring
1408 .element(&wire.lift)
1409 .map_err(serde::de::Error::custom)?;
1410 if value.lift != wire.lift {
1411 return Err(serde::de::Error::custom(
1412 "serialized quotient lift is not the canonical normal form",
1413 ));
1414 }
1415 Ok(value)
1416 }
1417}