From 76eed9317822ee68c25e1be174604b11ca79da20 Mon Sep 17 00:00:00 2001 From: cxzhong Date: Wed, 19 Nov 2025 17:09:48 +0800 Subject: [PATCH 1/6] fix the infinite loop --- src/sage/algebras/lie_algebras/lie_algebra.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/sage/algebras/lie_algebras/lie_algebra.py b/src/sage/algebras/lie_algebras/lie_algebra.py index 480fa91b788..c0b99ccc247 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra.py +++ b/src/sage/algebras/lie_algebras/lie_algebra.py @@ -1240,9 +1240,34 @@ def _element_constructor_(self, x): -[1, 3, 2] + [3, 2, 1] sage: L(2) 2*[1, 2, 3] + + Test that constructing elements from vectors works correctly + (this used to cause infinite recursion):: + + sage: gl = lie_algebras.gl(QQ, 2) + sage: v = gl.an_element().to_vector() + sage: gl(v) == gl.an_element() + True + + Test that morphisms can be constructed (this used to fail with + RecursionError):: + + sage: gl = lie_algebras.gl(QQ, 2) + sage: phi = gl.morphism({e: e for e in gl.gens()}) + sage: all(phi(e) == e for e in gl.gens()) + True """ if isinstance(x, list) and len(x) == 2: return self(x[0])._bracket_(self(x[1])) + + # Check if x is a vector from the module to avoid infinite recursion + # when constructing elements from vectors (e.g., in morphism construction) + try: + if x in self.module(): + return self.from_vector(x) + except (AttributeError, NotImplementedError): + pass + return self.element_class(self, self._assoc(x)) def associative_algebra(self): From 2b562665eabb29f4ee34b252a7ab2ad4117fc641 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Wed, 19 Nov 2025 17:17:47 +0800 Subject: [PATCH 2/6] fix lint --- src/sage/algebras/lie_algebras/lie_algebra.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/algebras/lie_algebras/lie_algebra.py b/src/sage/algebras/lie_algebras/lie_algebra.py index c0b99ccc247..afc0cca5559 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra.py +++ b/src/sage/algebras/lie_algebras/lie_algebra.py @@ -1259,7 +1259,7 @@ def _element_constructor_(self, x): """ if isinstance(x, list) and len(x) == 2: return self(x[0])._bracket_(self(x[1])) - + # Check if x is a vector from the module to avoid infinite recursion # when constructing elements from vectors (e.g., in morphism construction) try: @@ -1267,7 +1267,7 @@ def _element_constructor_(self, x): return self.from_vector(x) except (AttributeError, NotImplementedError): pass - + return self.element_class(self, self._assoc(x)) def associative_algebra(self): From 89115ec5bd097a25140779738e9ab4cc39ea879e Mon Sep 17 00:00:00 2001 From: cxzhong Date: Wed, 19 Nov 2025 18:41:07 +0800 Subject: [PATCH 3/6] fix: prevent infinite recursion in LieAlgebraFromAssociative class --- src/sage/algebras/lie_algebras/lie_algebra.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/algebras/lie_algebras/lie_algebra.py b/src/sage/algebras/lie_algebras/lie_algebra.py index afc0cca5559..156affb6eca 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra.py +++ b/src/sage/algebras/lie_algebras/lie_algebra.py @@ -1260,10 +1260,13 @@ def _element_constructor_(self, x): if isinstance(x, list) and len(x) == 2: return self(x[0])._bracket_(self(x[1])) + if x == 0: + x = self._assoc.zero() + # Check if x is a vector from the module to avoid infinite recursion # when constructing elements from vectors (e.g., in morphism construction) try: - if x in self.module(): + if hasattr(x, 'parent') and x.parent() == self.module(): return self.from_vector(x) except (AttributeError, NotImplementedError): pass From 0d33f320c80d56f218e77e6082a507c7cdd37f01 Mon Sep 17 00:00:00 2001 From: cxzhong Date: Wed, 19 Nov 2025 18:50:57 +0800 Subject: [PATCH 4/6] remove special 0 case --- src/sage/algebras/lie_algebras/lie_algebra.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/algebras/lie_algebras/lie_algebra.py b/src/sage/algebras/lie_algebras/lie_algebra.py index 156affb6eca..124b415492e 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra.py +++ b/src/sage/algebras/lie_algebras/lie_algebra.py @@ -1260,9 +1260,6 @@ def _element_constructor_(self, x): if isinstance(x, list) and len(x) == 2: return self(x[0])._bracket_(self(x[1])) - if x == 0: - x = self._assoc.zero() - # Check if x is a vector from the module to avoid infinite recursion # when constructing elements from vectors (e.g., in morphism construction) try: From aa9b8370cbdf95018d924a349b8e4948fdc0a605 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Thu, 20 Nov 2025 14:07:41 +0800 Subject: [PATCH 5/6] Fix test documentation for morphism construction Updated test documentation for morphism construction to clarify error handling. --- src/sage/algebras/lie_algebras/lie_algebra.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/sage/algebras/lie_algebras/lie_algebra.py b/src/sage/algebras/lie_algebras/lie_algebra.py index 124b415492e..de24811fb19 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra.py +++ b/src/sage/algebras/lie_algebras/lie_algebra.py @@ -1241,6 +1241,8 @@ def _element_constructor_(self, x): sage: L(2) 2*[1, 2, 3] + TESTS: + Test that constructing elements from vectors works correctly (this used to cause infinite recursion):: @@ -1249,8 +1251,7 @@ def _element_constructor_(self, x): sage: gl(v) == gl.an_element() True - Test that morphisms can be constructed (this used to fail with - RecursionError):: + Test that morphisms can be constructed(this used to fail with :exc:`RecursionError`):: sage: gl = lie_algebras.gl(QQ, 2) sage: phi = gl.morphism({e: e for e in gl.gens()}) @@ -1262,11 +1263,8 @@ def _element_constructor_(self, x): # Check if x is a vector from the module to avoid infinite recursion # when constructing elements from vectors (e.g., in morphism construction) - try: - if hasattr(x, 'parent') and x.parent() == self.module(): - return self.from_vector(x) - except (AttributeError, NotImplementedError): - pass + if hasattr(x, 'parent') and x.parent() == self.module(): + return self.from_vector(x) return self.element_class(self, self._assoc(x)) From f0ca6ce00573919e1b55073db37d6052f32ce428 Mon Sep 17 00:00:00 2001 From: Chenxin Zhong Date: Mon, 24 Nov 2025 00:55:37 +0800 Subject: [PATCH 6/6] Use more robost check --- src/sage/algebras/lie_algebras/lie_algebra.py | 85 ++++++++++++++++--- 1 file changed, 71 insertions(+), 14 deletions(-) diff --git a/src/sage/algebras/lie_algebras/lie_algebra.py b/src/sage/algebras/lie_algebras/lie_algebra.py index de24811fb19..b57f11127d8 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra.py +++ b/src/sage/algebras/lie_algebras/lie_algebra.py @@ -553,6 +553,15 @@ def _element_constructor_(self, x): sage: L = lie_algebras.pwitt(GF(5), 5) sage: L(0) 0 + + Vectors coming from the underlying module coerce back in:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'x': 1}}) + sage: v = (x + 2*y).to_vector() + sage: L(v) + x + 2*y + sage: L(v.change_ring(ZZ)) + x + 2*y """ if isinstance(x, list) and len(x) == 2: return self(x[0])._bracket_(self(x[1])) @@ -560,11 +569,9 @@ def _element_constructor_(self, x): if x == 0: return self.zero() - try: - if x in self.module(): - return self.from_vector(x) - except AttributeError: - pass + coerced = self._coerce_from_module_vector(x) + if coerced is not None: + return coerced if x in self.base_ring(): # We have already handled the case when x == 0 @@ -572,6 +579,60 @@ def _element_constructor_(self, x): return self.element_class(self, x) + def _coerce_from_module_vector(self, x): + """ + Try to coerce ``x`` coming from ``self.module()``. + + INPUT: + + - ``x`` -- an element to be coerced + + OUTPUT: + + - an element of ``self`` if coercion is possible, ``None`` otherwise + """ + from_vector = getattr(self, "from_vector", None) + if from_vector is None: + return None + + try: + x_parent = x.parent() + except AttributeError: + return None + + vector_modules = [] + for attr in ("_M", "_module"): + mod = getattr(self, attr, None) + if mod is None or mod is NotImplemented or callable(mod): + continue + vector_modules.append(mod) + + module_method = getattr(self, "module", None) + if callable(module_method): + try: + mod = module_method() + except (AttributeError, TypeError, NotImplementedError): + mod = None + if mod is not None: + vector_modules.append(mod) + + seen_ids = set() + for mod in vector_modules: + key = id(mod) + seen_ids.add(key) + if x_parent is mod: + return from_vector(x) + has_cm = getattr(mod, "has_coerce_map_from", None) + if (x_parent is not None and callable(has_cm) + and has_cm(x_parent)): + try: + vec = mod(x) + except (TypeError, ValueError): + continue + return from_vector(vec) + + return None + def __getitem__(self, x): """ If ``x`` is a pair `(a, b)`, return the Lie bracket `[a, b] @@ -1243,15 +1304,14 @@ def _element_constructor_(self, x): TESTS: - Test that constructing elements from vectors works correctly - (this used to cause infinite recursion):: + Test that constructing elements from vectors works correctly:: sage: gl = lie_algebras.gl(QQ, 2) sage: v = gl.an_element().to_vector() sage: gl(v) == gl.an_element() True - Test that morphisms can be constructed(this used to fail with :exc:`RecursionError`):: + Test that morphisms can be constructed:: sage: gl = lie_algebras.gl(QQ, 2) sage: phi = gl.morphism({e: e for e in gl.gens()}) @@ -1260,12 +1320,9 @@ def _element_constructor_(self, x): """ if isinstance(x, list) and len(x) == 2: return self(x[0])._bracket_(self(x[1])) - - # Check if x is a vector from the module to avoid infinite recursion - # when constructing elements from vectors (e.g., in morphism construction) - if hasattr(x, 'parent') and x.parent() == self.module(): - return self.from_vector(x) - + coerced = self._coerce_from_module_vector(x) + if coerced is not None: + return coerced return self.element_class(self, self._assoc(x)) def associative_algebra(self):