3 * Implementation of GiNaC's symmetry definitions. */
6 * GiNaC Copyright (C) 1999-2021 Johannes Gutenberg University Mainz, Germany
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 #include "numeric.h" // for factorial()
27 #include "operators.h"
30 #include "hash_seed.h"
39 GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(symmetry, basic,
40 print_func<print_context>(&symmetry::do_print).
41 print_func<print_tree>(&symmetry::do_print_tree))
44 Some notes about the structure of a symmetry tree:
45 - The leaf nodes of the tree are of type "none", have one index, and no
46 children (of course). They are constructed by the symmetry(unsigned)
48 - Leaf nodes are the only nodes that only have one index.
49 - Container nodes contain two or more children. The "indices" set member
50 is the set union of the index sets of all children, and the "children"
51 vector stores the children themselves.
52 - The index set of each child of a "symm", "anti" or "cycl" node must
53 have the same size. It follows that the children of such a node are
54 either all leaf nodes, or all container nodes with two or more indices.
58 // default constructor
61 symmetry::symmetry() : type(none)
63 setflag(status_flags::evaluated | status_flags::expanded);
70 symmetry::symmetry(unsigned i) : type(none)
73 setflag(status_flags::evaluated | status_flags::expanded);
76 symmetry::symmetry(symmetry_type t, const symmetry &c1, const symmetry &c2) : type(t)
79 setflag(status_flags::evaluated | status_flags::expanded);
86 /** Construct object from archive_node. */
87 void symmetry::read_archive(const archive_node &n, lst &sym_lst)
89 inherited::read_archive(n, sym_lst);
91 if (!(n.find_unsigned("type", t)))
92 throw (std::runtime_error("unknown symmetry type in archive"));
93 type = (symmetry_type)t;
98 if (n.find_ex("child", e, sym_lst, i))
99 add(ex_to<symmetry>(e));
108 if (n.find_unsigned("index", u, i))
116 GINAC_BIND_UNARCHIVER(symmetry);
118 /** Archive the object. */
119 void symmetry::archive(archive_node &n) const
121 inherited::archive(n);
123 n.add_unsigned("type", type);
125 if (children.empty()) {
126 for (auto & i : indices) {
127 n.add_unsigned("index", i);
130 for (auto & i : children) {
131 n.add_ex("child", i);
137 // functions overriding virtual functions from base classes
140 int symmetry::compare_same_type(const basic & other) const
142 GINAC_ASSERT(is_a<symmetry>(other));
144 // For archiving purposes we need to have an ordering of symmetries.
145 const symmetry &othersymm = ex_to<symmetry>(other);
148 if (type > othersymm.type)
150 if (type < othersymm.type)
153 // Compare the index set.
154 size_t this_size = indices.size();
155 size_t that_size = othersymm.indices.size();
156 if (this_size > that_size)
158 if (this_size < that_size)
160 auto end = indices.end();
161 for (auto i=indices.begin(),j=othersymm.indices.begin(); i!=end; ++i,++j) {
168 // Compare the children.
169 if (children.size() > othersymm.children.size())
171 if (children.size() < othersymm.children.size())
173 for (size_t i=0; i<children.size(); ++i) {
174 int cmpval = ex_to<symmetry>(children[i])
175 .compare_same_type(ex_to<symmetry>(othersymm.children[i]));
183 unsigned symmetry::calchash() const
185 unsigned v = make_hash_seed(typeid(*this));
189 if (!indices.empty())
190 v ^= *(indices.begin());
192 for (auto & i : children) {
198 if (flags & status_flags::evaluated) {
199 setflag(status_flags::hash_calculated);
206 void symmetry::do_print(const print_context & c, unsigned level) const
208 if (children.empty()) {
209 if (indices.size() > 0)
210 c.s << *(indices.begin());
215 case none: c.s << '!'; break;
216 case symmetric: c.s << '+'; break;
217 case antisymmetric: c.s << '-'; break;
218 case cyclic: c.s << '@'; break;
219 default: c.s << '?'; break;
222 size_t num = children.size();
223 for (size_t i=0; i<num; i++) {
224 children[i].print(c);
232 void symmetry::do_print_tree(const print_tree & c, unsigned level) const
234 c.s << std::string(level, ' ') << class_name() << " @" << this
235 << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec
239 case none: c.s << "none"; break;
240 case symmetric: c.s << "symm"; break;
241 case antisymmetric: c.s << "anti"; break;
242 case cyclic: c.s << "cycl"; break;
243 default: c.s << "<unknown>"; break;
246 c.s << ", indices=(";
247 if (!indices.empty()) {
248 auto i = indices.begin(), end = indices.end();
256 for (auto & i : children) {
257 i.print(c, level + c.delta_indent);
262 // non-virtual functions in this class
265 bool symmetry::has_nonsymmetric() const
267 if (type == antisymmetric || type == cyclic)
270 for (auto & i : children)
271 if (ex_to<symmetry>(i).has_nonsymmetric())
277 bool symmetry::has_cyclic() const
282 for (auto & i : children)
283 if (ex_to<symmetry>(i).has_cyclic())
289 symmetry &symmetry::add(const symmetry &c)
291 // All children must have the same number of indices
292 if (type != none && !children.empty()) {
293 GINAC_ASSERT(is_exactly_a<symmetry>(children[0]));
294 if (ex_to<symmetry>(children[0]).indices.size() != c.indices.size())
295 throw (std::logic_error("symmetry:add(): children must have same number of indices"));
298 // Compute union of indices and check whether the two sets are disjoint
299 std::set<unsigned> un;
300 set_union(indices.begin(), indices.end(), c.indices.begin(), c.indices.end(), inserter(un, un.begin()));
301 if (un.size() != indices.size() + c.indices.size())
302 throw (std::logic_error("symmetry::add(): the same index appears in more than one child"));
308 children.push_back(c);
312 void symmetry::validate(unsigned n)
314 if (indices.upper_bound(n - 1) != indices.end())
315 throw (std::range_error("symmetry::verify(): index values are out of range"));
316 if (type != none && indices.empty()) {
317 for (unsigned i=0; i<n; i++)
326 static const symmetry & index0()
328 static ex s = dynallocate<symmetry>(0);
329 return ex_to<symmetry>(s);
332 static const symmetry & index1()
334 static ex s = dynallocate<symmetry>(1);
335 return ex_to<symmetry>(s);
338 static const symmetry & index2()
340 static ex s = dynallocate<symmetry>(2);
341 return ex_to<symmetry>(s);
344 static const symmetry & index3()
346 static ex s = dynallocate<symmetry>(3);
347 return ex_to<symmetry>(s);
350 const symmetry & not_symmetric()
352 static ex s = dynallocate<symmetry>();
353 return ex_to<symmetry>(s);
356 const symmetry & symmetric2()
358 static ex s = dynallocate<symmetry>(symmetry::symmetric, index0(), index1());
359 return ex_to<symmetry>(s);
362 const symmetry & symmetric3()
364 static ex s = dynallocate<symmetry>(symmetry::symmetric, index0(), index1()).add(index2());
365 return ex_to<symmetry>(s);
368 const symmetry & symmetric4()
370 static ex s = dynallocate<symmetry>(symmetry::symmetric, index0(), index1()).add(index2()).add(index3());
371 return ex_to<symmetry>(s);
374 const symmetry & antisymmetric2()
376 static ex s = dynallocate<symmetry>(symmetry::antisymmetric, index0(), index1());
377 return ex_to<symmetry>(s);
380 const symmetry & antisymmetric3()
382 static ex s = dynallocate<symmetry>(symmetry::antisymmetric, index0(), index1()).add(index2());
383 return ex_to<symmetry>(s);
386 const symmetry & antisymmetric4()
388 static ex s = dynallocate<symmetry>(symmetry::antisymmetric, index0(), index1()).add(index2()).add(index3());
389 return ex_to<symmetry>(s);
393 exvector::iterator v;
396 sy_is_less(exvector::iterator v_) : v(v_) {}
398 bool operator() (const ex &lh, const ex &rh) const
400 GINAC_ASSERT(is_exactly_a<symmetry>(lh));
401 GINAC_ASSERT(is_exactly_a<symmetry>(rh));
402 GINAC_ASSERT(ex_to<symmetry>(lh).indices.size() == ex_to<symmetry>(rh).indices.size());
403 auto ait = ex_to<symmetry>(lh).indices.begin(), aitend = ex_to<symmetry>(lh).indices.end(), bit = ex_to<symmetry>(rh).indices.begin();
404 while (ait != aitend) {
405 int cmpval = v[*ait].compare(v[*bit]);
417 exvector::iterator v;
422 sy_swap(exvector::iterator v_, bool &s) : v(v_), swapped(s) {}
424 void operator() (const ex &lh, const ex &rh)
426 GINAC_ASSERT(is_exactly_a<symmetry>(lh));
427 GINAC_ASSERT(is_exactly_a<symmetry>(rh));
428 GINAC_ASSERT(ex_to<symmetry>(lh).indices.size() == ex_to<symmetry>(rh).indices.size());
429 auto ait = ex_to<symmetry>(lh).indices.begin(), aitend = ex_to<symmetry>(lh).indices.end(), bit = ex_to<symmetry>(rh).indices.begin();
430 while (ait != aitend) {
431 v[*ait].swap(v[*bit]);
438 int canonicalize(exvector::iterator v, const symmetry &symm)
440 // Less than two elements? Then do nothing
441 if (symm.indices.size() < 2)
442 return std::numeric_limits<int>::max();
444 // Canonicalize children first
445 bool something_changed = false;
447 auto first = symm.children.begin(), last = symm.children.end();
448 while (first != last) {
449 GINAC_ASSERT(is_exactly_a<symmetry>(*first));
450 int child_sign = canonicalize(v, ex_to<symmetry>(*first));
453 if (child_sign != std::numeric_limits<int>::max()) {
454 something_changed = true;
460 // Now reorder the children
461 first = symm.children.begin();
463 case symmetry::symmetric:
464 // Sort the children in ascending order
465 shaker_sort(first, last, sy_is_less(v), sy_swap(v, something_changed));
467 case symmetry::antisymmetric:
468 // Sort the children in ascending order, keeping track of the signum
469 sign *= permutation_sign(first, last, sy_is_less(v), sy_swap(v, something_changed));
473 case symmetry::cyclic:
474 // Permute the smallest child to the front
475 cyclic_permutation(first, last, min_element(first, last, sy_is_less(v)), sy_swap(v, something_changed));
480 return something_changed ? sign : std::numeric_limits<int>::max();
484 // Symmetrize/antisymmetrize over a vector of objects
485 static ex symm(const ex & e, exvector::const_iterator first, exvector::const_iterator last, bool asymmetric)
487 // Need at least 2 objects for this operation
488 unsigned num = last - first;
492 // Transform object vector to a lst (for subs())
493 lst orig_lst(first, last);
495 // Create index vectors for permutation
496 unsigned *iv = new unsigned[num], *iv2;
497 for (unsigned i=0; i<num; i++)
499 iv2 = (asymmetric ? new unsigned[num] : nullptr);
501 // Loop over all permutations (the first permutation, which is the
502 // identity, is unrolled)
505 while (std::next_permutation(iv, iv + num)) {
507 for (unsigned i=0; i<num; i++)
508 new_lst.append(orig_lst.op(iv[i]));
509 ex term = e.subs(orig_lst, new_lst, subs_options::no_pattern|subs_options::no_index_renaming);
511 memcpy(iv2, iv, num * sizeof(unsigned));
512 term *= permutation_sign(iv2, iv2 + num);
514 sum_v.push_back(term);
516 ex sum = dynallocate<add>(sum_v);
521 return sum / factorial(numeric(num));
524 ex symmetrize(const ex & e, exvector::const_iterator first, exvector::const_iterator last)
526 return symm(e, first, last, false);
529 ex antisymmetrize(const ex & e, exvector::const_iterator first, exvector::const_iterator last)
531 return symm(e, first, last, true);
534 ex symmetrize_cyclic(const ex & e, exvector::const_iterator first, exvector::const_iterator last)
536 // Need at least 2 objects for this operation
537 unsigned num = last - first;
541 // Transform object vector to a lst (for subs())
542 lst orig_lst(first, last);
543 lst new_lst = orig_lst;
545 // Loop over all cyclic permutations (the first permutation, which is
546 // the identity, is unrolled)
548 for (unsigned i=0; i<num-1; i++) {
549 ex perm = new_lst.op(0);
550 new_lst.remove_first().append(perm);
551 sum += e.subs(orig_lst, new_lst, subs_options::no_pattern|subs_options::no_index_renaming);
556 /** Symmetrize expression over a list of objects (symbols, indices). */
557 ex ex::symmetrize(const lst & l) const
559 exvector v(l.begin(), l.end());
560 return symm(*this, v.begin(), v.end(), false);
563 /** Antisymmetrize expression over a list of objects (symbols, indices). */
564 ex ex::antisymmetrize(const lst & l) const
566 exvector v(l.begin(), l.end());
567 return symm(*this, v.begin(), v.end(), true);
570 /** Symmetrize expression by cyclic permutation over a list of objects
571 * (symbols, indices). */
572 ex ex::symmetrize_cyclic(const lst & l) const
574 exvector v(l.begin(), l.end());
575 return GiNaC::symmetrize_cyclic(*this, v.begin(), v.end());