Skip to content

Commit cb51da1

Browse files
author
Release Manager
committed
Trac #17965: Uniformize the API to compute the inverse of an element
Some classes in Sage implement the inverse of an element through the inverse method: {{{ mistral-/opt/sage/src/sage>grep "def inverse(" **/*.py algebras/finite_dimensional_algebras/finite_dimensional_algebra_element. py: def inverse(self): algebras/iwahori_hecke_algebra.py: def inverse(self): categories/coxeter_groups.py: def inverse(self): combinat/affine_permutation.py: def inverse(self): combinat/permutation.py: def inverse(self): combinat/tableau_tuple.py: def inverse(self,k): crypto/classical_cipher.py: def inverse(self): crypto/classical_cipher.py: def inverse(self): crypto/classical_cipher.py: def inverse(self): crypto/classical_cipher.py: def inverse(self): dynamics/interval_exchanges/iet.py: def inverse(self): groups/abelian_gps/element_base.py: def inverse(self): groups/abelian_gps/values.py: def inverse(self): groups/affine_gps/group_element.py: def inverse(self): modules/matrix_morphism.py: def inverse(self): rings/number_field/class_group.py: def inverse(self): rings/universal_cyclotomic_field/universal_cyclotomic_field.py: def inverse(self): schemes/elliptic_curves/formal_group.py: def inverse(self, prec=20): schemes/elliptic_curves/weierstrass_transform.py: def inverse(self): }}} Some other through the `__invert__` method: {{{ mistral-/opt/sage/src/sage>grep "def __invert__(" **/*.py categories/algebras_with_basis.py: def __invert__(self): categories/magmas.py: def __invert__(self): categories/modules_with_basis.py: def __invert__(self): categories/modules_with_basis.py: def __invert__(self): combinat/combinatorial_algebra.py: def __invert__(self): combinat/sf/dual.py: def __invert__(self): combinat/species/generating_series.py: def __invert__(self): groups/indexed_free_group.py: def __invert__(self): groups/indexed_free_group.py: def __invert__(self): groups/matrix_gps/group_element.py: def __invert__(self): groups/raag.py: def __invert__(self): libs/coxeter3/coxeter_group.py: def __invert__(self): logic/boolformula.py: def __invert__(self): misc/sage_input.py: def __invert__(self): modular/dirichlet.py: def __invert__(self): modular/local_comp/smoothchar.py: def __invert__(self): modules/matrix_morphism.py: def __invert__(self): rings/continued_fraction.py: def __invert__(self): rings/finite_rings/element_ext_pari.py: def __invert__(self): rings/function_field/function_field_ideal.py: def __invert__(self): rings/infinity.py: def __invert__(self): rings/multi_power_series_ring_element.py: def __invert__(self): rings/number_field/morphism.py: def __invert__(self): rings/number_field/number_field_ideal.py: def __invert__(self): rings/number_field/number_field_ideal_rel.py: def __invert__(self): rings/pari_ring.py: def __invert__(self): rings/polynomial/polynomial_quotient_ring_element.py: def __invert__(self): rings/qqbar.py: def __invert__(self): rings/quotient_ring_element.py: def __invert__(self): rings/universal_cyclotomic_field/universal_cyclotomic_field.py: def __invert__(self): sandpiles/sandpile.py: def __invert__(self): schemes/elliptic_curves/heegner.py: def __invert__(self): schemes/elliptic_curves/height.py: def __invert__(self): schemes/elliptic_curves/weierstrass_morphism.py: def __invert__(self): schemes/elliptic_curves/weierstrass_morphism.py: def __invert__(self): schemes/hyperelliptic_curves/monsky_washnitzer.py: def __invert__(self): structure/factorization.py: def __invert__(self): }}} Usually they provide a crosslink so that `__invert__` and `inverse` are equivalent, but this is done on a case by case bases, so of course such links are missing here and there: {{{ sage: ~AA(sqrt(~2)) 1.414213562373095? sage: AA(sqrt(~2)).inverse() ... AttributeError: 'AlgebraicReal' object has no attribute 'inverse' }}} {{{ sage: R.<u,v,w> = QQ[] sage: f = EllipticCurve_from_cubic(u^3 + v^3 + w^3, [1,-1,0], morphism=True) sage: f.inverse() Scheme morphism: ... sage: ~f ... TypeError: bad operand type for unary ~: 'WeierstrassTransformationWithInverse_class' }}} Shall we change the code to systematically implement `__invert__` as per Python's convention, and then implement the cross link `inverse` -> `__invert__` once for all high up in the class hierarchy, typically in `Magmas.ElementMethods`? Caveat: this won't cover all cases since we have invertible elements that don't belong to a magma; e.g. a isomorphisms between two different parents; so it will still be necessary to handle a couple special cases by hand. See also comment about `__inverse__` in sage.categories.coxeter_groups.py around line 699. Note: the default implementation ~f = 1/f provided by Element should probably be implemented in `Monoids.ElementMethods`; see also #17692. Note: the qqbar classes also implement an `invert` method, but that's for a slightly different use case. So we may, or not, want to make this uniform too. `invert` does not fit Sage's usual verb/noun convention since it's a verb while it is not inplace. URL: https://trac.sagemath.org/17965 Reported by: nthiery Ticket author(s): Frédéric Chapoton Reviewer(s): Travis Scrimshaw
2 parents a833b0e + 312a91d commit cb51da1

