algebraeon_rings/polynomial/
symmetric.rs1use super::multipoly_structure::*;
2use crate::{
3 polynomial::{MultiPolynomial, Variable},
4 structure::*,
5};
6use algebraeon_sets::structure::*;
7use itertools::Itertools;
8use std::{borrow::Borrow, collections::HashSet};
9
10pub fn ss_num(n: usize) -> String {
11 let mut ss = String::new();
12 for x in n.to_string().chars() {
13 match x {
14 '0' => ss += "₀",
15 '1' => ss += "₁",
16 '2' => ss += "₂",
17 '3' => ss += "₃",
18 '4' => ss += "₄",
19 '5' => ss += "₅",
20 '6' => ss += "₆",
21 '7' => ss += "₇",
22 '8' => ss += "₈",
23 '9' => ss += "₉",
24 c => ss += &mut c.to_string(),
25 }
26 }
27 ss
28}
29
30impl<RS: IntegralDomainSignature, RSB: BorrowedStructure<RS>> MultiPolynomialStructure<RS, RSB>
33where
35 MultiPolynomialStructure<RS, RSB>:
36 SetSignature<Set = MultiPolynomial<RS::Set>> + ToStringSignature,
37{
38 pub fn is_symmetric(
39 &self,
40 vars: Vec<impl Borrow<Variable>>,
41 poly: &MultiPolynomial<RS::Set>,
42 ) -> bool {
43 let vars_hash: HashSet<_> = vars.iter().map(|v| v.borrow().clone()).collect();
44 for v in poly.free_vars() {
45 if !vars_hash.contains(&v) {
46 return false;
47 }
48 }
49
50 let n = vars.len();
51 let perms = match n {
53 0 => {
54 vec![]
56 }
57 1 => {
58 vec![vec![0]]
60 }
61 2 => {
62 vec![
64 (0..n)
65 .map(|i| match i {
66 0 => 1,
67 1 => 0,
68 x => x,
69 })
70 .collect(),
71 ]
72 }
73 n => {
74 vec![
76 (0..n)
77 .map(|i| match i {
78 0 => 1,
79 1 => 0,
80 x => x,
81 })
82 .collect(),
83 (0..n).map(|i| (i + 1) % n).collect(),
84 ]
85 }
86 };
87
88 perms.into_iter().all(|perm| {
89 let perm_poly = poly.clone().apply_map_vars(
90 perm.into_iter()
91 .enumerate()
92 .map(|(i, j)| (vars[i].borrow().clone(), vars[j].borrow().clone()))
93 .collect(),
94 );
95 self.equal(poly, &perm_poly)
96 })
97 }
98
99 pub fn elementary_symmetric(
100 &self,
101 n: usize,
102 vars: &[impl Borrow<Variable>],
103 ) -> MultiPolynomial<RS::Set> {
104 let mp_ring = self.coeff_ring().multivariable_polynomial_ring();
105 let mut e = mp_ring.zero();
106 for vs in algebraeon_sets::combinatorics::subsets_of_vec(vars.iter().collect(), n) {
107 let mut p = mp_ring.one();
108 for v in vs {
109 mp_ring.mul_mut(&mut p, &mp_ring.var(v.borrow().clone()));
110 }
111 mp_ring.add_mut(&mut e, &p);
112 }
113 e
114 }
115
116 fn as_elementary_symmetric_polynomials_homogeneous_impl(
117 &self,
118 vars: &Vec<Variable>,
119 p: &MultiPolynomial<RS::Set>,
120 e: &Vec<Variable>,
121 ) -> MultiPolynomial<RS::Set> {
122 let (last_var, first_vars) = vars.split_last().unwrap();
126
127 let p_tilde = p.clone().evaluate_var_zero(last_var);
128 let r_sym =
129 self.as_elementary_symmetric_polynomials_impl(&first_vars.to_vec(), &p_tilde, e);
130 let r = self.multivariable_polynomial_ring().evaluate(
133 &r_sym.apply_map(|x| MultiPolynomial::constant(x.clone())),
134 (0..first_vars.len())
135 .map(|i| (e[i].clone(), self.elementary_symmetric(i + 1, vars)))
136 .collect(),
137 );
138 let q = self.add(p, &self.neg(&r));
143 let en = self.product(vars.iter().map(|v| self.var(v.clone())).collect());
145 let en_sym = self.var(e.get(vars.len() - 1).unwrap().clone());
146 let p_sym = self.add(
151 &r_sym,
152 &self.mul(
153 &en_sym,
154 &self.as_elementary_symmetric_polynomials_impl(
155 vars,
156 &self.try_divide(&q, &en).unwrap(),
157 e,
158 ),
159 ),
160 );
161
162 #[allow(clippy::let_and_return)]
165 p_sym
166 }
167
168 fn as_elementary_symmetric_polynomials_impl(
169 &self,
170 vars: &Vec<Variable>,
171 poly: &MultiPolynomial<RS::Set>,
172 e: &Vec<Variable>,
173 ) -> MultiPolynomial<RS::Set> {
174 let mut total = self.zero();
175 for (d, hom_poly) in self.split_by_degree(poly.clone()) {
176 if d == 0 {
177 self.add_mut(&mut total, &hom_poly);
178 } else {
179 self.add_mut(
180 &mut total,
181 &self.as_elementary_symmetric_polynomials_homogeneous_impl(vars, &hom_poly, e),
182 );
183 }
184 }
185 total
186 }
187
188 pub fn as_elementary_symmetric_polynomials_unchecked(
190 &self,
191 vars: Vec<impl Borrow<Variable>>,
192 poly: &MultiPolynomial<RS::Set>,
193 ) -> (Vec<Variable>, MultiPolynomial<RS::Set>) {
194 let e = (0..vars.len())
195 .map(|i| Variable::new(format!("e{}", ss_num(i + 1))))
196 .collect_vec();
197
198 #[cfg(debug_assertions)]
199 let vars_clone = vars.iter().map(|v| v.borrow().clone()).collect::<Vec<_>>();
200
201 let poly_sym = self.as_elementary_symmetric_polynomials_impl(
202 &vars.iter().map(|v| v.borrow().clone()).collect(),
203 poly,
204 &e,
205 );
206
207 #[cfg(debug_assertions)]
208 {
209 let poly_check = self.multivariable_polynomial_ring().evaluate(
210 &poly_sym.apply_map(|x| MultiPolynomial::constant(x.clone())),
211 (0..e.len())
212 .map(|i| (e[i].clone(), self.elementary_symmetric(i + 1, &vars_clone)))
213 .collect(),
214 );
215 assert!(self.equal(poly, &poly_check));
216 }
217
218 (e, poly_sym)
219 }
220
221 pub fn as_elementary_symmetric_polynomials(
223 &self,
224 vars: Vec<impl Borrow<Variable>>,
225 poly: &MultiPolynomial<RS::Set>,
226 ) -> Option<(Vec<Variable>, MultiPolynomial<RS::Set>)> {
227 if self.is_symmetric(vars.iter().map(|v| v.borrow().clone()).collect(), poly) {
228 Some(self.as_elementary_symmetric_polynomials_unchecked(vars, poly))
229 } else {
230 None
231 }
232 }
233}
234
235impl<R: MetaType> MultiPolynomial<R>
236where
237 R::Signature: IntegralDomainSignature,
238 MultiPolynomialStructure<R::Signature, R::Signature>:
239 SetSignature<Set = Self> + ToStringSignature,
240{
241 pub fn is_symmetric(&self, vars: Vec<impl Borrow<Variable>>) -> bool {
242 Self::structure().is_symmetric(vars, self)
243 }
244
245 pub fn elementary_symmetric(n: usize, vars: Vec<impl Borrow<Variable>>) -> MultiPolynomial<R> {
246 Self::structure().elementary_symmetric(n, &vars)
247 }
248
249 pub fn as_elementary_symmetric_polynomials(
250 &self,
251 vars: Vec<impl Borrow<Variable>>,
252 ) -> Option<(Vec<Variable>, MultiPolynomial<R>)> {
253 Self::structure().as_elementary_symmetric_polynomials(vars, self)
254 }
255
256 pub fn as_elementary_symmetric_polynomials_unchecked(
257 &self,
258 vars: Vec<impl Borrow<Variable>>,
259 ) -> (Vec<Variable>, MultiPolynomial<R>) {
260 Self::structure().as_elementary_symmetric_polynomials_unchecked(vars, self)
261 }
262}
263
264#[cfg(test)]
265mod tests {
266
267 use super::*;
268
269 use algebraeon_nzq::*;
270
271 #[test]
272 fn test_ffosp() {
273 let x_var = Variable::new("x");
274 let y_var = Variable::new("y");
275 let z_var = Variable::new("z");
276
277 let x = &MultiPolynomial::<Integer>::var(x_var.clone()).into_ergonomic();
298 let y = &MultiPolynomial::<Integer>::var(y_var.clone()).into_ergonomic();
299 let z = &MultiPolynomial::<Integer>::var(z_var.clone()).into_ergonomic();
300
301 let f =
302 (x * x * y + x * x * z + y * y * x + y * y * z + z * z * x + z * z * y).into_verbose();
303
304 println!(
305 "f={} {}",
306 f,
307 f.is_symmetric(
308 vec![x_var.clone(), y_var.clone(), z_var.clone()]
309 .into_iter()
310 .collect()
311 )
312 );
313
314 let (e, g) = f
315 .as_elementary_symmetric_polynomials(vec![&x_var, &y_var, &z_var])
316 .unwrap();
317
318 println!("{:?}", e);
319 println!("{}", g);
320 }
321
322 #[test]
323 fn run() {
324 let x_var = Variable::new("x");
325 let y_var = Variable::new("y");
326 let z_var = Variable::new("z");
327
328 let x = &MultiPolynomial::<Integer>::var(x_var.clone()).into_ergonomic();
329 let y = &MultiPolynomial::<Integer>::var(y_var.clone()).into_ergonomic();
330 let z = &MultiPolynomial::<Integer>::var(z_var.clone()).into_ergonomic();
331
332 let f = (x.pow(12) + y.pow(12) + z.pow(12)).into_verbose();
333
334 let (e, g) = f
335 .as_elementary_symmetric_polynomials(vec![&x_var, &y_var, &z_var])
336 .unwrap();
337
338 println!("{:?}", e);
339 println!("{}", g);
340 }
341}