glucat 0.12.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 __iadd__(self, rhs):
752 """
753 Geometric sum.
754
755 >>> x = clifford(1); x += clifford("{2}"); print(x)
756 1+{2}
757 """
758 return self.wrap( self.unwrap() + toClifford(rhs) )
759
760 def __sub__(lhs, rhs):
761 """
762 Geometric difference.
763
764 >>> print(clifford(1) - clifford("{2}"))
765 1-{2}
766 >>> print(clifford("{1}") - clifford("{2}"))
767 {1}-{2}
768 """
769 return clifford().wrap( toClifford(lhs) - toClifford(rhs) )
770
771 def __isub__(self, rhs):
772 """
773 Geometric difference.
774
775 >>> x = clifford(1); x -= clifford("{2}"); print(x)
776 1-{2}
777 """
778 return self.wrap( self.unwrap() - toClifford(rhs) )
779
780 def __mul__(lhs, rhs):
781 """
782 Geometric product.
783
784 >>> print(clifford("{1}") * clifford("{2}"))
785 {1,2}
786 >>> print(clifford(2) * clifford("{2}"))
787 2{2}
788 >>> print(clifford("{1}") * clifford("{1,2}"))
789 {2}
790 """
791 return clifford().wrap( toClifford(lhs) * toClifford(rhs) )
792
793 def __imul__(self, rhs):
794 """
795 Geometric product.
796
797 >>> x = clifford(2); x *= clifford("{2}"); print(x)
798 2{2}
799 >>> x = clifford("{1}"); x *= clifford("{2}"); print(x)
800 {1,2}
801 >>> x = clifford("{1}"); x *= clifford("{1,2}"); print(x)
802 {2}
803 """
804 return self.wrap( self.unwrap() * toClifford(rhs) )
805
806 def __mod__(lhs, rhs):
807 """
808 Contraction.
809
810 >>> print(clifford("{1}") % clifford("{2}"))
811 0
812 >>> print(clifford(2) % clifford("{2}"))
813 2{2}
814 >>> print(clifford("{1}") % clifford("{1}"))
815 1
816 >>> print(clifford("{1}") % clifford("{1,2}"))
817 {2}
818 """
819 return clifford().wrap( toClifford(lhs) % toClifford(rhs) )
820
821 def __imod__(self, rhs):
822 """
823 Contraction.
824
825 >>> x = clifford("{1}"); x %= clifford("{2}"); print(x)
826 0
827 >>> x = clifford(2); x %= clifford("{2}"); print(x)
828 2{2}
829 >>> x = clifford("{1}"); x %= clifford("{1}"); print(x)
830 1
831 >>> x = clifford("{1}"); x %= clifford("{1,2}"); print(x)
832 {2}
833 """
834 return self.wrap( self.unwrap() % toClifford(rhs) )
835
836 def __and__(lhs, rhs):
837 """
838 Inner product.
839
840 >>> print(clifford("{1}") & clifford("{2}"))
841 0
842 >>> print(clifford(2) & clifford("{2}"))
843 0
844 >>> print(clifford("{1}") & clifford("{1}"))
845 1
846 >>> print(clifford("{1}") & clifford("{1,2}"))
847 {2}
848 """
849 return clifford().wrap( toClifford(lhs) & toClifford(rhs) )
850
851 def __iand__(self, rhs):
852 """
853 Inner product.
854
855 >>> x = clifford("{1}"); x &= clifford("{2}"); print(x)
856 0
857 >>> x = clifford(2); x &= clifford("{2}"); print(x)
858 0
859 >>> x = clifford("{1}"); x &= clifford("{1}"); print(x)
860 1
861 >>> x = clifford("{1}"); x &= clifford("{1,2}"); print(x)
862 {2}
863 """
864 return self.wrap( self.unwrap() & toClifford(rhs) )
865
866 def __xor__(lhs, rhs):
867 """
868 Outer product.
869
870 >>> print(clifford("{1}") ^ clifford("{2}"))
871 {1,2}
872 >>> print(clifford(2) ^ clifford("{2}"))
873 2{2}
874 >>> print(clifford("{1}") ^ clifford("{1}"))
875 0
876 >>> print(clifford("{1}") ^ clifford("{1,2}"))
877 0
878 """
879 return clifford().wrap( toClifford(lhs) ^ toClifford(rhs) )
880
881 def __ixor__(self, rhs):
882 """
883 Outer product.
884
885 >>> x = clifford("{1}"); x ^= clifford("{2}"); print(x)
886 {1,2}
887 >>> x = clifford(2); x ^= clifford("{2}"); print(x)
888 2{2}
889 >>> x = clifford("{1}"); x ^= clifford("{1}"); print(x)
890 0
891 >>> x = clifford("{1}"); x ^= clifford("{1,2}"); print(x)
892 0
893 """
894 return self.wrap( self.unwrap() ^ toClifford(rhs) )
895
896 def __truediv__(lhs, rhs):
897 """
898 Geometric quotient.
899
900 >>> print(clifford("{1}") / clifford("{2}"))
901 {1,2}
902 >>> print(clifford(2) / clifford("{2}"))
903 2{2}
904 >>> print(clifford("{1}") / clifford("{1}"))
905 1
906 >>> print(clifford("{1}") / clifford("{1,2}"))
907 -{2}
908 """
909 return clifford().wrap( toClifford(lhs) / toClifford(rhs) )
910
911 def __idiv__(self, rhs):
912 """
913 Geometric quotient.
914
915 >>> x = clifford("{1}"); x /= clifford("{2}"); print(x)
916 {1,2}
917 >>> x = clifford(2); x /= clifford("{2}"); print(x)
918 2{2}
919 >>> x = clifford("{1}"); x /= clifford("{1}"); print(x)
920 1
921 >>> x = clifford("{1}"); x /= clifford("{1,2}"); print(x)
922 -{2}
923 """
924 return self.wrap( self.unwrap() / toClifford(rhs) )
925
926 def inv(self):
927 """
928 Geometric multiplicative inverse.
929
930 >>> x = clifford("{1}"); print(x.inv())
931 {1}
932 >>> x = clifford(2); print(x.inv())
933 0.5
934 >>> x = clifford("{1,2}"); print(x.inv())
935 -{1,2}
936 """
937 return clifford().wrap( self.instance.inv() )
938
939 def __or__(lhs, rhs):
940 """
941 Transform left hand side, using right hand side as a transformation.
942
943 >>> x=clifford("{1,2}") * pi/2; y=clifford("{1}"); print(y|x)
944 -{1}
945 >>> x=clifford("{1,2}") * pi/2; y=clifford("{1}"); print(y|exp(x))
946 -{1}
947 """
948 return clifford().wrap( toClifford(lhs) | toClifford(rhs) )
949
950 def __ior__(self, rhs):
951 """
952 Transform left hand side, using right hand side as a transformation.
953
954 >>> x=clifford("{1,2}") * pi/2; y=clifford("{1}"); y|=x; print(y)
955 -{1}
956 >>> x=clifford("{1,2}") * pi/2; y=clifford("{1}"); y|=exp(x); print(y)
957 -{1}
958 """
959 return self.wrap( self.unwrap() | toClifford(rhs) )
960
961 def __pow__(self, m, dummy):
962 """
963 Power: self to the m.
964
965 >>> x=clifford("{1}"); print(x ** 2)
966 1
967 >>> x=clifford("2"); print(x ** 2)
968 4
969 >>> x=clifford("2+{1}"); print(x ** 0)
970 1
971 >>> x=clifford("2+{1}"); print(x ** 1)
972 2+{1}
973 >>> x=clifford("2+{1}"); print(x ** 2)
974 5+4{1}
975 >>> i=clifford("{1,2}"); print(exp(pi/2) * (i ** i))
976 1
977 """
978 return pow(self, m)
979
980 def pow(self, m):
981 """
982 Power: self to the m.
983
984 >>> x=clifford("{1}"); print(x.pow(2))
985 1
986 >>> x=clifford("2"); print(x.pow(2))
987 4
988 >>> x=clifford("2+{1}"); print(x.pow(0))
989 1
990 >>> x=clifford("2+{1}"); print(x.pow(1))
991 2+{1}
992 >>> x=clifford("2+{1}"); print(x.pow(2))
993 5+4{1}
994 >>> print(clifford("1+{1}+{1,2}").pow(3))
995 1+3{1}+3{1,2}
996 >>> i=clifford("{1,2}"); print(exp(pi/2) * i.pow(i))
997 1
998 """
999 if isinstance(m, numbers.Integral):
1000 return clifford().wrap( self.instance.pow(m) )
1001 else:
1002 return exp(m * log(self))
1003
1004 def outer_pow(self, m):
1005 """
1006 Outer product power.
1007
1008 >>> x=clifford("2+{1}"); print(x.outer_pow(0))
1009 1
1010 >>> x=clifford("2+{1}"); print(x.outer_pow(1))
1011 2+{1}
1012 >>> x=clifford("2+{1}"); print(x.outer_pow(2))
1013 4+4{1}
1014 >>> print(clifford("1+{1}+{1,2}").outer_pow(3))
1015 1+3{1}+3{1,2}
1016
1017 """
1018 return clifford().wrap( self.instance.outer_pow(m) )
1019
1020 def __call__(self, grade):
1021 """
1022 Pure grade-vector part.
1023
1024 >>> print(clifford("{1}")(1))
1025 {1}
1026 >>> print(clifford("{1}")(0))
1027 0
1028 >>> print(clifford("1+{1}+{1,2}")(0))
1029 1
1030 >>> print(clifford("1+{1}+{1,2}")(1))
1031 {1}
1032 >>> print(clifford("1+{1}+{1,2}")(2))
1033 {1,2}
1034 >>> print(clifford("1+{1}+{1,2}")(3))
1035 0
1036 """
1037 return clifford().wrap( self.instance.call(grade) )
1038
1039 def scalar(self):
1040 """
1041 Scalar part.
1042
1043 >>> clifford("1+{1}+{1,2}").scalar()
1044 1.0
1045 >>> clifford("{1,2}").scalar()
1046 0.0
1047 """
1048 return self.instance.scalar()
1049
1050 def pure(self):
1051 """
1052 Pure part.
1053
1054 >>> print(clifford("1+{1}+{1,2}").pure())
1055 {1}+{1,2}
1056 >>> print(clifford("{1,2}").pure())
1057 {1,2}
1058 """
1059 return clifford().wrap( self.instance.pure() )
1060
1061 def even(self):
1062 """
1063 Even part of multivector, sum of even grade terms.
1064
1065 >>> print(clifford("1+{1}+{1,2}").even())
1066 1+{1,2}
1067 """
1068 return clifford().wrap( self.instance.even() )
1069
1070 def odd(self):
1071 """
1072 Odd part of multivector, sum of odd grade terms.
1073
1074 >>> print(clifford("1+{1}+{1,2}").odd())
1075 {1}
1076 """
1077 return clifford().wrap( self.instance.odd() )
1078
1079 def vector_part(self, frm = None):
1080 """
1081 Vector part of multivector, as a Python list, with respect to frm.
1082
1083 >>> print(clifford("1+2{1}+3{2}+4{1,2}").vector_part())
1084 [2.0, 3.0]
1085 >>> print(clifford("1+2{1}+3{2}+4{1,2}").vector_part(index_set({-1,1,2})))
1086 [0.0, 2.0, 3.0]
1087 """
1088 error_msg_prefix = "Cannot take vector part of "
1089 cdef vector[scalar_t] vec
1090 cdef int n
1091 cdef int i
1092 try:
1093 if frm is None:
1094 vec = self.instance.vector_part()
1095 else:
1096 vec = self.instance.vector_part((<index_set>frm).unwrap())
1097 n = vec.size()
1098 lst = [0.0]*n
1099 for i in xrange(n):
1100 lst[i] = vec[i]
1101 return lst
1102 except RuntimeError as err:
1103 raise ValueError(error_msg_prefix + str(self) + " using invalid "
1104 + repr(frm) + " as frame:\n\t"
1105 + str(err))
1106
1107 def involute(self):
1108 """
1109 Main involution, each {i} is replaced by -{i} in each term,
1110 eg. clifford("{1}") -> -clifford("{1}").
1111
1112 >>> print(clifford("{1}").involute())
1113 -{1}
1114 >>> print((clifford("{2}") * clifford("{1}")).involute())
1115 -{1,2}
1116 >>> print((clifford("{1}") * clifford("{2}")).involute())
1117 {1,2}
1118 >>> print(clifford("1+{1}+{1,2}").involute())
1119 1-{1}+{1,2}
1120 """
1121 return clifford().wrap( self.instance.involute() )
1122
1123 def reverse(self):
1124 """
1125 Reversion, eg. clifford("{1}")*clifford("{2}") -> clifford("{2}")*clifford("{1}").
1126
1127 >>> print(clifford("{1}").reverse())
1128 {1}
1129 >>> print((clifford("{2}") * clifford("{1}")).reverse())
1130 {1,2}
1131 >>> print((clifford("{1}") * clifford("{2}")).reverse())
1132 -{1,2}
1133 >>> print(clifford("1+{1}+{1,2}").reverse())
1134 1+{1}-{1,2}
1135 """
1136 return clifford().wrap( self.instance.reverse() )
1137
1138 def conj(self):
1139 """
1140 Conjugation, reverse o involute == involute o reverse.
1141
1142 >>> print((clifford("{1}")).conj())
1143 -{1}
1144 >>> print((clifford("{2}") * clifford("{1}")).conj())
1145 {1,2}
1146 >>> print((clifford("{1}") * clifford("{2}")).conj())
1147 -{1,2}
1148 >>> print(clifford("1+{1}+{1,2}").conj())
1149 1-{1}-{1,2}
1150 """
1151 return clifford().wrap( self.instance.conj() )
1152
1153 def quad(self):
1154 """
1155 Quadratic form == (rev(x)*x)(0).
1156
1157 >>> print(clifford("1+{1}+{1,2}").quad())
1158 3.0
1159 >>> print(clifford("1+{-1}+{1,2}+{1,2,3}").quad())
1160 2.0
1161 """
1162 return self.instance.quad()
1163
1164 def norm(self):
1165 """
1166 Norm == sum of squares of coordinates.
1167
1168 >>> clifford("1+{1}+{1,2}").norm()
1169 3.0
1170 >>> clifford("1+{-1}+{1,2}+{1,2,3}").norm()
1171 4.0
1172 """
1173 return self.instance.norm()
1174
1175 def abs(self):
1176 """
1177 Absolute value: square root of norm.
1178
1179 >>> clifford("1+{-1}+{1,2}+{1,2,3}").abs()
1180 2.0
1181 """
1182 return glucat.abs( self.unwrap() )
1183
1184 def max_abs(self):
1185 """
1186 Maximum of absolute values of components of multivector: multivector infinity norm.
1187
1188 >>> clifford("1+{-1}+{1,2}+{1,2,3}").max_abs()
1189 1.0
1190 >>> clifford("3+2{1}+{1,2}").max_abs()
1191 3.0
1192 """
1193 return self.instance.max_abs()
1194
1195 def truncated(self, limit):
1196 """
1197 Remove all terms of self with relative size smaller than limit.
1198
1199 >>> clifford("1e8+{1}+1e-8{1,2}").truncated(1.0e-6)
1200 clifford("100000000")
1201 >>> clifford("1e4+{1}+1e-4{1,2}").truncated(1.0e-6)
1202 clifford("10000+{1}")
1203 """
1204 return clifford().wrap( self.instance.truncated(limit) )
1205
1206 def isinf(self):
1207 """
1208 Check if a multivector contains any infinite values.
1209
1210 >>> clifford().isinf()
1211 False
1212 """
1213 return self.instance.isnan()
1214
1215 def isnan(self):
1216 """
1217 Check if a multivector contains any IEEE NaN values.
1218
1219 >>> clifford().isnan()
1220 False
1221 """
1222 return self.instance.isnan()
1223
1224 def frame(self):
1225 """
1226 Subalgebra generated by all generators of terms of given multivector.
1227
1228 >>> print(clifford("1+3{-1}+2{1,2}+4{-2,7}").frame())
1229 {-2,-1,1,2,7}
1230 >>> s=clifford("1+3{-1}+2{1,2}+4{-2,7}").frame(); type(s)
1231 <class 'PyClical.index_set'>
1232 """
1233 return index_set().wrap( self.instance.frame() )
1234
1235 def __repr__(self):
1236 """
1237 The “official” string representation of self.
1238
1239 >>> clifford("1+3{-1}+2{1,2}+4{-2,7}").__repr__()
1240 'clifford("1+3{-1}+2{1,2}+4{-2,7}")'
1241 """
1242 return clifford_to_repr( self.unwrap() ).decode()
1243
1244 def __str__(self):
1245 """
1246 The “informal” string representation of self.
1247
1248 >>> clifford("1+3{-1}+2{1,2}+4{-2,7}").__str__()
1249 '1+3{-1}+2{1,2}+4{-2,7}'
1250 """
1251 return clifford_to_str( self.unwrap() ).decode()
1252
1254 """
1255 Tests for functions that Doctest cannot see.
1256
1257 For clifford.__cinit__: Construct an object of type clifford.
1258
1259 >>> print(clifford(2))
1260 2
1261 >>> print(clifford(2.0))
1262 2
1263 >>> print(clifford(1.0e-1))
1264 0.1
1265 >>> print(clifford("2"))
1266 2
1267 >>> print(clifford("2{1,2,3}"))
1268 2{1,2,3}
1269 >>> print(clifford(clifford("2{1,2,3}")))
1270 2{1,2,3}
1271 >>> print(clifford("-{1}"))
1272 -{1}
1273 >>> print(clifford(2,index_set({1,2})))
1274 2{1,2}
1275 >>> print(clifford([2,3],index_set({1,2})))
1276 2{1}+3{2}
1277 >>> print(clifford([1,2]))
1278 Traceback (most recent call last):
1279 ...
1280 TypeError: Cannot initialize clifford object from <class 'list'>.
1281 >>> print(clifford(None))
1282 Traceback (most recent call last):
1283 ...
1284 TypeError: Cannot initialize clifford object from <class 'NoneType'>.
1285 >>> print(clifford(None,[1,2]))
1286 Traceback (most recent call last):
1287 ...
1288 TypeError: Cannot initialize clifford object from (<class 'NoneType'>, <class 'list'>).
1289 >>> print(clifford([1,2],[1,2]))
1290 Traceback (most recent call last):
1291 ...
1292 TypeError: Cannot initialize clifford object from (<class 'list'>, <class 'list'>).
1293 >>> print(clifford(""))
1294 Traceback (most recent call last):
1295 ...
1296 ValueError: Cannot initialize clifford object from invalid string ''.
1297 >>> print(clifford("{"))
1298 Traceback (most recent call last):
1299 ...
1300 ValueError: Cannot initialize clifford object from invalid string '{'.
1301 >>> print(clifford("{1"))
1302 Traceback (most recent call last):
1303 ...
1304 ValueError: Cannot initialize clifford object from invalid string '{1'.
1305 >>> print(clifford("+"))
1306 Traceback (most recent call last):
1307 ...
1308 ValueError: Cannot initialize clifford object from invalid string '+'.
1309 >>> print(clifford("-"))
1310 Traceback (most recent call last):
1311 ...
1312 ValueError: Cannot initialize clifford object from invalid string '-'.
1313 >>> print(clifford("{1}+"))
1314 Traceback (most recent call last):
1315 ...
1316 ValueError: Cannot initialize clifford object from invalid string '{1}+'.
1317
1318 For clifford.__richcmp__: Compare objects of type clifford.
1319
1320 >>> clifford("{1}") == clifford("1{1}")
1321 True
1322 >>> clifford("{1}") != clifford("1.0{1}")
1323 False
1324 >>> clifford("{1}") != clifford("1.0")
1325 True
1326 >>> clifford("{1,2}") == None
1327 False
1328 >>> clifford("{1,2}") != None
1329 True
1330 >>> None == clifford("{1,2}")
1331 False
1332 >>> None != clifford("{1,2}")
1333 True
1334 """
1335 return
1336
1337cpdef inline error_squared_tol(obj):
1338 """
1339 Quadratic norm error tolerance relative to a specific multivector.
1340
1341 >>> print(error_squared_tol(clifford("{1}")) * 3.0 - error_squared_tol(clifford("1{1}-2{2}+3{3}")))
1342 0.0
1343 """
1344 return glucat.error_squared_tol(toClifford(obj))
1345
1346cpdef inline error_squared(lhs, rhs, threshold):
1347 """
1348 Relative or absolute error using the quadratic norm.
1349
1350 >>> err2=scalar_epsilon*scalar_epsilon
1351
1352 >>> print(error_squared(clifford("{1}"), clifford("1{1}"), err2))
1353 0.0
1354 >>> print(error_squared(clifford("1{1}-3{2}+4{3}"), clifford("{1}"), err2))
1355 25.0
1356 """
1357 return glucat.error_squared(toClifford(lhs), toClifford(rhs), <scalar_t>threshold)
1358
1359cpdef inline approx_equal(lhs, rhs, threshold=None, tol=None):
1360 """
1361 Test for approximate equality of multivectors.
1362
1363 >>> err2=scalar_epsilon*scalar_epsilon
1364
1365 >>> print(approx_equal(clifford("{1}"), clifford("1{1}")))
1366 True
1367 >>> print(approx_equal(clifford("1{1}-3{2}+4{3}"), clifford("{1}")))
1368 False
1369 >>> print(approx_equal(clifford("1{1}-3{2}+4{3}+0.001"), clifford("1{1}-3{2}+4{3}"), err2, err2))
1370 False
1371 >>> print(approx_equal(clifford("1{1}-3{2}+4{3}+1.0e-30"), clifford("1{1}-3{2}+4{3}"), err2, err2))
1372 True
1373 """
1374 threshold = error_squared_tol(rhs) if threshold is None else threshold
1375 tol = error_squared_tol(rhs) if tol is None else tol
1376 return glucat.approx_equal(toClifford(lhs), toClifford(rhs), <scalar_t>threshold, <scalar_t>tol)
1377
1378cpdef inline inv(obj):
1379 """
1380 Geometric multiplicative inverse.
1381
1382 >>> print(inv(clifford("{1}")))
1383 {1}
1384 >>> print(inv(clifford("{-1}")))
1385 -{-1}
1386 >>> print(inv(clifford("{-2,-1}")))
1387 -{-2,-1}
1388 >>> print(inv(clifford("{-1}+{1}")))
1389 nan
1390 """
1391 return clifford(obj).inv()
1392
1393cpdef inline scalar(obj):
1394 """
1395 Scalar part.
1396
1397 >>> scalar(clifford("1+{1}+{1,2}"))
1398 1.0
1399 >>> scalar(clifford("{1,2}"))
1400 0.0
1401 """
1402 return clifford(obj).scalar()
1403
1404cpdef inline real(obj):
1405 """
1406 Real part: synonym for scalar part.
1407
1408 >>> real(clifford("1+{1}+{1,2}"))
1409 1.0
1410 >>> real(clifford("{1,2}"))
1411 0.0
1412 """
1413 return clifford(obj).scalar()
1414
1415cpdef inline imag(obj):
1416 """
1417 Imaginary part: deprecated (always 0).
1418
1419 >>> imag(clifford("1+{1}+{1,2}"))
1420 0.0
1421 >>> imag(clifford("{1,2}"))
1422 0.0
1423 """
1424 return 0.0
1425
1426cpdef inline pure(obj):
1427 """
1428 Pure part
1429
1430 >>> print(pure(clifford("1+{1}+{1,2}")))
1431 {1}+{1,2}
1432 >>> print(pure(clifford("{1,2}")))
1433 {1,2}
1434 """
1435 return clifford(obj).pure()
1436
1437cpdef inline even(obj):
1438 """
1439 Even part of multivector, sum of even grade terms.
1440
1441 >>> print(even(clifford("1+{1}+{1,2}")))
1442 1+{1,2}
1443 """
1444 return clifford(obj).even()
1445
1446cpdef inline odd(obj):
1447 """
1448 Odd part of multivector, sum of odd grade terms.
1449
1450 >>> print(odd(clifford("1+{1}+{1,2}")))
1451 {1}
1452 """
1453 return clifford(obj).odd()
1454
1455cpdef inline involute(obj):
1456 """
1457 Main involution, each {i} is replaced by -{i} in each term, eg. {1}*{2} -> (-{2})*(-{1})
1458
1459 >>> print(involute(clifford("{1}")))
1460 -{1}
1461 >>> print(involute(clifford("{2}") * clifford("{1}")))
1462 -{1,2}
1463 >>> print(involute(clifford("{1}") * clifford("{2}")))
1464 {1,2}
1465 >>> print(involute(clifford("1+{1}+{1,2}")))
1466 1-{1}+{1,2}
1467 """
1468 return clifford(obj).involute()
1469
1470cpdef inline reverse(obj):
1471 """
1472 Reversion, eg. {1}*{2} -> {2}*{1}
1473
1474 >>> print(reverse(clifford("{1}")))
1475 {1}
1476 >>> print(reverse(clifford("{2}") * clifford("{1}")))
1477 {1,2}
1478 >>> print(reverse(clifford("{1}") * clifford("{2}")))
1479 -{1,2}
1480 >>> print(reverse(clifford("1+{1}+{1,2}")))
1481 1+{1}-{1,2}
1482 """
1483 return clifford(obj).reverse()
1484
1485cpdef inline conj(obj):
1486 """
1487 Conjugation, reverse o involute == involute o reverse.
1488
1489 >>> print(conj(clifford("{1}")))
1490 -{1}
1491 >>> print(conj(clifford("{2}") * clifford("{1}")))
1492 {1,2}
1493 >>> print(conj(clifford("{1}") * clifford("{2}")))
1494 -{1,2}
1495 >>> print(conj(clifford("1+{1}+{1,2}")))
1496 1-{1}-{1,2}
1497 """
1498 return clifford(obj).conj()
1499
1500cpdef inline quad(obj):
1501 """
1502 Quadratic form == (rev(x)*x)(0).
1503
1504 >>> print(quad(clifford("1+{1}+{1,2}")))
1505 3.0
1506 >>> print(quad(clifford("1+{-1}+{1,2}+{1,2,3}")))
1507 2.0
1508 """
1509 return clifford(obj).quad()
1510
1511cpdef inline norm(obj):
1512 """
1513 norm == sum of squares of coordinates.
1514
1515 >>> norm(clifford("1+{1}+{1,2}"))
1516 3.0
1517 >>> norm(clifford("1+{-1}+{1,2}+{1,2,3}"))
1518 4.0
1519 """
1520 return clifford(obj).norm()
1521
1522cpdef inline abs(obj):
1523 """
1524 Absolute value of multivector: multivector 2-norm.
1525
1526 >>> abs(clifford("1+{-1}+{1,2}+{1,2,3}"))
1527 2.0
1528 """
1529 return glucat.abs(toClifford(obj))
1530
1531cpdef inline max_abs(obj):
1532 """
1533 Maximum absolute value of coordinates multivector: multivector infinity-norm.
1534
1535 >>> max_abs(clifford("1+{-1}+{1,2}+{1,2,3}"))
1536 1.0
1537 >>> max_abs(clifford("3+2{1}+{1,2}"))
1538 3.0
1539
1540 """
1541 return glucat.max_abs(toClifford(obj))
1542
1543cpdef inline pow(obj, m):
1544 """
1545 Integer power of multivector: obj to the m.
1546
1547 >>> x=clifford("{1}"); print(pow(x,2))
1548 1
1549 >>> x=clifford("2"); print(pow(x,2))
1550 4
1551 >>> x=clifford("2+{1}"); print(pow(x,0))
1552 1
1553 >>> x=clifford("2+{1}"); print(pow(x,1))
1554 2+{1}
1555 >>> x=clifford("2+{1}"); print(pow(x,2))
1556 5+4{1}
1557 >>> print(pow(clifford("1+{1}+{1,2}"),3))
1558 1+3{1}+3{1,2}
1559 >>> i=clifford("{1,2}"); print(exp(pi/2) * pow(i, i))
1560 1
1561 """
1562 try:
1563 math.pow(obj, m)
1564 except:
1565 return clifford(obj).pow(m)
1566
1567cpdef inline outer_pow(obj, m):
1568 """
1569 Outer product power of multivector.
1570
1571 >>> print(outer_pow(clifford("1+{1}+{1,2}"),3))
1572 1+3{1}+3{1,2}
1573 """
1574 return clifford(obj).outer_pow(m)
1575
1576cpdef inline complexifier(obj):
1577 """
1578 Square root of -1 which commutes with all members of the frame of the given multivector.
1579
1580 >>> print(complexifier(clifford(index_set({1}))))
1581 {1,2,3}
1582 >>> print(complexifier(clifford(index_set({-1}))))
1583 {-1}
1584 >>> print(complexifier(index_set({1})))
1585 {1,2,3}
1586 >>> print(complexifier(index_set({-1})))
1587 {-1}
1588 """
1589 return clifford().wrap( glucat.complexifier(toClifford(obj)) )
1590
1591cpdef inline sqrt(obj, i = None):
1592 """
1593 Square root of multivector with optional complexifier.
1594
1595 >>> print(sqrt(-1))
1596 {-1}
1597 >>> print(sqrt(clifford("2{-1}")))
1598 1+{-1}
1599 >>> j=sqrt(-1,complexifier(index_set({1}))); print(j); print(j*j)
1600 {1,2,3}
1601 -1
1602 >>> j=sqrt(-1,"{1,2,3}"); print(j); print(j*j)
1603 {1,2,3}
1604 -1
1605 """
1606 if not (i is None):
1607 return clifford().wrap( glucat.sqrt(toClifford(obj), toClifford(i)) )
1608 else:
1609 try:
1610 return math.sqrt(obj)
1611 except:
1612 return clifford().wrap( glucat.sqrt(toClifford(obj)) )
1613
1614cpdef inline exp(obj):
1615 """
1616 Exponential of multivector.
1617
1618 >>> x=clifford("{1,2}") * pi/4; print(exp(x))
1619 0.7071+0.7071{1,2}
1620 >>> x=clifford("{1,2}") * pi/2; print(exp(x))
1621 {1,2}
1622 """
1623 try:
1624 return math.exp(obj)
1625 except:
1626 return clifford().wrap( glucat.exp(toClifford(obj)) )
1627
1628cpdef inline log(obj,i = None):
1629 """
1630 Natural logarithm of multivector with optional complexifier.
1631
1632 >>> x=clifford("{-1}"); print((log(x,"{-1}") * 2/pi))
1633 {-1}
1634 >>> x=clifford("{1,2}"); print((log(x,"{1,2,3}") * 2/pi))
1635 {1,2}
1636 >>> x=clifford("{1,2}"); print((log(x) * 2/pi))
1637 {1,2}
1638 >>> x=clifford("{1,2}"); print((log(x,"{1,2}") * 2/pi))
1639 Traceback (most recent call last):
1640 ...
1641 RuntimeError: check_complex(val, i): i is not a valid complexifier for val
1642 """
1643 if not (i is None):
1644 return clifford().wrap( glucat.log(toClifford(obj), toClifford(i)) )
1645 else:
1646 try:
1647 return math.log(obj)
1648 except:
1649 return clifford().wrap( glucat.log(toClifford(obj)) )
1650
1651cpdef inline cos(obj,i = None):
1652 """
1653 Cosine of multivector with optional complexifier.
1654
1655 >>> x=clifford("{1,2}"); print(cos(acos(x),"{1,2,3}"))
1656 {1,2}
1657 >>> x=clifford("{1,2}"); print(cos(acos(x)))
1658 {1,2}
1659 """
1660 if not (i is None):
1661 return clifford().wrap( glucat.cos(toClifford(obj), toClifford(i)) )
1662 else:
1663 try:
1664 return math.cos(obj)
1665 except:
1666 return clifford().wrap( glucat.cos(toClifford(obj)) )
1667
1668cpdef inline acos(obj,i = None):
1669 """
1670 Inverse cosine of multivector with optional complexifier.
1671
1672 >>> x=clifford("{1,2}"); print(cos(acos(x),"{1,2,3}"))
1673 {1,2}
1674 >>> x=clifford("{1,2}"); print(cos(acos(x),"{-1,1,2,3,4}"))
1675 {1,2}
1676 >>> print(acos(0) / pi)
1677 0.5
1678 >>> x=clifford("{1,2}"); print(cos(acos(x)))
1679 {1,2}
1680 """
1681 if not (i is None):
1682 return clifford().wrap( glucat.acos(toClifford(obj), toClifford(i)) )
1683 else:
1684 try:
1685 return math.acos(obj)
1686 except:
1687 return clifford().wrap( glucat.acos(toClifford(obj)) )
1688
1689cpdef inline cosh(obj):
1690 """
1691 Hyperbolic cosine of multivector.
1692
1693 >>> x=clifford("{1,2}") * pi; print(cosh(x))
1694 -1
1695 >>> x=clifford("{1,2,3}"); print(cosh(acosh(x)))
1696 {1,2,3}
1697 >>> x=clifford("{1,2}"); print(cosh(acosh(x)))
1698 {1,2}
1699 """
1700 try:
1701 return math.cosh(obj)
1702 except:
1703 return clifford().wrap( glucat.cosh(toClifford(obj)) )
1704
1705cpdef inline acosh(obj,i = None):
1706 """
1707 Inverse hyperbolic cosine of multivector with optional complexifier.
1708
1709 >>> print(acosh(0,"{-2,-1,1}"))
1710 1.571{-2,-1,1}
1711 >>> x=clifford("{1,2,3}"); print(cosh(acosh(x,"{-1,1,2,3,4}")))
1712 {1,2,3}
1713 >>> print(acosh(0))
1714 1.571{-1}
1715 >>> x=clifford("{1,2,3}"); print(cosh(acosh(x)))
1716 {1,2,3}
1717 >>> x=clifford("{1,2}"); print(cosh(acosh(x)))
1718 {1,2}
1719 """
1720 if not (i is None):
1721 return clifford().wrap( glucat.acosh(toClifford(obj), toClifford(i)) )
1722 else:
1723 try:
1724 return math.acosh(obj)
1725 except:
1726 return clifford().wrap( glucat.acosh(toClifford(obj)) )
1727
1728cpdef inline sin(obj,i = None):
1729 """
1730 Sine of multivector with optional complexifier.
1731
1732 >>> s="{-1}"; x=clifford(s); print(asin(sin(x,s),s))
1733 {-1}
1734 >>> s="{-1}"; x=clifford(s); print(asin(sin(x,s),"{-2,-1,1}"))
1735 {-1}
1736 >>> x=clifford("{1,2,3}"); print(asin(sin(x)))
1737 {1,2,3}
1738 """
1739 if not (i is None):
1740 return clifford().wrap( glucat.sin(toClifford(obj), toClifford(i)) )
1741 else:
1742 try:
1743 return math.sin(obj)
1744 except:
1745 return clifford().wrap( glucat.sin(toClifford(obj)) )
1746
1747cpdef inline asin(obj,i = None):
1748 """
1749 Inverse sine of multivector with optional complexifier.
1750
1751 >>> s="{-1}"; x=clifford(s); print(asin(sin(x,s),s))
1752 {-1}
1753 >>> s="{-1}"; x=clifford(s); print(asin(sin(x,s),"{-2,-1,1}"))
1754 {-1}
1755 >>> print(asin(1) / pi)
1756 0.5
1757 >>> x=clifford("{1,2,3}"); print(asin(sin(x)))
1758 {1,2,3}
1759 """
1760 if not (i is None):
1761 return clifford().wrap( glucat.asin(toClifford(obj), toClifford(i)) )
1762 else:
1763 try:
1764 return math.asin(obj)
1765 except:
1766 return clifford().wrap( glucat.asin(toClifford(obj)) )
1767
1768cpdef inline sinh(obj):
1769 """
1770 Hyperbolic sine of multivector.
1771
1772 >>> x=clifford("{1,2}") * pi/2; print(sinh(x))
1773 {1,2}
1774 >>> x=clifford("{1,2}") * pi/6; print(sinh(x))
1775 0.5{1,2}
1776 """
1777 try:
1778 return math.sinh(obj)
1779 except:
1780 return clifford().wrap( glucat.sinh(toClifford(obj)) )
1781
1782cpdef inline asinh(obj,i = None):
1783 """
1784 Inverse hyperbolic sine of multivector with optional complexifier.
1785
1786 >>> x=clifford("{1,2}"); print(asinh(x,"{1,2,3}") * 2/pi)
1787 {1,2}
1788 >>> x=clifford("{1,2}"); print(asinh(x) * 2/pi)
1789 {1,2}
1790 >>> x=clifford("{1,2}") / 2; print(asinh(x) * 6/pi)
1791 {1,2}
1792 """
1793 if not (i is None):
1794 return clifford().wrap( glucat.asinh(toClifford(obj), toClifford(i)) )
1795 else:
1796 try:
1797 return math.asinh(obj)
1798 except:
1799 return clifford().wrap( glucat.asinh(toClifford(obj)) )
1800
1801cpdef inline tan(obj,i = None):
1802 """
1803 Tangent of multivector with optional complexifier.
1804
1805 >>> x=clifford("{1,2}"); print(tan(x,"{1,2,3}"))
1806 0.7616{1,2}
1807 >>> x=clifford("{1,2}"); print(tan(x))
1808 0.7616{1,2}
1809 """
1810 if not (i is None):
1811 return clifford().wrap( glucat.tan(toClifford(obj), toClifford(i)) )
1812 else:
1813 try:
1814 return math.tan(obj)
1815 except:
1816 return clifford().wrap( glucat.tan(toClifford(obj)) )
1817
1818cpdef inline atan(obj,i = None):
1819 """
1820 Inverse tangent of multivector with optional complexifier.
1821
1822 >>> s=index_set({1,2,3}); x=clifford("{1}"); print(tan(atan(x,s),s))
1823 {1}
1824 >>> x=clifford("{1}"); print(tan(atan(x)))
1825 {1}
1826 """
1827 if not (i is None):
1828 return clifford().wrap( glucat.atan(toClifford(obj), toClifford(i)) )
1829 else:
1830 try:
1831 return math.atan(obj)
1832 except:
1833 return clifford().wrap( glucat.atan(toClifford(obj)) )
1834
1835cpdef inline tanh(obj):
1836 """
1837 Hyperbolic tangent of multivector.
1838
1839 >>> x=clifford("{1,2}") * pi/4; print(tanh(x))
1840 {1,2}
1841 """
1842 try:
1843 return math.tanh(obj)
1844 except:
1845 return clifford().wrap( glucat.tanh(toClifford(obj)) )
1846
1847cpdef inline atanh(obj,i = None):
1848 """
1849 Inverse hyperbolic tangent of multivector with optional complexifier.
1850
1851 >>> s=index_set({1,2,3}); x=clifford("{1,2}"); print(tanh(atanh(x,s)))
1852 {1,2}
1853 >>> x=clifford("{1,2}"); print(tanh(atanh(x)))
1854 {1,2}
1855 """
1856 if not (i is None):
1857 return clifford().wrap( glucat.atanh(toClifford(obj), toClifford(i)) )
1858 else:
1859 try:
1860 return math.atanh(obj)
1861 except:
1862 return clifford().wrap( glucat.atanh(toClifford(obj)) )
1863
1864cpdef inline random_clifford(index_set ixt, fill = 1.0):
1865 """
1866 Random multivector within a frame.
1867
1868 >>> print(random_clifford(index_set({-3,-1,2})).frame())
1869 {-3,-1,2}
1870 """
1871 return clifford().wrap( clifford().instance.random(ixt.unwrap(), <scalar_t>fill) )
1872
1873cpdef inline cga3(obj):
1874 """
1875 Convert Euclidean 3D multivector to Conformal Geometric Algebra using Doran and Lasenby definition.
1876
1877 >>> x=clifford("2{1}+9{2}+{3}"); print(cga3(x))
1878 87{-1}+4{1}+18{2}+2{3}+85{4}
1879 """
1880 return clifford().wrap( glucat.cga3(toClifford(obj)) )
1881
1882cpdef inline cga3std(obj):
1883 """
1884 Convert CGA3 null vector to standard conformal null vector using Doran and Lasenby definition.
1885
1886 >>> x=clifford("2{1}+9{2}+{3}"); print(cga3std(cga3(x)))
1887 87{-1}+4{1}+18{2}+2{3}+85{4}
1888 >>> x=clifford("2{1}+9{2}+{3}"); print(cga3std(cga3(x))-cga3(x))
1889 0
1890 """
1891 return clifford().wrap( glucat.cga3std(toClifford(obj)) )
1892
1893cpdef inline agc3(obj):
1894 """
1895 Convert CGA3 null vector to Euclidean 3D vector using Doran and Lasenby definition.
1896
1897 >>> x=clifford("2{1}+9{2}+{3}"); print(agc3(cga3(x)))
1898 2{1}+9{2}+{3}
1899 >>> x=clifford("2{1}+9{2}+{3}"); print(agc3(cga3(x))-x)
1900 0
1901 """
1902 return clifford().wrap( glucat.agc3(toClifford(obj)) )
1903
1904# Some abbreviations.
1905scalar_epsilon = epsilon
1906
1907pi = atan(clifford(1.0)) * 4.0
1908tau = atan(clifford(1.0)) * 8.0
1909
1910cl = clifford
1911"""
1912Abbreviation for clifford.
1913
1914>>> print(cl(2))
19152
1916>>> print(cl(2.0))
19172
1918>>> print(cl(5.0e-1))
19190.5
1920>>> print(cl("2"))
19212
1922>>> print(cl("2{1,2,3}"))
19232{1,2,3}
1924>>> print(cl(cl("2{1,2,3}")))
19252{1,2,3}
1926"""
1927
1928ist = index_set
1929"""
1930Abbreviation for index_set.
1931
1932>>> print(ist("{1,2,3}"))
1933{1,2,3}
1934"""
1935
1936def e(obj):
1937 """
1938 Abbreviation for clifford(index_set(obj)).
1939
1940 >>> print(e(1))
1941 {1}
1942 >>> print(e(-1))
1943 {-1}
1944 >>> print(e(0))
1945 1
1946 """
1947 return clifford(index_set(obj))
1948
1949def istpq(p, q):
1950 """
1951 Abbreviation for index_set({-q,...p}).
1952
1953 >>> print(istpq(2,3))
1954 {-3,-2,-1,1,2}
1955 """
1956 return index_set(set(range(-q,p+1)))
1957
1958ninf3 = e(4) + e(-1) # Null infinity point in 3D Conformal Geometric Algebra [DL].
1959nbar3 = e(4) - e(-1) # Null bar point in 3D Conformal Geometric Algebra [DL].
1960
1961# Doctest interface.
1962def _test():
1963 import PyClical, doctest
1964 return doctest.testmod(PyClical)
1965
1966if __name__ == "__main__":
1967 _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
__getitem__(self, ixt)
Definition PyClical.pyx:707
__and__(lhs, rhs)
Definition PyClical.pyx:836
__imod__(self, rhs)
Definition PyClical.pyx:821
truncated(self, limit)
__idiv__(self, rhs)
Definition PyClical.pyx:911
__cinit__(self, other=0, ixt=None)
Definition PyClical.pyx:565
__truediv__(lhs, rhs)
Definition PyClical.pyx:896
__imul__(self, rhs)
Definition PyClical.pyx:793
__add__(lhs, rhs)
Definition PyClical.pyx:740
__call__(self, grade)
__iadd__(self, rhs)
Definition PyClical.pyx:751
__pow__(self, m, dummy)
Definition PyClical.pyx:961
__ixor__(self, rhs)
Definition PyClical.pyx:881
__contains__(self, x)
Definition PyClical.pyx:627
__or__(lhs, rhs)
Definition PyClical.pyx:939
__iand__(self, rhs)
Definition PyClical.pyx:851
__isub__(self, rhs)
Definition PyClical.pyx:771
vector_part(self, frm=None)
__richcmp__(lhs, rhs, int, op)
Definition PyClical.pyx:672
reframe(self, ixt)
Definition PyClical.pyx:649
__sub__(lhs, rhs)
Definition PyClical.pyx:760
__ior__(self, rhs)
Definition PyClical.pyx:950
__mul__(lhs, rhs)
Definition PyClical.pyx:780
__xor__(lhs, rhs)
Definition PyClical.pyx:866
__mod__(lhs, rhs)
Definition PyClical.pyx:806
__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.