21 files changed

+141
-106
lines changed

src/sage/algebras/hecke_algebras/ariki_koike_algebra.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -1125,7 +1125,7 @@ def inverse_T(self, i):
11251125
return self._from_dict({m: ~self._q, self.one_basis(): c})
11261126

11271127
class Element(CombinatorialFreeModule.Element):
1128-
def inverse(self):
1128+
def __invert__(self):
11291129
r"""
11301130
Return the inverse if ``self`` is a basis element.
11311131
@@ -1134,7 +1134,7 @@ def inverse(self):
11341134
sage: LT = algebras.ArikiKoike(3, 4).LT()
11351135
sage: t = LT.T(1) * LT.T(2) * LT.T(3); t
11361136
T[1,2,3]
1137-
sage: t.inverse()
1137+
sage: t.inverse() # indirect doctest
11381138
(q^-3-3*q^-2+3*q^-1-1) + (q^-3-2*q^-2+q^-1)*T[3]
11391139
+ (q^-3-2*q^-2+q^-1)*T[2] + (q^-3-q^-2)*T[3,2]
11401140
+ (q^-3-2*q^-2+q^-1)*T[1] + (q^-3-q^-2)*T[1,3]
@@ -1148,8 +1148,6 @@ def inverse(self):
11481148
H = self.parent()
11491149
return ~self[l,w] * H.prod(H.inverse_T(i) for i in reversed(w.reduced_word()))
11501150

1151-
__invert__ = inverse
1152-
11531151
class T(_Basis):
11541152
r"""
11551153
The basis of the Ariki-Koike algebra given by monomials of the

src/sage/algebras/iwahori_hecke_algebra.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -1732,7 +1732,7 @@ class Element(CombinatorialFreeModule.Element):
17321732
sage: T1.parent()
17331733
Iwahori-Hecke algebra of type A2 in 1,-1 over Integer Ring in the T-basis
17341734
"""
1735-
def inverse(self):
1735+
def __invert__(self):
17361736
r"""
17371737
Return the inverse if ``self`` is a basis element.
17381738
@@ -1746,7 +1746,7 @@ def inverse(self):
17461746
sage: R.<q> = LaurentPolynomialRing(QQ)
17471747
sage: H = IwahoriHeckeAlgebra("A2", q).T()
17481748
sage: [T1,T2] = H.algebra_generators()
1749-
sage: x = (T1*T2).inverse(); x
1749+
sage: x = (T1*T2).inverse(); x # indirect doctest
17501750
(q^-2)*T[2,1] + (q^-2-q^-1)*T[1] + (q^-2-q^-1)*T[2] + (q^-2-2*q^-1+1)
17511751
sage: x*T1*T2
17521752
1
@@ -1771,8 +1771,6 @@ def inverse(self):
17711771

17721772
return H.prod(H.inverse_generator(i) for i in reversed(w.reduced_word()))
17731773

1774-
__invert__ = inverse
1775-
17761774
standard = T
17771775

17781776
class _KLHeckeBasis(_Basis):

src/sage/algebras/yokonuma_hecke_algebra.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ def inverse_g(self, i):
454454
return self.g(i) + (~self._q + self._q) * self.e(i)
455455

