glucat  0.12.1
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 
29 import math
30 import numbers
31 import collections
32 
33 from PyClical cimport *
34 
35 __version__ = str(glucat_package_version,'utf-8')
36 
37 # Forward reference
38 cdef class index_set
39 
40 cdef 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 
46 cdef 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 
492 cpdef 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 
504 cpdef 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 
513 cpdef 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 
522 cdef 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.
532 cdef class clifford
533 
534 cdef inline Clifford toClifford(obj):
535  return clifford(obj).instance[0]
536 
537 cdef 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 
1400 cpdef 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 
1409 cpdef 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 
1422 cpdef 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 
1441 cpdef 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 
1456 cpdef 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 
1467 cpdef 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 
1478 cpdef 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 
1489 cpdef 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 
1500 cpdef 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 
1509 cpdef 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 
1518 cpdef 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 
1533 cpdef 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 
1548 cpdef 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 
1563 cpdef 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 
1574 cpdef 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 
1585 cpdef 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 
1594 cpdef 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 
1606 cpdef 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 
1630 cpdef 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 
1639 cpdef 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 
1654 cpdef 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 
1677 cpdef 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 
1691 cpdef 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 
1714 cpdef 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 
1731 cpdef 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 
1752 cpdef 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 
1768 cpdef 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 
1791 cpdef 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 
1810 cpdef 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 
1831 cpdef 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 
1845 cpdef 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 
1864 cpdef 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 
1881 cpdef 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 
1898 cpdef 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 
1910 cpdef 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 
1927 cpdef 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 
1936 cpdef 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 
1945 cpdef 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 
1956 cpdef 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.
1968 scalar_epsilon = epsilon
1969 
1970 pi = atan(clifford(1.0)) * 4.0
1971 tau = atan(clifford(1.0)) * 8.0
1972 
1973 cl = clifford
1974 """
1975 Abbreviation for clifford.
1976 
1977 >>> print(cl(2))
1978 2
1979 >>> print(cl(2.0))
1980 2
1981 >>> print(cl(5.0e-1))
1982 0.5
1983 >>> print(cl("2"))
1984 2
1985 >>> print(cl("2{1,2,3}"))
1986 2{1,2,3}
1987 >>> print(cl(cl("2{1,2,3}")))
1988 2{1,2,3}
1989 """
1990 
1991 ist = index_set
1992 """
1993 Abbreviation for index_set.
1994 
1995 >>> print(ist("{1,2,3}"))
1996 {1,2,3}
1997 """
1998 
1999 def 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 
2012 def 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 
2021 ninf3 = e(4) + e(-1) # Null infinity point in 3D Conformal Geometric Algebra [DL].
2022 nbar3 = e(4) - e(-1) # Null bar point in 3D Conformal Geometric Algebra [DL].
2023 
2024 # Doctest interface.
2025 def _test():
2026  import PyClical, doctest
2027  return doctest.testmod(PyClical)
2028 
2029 if __name__ == "__main__":
2030  _test()
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.
def __ior__(self, rhs)
Definition: PyClical.pyx:304
def __getitem__(self, idx)
Definition: PyClical.pyx:191
def __imul__(self, rhs)
Definition: PyClical.pyx:820
def __invert__(self)
Definition: PyClical.pyx:240
def __iand__(self, rhs)
Definition: PyClical.pyx:282
def involute(self)
Definition: PyClical.pyx:1170
def __pos__(self)
Definition: PyClical.pyx:731
def istpq(p, q)
Definition: PyClical.pyx:2012
String clifford_to_str(const Multivector_T &mv)
The "informal" string representation of Multivector_T mv.
Definition: PyClical.h:86
def count(self)
Definition: PyClical.pyx:315
String index_set_to_repr(const Index_Set_T &ist)
The “official” string representation of Index_Set_T ist.
Definition: PyClical.h:57
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 exp(const matrix_multi< Scalar_T, LO, HI, Tune_P > &val) -> const matrix_multi< Scalar_T, LO, HI, Tune_P >
Exponential of multivector.
auto asin(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Inverse sine of multivector.
def __xor__(lhs, rhs)
Definition: PyClical.pyx:249
auto tan(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Tangent of multivector.
def __sub__(lhs, rhs)
Definition: PyClical.pyx:769
auto max_pos(const index_set< LO, HI > &ist) -> index_t
Maximum positive index, or 0 if none.
auto odd(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Odd part.
auto min_neg(const index_set< LO, HI > &ist) -> index_t
Minimum negative index, or 0 if none.
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.
def __idiv__(self, rhs)
Definition: PyClical.pyx:974
def __mul__(lhs, rhs)
Definition: PyClical.pyx:798
auto norm(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> Scalar_T
Scalar_T norm == sum of norm of coordinates.
def sign_of_square(self)
Definition: PyClical.pyx:375
auto scalar(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> Scalar_T
Scalar part.
def __rmod__(rhs, lhs)
Definition: PyClical.pyx:848
def reframe(self, ixt)
Definition: PyClical.pyx:649
auto atanh(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Inverse hyperbolic tangent of multivector.
def count_pos(self)
Definition: PyClical.pyx:333
def outer_pow(self, m)
Definition: PyClical.pyx:1067
def __neg__(self)
Definition: PyClical.pyx:722
def __rxor__(rhs, lhs)
Definition: PyClical.pyx:926
def __getitem__(self, ixt)
Definition: PyClical.pyx:707
def __xor__(lhs, rhs)
Definition: PyClical.pyx:911
auto even(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Even part.
def __rand__(rhs, lhs)
Definition: PyClical.pyx:887
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 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.
def __contains__(self, idx)
Definition: PyClical.pyx:210
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 sinh(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Hyperbolic sine of multivector.
def __truediv__(lhs, rhs)
Definition: PyClical.pyx:950
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.
def __and__(lhs, rhs)
Definition: PyClical.pyx:872
String clifford_to_repr(const Multivector_T &mv)
The “official” string representation of Multivector_T mv.
Definition: PyClical.h:75
def count_neg(self)
Definition: PyClical.pyx:324
def __dealloc__(self)
Definition: PyClical.pyx:621
def __str__(self)
Definition: PyClical.pyx:395
def isnan(self)
Definition: PyClical.pyx:1278
def __or__(lhs, rhs)
Definition: PyClical.pyx:1002
def conj(self)
Definition: PyClical.pyx:1201
def __richcmp__(lhs, rhs, int, op)
Definition: PyClical.pyx:672
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.
def __or__(lhs, rhs)
Definition: PyClical.pyx:293
def __mod__(lhs, rhs)
Definition: PyClical.pyx:833
def __iadd__(self, rhs)
Definition: PyClical.pyx:760
auto atan(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Inverse tangent of multivector.
def __imod__(self, rhs)
Definition: PyClical.pyx:857
auto tanh(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Hyperbolic tangent of multivector.
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 acosh(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Inverse hyperbolic cosine of multivector.
auto outer_pow(const Multivector< Scalar_T, LO, HI, Tune_P > &lhs, int rhs) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Outer product power of multivector.
auto pure(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Pure part.
auto asinh(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Inverse hyperbolic sine of multivector.
auto abs(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> Scalar_T
Absolute value == sqrt(norm)
auto inv(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Geometric multiplicative inverse.
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}
def inv(self)
Definition: PyClical.pyx:989
def __richcmp__(lhs, rhs, int, op)
Definition: PyClical.pyx:122
auto acos(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Inverse cosine of multivector.
auto sqrt(const matrix_multi< Scalar_T, LO, HI, Tune_P > &val, const matrix_multi< Scalar_T, LO, HI, Tune_P > &i, bool prechecked) -> const matrix_multi< Scalar_T, LO, HI, Tune_P >
Square root of multivector with specified complexifier.
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.
def __str__(self)
Definition: PyClical.pyx:1307
def isinf(self)
Definition: PyClical.pyx:1269
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.
def sign_of_mult(self, rhs)
Definition: PyClical.pyx:366
def __cinit__(self, other=0)
Definition: PyClical.pyx:74
def __ixor__(self, rhs)
Definition: PyClical.pyx:935
Definitions for 3D Conformal Geometric Algebra [DL].
Definition: PyClical.h:99
auto involute(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Main involution, each {i} is replaced by -{i} in each term, eg. {1}*{2} -> (-{2})*(-{1}) ...
def __rtruediv__(rhs, lhs)
Definition: PyClical.pyx:965
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.
def hash_fn(self)
Definition: PyClical.pyx:360
def __repr__(self)
Definition: PyClical.pyx:1298
def __call__(self, grade)
Definition: PyClical.pyx:1083
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.
def __radd__(rhs, lhs)
Definition: PyClical.pyx:751
def __iter__(self)
Definition: PyClical.pyx:638
def truncated(self, limit)
Definition: PyClical.pyx:1258
def reverse(self)
Definition: PyClical.pyx:1186
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.
def scalar(self)
Definition: PyClical.pyx:1102
def __ixor__(self, rhs)
Definition: PyClical.pyx:260
def pure(self)
Definition: PyClical.pyx:1113
def __iter__(self)
Definition: PyClical.pyx:229
auto reverse(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Reversion, eg. {1}*{2} -> {2}*{1}.
def __pow__(self, m, dummy)
Definition: PyClical.pyx:1024
auto sin(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Sine of multivector.
auto approx_equal(const Multivector< Scalar_T, LO, HI, Tune_P > &lhs, const RHS< Scalar_T, LO, HI, Tune_P > &rhs) -> bool
Test for approximate equality of multivectors.
def max_abs(self)
Definition: PyClical.pyx:1247
def even(self)
Definition: PyClical.pyx:1124
def __dealloc__(self)
Definition: PyClical.pyx:116
def __ior__(self, rhs)
Definition: PyClical.pyx:1013
def __setitem__(self, idx, val)
Definition: PyClical.pyx:179
def __contains__(self, x)
Definition: PyClical.pyx:627
auto pow(const Multivector< Scalar_T, LO, HI, Tune_P > &lhs, int rhs) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Integer power of multivector.
auto log(const matrix_multi< Scalar_T, LO, HI, Tune_P > &val, const matrix_multi< Scalar_T, LO, HI, Tune_P > &i, bool prechecked) -> const matrix_multi< Scalar_T, LO, HI, Tune_P >
Natural logarithm of multivector with specified complexifier.
def __isub__(self, rhs)
Definition: PyClical.pyx:789
String index_set_to_str(const Index_Set_T &ist)
The "informal" string representation of Index_Set_T ist.
Definition: PyClical.h:66
auto real(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> Scalar_T
Real part: synonym for scalar part.
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 cosh(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Hyperbolic cosine of multivector.
def pow(self, m)
Definition: PyClical.pyx:1043
def norm(self)
Definition: PyClical.pyx:1227
def __cinit__(self, other=0, ixt=None)
Definition: PyClical.pyx:565
def index_set_hidden_doctests()
Definition: PyClical.pyx:406
def clifford_hidden_doctests()
Definition: PyClical.pyx:1316
def quad(self)
Definition: PyClical.pyx:1216
auto imag(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> Scalar_T
Imaginary part: deprecated (always 0)
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 conj(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Conjugation, rev o invo == invo o rev.
def __add__(lhs, rhs)
Definition: PyClical.pyx:740
def e(obj)
Definition: PyClical.pyx:1999
auto quad(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> Scalar_T
Scalar_T quadratic form == (rev(x)*x)(0)
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.
def frame(self)
Definition: PyClical.pyx:1287
def __rsub__(rhs, lhs)
Definition: PyClical.pyx:780
def __and__(lhs, rhs)
Definition: PyClical.pyx:271
auto cos(const Multivector< Scalar_T, LO, HI, Tune_P > &val) -> const Multivector< Scalar_T, LO, HI, Tune_P >
Cosine of multivector.
def _test()
Definition: PyClical.pyx:2025
Multivector_T cga3std(const Multivector_T &X)
Convert CGA3 null vector to standard Conformal Geometric Algebra null vector [DL (10.52)].
Definition: PyClical.h:114
def __repr__(self)
Definition: PyClical.pyx:384
def __iand__(self, rhs)
Definition: PyClical.pyx:896
def __rmul__(rhs, lhs)
Definition: PyClical.pyx:811
def vector_part(self, frm=None)
Definition: PyClical.pyx:1142
Multivector_T agc3(const Multivector_T &X)
Convert CGA3 null vector to Euclidean 3D vector [DL (10.50)].
Definition: PyClical.h:126