Quotients of modules with basis - Modules

Skip to content
Back to top
View this page
Edit this page

# Quotients of modules with basis

class sage.modules.with_basis.subquotient.QuotientModuleWithBasis(submodule, category, *args, **opts)

  Bases: `CombinatorialFreeModule`

  A class for quotients of a module with basis by a submodule.

  INPUT:

  - `submodule` – a submodule of `self`

  - `category` – a category (default: `ModulesWithBasis(submodule.base_ring())`)

  `submodule` should be a free submodule admitting a basis in unitriangular echelon form. Typically `submodule` is a `SubmoduleWithBasis` as returned by `Modules.WithBasis.ParentMethods.submodule()`.

  The `lift` method should have a method `.cokernel_basis_indices` that computes the indexing set of a subset \(B\) of the basis of `self` that spans some supplementary of `submodule` in `self` (typically the non characteristic columns of the aforementioned echelon form). `submodule` should further implement a `submodule.reduce(x)` method that returns the unique element in the span of \(B\) which is equivalent to \(x\) modulo `submodule`.

  This is meant to be constructed via `Modules.WithBasis.FiniteDimensional.ParentMethods.quotient_module()`

  This differs from `sage.rings.quotient_ring.QuotientRing` in the following ways:

  - `submodule` needs not be an ideal. If it is, the transportation of the ring structure is taken care of by the `Subquotients` categories.

  - Thanks to `.cokernel_basis_indices`, we know the indices of a basis of the quotient, and elements are represented directly in the free module spanned by those indices rather than by wrapping elements of the ambient space.

  There is room for sharing more code between those two implementations and generalizing them. See Issue #18204.

  See also

  - `Modules.WithBasis.ParentMethods.submodule()`

  - `Modules.WithBasis.FiniteDimensional.ParentMethods.quotient_module()`

  - `SubmoduleWithBasis`

  - `sage.rings.quotient_ring.QuotientRing`

  ambient()

    Return the ambient space of `self`.

    EXAMPLES:

    Sage

        sage: X = CombinatorialFreeModule(QQ, range(3), prefix='x'); x = X.basis()
        sage: Y = X.quotient_module((x[0]-x[1], x[1]-x[2]))
        sage: Y.ambient() is X
        True

    Python

        >>> from sage.all import *
        >>> X = CombinatorialFreeModule(QQ, range(Integer(3)), prefix='x'); x = X.basis()
        >>> Y = X.quotient_module((x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(2)]))
        >>> Y.ambient() is X
        True

  lift(x)

    Lift `x` to the ambient space of `self`.

    INPUT:

    - `x` – an element of `self`

    EXAMPLES:

    Sage

        sage: X = CombinatorialFreeModule(QQ, range(3), prefix='x'); x = X.basis()
        sage: Y = X.quotient_module((x[0]-x[1], x[1]-x[2]));         y = Y.basis()
        sage: Y.lift(y[2])
        x[2]

    Python

        >>> from sage.all import *
        >>> X = CombinatorialFreeModule(QQ, range(Integer(3)), prefix='x'); x = X.basis()
        >>> Y = X.quotient_module((x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(2)]));         y = Y.basis()
        >>> Y.lift(y[Integer(2)])
        x[2]

  retract(x)

    Retract an element of the ambient space by projecting it back to `self`.

    INPUT:

    - `x` – an element of the ambient space of `self`

    EXAMPLES:

    Sage

        sage: X = CombinatorialFreeModule(QQ, range(3), prefix='x'); x = X.basis()
        sage: Y = X.quotient_module((x[0]-x[1], x[1]-x[2]));         y = Y.basis()
        sage: Y.print_options(prefix='y')
        sage: Y.retract(x[0])
        y[2]
        sage: Y.retract(x[1])
        y[2]
        sage: Y.retract(x[2])
        y[2]

    Python

        >>> from sage.all import *
        >>> X = CombinatorialFreeModule(QQ, range(Integer(3)), prefix='x'); x = X.basis()
        >>> Y = X.quotient_module((x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(2)]));         y = Y.basis()
        >>> Y.print_options(prefix='y')
        >>> Y.retract(x[Integer(0)])
        y[2]
        >>> Y.retract(x[Integer(1)])
        y[2]
        >>> Y.retract(x[Integer(2)])
        y[2]