456456
class Element(CombinatorialFreeModule.Element):
457-
def inverse(self):
457+
def __invert__(self):
458458
r"""
459459
Return the inverse if ``self`` is a basis element.
460460
@@ -463,7 +463,7 @@ def inverse(self):
463463
sage: Y = algebras.YokonumaHecke(3, 3)
464464
sage: t = prod(Y.t()); t
465465
t1*t2*t3
466-
sage: ~t
466+
sage: t.inverse() # indirect doctest
467467
t1^2*t2^2*t3^2
468468
sage: [3*~(t*g) for g in Y.g()]
469469
[(q^-1+q)*t2*t3^2 + (q^-1+q)*t1*t3^2
@@ -494,5 +494,3 @@ def inverse(self):
494494
c = ~self.coefficients()[0]
495495
telt = H.monomial( (tuple((H._d - e) % H._d for e in t), H._Pn.one()) )
496496
return c * telt * H.prod(H.inverse_g(i) for i in reversed(w.reduced_word()))
497-
498-
__invert__ = inverse

src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -1142,15 +1142,15 @@ def _mul_(self, other):
11421142
"""
11431143
return self.apply_simple_reflections(other.reduced_word())
11441144

1145-
def inverse(self):
1145+
def __invert__(self):
11461146
"""
11471147
Return the inverse of ``self``.
11481148
11491149
EXAMPLES::
11501150
11511151
sage: W = WeylGroup(['B',7])
11521152
sage: w = W.an_element()
1153-
sage: u = w.inverse()
1153+
sage: u = w.inverse() # indirect doctest
11541154
sage: u == ~w
11551155
True
11561156
sage: u * w == w * u
@@ -1166,8 +1166,6 @@ def inverse(self):
11661166
"""
11671167
return self.parent().one().apply_simple_reflections(self.reduced_word_reverse_iterator())
11681168

1169-
__invert__ = inverse
1170-
11711169
def apply_conjugation_by_simple_reflection(self, i):
11721170
r"""
11731171
Conjugate ``self`` by the ``i``-th simple reflection.

src/sage/categories/groups.py

-21
Original file line numberDiff line numberDiff line change
@@ -640,27 +640,6 @@ def order(self):
640640
from sage.misc.misc_c import prod
641641
return prod(c.cardinality() for c in self.cartesian_factors())
642642

643-
class ElementMethods:
644-
def multiplicative_order(self):
645-
r"""
646-
Return the multiplicative order of this element.
647-
648-
EXAMPLES::
649-
650-
sage: G1 = SymmetricGroup(3)
651-
sage: G2 = SL(2,3)
652-
sage: G = cartesian_product([G1,G2])
653-
sage: G((G1.gen(0), G2.gen(1))).multiplicative_order()
654-
12
655-
"""
656-
from sage.rings.infinity import Infinity
657-
orders = [x.multiplicative_order() for x in self.cartesian_factors()]
658-
if any(o is Infinity for o in orders):
659-
return Infinity
660-
else:
661-
from sage.arith.functions import LCM_list
662-
return LCM_list(orders)
663-
664643
class Topological(TopologicalSpacesCategory):
665644
"""
666645
Category of topological groups.

