Tutorial: Using Free Modules and Vector Spaces
In this tutorial, we show how to construct and manipulate free modules
and vector spaces and their elements.
Sage currently provides two implementations of free modules:
FreeModule and CombinatorialFreeModule. The
distinction between the two is mostly an accident in history. The
later allows for the basis to be indexed by any kind of objects,
instead of just
. They also differ by feature set and
efficiency. Eventually, both implementations will be merged under the
name FreeModule. In the mean time, we focus here on
CombinatorialFreeModule. We recommend to start by browsing
the documentation.
sage: CombinatorialFreeModule?
We begin with a minimal example. What does this give us?
{{{id=0|
G = Zmod(5)
A = CombinatorialFreeModule(ZZ, G);
A.
A.an_element()
///
B[0] + 3*B[1] + 3*B[2]
}}}
We can use any set whose elements are immutable to index the
basis. Here is the
free module whose basis is indexed by complex
numbers:
{{{id=1|
A = CombinatorialFreeModule(ZZ, CC); A.an_element()
///
B[1.00000000000000*I]
}}}
{{{id=2|
A = CombinatorialFreeModule(ZZ, Partitions(NonNegativeIntegers(), max_part=3)); A.an_element()
///
B[[]] + 2*B[[1]] + 3*B[[2]]
}}}
{{{id=3|
A = CombinatorialFreeModule(ZZ, ['spam', 'eggs', 42]); A.an_element()
///
2*B['eggs'] + 2*B['spam'] + 3*B[42]
}}}
{{{id=4|
A = CombinatorialFreeModule(ZZ, ([1],[2],[3])); A.an_element()
///
TypeError: unhashable type: 'list'
}}}
We can customize the name of the basis however we want:
{{{id=5|
A = CombinatorialFreeModule(ZZ, Zmod(5), prefix='a'); A.an_element()
///
a[0] + 3*a[1] + 3*a[2]
}}}
Let us do some arithmetic with elements of
:
{{{id=6|
f = A.an_element(); f
///
a[0] + 3*a[1] + 3*a[2]
}}}
{{{id=7|
2*f
///
2*a[0] + 6*a[1] + 6*a[2]
}}}
{{{id=8|
2*f - f
///
a[0] + 3*a[1] + 3*a[2]
}}}
{{{id=9|
a[0] + 3*a[1]
///
NameError: name 'a' is not defined
}}}
To construct elements directly, we must first get the basis for the
module:
{{{id=10|
a = A.basis()
a[0] + 3*a[1]
///
a[0] + 3*a[1]
}}}
copy-paste works if the prefix matches the name of the basis:
{{{id=11|
a[0] + 3*a[1] + 3*a[2] == f
///
True
}}}
Be careful, that the input is currently not checked:
{{{id=12|
a['is'] + a['this'] + a['a'] + a['bug']
///
a['a'] + a['bug'] + a['is'] + a['this']
}}}
{{{id=13|
a
///
Lazy family (Term map from Ring of integers modulo 5 to Free module generated by Ring of integers modulo 5 over Integer Ring(i))_{i in Ring of integers modulo 5}
}}}
A.basis() models the family
. See the
documentation for Family for more information:
The elements of our module come with many methods for
exploring and manipulating them:
{{{id=14|
f.
///
}}}
Some notation:
- term: coefficient * basis_element
- monomial: basis_element without a coefficient
- support: the index of a basis_element
- item : a tuple (index, coefficient)
Note that elements are printed starting with the (lexicographically by
default) least index. Leading/trailing refers to the greatest/least
index, respectively:
{{{id=15|
f
///
a[0] + 3*a[1] + 3*a[2]
}}}
{{{id=16|
"Leading term: ",f.leading_term()
///
Leading term: 3*a[2]
}}}
{{{id=17|
print "Leading monomial: ",f.leading_monomial()
///
Leading monomial: a[2]
}}}
{{{id=18|
print "Leading support: ",f.leading_support()
///
Leading support: 2
}}}
{{{id=19|
print "Leading coefficient: ",f.leading_coefficient()
///
Leading coefficient: 3
}}}
{{{id=20|
print "Leading item: ",f.leading_item()
///
Leading item: (2, 3)
}}}
{{{id=21|
f.leading_term
print "Support: ",f.support()
///
Support: [0, 1, 2]
}}}
{{{id=22|
print "Monomials: ",f.monomials()
///
Monomials: [a[0], a[1], a[2]]
}}}
{{{id=23|
print "Coefficients: ",f.coefficients()
///
Coefficients: [1, 3, 3]
}}}
We can iterate through the items in an element:
{{{id=24|
for index, coeff in f:
print "The coefficient of a_{%s} is %s"%(index, coeff)
///
The coefficient of a_{0} is 1
The coefficient of a_{1} is 3
The coefficient of a_{2} is 3
}}}
{{{id=25|
# This uses the fact that f can be thought of as a dictionary index-->coefficient
print f[0], f[1], f[2]
///
1 3 3
}}}
{{{id=26|
# This dictionary can be accessed explicitly with the monomial_coefficients method
f.monomial_coefficients()
///
{0: 1, 1: 3, 2: 3}
}}}
The parent (
in our example) has several utility methods for
constructing elements:
{{{id=27|
A.
A.zero()
///
0
}}}
{{{id=28|
A.sum_of_monomials(i for i in Zmod(5) if i > 2)
///
a[3] + a[4]
}}}
{{{id=29|
A.sum_of_terms((i+1,i) for i in Zmod(5) if i > 2)
///
4*a[0] + 3*a[4]
}}}
{{{id=30|
A.sum(ZZ(i)*a[i+1] for i in Zmod(5) if i > 2) # Note coeff is not (currently) implicitly coerced
///
4*a[0] + 3*a[4]
}}}
Note that it is safer to use A.sum() then to use sum(), in
case the input is an empty iterable:
{{{id=31|
print A.sum([]),':', parent(A.sum([]))
///
0 : Free module generated by Ring of integers modulo 5 over Integer Ring
}}}
{{{id=32|
print sum([]),':', parent(sum([]))
///
0 :
}}}
The map methods are useful ways to transform elements:
{{{id=33|
f.map_
print f,"-->", f.map_support (lambda i : i+3)
///
a[0] + 3*a[1] + 3*a[2] --> 3*a[0] + a[3] + 3*a[4]
}}}
{{{id=34|
print f,"-->", f.map_coefficients(lambda c : c-1)
///
a[0] + 3*a[1] + 3*a[2] --> 2*a[1] + 2*a[2]
}}}
{{{id=35|
print f,"-->", f.map_term (lambda i,c: (i+3,c-1))
///
a[0] + 3*a[1] + 3*a[2] --> 2*a[0] + 2*a[4]
}}}
f.map_mc is a deprecated synonym for f.map_term.
Note that term and item are not yet used completely consistently.
This fully functional
-module was created with the simple commands:
{{{id=36|
A = CombinatorialFreeModule(ZZ, Zmod(5), prefix='a')
a = A.basis()
///
}}}