class sage.modules.with_basis.subquotient.SubmoduleWithBasis(basis, support_order, ambient, unitriangular, category, *args, **opts)

  Bases: `CombinatorialFreeModule`

  A base class for submodules of a ModuleWithBasis spanned by a (possibly infinite) basis in echelon form.

  INPUT:

  - `basis` – a family of elements in echelon form in some `module with basis` \(V\), or data that can be converted into such a family

  - `support_order` – an ordering of the support of `basis` expressed in `ambient` given as a list

  - `unitriangular` – if the lift morphism is unitriangular

  - `ambient` – the ambient space \(V\)

  - `category` – a category

  Further arguments are passed down to `CombinatorialFreeModule`.

  This is meant to be constructed via `Modules.WithBasis.ParentMethods.submodule()`.

  See also

  - `Modules.WithBasis.ParentMethods.submodule()`

  - `QuotientModuleWithBasis`

  ambient()

    Return the ambient space of `self`.

    EXAMPLES:

    Sage

        sage: X = CombinatorialFreeModule(QQ, range(3)); x = X.basis()
        sage: Y = X.submodule((x[0]-x[1], x[1]-x[2]))
        sage: Y.ambient() is X
        True

    Python

        >>> from sage.all import *
        >>> X = CombinatorialFreeModule(QQ, range(Integer(3))); x = X.basis()
        >>> Y = X.submodule((x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(2)]))
        >>> Y.ambient() is X
        True

  intersection(other)

    Return the intersection of `self` and `other`.

    EXAMPLES:

    Sage

        sage: X = CombinatorialFreeModule(QQ, range(4)); x = X.basis()
        sage: F = X.submodule([x[0]-x[1], x[1]-x[2], x[2]-x[3]])
        sage: G = X.submodule([x[0]-x[2]])
        sage: H = X.submodule([x[0]-x[1], x[2]])
        sage: FG = F & G; FG
        Free module generated by {0} over Rational Field
        sage: [FG.lift(b) for b in FG.basis()]
        [B[0] - B[2]]
        sage: FH = F & H; FH
        Free module generated by {0} over Rational Field
        sage: [FH.lift(b) for b in FH.basis()]
        [B[0] - B[1]]
        sage: GH = G & H; GH
        Free module generated by {} over Rational Field
        sage: [GH.lift(b) for b in GH.basis()]
        []

        sage: F.intersection(X) is F
        True

    Python

        >>> from sage.all import *
        >>> X = CombinatorialFreeModule(QQ, range(Integer(4))); x = X.basis()
        >>> F = X.submodule([x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(2)], x[Integer(2)]-x[Integer(3)]])
        >>> G = X.submodule([x[Integer(0)]-x[Integer(2)]])
        >>> H = X.submodule([x[Integer(0)]-x[Integer(1)], x[Integer(2)]])
        >>> FG = F & G; FG
        Free module generated by {0} over Rational Field
        >>> [FG.lift(b) for b in FG.basis()]
        [B[0] - B[2]]
        >>> FH = F & H; FH
        Free module generated by {0} over Rational Field
        >>> [FH.lift(b) for b in FH.basis()]
        [B[0] - B[1]]
        >>> GH = G & H; GH
        Free module generated by {} over Rational Field
        >>> [GH.lift(b) for b in GH.basis()]
        []

        >>> F.intersection(X) is F
        True

  is_equal_subspace(other)

    Return whether `self` is an equal submodule to `other`.

    Note

    This is the mathematical notion of equality (as sets that are isomorphic as vector spaces), which is weaker than the \(==\) which takes into account things like the support order.

    INPUT:

    - `other` – another submodule of the same ambient module or the ambient module itself

    EXAMPLES:

    Sage

        sage: R.<z> = LaurentPolynomialRing(QQ)
        sage: X = CombinatorialFreeModule(R, range(4)); x = X.basis()
        sage: F = X.submodule([x[0]-x[1], z*x[1]-z*x[2], z^2*x[2]-z^2*x[3]])
        sage: G = X.submodule([x[0]-x[1], x[1]-x[2], x[2]-x[3]])
        sage: H = X.submodule([x[0]-x[1], x[1]-x[2], x[2]-x[3]], support_order=(3,2,1,0))
        sage: F.is_equal_subspace(F)
        True
        sage: F == G
        False
        sage: F.is_equal_subspace(G)
        True
        sage: F.is_equal_subspace(H)
        True
        sage: G == H  # different support orders
        False
        sage: G.is_equal_subspace(H)
        True

    Python

        >>> from sage.all import *
        >>> R = LaurentPolynomialRing(QQ, names=('z',)); (z,) = R._first_ngens(1)
        >>> X = CombinatorialFreeModule(R, range(Integer(4))); x = X.basis()
        >>> F = X.submodule([x[Integer(0)]-x[Integer(1)], z*x[Integer(1)]-z*x[Integer(2)], z**Integer(2)*x[Integer(2)]-z**Integer(2)*x[Integer(3)]])
        >>> G = X.submodule([x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(2)], x[Integer(2)]-x[Integer(3)]])
        >>> H = X.submodule([x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(2)], x[Integer(2)]-x[Integer(3)]], support_order=(Integer(3),Integer(2),Integer(1),Integer(0)))
        >>> F.is_equal_subspace(F)
        True
        >>> F == G
        False
        >>> F.is_equal_subspace(G)
        True
        >>> F.is_equal_subspace(H)
        True
        >>> G == H  # different support orders
        False
        >>> G.is_equal_subspace(H)
        True

    Sage

        sage: X = CombinatorialFreeModule(QQ, ZZ); x = X.basis()
        sage: F = X.submodule([x[0]-x[1], x[1]-x[3]])
        sage: G = X.submodule([x[0]-x[1], x[2]])
        sage: H = X.submodule([x[0]+x[1], x[1]+3*x[2]])
        sage: Hp = X.submodule([x[0]+x[1], x[1]+3*x[2]], prefix='Hp')
        sage: F.is_equal_subspace(X)
        False
        sage: F.is_equal_subspace(G)
        False
        sage: G.is_equal_subspace(H)
        False
        sage: H == Hp
        False
        sage: H.is_equal_subspace(Hp)
        True

    [Python]

        >>> from sage.all import *
        >>> X = CombinatorialFreeModule(QQ, ZZ); x = X.basis()
        >>> F = X.submodule([x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(3)]])
        >>> G = X.submodule([x[Integer(0)]-x[Integer(1)], x[Integer(2)]])
        >>> H = X.submodule([x[Integer(0)]+x[Integer(1)], x[Integer(1)]+Integer(3)*x[Integer(2)]])
        >>> Hp = X.submodule([x[Integer(0)]+x[Integer(1)], x[Integer(1)]+Integer(3)*x[Integer(2)]], prefix='Hp')
        >>> F.is_equal_subspace(X)
        False
        >>> F.is_equal_subspace(G)
        False
        >>> G.is_equal_subspace(H)
        False
        >>> H == Hp
        False
        >>> H.is_equal_subspace(Hp)
        True

  is_submodule(other)

    Return whether `self` is a submodule of `other`.

    INPUT:

    - `other` – another submodule of the same ambient module or the ambient module itself

    EXAMPLES:

    Sage

        sage: X = CombinatorialFreeModule(QQ, range(4)); x = X.basis()
        sage: F = X.submodule([x[0]-x[1], x[1]-x[2], x[2]-x[3]])
        sage: G = X.submodule([x[0]-x[2]])
        sage: H = X.submodule([x[0]-x[1], x[2]])
        sage: F.is_submodule(X)
        True
        sage: G.is_submodule(F)
        True
        sage: H.is_submodule(F)
        False
        sage: H.is_submodule(G)
        False

    Python

        >>> from sage.all import *
        >>> X = CombinatorialFreeModule(QQ, range(Integer(4))); x = X.basis()
        >>> F = X.submodule([x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(2)], x[Integer(2)]-x[Integer(3)]])
        >>> G = X.submodule([x[Integer(0)]-x[Integer(2)]])
        >>> H = X.submodule([x[Integer(0)]-x[Integer(1)], x[Integer(2)]])
        >>> F.is_submodule(X)
        True
        >>> G.is_submodule(F)
        True
        >>> H.is_submodule(F)
        False
        >>> H.is_submodule(G)
        False

    Infinite dimensional examples:

    Sage

        sage: X = CombinatorialFreeModule(QQ, ZZ); x = X.basis()
        sage: F = X.submodule([x[0]-x[1], x[1]-x[2], x[2]-x[3]])
        sage: G = X.submodule([x[0]-x[2]])
        sage: H = X.submodule([x[0]-x[1]])
        sage: F.is_submodule(X)
        True
        sage: G.is_submodule(F)
        True
        sage: H.is_submodule(F)
        True
        sage: H.is_submodule(G)
        False

    [Python]

        >>> from sage.all import *
        >>> X = CombinatorialFreeModule(QQ, ZZ); x = X.basis()
        >>> F = X.submodule([x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(2)], x[Integer(2)]-x[Integer(3)]])
        >>> G = X.submodule([x[Integer(0)]-x[Integer(2)]])
        >>> H = X.submodule([x[Integer(0)]-x[Integer(1)]])
        >>> F.is_submodule(X)
        True
        >>> G.is_submodule(F)
        True
        >>> H.is_submodule(F)
        True
        >>> H.is_submodule(G)
        False

    Different ambient spaces:

    Sage

        sage: X = CombinatorialFreeModule(QQ, range(4)); x = X.basis()
        sage: F = X.submodule([x[0]-x[1], x[1]-x[2], x[2]-x[3]])
        sage: Y = CombinatorialFreeModule(QQ, range(6)); y = Y.basis()
        sage: G = Y.submodule([y[0]-y[1], y[1]-y[2], y[2]-y[3]])
        sage: F.is_submodule(G)
        False

    Python

        >>> from sage.all import *
        >>> X = CombinatorialFreeModule(QQ, range(Integer(4))); x = X.basis()
        >>> F = X.submodule([x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(2)], x[Integer(2)]-x[Integer(3)]])
        >>> Y = CombinatorialFreeModule(QQ, range(Integer(6))); y = Y.basis()
        >>> G = Y.submodule([y[Integer(0)]-y[Integer(1)], y[Integer(1)]-y[Integer(2)], y[Integer(2)]-y[Integer(3)]])
        >>> F.is_submodule(G)
        False

  lift()

    The lift (embedding) map from `self` to the ambient space.

    EXAMPLES:

    Sage

        sage: X = CombinatorialFreeModule(QQ, range(3), prefix='x');             x = X.basis()
        sage: Y = X.submodule((x[0]-x[1], x[1]-x[2]), already_echelonized=True); y = Y.basis()
        sage: Y.lift
        Generic morphism:
          From: Free module generated by {0, 1} over Rational Field
          To:   Free module generated by {0, 1, 2} over Rational Field
        sage: [ Y.lift(u) for u in y ]
        [x[0] - x[1], x[1] - x[2]]
        sage: (y[0] + y[1]).lift()
        x[0] - x[2]

    Python

        >>> from sage.all import *
        >>> X = CombinatorialFreeModule(QQ, range(Integer(3)), prefix='x');             x = X.basis()
        >>> Y = X.submodule((x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(2)]), already_echelonized=True); y = Y.basis()
        >>> Y.lift
        Generic morphism:
          From: Free module generated by {0, 1} over Rational Field
          To:   Free module generated by {0, 1, 2} over Rational Field
        >>> [ Y.lift(u) for u in y ]
        [x[0] - x[1], x[1] - x[2]]
        >>> (y[Integer(0)] + y[Integer(1)]).lift()
        x[0] - x[2]

  reduce()

    The reduce map.

    This map reduces elements of the ambient space modulo this submodule.

    EXAMPLES:

    Sage

        sage: X = CombinatorialFreeModule(QQ, range(3), prefix='x'); x = X.basis()
        sage: Y = X.submodule((x[0]-x[1], x[1]-x[2]), already_echelonized=True)
        sage: Y.reduce
        Generic endomorphism of Free module generated by {0, 1, 2} over Rational Field
        sage: Y.reduce(x[1])
        x[2]
        sage: Y.reduce(2*x[0] + x[1])
        3*x[2]

    Python

        >>> from sage.all import *
        >>> X = CombinatorialFreeModule(QQ, range(Integer(3)), prefix='x'); x = X.basis()
        >>> Y = X.submodule((x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(2)]), already_echelonized=True)
        >>> Y.reduce
        Generic endomorphism of Free module generated by {0, 1, 2} over Rational Field
        >>> Y.reduce(x[Integer(1)])
        x[2]
        >>> Y.reduce(Integer(2)*x[Integer(0)] + x[Integer(1)])
        3*x[2]

  retract()

    The retract map from the ambient space.

    EXAMPLES:

    Sage

        sage: X = CombinatorialFreeModule(QQ, range(3), prefix='x'); x = X.basis()
        sage: Y = X.submodule((x[0]-x[1], x[1]-x[2]), already_echelonized=True)
        sage: Y.print_options(prefix='y')
        sage: Y.retract
        Generic morphism:
          From: Free module generated by {0, 1, 2} over Rational Field
          To:   Free module generated by {0, 1} over Rational Field
        sage: Y.retract(x[0] - x[2])
        y[0] + y[1]

    Python

        >>> from sage.all import *
        >>> X = CombinatorialFreeModule(QQ, range(Integer(3)), prefix='x'); x = X.basis()
        >>> Y = X.submodule((x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(2)]), already_echelonized=True)
        >>> Y.print_options(prefix='y')
        >>> Y.retract
        Generic morphism:
          From: Free module generated by {0, 1, 2} over Rational Field
          To:   Free module generated by {0, 1} over Rational Field
        >>> Y.retract(x[Integer(0)] - x[Integer(2)])
        y[0] + y[1]

  subspace(gens, *args, **opts)

    The submodule of the ambient space spanned by a finite set of generators `gens` (as a submodule).

    INPUT:

    - `gens` – list or family of elements of `self`

    For additional optional arguments, see `ModulesWithBasis.ParentMethods.submodule()`.

    EXAMPLES:

    Sage

        sage: X = CombinatorialFreeModule(QQ, range(4), prefix='X'); x = X.basis()
        sage: F = X.submodule([x[0]-x[1], x[1]-x[2], x[2]-x[3]], prefix='F'); f = F.basis()
        sage: U = F.submodule([f[0] + 2*f[1] - 5*f[2], f[1] + 2*f[2]]); U
        Free module generated by {0, 1} over Rational Field
        sage: [U.lift(u) for u in U.basis()]
        [F[0] - 9*F[2], F[1] + 2*F[2]]
        sage: V = F.subspace([f[0] + 2*f[1] - 5*f[2], f[1] + 2*f[2]]); V
        Free module generated by {0, 1} over Rational Field
        sage: [V.lift(u) for u in V.basis()]
        [X[0] - 9*X[2] + 8*X[3], X[1] + 2*X[2] - 3*X[3]]

    Python

        >>> from sage.all import *
        >>> X = CombinatorialFreeModule(QQ, range(Integer(4)), prefix='X'); x = X.basis()
        >>> F = X.submodule([x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(2)], x[Integer(2)]-x[Integer(3)]], prefix='F'); f = F.basis()
        >>> U = F.submodule([f[Integer(0)] + Integer(2)*f[Integer(1)] - Integer(5)*f[Integer(2)], f[Integer(1)] + Integer(2)*f[Integer(2)]]); U
        Free module generated by {0, 1} over Rational Field
        >>> [U.lift(u) for u in U.basis()]
        [F[0] - 9*F[2], F[1] + 2*F[2]]
        >>> V = F.subspace([f[Integer(0)] + Integer(2)*f[Integer(1)] - Integer(5)*f[Integer(2)], f[Integer(1)] + Integer(2)*f[Integer(2)]]); V
        Free module generated by {0, 1} over Rational Field
        >>> [V.lift(u) for u in V.basis()]
        [X[0] - 9*X[2] + 8*X[3], X[1] + 2*X[2] - 3*X[3]]

  subspace_sum(other)

    Return the sum of `self` and `other`.

    EXAMPLES:

    Sage

        sage: X = CombinatorialFreeModule(QQ, range(4)); x = X.basis()
        sage: F = X.submodule([x[0]-x[1], x[1]-x[2], x[2]-x[3]])
        sage: G = X.submodule([x[0]-x[2]])
        sage: H = X.submodule([x[0]-x[1], x[2]])
        sage: FG = F + G; FG
        Free module generated by {0, 1, 2} over Rational Field
        sage: [FG.lift(b) for b in FG.basis()]
        [B[0] - B[3], B[1] - B[3], B[2] - B[3]]
        sage: FH = F + H; FH
        Free module generated by {0, 1, 2, 3} over Rational Field
        sage: [FH.lift(b) for b in FH.basis()]
        [B[0], B[1], B[2], B[3]]
        sage: GH = G + H; GH
        Free module generated by {0, 1, 2} over Rational Field
        sage: [GH.lift(b) for b in GH.basis()]
        [B[0], B[1], B[2]]

    Python

        >>> from sage.all import *
        >>> X = CombinatorialFreeModule(QQ, range(Integer(4))); x = X.basis()
        >>> F = X.submodule([x[Integer(0)]-x[Integer(1)], x[Integer(1)]-x[Integer(2)], x[Integer(2)]-x[Integer(3)]])
        >>> G = X.submodule([x[Integer(0)]-x[Integer(2)]])
        >>> H = X.submodule([x[Integer(0)]-x[Integer(1)], x[Integer(2)]])
        >>> FG = F + G; FG
        Free module generated by {0, 1, 2} over Rational Field
        >>> [FG.lift(b) for b in FG.basis()]
        [B[0] - B[3], B[1] - B[3], B[2] - B[3]]
        >>> FH = F + H; FH
        Free module generated by {0, 1, 2, 3} over Rational Field
        >>> [FH.lift(b) for b in FH.basis()]
        [B[0], B[1], B[2], B[3]]
        >>> GH = G + H; GH
        Free module generated by {0, 1, 2} over Rational Field
        >>> [GH.lift(b) for b in GH.basis()]
        [B[0], B[1], B[2]]