src/sage/categories/magmas.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -671,9 +671,7 @@ def __invert__(self):
671671
ZeroDivisionError: rational division by zero
672672
673673
sage: ~C([2,2,2,2])
674-
Traceback (most recent call last):
675-
...
676-
TypeError: no conversion of this rational to integer
674+
(1/2, 1/2, 0.500000000000000, 3)
677675
"""
678676
# variant without coercion:
679677
# return self.parent()._cartesian_product_of_elements(

src/sage/categories/monoids.py

+74-6
Original file line numberDiff line numberDiff line change
@@ -252,18 +252,18 @@ def _div_(left, right):
252252
sage: c1._div_(c2)
253253
(x0*x1^-1, x1*x0^-1)
254254
255-
With this implementation, division will fail as soon
256-
as ``right`` is not invertible, even if ``right``
255+
With this default implementation, division will fail as
256+
soon as ``right`` is not invertible, even if ``right``
257257
actually divides ``left``::
258258
259-
sage: x = cartesian_product([2, 1])
259+
sage: x = cartesian_product([2, 0])
260260
sage: y = cartesian_product([1, 1])
261261
sage: x / y
262-
(2, 1)
263-
sage: x / x
262+
(2, 0)
263+
sage: y / x
264264
Traceback (most recent call last):
265265
...
266-
TypeError: no conversion of this rational to integer
266+
ZeroDivisionError: rational division by zero
267267
268268
TESTS::
269269
@@ -354,6 +354,37 @@ def powers(self, n):
354354
l.append(x)
355355
return l
356356

357+
def __invert__(self):
358+
r"""
359+
Return the multiplicative inverse of ``self``.
360+
361+
There is no default implementation, to avoid conflict
362+
with the default implementation of ``_div_``.
363+
364+
EXAMPLES::
365+
366+
sage: A = Matrix([[1, 0], [1, 1]])
367+
sage: ~A
368+
[ 1 0]
369+
[-1 1]
370+
"""
371+
raise NotImplementedError("please implement __invert__")
372+
373+
def inverse(self):
374+
"""
375+
Return the multiplicative inverse of ``self``.
376+
377+
This is an alias for inversion, which can also be invoked
378+
by ``~x`` for an element ``x``.
379+
380+
EXAMPLES::
381+
382+
sage: AA(sqrt(~2)).inverse()
383+
1.414213562373095?
384+
"""
385+
# Nota Bene: Element classes should implement ``__invert__`` only.
386+
return self.__invert__()
387+
357388
class Commutative(CategoryWithAxiom):
358389
r"""
359390
Category of commutative (abelian) monoids.
@@ -639,3 +670,40 @@ def lift(i, gen):
639670
lambda g: (i, g))
640671
for i, M in enumerate(F)])
641672
return Family(gens_prod, lift, name="gen")
673+
674+
class ElementMethods:
675+
def multiplicative_order(self):
676+
r"""
677+
Return the multiplicative order of this element.
678+
679+
EXAMPLES::
680+
681+
sage: G1 = SymmetricGroup(3)
682+
sage: G2 = SL(2,3)
683+
sage: G = cartesian_product([G1,G2])
684+
sage: G((G1.gen(0), G2.gen(1))).multiplicative_order()
685+
12
686+
"""
687+
from sage.rings.infinity import Infinity
688+
orders = [x.multiplicative_order() for x in self.cartesian_factors()]
689+
if any(o is Infinity for o in orders):
690+
return Infinity
691+
else:
692+
from sage.arith.functions import LCM_list
693+
return LCM_list(orders)
694+
695+
def __invert__(self):
696+
"""
697+
Return the inverse.
698+
699+
EXAMPLES::
700+
701+
sage: a1 = Permutation((4,2,1,3))
702+
sage: a2 = SL(2,3)([2,1,1,1])
703+
sage: h = cartesian_product([a1,a2])
704+
sage: ~h
705+
([2, 4, 1, 3], [1 2]
706+
[2 2])
707+
"""
708+
build = self.parent()._cartesian_product_of_elements
709+
return build([x.__invert__() for x in self.cartesian_factors()])

src/sage/combinat/affine_permutation.py

+6-7
Original file line numberDiff line numberDiff line change
@@ -161,21 +161,19 @@ def __mul__(self, q):
161161
return self.__rmul__(q)
162162

163163
@cached_method
164-
def inverse(self):
164+
def __invert__(self):
165165
r"""
166166
Return the inverse affine permutation.
167167
168168
EXAMPLES::
169169
170170
sage: p = AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9])
171-
sage: p.inverse()
171+
sage: p.inverse() # indirect doctest
172172
Type A affine permutation with window [0, -1, 1, 6, 5, 4, 10, 11]
173173
"""
174-
inv = [self.position(i) for i in range(1,len(self)+1)]
174+
inv = [self.position(i) for i in range(1, len(self) + 1)]
175175
return type(self)(self.parent(), inv, check=False)
176176

