Free modules of finite rank¶
The class FiniteRankFreeModule
implements free modules of finite rank
over a commutative ring.
A free module of finite rank over a commutative ring \(R\) is a module \(M\) over \(R\) that admits a finite basis, i.e. a finite familly of linearly independent generators. Since \(R\) is commutative, it has the invariant basis number property, so that the rank of the free module \(M\) is defined uniquely, as the cardinality of any basis of \(M\).
No distinguished basis of \(M\) is assumed. On the contrary, many bases can be introduced on the free module along with change-of-basis rules (as module automorphisms). Each module element has then various representations over the various bases.
Note
The class FiniteRankFreeModule
does not inherit from
class FreeModule_generic
nor from class
CombinatorialFreeModule
, since
both classes deal with modules with a distinguished basis (see
details below). Accordingly, the class
FiniteRankFreeModule
inherits directly from the generic class
Parent
with the category set to
Modules
(and not to
ModulesWithBasis
).
Todo
- implement submodules
- create a FreeModules category (cf. the TODO statement in the
documentation of
Modules
: Implement a ``FreeModules(R)`` category, when so prompted by a concrete use case)
AUTHORS:
- Eric Gourgoulhon, Michal Bejger (2014-2015): initial version
- Travis Scrimshaw (2016): category set to Modules(ring).FiniteDimensional() (trac ticket #20770)
REFERENCES:
EXAMPLES:
Let us define a free module of rank 2 over \(\ZZ\):
sage: M = FiniteRankFreeModule(ZZ, 2, name='M') ; M
Rank-2 free module M over the Integer Ring
sage: M.category()
Category of finite dimensional modules over Integer Ring
We introduce a first basis on M
:
sage: e = M.basis('e') ; e
Basis (e_0,e_1) on the Rank-2 free module M over the Integer Ring
The elements of the basis are of course module elements:
sage: e[0]
Element e_0 of the Rank-2 free module M over the Integer Ring
sage: e[1]
Element e_1 of the Rank-2 free module M over the Integer Ring
sage: e[0].parent()
Rank-2 free module M over the Integer Ring
We define a module element by its components w.r.t. basis e
:
sage: u = M([2,-3], basis=e, name='u')
sage: u.display(e)
u = 2 e_0 - 3 e_1
Module elements can be also be created by arithmetic expressions:
sage: v = -2*u + 4*e[0] ; v
Element of the Rank-2 free module M over the Integer Ring
sage: v.display(e)
6 e_1
sage: u == 2*e[0] - 3*e[1]
True
We define a second basis on M
from a family of linearly independent
elements:
sage: f = M.basis('f', from_family=(e[0]-e[1], -2*e[0]+3*e[1])) ; f
Basis (f_0,f_1) on the Rank-2 free module M over the Integer Ring
sage: f[0].display(e)
f_0 = e_0 - e_1
sage: f[1].display(e)
f_1 = -2 e_0 + 3 e_1
We may of course express the elements of basis e
in terms of basis f
:
sage: e[0].display(f)
e_0 = 3 f_0 + f_1
sage: e[1].display(f)
e_1 = 2 f_0 + f_1
as well as any module element:
sage: u.display(f)
u = -f_1
sage: v.display(f)
12 f_0 + 6 f_1
The two bases are related by a module automorphism:
sage: a = M.change_of_basis(e,f) ; a
Automorphism of the Rank-2 free module M over the Integer Ring
sage: a.parent()
General linear group of the Rank-2 free module M over the Integer Ring
sage: a.matrix(e)
[ 1 -2]
[-1 3]
Let us check that basis f
is indeed the image of basis e
by a
:
sage: f[0] == a(e[0])
True
sage: f[1] == a(e[1])
True
The reverse change of basis is of course the inverse automorphism:
sage: M.change_of_basis(f,e) == a^(-1)
True
We introduce a new module element via its components w.r.t. basis f
:
sage: v = M([2,4], basis=f, name='v')
sage: v.display(f)
v = 2 f_0 + 4 f_1
The sum of the two module elements u
and v
can be performed even if
they have been defined on different bases, thanks to the known relation
between the two bases:
sage: s = u + v ; s
Element u+v of the Rank-2 free module M over the Integer Ring
We can display the result in either basis:
sage: s.display(e)
u+v = -4 e_0 + 7 e_1
sage: s.display(f)
u+v = 2 f_0 + 3 f_1
Tensor products of elements are implemented:
sage: t = u*v ; t
Type-(2,0) tensor u*v on the Rank-2 free module M over the Integer Ring
sage: t.parent()
Free module of type-(2,0) tensors on the
Rank-2 free module M over the Integer Ring
sage: t.display(e)
u*v = -12 e_0*e_0 + 20 e_0*e_1 + 18 e_1*e_0 - 30 e_1*e_1
sage: t.display(f)
u*v = -2 f_1*f_0 - 4 f_1*f_1
We can access to tensor components w.r.t. to a given basis via the square bracket operator:
sage: t[e,0,1]
20
sage: t[f,1,0]
-2
sage: u[e,0]
2
sage: u[e,:]
[2, -3]
sage: u[f,:]
[0, -1]
The parent of the automorphism a
is the group \(\mathrm{GL}(M)\), but
a
can also be considered as a tensor of type \((1,1)\) on M
:
sage: a.parent()
General linear group of the Rank-2 free module M over the Integer Ring
sage: a.tensor_type()
(1, 1)
sage: a.display(e)
e_0*e^0 - 2 e_0*e^1 - e_1*e^0 + 3 e_1*e^1
sage: a.display(f)
f_0*f^0 - 2 f_0*f^1 - f_1*f^0 + 3 f_1*f^1
As such, we can form its tensor product with t
, yielding a tensor of
type \((3,1)\):
sage: t*a
Type-(3,1) tensor on the Rank-2 free module M over the Integer Ring
sage: (t*a).display(e)
-12 e_0*e_0*e_0*e^0 + 24 e_0*e_0*e_0*e^1 + 12 e_0*e_0*e_1*e^0
- 36 e_0*e_0*e_1*e^1 + 20 e_0*e_1*e_0*e^0 - 40 e_0*e_1*e_0*e^1
- 20 e_0*e_1*e_1*e^0 + 60 e_0*e_1*e_1*e^1 + 18 e_1*e_0*e_0*e^0
- 36 e_1*e_0*e_0*e^1 - 18 e_1*e_0*e_1*e^0 + 54 e_1*e_0*e_1*e^1
- 30 e_1*e_1*e_0*e^0 + 60 e_1*e_1*e_0*e^1 + 30 e_1*e_1*e_1*e^0
- 90 e_1*e_1*e_1*e^1
The parent of \(t\otimes a\) is itself a free module of finite rank over \(\ZZ\):
sage: T = (t*a).parent() ; T
Free module of type-(3,1) tensors on the Rank-2 free module M over the
Integer Ring
sage: T.base_ring()
Integer Ring
sage: T.rank()
16
Differences between FiniteRankFreeModule
and FreeModule
(or VectorSpace
)
To illustrate the differences, let us create two free modules of rank 3 over
\(\ZZ\), one with FiniteRankFreeModule
and the other one with
FreeModule
:
sage: M = FiniteRankFreeModule(ZZ, 3, name='M') ; M
Rank-3 free module M over the Integer Ring
sage: N = FreeModule(ZZ, 3) ; N
Ambient free module of rank 3 over the principal ideal domain Integer Ring
The main difference is that FreeModule
returns a free module with a
distinguished basis, while FiniteRankFreeModule
does not:
sage: N.basis()
[
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
sage: M.bases()
[]
sage: M.print_bases()
No basis has been defined on the Rank-3 free module M over the Integer Ring
This is also revealed by the category of each module:
sage: M.category()
Category of finite dimensional modules over Integer Ring
sage: N.category()
Category of finite dimensional modules with basis over
(euclidean domains and infinite enumerated sets and metric spaces)
In other words, the module created by FreeModule
is actually \(\ZZ^3\),
while, in the absence of any distinguished basis, no canonical isomorphism
relates the module created by FiniteRankFreeModule
to \(\ZZ^3\):
sage: N is ZZ^3
True
sage: M is ZZ^3
False
sage: M == ZZ^3
False
Because it is \(\ZZ^3\), N
is unique, while there may be various modules
of the same rank over the same ring created by FiniteRankFreeModule
;
they are then distinguished by their names (actually by the complete
sequence of arguments of FiniteRankFreeModule
):
sage: N1 = FreeModule(ZZ, 3) ; N1
Ambient free module of rank 3 over the principal ideal domain Integer Ring
sage: N1 is N # FreeModule(ZZ, 3) is unique
True
sage: M1 = FiniteRankFreeModule(ZZ, 3, name='M_1') ; M1
Rank-3 free module M_1 over the Integer Ring
sage: M1 is M # M1 and M are different rank-3 modules over ZZ
False
sage: M1b = FiniteRankFreeModule(ZZ, 3, name='M_1') ; M1b
Rank-3 free module M_1 over the Integer Ring
sage: M1b is M1 # because M1b and M1 have the same name
True
As illustrated above, various bases can be introduced on the module created by
FiniteRankFreeModule
:
sage: e = M.basis('e') ; e
Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring
sage: f = M.basis('f', from_family=(-e[0], e[1]-e[2], -2*e[1]+3*e[2])) ; f
Basis (f_0,f_1,f_2) on the Rank-3 free module M over the Integer Ring
sage: M.bases()
[Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring,
Basis (f_0,f_1,f_2) on the Rank-3 free module M over the Integer Ring]
Each element of a basis is accessible via its index:
sage: e[0]
Element e_0 of the Rank-3 free module M over the Integer Ring
sage: e[0].parent()
Rank-3 free module M over the Integer Ring
sage: f[1]
Element f_1 of the Rank-3 free module M over the Integer Ring
sage: f[1].parent()
Rank-3 free module M over the Integer Ring
while on module N
, the element of the (unique) basis is accessible
directly from the module symbol:
sage: N.0
(1, 0, 0)
sage: N.1
(0, 1, 0)
sage: N.0.parent()
Ambient free module of rank 3 over the principal ideal domain Integer Ring
The arithmetic of elements is similar; the difference lies in the display:
a basis has to be specified for elements of M
, while elements of N
are
displayed directly as elements of \(\ZZ^3\):
sage: u = 2*e[0] - 3*e[2] ; u
Element of the Rank-3 free module M over the Integer Ring
sage: u.display(e)
2 e_0 - 3 e_2
sage: u.display(f)
-2 f_0 - 6 f_1 - 3 f_2
sage: u[e,:]
[2, 0, -3]
sage: u[f,:]
[-2, -6, -3]
sage: v = 2*N.0 - 3*N.2 ; v
(2, 0, -3)
For the case of M
, in order to avoid to specify the basis if the user is
always working with the same basis (e.g. only one basis has been defined),
the concept of default basis has been introduced:
sage: M.default_basis()
Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring
sage: M.print_bases()
Bases defined on the Rank-3 free module M over the Integer Ring:
- (e_0,e_1,e_2) (default basis)
- (f_0,f_1,f_2)
This is different from the distinguished basis of N
: it simply means that
the mention of the basis can be omitted in function arguments:
sage: u.display() # equivalent to u.display(e)
2 e_0 - 3 e_2
sage: u[:] # equivalent to u[e,:]
[2, 0, -3]
At any time, the default basis can be changed:
sage: M.set_default_basis(f)
sage: u.display()
-2 f_0 - 6 f_1 - 3 f_2
Another difference between FiniteRankFreeModule
and FreeModule
is that
for the former the range of indices can be specified (by default, it starts
from 0):
sage: M = FiniteRankFreeModule(ZZ, 3, name='M', start_index=1) ; M
Rank-3 free module M over the Integer Ring
sage: e = M.basis('e') ; e # compare with (e_0,e_1,e_2) above
Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring
sage: e[1], e[2], e[3]
(Element e_1 of the Rank-3 free module M over the Integer Ring,
Element e_2 of the Rank-3 free module M over the Integer Ring,
Element e_3 of the Rank-3 free module M over the Integer Ring)
All the above holds for VectorSpace
instead of FreeModule
: the object
created by VectorSpace
is actually a Cartesian power of the base field:
sage: V = VectorSpace(QQ,3) ; V
Vector space of dimension 3 over Rational Field
sage: V.category()
Category of finite dimensional vector spaces with basis
over (number fields and quotient fields and metric spaces)
sage: V is QQ^3
True
sage: V.basis()
[
(1, 0, 0),
(0, 1, 0),
(0, 0, 1)
]
To create a vector space without any distinguished basis, one has to use
FiniteRankFreeModule
:
sage: V = FiniteRankFreeModule(QQ, 3, name='V') ; V
3-dimensional vector space V over the Rational Field
sage: V.category()
Category of finite dimensional vector spaces over Rational Field
sage: V.bases()
[]
sage: V.print_bases()
No basis has been defined on the 3-dimensional vector space V over the
Rational Field
The class FiniteRankFreeModule
has been created for the needs
of the SageManifolds project, where
free modules do not have any distinguished basis. Too kinds of free modules
occur in the context of differentiable manifolds (see
here for more
details):
- the tangent vector space at any point of the manifold (cf.
TangentSpace
); - the set of vector fields on a parallelizable open subset \(U\) of the manifold,
which is a free module over the algebra of scalar fields on \(U\) (cf.
VectorFieldFreeModule
).
For instance, without any specific coordinate choice, no basis can be distinguished in a tangent space.
On the other side, the modules created by FreeModule
have much more
algebraic functionalities than those created by FiniteRankFreeModule
. In
particular, submodules have not been implemented yet in
FiniteRankFreeModule
. Moreover, modules resulting from FreeModule
are tailored to the specific kind of their base ring:
free module over a commutative ring that is not an integral domain (\(\ZZ/6\ZZ\)):
sage: R = IntegerModRing(6) ; R Ring of integers modulo 6 sage: FreeModule(R, 3) Ambient free module of rank 3 over Ring of integers modulo 6 sage: type(FreeModule(R, 3)) <class 'sage.modules.free_module.FreeModule_ambient_with_category'>
free module over an integral domain that is not principal (\(\ZZ[X]\)):
sage: R.<X> = ZZ[] ; R Univariate Polynomial Ring in X over Integer Ring sage: FreeModule(R, 3) Ambient free module of rank 3 over the integral domain Univariate Polynomial Ring in X over Integer Ring sage: type(FreeModule(R, 3)) <class 'sage.modules.free_module.FreeModule_ambient_domain_with_category'>
free module over a principal ideal domain (\(\ZZ\)):
sage: R = ZZ ; R Integer Ring sage: FreeModule(R,3) Ambient free module of rank 3 over the principal ideal domain Integer Ring sage: type(FreeModule(R, 3)) <class 'sage.modules.free_module.FreeModule_ambient_pid_with_category'>
On the contrary, all objects constructed with FiniteRankFreeModule
belong
to the same class:
sage: R = IntegerModRing(6)
sage: type(FiniteRankFreeModule(R, 3))
<class 'sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule_with_category'>
sage: R.<X> = ZZ[]
sage: type(FiniteRankFreeModule(R, 3))
<class 'sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule_with_category'>
sage: R = ZZ
sage: type(FiniteRankFreeModule(R, 3))
<class 'sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule_with_category'>
Differences between FiniteRankFreeModule
and
CombinatorialFreeModule
An alternative to construct free modules in Sage is
CombinatorialFreeModule
.
However, as FreeModule
, it leads to a module with a distinguished basis:
sage: N = CombinatorialFreeModule(ZZ, [1,2,3]) ; N
Free module generated by {1, 2, 3} over Integer Ring
sage: N.category()
Category of finite dimensional modules with basis over Integer Ring
The distinguished basis is returned by the method basis()
:
sage: b = N.basis() ; b
Finite family {1: B[1], 2: B[2], 3: B[3]}
sage: b[1]
B[1]
sage: b[1].parent()
Free module generated by {1, 2, 3} over Integer Ring
For the free module M
created above with FiniteRankFreeModule
, the
method basis
has at least one argument: the symbol string that
specifies which basis is required:
sage: e = M.basis('e') ; e
Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring
sage: e[1]
Element e_1 of the Rank-3 free module M over the Integer Ring
sage: e[1].parent()
Rank-3 free module M over the Integer Ring
The arithmetic of elements is similar:
sage: u = 2*e[1] - 5*e[3] ; u
Element of the Rank-3 free module M over the Integer Ring
sage: v = 2*b[1] - 5*b[3] ; v
2*B[1] - 5*B[3]
One notices that elements of N
are displayed directly in terms of their
expansions on the distinguished basis. For elements of M
, one has to use
the method
display()
in order to specify the basis:
sage: u.display(e)
2 e_1 - 5 e_3
The components on the basis are returned by the square bracket operator for
M
and by the method coefficient
for N
:
sage: [u[e,i] for i in {1,2,3}]
[2, 0, -5]
sage: u[e,:] # a shortcut for the above
[2, 0, -5]
sage: [v.coefficient(i) for i in {1,2,3}]
[2, 0, -5]
-
sage.tensor.modules.finite_rank_free_module.
FiniteRankFreeModule
¶ Free module of finite rank over a commutative ring.
A free module of finite rank over a commutative ring \(R\) is a module \(M\) over \(R\) that admits a finite basis, i.e. a finite familly of linearly independent generators. Since \(R\) is commutative, it has the invariant basis number property, so that the rank of the free module \(M\) is defined uniquely, as the cardinality of any basis of \(M\).
No distinguished basis of \(M\) is assumed. On the contrary, many bases can be introduced on the free module along with change-of-basis rules (as module automorphisms). Each module element has then various representations over the various bases.
Note
The class
FiniteRankFreeModule
does not inherit from classFreeModule_generic
nor from classCombinatorialFreeModule
, since both classes deal with modules with a distinguished basis (see details above). Moreover, following the recommendation exposed in trac ticket #16427 the classFiniteRankFreeModule
inherits directly fromParent
(with the category set toModules
) and not from the Cython classModule
.The class
FiniteRankFreeModule
is a Sage parent class, the corresponding element class beingFiniteRankFreeModuleElement
.INPUT:
ring
– commutative ring \(R\) over which the free module is constructedrank
– positive integer; rank of the free modulename
– (default:None
) string; name given to the free modulelatex_name
– (default:None
) string; LaTeX symbol to denote the freemodule; if none is provided, it is set toname
start_index
– (default: 0) integer; lower bound of the range of indices in bases defined on the free moduleoutput_formatter
– (default:None
) function or unbound method called to format the output of the tensor components;output_formatter
must take 1 or 2 arguments: the first argument must be an element of the ring \(R\) and the second one, if any, some format specification
EXAMPLES:
Free module of rank 3 over \(\ZZ\):
sage: FiniteRankFreeModule._clear_cache_() # for doctests only sage: M = FiniteRankFreeModule(ZZ, 3) ; M Rank-3 free module over the Integer Ring sage: M = FiniteRankFreeModule(ZZ, 3, name='M') ; M # declaration with a name Rank-3 free module M over the Integer Ring sage: M.category() Category of finite dimensional modules over Integer Ring sage: M.base_ring() Integer Ring sage: M.rank() 3
If the base ring is a field, the free module is in the category of vector spaces:
sage: V = FiniteRankFreeModule(QQ, 3, name='V') ; V 3-dimensional vector space V over the Rational Field sage: V.category() Category of finite dimensional vector spaces over Rational Field
The LaTeX output is adjusted via the parameter
latex_name
:sage: latex(M) # the default is the symbol provided in the string ``name`` M sage: M = FiniteRankFreeModule(ZZ, 3, name='M', latex_name=r'\mathcal{M}') sage: latex(M) \mathcal{M}
The free module M has no distinguished basis:
sage: M in ModulesWithBasis(ZZ) False sage: M in Modules(ZZ) True
In particular, no basis is initialized at the module construction:
sage: M.print_bases() No basis has been defined on the Rank-3 free module M over the Integer Ring sage: M.bases() []
Bases have to be introduced by means of the method
basis()
, the first defined basis being considered as the default basis, meaning it can be skipped in function arguments required a basis (this can be changed by means of the methodset_default_basis()
):sage: e = M.basis('e') ; e Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring sage: M.default_basis() Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring
A second basis can be created from a family of linearly independent elements expressed in terms of basis
e
:sage: f = M.basis('f', from_family=(-e[0], e[1]+e[2], 2*e[1]+3*e[2])) sage: f Basis (f_0,f_1,f_2) on the Rank-3 free module M over the Integer Ring sage: M.print_bases() Bases defined on the Rank-3 free module M over the Integer Ring: - (e_0,e_1,e_2) (default basis) - (f_0,f_1,f_2) sage: M.bases() [Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring, Basis (f_0,f_1,f_2) on the Rank-3 free module M over the Integer Ring]
M is a parent object, whose elements are instances of
FiniteRankFreeModuleElement
(actually a dynamically generated subclass of it):sage: v = M.an_element() ; v Element of the Rank-3 free module M over the Integer Ring sage: from sage.tensor.modules.free_module_element import FiniteRankFreeModuleElement sage: isinstance(v, FiniteRankFreeModuleElement) True sage: v in M True sage: M.is_parent_of(v) True sage: v.display() # expansion w.r.t. the default basis (e) e_0 + e_1 + e_2 sage: v.display(f) -f_0 + f_1
The test suite of the category of modules is passed:
sage: TestSuite(M).run()
Constructing an element of
M
from (the integer) 0 yields the zero element ofM
:sage: M(0) Element zero of the Rank-3 free module M over the Integer Ring sage: M(0) is M.zero() True
Non-zero elements are constructed by providing their components in a given basis:
sage: v = M([-1,0,3]) ; v # components in the default basis (e) Element of the Rank-3 free module M over the Integer Ring sage: v.display() # expansion w.r.t. the default basis (e) -e_0 + 3 e_2 sage: v.display(f) f_0 - 6 f_1 + 3 f_2 sage: v = M([-1,0,3], basis=f) ; v # components in a specific basis Element of the Rank-3 free module M over the Integer Ring sage: v.display(f) -f_0 + 3 f_2 sage: v.display() e_0 + 6 e_1 + 9 e_2 sage: v = M([-1,0,3], basis=f, name='v') ; v Element v of the Rank-3 free module M over the Integer Ring sage: v.display(f) v = -f_0 + 3 f_2 sage: v.display() v = e_0 + 6 e_1 + 9 e_2
An alternative is to construct the element from an empty list of componentsand to set the nonzero components afterwards:
sage: v = M([], name='v') sage: v[e,0] = -1 sage: v[e,2] = 3 sage: v.display(e) v = -e_0 + 3 e_2
Indices on the free module, such as indices labelling the element of a basis, are provided by the generator method
irange()
. By default, they range from 0 to the module’s rank minus one:sage: list(M.irange()) [0, 1, 2]
This can be changed via the parameter
start_index
in the module construction:sage: M1 = FiniteRankFreeModule(ZZ, 3, name='M', start_index=1) sage: list(M1.irange()) [1, 2, 3]
The parameter
output_formatter
in the constructor of the free module is used to set the output format of tensor components:sage: N = FiniteRankFreeModule(QQ, 3, output_formatter=Rational.numerical_approx) sage: e = N.basis('e') sage: v = N([1/3, 0, -2], basis=e) sage: v[e,:] [0.333333333333333, 0.000000000000000, -2.00000000000000] sage: v.display(e) # default format (53 bits of precision) 0.333333333333333 e_0 - 2.00000000000000 e_2 sage: v.display(e, format_spec=10) # 10 bits of precision 0.33 e_0 - 2.0 e_2