glucat 0.13.0
PyClical.pyx
Go to the documentation of this file.
1# -*- coding: utf-8 -*-
2# cython: language_level=3
3# distutils: language = c++
4#
5# PyClical: Python interface to GluCat:
6# Generic library of universal Clifford algebra templates
7#
8# PyClical.pyx: Cython definitions visible from Python.
9#
10# copyright : (C) 2008-2021 by Paul C. Leopardi
11#
12# This library is free software: you can redistribute it and/or modify
13# it under the terms of the GNU Lesser General Public License as published
14# by the Free Software Foundation, either version 3 of the License, or
15# (at your option) any later version.
16#
17# This library is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20# GNU Lesser General Public License for more details.
21#
22# You should have received a copy of the GNU Lesser General Public License
23# along with this library. If not, see <http://www.gnu.org/licenses/>.
24
25# References for definitions:
26# [DL]:
27# C. Doran and A. Lasenby, "Geometric algebra for physicists", Cambridge, 2003.
28
29import math
30import numbers
31import collections
32
33from PyClical cimport *
34
35__version__ = str(glucat_package_version,'utf-8')
36
37# Forward reference
38cdef class index_set
39
40cdef inline IndexSet toIndexSet(obj):
41 """
42 Return the C++ IndexSet instance wrapped by index_set(obj).
43 """
44 return index_set(obj).instance[0]
45
46cdef class index_set:
47 """
48 Python class index_set wraps C++ class IndexSet.
49 """
50 cdef IndexSet *instance # Wrapped instance of C++ class IndexSet.
51
52 cdef inline wrap(index_set self, IndexSet other):
53 """
54 Wrap an instance of the C++ class IndexSet.
55 """
56 self.instance[0] = other
57 return self
58
59 cdef inline IndexSet unwrap(index_set self):
60 """
61 Return the wrapped C++ IndexSet instance.
62 """
63 return self.instance[0]
64
65 cpdef copy(index_set self):
66 """
67 Copy this index_set object.
68
69 >>> s=index_set(1); t=s.copy(); print(t)
70 {1}
71 """
72 return index_set(self)
73
74 def __cinit__(self, other = 0):
75 """
76 Construct an object of type index_set.
77
78 >>> print(index_set(1))
79 {1}
80 >>> print(index_set({1,2}))
81 {1,2}
82 >>> print(index_set(index_set({1,2})))
83 {1,2}
84 >>> print(index_set({1,2}))
85 {1,2}
86 >>> print(index_set({1,2,1}))
87 {1,2}
88 >>> print(index_set("{1,2,1}"))
89 {1,2}
90 >>> print(index_set(""))
91 {}
92 """
93 error_msg_prefix = "Cannot initialize index_set object from"
94 if isinstance(other, index_set):
95 self.instance = new IndexSet((<index_set>other).unwrap())
96 elif isinstance(other, numbers.Integral):
97 self.instance = new IndexSet(<int>other)
98 elif isinstance(other, (set, frozenset)):
99 try:
100 self.instance = new IndexSet()
101 for idx in other:
102 self[idx] = True
103 except IndexError:
104 raise IndexError(error_msg_prefix + " invalid " + repr(other) + ".")
105 except (RuntimeError, TypeError):
106 raise ValueError(error_msg_prefix + " invalid " + repr(other) + ".")
107 elif isinstance(other, str):
108 try:
109 bother = other.encode("UTF-8")
110 self.instance = new IndexSet(<char *>bother)
111 except RuntimeError:
112 raise ValueError(error_msg_prefix + " invalid string " + repr(other) + ".")
113 else:
114 raise TypeError(error_msg_prefix + " " + str(type(other)) + ".")
115
116 def __dealloc__(self):
117 """
118 Clean up by deallocating the instance of C++ class IndexSet.
119 """
120 del self.instance
121
122 def __richcmp__(lhs, rhs, int op):
123 """
124 Compare two objects of class index_set.
125
126 >>> index_set(1) == index_set({1})
127 True
128 >>> index_set({1}) != index_set({1})
129 False
130 >>> index_set({1}) != index_set({2})
131 True
132 >>> index_set({1}) == index_set({2})
133 False
134 >>> index_set({1}) < index_set({2})
135 True
136 >>> index_set({1}) <= index_set({2})
137 True
138 >>> index_set({1}) > index_set({2})
139 False
140 >>> index_set({1}) >= index_set({2})
141 False
142 """
143 if (lhs is None) or (rhs is None):
144 eq = bool(lhs is rhs)
145 if op == 2: # ==
146 return eq
147 elif op == 3: # !=
148 return not eq
149 else:
150 if op == 0: # <
151 return False
152 elif op == 1: # <=
153 return eq
154 elif op == 4: # >
155 return False
156 elif op == 5: # >=
157 return eq
158 else:
159 return NotImplemented
160 else:
161 eq = bool( toIndexSet(lhs) == toIndexSet(rhs) )
162 if op == 2: # ==
163 return eq
164 elif op == 3: # !=
165 return not eq
166 else:
167 lt = bool( toIndexSet(lhs) < toIndexSet(rhs) )
168 if op == 0: # <
169 return lt
170 elif op == 1: # <=
171 return lt or eq
172 elif op == 4: # >
173 return not (lt or eq)
174 elif op == 5: # >=
175 return not lt
176 else:
177 return NotImplemented
178
179 def __setitem__(self, idx, val):
180 """
181 Set the value of an index_set object at index idx to value val.
182
183 >>> s=index_set({1}); s[2] = True; print(s)
184 {1,2}
185 >>> s=index_set({1,2}); s[1] = False; print(s)
186 {2}
187 """
188 self.instance.set(idx, val)
189 return
190
191 def __getitem__(self, idx):
192 """
193 Get the value of an index_set object at an index.
194
195 >>> index_set({1})[1]
196 True
197 >>> index_set({1})[2]
198 False
199 >>> index_set({2})[-1]
200 False
201 >>> index_set({2})[1]
202 False
203 >>> index_set({2})[2]
204 True
205 >>> index_set({2})[33]
206 False
207 """
208 return self.instance.getitem(idx)
209
210 def __contains__(self, idx):
211 """
212 Check that an index_set object contains the index idx: idx in self.
213
214 >>> 1 in index_set({1})
215 True
216 >>> 2 in index_set({1})
217 False
218 >>> -1 in index_set({2})
219 False
220 >>> 1 in index_set({2})
221 False
222 >>> 2 in index_set({2})
223 True
224 >>> 33 in index_set({2})
225 False
226 """
227 return self.instance.getitem(idx)
228
229 def __iter__(self):
230 """
231 Iterate over the indices of an index_set.
232
233 >>> for i in index_set({-3,4,7}):print(i, end=",")
234 -3,4,7,
235 """
236 for idx in range(self.min(), self.max()+1):
237 if idx in self:
238 yield idx
239
240 def __invert__(self):
241 """
242 Set complement: not.
243
244 >>> print(~index_set({-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}))
245 {-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32}
246 """
247 return index_set().wrap( self.instance.invert() )
248
249 def __xor__(lhs, rhs):
250 """
251 Symmetric set difference: exclusive or.
252
253 >>> print(index_set({1}) ^ index_set({2}))
254 {1,2}
255 >>> print(index_set({1,2}) ^ index_set({2}))
256 {1}
257 """
258 return index_set().wrap( toIndexSet(lhs) ^ toIndexSet(rhs) )
259
260 def __ixor__(self, rhs):
261 """
262 Symmetric set difference: exclusive or.
263
264 >>> x = index_set({1}); x ^= index_set({2}); print(x)
265 {1,2}
266 >>> x = index_set({1,2}); x ^= index_set({2}); print(x)
267 {1}
268 """
269 return self.wrap( self.unwrap() ^ toIndexSet(rhs) )
270
271 def __and__(lhs, rhs):
272 """
273 Set intersection: and.
274
275 >>> print(index_set({1}) & index_set({2}))
276 {}
277 >>> print(index_set({1,2}) & index_set({2}))
278 {2}
279 """
280 return index_set().wrap( toIndexSet(lhs) & toIndexSet(rhs) )
281
282 def __iand__(self, rhs):
283 """
284 Set intersection: and.
285
286 >>> x = index_set({1}); x &= index_set({2}); print(x)
287 {}
288 >>> x = index_set({1,2}); x &= index_set({2}); print(x)
289 {2}
290 """
291 return self.wrap( self.unwrap() & toIndexSet(rhs) )
292
293 def __or__(lhs, rhs):
294 """
295 Set union: or.
296
297 >>> print(index_set({1}) | index_set({2}))
298 {1,2}
299 >>> print(index_set({1,2}) | index_set({2}))
300 {1,2}
301 """
302 return index_set().wrap( toIndexSet(lhs) | toIndexSet(rhs) )
303
304 def __ior__(self, rhs):
305 """
306 Set union: or.
307
308 >>> x = index_set({1}); x |= index_set({2}); print(x)
309 {1,2}
310 >>> x = index_set({1,2}); x |= index_set({2}); print(x)
311 {1,2}
312 """
313 return self.wrap( self.unwrap() | toIndexSet(rhs) )
314
315 def count(self):
316 """
317 Cardinality: Number of indices included in set.
318
319 >>> index_set({-1,1,2}).count()
320 3
321 """
322 return self.instance.count()
323
324 def count_neg(self):
325 """
326 Number of negative indices included in set.
327
328 >>> index_set({-1,1,2}).count_neg()
329 1
330 """
331 return self.instance.count_neg()
332
333 def count_pos(self):
334 """
335 Number of positive indices included in set.
336
337 >>> index_set({-1,1,2}).count_pos()
338 2
339 """
340 return self.instance.count_pos()
341
342 def min(self):
343 """
344 Minimum member.
345
346 >>> index_set({-1,1,2}).min()
347 -1
348 """
349 return self.instance.min()
350
351 def max(self):
352 """
353 Maximum member.
354
355 >>> index_set({-1,1,2}).max()
356 2
357 """
358 return self.instance.max()
359
360 def hash_fn(self):
361 """
362 Hash function.
363 """
364 return self.instance.hash_fn()
365
366 def sign_of_mult(self, rhs):
367 """
368 Sign of geometric product of two Clifford basis elements.
369
370 >>> s = index_set({1,2}); t=index_set({-1}); s.sign_of_mult(t)
371 1
372 """
373 return self.instance.sign_of_mult(toIndexSet(rhs))
374
375 def sign_of_square(self):
376 """
377 Sign of geometric square of a Clifford basis element.
378
379 >>> s = index_set({1,2}); s.sign_of_square()
380 -1
381 """
382 return self.instance.sign_of_square()
383
384 def __repr__(self):
385 """
386 The “official” string representation of self.
387
388 >>> index_set({1,2}).__repr__()
389 'index_set({1,2})'
390 >>> repr(index_set({1,2}))
391 'index_set({1,2})'
392 """
393 return index_set_to_repr( self.unwrap() ).decode()
394
395 def __str__(self):
396 """
397 The “informal” string representation of self.
398
399 >>> index_set({1,2}).__str__()
400 '{1,2}'
401 >>> str(index_set({1,2}))
402 '{1,2}'
403 """
404 return index_set_to_str( self.unwrap() ).decode()
405
407 """
408 Tests for functions that Doctest cannot see.
409
410 For index_set.__cinit__: Construct index_set.
411
412 >>> print(index_set(1))
413 {1}
414 >>> print(index_set({1,2}))
415 {1,2}
416 >>> print(index_set(index_set({1,2})))
417 {1,2}
418 >>> print(index_set({1,2}))
419 {1,2}
420 >>> print(index_set({1,2,1}))
421 {1,2}
422 >>> print(index_set({1,2,1}))
423 {1,2}
424 >>> print(index_set(""))
425 {}
426 >>> print(index_set("{"))
427 Traceback (most recent call last):
428 ...
429 ValueError: Cannot initialize index_set object from invalid string '{'.
430 >>> print(index_set("{1"))
431 Traceback (most recent call last):
432 ...
433 ValueError: Cannot initialize index_set object from invalid string '{1'.
434 >>> print(index_set("{1,2,100}"))
435 Traceback (most recent call last):
436 ...
437 ValueError: Cannot initialize index_set object from invalid string '{1,2,100}'.
438 >>> print(index_set({1,2,100}))
439 Traceback (most recent call last):
440 ...
441 IndexError: Cannot initialize index_set object from invalid {1, 2, 100}.
442 >>> print(index_set([1,2]))
443 Traceback (most recent call last):
444 ...
445 TypeError: Cannot initialize index_set object from <class 'list'>.
446
447 For index_set.__richcmp__: Compare two objects of class index_set.
448
449 >>> index_set(1) == index_set({1})
450 True
451 >>> index_set({1}) != index_set({1})
452 False
453 >>> index_set({1}) != index_set({2})
454 True
455 >>> index_set({1}) == index_set({2})
456 False
457 >>> index_set({1}) < index_set({2})
458 True
459 >>> index_set({1}) <= index_set({2})
460 True
461 >>> index_set({1}) > index_set({2})
462 False
463 >>> index_set({1}) >= index_set({2})
464 False
465 >>> None == index_set({1,2})
466 False
467 >>> None != index_set({1,2})
468 True
469 >>> None < index_set({1,2})
470 False
471 >>> None <= index_set({1,2})
472 False
473 >>> None > index_set({1,2})
474 False
475 >>> None >= index_set({1,2})
476 False
477 >>> index_set({1,2}) == None
478 False
479 >>> index_set({1,2}) != None
480 True
481 >>> index_set({1,2}) < None
482 False
483 >>> index_set({1,2}) <= None
484 False
485 >>> index_set({1,2}) > None
486 False
487 >>> index_set({1,2}) >= None
488 False
489 """
490 return
491
492cpdef inline compare(lhs,rhs):
493 """
494 "lexicographic compare" eg. {3,4,5} is less than {3,7,8};
495 -1 if a<b, +1 if a>b, 0 if a==b.
496
497 >>> compare(index_set({1,2}),index_set({-1,3}))
498 -1
499 >>> compare(index_set({-1,4}),index_set({-1,3}))
500 1
501 """
502 return glucat.compare( toIndexSet(lhs), toIndexSet(rhs) )
503
504cpdef inline min_neg(obj):
505 """
506 Minimum negative index, or 0 if none.
507
508 >>> min_neg(index_set({1,2}))
509 0
510 """
511 return glucat.min_neg( toIndexSet(obj) )
512
513cpdef inline max_pos(obj):
514 """
515 Maximum positive index, or 0 if none.
516
517 >>> max_pos(index_set({1,2}))
518 2
519 """
520 return glucat.max_pos( toIndexSet(obj) )
521
522cdef inline vector[scalar_t] list_to_vector(lst):
523 """
524 Create a C++ std:vector[scalar_t] from an iterable Python object.
525 """
526 cdef vector[scalar_t] v
527 for s in lst:
528 v.push_back(<scalar_t>s)
529 return v
530
531# Forward reference.
532cdef class clifford
533
534cdef inline Clifford toClifford(obj):
535 return clifford(obj).instance[0]
536
537cdef class clifford:
538 """
539 Python class clifford wraps C++ class Clifford.
540 """
541 cdef Clifford *instance # Wrapped instance of C++ class Clifford.
542
543 cdef inline wrap(clifford self, Clifford other):
544 """
545 Wrap an instance of the C++ class Clifford.
546 """
547 self.instance[0] = other
548 return self
549
550 cdef inline Clifford unwrap(clifford self):
551 """
552 Return the wrapped C++ Clifford instance.
553 """
554 return self.instance[0]
555
556 cpdef copy(clifford self):
557 """
558 Copy this clifford object.
559
560 >>> x=clifford("1{2}"); y=x.copy(); print(y)
561 {2}
562 """
563 return clifford(self)
564
565 def __cinit__(self, other = 0, ixt = None):
566 """
567 Construct an object of type clifford.
568
569 >>> print(clifford(2))
570 2
571 >>> print(clifford(2.0))
572 2
573 >>> print(clifford(1.0e-1))
574 0.1
575 >>> print(clifford("2"))
576 2
577 >>> print(clifford("2{1,2,3}"))
578 2{1,2,3}
579 >>> print(clifford(clifford("2{1,2,3}")))
580 2{1,2,3}
581 >>> print(clifford("-{1}"))
582 -{1}
583 >>> print(clifford(2,index_set({1,2})))
584 2{1,2}
585 >>> print(clifford([2,3],index_set({1,2})))
586 2{1}+3{2}
587 """
588 error_msg_prefix = "Cannot initialize clifford object from"
589 if ixt is None:
590 try:
591 if isinstance(other, clifford):
592 self.instance = new Clifford((<clifford>other).unwrap())
593 elif isinstance(other, index_set):
594 self.instance = new Clifford((<index_set>other).unwrap(), <scalar_t>1.0)
595 elif isinstance(other, numbers.Real):
596 self.instance = new Clifford(<scalar_t>other)
597 elif isinstance(other, str):
598 try:
599 bother = other.encode("UTF-8")
600 self.instance = new Clifford(<char *>bother)
601 except RuntimeError:
602 raise ValueError(error_msg_prefix + " invalid string " + repr(other) + ".")
603 else:
604 raise TypeError(error_msg_prefix + " " + str(type(other)) + ".")
605 except RuntimeError as err:
606 raise ValueError(error_msg_prefix + " " + str(type(other))
607 + " value " + repr(other) + ":"
608 + "\n\t" + str(err))
609 elif isinstance(ixt, index_set):
610 if isinstance(other, numbers.Real):
611 self.instance = new Clifford((<index_set>ixt).unwrap(), <scalar_t>other)
612 elif isinstance(other, collections.abc.Sequence):
613 self.instance = new Clifford(list_to_vector(other), (<index_set>ixt).unwrap())
614 else:
615 raise TypeError(error_msg_prefix + " (" + str(type(other))
616 + ", " + repr(ixt) + ").")
617 else:
618 raise TypeError(error_msg_prefix + " (" + str(type(other))
619 + ", " + str(type(ixt)) + ").")
620
621 def __dealloc__(self):
622 """
623 Clean up by deallocating the instance of C++ class Clifford.
624 """
625 del self.instance
626
627 def __contains__(self, x):
628 """
629 Not applicable.
630
631 >>> x=clifford(index_set({-3,4,7})); -3 in x
632 Traceback (most recent call last):
633 ...
634 TypeError: Not applicable.
635 """
636 raise TypeError("Not applicable.")
637
638 def __iter__(self):
639 """
640 Not applicable.
641
642 >>> for a in clifford(index_set({-3,4,7})):print(a, end=",")
643 Traceback (most recent call last):
644 ...
645 TypeError: Not applicable.
646 """
647 raise TypeError("Not applicable.")
648
649 def reframe(self, ixt):
650 """
651 Put self into a larger frame, containing the union of self.frame() and index set ixt.
652 This can be used to make multiplication faster, by multiplying within a common frame.
653
654 >>> clifford("2+3{1}").reframe(index_set({1,2,3}))
655 clifford("2+3{1}")
656 >>> s=index_set({1,2,3});t=index_set({-3,-2,-1});x=random_clifford(s); x.reframe(t).frame() == (s|t);
657 True
658 """
659 error_msg_prefix = "Cannot reframe"
660 if isinstance(ixt, index_set):
661 try:
662 result = clifford()
663 result.instance = new Clifford(self.unwrap(), (<index_set>ixt).unwrap())
664 except RuntimeError as err:
665 raise ValueError(error_msg_prefix + " from " + str(self) + " to frame "
666 + str(ixt) + ":"
667 + "\n\t" + str(err))
668 else:
669 raise TypeError(error_msg_prefix + " using (" + str(type(ixt)) + ").")
670 return result
671
672 def __richcmp__(lhs, rhs, int op):
673 """
674 Compare objects of type clifford.
675
676 >>> clifford("{1}") == clifford("1{1}")
677 True
678 >>> clifford("{1}") != clifford("1.0{1}")
679 False
680 >>> clifford("{1}") != clifford("1.0")
681 True
682 >>> clifford("{1,2}") == None
683 False
684 >>> clifford("{1,2}") != None
685 True
686 >>> None == clifford("{1,2}")
687 False
688 >>> None != clifford("{1,2}")
689 True
690 """
691 if op == 2: # ==
692 if (lhs is None) or (rhs is None):
693 return bool(lhs is rhs)
694 else:
695 return bool( toClifford(lhs) == toClifford(rhs) )
696 elif op == 3: # !=
697 if (lhs is None) or (rhs is None):
698 return not bool(lhs is rhs)
699 else:
700 return bool( toClifford(lhs) != toClifford(rhs) )
701 elif isinstance(lhs, clifford) or isinstance(rhs, clifford):
702 raise TypeError("This comparison operator is not implemented for "
703 + str(type(lhs)) + ", " + str(type(rhs)) + ".")
704 else:
705 return NotImplemented
706
707 def __getitem__(self, ixt):
708 """
709 Subscripting: map from index set to scalar coordinate.
710
711 >>> clifford("{1}")[index_set(1)]
712 1.0
713 >>> clifford("{1}")[index_set({1})]
714 1.0
715 >>> clifford("{1}")[index_set({1,2})]
716 0.0
717 >>> clifford("2{1,2}")[index_set({1,2})]
718 2.0
719 """
720 return self.instance.getitem(toIndexSet(ixt))
721
722 def __neg__(self):
723 """
724 Unary -.
725
726 >>> print(-clifford("{1}"))
727 -{1}
728 """
729 return clifford().wrap( self.instance.neg() )
730
731 def __pos__(self):
732 """
733 Unary +.
734
735 >>> print(+clifford("{1}"))
736 {1}
737 """
738 return clifford(self)
739
740 def __add__(lhs, rhs):
741 """
742 Geometric sum.
743
744 >>> print(clifford(1) + clifford("{2}"))
745 1+{2}
746 >>> print(clifford("{1}") + clifford("{2}"))
747 {1}+{2}
748 """
749 return clifford().wrap( toClifford(lhs) + toClifford(rhs) )
750
751 def __radd__(rhs, lhs):
752 """
753 Geometric sum.
754
755 >>> print(1 + clifford("{2}"))
756 1+{2}
757 """
758 return clifford().wrap( toClifford(lhs) + toClifford(rhs) )
759
760 def __iadd__(self, rhs):
761 """
762 Geometric sum.
763
764 >>> x = clifford(1); x += clifford("{2}"); print(x)
765 1+{2}
766 """
767 return self.wrap( self.unwrap() + toClifford(rhs) )
768
769 def __sub__(lhs, rhs):
770 """
771 Geometric difference.
772
773 >>> print(clifford(1) - clifford("{2}"))
774 1-{2}
775 >>> print(clifford("{1}") - clifford("{2}"))
776 {1}-{2}
777 """
778 return clifford().wrap( toClifford(lhs) - toClifford(rhs) )
779
780 def __rsub__(rhs, lhs):
781 """
782 Geometric difference.
783
784 >>> print(1 - clifford("{2}"))
785 1-{2}
786 """
787 return clifford().wrap( toClifford(lhs) - toClifford(rhs) )
788
789 def __isub__(self, rhs):
790 """
791 Geometric difference.
792
793 >>> x = clifford(1); x -= clifford("{2}"); print(x)
794 1-{2}
795 """
796 return self.wrap( self.unwrap() - toClifford(rhs) )
797
798 def __mul__(lhs, rhs):
799 """
800 Geometric product.
801
802 >>> print(clifford("{1}") * clifford("{2}"))
803 {1,2}
804 >>> print(clifford(2) * clifford("{2}"))
805 2{2}
806 >>> print(clifford("{1}") * clifford("{1,2}"))
807 {2}
808 """
809 return clifford().wrap( toClifford(lhs) * toClifford(rhs) )
810
811 def __rmul__(rhs, lhs):
812 """
813 Geometric product.
814
815 >>> print(2 * clifford("{2}"))
816 2{2}
817 """
818 return clifford().wrap( toClifford(lhs) * toClifford(rhs) )
819
820 def __imul__(self, rhs):
821 """
822 Geometric product.
823
824 >>> x = clifford(2); x *= clifford("{2}"); print(x)
825 2{2}
826 >>> x = clifford("{1}"); x *= clifford("{2}"); print(x)
827 {1,2}
828 >>> x = clifford("{1}"); x *= clifford("{1,2}"); print(x)
829 {2}
830 """
831 return self.wrap( self.unwrap() * toClifford(rhs) )
832
833 def __mod__(lhs, rhs):
834 """
835 Contraction.
836
837 >>> print(clifford("{1}") % clifford("{2}"))
838 0
839 >>> print(clifford(2) % clifford("{2}"))
840 2{2}
841 >>> print(clifford("{1}") % clifford("{1}"))
842 1
843 >>> print(clifford("{1}") % clifford("{1,2}"))
844 {2}
845 """
846 return clifford().wrap( toClifford(lhs) % toClifford(rhs) )
847
848 def __rmod__(rhs, lhs):
849 """
850 Contraction.
851
852 >>> print(2 % clifford("{2}"))
853 2{2}
854 """
855 return clifford().wrap( toClifford(lhs) % toClifford(rhs) )
856
857 def __imod__(self, rhs):
858 """
859 Contraction.
860
861 >>> x = clifford("{1}"); x %= clifford("{2}"); print(x)
862 0
863 >>> x = clifford(2); x %= clifford("{2}"); print(x)
864 2{2}
865 >>> x = clifford("{1}"); x %= clifford("{1}"); print(x)
866 1
867 >>> x = clifford("{1}"); x %= clifford("{1,2}"); print(x)
868 {2}
869 """
870 return self.wrap( self.unwrap() % toClifford(rhs) )
871
872 def __and__(lhs, rhs):
873 """
874 Inner product.
875
876 >>> print(clifford("{1}") & clifford("{2}"))
877 0
878 >>> print(clifford(2) & clifford("{2}"))
879 0
880 >>> print(clifford("{1}") & clifford("{1}"))
881 1
882 >>> print(clifford("{1}") & clifford("{1,2}"))
883 {2}
884 """
885 return clifford().wrap( toClifford(lhs) & toClifford(rhs) )
886
887 def __rand__(rhs, lhs):
888 """
889 Inner product.
890
891 >>> print(2 & clifford("{2}"))
892 0
893 """
894 return clifford().wrap( toClifford(lhs) & toClifford(rhs) )
895
896 def __iand__(self, rhs):
897 """
898 Inner product.
899
900 >>> x = clifford("{1}"); x &= clifford("{2}"); print(x)
901 0
902 >>> x = clifford(2); x &= clifford("{2}"); print(x)
903 0
904 >>> x = clifford("{1}"); x &= clifford("{1}"); print(x)
905 1
906 >>> x = clifford("{1}"); x &= clifford("{1,2}"); print(x)
907 {2}
908 """
909 return self.wrap( self.unwrap() & toClifford(rhs) )
910
911 def __xor__(lhs, rhs):
912 """
913 Outer product.
914
915 >>> print(clifford("{1}") ^ clifford("{2}"))
916 {1,2}
917 >>> print(clifford(2) ^ clifford("{2}"))
918 2{2}
919 >>> print(clifford("{1}") ^ clifford("{1}"))
920 0
921 >>> print(clifford("{1}") ^ clifford("{1,2}"))
922 0
923 """
924 return clifford().wrap( toClifford(lhs) ^ toClifford(rhs) )
925
926 def __rxor__(rhs, lhs):
927 """
928 Outer product.
929
930 >>> print(2 ^ clifford("{2}"))
931 2{2}
932 """
933 return clifford().wrap( toClifford(lhs) ^ toClifford(rhs) )
934
935 def __ixor__(self, rhs):
936 """
937 Outer product.
938
939 >>> x = clifford("{1}"); x ^= clifford("{2}"); print(x)
940 {1,2}
941 >>> x = clifford(2); x ^= clifford("{2}"); print(x)
942 2{2}
943 >>> x = clifford("{1}"); x ^= clifford("{1}"); print(x)
944 0
945 >>> x = clifford("{1}"); x ^= clifford("{1,2}"); print(x)
946 0
947 """
948 return self.wrap( self.unwrap() ^ toClifford(rhs) )
949
950 def __truediv__(lhs, rhs):
951 """
952 Geometric quotient.
953
954 >>> print(clifford("{1}") / clifford("{2}"))
955 {1,2}
956 >>> print(clifford(2) / clifford("{2}"))
957 2{2}
958 >>> print(clifford("{1}") / clifford("{1}"))
959 1
960 >>> print(clifford("{1}") / clifford("{1,2}"))
961 -{2}
962 """
963 return clifford().wrap( toClifford(lhs) / toClifford(rhs) )
964
965 def __rtruediv__(rhs, lhs):
966 """
967 Geometric quotient.
968
969 >>> print(2 / clifford("{2}"))
970 2{2}
971 """
972 return clifford().wrap( toClifford(lhs) / toClifford(rhs) )
973
974 def __idiv__(self, rhs):
975 """
976 Geometric quotient.
977
978 >>> x = clifford("{1}"); x /= clifford("{2}"); print(x)
979 {1,2}
980 >>> x = clifford(2); x /= clifford("{2}"); print(x)
981 2{2}
982 >>> x = clifford("{1}"); x /= clifford("{1}"); print(x)
983 1
984 >>> x = clifford("{1}"); x /= clifford("{1,2}"); print(x)
985 -{2}
986 """
987 return self.wrap( self.unwrap() / toClifford(rhs) )
988
989 def inv(self):
990 """
991 Geometric multiplicative inverse.
992
993 >>> x = clifford("{1}"); print(x.inv())
994 {1}
995 >>> x = clifford(2); print(x.inv())
996 0.5
997 >>> x = clifford("{1,2}"); print(x.inv())
998 -{1,2}
999 """
1000 return clifford().wrap( self.instance.inv() )
1001
1002 def __or__(lhs, rhs):
1003 """
1004 Transform left hand side, using right hand side as a transformation.
1005
1006 >>> x=clifford("{1,2}") * pi/2; y=clifford("{1}"); print(y|x)
1007 -{1}
1008 >>> x=clifford("{1,2}") * pi/2; y=clifford("{1}"); print(y|exp(x))
1009 -{1}
1010 """
1011 return clifford().wrap( toClifford(lhs) | toClifford(rhs) )
1012
1013 def __ior__(self, rhs):
1014 """
1015 Transform left hand side, using right hand side as a transformation.
1016
1017 >>> x=clifford("{1,2}") * pi/2; y=clifford("{1}"); y|=x; print(y)
1018 -{1}
1019 >>> x=clifford("{1,2}") * pi/2; y=clifford("{1}"); y|=exp(x); print(y)
1020 -{1}
1021 """
1022 return self.wrap( self.unwrap() | toClifford(rhs) )
1023
1024 def __pow__(self, m, dummy):
1025 """
1026 Power: self to the m.
1027
1028 >>> x=clifford("{1}"); print(x ** 2)
1029 1
1030 >>> x=clifford("2"); print(x ** 2)
1031 4
1032 >>> x=clifford("2+{1}"); print(x ** 0)
1033 1
1034 >>> x=clifford("2+{1}"); print(x ** 1)
1035 2+{1}
1036 >>> x=clifford("2+{1}"); print(x ** 2)
1037 5+4{1}
1038 >>> i=clifford("{1,2}"); print(exp(pi/2) * (i ** i))
1039 1
1040 """
1041 return pow(self, m)
1042
1043 def pow(self, m):
1044 """
1045 Power: self to the m.
1046
1047 >>> x=clifford("{1}"); print(x.pow(2))
1048 1
1049 >>> x=clifford("2"); print(x.pow(2))
1050 4
1051 >>> x=clifford("2+{1}"); print(x.pow(0))
1052 1
1053 >>> x=clifford("2+{1}"); print(x.pow(1))
1054 2+{1}
1055 >>> x=clifford("2+{1}"); print(x.pow(2))
1056 5+4{1}
1057 >>> print(clifford("1+{1}+{1,2}").pow(3))
1058 1+3{1}+3{1,2}
1059 >>> i=clifford("{1,2}"); print(exp(pi/2) * i.pow(i))
1060 1
1061 """
1062 if isinstance(m, numbers.Integral):
1063 return clifford().wrap( self.instance.pow(m) )
1064 else:
1065 return exp(m * log(self))
1066
1067 def outer_pow(self, m):
1068 """
1069 Outer product power.
1070
1071 >>> x=clifford("2+{1}"); print(x.outer_pow(0))
1072 1
1073 >>> x=clifford("2+{1}"); print(x.outer_pow(1))
1074 2+{1}
1075 >>> x=clifford("2+{1}"); print(x.outer_pow(2))
1076 4+4{1}
1077 >>> print(clifford("1+{1}+{1,2}").outer_pow(3))
1078 1+3{1}+3{1,2}
1079
1080 """
1081 return clifford().wrap( self.instance.outer_pow(m) )
1082
1083 def __call__(self, grade):
1084 """
1085 Pure grade-vector part.
1086
1087 >>> print(clifford("{1}")(1))
1088 {1}
1089 >>> print(clifford("{1}")(0))
1090 0
1091 >>> print(clifford("1+{1}+{1,2}")(0))
1092 1
1093 >>> print(clifford("1+{1}+{1,2}")(1))
1094 {1}
1095 >>> print(clifford("1+{1}+{1,2}")(2))
1096 {1,2}
1097 >>> print(clifford("1+{1}+{1,2}")(3))
1098 0
1099 """
1100 return clifford().wrap( self.instance.call(grade) )
1101
1102 def scalar(self):
1103 """
1104 Scalar part.
1105
1106 >>> clifford("1+{1}+{1,2}").scalar()
1107 1.0
1108 >>> clifford("{1,2}").scalar()
1109 0.0
1110 """
1111 return self.instance.scalar()
1112
1113 def pure(self):
1114 """
1115 Pure part.
1116
1117 >>> print(clifford("1+{1}+{1,2}").pure())
1118 {1}+{1,2}
1119 >>> print(clifford("{1,2}").pure())
1120 {1,2}
1121 """
1122 return clifford().wrap( self.instance.pure() )
1123
1124 def even(self):
1125 """
1126 Even part of multivector, sum of even grade terms.
1127
1128 >>> print(clifford("1+{1}+{1,2}").even())
1129 1+{1,2}
1130 """
1131 return clifford().wrap( self.instance.even() )
1132
1133 def odd(self):
1134 """
1135 Odd part of multivector, sum of odd grade terms.
1136
1137 >>> print(clifford("1+{1}+{1,2}").odd())
1138 {1}
1139 """
1140 return clifford().wrap( self.instance.odd() )
1141
1142 def vector_part(self, frm = None):
1143 """
1144 Vector part of multivector, as a Python list, with respect to frm.
1145
1146 >>> print(clifford("1+2{1}+3{2}+4{1,2}").vector_part())
1147 [2.0, 3.0]
1148 >>> print(clifford("1+2{1}+3{2}+4{1,2}").vector_part(index_set({-1,1,2})))
1149 [0.0, 2.0, 3.0]
1150 """
1151 error_msg_prefix = "Cannot take vector part of "
1152 cdef vector[scalar_t] vec
1153 cdef int n
1154 cdef int i
1155 try:
1156 if frm is None:
1157 vec = self.instance.vector_part()
1158 else:
1159 vec = self.instance.vector_part((<index_set>frm).unwrap())
1160 n = vec.size()
1161 lst = [0.0]*n
1162 for i in xrange(n):
1163 lst[i] = vec[i]
1164 return lst
1165 except RuntimeError as err:
1166 raise ValueError(error_msg_prefix + str(self) + " using invalid "
1167 + repr(frm) + " as frame:\n\t"
1168 + str(err))
1169
1170 def involute(self):
1171 """
1172 Main involution, each {i} is replaced by -{i} in each term,
1173 eg. clifford("{1}") -> -clifford("{1}").
1174
1175 >>> print(clifford("{1}").involute())
1176 -{1}
1177 >>> print((clifford("{2}") * clifford("{1}")).involute())
1178 -{1,2}
1179 >>> print((clifford("{1}") * clifford("{2}")).involute())
1180 {1,2}
1181 >>> print(clifford("1+{1}+{1,2}").involute())
1182 1-{1}+{1,2}
1183 """
1184 return clifford().wrap( self.instance.involute() )
1185
1186 def reverse(self):
1187 """
1188 Reversion, eg. clifford("{1}")*clifford("{2}") -> clifford("{2}")*clifford("{1}").
1189
1190 >>> print(clifford("{1}").reverse())
1191 {1}
1192 >>> print((clifford("{2}") * clifford("{1}")).reverse())
1193 {1,2}
1194 >>> print((clifford("{1}") * clifford("{2}")).reverse())
1195 -{1,2}
1196 >>> print(clifford("1+{1}+{1,2}").reverse())
1197 1+{1}-{1,2}
1198 """
1199 return clifford().wrap( self.instance.reverse() )
1200
1201 def conj(self):
1202 """
1203 Conjugation, reverse o involute == involute o reverse.
1204
1205 >>> print((clifford("{1}")).conj())
1206 -{1}
1207 >>> print((clifford("{2}") * clifford("{1}")).conj())
1208 {1,2}
1209 >>> print((clifford("{1}") * clifford("{2}")).conj())
1210 -{1,2}
1211 >>> print(clifford("1+{1}+{1,2}").conj())
1212 1-{1}-{1,2}
1213 """
1214 return clifford().wrap( self.instance.conj() )
1215
1216 def quad(self):
1217 """
1218 Quadratic form == (rev(x)*x)(0).
1219
1220 >>> print(clifford("1+{1}+{1,2}").quad())
1221 3.0
1222 >>> print(clifford("1+{-1}+{1,2}+{1,2,3}").quad())
1223 2.0
1224 """
1225 return self.instance.quad()
1226
1227 def norm(self):
1228 """
1229 Norm == sum of squares of coordinates.
1230
1231 >>> clifford("1+{1}+{1,2}").norm()
1232 3.0
1233 >>> clifford("1+{-1}+{1,2}+{1,2,3}").norm()
1234 4.0
1235 """
1236 return self.instance.norm()
1237
1238 def abs(self):
1239 """
1240 Absolute value: square root of norm.
1241
1242 >>> clifford("1+{-1}+{1,2}+{1,2,3}").abs()
1243 2.0
1244 """
1245 return glucat.abs( self.unwrap() )
1246
1247 def max_abs(self):
1248 """
1249 Maximum of absolute values of components of multivector: multivector infinity norm.
1250
1251 >>> clifford("1+{-1}+{1,2}+{1,2,3}").max_abs()
1252 1.0
1253 >>> clifford("3+2{1}+{1,2}").max_abs()
1254 3.0
1255 """
1256 return self.instance.max_abs()
1257
1258 def truncated(self, limit):
1259 """
1260 Remove all terms of self with relative size smaller than limit.
1261
1262 >>> clifford("1e8+{1}+1e-8{1,2}").truncated(1.0e-6)
1263 clifford("100000000")
1264 >>> clifford("1e4+{1}+1e-4{1,2}").truncated(1.0e-6)
1265 clifford("10000+{1}")
1266 """
1267 return clifford().wrap( self.instance.truncated(limit) )
1268
1269 def isinf(self):
1270 """
1271 Check if a multivector contains any infinite values.
1272
1273 >>> clifford().isinf()
1274 False
1275 """
1276 return self.instance.isnan()
1277
1278 def isnan(self):
1279 """
1280 Check if a multivector contains any IEEE NaN values.
1281
1282 >>> clifford().isnan()
1283 False
1284 """
1285 return self.instance.isnan()
1286
1287 def frame(self):
1288 """
1289 Subalgebra generated by all generators of terms of given multivector.
1290
1291 >>> print(clifford("1+3{-1}+2{1,2}+4{-2,7}").frame())
1292 {-2,-1,1,2,7}
1293 >>> s=clifford("1+3{-1}+2{1,2}+4{-2,7}").frame(); type(s)
1294 <class 'PyClical.index_set'>
1295 """
1296 return index_set().wrap( self.instance.frame() )
1297
1298 def __repr__(self):
1299 """
1300 The “official” string representation of self.
1301
1302 >>> clifford("1+3{-1}+2{1,2}+4{-2,7}").__repr__()
1303 'clifford("1+3{-1}+2{1,2}+4{-2,7}")'
1304 """
1305 return clifford_to_repr( self.unwrap() ).decode()
1306
1307 def __str__(self):
1308 """
1309 The “informal” string representation of self.
1310
1311 >>> clifford("1+3{-1}+2{1,2}+4{-2,7}").__str__()
1312 '1+3{-1}+2{1,2}+4{-2,7}'
1313 """
1314 return clifford_to_str( self.unwrap() ).decode()
1315
1317 """
1318 Tests for functions that Doctest cannot see.
1319
1320 For clifford.__cinit__: Construct an object of type clifford.
1321
1322 >>> print(clifford(2))
1323 2
1324 >>> print(clifford(2.0))
1325 2
1326 >>> print(clifford(1.0e-1))
1327 0.1
1328 >>> print(clifford("2"))
1329 2
1330 >>> print(clifford("2{1,2,3}"))
1331 2{1,2,3}
1332 >>> print(clifford(clifford("2{1,2,3}")))
1333 2{1,2,3}
1334 >>> print(clifford("-{1}"))
1335 -{1}
1336 >>> print(clifford(2,index_set({1,2})))
1337 2{1,2}
1338 >>> print(clifford([2,3],index_set({1,2})))
1339 2{1}+3{2}
1340 >>> print(clifford([1,2]))
1341 Traceback (most recent call last):
1342 ...
1343 TypeError: Cannot initialize clifford object from <class 'list'>.
1344 >>> print(clifford(None))
1345 Traceback (most recent call last):
1346 ...
1347 TypeError: Cannot initialize clifford object from <class 'NoneType'>.
1348 >>> print(clifford(None,[1,2]))
1349 Traceback (most recent call last):
1350 ...
1351 TypeError: Cannot initialize clifford object from (<class 'NoneType'>, <class 'list'>).
1352 >>> print(clifford([1,2],[1,2]))
1353 Traceback (most recent call last):
1354 ...
1355 TypeError: Cannot initialize clifford object from (<class 'list'>, <class 'list'>).
1356 >>> print(clifford(""))
1357 Traceback (most recent call last):
1358 ...
1359 ValueError: Cannot initialize clifford object from invalid string ''.
1360 >>> print(clifford("{"))
1361 Traceback (most recent call last):
1362 ...
1363 ValueError: Cannot initialize clifford object from invalid string '{'.
1364 >>> print(clifford("{1"))
1365 Traceback (most recent call last):
1366 ...
1367 ValueError: Cannot initialize clifford object from invalid string '{1'.
1368 >>> print(clifford("+"))
1369 Traceback (most recent call last):
1370 ...
1371 ValueError: Cannot initialize clifford object from invalid string '+'.
1372 >>> print(clifford("-"))
1373 Traceback (most recent call last):
1374 ...
1375 ValueError: Cannot initialize clifford object from invalid string '-'.
1376 >>> print(clifford("{1}+"))
1377 Traceback (most recent call last):
1378 ...
1379 ValueError: Cannot initialize clifford object from invalid string '{1}+'.
1380
1381 For clifford.__richcmp__: Compare objects of type clifford.
1382
1383 >>> clifford("{1}") == clifford("1{1}")
1384 True
1385 >>> clifford("{1}") != clifford("1.0{1}")
1386 False
1387 >>> clifford("{1}") != clifford("1.0")
1388 True
1389 >>> clifford("{1,2}") == None
1390 False
1391 >>> clifford("{1,2}") != None
1392 True
1393 >>> None == clifford("{1,2}")
1394 False
1395 >>> None != clifford("{1,2}")
1396 True
1397 """
1398 return
1399
1400cpdef inline error_squared_tol(obj):
1401 """
1402 Quadratic norm error tolerance relative to a specific multivector.
1403
1404 >>> print(error_squared_tol(clifford("{1}")) * 3.0 - error_squared_tol(clifford("1{1}-2{2}+3{3}")))
1405 0.0
1406 """
1407 return glucat.error_squared_tol(toClifford(obj))
1408
1409cpdef inline error_squared(lhs, rhs, threshold):
1410 """
1411 Relative or absolute error using the quadratic norm.
1412
1413 >>> err2=scalar_epsilon*scalar_epsilon
1414
1415 >>> print(error_squared(clifford("{1}"), clifford("1{1}"), err2))
1416 0.0
1417 >>> print(error_squared(clifford("1{1}-3{2}+4{3}"), clifford("{1}"), err2))
1418 25.0
1419 """
1420 return glucat.error_squared(toClifford(lhs), toClifford(rhs), <scalar_t>threshold)
1421
1422cpdef inline approx_equal(lhs, rhs, threshold=None, tol=None):
1423 """
1424 Test for approximate equality of multivectors.
1425
1426 >>> err2=scalar_epsilon*scalar_epsilon
1427
1428 >>> print(approx_equal(clifford("{1}"), clifford("1{1}")))
1429 True
1430 >>> print(approx_equal(clifford("1{1}-3{2}+4{3}"), clifford("{1}")))
1431 False
1432 >>> print(approx_equal(clifford("1{1}-3{2}+4{3}+0.001"), clifford("1{1}-3{2}+4{3}"), err2, err2))
1433 False
1434 >>> print(approx_equal(clifford("1{1}-3{2}+4{3}+1.0e-30"), clifford("1{1}-3{2}+4{3}"), err2, err2))
1435 True
1436 """
1437 threshold = error_squared_tol(rhs) if threshold is None else threshold
1438 tol = error_squared_tol(rhs) if tol is None else tol
1439 return glucat.approx_equal(toClifford(lhs), toClifford(rhs), <scalar_t>threshold, <scalar_t>tol)
1440
1441cpdef inline inv(obj):
1442 """
1443 Geometric multiplicative inverse.
1444
1445 >>> print(inv(clifford("{1}")))
1446 {1}
1447 >>> print(inv(clifford("{-1}")))
1448 -{-1}
1449 >>> print(inv(clifford("{-2,-1}")))
1450 -{-2,-1}
1451 >>> print(inv(clifford("{-1}+{1}")))
1452 nan
1453 """
1454 return clifford(obj).inv()
1455
1456cpdef inline scalar(obj):
1457 """
1458 Scalar part.
1459
1460 >>> scalar(clifford("1+{1}+{1,2}"))
1461 1.0
1462 >>> scalar(clifford("{1,2}"))
1463 0.0
1464 """
1465 return clifford(obj).scalar()
1466
1467cpdef inline real(obj):
1468 """
1469 Real part: synonym for scalar part.
1470
1471 >>> real(clifford("1+{1}+{1,2}"))
1472 1.0
1473 >>> real(clifford("{1,2}"))
1474 0.0
1475 """
1476 return clifford(obj).scalar()
1477
1478cpdef inline imag(obj):
1479 """
1480 Imaginary part: deprecated (always 0).
1481
1482 >>> imag(clifford("1+{1}+{1,2}"))
1483 0.0
1484 >>> imag(clifford("{1,2}"))
1485 0.0
1486 """
1487 return 0.0
1488
1489cpdef inline pure(obj):
1490 """
1491 Pure part
1492
1493 >>> print(pure(clifford("1+{1}+{1,2}")))
1494 {1}+{1,2}
1495 >>> print(pure(clifford("{1,2}")))
1496 {1,2}
1497 """
1498 return clifford(obj).pure()
1499
1500cpdef inline even(obj):
1501 """
1502 Even part of multivector, sum of even grade terms.
1503
1504 >>> print(even(clifford("1+{1}+{1,2}")))
1505 1+{1,2}
1506 """
1507 return clifford(obj).even()
1508
1509cpdef inline odd(obj):
1510 """
1511 Odd part of multivector, sum of odd grade terms.
1512
1513 >>> print(odd(clifford("1+{1}+{1,2}")))
1514 {1}
1515 """
1516 return clifford(obj).odd()
1517
1518cpdef inline involute(obj):
1519 """
1520 Main involution, each {i} is replaced by -{i} in each term, eg. {1}*{2} -> (-{2})*(-{1})
1521
1522 >>> print(involute(clifford("{1}")))
1523 -{1}
1524 >>> print(involute(clifford("{2}") * clifford("{1}")))
1525 -{1,2}
1526 >>> print(involute(clifford("{1}") * clifford("{2}")))
1527 {1,2}
1528 >>> print(involute(clifford("1+{1}+{1,2}")))
1529 1-{1}+{1,2}
1530 """
1531 return clifford(obj).involute()
1532
1533cpdef inline reverse(obj):
1534 """
1535 Reversion, eg. {1}*{2} -> {2}*{1}
1536
1537 >>> print(reverse(clifford("{1}")))
1538 {1}
1539 >>> print(reverse(clifford("{2}") * clifford("{1}")))
1540 {1,2}
1541 >>> print(reverse(clifford("{1}") * clifford("{2}")))
1542 -{1,2}
1543 >>> print(reverse(clifford("1+{1}+{1,2}")))
1544 1+{1}-{1,2}
1545 """
1546 return clifford(obj).reverse()
1547
1548cpdef inline conj(obj):
1549 """
1550 Conjugation, reverse o involute == involute o reverse.
1551
1552 >>> print(conj(clifford("{1}")))
1553 -{1}
1554 >>> print(conj(clifford("{2}") * clifford("{1}")))
1555 {1,2}
1556 >>> print(conj(clifford("{1}") * clifford("{2}")))
1557 -{1,2}
1558 >>> print(conj(clifford("1+{1}+{1,2}")))
1559 1-{1}-{1,2}
1560 """
1561 return clifford(obj).conj()
1562
1563cpdef inline quad(obj):
1564 """
1565 Quadratic form == (rev(x)*x)(0).
1566
1567 >>> print(quad(clifford("1+{1}+{1,2}")))
1568 3.0
1569 >>> print(quad(clifford("1+{-1}+{1,2}+{1,2,3}")))
1570 2.0
1571 """
1572 return clifford(obj).quad()
1573
1574cpdef inline norm(obj):
1575 """
1576 norm == sum of squares of coordinates.
1577
1578 >>> norm(clifford("1+{1}+{1,2}"))
1579 3.0
1580 >>> norm(clifford("1+{-1}+{1,2}+{1,2,3}"))
1581 4.0
1582 """
1583 return clifford(obj).norm()
1584
1585cpdef inline abs(obj):
1586 """
1587 Absolute value of multivector: multivector 2-norm.
1588
1589 >>> abs(clifford("1+{-1}+{1,2}+{1,2,3}"))
1590 2.0
1591 """
1592 return glucat.abs(toClifford(obj))
1593
1594cpdef inline max_abs(obj):
1595 """
1596 Maximum absolute value of coordinates multivector: multivector infinity-norm.
1597
1598 >>> max_abs(clifford("1+{-1}+{1,2}+{1,2,3}"))
1599 1.0
1600 >>> max_abs(clifford("3+2{1}+{1,2}"))
1601 3.0
1602
1603 """
1604 return glucat.max_abs(toClifford(obj))
1605
1606cpdef inline pow(obj, m):
1607 """
1608 Integer power of multivector: obj to the m.
1609
1610 >>> x=clifford("{1}"); print(pow(x,2))
1611 1
1612 >>> x=clifford("2"); print(pow(x,2))
1613 4
1614 >>> x=clifford("2+{1}"); print(pow(x,0))
1615 1
1616 >>> x=clifford("2+{1}"); print(pow(x,1))
1617 2+{1}
1618 >>> x=clifford("2+{1}"); print(pow(x,2))
1619 5+4{1}
1620 >>> print(pow(clifford("1+{1}+{1,2}"),3))
1621 1+3{1}+3{1,2}
1622 >>> i=clifford("{1,2}"); print(exp(pi/2) * pow(i, i))
1623 1
1624 """
1625 try:
1626 math.pow(obj, m)
1627 except:
1628 return clifford(obj).pow(m)
1629
1630cpdef inline outer_pow(obj, m):
1631 """
1632 Outer product power of multivector.
1633
1634 >>> print(outer_pow(clifford("1+{1}+{1,2}"),3))
1635 1+3{1}+3{1,2}
1636 """
1637 return clifford(obj).outer_pow(m)
1638
1639cpdef inline complexifier(obj):
1640 """
1641 Square root of -1 which commutes with all members of the frame of the given multivector.
1642
1643 >>> print(complexifier(clifford(index_set({1}))))
1644 {1,2,3}
1645 >>> print(complexifier(clifford(index_set({-1}))))
1646 {-1}
1647 >>> print(complexifier(index_set({1})))
1648 {1,2,3}
1649 >>> print(complexifier(index_set({-1})))
1650 {-1}
1651 """
1652 return clifford().wrap( glucat.complexifier(toClifford(obj)) )
1653
1654cpdef inline sqrt(obj, i = None):
1655 """
1656 Square root of multivector with optional complexifier.
1657
1658 >>> print(sqrt(-1))
1659 {-1}
1660 >>> print(sqrt(clifford("2{-1}")))
1661 1+{-1}
1662 >>> j=sqrt(-1,complexifier(index_set({1}))); print(j); print(j*j)
1663 {1,2,3}
1664 -1
1665 >>> j=sqrt(-1,"{1,2,3}"); print(j); print(j*j)
1666 {1,2,3}
1667 -1
1668 """
1669 if not (i is None):
1670 return clifford().wrap( glucat.sqrt(toClifford(obj), toClifford(i)) )
1671 else:
1672 try:
1673 return math.sqrt(obj)
1674 except:
1675 return clifford().wrap( glucat.sqrt(toClifford(obj)) )
1676
1677cpdef inline exp(obj):
1678 """
1679 Exponential of multivector.
1680
1681 >>> x=clifford("{1,2}") * pi/4; print(exp(x))
1682 0.7071+0.7071{1,2}
1683 >>> x=clifford("{1,2}") * pi/2; print(exp(x))
1684 {1,2}
1685 """
1686 try:
1687 return math.exp(obj)
1688 except:
1689 return clifford().wrap( glucat.exp(toClifford(obj)) )
1690
1691cpdef inline log(obj,i = None):
1692 """
1693 Natural logarithm of multivector with optional complexifier.
1694
1695 >>> x=clifford("{-1}"); print((log(x,"{-1}") * 2/pi))
1696 {-1}
1697 >>> x=clifford("{1,2}"); print((log(x,"{1,2,3}") * 2/pi))
1698 {1,2}
1699 >>> x=clifford("{1,2}"); print((log(x) * 2/pi))
1700 {1,2}
1701 >>> x=clifford("{1,2}"); print((log(x,"{1,2}") * 2/pi))
1702 Traceback (most recent call last):
1703 ...
1704 RuntimeError: check_complex(val, i): i is not a valid complexifier for val
1705 """
1706 if not (i is None):
1707 return clifford().wrap( glucat.log(toClifford(obj), toClifford(i)) )
1708 else:
1709 try:
1710 return math.log(obj)
1711 except:
1712 return clifford().wrap( glucat.log(toClifford(obj)) )
1713
1714cpdef inline cos(obj,i = None):
1715 """
1716 Cosine of multivector with optional complexifier.
1717
1718 >>> x=clifford("{1,2}"); print(cos(acos(x),"{1,2,3}"))
1719 {1,2}
1720 >>> x=clifford("{1,2}"); print(cos(acos(x)))
1721 {1,2}
1722 """
1723 if not (i is None):
1724 return clifford().wrap( glucat.cos(toClifford(obj), toClifford(i)) )
1725 else:
1726 try:
1727 return math.cos(obj)
1728 except:
1729 return clifford().wrap( glucat.cos(toClifford(obj)) )
1730
1731cpdef inline acos(obj,i = None):
1732 """
1733 Inverse cosine of multivector with optional complexifier.
1734
1735 >>> x=clifford("{1,2}"); print(cos(acos(x),"{1,2,3}"))
1736 {1,2}
1737 >>> x=clifford("{1,2}"); print(cos(acos(x),"{-1,1,2,3,4}"))
1738 {1,2}
1739 >>> print(acos(0) / pi)
1740 0.5
1741 >>> x=clifford("{1,2}"); print(cos(acos(x)))
1742 {1,2}
1743 """
1744 if not (i is None):
1745 return clifford().wrap( glucat.acos(toClifford(obj), toClifford(i)) )
1746 else:
1747 try:
1748 return math.acos(obj)
1749 except:
1750 return clifford().wrap( glucat.acos(toClifford(obj)) )
1751
1752cpdef inline cosh(obj):
1753 """
1754 Hyperbolic cosine of multivector.
1755
1756 >>> x=clifford("{1,2}") * pi; print(cosh(x))
1757 -1
1758 >>> x=clifford("{1,2,3}"); print(cosh(acosh(x)))
1759 {1,2,3}
1760 >>> x=clifford("{1,2}"); print(cosh(acosh(x)))
1761 {1,2}
1762 """
1763 try:
1764 return math.cosh(obj)
1765 except:
1766 return clifford().wrap( glucat.cosh(toClifford(obj)) )
1767
1768cpdef inline acosh(obj,i = None):
1769 """
1770 Inverse hyperbolic cosine of multivector with optional complexifier.
1771
1772 >>> print(acosh(0,"{-2,-1,1}"))
1773 1.571{-2,-1,1}
1774 >>> x=clifford("{1,2,3}"); print(cosh(acosh(x,"{-1,1,2,3,4}")))
1775 {1,2,3}
1776 >>> print(acosh(0))
1777 1.571{-1}
1778 >>> x=clifford("{1,2,3}"); print(cosh(acosh(x)))
1779 {1,2,3}
1780 >>> x=clifford("{1,2}"); print(cosh(acosh(x)))
1781 {1,2}
1782 """
1783 if not (i is None):
1784 return clifford().wrap( glucat.acosh(toClifford(obj), toClifford(i)) )
1785 else:
1786 try:
1787 return math.acosh(obj)
1788 except:
1789 return clifford().wrap( glucat.acosh(toClifford(obj)) )
1790
1791cpdef inline sin(obj,i = None):
1792 """
1793 Sine of multivector with optional complexifier.
1794
1795 >>> s="{-1}"; x=clifford(s); print(asin(sin(x,s),s))
1796 {-1}
1797 >>> s="{-1}"; x=clifford(s); print(asin(sin(x,s),"{-2,-1,1}"))
1798 {-1}
1799 >>> x=clifford("{1,2,3}"); print(asin(sin(x)))
1800 {1,2,3}
1801 """
1802 if not (i is None):
1803 return clifford().wrap( glucat.sin(toClifford(obj), toClifford(i)) )
1804 else:
1805 try:
1806 return math.sin(obj)
1807 except:
1808 return clifford().wrap( glucat.sin(toClifford(obj)) )
1809
1810cpdef inline asin(obj,i = None):
1811 """
1812 Inverse sine of multivector with optional complexifier.
1813
1814 >>> s="{-1}"; x=clifford(s); print(asin(sin(x,s),s))
1815 {-1}
1816 >>> s="{-1}"; x=clifford(s); print(asin(sin(x,s),"{-2,-1,1}"))
1817 {-1}
1818 >>> print(asin(1) / pi)
1819 0.5
1820 >>> x=clifford("{1,2,3}"); print(asin(sin(x)))
1821 {1,2,3}
1822 """
1823 if not (i is None):
1824 return clifford().wrap( glucat.asin(toClifford(obj), toClifford(i)) )
1825 else:
1826 try:
1827 return math.asin(obj)
1828 except:
1829 return clifford().wrap( glucat.asin(toClifford(obj)) )
1830
1831cpdef inline sinh(obj):
1832 """
1833 Hyperbolic sine of multivector.
1834
1835 >>> x=clifford("{1,2}") * pi/2; print(sinh(x))
1836 {1,2}
1837 >>> x=clifford("{1,2}") * pi/6; print(sinh(x))
1838 0.5{1,2}
1839 """
1840 try:
1841 return math.sinh(obj)
1842 except:
1843 return clifford().wrap( glucat.sinh(toClifford(obj)) )
1844
1845cpdef inline asinh(obj,i = None):
1846 """
1847 Inverse hyperbolic sine of multivector with optional complexifier.
1848
1849 >>> x=clifford("{1,2}"); print(asinh(x,"{1,2,3}") * 2/pi)
1850 {1,2}
1851 >>> x=clifford("{1,2}"); print(asinh(x) * 2/pi)
1852 {1,2}
1853 >>> x=clifford("{1,2}") / 2; print(asinh(x) * 6/pi)
1854 {1,2}
1855 """
1856 if not (i is None):
1857 return clifford().wrap( glucat.asinh(toClifford(obj), toClifford(i)) )
1858 else:
1859 try:
1860 return math.asinh(obj)
1861 except:
1862 return clifford().wrap( glucat.asinh(toClifford(obj)) )
1863
1864cpdef inline tan(obj,i = None):
1865 """
1866 Tangent of multivector with optional complexifier.
1867
1868 >>> x=clifford("{1,2}"); print(tan(x,"{1,2,3}"))
1869 0.7616{1,2}
1870 >>> x=clifford("{1,2}"); print(tan(x))
1871 0.7616{1,2}
1872 """
1873 if not (i is None):
1874 return clifford().wrap( glucat.tan(toClifford(obj), toClifford(i)) )
1875 else:
1876 try:
1877 return math.tan(obj)
1878 except:
1879 return clifford().wrap( glucat.tan(toClifford(obj)) )
1880
1881cpdef inline atan(obj,i = None):
1882 """
1883 Inverse tangent of multivector with optional complexifier.
1884
1885 >>> s=index_set({1,2,3}); x=clifford("{1}"); print(tan(atan(x,s),s))
1886 {1}
1887 >>> x=clifford("{1}"); print(tan(atan(x)))
1888 {1}
1889 """
1890 if not (i is None):
1891 return clifford().wrap( glucat.atan(toClifford(obj), toClifford(i)) )
1892 else:
1893 try:
1894 return math.atan(obj)
1895 except:
1896 return clifford().wrap( glucat.atan(toClifford(obj)) )
1897
1898cpdef inline tanh(obj):
1899 """
1900 Hyperbolic tangent of multivector.
1901
1902 >>> x=clifford("{1,2}") * pi/4; print(tanh(x))
1903 {1,2}
1904 """
1905 try:
1906 return math.tanh(obj)
1907 except:
1908 return clifford().wrap( glucat.tanh(toClifford(obj)) )
1909
1910cpdef inline atanh(obj,i = None):
1911 """
1912 Inverse hyperbolic tangent of multivector with optional complexifier.
1913
1914 >>> s=index_set({1,2,3}); x=clifford("{1,2}"); print(tanh(atanh(x,s)))
1915 {1,2}
1916 >>> x=clifford("{1,2}"); print(tanh(atanh(x)))
1917 {1,2}
1918 """
1919 if not (i is None):
1920 return clifford().wrap( glucat.atanh(toClifford(obj), toClifford(i)) )
1921 else:
1922 try:
1923 return math.atanh(obj)
1924 except:
1925 return clifford().wrap( glucat.atanh(toClifford(obj)) )
1926
1927cpdef inline random_clifford(index_set ixt, fill = 1.0):
1928 """
1929 Random multivector within a frame.
1930
1931 >>> print(random_clifford(index_set({-3,-1,2})).frame())
1932 {-3,-1,2}
1933 """
1934 return clifford().wrap( clifford().instance.random(ixt.unwrap(), <scalar_t>fill) )
1935
1936cpdef inline cga3(obj):
1937 """
1938 Convert Euclidean 3D multivector to Conformal Geometric Algebra using Doran and Lasenby definition.
1939
1940 >>> x=clifford("2{1}+9{2}+{3}"); print(cga3(x))
1941 87{-1}+4{1}+18{2}+2{3}+85{4}
1942 """
1943 return clifford().wrap( glucat.cga3(toClifford(obj)) )
1944
1945cpdef inline cga3std(obj):
1946 """
1947 Convert CGA3 null vector to standard conformal null vector using Doran and Lasenby definition.
1948
1949 >>> x=clifford("2{1}+9{2}+{3}"); print(cga3std(cga3(x)))
1950 87{-1}+4{1}+18{2}+2{3}+85{4}
1951 >>> x=clifford("2{1}+9{2}+{3}"); print(cga3std(cga3(x))-cga3(x))
1952 0
1953 """
1954 return clifford().wrap( glucat.cga3std(toClifford(obj)) )
1955
1956cpdef inline agc3(obj):
1957 """
1958 Convert CGA3 null vector to Euclidean 3D vector using Doran and Lasenby definition.
1959
1960 >>> x=clifford("2{1}+9{2}+{3}"); print(agc3(cga3(x)))
1961 2{1}+9{2}+{3}
1962 >>> x=clifford("2{1}+9{2}+{3}"); print(agc3(cga3(x))-x)
1963 0
1964 """
1965 return clifford().wrap( glucat.agc3(toClifford(obj)) )
1966
1967# Some abbreviations.
1968scalar_epsilon = epsilon
1969
1970pi = atan(clifford(1.0)) * 4.0
1971tau = atan(clifford(1.0)) * 8.0
1972
1973cl = clifford
1974"""
1975Abbreviation for clifford.
1976
1977>>> print(cl(2))
19782
1979>>> print(cl(2.0))
19802
1981>>> print(cl(5.0e-1))
19820.5
1983>>> print(cl("2"))
19842
1985>>> print(cl("2{1,2,3}"))
19862{1,2,3}
1987>>> print(cl(cl("2{1,2,3}")))
19882{1,2,3}
1989"""
1990
1991ist = index_set
1992"""
1993Abbreviation for index_set.
1994
1995>>> print(ist("{1,2,3}"))
1996{1,2,3}
1997"""
1998
1999def e(obj):
2000 """
2001 Abbreviation for clifford(index_set(obj)).
2002
2003 >>> print(e(1))
2004 {1}
2005 >>> print(e(-1))
2006 {-1}
2007 >>> print(e(0))
2008 1
2009 """
2010 return clifford(index_set(obj))
2011
2012def istpq(p, q):
2013 """
2014 Abbreviation for index_set({-q,...p}).
2015
2016 >>> print(istpq(2,3))
2017 {-3,-2,-1,1,2}
2018 """
2019 return index_set(set(range(-q,p+1)))
2020
2021ninf3 = e(4) + e(-1) # Null infinity point in 3D Conformal Geometric Algebra [DL].
2022nbar3 = e(4) - e(-1) # Null bar point in 3D Conformal Geometric Algebra [DL].
2023
2024# Doctest interface.
2025def _test():
2026 import PyClical, doctest
2027 return doctest.testmod(PyClical)
2028
2029if __name__ == "__main__":
2030 _test()
String clifford_to_str(const Multivector_T &mv)
The "informal" string representation of Multivector_T mv.
Definition PyClical.h:86
String clifford_to_repr(const Multivector_T &mv)
The “official” string representation of Multivector_T mv.
Definition PyClical.h:75
String index_set_to_str(const Index_Set_T &ist)
The "informal" string representation of Index_Set_T ist.
Definition PyClical.h:66
String index_set_to_repr(const Index_Set_T &ist)
The “official” string representation of Index_Set_T ist.
Definition PyClical.h:57
__rsub__(rhs, lhs)
Definition PyClical.pyx:780
__getitem__(self, ixt)
Definition PyClical.pyx:707
__and__(lhs, rhs)
Definition PyClical.pyx:872
__imod__(self, rhs)
Definition PyClical.pyx:857
truncated(self, limit)
__idiv__(self, rhs)
Definition PyClical.pyx:974
__cinit__(self, other=0, ixt=None)
Definition PyClical.pyx:565
__rtruediv__(rhs, lhs)
Definition PyClical.pyx:965
__truediv__(lhs, rhs)
Definition PyClical.pyx:950
__imul__(self, rhs)
Definition PyClical.pyx:820
__add__(lhs, rhs)
Definition PyClical.pyx:740
__rand__(rhs, lhs)
Definition PyClical.pyx:887
__call__(self, grade)
__iadd__(self, rhs)
Definition PyClical.pyx:760
__pow__(self, m, dummy)
__ixor__(self, rhs)
Definition PyClical.pyx:935
__rmul__(rhs, lhs)
Definition PyClical.pyx:811
__contains__(self, x)
Definition PyClical.pyx:627
__iand__(self, rhs)
Definition PyClical.pyx:896
__isub__(self, rhs)
Definition PyClical.pyx:789
__rxor__(rhs, lhs)
Definition PyClical.pyx:926
vector_part(self, frm=None)
__radd__(rhs, lhs)
Definition PyClical.pyx:751
__richcmp__(lhs, rhs, int, op)
Definition PyClical.pyx:672
reframe(self, ixt)
Definition PyClical.pyx:649
__sub__(lhs, rhs)
Definition PyClical.pyx:769
__ior__(self, rhs)
__mul__(lhs, rhs)
Definition PyClical.pyx:798
__xor__(lhs, rhs)
Definition PyClical.pyx:911
__mod__(lhs, rhs)
Definition PyClical.pyx:833
__rmod__(rhs, lhs)
Definition PyClical.pyx:848
__cinit__(self, other=0)
Definition PyClical.pyx:74
__setitem__(self, idx, val)
Definition PyClical.pyx:179
__xor__(lhs, rhs)
Definition PyClical.pyx:249
__getitem__(self, idx)
Definition PyClical.pyx:191
__and__(lhs, rhs)
Definition PyClical.pyx:271
__ior__(self, rhs)
Definition PyClical.pyx:304
__richcmp__(lhs, rhs, int, op)
Definition PyClical.pyx:122
sign_of_mult(self, rhs)
Definition PyClical.pyx:366
__iand__(self, rhs)
Definition PyClical.pyx:282
__contains__(self, idx)
Definition PyClical.pyx:210
__ixor__(self, rhs)
Definition PyClical.pyx:260
clifford_hidden_doctests()
index_set_hidden_doctests()
Definition PyClical.pyx:406
istpq(p, q)
Definitions for 3D Conformal Geometric Algebra [DL].
Definition PyClical.h:100
auto exp(const framed_multi< Scalar_T, LO, HI, Tune_P > &val) -> const framed_multi< Scalar_T, LO, HI, Tune_P >
Exponential of multivector.
auto compare(const index_set< LO, HI > &a, const index_set< LO, HI > &b) -> int
"lexicographic compare" eg. {3,4,5} is less than {3,7,8}
auto cosh(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Hyperbolic cosine of multivector.
auto tanh(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Hyperbolic tangent of multivector.
auto approx_equal(const Multivector< Scalar_T, LO, HI, Tune_P > &lhs, const RHS< Scalar_T, LO, HI, Tune_P > &rhs, const Scalar_T threshold, const Scalar_T tolerance) -> bool
Test for approximate equality of multivectors.
auto min_neg(const index_set< LO, HI > &ist) -> index_t
Minimum negative index, or 0 if none.
auto tan(const Multivector< Scalar_T, LO, HI, Tune_P > &val, const Multivector< Scalar_T, LO, HI, Tune_P > &i, const bool prechecked=false) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Tangent of multivector with specified complexifier.
auto error_squared(const Multivector< Scalar_T, LO, HI, Tune_P > &lhs, const RHS< Scalar_T, LO, HI, Tune_P > &rhs, const Scalar_T threshold) -> Scalar_T
Relative or absolute error using the quadratic norm.
auto sinh(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Hyperbolic sine of multivector.
auto sin(const Multivector< Scalar_T, LO, HI, Tune_P > &val, const Multivector< Scalar_T, LO, HI, Tune_P > &i, const bool prechecked=false) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Sine of multivector with specified complexifier.
auto abs(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> Scalar_T
Absolute value == sqrt(norm)
auto asinh(const Multivector< Scalar_T, LO, HI, Tune_P > &val, const Multivector< Scalar_T, LO, HI, Tune_P > &i, const bool prechecked=false) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Inverse hyperbolic sine of multivector with specified complexifier.
auto atanh(const Multivector< Scalar_T, LO, HI, Tune_P > &val, const Multivector< Scalar_T, LO, HI, Tune_P > &i, const bool prechecked=false) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Inverse hyperbolic tangent of multivector with specified complexifier.
auto atan(const Multivector< Scalar_T, LO, HI, Tune_P > &val, const Multivector< Scalar_T, LO, HI, Tune_P > &i, const bool prechecked=false) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Inverse tangent of multivector with specified complexifier.
auto cos(const Multivector< Scalar_T, LO, HI, Tune_P > &val, const Multivector< Scalar_T, LO, HI, Tune_P > &i, const bool prechecked=false) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Cosine of multivector with specified complexifier.
auto log(const Multivector< Scalar_T, LO, HI, Tune_P > &val, const Multivector< Scalar_T, LO, HI, Tune_P > &i, const bool prechecked=false) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Natural logarithm of multivector with specified complexifier.
auto asin(const Multivector< Scalar_T, LO, HI, Tune_P > &val, const Multivector< Scalar_T, LO, HI, Tune_P > &i, const bool prechecked=false) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Inverse sine of multivector with specified complexifier.
auto acosh(const Multivector< Scalar_T, LO, HI, Tune_P > &val, const Multivector< Scalar_T, LO, HI, Tune_P > &i, const bool prechecked=false) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Inverse hyperbolic cosine of multivector with specified complexifier.
auto error_squared_tol(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> Scalar_T
Quadratic norm error tolerance relative to a specific multivector.
auto max_abs(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> Scalar_T
Maximum of absolute values of components of multivector: multivector infinity norm.
auto sqrt(const Multivector< Scalar_T, LO, HI, Tune_P > &val, const Multivector< Scalar_T, LO, HI, Tune_P > &i, const bool prechecked=false) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Square root of multivector with specified complexifier.
auto complexifier(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Square root of -1 which commutes with all members of the frame of the given multivector.
auto acos(const Multivector< Scalar_T, LO, HI, Tune_P > &val, const Multivector< Scalar_T, LO, HI, Tune_P > &i, const bool prechecked=false) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Inverse cosine of multivector with specified complexifier.
auto max_pos(const index_set< LO, HI > &ist) -> index_t
Maximum positive index, or 0 if none.