177-
__invert__=inverse
178-
179177
def apply_simple_reflection(self, i, side='right'):
180178
r"""
181179
Apply a simple reflection.
@@ -855,8 +853,8 @@ def to_lehmer_code(self, typ='decreasing', side='right'):
855853
a=self(i)
856854
for j in range(i-self.k, i):
857855
b=self(j)
858-
#A small rotation is necessary for the reduced word from
859-
#the lehmer code to match the element.
856+
# A small rotation is necessary for the reduced word from
857+
# the Lehmer code to match the element.
860858
if a < b:
861859
code[i-1]+=((b-a)//(self.k+1)+1)
862860
elif typ[0] == 'i' and side[0] == 'l':
@@ -2330,6 +2328,7 @@ def from_lehmer_code(self, C, typ='decreasing', side='right'):
23302328

23312329
Element = AffinePermutationTypeA
23322330

2331+
23332332
class AffinePermutationGroupTypeC(AffinePermutationGroupGeneric):
23342333
#------------------------
23352334
#Type-specific methods.

src/sage/combinat/colored_permutations.py

+4-8
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,15 @@ def _mul_(self, other):
109109
p = self._perm._left_to_right_multiply_on_right(other._perm)
110110
return self.__class__(self.parent(), colors, p)
111111

112-
def inverse(self):
112+
def __invert__(self):
113113
"""
114114
Return the inverse of ``self``.
115115
116116
EXAMPLES::
117117
118118
sage: C = ColoredPermutations(4, 3)
119119
sage: s1,s2,t = C.gens()
120-
sage: ~t
120+
sage: ~t # indirect doctest
121121
[[0, 0, 3], [1, 2, 3]]
122122
sage: all(x * ~x == C.one() for x in C.gens())
123123
True
@@ -127,8 +127,6 @@ def inverse(self):
127127
tuple(-self._colors[i - 1] for i in ip), # -1 for indexing
128128
ip)
129129

130-
__invert__ = inverse
131-
132130
def __eq__(self, other):
133131
"""
134132
Check equality.
@@ -1030,7 +1028,7 @@ def _mul_(self, other):
10301028
p = self._perm._left_to_right_multiply_on_right(other._perm)
10311029
return self.__class__(self.parent(), colors, p)
10321030

1033-
def inverse(self):
1031+
def __invert__(self):
10341032
"""
10351033
Return the inverse of ``self``.
10361034
@@ -1039,7 +1037,7 @@ def inverse(self):
10391037
sage: S = SignedPermutations(4)
10401038
sage: s1,s2,s3,s4 = S.gens()
10411039
sage: x = s4*s1*s2*s3*s4
1042-
sage: ~x
1040+
sage: ~x # indirect doctest
10431041
[2, 3, -4, -1]
10441042
sage: x * ~x == S.one()
10451043
True
@@ -1049,8 +1047,6 @@ def inverse(self):
10491047
tuple(self._colors[i - 1] for i in ip), # -1 for indexing
10501048
ip)
10511049

1052-
__invert__ = inverse
1053-
10541050
def __iter__(self):
10551051
"""
10561052
Iterate over ``self``.

src/sage/combinat/root_system/fundamental_group.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -249,15 +249,15 @@ def _repr_(self):
249249
"""
250250
return self.parent()._prefix + "[" + repr(self.value()) + "]"
251251

252-
def inverse(self):
252+
def __invert__(self):
253253
r"""
254254
Return the inverse element of ``self``.
255255
256256
EXAMPLES::
257257
258258
sage: from sage.combinat.root_system.fundamental_group import FundamentalGroupOfExtendedAffineWeylGroup
259259
sage: F = FundamentalGroupOfExtendedAffineWeylGroup(['A',3,1])
260-
sage: F(1).inverse()
260+
sage: F(1).inverse() # indirect doctest
261261
pi[3]
262262
sage: F = FundamentalGroupOfExtendedAffineWeylGroup(['E',6,1], prefix="f")
263263
sage: F(1).inverse()
@@ -266,8 +266,6 @@ def inverse(self):
266266
par = self.parent()
267267
return self.__class__(par, par.dual_node(self.value()))
268268

269-
__invert__ = inverse
270-
271269
def _richcmp_(self, x, op):
272270
r"""
273271
Compare ``self`` with `x`.

src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -324,21 +324,19 @@ def matrix(self):
324324
"""
325325
return self._matrix
326326

327-
def inverse(self):
327+
def __invert__(self):
328328
r"""
329329
Return the inverse of the isometry ``self``.
330330
331331
EXAMPLES::
332332
333333
sage: UHP = HyperbolicPlane().UHP()
334334
sage: A = UHP.get_isometry(matrix(2,[4,1,3,2]))
335-
sage: B = A.inverse()
335+
sage: B = A.inverse() # indirect doctest
336336
sage: A*B == UHP.get_isometry(identity_matrix(2))
337337
True
338338
"""
339-
return self.__class__(self.domain(), self.matrix().inverse())
340-
341-
__invert__ = inverse
339+
return self.__class__(self.domain(), self.matrix().__invert__())
342340

343341
def is_identity(self):
344342
"""

0 commit comments

Comments
 (0)