From 6663315b7b06974cd7c2f67dd4d5a061a10e784f Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 19 Dec 2023 20:32:57 +0100 Subject: [PATCH 1/3] helper method to concatenate vectors --- src/sage/modules/free_module_element.pyx | 31 ++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 3492dbf2eb1..ea330cc76f5 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -4148,6 +4148,37 @@ cdef class FreeModuleElement(Vector): # abstract base class nintegrate=nintegral + def concatenate(self, other): + r""" + Return the result of concatenating this vector with another + vector over the same ring. + + EXAMPLES:: + + sage: v = vector([1, 2, 3]) + sage: w = vector([4, 5]) + sage: v.concatenate(w) + (1, 2, 3, 4, 5) + sage: v.parent() + Ambient free module of rank 3 over the principal ideal domain Integer Ring + sage: w.parent() + Ambient free module of rank 2 over the principal ideal domain Integer Ring + sage: v.concatenate(w).parent() + Ambient free module of rank 5 over the principal ideal domain Integer Ring + + The method fails when the vectors aren't defined over the same ring:: + + sage: w2 = vector(QQ, [4, 5]) + sage: v.concatenate(w2) + Traceback (most recent call last): + ... + ValueError: can only concatenate vectors over the same base ring + """ + R = self.parent().base_ring() + if other.parent().base_ring() != R: + raise ValueError('can only concatenate vectors over the same base ring') + return vector(R, list(self) + list(other)) + ############################################# # Generic dense element ############################################# From 4b5f2a54475c72d82651aef1e90a06025dbd74aa Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Thu, 28 Dec 2023 23:06:26 +0100 Subject: [PATCH 2/3] add another type check following reviewer comments --- src/sage/modules/free_module_element.pyx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index ea330cc76f5..cfb8d32b834 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -4166,14 +4166,22 @@ cdef class FreeModuleElement(Vector): # abstract base class sage: v.concatenate(w).parent() Ambient free module of rank 5 over the principal ideal domain Integer Ring - The method fails when the vectors aren't defined over the same ring:: + The method fails when the inputs aren't both vectors, or the vectors + aren't defined over the same ring:: + sage: w2 = polygen(QQ)^4 + 5 + sage: v.concatenate(w2) + Traceback (most recent call last): + ... + TypeError: can only concatenate two vectors sage: w2 = vector(QQ, [4, 5]) sage: v.concatenate(w2) Traceback (most recent call last): ... ValueError: can only concatenate vectors over the same base ring """ + if not isinstance(other, FreeModuleElement): + raise TypeError('can only concatenate two vectors') R = self.parent().base_ring() if other.parent().base_ring() != R: raise ValueError('can only concatenate vectors over the same base ring') From 4d1f385d1670daae69e0b7d1e08d546aaff8acd6 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Sat, 13 Jan 2024 15:23:34 +0100 Subject: [PATCH 3/3] support arbitrary iterables per reviewer comments --- src/sage/modules/free_module_element.pyx | 82 ++++++++++++++++++------ 1 file changed, 62 insertions(+), 20 deletions(-) diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index cfb8d32b834..28da5ed21a7 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -4148,10 +4148,15 @@ cdef class FreeModuleElement(Vector): # abstract base class nintegrate=nintegral - def concatenate(self, other): + def concatenate(self, other, *, ring=None): r""" - Return the result of concatenating this vector with another - vector over the same ring. + Return the result of concatenating this vector with a sequence + of elements given by another iterable. + + If the optional keyword argument ``ring`` is passed, this method + will return a vector over the specified ring (or fail). If no + base ring is given, the base ring is determined automatically by + the :func:`vector` constructor. EXAMPLES:: @@ -4166,26 +4171,63 @@ cdef class FreeModuleElement(Vector): # abstract base class sage: v.concatenate(w).parent() Ambient free module of rank 5 over the principal ideal domain Integer Ring - The method fails when the inputs aren't both vectors, or the vectors - aren't defined over the same ring:: + Forcing a base ring is possible using the ``ring`` argument:: - sage: w2 = polygen(QQ)^4 + 5 + sage: v.concatenate(w, ring=QQ) + (1, 2, 3, 4, 5) + sage: v.concatenate(w, ring=QQ).parent() + Vector space of dimension 5 over Rational Field + + :: + + sage: v.concatenate(w, ring=Zmod(3)) + (1, 2, 0, 1, 2) + + The method accepts arbitrary iterables of elements which can + be coerced to a common base ring:: + + sage: v.concatenate(range(4,8)) + (1, 2, 3, 4, 5, 6, 7) + sage: v.concatenate(range(4,8)).parent() + Ambient free module of rank 7 over the principal ideal domain Integer Ring + + :: + + sage: w2 = [4, QQbar(-5).sqrt()] sage: v.concatenate(w2) - Traceback (most recent call last): - ... - TypeError: can only concatenate two vectors - sage: w2 = vector(QQ, [4, 5]) + (1, 2, 3, 4, 2.236...*I) + sage: v.concatenate(w2).parent() + Vector space of dimension 5 over Algebraic Field + sage: w2 = vector(w2) sage: v.concatenate(w2) - Traceback (most recent call last): - ... - ValueError: can only concatenate vectors over the same base ring - """ - if not isinstance(other, FreeModuleElement): - raise TypeError('can only concatenate two vectors') - R = self.parent().base_ring() - if other.parent().base_ring() != R: - raise ValueError('can only concatenate vectors over the same base ring') - return vector(R, list(self) + list(other)) + (1, 2, 3, 4, 2.236...*I) + sage: v.concatenate(w2).parent() + Vector space of dimension 5 over Algebraic Field + + :: + + sage: w2 = polygen(QQ)^4 + 5 + sage: v.concatenate(w2) + (1, 2, 3, 5, 0, 0, 0, 1) + sage: v.concatenate(w2).parent() + Vector space of dimension 8 over Rational Field + sage: v.concatenate(w2, ring=ZZ) + (1, 2, 3, 5, 0, 0, 0, 1) + sage: v.concatenate(w2, ring=ZZ).parent() + Ambient free module of rank 8 over the principal ideal domain Integer Ring + + :: + + sage: v.concatenate(GF(9).gens()) + (1, 2, 0, z2) + sage: v.concatenate(GF(9).gens()).parent() + Vector space of dimension 4 over Finite Field in z2 of size 3^2 + """ + from itertools import chain + coeffs = chain(self, other) + if ring is not None: + return vector(ring, coeffs) + return vector(coeffs) ############################################# # Generic dense element