Skip to content

Commit

Permalink
sagemathgh-38508: Hashing affine group elements
Browse files Browse the repository at this point in the history
    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->

Fixes sagemath#38381 and makes sure the defining parts of the group element are
immutable.

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->
    
URL: sagemath#38508
Reported by: Travis Scrimshaw
Reviewer(s): Frédéric Chapoton, Matthias Köppe, Travis Scrimshaw
  • Loading branch information
Release Manager committed Dec 5, 2024
2 parents ff8b6ed + b10beae commit c2fb907
Showing 1 changed file with 51 additions and 11 deletions.
62 changes: 51 additions & 11 deletions src/sage/groups/affine_gps/group_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,17 @@
- Volker Braun
"""

#*****************************************************************************
# ****************************************************************************
# Copyright (C) 2013 Volker Braun <vbraun.name@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# http://www.gnu.org/licenses/
#*****************************************************************************
# https://www.gnu.org/licenses/
# ****************************************************************************

from copy import copy

from sage.structure.element import Matrix
from sage.misc.cachefunc import cached_method
Expand Down Expand Up @@ -119,15 +121,17 @@ def __init__(self, parent, A, b=0, convert=True, check=True):
A = A.matrix()
except AttributeError:
pass
if isinstance(A, Matrix) and A.nrows() == A.ncols() == parent.degree()+1:
if isinstance(A, Matrix) and A.nrows() == A.ncols() == parent.degree() + 1:
g = A
d = parent.degree()
A = g.submatrix(0, 0, d, d)
b = [ g[i,d] for i in range(d) ]
b = [g[i,d] for i in range(d)]
convert = True
if convert:
A = parent.matrix_space()(A)
b = parent.vector_space()(b)
A.set_immutable()
b.set_immutable()
if check:
# Note: the coercion framework expects that we raise TypeError for invalid input
if not isinstance(A, Matrix):
Expand All @@ -138,6 +142,12 @@ def __init__(self, parent, A, b=0, convert=True, check=True):
raise TypeError('b must be an element of ' + str(parent.vector_space()))
parent._element_constructor_check(A, b)
super().__init__(parent)
if not A.is_immutable():
A = copy(A)
A.set_immutable()
if not b.is_immutable():
b = copy(b)
b.set_immutable()
self._A = A
self._b = b

Expand All @@ -151,10 +161,12 @@ def A(self):
sage: G = AffineGroup(3, QQ)
sage: g = G([1,2,3,4,5,6,7,8,0], [10,11,12])
sage: g.A()
sage: A = g.A(); A
[1 2 3]
[4 5 6]
[7 8 0]
sage: A.is_immutable()
True
"""
return self._A

Expand All @@ -168,8 +180,10 @@ def b(self):
sage: G = AffineGroup(3, QQ)
sage: g = G([1,2,3,4,5,6,7,8,0], [10,11,12])
sage: g.b()
sage: b = g.b(); b
(10, 11, 12)
sage: b.is_immutable()
True
"""
return self._b

Expand Down Expand Up @@ -345,7 +359,9 @@ def _mul_(self, other):
parent = self.parent()
A = self._A * other._A
b = self._b + self._A * other._b
return parent.element_class(parent, A, b, check=False)
A.set_immutable()
b.set_immutable()
return parent.element_class(parent, A, b, convert=False, check=False)

def __call__(self, v):
"""
Expand Down Expand Up @@ -439,13 +455,14 @@ def _act_on_(self, x, self_on_left):
x |-> [0 1] x + [0]
sage: v = vector(GF(3), [1,-1]); v
(1, 2)
sage: g*v
sage: g * v
(1, 2)
sage: g*v == g.A() * v + g.b()
sage: g * v == g.A() * v + g.b()
True
"""
if self_on_left:
return self(x)
return None

def __invert__(self):
"""
Expand All @@ -472,7 +489,9 @@ def __invert__(self):
parent = self.parent()
A = parent.matrix_space()(~self._A)
b = -A * self.b()
return parent.element_class(parent, A, b, check=False)
A.set_immutable()
b.set_immutable()
return parent.element_class(parent, A, b, convert=False, check=False)

def _richcmp_(self, other, op):
"""
Expand All @@ -497,6 +516,27 @@ def _richcmp_(self, other, op):

return richcmp(self._b, other._b, op)

def __hash__(self):
"""
Return the hash of ``self``.
OUTPUT: int
EXAMPLES::
sage: F = AffineGroup(3, QQ)
sage: g = F([1,2,3,4,5,6,7,8,0], [10,11,12])
sage: h = F([1,2,3,4,5,6,7,8,0], [10,11,0])
sage: hash(g) == hash(h)
False
sage: hash(g) == hash(copy(g))
True
sage: f = g * h
sage: hash(f) == hash(~f)
False
"""
return hash((self._A, self._b))

def list(self):
"""
Return list representation of ``self``.
Expand Down

0 comments on commit c2fb907

Please sign in to comment.