This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
More Sage Thematic Tutorials¶
This is a repository of SageMath demonstrations, quick reference cards, primers, and thematic tutorials, grouped by theme, and licensed under a Creative Commons Attribution-Share Alike 3.0 License.
- A demonstration is a short document giving a broad view of the available features on a given theme; it is typically presented during a talk, and lasts a couple minutes.
- A quickref (or quick reference card) is a one page document with the essential examples, and pointers to the main entry points.
- A primer is a document meant for a user to get started by himself on a theme in a matter of minutes.
- A tutorial is more in-depth and could take as much as an hour or more to get through.
This repository is meant as a place to collectively share and evolve documents for SageMath with the aim to merge the mature ones into Sage’s official documentation, and in particular its official thematic tutorials. For the convenience of the reader, the index below also includes links to some of the latter.
Contributions, from typo fixes to full-fledged tutorials are more than welcome. See Contributing.
Warning
Most of the documents below have been recently resurrected from an old repository. They are of varying quality and may be outdated or require additional software. It is planned to add status information on each of them.
Documents for specific events¶
Introduction to Sage¶
- Demonstration: Basics
- Demonstration: graphics (short)
- Demonstration: Documentation
- Demonstration: Databases
- Demonstration: Sage combines the power of multiple software
- Start here!
- Logging on and Making a Worksheet
- Introductory Sage Tutorial
- Tutorial: Using the Sage notebook, navigating the help system, first exercises
- Welcome to the Sage Tutorial!
Calculus¶
Algebra¶
- Demontration: Computing with ideals using Singular (early draft)
- Linear Programming (Mixed Integer)
- Group Theory and Sage
- Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Groupe Symétrique et groupes de permutations
- Lie Methods and Related Combinatorics in Sage
- Tutorial: Using Free Modules and Vector Spaces
- Tutorial: Implementing Algebraic Structures
Number Theory¶
Monoids, representation Theory¶
- Demonstration: Sage + GAP4 + GAP3 + Chevie + Semigroupe (experimental)
- Demonstration: Calculations with character rings of the biHecke monoid (experimental)
- Demonstration: Computational representation theory for finite monoids (experimental)
- Demonstration: Representation theory of monoids and Markov chains: generalized Tsetlin library (experimental)
- Demonstration: A real life example, parallel testing of a conjecture on J-Trivial monoids using MuPAD (experimental)
Combinatorics¶
- Demonstration: Combinatorics (short)
- Demonstration: Sage-Combinat
- Introduction to combinatorics in Sage
- Tutorial: Enumerated sets
Algebraic Combinatorics¶
Dynamics¶
Numerical computations¶
Programming and Design¶
- Demonstration: Cython: Python -> C
- Sage Introductory Programming Tutorial
- Tutorial: Comprehensions, Iterators, and Iterables
- Tutorial: Programming in Python and Sage
- Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Tris et complexité
- Functional Programming for Mathematicians
- Tutorial: Objects and Classes in Python and Sage
- Tutorial: Testing a conjecture in parallel (draft)
Advanced programming¶
Design and Categories¶
Indices and tables¶
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
About More SageMath thematic tutorials¶
This repository is meant as a place to collectively share and evolve thematic tutorials for the SageMath with the aim to merge the mature ones into Sage’s official thematic tutorials.
Rationale¶
Over the years, many of us have grown personal collections of tutorials written at the occasion of various events (courses, Sage Days, …). We hope that putting them together will foster reuse, collective writing, cross references, cross-reviews, and maturation in general.
We use ReST + Sphinx as authoring format, following Sage’s documentation conventions. Here is our rationale:
- Can be tested, version controlled, …;
- Can be converted to many formats: ipynb, web page, pdf, …;
- Consistency with Sage’s documentation and documentation tools;
- Enable powerful cross linking; in particular crosslinks to the Python and Sage documentation are fully supported;
- Paves the way for integration into the Sage.
Contributing¶
Contributions of all kinds are most welcome! If you spot a typo while reading the documents on ReadTheDocs please follow “Edit on GitHub” -> “Edit this file” and submit your change (this requires a GitHub account).
For larger changes, pull requests are very welcome. For regular contributions, ask e.g. @nthiery for direct access to the project’s repository.
Recommendations¶
Break the tutorials in small units (10-20 minutes), each in its separate file.
Specify at the beginning of the unit its aim: what the reader can expect to learn.
Write a summary at the end of what was learned, with links to related or followup tutorials: ‘too learn more about xxx, you may want to read yyy’.
Include lots of cross links.
Include lots of exercises.
Include corrections for the exercises, possibly in a separate file. Alternatively, we could use the ifconfig sphinx extension.
Whenever relevant: explain the math behind. Think you are writing a math book, illustrated with Sage.
If there is a natural location for the unit in the Sage sources (e.g. a tutorial about rings in Sage could go in \(sage.rings.tutorial\)) plan to put it there, in a python file \(sage/rings/tutorial.py\). This way, Once integrated into Sage, it will be accessible to the user with \(sage.rings.tutorial?\).
To avoid naming conflicts, the file in this repository should actually be \(mocksage/rings/tutorial.py\).
Units that are tied to a given event (talk, workshop, course) should be seen as mostly owned by their main author(s). Typo fixes are very welcome, but refrain from other modifications.
All other units are joint property. Any refactoring is welcome as long at the original aim maintained.
Test the examples in the documents; e.g.:
sage -t foo.rst
Usage¶
A copy of the html outputs is built and hosted on ReadTheDocs It’s automatically updated each time commits are pushed on the repository. This takes a couple minutes. In case some configuration needs to be tweaked, the ReadTheDoc’s project is currently owned by @nthiery.
To compile the documents locally:
git clone https://github.com/sagemath/more-sagemath-tutorials.git
cd more-sagemath-tutorials
pip install --user -e . # Alternative: python setup.py install
make html
A few files are automatically generated. When adding/removing a document one need to regenerate them:
make distclean
make html
For now those files are version controlled in the git repository. So make a commit.
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Events: Talks, workshops, courses¶
2017¶
2012¶
- École CIMPA Bobo 2012: Mathématiques discrètes, Aspects Combinatoire, Dynamique et Algorithmique
- Journée de l’Informatique Libre, 28/04/2012: Développement collaboratif libre d’applications métier; étude de cas: Sage pour les Mathématiques
- Groupe d’utilisateur de Sage, 16/02/2012: Démonstration rapide de Sage
2011¶
2010¶
- Nikolaus Conference 2010, Aachen: Sage-Combinat demo
- Montreal Python: Sage Demo
- LACIM 2010, Montreal: Sage and Sage-Combinat demo
- FPSAC‘10, San Francisco: Sage and Sage-Combinat demo
- Affine Schubert Calculus Workshop, Toronto: Sage and Sage-Combinat demo
- Joint Sage-Combinat & Chevie workshop, Orsay: Sage and Sage-Combinat demo
- LAGA, Villetaneuse, 2010/06/11: Sage demo
- Sage Days 20.5: Representation theory of finite monoids demo
- SLC 64: Sage and Sage-Combinat demo
2009¶
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Università di Siena, Maggio 2009¶
Tutorials¶
- Finding help
- Working with Lists
- First steps towards programming
- Calculus, plotting & interact
- The 3n+1 Conjecture
- Linear Algebra
- Strings and the Burrows-Wheeler Transform
- Dictionaries and Graph Theory
- Combinatorics in Sage
- Introduction to Cython
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Here are several ways of getting help from within Sage.
Does Sage have a command for defining a permutation? (Hint: Start typing
Perm
and then hit the tab key.)
sage: Perm
To see documentation and examples for the Permutation command, type
Permutation?
or Permutation(
and hit tab (or enter).
sage: Permutation
Exercises:
Create the permutation \(51324\) and assign it to the variable
p
.sage: # edit here
Find the inverse and the length of
p
. (Hint: to see the methods available top
, you can type ‘p.
‘ and hit tab.)sage: p.
Does
p
have the pattern \(123\)? What about \(1234\)? And \(312\)?sage: p.
To see the how the inverse of p
is computed, type p.inverse??
and
hit tab (or enter).
sage: p.inverse()
There are other ways to get help.
Click on Help on the top right of this page.
Use the command ‘search_doc’:
sage: search_doc()
Use the command ‘search_src’:
sage: search_src()
Use the command ‘search_def’:
sage: search_def()
Exercises:
Use ‘search_doc’ to find information about Taylor series, then define the function \(f(t) = sin(t)\) and find its Taylor series expanded about \(t=0\) up to degree \(14\).
sage: search_doc()
Can you guess an expression for the \(n\)-th term of the Taylor series of \(f\)? (Hint: you might find the command
sloane_find
useful in finding an expression for the denominators.)sage: sloane_find()
Several of your exercises will from from the Project Euler website:
Project Euler is a series of challenging mathematical/computer programming problems that will require more than just mathematical insights to solve. Although mathematics will help you arrive at elegant and efficient methods, the use of a computer and programming skills will be required to solve most problems.
Each problem has been designed according to a “one-minute rule”, which means that although it may take several hours to design a successful algorithm with more difficult problems, an efficient implementation will allow a solution to be obtained on a modestly powered computer in less than one minute.
Exercise: Go to the Project Euler website ( www.projecteuler.net ) and create an account.
The prime factors of \(13195\) are \(5\), \(7\), \(13\) and \(29\).
What is the largest prime factor of the number \(600851475143\)?
(After you solve this problem, visit the Project Euler website and enter your answer. Visit the forums and read some of the other solutions. Pick one that you like best.)
\(2520\) is the smallest number that can be divided by each of the numbers from \(1\) to \(10\) without any remainder. What is the smallest number that is evenly divisible by all of the numbers from \(1\) to \(20\)?
(After you solve this problem, visit the Project Euler website and enter your answer. Visit the forums and read some of the other solutions. Pick one that you like best.)
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
To create a list of objects, use square brackets.
Create the list
[63, 12, -10, 'a', 12]
, assign it to the variableL
, and print the list. (Hint : Variable assignment in Sage/Python is done with=
. For example,a = 3
defines thea
to be3
.)sage: # edit here
Use the
len
command to find the length of the listL
.sage: # edit here
To access an element of the list, use the syntax
L[i]
, wherei
is the index of the item. What isL[3]
?sage: # edit here
What is
L[1]
?sage: # edit here
What is the index of the first item of
L
?sage: # edit here
What is
L[-1], L[-2]
?sage: # edit here
Access the last item in
L
.sage: # edit here
An important concept about lists is that (like dictionaries, but unlike many objects in Sage), they can be directly changed. This property is known as mutability.
Change
L[3]
to17
.sage: # edit here
This concept can lead to some confusion at first. See if you can guess what the output of the following commands will be.:
sage: a = [1,2,3]
sage: b = a
sage: b[0] = 7
sage: print a, b
This result makes sense when you understand that a
and b
are both labels
attached to the same list. Compare that result with the following.:
sage: a = 2
sage: b = a
sage: b = b + 1
sage: print a, b
In this case, we changed the object that b
is attached to (to the object
2
plus the object 1
, which is the object 3
), while a
continues
to be attached to the object 2
. This concept will be useful to keep in
mind, as we discuss some methods which can be used to modify lists.
By typing
L.<tab key>
, you get a list of methods forL
. Use one of these methods to append 17 to the end ofL
.sage: # edit here
Insert the letter ‘b’ at index position 2 (do not change the element in position 2, but add a new element).
sage: # edit here
Remove the second occurrence of \(12\) from
L
.sage: # edit here
Redefine
L
to be the list[3, 1, 4, 1, 5, -1, 0]
.sage: # edit here
Reverse the list
L
.sage: # edit here
Sort the list
L
.sage: # edit here
Guess the result of the following commands.
sage: L = [3, 1, 2] sage: M = L.sort() sage: print L, M
Now try the following.
sage: L = [3, 1, 2] sage: M = sorted(L) sage: print L, M
The range
command provides an easy way to construct a list of integers.
Read the documentation (type:
range?
and hit enter or tab). Use it to create the list \([1,2,\ldots,50]\).sage: # edit here
Create the list of even numbers between 1 and 100 (including 100).
sage: # edit here
The
step
argument in therange
command can be negative. Userange
to construct the list \([10, 7, 4, 1, -2]\).sage: # edit here
Sage (but not Python!) includes syntax to simplify creating lists like the above easier. What is the output of the command
[2, 4, .., 100]
?sage: # edit here
Create the list \([1, 1.5, 2.0, 2.5, ..., 5]\) using Sage’s special syntax. Compare this with the output of
range(1,5,0.5)
.sage: # edit here
We already know how to create the list \([1, 2, \ldots, 10]\):
sage: range(1,11)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Using a list comprehension, we can now create the list \([1^2, 2^2, 3^2, ..., 10^2]\)
sage: [i^2 for i in range(1,11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Exercises:
Create two lists:
\[\begin{split}x = [1, 2, \ldots, 100] \\ y = [1^2, 2^2, \ldots, 100^2]\end{split}\]sage: # edit here
Use a list comprehension to construct the list
\[[x_0 + y_0, x_1 + y_1, \ldots, x_{99}+y_{99}]\]sage: # edit here
Using a list comprehension and the command
sum
, compute\[\sum_{i=0}^{99} x_i y_i\]sage: # edit here
The sum of the squares of the first ten natural numbers is:
The square of the sum of the first ten natural numbers is:
Hence the difference between the sum of the squares of the first ten natural numbers and the square of the sum is
Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum.
sage: # edit here
A list can be filtered using a list comprehension. For example, to create a list of the squares of the prime numbers between 1 and 100, we use a list comprehension as follows:
sage: [p^2 for p in [1,2,..,100] if is_prime(p)]
Exercise: Use a list comprehension to list all the natural numbers below 20 that are multiples of 3 or 5. Hints:
- To get the remainder of 7 divided by 3 use
7 % 3
.- To test for equality use two equal signs (
==
); for example,3 == 7
.
sage: # edit here
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.
sage: # edit here
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
In this worksheet, you will learn to define functions, write for loops, conditional statements and and continue to learn about lists.
To define a function in Sage, use the def
command and a colon after the
variable names. For example:
sage: def is_even(n):
....: return n % 2 == 0
Notice that body of the function (the line: return n % 2 == 0
) is
indented. The indentation defines the body of the function.
Exercises:
Define a function called
square
that returns the square of a number:sage: # edit here
Below is an example of an if
statement:
sage: if n % 2 == 0:
....: return True
....: else:
....: return False
Notice again how the commands in the first block and the second block of the statement are indented.
The following example defines the factorial_function
, which takes a
number \(n\) and returns the product \(n(n-1)(n-2)\cdots1\):
sage: def factorial_function(n):
....: if n == 0:
....: return 1
....: elif n == 1:
....: return 1
....: else:
....: return n*factorial_function(n-1)
Exercises:
Define a function
sign
that returns the sign of a number:sage: # edit here
The following example uses a for loop to create a list of all the numbers between 1 and 1000 that are multiples of 3 and 5:
sage: nums = []
sage: for i in range(1,1001):
....: if i % 3 == 0 and i % 5 == 0:
....: nums.append(i)
sage: print nums
[15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 255, 270, 285, 300, 315, 330, 345, 360, 375, 390, 405, 420, 435, 450, 465, 480, 495, 510, 525, 540, 555, 570, 585, 600, 615, 630, 645, 660, 675, 690, 705, 720, 735, 750, 765, 780, 795, 810, 825, 840, 855, 870, 885, 900, 915, 930, 945, 960, 975, 990]
Exercises:
Recall that the Fibonacci sequence is the sequence of numbers that begins with \(F_0 = 0\), \(F_1=1\), and that satisfies the equation \(F_n = F_{n-1} + F_{n-2}\) for all \(n\geq2\). Define a function that returns a list of the first
m
terms in the Fibonacci sequence:sage: # edit here
Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
Find the sum of all the even-valued terms in the sequence which do not exceed four million.
sage: # edit here
You can slice a list to obtain only part of it. The syntax is
L[start:stop:step]
.
Let
L = range(100)
, and try the followingL[0:3], L[:3], L[1:], L[1:-1], L[::2]
.sage: # edit here
Create a list
L
.sage: # edit here
Use a slice to obtain the reversal of
L
.sage: # edit here
Revese the list
L
usingL.reverse()
.sage: # edit here
What is the difference between these methods of reversing a list?
sage: # edit here
A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is \(9009 = 91 \times 99\).
Find the largest palindrome made from the product of two 3-digit numbers.
Hints:
7%3
returns the remainder of 7 divided by 3.7//3
returns the integer quotient of 7 by 3.
sage: # edit here
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Exercises
Let \(f(x) = x^4 + x^3 - 13 x^2 - x + 12\). Define \(f\) as a symbolic function.
sage: # edit here
Plot \(f\) on the domain \(-4.5 \leq x \leq 3.5\).
sage: # edit here
Find numerical approximations for the critical values of \(f\) by taking the derivative of \(f\) and using the
find_root
method. (Hint: plot the derivative.)sage: # edit here
Find numerical approximations for the critical values of \(f\) by taking the derivative of \(f\) and using the
roots(ring=RR)
method. (Here,RR
stands for the real numbers.) Are there any roots over the ring of rationals (QQ
)?sage: # edit here
Compute the equation \(y = mx +b\) of the tangent line to the function \(f\) at the points \(x=-1\) and \(x=2\).
sage: # edit here
Write a function that takes \(x\) as an argument and returns the equation of the tangent line to \(f\) through the point \(x\).
sage: # edit here
Write a function that takes \(x\) as an argument and plots \(f\) together with the the tangent line to \(f\) through the point \(x\). Make the line red.
sage: # edit here
Convert the function you created above into an
@interact
object. Turn the argument \(x\) into aslider
. (Hint: see the documentation forinteract
for examples on creatingsliders
.)sage: # edit here
Using symbolic functions and the command desolve
in Sage, we can
define and solve differential equations. Here is an example.
We will solve the following differential equation:
First we define the variable \(t\):
sage: var('t')
t
Next, we define the symbolic function \(y\):
sage: y = function('y', t)
sage: y
y(t)
We can now create the differential equation:
sage: diff_eqn = diff(y,t) + y - 1
sage: diff_eqn
diff(y(t), t, 1) + y(t) - 1
We can use the show
command to typeset the above equation to make it
easier to read:
sage: show(diff_eqn)
Finally, we use the desolve
command to solve the differential equation:
sage: soln = desolve(diff_eqn, y)
sage: soln
e^(-t)*(e^t + c)
sage: show(soln)
Exercises
Find and plot the solution to the following differential equation with the intial condition \(y(0) = -2\).
\[y'(t) = y(t)^2 - 1\](Hint: see the documentation of the
desolve
command for dealing with initial conditions.)sage: # edit here
Find and plot the solution to the differential equation
\[t y'(t) + 2 y(t) = \frac{e^t}{t}\]with initial conditions \(y(1) = -2\). (Hint: see the documentation of the
desolve
command for dealing with initial conditions.) [Introductory Differential Equations using SAGE, David Joyner]sage: # edit here
Let \(a>b>0\) be fixed real numbers and form a triangle with one vertex on the line \(y=x\), one vertex on the line \(y=0\) and the third vertex equal to \((a,b)\).
Find the coordinates of the vertices that minimize the perimeter of the triangle (remember that (a,b) is fixed!). What is the perimeter?
sage: # edit here
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
The \(3n+1\) conjecture is an unsolved conjecture in mathematics. It is also known as the Collatz conjecture, as the Ulam conjecture (after Stanislaw Ulam), or as the Syracuse problem. Lothar Collatz was the first person to propose the problem in 1937.
Consider the following operation on positive integers \(n\).
- If \(n\) is even, then divide it by \(2\).
- If \(n\) is odd, then multiply it by \(3\) and add \(1\).
For example, if we apply this transformation to \(6\) , then we get \(3\) since \(6\) is even; and if we apply this operation to \(11\) , then we get \(34\) since \(11\) is odd.
Exercises
Write a function that implements this operation, and compute the images of \(1, 2, \ldots 100\).
sage: # edit here
If we start with \(n=6\) and apply this operation, then we get \(3\).
If we now apply this operation to \(3\), then we get \(10\).
Applying the operation to \(10\) outputs \(5\).
Continuing in this way, we get a sequence of integers.
For example, starting with \(n=6\), we get the sequence:
Notice that this sequence has entered the loop \(4 \mapsto 2 \mapsto 1 \mapsto 4\). One formulation of the Collatz conjecture is the following.
- 3n+1 conjecture:
- For every positive integer \(n\), the resulting sequence will always reach the number \(1\).
Exercises
Write a function that takes a positive integer and returns the sequence until it reaches \(1\) . For example, for
6
, your function will return[ 6, 3, 10, 5, 16, 8, 4, 2, 1 ]
. Find the largest values in the sequences for1, 3, 6, 9, 16, 27
(Hint : You might find a
while
helpful here. Below is a very simple example that repeatedly adds2
to the variablex
untilx
is no longer less than 7.)x = 0 while x < 7: x = x + 2 print x
sage: # edit here
Use the
line
command to plot the sequence for27
.sage: # edit here
Write an
@interact
function that takes an integer \(n\) and plots the sequence for \(n\).sage: # edit here
The number of steps it takes for a sequence to reach \(1\) is the stopping time . For example, the stopping time of \(1\) is \(0\) and the stopping time of \(6\) is \(8\).
Exercises
Write a function that returns the stopping time of a poisitve integer \(n\). Plot the stopping times for \(1, 2, ..., 100\) in a
bar chart
.sage: # edit here
Find the number less than \(1000\) with the largest stopping time. What is its stopping time? Repeat this for \(2000, 3000, ..., 10000\).
sage: # edit here
If \(n\) is odd, then \(3n+1\) is even. So we can instead consider the \(\frac{3n+1}{2}\)-operator that maps \(n\) to \(\frac{n}{2}\), if \(n\) is even; and to \(\frac{3n+1}{2}\), if \(n\) is odd.
Exercises
Implement the \(\frac{3n+1}{2}\)-operator.
sage: # edit here
Consider the following function.
\[f(z)=\frac z 2 \cos^2\left(z \frac \pi 2 \right)+\frac{(3z+1)}{2}\sin^2\left(z \frac \pi 2 \right)\]Construct \(f\) as a symbolic function and use Sage to verify that \(f(n) = T(n)\) for all \(1 \leq n \leq 1000\), where \(T\) is the \(\frac{3n+1}{2}\)-operator. Argue that \(f\) is a smooth extension of \(T\) to the complex plane.
sage: edit here
Let \(g(z)\) be the complex function:
\[g(z) = \frac{1}{4}(1 + 4z - (1 + 2z)\cos(\pi z))\]Construct \(g\) as a symbolic function, and show that \(f\) and \(g\) are equal. Hint: Explore the various methods for symbolic functions beginning with
.trig_
.sage: edit here
Use the
complex_plot
command to plotg
in the domain \(x=-5,...,5\) and \(y=-5,...,5\).sage: edit here
Consider the composition \(h_n(z) = (g \circ g \circ \cdots \circ g)\) (where there are \(n\) copies of \(g\) in this composition). Use
complex_plot
andgraphics_array
to plot \(h_1\), \(h_2\), \(h_3\), …, \(h_6\) on the domain \(x=1,...,5\) and \(y=-0.5,...,0.5\).(Hint: To speed things up or control the precision of the computations, you may want to replace
pi
in your equation withCDF.pi()
. TypeCDF?
andCDF.pi?
for more information.)sage: edit here
Generate some really nice images of \(h_n\) that illustrate the fractal-like behaviour of \(h_n\). (Hint: You may want to explore the
plot_points
andinterpolation
options for thecomplex_plot
command.)sage: edit here
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
To create a vector in Sage, use the vector
command.
Note
vectors in Sage are row vectors!
Exercises
Create the vector \(x = (1, 2, \ldots, 100)\).
sage: # edit here
Create the vector \(y = (1^2, 2^2, \ldots, 100^2)\).
sage: # edit here
Type
x.
and hit tab to see the available methods for vectors. Find the norm (length) of the vectorsx
andy
.sage: # edit here
Find the dot product of
x
andy
.sage: # edit here
[The above exercises are essentially the first problem on Exercise Set 1 of William Stein’s Math 480b]
Use the
matrix
command to create the following matrix over the rational numbers (hint: in Sage,QQ
denotes the field of rational numbers).\[\begin{split}\left(\begin{array}{rrrrrr}3 & 2 & 2 & 1 & 1 & 0 \\2 & 3 & 1 & 0 & 2 & 1 \\2 & 1 & 3 & 2 & 0 & 1 \\1 & 0 & 2 & 3 & 1 & 2 \\1 & 2 & 0 & 1 & 3 & 2 \\0 & 1 & 1 & 2 & 2 & 3\end{array}\right)\end{split}\]Find the echelon form of the above matrix.
sage: # edit here
Find the right kernel of the matrix.
sage: # edit here
Find the eigenvalues of the matrix.
sage: # edit here
Find the left eigenvectors of the matrix.
sage: # edit here
Find the right eigenspaces of the matrix.
sage: # edit here
For what values of \(k\) is the determinant of the following matrix \(0\)?
\[\begin{split}\left(\begin{array}{rrr}1 & 1 & -1 \\2 & 3 & k \\1 & k & 3\end{array}\right)\end{split}\]sage: # edit here
[K. R. Matthews, Elementary Linear Algebra , Chapter 4, Problem 8]
Prove that the determinant of the following matrix is \(-8\).
\[\begin{split}\left(\begin{array}{rrr}{n}^{2} & {\left( n + 1 \right)}^{2} & {\left( n + 2\right)}^{2} \\{\left( n + 1 \right)}^{2} & {\left( n + 2 \right)}^{2} &{\left( n + 3 \right)}^{2} \\{\left( n + 2 \right)}^{2} & {\left( n + 3 \right)}^{2} & {\left( n + 4 \right)}^{2}\end{array}\right)\end{split}\]sage: # edit here
[K. R. Matthews, Elementary Linear Algebra , Chapter 4, Problem 3]
Prove that if \(a \neq c\), then the line through the points \((a,b)\) and \((c,d)\) is given by the following equation.
\[\begin{split}\det\left(\begin{array}{rrr}x & y & 1 \\a & b & 1 \\c & d & 1\end{array}\right) = 0.\end{split}\]sage: # edit here
Find the determinant of the following matrices.
\[\begin{split}\left(\begin{array}{r}1\end{array}\right),\left(\begin{array}{rr}1 & 1 \\r & 1\end{array}\right),\left(\begin{array}{rrr}1 & 1 & 1 \\r & 1 & 1 \\r & r & 1\end{array}\right),\left(\begin{array}{rrrr}1 & 1 & 1 & 1 \\r & 1 & 1 & 1 \\r & r & 1 & 1 \\r & r & r & 1\end{array}\right),\left(\begin{array}{rrrrr}1 & 1 & 1 & 1 & 1 \\r & 1 & 1 & 1 & 1 \\r & r & 1 & 1 & 1 \\r & r & r & 1 & 1 \\r & r & r & r & 1\end{array}\right)\end{split}\]Make a conjecture about the determinant of an arbitrary matrix in this sequence. Can you prove it your conjecture?
sage: # edit here
[Adapted from: K. R. Matthews, Elementary Linear Algebra , Chapter 4, Problem 19]
What is the largest determinant possible for a \(3\times3\) matrix whose entries are \(1, 2, \dots, 9\) (each occurring exactly once, in any order). How many matrices \(M\) achieve this maximum?
(Hint: You might find the command
Permutations
useful. The following code will construct all the lists that have the entries \(1, 2, 3, 4\), each appearing exactly once.)for P in Permutations(4): L = list(P) print L
sage: for P in Permutations(4): ....: L = list(P) ....: print L [1, 2, 3, 4] [1, 2, 4, 3] [1, 3, 2, 4] [1, 3, 4, 2] [1, 4, 2, 3] [1, 4, 3, 2] [2, 1, 3, 4] [2, 1, 4, 3] [2, 3, 1, 4] [2, 3, 4, 1] [2, 4, 1, 3] [2, 4, 3, 1] [3, 1, 2, 4] [3, 1, 4, 2] [3, 2, 1, 4] [3, 2, 4, 1] [3, 4, 1, 2] [3, 4, 2, 1] [4, 1, 2, 3] [4, 1, 3, 2] [4, 2, 1, 3] [4, 2, 3, 1] [4, 3, 1, 2] [4, 3, 2, 1]
sage: # edit here
In the \(20 \times 20\) grid below, four numbers along a diagonal line have been highlighted.
08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 0849 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 0081 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 6552 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 9122 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 8024 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 5032 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 7067 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 2124 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 7221 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 9578 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 9216 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 5786 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 5819 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 4004 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 6688 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 6904 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 3620 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 1620 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 5401 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48
The product of these numbers is \(26 \times 63 \times 78 \times 14 = 1788696\).
What is the greatest product of four adjacent numbers in any direction (up, down, left, right, or diagonally) in the \(20 \times 20\) grid?
sage: A = matrix(20, 20, [
....: 8, 2,22,97,38,15, 0,40, 0,75, 4, 5, 7,78,52,12,50,77,91, 8,
....: 49,49,99,40,17,81,18,57,60,87,17,40,98,43,69,48, 4,56,62, 0,
....: 81,49,31,73,55,79,14,29,93,71,40,67,53,88,30, 3,49,13,36,65,
....: 52,70,95,23, 4,60,11,42,69,24,68,56, 1,32,56,71,37, 2,36,91,
....: 22,31,16,71,51,67,63,89,41,92,36,54,22,40,40,28,66,33,13,80,
....: 24,47,32,60,99, 3,45, 2,44,75,33,53,78,36,84,20,35,17,12,50,
....: 32,98,81,28,64,23,67,10,26,38,40,67,59,54,70,66,18,38,64,70,
....: 67,26,20,68, 2,62,12,20,95,63,94,39,63, 8,40,91,66,49,94,21,
....: 24,55,58, 5,66,73,99,26,97,17,78,78,96,83,14,88,34,89,63,72,
....: 21,36,23, 9,75, 0,76,44,20,45,35,14, 0,61,33,97,34,31,33,95,
....: 78,17,53,28,22,75,31,67,15,94, 3,80, 4,62,16,14, 9,53,56,92,
....: 16,39, 5,42,96,35,31,47,55,58,88,24, 0,17,54,24,36,29,85,57,
....: 86,56, 0,48,35,71,89, 7, 5,44,44,37,44,60,21,58,51,54,17,58,
....: 19,80,81,68, 5,94,47,69,28,73,92,13,86,52,17,77, 4,89,55,40,
....: 4,52, 8,83,97,35,99,16, 7,97,57,32,16,26,26,79,33,27,98,66,
....: 88,36,68,87,57,62,20,72, 3,46,33,67,46,55,12,32,63,93,53,69,
....: 4,42,16,73,38,25,39,11,24,94,72,18, 8,46,29,32,40,62,76,36,
....: 20,69,36,41,72,30,23,88,34,62,99,69,82,67,59,85,74, 4,36,16,
....: 20,73,35,29,78,31,90, 1,74,31,49,71,48,86,81,16,23,57, 5,54,
....: 1,70,54,71,83,51,54,69,16,92,33,48,61,43,52, 1,89,19,67,48
....: ])
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Sage/Python includes a builtin datastructure from strings.
There are several ways to input strings. You can input a string using single quotes (‘) or double quotes (“):
sage: s = "This is a string!"
sage: s
'This is a string!'
sage: t = 'So is this!'
sage: print t
So is this!
You can also input a string using three quotes (“”” or ‘’‘). This is useful if you want to use both ” and ‘ in your string, or you want your string to span multiple lines:
sage: s = """
sage: This is a multi-line
....: string
sage: that includes 'single quotes'
....: and "double quotes".
sage: """
sage: print s
This is a multi-line
string
that includes 'single quotes'
and "double quotes".
Exercises
Create and print the following string
\ | ( | ) / / _________________ | | | | | I <3 Coffee! /--\ | | | \ /\--/ \___________/
Without using cut-and-paste(!) replace the substring
I <3 Coffee!
with the substringI <3 Tea!
.Print a copy of your string with all the letters capitalized (upercase).
Strings behave very much like lists. The table below summarizes their common operations.
Operation Syntax for lists Syntax for strings Accessing a letter list[3]
string[3]
Slicing list[3:17:2]
string[3:17:2]
Concatenation list1 + list2
string1 + sting2
A copy list[:]
string[:]
A reversed copy list[::-1]
string[::-1]
Length len(list)
len(string)
Exercises
The factors of length 2 of ‘rhubarb’ are
rhhuubbaarrbWrite a function called
factors
that returns a list of the factors of lengthl
ofs
, and list all the factors of length 3 of ‘rhubarb’.sage: # edit here
What happens if you apply your function
factors
to the list[0,1,1,0,1,0,0,1]
? If it doesn’t work for both lists and strings, go back and modify your function so that it does work for both.sage: # edit here
The string
WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWBWWWWWWWWWWWWWW
begins with W
12 times, then B
once, then W
12 times, then B
3
times, then W
24 times, then B
once and then W
14 times. Thus, it
can be encoded by the tuples:
(W, 12), (B, 1), (W, 12), (B, 3), (W, 24), (B, 1), (W, 14)
This is called the run-length encoding of the string.
Exercises
Write a function that returns the run-length encoding of a string. Does your function work for lists as well as strings? If not, then can you make it so that it works for both strings and lists? Use your function to compute the run-length encoding of the list:
[0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
sage: # edit here
The rotations of the string ‘bananas’ are:
bananasananasbnanasbaanasbannasbanaasbanansbanana
and if we sort these alphabetically, then we get:
ananasbanasbanasbananbananasnanasbanasbanasbanana
Exercises
Define a function
print_sorted_rotations
that sorts all the rotations of a string and prints them in an array as above. Print the sorted rotations of the strings ‘ananas’ and ‘cocomero’.sage: # edit here
The Burrows-Wheeler Transform (BWT) of a string s
sorts all the
rotations of s
and then returns the last column.
For example, if we sort the rotations of ‘bananas’:
ananasbanasbanasbananbananasnanasbanasbanasbanana
then the last column is bnnsaaa , so the BWT of bananas is bnnsaaa.
Exercises
Write a function that returns the BWT of a string. Compute the BWT of bananas , ananas and cocomero . (Hint: You can return you answer as a list, but if you want to return a string, then you might want to use the
join
method for strings.)sage: # edit here
Combine the functions you defined above to create an
@interact
object that takes a strings
and prints:- the sorted rotations of
s
- the run-length encoding of
s
- the BWT of
s
- the run-length encoding of the BWT of
s
(Hint: String formatting can be done using the
%
operator. Here is an example:sage: print 'The sum of %s and %s is %s.' % (3,2,3+2) The sum of 3 and 2 is 5.
If you are familiar with C then you will notice that string formating is very similar to C ‘s
sprintf
statement.)sage: # edit here
- the sorted rotations of
Use your interact object to explore this transformation, and to answer the following questions.
- Compute the BWT of the following.
xxyxyxyxyxyxyxyxyxxyxyxyxyxyxyxyxyxy
01101001100101101001011001101001100101100110100101
cdccdcdccdccdcdccdcdccdccdcdccdccdcdccdcdccdccdcdc
- Do you notice any patterns in the BWT of a string?
- Can you think of an application for this transformation?
- Find 3 other strings that have a ‘nice’ image under the BWT.
- Is the Burrows-Wheeler transformation invertible? (That is, can you find two strings that have the same BWT?)
sage: # edit here
- Compute the BWT of the following.
By comparing the BWT of a string with the first column of the array of sorted rotations of a string
s
, devise and implement an algorithm that reconstructs the first column of the array from the BWT ofs
.sage: # edit here
By examining the first two columns of the array, devise and implement an algorithm that reconstructs the first two columns of the array from the BWT of a string. ( Hint: compare the last and first column with the first two columns.)
sage: # edit here
By examining the first three columns of the array, devise and implement an algorithm that reconstructs the first three columns of the array from the BWT of a string.
sage: # edit here
Write a function that reconstructs the entire array of sorted rotations of a string from the BWT of the string.
sage: # edit here
A Lyndon word is a word w that comes first in alphabetical order among all its rotations. Is the BWT invertible on Lyndon words?
sage: # edit here
Explain how one can modify the BWT to make it invertible on arbitrary words. Implement your modified transformation and the inverse transformation.
sage: # edit here
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
A dictionary is another builtin datatype. Unlike lists or tuples, which are indexed by a range of numbers, dictionaries are indexed by keys , which can be any immutable type. Strings and numbers can always be keys. Dictionaries are sometimes called “associative arrays” in other programming languages.
There are several ways to define dictionaries. Below are three different methods.
sage: d = {1:37, 17:'a', 'x':9}
sage: d
{1: 37, 'x': 9, 17: 'a'}
sage: d = dict([(1,37), (17,'a'), ('x',9)])
sage: d
{1: 37, 'x': 9, 17: 'a'}
If all the keys are strings, then the following shorthand is sometimes useful:
sage: d = dict(key1='value1', key2='value2')
sage: d
{'key2': 'value2', 'key1': 'value1'}
Dictionaries behave as lists, tuples, and strings for several important operations.
Operation Syntax for lists Syntax for dictionaies Accessing elements L[3]
D[3]
Length len(L)
len(D)
Modifying L[3] = 17
D[3] = 17
Deleting items del L[3]
del D[3]
Exercises
- In the directed graph below, the vertex 1 points to the vertices in the list [2, 3].
Use the
DiGraph
command to contruct the above directed graph, and plot the directed graph (Hint : In the documentation forDiGraph
, take a look at the dictionary of lists example.)sage: # edit here
Find the adjacency matrix of the graph you constructed above.
sage: # edit here
Compute the square of the adjacency matrix. Give a graph-theoretic intepretation of the numbers in this matrix. Does your intepretation hold for the cube of the adjacency matrix?
sage: # edit here
Exercise: The Seven Bridges of Königsberg is the following famous historical problem solved by Leonhard Euler in 1735. This is the problem that started graph theory.
“The city of Königsberg in Prussia (now Kaliningrad, Russia) was set on both sides of the Pregel River, and included two large islands which were connected to each other and the mainland by seven bridges.
The problem was to find a walk through the city that would cross each bridge once and only once. The islands could not be reached by any route other than the bridges, and every bridge must have been crossed completely every time (one could not walk halfway onto the bridge and then turn around to come at it from another side).”
Exercises
- Enter the graph on the right into Sage (use the
Graph
command, not theDiGraph
command). - Solve the problem; that is, does such a walk exist? (Hint: Take a look
at the documentation for the
eulerian_circuit
method; look up Eulerian circuit in Wikipedia if you don’t know its definition.)
The Coxeter graph is the graph with vertices 28 vertices \(v_{i,j}\), for \(0 \leq i, j, \leq 6\), and with edges described by the rules:
- \(v_{0,i}\) is connected to \(v_{1,i}, v_{2,i}, v_{3,i}\) for all \(0\leq i \leq 6\);
- \(v_{1,j}\) is connected to \(v_{1, j+1 (mod\, 7)}\) for all \(0\leq j \leq 6\);
- \(v_{2,j}\) is connected to \(v_{2, j+2 (mod\, 7)}\) for all \(0\leq j \leq 6\);
- \(v_{3,j}\) is connected to \(v_{3, j+3 (mod\, 7)}\) for all \(0\leq j \leq 6\).
Exercises
Construct a dictionary
V
such thatV[(i,j)]
is the list of vertices(r,s)
that are connected to(i,j)
. Use this dictionary to construct and plot the Coxeter graph . (Hints: Note that writingV[i,j]
is shorthand for writingV[(i,j)]
. You should be able to generate the lists of vertices by using loops and list comprehensions.)sage: # edit here
The spectrum of a graph is the set of eigenvalues of the adjacency matrix of the graph. The spectrum of the Coxeter graph is
- \(-1-\sqrt{6}\), with multiplicity 6,
- \(-1\), with multiplicity 7,
- \(\sqrt{2}-1\), with multiplicity 6,
- \(2\), with multiplicity 8,
- \(3\), with multiplicity 1.
It turns out that no other graph has this same spectrum (in this case, we say that the graph is determined by its spectrum ).
Exercises
Test to see that you correctly constructed the Coxeter graph in the previous exercise. That is, compute the adjacency matrix of the Coxeter graph, find the eigenvalues of the adjacency matrix, and then compare them with the above.
sage: # edit here
The command
graphs(n)
generates all the graphs on \(n\) vertices (up to isomorphism). Use this command to test whether there are two graphs with less than 7 vertices that have the same spectrum.sage: # edit here
In the following exercises, we will use Sage to estimate the probability that in a group of \(n\) people, two of them have the same birthday.
Exercises
Using the command
graphs.RandomGNP
, create a function that returns a graph with \(n\) vertices and where the probability that any two of the vertices is connected is 1/365.sage: # edit here
Plot a graph
g
created by your function above using theg.plot(layout='circular')
.sage: # edit here
Create 100 random graphs (using your above function) with \(n=23\) vertices. What ratio of them contains an edge? (Hint: For a graph
g
, the commandg.num_edges()
returns the number of edges ing
.)sage: # edit here
Repeat the above exercise with \(n=57\) vertices.
sage: # edit here
Repeat the above exercises for all the values \(1, 2, ..., 120\). Plot the results using a line graph.
sage: # edit here
[This problem is from William Stein’s Graph Theory Homework for Math 480b 2009]
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
You can use tab completion to explore modules in Sage. For instance, place your cursor at the end of the following lines and hit tab.
sage: sage.combinat.
sage: sage.combinat.sf.
sage: sage.combinat.words.
There is a chapter on Combinatorics in the Reference Manual.
There is a wiki page describing the sage-combinat project.
There is a sage-combinat Google group.
An iterator is an object that allows one to iterate through all the elements of a collection. Iterators have a next()
method that return the next element in the collection.
The Python command iter
returns an iterator from an object (the object itself must support iteration).
sage: it = iter([1,2,3])
sage: it
<listiterator object at 0x35a3950>
sage: it.next()
1
sage: it.next()
2
sage: it.next()
3
sage: it.next()
Traceback (most recent call last):
...
StopIteration
A generator is a function that is used to define an iterator. Instead of return
statements, it has yield
statements (it cannot have both!). When the function encounters a yield
statement, the function pauses and returns that value to the user. It continues from where it left off when it is asked for the next value.
sage: def fibonacci_iterator(a=0, b=1):
....: while True:
....: yield b
....: a, b = b, a+b
sage: f = fibonacci_iterator()
sage: f.next()
1
sage: f.next()
1
sage: f.next()
2
sage: f.next()
3
sage: f.next()
5
sage: f.next()
8
Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, …
Find the sum of all the even-valued terms in the sequence which do not exceed four million.
sage: result = 0
sage: for f in fibonacci_iterator():
....: if f > 4000000:
....: break
....: if is_even(f):
....: result += f
sage: result
4613732
There are many objects in Sage that model sets of combinatorial objects.
sage: P = Permutations(3)
sage: P
Standard permutations of 3
sage: P.cardinality()
6
sage: P.list()
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
sage: Permutations(1000)
Standard permutations of 1000
sage: time P = Permutations(7, avoiding=[2,1,4,3])
sage: P
Time: CPU 0.00 s, Wall: 0.00 s
Standard permutations of 7 avoiding [[2, 1, 4, 3]]
sage: time P.cardinality()
2761
Time: CPU 4.10 s, Wall: 4.20 s
sage: P = Partitions(4)
sage: P
Partitions of the integer 4
sage: for p in Partitions(4):
....: print p
[4]
[3, 1]
[2, 2]
[2, 1, 1]
[1, 1, 1, 1]
sage: for c in Compositions(4):
....: print c
[1, 1, 1, 1]
[1, 1, 2]
[1, 2, 1]
[1, 3]
[2, 1, 1]
[2, 2]
[3, 1]
[4]
sage: DyckWords(4)
Dyck words with 4 opening parentheses and 4 closing parentheses
sage: DyckWords(4).cardinality()
14
sage: for dw in DyckWords(4):
....: print dw
()()()()
()()(())
()(())()
()(()())
()((()))
(())()()
(())(())
(()())()
(()()())
(()(()))
((()))()
((())())
((()()))
(((())))
sage: W = Words("ab")
sage: W
Words over Ordered Alphabet ['a', 'b']
sage: W.cardinality()
+Infinity
sage: it = iter(W)
sage: for a in range(16):
....: print it.next()
word:
word: a
word: b
word: aa
word: ab
word: ba
word: bb
word: aaa
word: aab
word: aba
word: abb
word: baa
word: bab
word: bba
word: bbb
word: aaaa
sage: P = posets()
sage: P
Posets
sage: P.cardinality()
+Infinity
sage: it = iter(P)
sage: for a in range(10):
....: print it.next()
Finite poset containing 0 elements
Finite poset containing 1 elements
Finite poset containing 2 elements
Finite poset containing 2 elements
Finite poset containing 3 elements
Finite poset containing 3 elements
Finite poset containing 3 elements
Finite poset containing 3 elements
Finite poset containing 3 elements
Finite poset containing 4 elements
Sage supports several ways of creating new combinatorial classes from objects.
sage: C = Combinations([1,2,3], 2)
sage: C
Combinations of [1, 2, 3] of length 2
sage: C.list()
[[1, 2], [1, 3], [2, 3]]
sage: S = Subsets([1,2,3], 2)
sage: S
Subsets of {1, 2, 3} of size 2
sage: S.list()
[{1, 2}, {1, 3}, {2, 3}]
sage: S = SetPartitions(['a','b','c'])
sage: S
Set partitions of ['a', 'b', 'c']
sage: S.list()
[{{'a', 'c', 'b'}}, {{'c', 'b'}, {'a'}}, {{'c'}, {'a', 'b'}}, {{'a', 'c'}, {'b'}}, {{'c'}, {'b'}, {'a'}}]
sage: S.cardinality()
5
A vexillary involution is a permutation that:
- avoids the pattern 2143;
- is an involution (that is, \(p = p^{-1}\)).
We can create the set of vexillary involutions of the set {1,2,3,4} in Sage by filtering the set of permutations of {1,2,3,4}.
sage: def is_involution(p):
....: return p == p.inverse()
sage: P = Permutations(4, avoiding=[2,1,4,3]).filter(is_involution)
sage: P
Filtered sublass of Standard permutations of 4 avoiding [[2, 1, 4, 3]]
sage: P.cardinality()
9
sage: P.list()
[[1, 2, 3, 4], [1, 2, 4, 3], [1, 3, 2, 4], [1, 4, 3, 2], [3, 4, 1, 2], [2, 1, 3, 4], [4, 2, 3, 1], [3, 2, 1, 4], [4, 3, 2, 1]]
sage: def number_of_vexillary_involutions(n):
....: P = Permutations(n, avoiding=[2,1,4,3]).filter(is_involution)
....: return P.cardinality()
sage: SL = sloane_find([number_of_vexillary_involutions(n) for n in range(1,7)])
Searching Sloane's online database...
sage: SL[0]
[1006, 'Motzkin numbers: number of ways of drawing any number of nonintersecting chords joining n (labeled) points on a circle.', [1, 1, 2, 4, 9, 21, 51, 127, 323, 835, 2188, 5798, 15511, 41835, 113634, 310572, 853467, 2356779, 6536382, 18199284, 50852019, 142547559, 400763223, 1129760415, 3192727797, 9043402501, 25669818476, 73007772802, 208023278209, 593742784829]]
Warning
Combinatorial classes are now deprecated, and will disappear as soon as all derived classes in Sage’s library will have been fixed.
If you want to work with a set of objects that is not defined in Sage, then you can use the object-oriented features of Python/Sage to define a new class to model your set.
By inheriting from the CombinatorialClass
class, your object will behave like the objects we saw above ( Permutations(3)
, Compositions(6)
, etc.).
At the very minimum, you should implement the following methods:
__init__
: takes as arguments what is needed to define the set;__iter__
: the algorithm toiter
ate through the elements of the set;__repr__
: (optional) a stringrepr
esentation of the set.
sage: class VexillaryInvolutions(CombinatorialClass):
....: def __init__(self, n):
....: """
....: The combinatorial class of vexillary involutions
....: """
....: self._n = n
....:
....: def __iter__(self):
....: P = Permutations(self._n, avoiding=[2,1,4,3]).filter(lambda p : p == p.inverse())
....: for p in P:
....: yield p
....:
....: def __repr__(self):
....: return "Vexillary involutions of %s" % self._n
sage: V.list()
[[1, 2, 3, 4], [1, 2, 4, 3], [1, 3, 2, 4], [1, 4, 3, 2], [3, 4, 1, 2], [2, 1, 3, 4], [4, 2, 3, 1], [3, 2, 1, 4], [4, 3, 2, 1]]
sage: V.cardinality()
9
sage: [2,1,3,4] in V
Traceback (most recent call last):
...
NotImplementedError
sage: class VexillaryInvolutions(CombinatorialClass):
....: def __init__(self, n):
....: """
....: The combinatorial class of vexillary involutions
....: """
....: self._n = n
....:
....: def __iter__(self):
....: P = Permutations(self._n, avoiding=[2,1,4,3]).filter(lambda p : p == p.inverse())
....: for p in P:
....: yield p
....:
....: def __repr__(self):
....: return "Vexillary involutions of %s" % self._n
....:
....: def __contains__(self, p):
....: p = Permutation(p)
....: return len(p) == self._n and p.avoids([2,1,4,3]) and p == p.inverse()
sage: V = VexillaryInvolutions(4)
sage: V
Vexillary involutions of 4
sage: [2,1,3,4] in V
True
sage: [2,1,4,3] in V
False
(Inspired by Project Euler Problem 201)
For any set \(A\) of numbers, let \(\sigma(A)\) be the sum of the elements of \(A\).
Consider the set \(B = \{ 1, 2, 3, 4 \}\). There are 6 subsets of \(B\) of size \(2\):
And the sums of the numbers in these subsets are
Some of these sums occur more than once, others are unique. The set of unique sums is \(\{3, 4, 6, 7\}\), and the sum of this set is \(\sigma(\{3, 4, 6, 7\}) = 20\).
Exercises
How many subsets of \(\{1,2,3,4,5,6,7,8\}\) are there containing exactly 3 elements? (Hint: Use the
Subsets
command.)sage: # edit here
Use the
union
method to construct the subsets of \(\{1,2,3,4,5,6,7,8\}\) that contain 3 or 5 elements. What is its cardinality?sage: # edit here
List all the subsets of \(\{1,3,6,8,10,11\}\) of size three.
sage: # edit here
Determine the sum of all integers that are the sum of exactly one of the 3-element subsets of \(\{1,3,6,8,10,11\}\).
sage: # edit here
How many 12-element subsets of \(\{1^2, 2^2, \dots, 24^2\}\) are there?
sage: # edit here
Determine the sum of all integers that are the sum of exactly one of the 12-element subsets of \(\{1^2, 2^2, \dots, 24^2\}\).
sage: # edit here
Remark. The Project Euler Problem 201 is to determine the sum of all the integers that are the sum of exactly one of the 50-element subsets of \(\{1^2, 2^2, \dots, 100^2\}\), and to do this in under two minutes of computation time!
A fixed point of a permutation \(p\) is an element \(i\) such that \(p(i) = i\). A derangement is a permutation that has no fixed points.
Define a function called
is_derangement
that returnsTrue
if \(p\) is a derangement and returnsFalse
otherwise.sage: # edit here
Use the
filter
method ofPermutations
to create a combinatorial class of all the derangements of[1,2,3,4]
, and list them.sage: # edit here
Create a list of the number of derangements of an \(n\)-element set, for \(n = 1, 2, \dots, 7\).
sage: # edit here
Visit the The On-Line Encyclopedia of Integer Sequences webpage to find a fomula for the number of derangements of an \(n\)-element set, and implement the function.
sage: # edit here
Warning
Combinatorial classes are now deprecated, and will disappear as soon as all derived classes in Sage’s library will have been fixed.
Using the VexillaryInvolutions
class above as a guide, create a class called Derangements
that inherits from CombinatorialClass
and implement the following methods.
__init__(self, n):
this method will take as argument a positive integern
, and it will store the value in a data attribute for later access.__repr__(self):
a string representation of the object. The commandDerangements(5)
should print ‘Derangements of a 5-element set’.__iter__(self):
implement a generator that iterates through all the derangements. ( Hint: In the exercise above you used thefilter
method to construct derangements; it is okay to use that here.)__contains__(self, p):
implement a method that tests whetherp
belongs to this combinatorial class (tests whether p is a derangement).cardinality(self):
implement the method cardinality that returns the number of derangements. ( Hint: You should have already implemented the function in the previous exercise.) ( Remark: by default, this method iterates through the iterator to get the cardinality, which can be slow if the class contains a lot of elements.)
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Cython is a programming language specially designed for writing Python extension modules. It’s designed to bridge the gap between the nice, high-level, easy-to-use world of Python and the messy, low-level world of C.
Consider the following Python function that outputs a list of the first m
prime numbers:
sage: def first_primes_python(m):
....: primes_list = []
....: n = 2
....: while len(primes_list) < m:
....: n_is_prime = True
....: for p in primes_list:
....: if n % p == 0:
....: n_is_prime = False
....: break
....: if n_is_prime == True:
....: primes_list.append(n)
....: n = n + 1
....: return primes_list
To time a function in Python, use the time
command:
sage: time p = first_primes_python(5000)
Time: CPU 6.20 s, Wall: 6.64 s
sage: p[:100]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541]
To Cythonize a function, just add %cython
as the first line in the notebook cell.
The Sage notebook will take the contents of this cell, convert it to Cython, compile it, and load the resulting function:
sage: %cython
sage: def first_primes_cython_v1(m):
....: primes_list = []
....: n = 2
....: while len(primes_list) < m:
....: n_is_prime = True
....: for p in primes_list:
....: if n % p == 0:
....: n_is_prime = False
....: break
....: if n_is_prime == True:
....: primes_list.append(n)
....: n = n + 1
....: return primes_list
Note the speed up we obtained by just adding %cython
:
sage: time p = first_primes_cython_v1(5000)
Time: CPU 0.88 s, Wall: 0.91 s
sage: time p = first_primes_cython_v1(10000)
Time: CPU 3.23 s, Wall: 3.45 s
Note that two links were returned above. The first one is a link to the C source code file created by Cython from our function. Go take a look. The conversion is a nontrivial process.
The second link above is an html page that identifies Python-to-C and C-to-Python conversions that are taking place. By minimizing such conversions and declaring data types, we can further improve the speed of our function.
Below, some object type declarations are made, we simplify some of the
loops and we use a C array instead of the Python list primes_list
. But since we want to return the data as a Python list, we convert to
a Python list at the end.
sage: %cython
sage: def first_primes_v3(int m):
....: cdef int k = 0
....: cdef int n = 2
....: cdef int i, n_is_prime
....: cdef int c_array[100000]
....: while k < m:
....: n_is_prime = 0
....: i = 0
....: while i < k:
....: if n % c_array[i] == 0:
....: n_is_prime = 1
....: break
....: i = i + 1
....: if n_is_prime == 0:
....: c_array[k] = n
....: k = k+1
....: n = n + 1
....: primes_list = []
....: i = 0
....: while i < k:
....: primes_list.append(c_array[i])
....: i = i+1
....: return primes_list
sage: time p = first_primes_v3(10000)
Time: CPU 0.22 s, Wall: 0.23 s
We did not screw up anything, this function actually does produce primes:
sage: first_primes_v3(17)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59]
And it agrees with the Sage version of the function:
sage: first_primes_v3(10000) == primes_first_n(10000)
True
But the Sage version is much, much better:
sage: time p = primes_first_n(10000)
Time: CPU 0.00 s, Wall: 0.00 s
sage: primes_first_n??
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
École CIMPA Bobo 2012: Mathématiques discrètes, Aspects Combinatoire, Dynamique et Algorithmique¶
- Photos
- tutoriels.zip: archive des principaux tutoriels, à charger dans le bloc-note (
Home
->Upload
).
Mardi 30 octobre:¶
Cours: Introduction à Sage
Mercredi 31 octobre:¶
Travaux Pratiques (1h30):
- Tutorial: Using the Sage notebook, navigating the help system, first exercises
- Chapitre «premiers pas» du livre «Calcul Mathématique avec Sage»
- Explorer d’autres chapitres, par exemple «graphiques», «théorie des graphes», …
Jeudi 1 novembre:¶
Travaux Pratiques (3h):
Vendredi 2 novembre:¶
Travaux Pratiques (1h30):
Jeudi 8 novembre:¶
Sage et LaTeX
Notes¶
Configuration du clavier français pour Sage 5.1 sous Windows avec VirtualBox:
Basculer en console texte:
CTRL-ALT-F1
Se connecter:
login: sage
password: sage
Devenir utilisateur gui
:
sudo su - gui
Éditer le fichier xinitrc:
vi .xinitrc
Et y ajouter la ligne suivante, avant la dernière ligne qui lance google-chrome:
setxkbmap fr
(rappels vi: \(i\) pour insérer, \(:wq\) pour sauver et quitter)
Redémarer:
reboot
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Note
Indications de difficulté des exercices
(*) Requiers un peu de programmation (fonctions, …)
(**) Plus difficile
(projet)
Début 1906
Morse (1920-1930): systèmes dynamiques
Gros développements: 1960
- École Française (Schützenberger)
- École Russe
Exercice: Mots finis
Construire le mot “adacbdafea”:
sage: # edit here
Indication: Consulter la documentation de la fonction
Word()
, ainsi que Demonstration: Combinatorics on wordsExplorer ses propriétés (est-il un palindrome?):
sage: # edit here
Indication: utiliser la complétion automatique
Exercice: Décimales de \(\pi\)
Construire le mot constitué des \(100\) premières décimales de \(pi\):
sage: # edit here
Indications:
numerical_approx()
et la méthode str.Compter ses facteurs de longueur \(2\):
sage: # edit here
Combien de décimales de \(\pi\) faut-il pour trouver tous les mots de longueur \(1\), \(2\), \(3\), … comme facteurs?
sage: # edit here
Comparer avec un mot aléatoire:
sage: # edit here
Exercice: Ensemble des mots
Lister tous les mots de longueur 6 sur l’alphabet “abc”:
sage: # edit here
Indication:
Words()
Lister tous les mots primitif de longueur \(6\):
sage: # edit here
Indication: Tutorial: Comprehensions, Iterators, and Iterables.
Trouver le plus petit mot tel que (.. TODO:: trouver une bonne propriété)
Définition
Un langage \(X\) est un sous ensemble de \(A^*\)
Définition
Un langage \(X\) est un code si tout mot \(w\in A^*\) admet au plus une factorisation sur \(X\).
Exemple
\(X = \{a,ba\}\)
Exemple: codes préfixes
\(X\) est un code préfixe si \(x\in X\) préfixe de \(y\) implique que \(x=y\)
Exercice
Factoriser le mot \(aaaababaaba\) sur le code \(X=\{a,ba\}\):
sage: # edit here
(*) Implanter une fonction
factor_a_ba(w)
qui renvoie la factorisation d’un mot sur le code \(X=\{a,ba\}\):sage: # edit here
(*) Implanter une fonction
factor_prefixe(w,X)
qui renvoie la factorisation d’un mot sur un code préfixe:sage: # edit here
(**) Implanter une fonction
factor(w, X)
qui renvoie la factorisation d’un mot \(w\) sur un code \(X\) quelconque:sage: # edit here
Déterminer la complexité des algorithmes sous-jacents.
Exercice
(*) Implanter une fonction
est_code_prefixe(X)
qui teste si un langage fini \(X\) est un code préfixe:sage: # edit here
(projet) Intégrer dans Sage des fonctionnalités autour de la factorisation sur les codes? Ou vaut-il mieux attendre que l’on ait des automates?
Définition
\(f:A^*\mapsto B^*\) est un morphisme si \(f(uv)=f(u)f(v)\)
Exercice
Montrer que \(f(\epsilon) = \epsilon\).
Indication: on pourra utiliser que \(A^*\) est simplifiable à gauche et à droite: si \(ux=uy\) ou \(xu=yu\), alors \(x=y\).
Exercice
Construire dans Sage les morphismes:
\(f: a\mapsto aba, b\mapsto cb, c\mapsto aba\):
sage: # edit here
\(g: a\mapsto ab, b\mapsto ba, c\mapsto a\):
sage: # edit here
Indication: consulter la documentation de WordMorphism()
Quelle est l’image de “acbccacbacaabcab” par ces morphismes?
sage: # edit here
Exercice: puissances itérées
Construire un mot et un morphisme de votre choix:
sage: # edit here
Quelle est la longueur de \(f^{10}(w)\)?
sage: # edit here
Tracer la courbe de la fonction \(n\mapsto l(n)\) où \(l(n)\) est la longueur de \(f^n(w)\):
sage: # edit here
Remarquer que la longueur de \(f^n(w)\) ne dépend pas de l’ordre des lettres de \(w\). Utiliser ce fait pour ramener le calcul de la longueur de \(f^n(w)\) au calcul d’une puissance de matrice (abélianisation):
sage: # edit here
Évaluer la complexité du calcul de \(l(n)\) par cet algorithme.
Utiliser ce fait pour retracer la courbe en échelle logarithmique pour \(n\) aussi grand que possible:
sage: # edit here
Explorer d’autres mots et d’autres morphismes et étudier comment la complexité évolue:
sage: # edit here
Proposition
Un morphisme \(f: A^*\mapsto B\) est injectif si et seulement si:
- \(f\) restreint à l’alphabet \(A\) est injectif
- \(f(A)\) est un code
Exercice
(*) Implanter une fonction
est_injective(f)
qui calcule si le morphisme \(f\) est injectif:sage: # edit here
(*) Implanter une fonction
preimage(f,w)
qui calcule la préimage d’un mot \(f\) par une fonction \(f\) injective:sage: # edit here
(projet) Intégrer ces méthodes dans Sage
Théorème du défaut
Deux formulations:
Soit \(X\subset A^*\) fini. Si \(X\) n’est pas un code, alors il existe \(Y\subset A^*\) tel que \(|Y|<|X|\) et tout mot de \(X\) se factorise sur \(Y\).
Par récurrence, on peut supposer sans perte de généralité que \(Y\) est un code.
Soit \(f: A^* dans B^*\) un morphisme, alors il existe un alphabet \(A'\), \(g:A'^*\mapsto B^*\) et \(g:A^*\mapsto A'^*\) tel que \(|A'|<|A|\) et \(f=g\circ h\).
Note
Manipuler un ensemble \(X\) de mots fini ou un morphisme est équivalent: un morphisme est juste une manière de nommer chacun des éléments de \(X\), ce qui est souvent pratique.
Note
Points clefs de la démonstration: faire une récurrence sur la somme des longueurs de mots dans l’image de \(f\). Cas de base: \(f\) est effaçante (il existe \(a\) tel que \(f(a)=\epsilon\)). Sinon la non injectivité force l’existence de \(a\) et \(b\) tels que \(f(a)\) est un préfixe de \(f(b)\) que l’on utilise pour appliquer la récurrence.
Corollaire
Soient \(x\) et \(y\) deux mots non vides. Alors les énoncés suivants sont équivalents:
- \(x\) et \(y\) commutent
- Il existe \(n,m>0\) tels que \(x^m=y^n\)
- Il existe \(z\in A^+\) tels que \(x,y\in z^*\)
- \(\{x,y\}\) n’est pas un code ou \(x=y\)
Il existe toute une littérature sur les équations sur les mots.
Définition
Un mot \(w\in A^+\) est primitif s’il n’est pas la puissance d’un mot plus petit.
Proposition
Soit \(w\in A^+\). Il existe un unique \(z\in A^+\), appelé racine primitive de \(w\), tel que \(z\) est primitif et \(w\in z^*\).
Proposition
Soit \(w\in A^+\). Alors le commutant de \(w\) est donné par \(C(w)=z^*\) où \(z\) est la racine primitive de \(w\).
Définition: période
Soit \(w\in A^*\) et \(x\in A^+\). Alors \(x\) est une période de \(w\) si il existe \(n\in \NN^*\) tel que \(w\) est un préfixe de \(x^n\).
Exemple::
sage: w = Word("abaabaa")
sage: w.periods()
[3, 6]
On note que Sage, comme pas mal de chercheurs, appellent période la longueur du mot \(x\) et non le mot lui-même. Voici les mots correspondants:
sage: w[:3]
word: aba
sage: w[:6]
word: abaaba
On peut avoir directement toutes les périodes comme mots:
sage: [w[:i] for i in w.periods()]
[word: aba, word: abaaba]
Warning
Sage ne considère pas \(w\) comme une période de lui-même!?!
Un autre exemple montrant qu’il n’y a pas forcément une unique période primitive:
sage: w = Word("aaabaaaa")
sage: sage: sage: w.periods()
[5, 6, 7]
sage: [w[:i] for i in w.periods()]
[word: aaaba, word: aaabaa, word: aaabaaa]
Proposition
Soit \(x\in A^+\) et \(w\in A^*\). Les énoncés suivants sont équivalents:
- \(x\) est une période de \(w\);
- \(w\) est un préfixe de \(xw\);
- \(w\) est un préfixe de \(x^nw\) pour tout \(n\).
Théorème (Fine et Wilf, 1965)
Soit \(w\in A^+\) et \(x\) et \(y\) deux périodes distinctes de \(w\) telles que \(|w|\geq |x|+|y|-pgcd(|x|,|y|)\), alors \(x\) et \(y\) ont la même racine primitive.
Exercice:
Montrer que le théorème de Fine & Wilf est optimal, c’est-à-dire que, pour tout \(p,q\) tel que \(pgcd(p,q)<p,q\), il existe un mot \(w\) de longueur \(p+q-pgcd(p,q)-1\) tel que \(w\) est \(p\)-périodique et \(q\)-périodique, mais pas \(pgcd(p,q)\)-périodique.
Indications: commencer par \(p,q\) premiers entre eux et utiliser l’algorithme d’Euclide (version soustractive)
Définition: topologie sur les mots infinis
Distance entre \(u\) et \(v\): \(2^{-k}\) où \(k\) est la position où \(u\) et \(v\) diffèrent.
Lemme de König
Soit \(X\subset A^*\) infini. Alors l’adhérence de \(X\) (dans \(A^\infty\)) contient un mot infini \(w\). Autrement dit une infinité de préfixes de \(w\) sont dans \(X\).
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Définition
Un système dynamique est donné par un ensemble \(X\), et une fonction \(T\) de \(X\) sur \(X\) que l’on itère.
L’orbite d’un point \(m\) de \(X\) est la suite \((T^n(m))_{n\in\NN}\).
Un point \(m\) est périodique si son orbite l’est (ou si elle est ultimement périodique?)
Questions typiques:
- Répartition d’une orbite dans \(X\)
- Existence de points périodiques?
Cas particuliers:
topologique: \(X\) compact, \(T\): continu
mesurable: \(X\) équipé d’une mesure \(\mu\) préservée par \(T\):
\(\mu (T^{-1}(A)) = \mu(A)\) pour tout \(A\) mesurable
Questions typiques:
- La suite … est-elle dense dans \(X\)?
Définition
\(T\) est une isométrie par morceaux si \(X=\RR^k\) et si \(T\) est une isométrie au voisinage de tout point \(m\)
Exemple
\(T: \RR^2 \mapsto \RR^2\) tel que \(T(z)\) vaut:
- \(i(z-z_0)+z_0-1\) si \(-1<Im(z)<0, Re(z)<0\)
- \(z-i+1\) si \(Im(z)<-1, Re(z)<0\)
- \(z\) si \(Im(z)>0\)
On s’intéresse principalement au cadrant sud-ouest.
On ignore les points où \(T\) est non définie (mesure nulle)
Todo
figure
Supposons que \(T\) est définie sur un nombre fini de morceaux \((P_i)_{i=1,\dots,k}\). Notons \(A\) l’alphabet \(\{1,\dots,k\}\).
Codage de \(T\): \(\Phi:\quad X\mapsto A^*,\quad m \mapsto (u_n)_{n\in \NN}\) telle que \(u_n=i\) si et seulement si \(T^n(m) \in P_i\).
Avec l’exemple précédent de \(\Phi(z_1)=1,2,1,2,1,2\).
Objectif: décrire la dynamique en étudiant les mots qui codent l’ensemble.
On a un diagramme commutatif: \(S\circ \Phi = \Phi\circ T\), où \(S\) est le shift sur les mots.
Définition
Le langage de l’isométrie est l’ensemble des facteurs finis des codes de points de \(X\).
Exercice
Implanter la fonction \(T\) de l’exemple précédent dans Sage.
Implanter une fonction
morceau(m)
qui renvoie le morceau \(P_i\) où se trouve le point \(m\) (plus précisément l’indice \(i\) de ce morceau).Implanter une fonction
code(T,m,n)
qui renvoie le préfixe de longueur \(n\) du code de \(m\) par \(T\).Implanter une fonction
code(T,m)
qui renvoie le code de \(m\) par \(T\).Indication: on construira un mot infini avec
Word()
, en passant en paramètre:- Soit une fonction
f(T,m,n)
qui renvoie le morceau \(P_i\) où se trouve \(T^m(n)\) (un peu plus simple). - Soit un itérateur qui renvoie successivement le morceau \(P_i\) où se trouve \(T^m(n)\), pour \(n=0,1,2,...\) (plus efficace; pourquoi?).
- Soit une fonction
Implanter une fonction
dessine_trajectoire(T,m,n)
qui dessine les \(n\) premiers points de l’orbite de \(m\) sous \(T\).Indication: voir
point()
Rajouter les frontières des morceaux \(P_i\).
Implanter un
interact
avec deux curseurs continus (sliders) pour \(x\) et \(y\) qui dessine l’orbite du point \(m\) de coordonnées \(x\) et \(y\), tout en affichant le code de \(m\).
Proposition
\(112\) n’appartient pas au langage.
Comment traiter systématiquement ce type de questions?
Définition
La cellule de \(v=v_0,\dots,v_{n-1}\) est l’ensemble des points de \(X\) dont le code commence par \(v_0,\dots,v_{n-1}\):
Exemple
La cellule de \((1,1)\) est le carré \([-1,0]^2\).
La cellule de \((1,1,2)\) est vide: on calcule l’image par \(T^2\) de \(\sigma_{1,1}\) et on constate qu’elle n’appartient pas à \(P_2\).
Exercice
Implanter une fonction cellule(T,v)
qui calcule la cellule du
mot \(v\) sous \(T\).
Implanter l’application réciproque de \(T\).
Proposition
Soit \(L_T\) le langage d’une isométrie par morceaux, et \(L_n\) l’ensemble des mots de longueurs \(n\) de \(L_T\).
Alors \(X\) est la réunion des \(\sigma_v\) pour \(v\) dans \(L_n\).
Définition: Application de premier retour
Soit \(Y\) un sous-ensemble de \(X\).
Soit \(x\in Y\); le temps de premier retour de \(x\) est le plus petit \(M_x:=k>0\) tel que \(T^{k(x)}\in Y\).
L’application de premier retour sur \(Y\) est la fonction de \(Y\) dans \(Y\) définie par \(T_Y(x) := T^{M_x}(x)\).
Note
Si \(X\) est compact, \(T_Y\) est défini via le théorème de récurrence de Poincaré (l’ensemble des éléments de temps de retour nul est de mesure nulle).
Les applications \(T\) et \(T_Y\) sont conjugées par une bijection \(h\) qui
Todo
finir la phrase …
Il existe une renormalisation pour \(T\). C’est par cette renormalisation que l’on peut décrire complètement le langage de \(T\) et \(T_Y\).
Exercice: échange d’intervales
On coupe l’intervalle \([0,1]\) en trois intervalles consécutifs \(A,B,C\) (par exemple en coupant en \(3/5\) et \(4/5\).
On défini l’application \(S:[0,1]\mapsto [0,1]\) qui échange les intervalle \(A,B,C\) en les translatant chacun de sorte que leurs images soient dans l’ordre \(S(C),S(B),S(A)\).
- Dessiner la partition par les cellules des mots de longueur \(2\) puis \(3\) puis \(4\).
- Décrire l’orbite d’un point.
Exercice
On considère l’isométrie par morceaux définie par la rotation par morceaux d’angle \(\pi/4\):
- Calculer l’orbite d’un point sous \(T\).
- Dessiner la partition associée à l’application \(T^n\).
- Coder l’orbite d’un point sous cette application en codant par
\(0\) sur le demi plan supérieur et \(1\) sur le demi plan
inférieur.
- Induire l’application sur un cône d’angle \(\pi/4\) centré en - 1.
- Recommencer avec \(\pi/7\).
Exercice
Soit \(A=[0,1]^2\) et \(B=[1,1+a]*[0,1]\), où \(a\) est un paramètre réel positif. On considère l’application définie sur \(A\cup B\) par
Pour \(a\) rationnel, décrire la partition à l’étape \(n\).
Comprendre la dynamique dans ce cas.
Étudier le cas \(a=\frac{\sqrt{2}}{2}\).
Indication: Induire sur un rectangle bien choisi.
Exercice: dynamique en dimension \(1\)
On considère la rotation d’angle \(\frac{\sqrt{2}}{2}\) donnée par \(x\mapsto x+\frac{\sqrt{2}}{2}\quad mod 1\).
- Écrire une fonction qui dessine l’orbite d’un point.
- Écrire une fonction qui donne le codage des \(n\) premiers termes de l’orbite d’un point.
- Comparer les orbites de deux points différents.
Définition
La complexité d’une isométrie par morceaux est la complexité du langage associé.
Note
Remarques
- Lorsque \(n\) augmente, le découpage en cellules est de plus en plus fin.
- Soit \(v\) un mot du langage de longueur \(n\). Il se prolonge en \(k\) mots, où \(k\) est le nombre de régions de \(T\) intersectant non trivialement (intérieur non vide) l’image \(T^n(\sigma_v)\) de la cellule de \(v\).
- En particulier, si l’intérieur de l’image d’une cellule ne contient aucune frontière de régions de \(T\), alors le mot correspondant se prolonge de manière unique dans le langage.
- Si c’est le cas pour toutes les cellules, alors \(T\) agit par permutation des images des cellules, et la complexité pour \(n+1\) est exactement celle pour \(n\).
Définition
On considère un intervalle compact découpé en un nombre fini d’intervalles. Un échange d’intervalles est une bijection de cet intervalle dans lui même dont la restriction à chaque sous intervalle est une translation.
Exercice: première rotation
On considère l’intervalle \([0,1]\) découpé en deux sous intervalles au point \(4/5\).
- Écrire une fonction qui donne l’application de premier retour sur un sous intervalle.
- Appliquer avec les intervalles \([0,1/2]\) puis \([0,4/5]\).
Exemple: rotations
Considérons un échange d’intervalle \(T\) avec deux intervales \(]0,\alpha[\) et \(]\alpha,1[\). On l’appelle rotation d’angle \(\alpha\) (identifier \([0,1]\) avec le cercle unité).
La complexité est alors:
Bornée si \(\alpha\) est rationnel: à chaque étape, l’image d’au plus une cellule sera coupée en deux par la frontière \(\alpha\); si à une étape aucune cellule n’est coupée, alors \(T\) agit par permutation des cellules au cran suivant, et donc à tous les crans suivants.
Le langage est alors le langage d’un mot périodique.
\(n+1\) si \(\alpha\) est irrationnel: l’image d’exactement une cellule sera coupée en deux par la frontière \(\alpha\).
Le langage est alors le langage d’un mot Sturmien .
Remarque
Une rotation est IDOC si et seulement si \(\alpha\) est irrationnel.
Théorème
Pour un échange de \(l\) intervalles IDOC, la complexité de l’échange d’intervalles vaut \(p(n)= (l-1) n+1\).
Définition
Un système dynamique est dit minimal si tout point a une orbite dense.
Exercice
On considère un échange de trois intervalles de permutation \(\begin{pmatrix}1&2&3\\3&2&1\end{pmatrix}\) et de longueurs \((\frac{\sqrt{2}}{10},\frac{\sqrt{2}}{5}, 1-\frac{3\sqrt{2}}{10})\).
- Implanter un interact qui permet de tracer les \(n\) premiers points de l’orbite d’un point sous cet échange d’intervalles.
- Cet échange est il minimal ?
Exemple rotation de paramètre \(\alpha=2/3\)
L’application de premier retour induite sur l’intervalle \([0,2/3]\) est une rotation. Elle est bien induite, car on reste dans la classe des rotations.
Par contre, si on prend l’intervalle \([0,2/3]\). C’est mauvais car on sort de la classe des rotations.
Principe: on se donne une classe de systèmes dynamiques; les bons intervales sont ceux pour lesquels l’induction reste dans la classe.
Exercice
On considère à nouveau l’échange de trois intervalles de permutation \(\begin{pmatrix}1&2&3\\3&2&1\end{pmatrix}\) et de longueurs \((\frac{\sqrt{2}}{10},\frac{\sqrt{2}}{5}, 1-\frac{3\sqrt{2}}{10})\).
- Implanter l’application de premier retour sur l’intervalle \([0,1-\frac{3\sqrt{2}}{10}\).
- Recommencer avec l’intervalle \([0,\frac{3\sqrt{2}}{10}\).
- Quelle est la meilleure des deux inductions?
C’est un moyen de décrire le langage d’un système dynamique.
Définition
Graphe de Rauzy d’ordre \(n\) d’un échange d’intervalle:
- Sommets: tous les mots de longueur \(n\)
- Arêtes: \(u\rightarrow v\) si \(u\) se prolonge en un mot de suffixe \(v\). Autrement dit, il existe \(x\) et \(y\) tels que \(ux = yv\).
Remarques
- C’est l’analogue du graphe de DeBruijn pour les mots de longueur \(n\) de \(A^*\).
- Le nombre d’arêtes au cran \(n\) donne le nombre de sommets au cran \(n+\).
- La complexité est bornée si et seulement si le graphe de Rauzy est constant à partir d’un certain rang. Il est alors composé d’une union de cycles; c’est le graphe de la permutation des (images) des cellules induite par \(T\).
Exercice
On considère la rotation d’angle \(\\frac{\sqrt{2}}{2}\).
- Implanter le codage d’un point sous l’action de la rotation.
- Vérifier que cette rotation est minimale.
- Tracer les graphes de Rauzy correspondant aux mots de longueur \(1,2,3,4\).
Exercice
Implanter un “interact” permettant de jouer au billard sur un polygone convexe (par exemple un rectangle) en choisisant un angle de tir et en affichant la trajectoire, le mot, …
Définition.
On considère un polygone convexe du plan muni d’une orientation. Le billard dual est une isométrie par morceaux bijective définie en dehors du polygone qui est localement une symétrie centrale par rapport à un sommet. A partir d’un point on choisit le sommet le plus proche suivant l’orientation.
Exercice
- Implanter le billard dual autour du carré.
- Dessiner la partition associée aux cellules des mots de longueur \(n\).
- Recommencer pour un polygone régulier à \(5,6,7\) sommets.
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Computational sessions at the CRM thematic semester: Algebra and Words in Combinatorics¶
Computer exploration has proven to be an effective tool while carrying research in algebra and combinatorics. To support this, researchers around the world have implemented many features and shared them in systems like GAP, Magma, or SageMath. Quite a few of those researchers are participating to the CRM semester on Algebra and Words in Combinatorics and happy to share their expertise. To this end we are running regular informal computational sessions during the schools and workshops.
There are no prerequisites to attend the sessions. If possible, bring your laptop. This page contains some material for those sessions.
Schools and workshops¶
Software¶
Sagemath and GAP are open source, and can be used online. For regular use, we recommend installing them on your computer and are happy to give a hand. Magma can be used online for small calculations. Installing it require a license.
- The SageMath cell server for simple calculations
- Cocalc: a full featured computational environment with Sage, GAP and many other software
- The Magma calculator
- CHAMP: A CHerednik Algebra Magma Package
References¶
- The official SageMath thematic tutorials.
- More Sage Thematic Tutorials
- Lie Methods and Related Combinatorics in Sage; see in particular the chapter on Coxeter Groups
- Root Systems,
CoxeterGroup()
,ReflectionGroup()
, … - Lie algebras in GAP
[1]:
1+1
[1]:
2
[3]:
10/6
[3]:
5/3
[4]:
factorial(10000)
[4]:
28462596809170545189064132121198688901480514017027992307941799942744113400037644437729907867577847758158840621423175288300423399401535187390524211613827161748198241998275924182892597878981242531205946599625986706560161572036032397926328736717055741975962099479720346153698119897092611277500484198845410475544642442136573303076703628825803548967461117097369578603670191071512730587281041158640561281165385325968425825995584688146430425589836649317059251717204276597407446133400054194052462303436869154059404066227828248371512038322178644627183822923899638992827221879702459387693803094627332292570555459690027875282242544348021127559019169425429028916907219097083690539873747452483372899521802363282741217040268086769210451555840567172555372015852132829034279989818449313610640381489304499621599999359670892980190336998484404665419236258424947163178961192041233108268651071354516845540936033009607210346944377982349430780626069422302681885227592057029230843126188497606560742586279448827155956831533440534425446648416894580425709461673613187605234982286326452921529423479870603344290737158688499178932580691483168854251956006172372636323974420786924642956012306288720122652952964091508301336630982733806353972901506581822574295475894399765113865541208125788683704239208764484761569001264889271590706306409661628038784044485191643790807186112370622133415415065991843875961023926713276546986163657706626438638029848051952769536195259240930908614471907390768585755934786981720734372093104825475628567777694081564074962275254993384112809289637516990219870492405617531786346939798024619737079041868329931016554150742308393176878366923694849025999607729684293977427536263119825416681531891763234839190821000147178932184227805135181734921901146246875769835373441456013122615221391178759688367364087207937002992038279198038702372078039140312368997608152840306051116709484722224870389199993442071395836983063962232079115624044250808919914319837120445598344047556759489212101498152454543594285414390843564419984224855478532163624030098442855331829253154206551237079705816393460296247697010388742206441536626733715428700789122749340684336442889847100840641600093623935261248037975293343928764398316390312776450722479267851700826669598389526150759007349215197592659192708873202594066382118801988854748266048342256457705743973122259700671936061763513579529821794290797705327283267501488024443528681645026165662837546519006171873442260438919298506071515390031106684727360135816706437861756757439184376479658136100599638689552334648781746143243573224864326798481981458432703035895508420534788493364582482592033288089025782388233265770205248970937047210214248413342465268206806732314214483854074182139621846870108359582946965235632764870475718351616879235068366271743711915723361143070121120767608697851559721846485985918643641716850899625516820910793570231118518174775010804622585521314764897490660752877082897667514951009682329689732000622392888056658036140311285465929084078033974900664953205873164948093883816198658850827382468034897864757116679890423568018303504133875731972630897909435710687797301633918087868474943633533893373586906405848417828065196275826434429258058422212947649402948622670761832988229004072390403733168207417413251656688443079339447019208905620788387585342512820957359307018197708340163817638278562539516825426644614941044711579533262372815468794080423718587423026200264221822694188626212107297776657401018376182280136857586442185863011539843712299107010094061929413223202773193959467006713695377097897778118288242442920864816134179562017471831609687661043140497958198236445807368209404022211181530051433387076607063149616107771117448059552764348333385744040212757031851527298377435921878558552795591028664457917362007221858143309977294778923720717942857756271300923982397921957581197264742642878266682353915687857271620146192244266266708400765665625807109474398740110772811669918806268726626565583345665007890309050656074633078027158530817691223772813510584527326591626219647620571434880215630815259005343721141000303039242866457207328473481712034168186328968865048287367933398443971236735084527340196309427697652684170174990756947982757825835229994315633322107439131550124459005324702680312912392297979030417587823398622373535054642646913502503951009239286585108682088070662734733200354995720397086488066040929854607006339409885836349865466136727880748764700702458790118046518296111277090609016152022111461543158317669957060974618085359390400067892878548827850938637353703904049412684618991272871562655001270833039950257879931705431882752659225814948950746639976007316927310831735883056612614782997663188070063044632429112260691931278881566221591523270457695867512821990938942686601963904489718918597472925310322480210543841044325828472830584297804162405108110326914001900568784396341502696521048920272140232160234898588827371428695339681755106287470907473718188014223487248498558198439094651708364368994306189650243288353279667190184527620551085707626204244509623323204744707831190434499351442625501701771017379551124746159471731862701565571266295855125077711738338208419705893367323724453280456537178514960308802580284067847809414641838659226652806867978843250660537943046250287105104929347267471267499892634627358167146935060495110340755404658170393481046758485625967767959768299409334026387269378365320912287718077451152622642548771835461108886360843272806227776643097283879056728618036048633464893371439415250259459652501520959536157977135595794965729775650902694428088479761276664847003619648906043761934694270444070215317943583831051404915462608728486678750541674146731648999356381312866931427616863537305634586626957894568275065810235950814888778955073939365341937365700848318504475682215444067599203138077073539978036339267334549549296668759922530893898086430606532961793164029612492673080638031873912596151131890359351266480818568366770286537742390746582390910955517179770580797789289752490230737801753142680363914244720257728891784950078117889336629750436804214668197824272980697579391742229456683185815676816288797870624531246651727622758295493421483658868919299587402095696000243560305289829866386892076992834030549710266514322306125231915131843876903823706205399206933943716880466429711476743564486375026847698148853105354063328845062012173302630676481322931561043551941761050712449024873277273112091945865137493190965162497691657553812198566432207978666300398938660238607357858114394715872800893374165033792965832618436073133327526023605115524227228447251463863269369763762510196714380125691227784428426999440829152215904694437282498658085205186576292992775508833128672638418713277780874446643875352644733562441139447628780974650683952982108174967958836452273344694873793471790710064978236466016680572034297929207446822322848665839522211446859572858403863377278030227591530497865873919513650246274195899088374387331594287372029770620207120213038572175933211162413330422773742416353553587977065309647685886077301432778290328894795818404378858567772932094476778669357537460048142376741194182671636870481056911156215614357516290527351224350080604653668917458196549482608612260750293062761478813268955280736149022525819682815051033318132129659664958159030421238775645990973296728066683849166257949747922905361845563741034791430771561168650484292490281102992529678735298767829269040788778480262479222750735948405817439086251877946890045942060168605142772244486272469911146200149880662723538837809380628544384763053235070132028029488392008132135446450056134987017834271106158177289819290656498688081045562233703067254251277277330283498433595772575956224703707793387146593033088629699440318332665797514676502717346298883777397848218700718026741265997158728035440478432478674907127921672898523588486943546692255101337606377915164597254257116968477339951158998349081888281263984400505546210066988792614558214565319696909827253934515760408613476258778165867294410775358824162315779082538054746933540582469717674324523451498483027170396543887737637358191736582454273347490424262946011299881916563713847111849156915054768140411749801454265712394204425441028075806001388198650613759288539038922644322947990286482840099598675963580999112695367601527173086852756572147583507122298296529564917835071750835741362282545055620270969417476799259229774888627411314587676147531456895328093117052696486410187407673296986649236437382565475022816471926815559883196629848307776666840622314315884384910519058281816740764463033300119710293036455866594651869074475250837841987622990415911793682799760654186088721626654886492344391030923256910633775969739051781122764668486791736049404393703339351900609387268397299246478483727274770977466693599784857120156789000241947269220974984127323147401549980920381459821416481176357147801554231599667838534854486406936410556913531335231184053581348940938191821898694825383960989942822027599339635206217705343572073396250574216769465101608495601439303244304271576099527308684609204422226103154229984444802110098161333824827375218998738205315164927134498105950159974800571591912202154487748750103473246190633941303030892399411985006225902184164409988173214324422108554248620896250260604398180189026317781146617454999771440665232863846363847001655618153861098188111181734191305505024860345856755585637511729774299329074944236579668332700918367338977347901759248885660379952771540569083017311723894140326159612292912225191095948743805673381278538616491842786938417556898047100859868372033615175158097022566275200160956192229925401759878522038545913771783976389811198485803291048751666921195104514896677761598249468727420663437593207852618922687285527671324883267794152912839165407968344190239094803676688707838011367042753971396201424784935196735301444404037823526674437556740883025225745273806209980451233188102729012042997989005423126217968135237758041162511459175993279134176507292826762236897291960528289675223521425234217247841869317397460411877634604625637135309801590617736758715336803958559054827361876112151384673432884325090045645358186681905108731791346215730339540580987172013844377099279532797675531099381365840403556795731894141976511436325526270639743146526348120032720096755667701926242585057770617893798231096986788448546659527327061670308918277206432551919393673591346037757083193180845929565158875244597601729455720505595085929175506510115665075521635142318153548176884196032085050871496270494017684183980582594038182593986461260275954247433376226256287153916069025098985070798660621732200163593938611475394561406635675718526617031471453516753007499213865207768523824884600623735896608054951652406480547295869918694358811197833680141488078321213457152360124065922208508912956907835370576734671667863780908811283450395784812212101117250718383359083886187574661201317298217131072944737656265172310694884425498369514147383892477742320940207831200807235326288053906266018186050424938788677872495503255424284226596271050692646071767467502337805671893450110737377034119346113374033865364675136733661394731550211457104671161445253324850197901083431641989998414045044901130163759520675715567509485243580269104077637210998671624254795385312852889930956570729218673523216666097874989635362610529821472569482799996220825775840988458484250391189447608729685184983976367918242266571167166580157914500811657192200233759765317495922397884982814705506190689275625210462185661305800255607974609726715033327032310025274640428755556546883765838802543227403507431684278620637697054791726484378174446361520570933228587284315690756255569305558818822603590006739339952504379887470935079276181116276309771257983975996526612120317495882059435754883862282508401408885720583992400971219212548074097752974278775912566026443482713647231849125180866278708626116699989634812405803684794587364820124653663228889011636572270887757736152003450102268890189101673572058661410011723664762657835396364297819011647056170279631922332294228739309233330748258937626198997596530084135383241125899639629445129082802023225498936627506499530838925632246794695960669046906686292645006219740121782899872979704859021775060092893328957272392019589994471945147360850770400725717439318148461909406269545285030526341000565022226152309364882887122046454267700577148994335147162504252365173710266068647253458120186683273953682547456536553597546685788700056988360286686450740256993087483441094086086303707908295240576731684941855810482475304758923392801571302824106234999945932390521409856559565661346003396150515164758852742214732517999548977992849522746029855666700811871200856155016457400484170210303038996339253337466556817824410737409336919294104632307731994759826307383499600770372410446285414648704116273895649834555162165685114551383822047005483996671706246467566101291382048909121117229386244253158913066987462045587244806052829378148302622164542280421757760762365459828223070815503469404938317755053305094698999476119419231280721807216964378433313606760676965187138394338772485493689061845700572043696666465080734495814495966306246698679832872586300064215220210171813917325275173672262621454945468506006334692713838311715849753092643252486960220059099802663765386225463265168414963306369548086551101256757717890616694758344043486218485369591602172030456183497524162039926441331651884768606830642004858557924473340290142588876403712518642229016333691585063273727199596362912783344786218887871009533753551054688980236378263714926913289564339440899470121452134572117715657591451734895195016800621353927175419843876163543479806920886666227099512371706241924914282576453125769939735341673046864585181979668232015693792684926999983992413571941496882273704022820805171808003400480615261792013978945186295290558440703738300533552421153903385185829366779190610116306233673144419202893857201855569596330833615450290424822309297087124788002017383072060482680156675397593789931793515799958929562156307338416294599900276730832827716595064217966523190439250543226753731811755315476780739470338931185107297724318378972674957455778183345495942317353558291046967315391275975687281861691161083156337232639968881490543943261197182274996791176628553401860198315809629981791107208804992292016062059067271273599461871634945774995805337947187105456452579396024210259136415528398395201773012712514892051061708228008339985665786646920737114269682301770416324829479409558694699089379165191006305185352102345189798127619143061864362703081977124992751056732909481202057747100687703379708934229207183903744167503493818836342229284946790660285674293251642569044363473087656797056595677285291081242733154406580199802711579126254172797452862574865921933293805915239524735518887119860391319654287576290190503964083560246277534314409155642181729459941596061979622633242715863425977947348682074802021538734729707999753332987785531053820162169791880380753006334350766147737135939362651905222242528141084747045295688647757913502160922040348449149950778743107189655725492651282693489515795075486172341394610365176616750329948642244039659511882264981315925080185126386635308622223491094629059317829408195640484702456538305432056506924422671863255307640761872086780391711356363501269525091291020496042823232628996502758951052844368177415730941874894428065427561430975828127698124936993313028946670560414084308942231140912722238148470364341019630413630736771060038159590829746410114421358321042574358350220737173219745089035573187350445827238770728271406162997919629357224104477155051652535867544109395079218369015261138440382680054150924346511711436477899444553993653667727589565713987505542990824585609510036934663100673714708029927656933435500927189854050109917474979991554392031908961967615444686048175400695689471463928245383807010444181045506171305160584355817521032338465829201071030061124283407458607006060194830551364867021020364708470807422704371893706965688795617928713045224516842027402021966415605280335061293558739079393524404092584248380607177444609964035221891022961909032569042381374492494906892314330884224399631396391545854065286326468807581148748371408284176455226386313520264894016262494802388568231599102952620337126449279901938211134518446387544516391239377974190576649911764237637722282802318465738050121277809680315691477264910257503508758792248110223544524410872448565700755187132146592093548504552829170749596775404450779494836371756062326925757412813110241910373338080434325310884694831555729402265394972913817581338619457057799561808755951413644907613109617155928376585840036489374076822257523935988731081689667688287403837192827690431514106997678303819085690713091931340846019511147482766350724676534922040058626677632935516631939622498979912708004465982264899125226813124300528104995058595676527123591494442612554437618645029202881358582871789577224116380815161831603129728796987480139828621645629196153096358337313619724773332353025466571196902611237380629030242904275794549030022660847446513161741691916851746464945459696005330885252792083472495235473110674109099223541055506299687642153951249355986311346661725116890785633328935569150449485189113488301876365100638502565916433021928565596263914382895068324838727165616560111531517055222955765944972454788815532316417453267167978861141165355597588331979638070962998880767303616940317736448140427867784251232449974693421348217179595190698204602997172001174857303889719205597414742453011135869766256607770970225633261701108463784795555258504578058879440756064974127974530918418405207558526462208821483646754652237609210787539190454684852349759986044943322828073120679922402477507514105890774627334319091255451352225329275913842047384603056163154236552935312278389759446515787337343463172280001031380425481404022090580405056003860937403435068863081434683848900708938565050027569059678069404698435184535134141031615133683043714786642925389717165978629010728400758939700388317742648163725113277369926827709465342583596111881955092462062153978121197244762623771534452048069819082524943963962251113831177428978535825590832490480497516047104257569753442551515779815600370847230603484753977513688390404316017486248871339311818523029425425676202485688393970836748788453789172574145155917919035398535077200900594979352939459631213445503368260690059828717723533375221941915547303742062343262892968397015058892191112049249864792053410872349115430987182160055762209075732304626106597744947658346313025598636315029959672352476943975462530206788193304372284800209305354155640664838569378144603138697563459200233462606995955513484754147891180830329816421587452922952678937925647752029052675349356673744293182673374571642465407748267901046778759085408130531447176455869894169668940436489952465247443988349583871206296485413357553813419500498743813369062703973874586604296871595820715766599826607317005624465541763024501349159567288942619746144496908671655859782729228702723774835097362901019130417812735773037781804081589136005207315806941034305003184349342360269244733060013861119781774472669608928321052543116496033420102032603863672532889648333405862204843616575362001468405476649666473566979572953394809138263703324220930839366954980688240491622063147911494642042500022450413425558561937442905257252436320054487441524307305215070491020434076572476865095751174125413729531644521765577235348601821566833352520532830000108344008762266843817023235605645158256954177359197813649975559601912567744942717986360045847405209290089397315276024304951653864431388147876977541478757432610159879709758855625806766197973098472460769484821127948427976536607055051639104415022554420329721292033009353356687294595912327965886376486894188433640548494009574965791657687213927330153555097865114767947399690623184878377515462613823651665956337209345708208301840482797005728071432925727577436229587047361641609731817241594204270366066404089740245521530725227388637241859646455223673260411164598464020010216920823315155388821071527191267876531795071908204525100447821291318544054814494151867114207103693891129125012750853466337717749376016543454696390042711129829255096830420665725364279472200020835313883708781649957189717629338794854271276882652003766325924561614868744897471519366219275665852462114457407010675380427564184440834805203838265052601698584060084788422421887856927897751810442805474427229455167420335686460609977973124950433321425205053675790499520783597650415379001132579536040655172654879022173595444151139429231648950663177813039057462082449171921311864129633704661406456900178942356738775523130952785912774533241855442484484493664210731348819180640189222317302156645813473186449997905781662091469870718039388885781280740226363602294114354869871402143572055947730892808653678920201935102605361567924483276749476117858316071865710310842200560259545115191391309119544447844361032741876102338843391687589233423790859841968266525610628751237572318491474951945985728897934981791761822652480408237128109790772638864286067917082288575852703470839714561619926247844794692794996845945632382702297364173503430783194115698247820013290851202878474805860188960045901745974055630732714487679085288867978809970695240681006625611440014983413580889737246844064948857074167687916413224205373654067330186392497910915474785959163865597507090581175924899502214799250945635582514315814464060134283490422798357939659258985200763845646681640732681928346007767285876284900068874564639274964415904034033672337814491597032941787294155061054129515400159393851663929325677429557549480046658273579653990940233543644649376827272541873627547532976808190325336141086433084237771738995221536763095302045902438694632702895293994483013577589081214884558493819874505920914067209522469096263076941753340983698859363700314973728977996360018626500174929290087931189997822963712306642297996163582572600112288983647651418045975770042120833949364659647336464289044499325396227091907373705772051322815957863227591912786054297862953188615559804728160710864132803585400160055575686855791785977899197902656592621283007225351401525973569300729015392211116868504740402172174442051738000251361000494534119324331668344243125963098812396962202358858395587831685194833126653577353244379935683215269177042249034574534858913812582681366908929476809052635560638119661306063936938411817713545929884317232912236262458868394202889981693561169865429884776513118227662526739978808816010470651542335015671353744817086234314662531190291040152262927104099285072418843329007277794754111637552176563589316326636049381218401837512818884771168975479483767664084842753623074019542183217985496260666590347925816342392670947839907062923166535037285019751324813803837070894638925470887039085723581006130628646664710006104352115778926613432214655311411882596942926284522109026688414975763341554921135581254616558078273470115814006008345762133130389987843270653719956709570847385786092649188858378739239165554263577301292243641604062551736892335636568854365851646207821875741724364525814143487632761341752707376754922276287782264765154315341585713773522730335403376364204258034257264749686217823666951353410677378421131371131987373222891805275062812277716412494412401207125954319991746574745892582613712825555535080404143944557295994554635608487251339462936358940832098964801619583130429720964794128539388996265368928263807677168759588502216464582430940165009688797366157733560316836710386895228270941509545222744002735499253670214715994056544813842186380128799900820933576320736369405991424263718294000613741900579513096298545330748197802568301089672873802234820488862973130369689882640657904781562389778485365025691064231795736025330908763271784911189748432246868086340383964176127605788646574472284824932687443062551220506955168464669477183681911432873544815836350548146411099960143390595799766290646881295025039150923633011076070632863317393378149693380247580035052789782755750928604039420506342939327064636161031822879248152679306862749237275631852225654266008556849497720285909150930495425967473648331437236349555448901598668408362176913559656039519670425368863482369587129462524759031776813184977588276576740482558136502103649585505703259219957675334264223783723586058509403583977103476670644788640831109650302565215607464019652716999732373465237173456595514559493098166644006211599349133180135150528651842178828026343325934755850761168697709125580056185683710540856081249519403148064618719402577663285267019698387567561524696759028106864896869293315954352097687527137201616160931174250199709289684940034696242325688410665113304377412256176258658941236728171145526423894512631717834790276921171452887352955019336759218908006048633737786728180610254782570436788449503518925787499836694785908612975543084122677060954347612133717433156783790162012337237023338316414706428592185977610158232721997915062871868186750981665537745013020880333904353639770263363809098526494532628146558065546504823486429495390613257400496912888340518222933644476683855037967975809619983575807027759535968788226194659612223044549275600274955168583542582295336042834426318478068825395450746691877897765406038432512843812811316856204608617289408229658626174420766920297427930088129519854678713548623236610413216581279267151545961594352593456757445992307889205519540082316409719591250025455237503106735639748835542480449681383030671851931491335789202123605308199952020584503423499932150962634977812456658304680581824563524814625849331926195406884818446445248429486063016169476663242625231476322371109695369483824482316410396224507675405614287468267835723704895606990652792688455844512046654853378534026646645042339638488257719874953611300494215593735545211926186721478265416885604094928290056616883807637656690510740892510549165222968878676968631652514917701499900066637344546120262780701925698706225540928945194718778004306130021828287425867048748480826948573444778244078734102710824870269523830804910960482013901294024631244800159336670212658317677879752965963472576894326540435889267293950687860830626266263287392087327302547910099932113388977807814336728791448768373686467748528777737403547472871644217767820712964506270880978637928144071192505141148004907055608097229299792441471062852247029870699869227676341773513258602908903875707454368077876422385333700692089616351009233587303986543906071880952557553380364725895007306772122528078179471056481171378557451057691044322925429024149433588396093679321361696954251299731031032804436954501929843820842383121265825740594509426942777307124802176915781835720087170538773256017987133005505911377823841791640280841409623820847637393013930778428554545222367559824666250608754284876104145661362227642405914304455580856318180935230407793891614902116292400515074914068443203230365609954878620999194306564455332547135557365318516011700321550690787716752062881527885897149410320986984083048966524351030502444679931779147659103428949129054120361601695671222140806369405940304552186212879933092856231022418446365289097444640151986623183881962444822590783585914043686193019041458962693878907034982169868696934448086213990534591792826654304798207219634134755646525483143771156678459077797196510772468000293581546267646310224279007313631352522067062951125935874473134186492497282784796644585448962932905262058065248588707020879389134476083344653170939242408249328008915731319541348311820927752486880548733943315867562666122179355051190609992911379445634995627391898459029021713155706096267881673302940198464237390445098028030948975981259252055850973537436556825780313681902007151675693827281818824587541710721180806556448039122504537089422695358382192535075692834095639859265599740391316709290043996275976830375217503360879028295673068862263077729733533853682668734519035709709687322323738300494090123239274318759046526327095178406267264828893646896593219169521106361729757074376148061601331104911692271318609404145014842866423634716982892418180484365230538864559809839273836490685480823014267803143937440431807822678779494006206489151248952516543005634448375046751754207043313372486870633237561645232360481932024377596890914783372179553676992603235715185513391098402739063753280702313301755754269396202629423910945323537910125948964941812563672992967084250667599803456273455598559628512281414582556024841783305645240508450065988755987518601335860624932784487772006842296591945516539562982960591610046578907214842054861830418175604559815168088031783080261445994444677918012432146400983610678683412974872596729258786806223080115822026289014364459002301645823666709265571264559925790622304745235625575111770791512002789380975775468546121017307522799241407026308137792971909461413145802081087738121624539858769697371425881836152605069380926917712087321915005831977113322793572385071940612761291872572099404930250277748156614021327434743881966413330052634229082906400927944924808556131183440161804801357032507836323938921567643159620442612809700944107776130638909071294456394056601559246025454204771186140420155233371270501377121034570009578009389265329385720478576508777149663403003562380595757191609382171312222810465858388943507176431939973012661591423837170284400120399485880996231859472474858776584355077006934099220340378772192728370301380838144394114984971730766162961342059105014814283949700695951676939041557902856356911055547312684571497449635320554677940775184056667637222969090346128706829887104278761090090999160443821794511763620835379716161833124364431267855435550800507986124664397724135502128238026726719914989727248512981287283697489276420792868666970177259794407858155909332508554131299946581118527691652464790819119384233275897699573012098103009171001695718791616942270079528915191912521053891838538959315167400505723817401030621004380243011187977704252328073236575129609372456053680037516596164236147709330391224409752871732067976128120428026739256557305675931512645750047875756531854825821411574030473147492511910835615765732002546109686701890307648531373832912682481741181359032826625082549313211431478953352317043989053928534946642886074268371824902498092479487226633686823799580875637040808655649321905489637785549531167397935270799470452399153297534358690514105864096534514182896474439367182852711843560799285895978176543950113088848419163516673213692860830956744502801800373716458009168082972708715609185038654053436660045504985624687376022557041595800250174095361839287643458003670864954057941720085136357127163768323493134230703821274484501440529541695374381945459456533165140990993722722801019654652726227831512103467686166826131471843610025517863247950150022953695466317739589344131481485834694374523981159954666071205997794363440185078360899108948073419633939259318973940943110042116729120199722626609871927014024105805515315100109804996044147291039451030312664114726736839973315035036742741546992633165270432940675237449075056739508929674779115800864399992564817208847429250821546279856079127768611946086210349405535850134472190244543824521089284409498132717010673966471114931896789977661595488186193176900175027901783824624387873831483279500879026433992577026588005849778984624295660321276945810824348129690840972550671054732471317254997191901039553305847040728081693158626093886019147689944137673621432083607375131574376316754666479186753896571555100850626810005119827486807780592667765654100834778571024250133253391587384761024129794736751001163498977803745930025457609870671092153597115178252014281216647543034075128600240297038428615984289816602143429849088917359682192284469123035904329877231843309914187264674607558318725713138832356015809009594182530207799397648462597901883341793830920965841463574411985878296475850943053008148341821747826603773762252997703468752903517310792083220038080809212164346586817989810504274375385786789186350517717501606531826406928883250135919517178537687865881752366421534010961295763074762648070312757365787762352859057153932484576503944390496668087711899192498933896524852395536795827530614167131757915756386606004839994179548705868209201195154952031294562451315422506574858629161606523796643010172693950282294667489681746821163996794950294284013099235901278250437428192557634533217576162292751110598368271567229778620053722932314082887058749444060116236521627717558503013451471452765841864277071769968435499620257547431811994883385806759692359580622165832464092095350648357935817742903018315351290014321495518177456908388719320697769695657771754499149911431368950836160692539606469893374870942933219185601299108564470256257163505508620689240297589684714283678684735455533583477652536156578189996983068654671736445996343136468195427420490472433064675001442697508322369013083895492637066778406531328664886080129513771720847581157719491012345141774941482773580041432667332379617716965698582785832300505265883502247868050648201444570593197343382923860072601696510903258980909912837652275381493529845099414966933862815568031306981064525192703818515872648691762563239441425216118427769145067718411735714396681005615483952443154944864238384298900399826113322468963346522104692545137969276009719645338955332105584245640187448611050959111766828942711640054010503770420346052521318228045892998637903572350665108782350043349942391285236308896510989246641056331584171142885304143772286629832318970869030400301325951476774237516158840915838059151673504519131178193943428482922272304061422582078027829148070426761629302539228321084917759984200595105312164731818409493139800444072847325902609169730998153853939031280878823902948001579008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
[7]:
Partitions(1000000).cardinality()
[7]:
1471684986358223398631004760609895943484030484439142125334612747351666117418918618276330148873983597555842015374130600288095929387347128232270327849578001932784396072064228659048713020170971840761025676479860846908142829356706929785991290519899445490672219997823452874982974022288229850136767566294781887494687879003824699988197729200632068668735996662273816798266213482417208446631027428001918132198177180646511234542595026728424452592296781193448139994664730105742564359154794989181485285351370551399476719981691459022015599101959601417474075715430750022184895815209339012481734469448319323280150665384042994054179587751761294916248142479998802936507195257074485047571662771763903391442495113823298195263008336489826045837712202455304996382144601028531832004519046591968302787537418118486000612016852593542741980215046267245473237321845833427512524227465399130174076941280847400831542217999286071108336303316298289102444649696805395416791875480010852636774022023128467646919775022348562520747741843343657801534130704761975530375169707999287040285677841619347472368171772154046664303121315630003467104673818
[11]:
f = sin(x)^2+cos(x)^2
[12]:
f.simplify_trig()
[12]:
1
[13]:
plot(sin(x))
[13]:

[38]:
R = QQ['q,t'].fraction_field()
q,t = R.gens()
Sym = SymmetricFunctions(R)
Sym.inject_shorthands()
/opt/sage-git2/local/lib/python2.7/site-packages/sage/combinat/sf/sf.py:1416: RuntimeWarning: redefining global value `h`
inject_variable(shorthand, getattr(self, shorthand)())
/opt/sage-git2/local/lib/python2.7/site-packages/sage/combinat/sf/sf.py:1416: RuntimeWarning: redefining global value `m`
inject_variable(shorthand, getattr(self, shorthand)())
[16]:
s = Sym.s()
[17]:
s
[17]:
Symmetric Functions over Rational Field in the Schur basis
[18]:
s.an_element()
[18]:
2*s[] + 2*s[1] + 3*s[2]
[19]:
e = Sym.e()
[24]:
s( e[5,5,3,2] * (s[2,1] +1) )
[24]:
s[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 3*s[2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + s[2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 6*s[2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 4*s[2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 9*s[2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 9*s[2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 11*s[2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1] + 15*s[2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 12*s[2, 2, 2, 2, 2, 1, 1, 1, 1, 1] + 20*s[2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1] + 10*s[2, 2, 2, 2, 2, 2, 1, 1, 1] + 23*s[2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1] + 6*s[2, 2, 2, 2, 2, 2, 2, 1] + 22*s[2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1] + 16*s[2, 2, 2, 2, 2, 2, 2, 2, 1, 1] + 6*s[2, 2, 2, 2, 2, 2, 2, 2, 2] + 3*s[3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 4*s[3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 8*s[3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 16*s[3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 13*s[3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1] + 35*s[3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 16*s[3, 2, 2, 2, 1, 1, 1, 1, 1, 1] + 56*s[3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 17*s[3, 2, 2, 2, 2, 1, 1, 1, 1] + 72*s[3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1] + 13*s[3, 2, 2, 2, 2, 2, 1, 1] + 78*s[3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] + 5*s[3, 2, 2, 2, 2, 2, 2] + 68*s[3, 2, 2, 2, 2, 2, 2, 1, 1, 1] + 40*s[3, 2, 2, 2, 2, 2, 2, 2, 1] + 6*s[3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 20*s[3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 12*s[3, 3, 2, 1, 1, 1, 1, 1, 1, 1] + 53*s[3, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 15*s[3, 3, 2, 2, 1, 1, 1, 1, 1] + 88*s[3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1] + 16*s[3, 3, 2, 2, 2, 1, 1, 1] + 112*s[3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1] + 10*s[3, 3, 2, 2, 2, 2, 1] + 116*s[3, 3, 2, 2, 2, 2, 1, 1, 1, 1] + 90*s[3, 3, 2, 2, 2, 2, 2, 1, 1] + 34*s[3, 3, 2, 2, 2, 2, 2, 2] + 6*s[3, 3, 3, 1, 1, 1, 1, 1, 1] + 39*s[3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 9*s[3, 3, 3, 2, 1, 1, 1, 1] + 80*s[3, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1] + 10*s[3, 3, 3, 2, 2, 1, 1] + 106*s[3, 3, 3, 2, 2, 1, 1, 1, 1, 1] + 4*s[3, 3, 3, 2, 2, 2] + 106*s[3, 3, 3, 2, 2, 2, 1, 1, 1] + 68*s[3, 3, 3, 2, 2, 2, 2, 1] + 3*s[3, 3, 3, 3, 1, 1, 1] + 42*s[3, 3, 3, 3, 1, 1, 1, 1, 1, 1] + 4*s[3, 3, 3, 3, 2, 1] + 68*s[3, 3, 3, 3, 2, 1, 1, 1, 1] + 66*s[3, 3, 3, 3, 2, 2, 1, 1] + 28*s[3, 3, 3, 3, 2, 2, 2] + s[3, 3, 3, 3, 3] + 26*s[3, 3, 3, 3, 3, 1, 1, 1] + 26*s[3, 3, 3, 3, 3, 2, 1] + 4*s[3, 3, 3, 3, 3, 3] + s[4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 6*s[4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 3*s[4, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 24*s[4, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 5*s[4, 2, 2, 1, 1, 1, 1, 1, 1, 1] + 51*s[4, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 6*s[4, 2, 2, 2, 1, 1, 1, 1, 1] + 78*s[4, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1] + 6*s[4, 2, 2, 2, 2, 1, 1, 1] + 96*s[4, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1] + 4*s[4, 2, 2, 2, 2, 2, 1] + 97*s[4, 2, 2, 2, 2, 2, 1, 1, 1, 1] + 74*s[4, 2, 2, 2, 2, 2, 2, 1, 1] + 28*s[4, 2, 2, 2, 2, 2, 2, 2] + 3*s[4, 3, 1, 1, 1, 1, 1, 1, 1, 1] + 35*s[4, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 6*s[4, 3, 2, 1, 1, 1, 1, 1, 1] + 90*s[4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 7*s[4, 3, 2, 2, 1, 1, 1, 1] + 142*s[4, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1] + 7*s[4, 3, 2, 2, 2, 1, 1] + 172*s[4, 3, 2, 2, 2, 1, 1, 1, 1, 1] + 3*s[4, 3, 2, 2, 2, 2] + 163*s[4, 3, 2, 2, 2, 2, 1, 1, 1] + 102*s[4, 3, 2, 2, 2, 2, 2, 1] + 3*s[4, 3, 3, 1, 1, 1, 1, 1] + 66*s[4, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1] + 4*s[4, 3, 3, 2, 1, 1, 1] + 127*s[4, 3, 3, 2, 1, 1, 1, 1, 1, 1] + 4*s[4, 3, 3, 2, 2, 1] + 159*s[4, 3, 3, 2, 2, 1, 1, 1, 1] + 140*s[4, 3, 3, 2, 2, 2, 1, 1] + 57*s[4, 3, 3, 2, 2, 2, 2] + s[4, 3, 3, 3, 1, 1] + 62*s[4, 3, 3, 3, 1, 1, 1, 1, 1] + s[4, 3, 3, 3, 2] + 94*s[4, 3, 3, 3, 2, 1, 1, 1] + 73*s[4, 3, 3, 3, 2, 2, 1] + 32*s[4, 3, 3, 3, 3, 1, 1] + 19*s[4, 3, 3, 3, 3, 2] + s[4, 4, 1, 1, 1, 1, 1, 1, 1] + 20*s[4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 2*s[4, 4, 2, 1, 1, 1, 1, 1] + 52*s[4, 4, 2, 1, 1, 1, 1, 1, 1, 1, 1] + 2*s[4, 4, 2, 2, 1, 1, 1] + 79*s[4, 4, 2, 2, 1, 1, 1, 1, 1, 1] + 2*s[4, 4, 2, 2, 2, 1] + 91*s[4, 4, 2, 2, 2, 1, 1, 1, 1] + 77*s[4, 4, 2, 2, 2, 2, 1, 1] + 31*s[4, 4, 2, 2, 2, 2, 2] + s[4, 4, 3, 1, 1, 1, 1] + 44*s[4, 4, 3, 1, 1, 1, 1, 1, 1, 1] + s[4, 4, 3, 2, 1, 1] + 80*s[4, 4, 3, 2, 1, 1, 1, 1, 1] + s[4, 4, 3, 2, 2] + 94*s[4, 4, 3, 2, 2, 1, 1, 1] + 68*s[4, 4, 3, 2, 2, 2, 1] + 37*s[4, 4, 3, 3, 1, 1, 1, 1] + 51*s[4, 4, 3, 3, 2, 1, 1] + 25*s[4, 4, 3, 3, 2, 2] + 14*s[4, 4, 3, 3, 3, 1] + 12*s[4, 4, 4, 1, 1, 1, 1, 1, 1] + 21*s[4, 4, 4, 2, 1, 1, 1, 1] + 23*s[4, 4, 4, 2, 2, 1, 1] + 11*s[4, 4, 4, 2, 2, 2] + 10*s[4, 4, 4, 3, 1, 1, 1] + 12*s[4, 4, 4, 3, 2, 1] + 2*s[4, 4, 4, 3, 3] + s[4, 4, 4, 4, 1, 1] + s[4, 4, 4, 4, 2] + 4*s[5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 16*s[5, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 33*s[5, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 48*s[5, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1] + 56*s[5, 2, 2, 2, 2, 1, 1, 1, 1, 1] + 52*s[5, 2, 2, 2, 2, 2, 1, 1, 1] + 32*s[5, 2, 2, 2, 2, 2, 2, 1] + 24*s[5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 59*s[5, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1] + 87*s[5, 3, 2, 2, 1, 1, 1, 1, 1, 1] + 99*s[5, 3, 2, 2, 2, 1, 1, 1, 1] + 83*s[5, 3, 2, 2, 2, 2, 1, 1] + 33*s[5, 3, 2, 2, 2, 2, 2] + 41*s[5, 3, 3, 1, 1, 1, 1, 1, 1, 1] + 72*s[5, 3, 3, 2, 1, 1, 1, 1, 1] + 84*s[5, 3, 3, 2, 2, 1, 1, 1] + 60*s[5, 3, 3, 2, 2, 2, 1] + 31*s[5, 3, 3, 3, 1, 1, 1, 1] + 43*s[5, 3, 3, 3, 2, 1, 1] + 21*s[5, 3, 3, 3, 2, 2] + 12*s[5, 3, 3, 3, 3, 1] + 16*s[5, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 39*s[5, 4, 2, 1, 1, 1, 1, 1, 1, 1] + 54*s[5, 4, 2, 2, 1, 1, 1, 1, 1] + 58*s[5, 4, 2, 2, 2, 1, 1, 1] + 40*s[5, 4, 2, 2, 2, 2, 1] + 30*s[5, 4, 3, 1, 1, 1, 1, 1, 1] + 48*s[5, 4, 3, 2, 1, 1, 1, 1] + 52*s[5, 4, 3, 2, 2, 1, 1] + 24*s[5, 4, 3, 2, 2, 2] + 18*s[5, 4, 3, 3, 1, 1, 1] + 22*s[5, 4, 3, 3, 2, 1] + 4*s[5, 4, 3, 3, 3] + 7*s[5, 4, 4, 1, 1, 1, 1, 1] + 10*s[5, 4, 4, 2, 1, 1, 1] + 10*s[5, 4, 4, 2, 2, 1] + 3*s[5, 4, 4, 3, 1, 1] + 3*s[5, 4, 4, 3, 2] + 4*s[5, 5, 1, 1, 1, 1, 1, 1, 1, 1] + 9*s[5, 5, 2, 1, 1, 1, 1, 1, 1] + 11*s[5, 5, 2, 2, 1, 1, 1, 1] + 11*s[5, 5, 2, 2, 2, 1, 1] + 5*s[5, 5, 2, 2, 2, 2] + 6*s[5, 5, 3, 1, 1, 1, 1, 1] + 8*s[5, 5, 3, 2, 1, 1, 1] + 8*s[5, 5, 3, 2, 2, 1] + 2*s[5, 5, 3, 3, 1, 1] + 2*s[5, 5, 3, 3, 2] + s[5, 5, 4, 1, 1, 1, 1] + s[5, 5, 4, 2, 1, 1] + s[5, 5, 4, 2, 2] + s[6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 4*s[6, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 8*s[6, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1] + 11*s[6, 2, 2, 2, 1, 1, 1, 1, 1, 1] + 12*s[6, 2, 2, 2, 2, 1, 1, 1, 1] + 10*s[6, 2, 2, 2, 2, 2, 1, 1] + 4*s[6, 2, 2, 2, 2, 2, 2] + 6*s[6, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 14*s[6, 3, 2, 1, 1, 1, 1, 1, 1, 1] + 19*s[6, 3, 2, 2, 1, 1, 1, 1, 1] + 20*s[6, 3, 2, 2, 2, 1, 1, 1] + 14*s[6, 3, 2, 2, 2, 2, 1] + 9*s[6, 3, 3, 1, 1, 1, 1, 1, 1] + 14*s[6, 3, 3, 2, 1, 1, 1, 1] + 15*s[6, 3, 3, 2, 2, 1, 1] + 7*s[6, 3, 3, 2, 2, 2] + 5*s[6, 3, 3, 3, 1, 1, 1] + 6*s[6, 3, 3, 3, 2, 1] + s[6, 3, 3, 3, 3] + 4*s[6, 4, 1, 1, 1, 1, 1, 1, 1, 1] + 9*s[6, 4, 2, 1, 1, 1, 1, 1, 1] + 11*s[6, 4, 2, 2, 1, 1, 1, 1] + 11*s[6, 4, 2, 2, 2, 1, 1] + 5*s[6, 4, 2, 2, 2, 2] + 6*s[6, 4, 3, 1, 1, 1, 1, 1] + 8*s[6, 4, 3, 2, 1, 1, 1] + 8*s[6, 4, 3, 2, 2, 1] + 2*s[6, 4, 3, 3, 1, 1] + 2*s[6, 4, 3, 3, 2] + s[6, 4, 4, 1, 1, 1, 1] + s[6, 4, 4, 2, 1, 1] + s[6, 4, 4, 2, 2] + s[6, 5, 1, 1, 1, 1, 1, 1, 1] + 2*s[6, 5, 2, 1, 1, 1, 1, 1] + 2*s[6, 5, 2, 2, 1, 1, 1] + 2*s[6, 5, 2, 2, 2, 1] + s[6, 5, 3, 1, 1, 1, 1] + s[6, 5, 3, 2, 1, 1] + s[6, 5, 3, 2, 2]
[25]:
p = Sym.p()
[26]:
Sym.inject_shorthands()
/opt/sage-git2/local/lib/python2.7/site-packages/sage/combinat/sf/sf.py:1416: RuntimeWarning: redefining global value `s`
inject_variable(shorthand, getattr(self, shorthand)())
/opt/sage-git2/local/lib/python2.7/site-packages/sage/combinat/sf/sf.py:1416: RuntimeWarning: redefining global value `e`
inject_variable(shorthand, getattr(self, shorthand)())
/opt/sage-git2/local/lib/python2.7/site-packages/sage/combinat/sf/sf.py:1416: RuntimeWarning: redefining global value `p`
inject_variable(shorthand, getattr(self, shorthand)())
[27]:
e[4]+ p[5,4] * (m[3,2,1] +1)
[27]:
e[1, 1, 1, 1, 1, 1, 1, 1, 1] - 9*e[2, 1, 1, 1, 1, 1, 1, 1] + 27*e[2, 2, 1, 1, 1, 1, 1] - 30*e[2, 2, 2, 1, 1, 1] + 10*e[2, 2, 2, 2, 1] + 9*e[3, 1, 1, 1, 1, 1, 1] - 45*e[3, 2, 1, 1, 1, 1] + e[3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 50*e[3, 2, 2, 1, 1] - 9*e[3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1] - 10*e[3, 2, 2, 2] + 27*e[3, 2, 2, 2, 1, 1, 1, 1, 1, 1] - 30*e[3, 2, 2, 2, 2, 1, 1, 1, 1] + 10*e[3, 2, 2, 2, 2, 2, 1, 1] + 20*e[3, 3, 1, 1, 1] - 3*e[3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1] - 20*e[3, 3, 2, 1] + 36*e[3, 3, 2, 1, 1, 1, 1, 1, 1, 1] - 126*e[3, 3, 2, 2, 1, 1, 1, 1, 1] + 140*e[3, 3, 2, 2, 2, 1, 1, 1] - 40*e[3, 3, 2, 2, 2, 2, 1] - 27*e[3, 3, 3, 1, 1, 1, 1, 1, 1] + 155*e[3, 3, 3, 2, 1, 1, 1, 1] - 170*e[3, 3, 3, 2, 2, 1, 1] + 30*e[3, 3, 3, 2, 2, 2] - 60*e[3, 3, 3, 3, 1, 1, 1] + 60*e[3, 3, 3, 3, 2, 1] + e[4] - 9*e[4, 1, 1, 1, 1, 1] - 3*e[4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 40*e[4, 2, 1, 1, 1] + 31*e[4, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1] - 30*e[4, 2, 2, 1] - 117*e[4, 2, 2, 1, 1, 1, 1, 1, 1, 1] + 198*e[4, 2, 2, 2, 1, 1, 1, 1, 1] - 150*e[4, 2, 2, 2, 2, 1, 1, 1] + 40*e[4, 2, 2, 2, 2, 2, 1] - 40*e[4, 3, 1, 1] - 27*e[4, 3, 1, 1, 1, 1, 1, 1, 1, 1] + 20*e[4, 3, 2] + 162*e[4, 3, 2, 1, 1, 1, 1, 1, 1] - 290*e[4, 3, 2, 2, 1, 1, 1, 1] + 200*e[4, 3, 2, 2, 2, 1, 1] - 40*e[4, 3, 2, 2, 2, 2] - 33*e[4, 3, 3, 1, 1, 1, 1, 1] - 20*e[4, 3, 3, 2, 1, 1, 1] + 30*e[4, 3, 3, 2, 2, 1] + 120*e[4, 3, 3, 3, 1, 1] - 60*e[4, 3, 3, 3, 2] + 20*e[4, 4, 1] + 27*e[4, 4, 1, 1, 1, 1, 1, 1, 1] - 156*e[4, 4, 2, 1, 1, 1, 1, 1] + 250*e[4, 4, 2, 2, 1, 1, 1] - 120*e[4, 4, 2, 2, 2, 1] + 120*e[4, 4, 3, 1, 1, 1, 1] - 200*e[4, 4, 3, 2, 1, 1] + 80*e[4, 4, 3, 2, 2] - 60*e[4, 4, 3, 3, 1] - 60*e[4, 4, 4, 1, 1, 1] + 80*e[4, 4, 4, 2, 1] + 5*e[5, 1, 1, 1, 1] + 7*e[5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - 20*e[5, 2, 1, 1] - 63*e[5, 2, 1, 1, 1, 1, 1, 1, 1, 1] + 10*e[5, 2, 2] + 189*e[5, 2, 2, 1, 1, 1, 1, 1, 1] - 210*e[5, 2, 2, 2, 1, 1, 1, 1] + 70*e[5, 2, 2, 2, 2, 1, 1] + 20*e[5, 3, 1] + 63*e[5, 3, 1, 1, 1, 1, 1, 1, 1] - 310*e[5, 3, 2, 1, 1, 1, 1, 1] + 330*e[5, 3, 2, 2, 1, 1, 1] - 60*e[5, 3, 2, 2, 2, 1] + 125*e[5, 3, 3, 1, 1, 1, 1] - 60*e[5, 3, 3, 2, 1, 1] - 30*e[5, 3, 3, 2, 2] - 60*e[5, 3, 3, 3, 1] - 20*e[5, 4] - 78*e[5, 4, 1, 1, 1, 1, 1, 1] + 360*e[5, 4, 2, 1, 1, 1, 1] - 320*e[5, 4, 2, 2, 1, 1] + 40*e[5, 4, 2, 2, 2] - 340*e[5, 4, 3, 1, 1, 1] + 200*e[5, 4, 3, 2, 1] + 60*e[5, 4, 3, 3] + 200*e[5, 4, 4, 1, 1] - 80*e[5, 4, 4, 2] + 35*e[5, 5, 1, 1, 1, 1, 1] - 140*e[5, 5, 2, 1, 1, 1] + 70*e[5, 5, 2, 2, 1] + 140*e[5, 5, 3, 1, 1] - 140*e[5, 5, 4, 1] - 12*e[6, 1, 1, 1, 1, 1, 1, 1, 1, 1] + 108*e[6, 2, 1, 1, 1, 1, 1, 1, 1] - 324*e[6, 2, 2, 1, 1, 1, 1, 1] + 360*e[6, 2, 2, 2, 1, 1, 1] - 120*e[6, 2, 2, 2, 2, 1] - 108*e[6, 3, 1, 1, 1, 1, 1, 1] + 540*e[6, 3, 2, 1, 1, 1, 1] - 600*e[6, 3, 2, 2, 1, 1] + 120*e[6, 3, 2, 2, 2] - 240*e[6, 3, 3, 1, 1, 1] + 240*e[6, 3, 3, 2, 1] + 108*e[6, 4, 1, 1, 1, 1, 1] - 480*e[6, 4, 2, 1, 1, 1] + 360*e[6, 4, 2, 2, 1] + 480*e[6, 4, 3, 1, 1] - 240*e[6, 4, 3, 2] - 240*e[6, 4, 4, 1] - 60*e[6, 5, 1, 1, 1, 1] + 240*e[6, 5, 2, 1, 1] - 120*e[6, 5, 2, 2] - 240*e[6, 5, 3, 1] + 240*e[6, 5, 4]
[28]:
f
[28]:
cos(x)^2 + sin(x)^2
[31]:
s[4,2].coproduct()
[31]:
[30]:
%display latex
[33]:
s[4] ( s[3] ) - s[3] (s[4])
[33]:
[34]:
s[4].expand(4)
[34]:
[40]:
s[4] ( s.one() * (1+t) )
[40]:
[44]:
res = sum( tensor( [s[mu],s[mu]] ) for mu in Partitions(4))
res
[44]:
[50]:
tensor([p,p]) (res)
[50]:
[52]:
s(h[4] ( h[2] ) - h[2] ( h[4] ))
[52]:
[ ]:
%%gap3
This is a jupyter notebook demo for the package multipolynomial_bases. The package is installed by default on CoCalc. If you want to run it on your local sage install, you need to install the package as explained here
The documentation is available here
[1]:
# Run this cell to load the librairy
from multipolynomial_bases import *
This package offers an implementation of multivariate polynomials seen as a multi bases algebra (it is not a classical implementation for symbolic computation on multivariate polynomials).
The main algebra is created by:
[2]:
A.<x> = MultivariatePolynomialAlgebra(QQ); A
[2]:
The Multivariate polynomial algebra on x over Rational Field
As you can see, we don’t specify the number of variables. The name \(x\) does not refer to a single variable but to the infinite alphabet \(x_1, x_2, x_3, \dots\). Or from an implementation point of view: to the monomial bases of the algebra.
[3]:
x
[3]:
The Multivariate polynomial algebra on x over Rational Field on the monomial basis
A basis element of the monomial basis is indexed by a vector. So an element of the algebra is just a formal sum of vectors.
[4]:
x[2,2,1] + x[3,4,2]
[4]:
x[2, 2, 1] + x[3, 4, 2]
In the world of classical multivariate polynomials, the values of the vectors are the exponents of the monomial.
[8]:
x[2,2,1].to_expr()
[8]:
x1^2*x2^2*x3
[9]:
(x[2,2,1] + x[3,4,2]).to_expr()
[9]:
x1^3*x2^4*x3^2 + x1^2*x2^2*x3
When you multiply two monomials, we make the sum of the vectors.
[10]:
x[2,2,1]*x[3,1,4]
[10]:
x[5, 3, 5]
[18]:
(x[2,2,1] + x[3,4,2]) * x[1,1,2]
[18]:
x[3, 3, 3] + x[4, 5, 4]
The number of variables is adjusted depending on the size of the vectors.
[11]:
x[1] + x[0,1] + x[0,0,1]
[11]:
x[1, 0, 0] + x[0, 1, 0] + x[0, 0, 1]
[12]:
x[1] == x[1,0,0]
[12]:
True
[13]:
x[0,1].to_expr()
[13]:
x2
[14]:
A.var(6)
[14]:
x[0, 0, 0, 0, 0, 1]
[15]:
A.var(6).to_expr()
[15]:
x6
In the monomial basis, the polynomial are always expanded, if you want to factorize, you have to move to the usual multivariate polynomials.
[5]:
p1 = (x[1] - x[0,1])**2
p1
[5]:
x[2, 0] + x[0, 2] - 2*x[1, 1]
[8]:
K.<x1,x2,x3> = QQ[]
p2 = K(p1.to_expr())
p2
[8]:
x1^2 - 2*x1*x2 + x2^2
[9]:
p2.factor()
[9]:
(x1 - x2)^2
On the other hand, this implementation allows for easy computation of divided differences. The following polynomial corresponds to
\(x_1^2 x_2^4 x_3 + 2 x_1 x_2 + x_4^2\)
[21]:
p = x[2,4,1] + 2*x[1,1] + x[0,0,0,4]
By applying \(\delta_2\) you get
[22]:
p.divided_difference(2)
[22]:
x[2, 3, 1, 0] + 2*x[1, 0, 0, 0] + x[2, 2, 2, 0] + x[2, 1, 3, 0]
Which is
\(x_1^2x_2^3x_3 + 2x_1 + x_1^2x_2^2x_3^2 + x_1^2x_2x_3^3\)
[ ]:
Expanding in the monomials is just one way of working with polynomials. The purpose of this program is to be able to use other bases (as for symmetric functions). The Schubert basis is one of the bases of the multivariate polynomial algebra.
[23]:
Y = A.schubert_basis()
Y
[23]:
The Multivariate polynomial algebra on x over Rational Field on the Schubert basis of type A
[24]:
Y.an_element()
[24]:
2*Y[1, 0, 0] + Y[2, 2, 3] + Y[0, 0, 0] + 3*Y[0, 1, 0]
Each element is index by a vector, you can create a Schubert polynomial by entering the vector.
[25]:
pol = Y[2,1,2]
pol
[25]:
Y[2, 1, 2]
If you want to compute the expansion of this polynomial in a sum of monomials, you just convert it.
[6]:
x(pol)
[6]:
x[2, 2, 1] + x[3, 1, 1] + x[2, 1, 2]
Similarly, you can write any sum of monomials into a sum of Schubert polynomials.
[7]:
Y(x[1,1,2] + x[2,3])
[7]:
-Y[3, 2, 0] - Y[1, 2, 1] + Y[2, 3, 0] + Y[1, 1, 2]
But what are those vectors?
You may be used to index the Schubert polynomials with permutations. The vectors we use are called Lehmer codes, they are in direct correspondence with permutations. Let \(\sigma = \sigma_1 \sigma_2 \dots \sigma_n\) the one line notation of a permutation of size \(n\). The corresponding Lehmer code \(v = v_1 \dots v_n\) is given by \(v_i = \# \lbrace j > i ; \sigma_j < \sigma_i \rbrace\). In particular, the sum of \(v\) is the number of inversions of the permutation.
Sage knows how to compute Lehmer codes.
[8]:
Permutation([5,2,1,4,3]).to_lehmer_code()
[8]:
[4, 1, 0, 1, 0]
[9]:
Permutation([3,2,5,4,1]).to_lehmer_code()
[9]:
[2, 1, 2, 1, 0]
[36]:
import sage.combinat.permutation as permutation
permutation.from_lehmer_code([4,1,0,1,0])
[36]:
[5, 2, 1, 4, 3]
[11]:
permutation.from_lehmer_code([2,1,2,1,0])
[11]:
[3, 2, 5, 4, 1]
Why using Lehmer codes instead of permutations?
This package has been written following the notations of Alain Lascoux who had good reasons to find vectors (Lehmer codes) a better indexing set than permutations. Indeed, this way, you don’t restrict yourself to one specific symmetric group. Especially, when you multiply two Schubert polynomials indexed by permutations of \(S_n\) you might end up in \(S_m\) with \(m > n\). On the other end, the size of the vector won’t change.
[25]:
Y[2,1,0] * Y[0,1]
[25]:
Y[3, 1, 0] + Y[2, 2, 0]
The code \(\left[ 2,1,0 \right]\) corresponds to \(\sigma = 321\) and \(\left[0,1 \right]\) is \(132\). But then \(\left[ 3,1,0 \right]\) is \(4213\) and \(\left[ 2,2,0 \right]\) is \(3412\).
Actually, the number of variables used to expand the polynomial in the monomial basis is given by the position of the last non zero value.
[12]:
x(Y[2,1])
[12]:
x[2, 1]
[13]:
Y[2,1].to_expr()
[13]:
x1^2*x2
[14]:
x(Y[0,1])
[14]:
x[0, 1] + x[1, 0]
[15]:
Y[0,1].to_expr()
[15]:
x1 + x2
In particular, you have:
[31]:
Y[2,1] == Y[2,1,0]
[31]:
True
[32]:
Y[2,1] == Y[2,1,0,0]
[32]:
True
Also, Lehmer codes are a very natural indexing set for applying divided differences. If \(v_{i} > v_{i+1}\) the divided difference \(\delta_i\) does \(v_i = v_{i+1}\) and \(v_{i+1} = v_i - 1\). In this example \(\left[1,3,2 \right]\) becomes \(\left[1,2,2\right]\).
[16]:
Y[1,3,2].divided_difference(2)
[16]:
Y[1, 2, 2]
When \(v_i \leq v_{i+1}\) the result is 0.
[17]:
Y[1,3,2].divided_difference(1)
[17]:
0
To expand Schubert polynomials into the monomial, we use divided differences. You can check that the Lehmer code of the maximum permutation \(\omega_n\) is \(\left[n-1, n-2, \dots 0 \right]\) and the corresponding Schubert polynomial is given by \(x\left[n-1, n-2, \dots 0 \right]\).
[35]:
x(Y[4,3,2,1,0])
[35]:
x[4, 3, 2, 1, 0]
This is actually true for any vectors where values are weakly decreasing.
[27]:
x(Y[4,4,2])
[27]:
x[4, 4, 2]
[28]:
x(Y[6,2])
[28]:
x[6, 2]
The other polynomial are obtained recursively by applying divided differences.
\(Y[4,2,2,1] = \delta_2(Y[4,3,2,1])\)
\(Y[2,3,2,1] = \delta_1(Y[4,2,2,1])\)
[29]:
x(Y[4,3,2,1]).divided_difference(2).divided_difference(1)
[29]:
x[2, 3, 2, 1] + x[3, 2, 2, 1]
[30]:
x(Y[2,3,2,1])
[30]:
x[2, 3, 2, 1] + x[3, 2, 2, 1]
To apply multiple divided differences, you can also write
[32]:
x(Y[4,3,2,1]).apply_reduced_word([2,1])
[32]:
x[2, 3, 2, 1] + x[3, 2, 2, 1]
Remark: when you apply the maximal reduced word to the the Schubert polynomial indexed by the maximal permutation, you obtain \(Y[0,0,\dots,0] = 1\).
[39]:
max_reduced_word = Permutation([5,4,3,2,1]).reduced_word()
[41]:
Y[4,3,2,1,0].apply_reduced_word(max_reduced_word)
[41]:
Y[0, 0, 0, 0, 0]
In general, if you apply the maximal reduced word of size \(n\) on a Shubert polynomial indexed by any decreasing vector of size \(n\), you get a Schubert polynomial indexed by a weakly increasing vector.
[43]:
s = Y[6,4,3,1,0].apply_reduced_word(max_reduced_word)
s
[43]:
Y[0, 0, 1, 1, 2]
This is actually a symmetric polynomial and more precisely, this is the expansion of a schur function indexed by the partition given by the vector.
[44]:
x(s)
[44]:
x[0, 2, 1, 1, 0] + x[0, 1, 0, 2, 1] + 3*x[1, 1, 1, 0, 1] + x[2, 0, 0, 1, 1] + x[1, 0, 2, 1, 0] + x[0, 1, 2, 1, 0] + x[0, 0, 2, 1, 1] + x[2, 1, 1, 0, 0] + x[0, 1, 2, 0, 1] + x[0, 0, 1, 2, 1] + x[1, 0, 1, 0, 2] + 3*x[1, 1, 0, 1, 1] + x[2, 1, 0, 1, 0] + x[1, 2, 0, 0, 1] + x[0, 1, 1, 2, 0] + 3*x[1, 1, 1, 1, 0] + x[0, 2, 0, 1, 1] + x[0, 1, 1, 0, 2] + x[1, 1, 0, 2, 0] + x[0, 0, 1, 1, 2] + x[0, 1, 0, 1, 2] + x[1, 0, 0, 1, 2] + x[1, 0, 0, 2, 1] + x[2, 0, 1, 1, 0] + x[1, 1, 0, 0, 2] + x[1, 1, 2, 0, 0] + 3*x[1, 0, 1, 1, 1] + x[2, 1, 0, 0, 1] + x[1, 0, 1, 2, 0] + x[1, 2, 1, 0, 0] + x[0, 2, 1, 0, 1] + x[2, 0, 1, 0, 1] + x[1, 2, 0, 1, 0] + 3*x[0, 1, 1, 1, 1] + x[1, 0, 2, 0, 1]
[47]:
S = SymmetricFunctions(QQ)
schur = S.schur()
[48]:
p1 = schur[2,1,1].expand(5)
p1
[48]:
x0^2*x1*x2 + x0*x1^2*x2 + x0*x1*x2^2 + x0^2*x1*x3 + x0*x1^2*x3 + x0^2*x2*x3 + 3*x0*x1*x2*x3 + x1^2*x2*x3 + x0*x2^2*x3 + x1*x2^2*x3 + x0*x1*x3^2 + x0*x2*x3^2 + x1*x2*x3^2 + x0^2*x1*x4 + x0*x1^2*x4 + x0^2*x2*x4 + 3*x0*x1*x2*x4 + x1^2*x2*x4 + x0*x2^2*x4 + x1*x2^2*x4 + x0^2*x3*x4 + 3*x0*x1*x3*x4 + x1^2*x3*x4 + 3*x0*x2*x3*x4 + 3*x1*x2*x3*x4 + x2^2*x3*x4 + x0*x3^2*x4 + x1*x3^2*x4 + x2*x3^2*x4 + x0*x1*x4^2 + x0*x2*x4^2 + x1*x2*x4^2 + x0*x3*x4^2 + x1*x3*x4^2 + x2*x3*x4^2
[50]:
# let's make both our expansions live in the same world
var("x0 x1 x2 x3 x4")
K.<x0,x1,x2,x3,x4> = QQ[]
p1 = K(p1)
p1
[50]:
x0^2*x1*x2 + x0*x1^2*x2 + x0*x1*x2^2 + x0^2*x1*x3 + x0*x1^2*x3 + x0^2*x2*x3 + 3*x0*x1*x2*x3 + x1^2*x2*x3 + x0*x2^2*x3 + x1*x2^2*x3 + x0*x1*x3^2 + x0*x2*x3^2 + x1*x2*x3^2 + x0^2*x1*x4 + x0*x1^2*x4 + x0^2*x2*x4 + 3*x0*x1*x2*x4 + x1^2*x2*x4 + x0*x2^2*x4 + x1*x2^2*x4 + x0^2*x3*x4 + 3*x0*x1*x3*x4 + x1^2*x3*x4 + 3*x0*x2*x3*x4 + 3*x1*x2*x3*x4 + x2^2*x3*x4 + x0*x3^2*x4 + x1*x3^2*x4 + x2*x3^2*x4 + x0*x1*x4^2 + x0*x2*x4^2 + x1*x2*x4^2 + x0*x3*x4^2 + x1*x3*x4^2 + x2*x3*x4^2
[52]:
p2 = K(x(s).to_expr(alphabet=[x0,x1,x2,x3,x4]))
[53]:
p2
[53]:
x0^2*x1*x2 + x0*x1^2*x2 + x0*x1*x2^2 + x0^2*x1*x3 + x0*x1^2*x3 + x0^2*x2*x3 + 3*x0*x1*x2*x3 + x1^2*x2*x3 + x0*x2^2*x3 + x1*x2^2*x3 + x0*x1*x3^2 + x0*x2*x3^2 + x1*x2*x3^2 + x0^2*x1*x4 + x0*x1^2*x4 + x0^2*x2*x4 + 3*x0*x1*x2*x4 + x1^2*x2*x4 + x0*x2^2*x4 + x1*x2^2*x4 + x0^2*x3*x4 + 3*x0*x1*x3*x4 + x1^2*x3*x4 + 3*x0*x2*x3*x4 + 3*x1*x2*x3*x4 + x2^2*x3*x4 + x0*x3^2*x4 + x1*x3^2*x4 + x2*x3^2*x4 + x0*x1*x4^2 + x0*x2*x4^2 + x1*x2*x4^2 + x0*x3*x4^2 + x1*x3*x4^2 + x2*x3*x4^2
[54]:
p1 == p2
[54]:
True
So, now I’d like to manipulate your polynomials and play with them, how do I do that?
Many operations are implemented, you can check the documentation to know more. Also, it is always possible to just to get list of index vectors and coefficients and just do whatever you want with those.
[55]:
p = Y[1,2,1] + 2*Y[3,2] - Y[2,2,3]
[57]:
list(p)
[57]:
[([3, 2, 0], 2), ([1, 2, 1], 1), ([2, 2, 3], -1)]
[63]:
# getting the set of permutations along with multiplicity that appear
import sage.combinat.permutation as permutation
p = p.change_nb_variables(6) # to have enough 0 at the end of the Lehmer codes so that they correspond to permutations of S6
{permutation.from_lehmer_code(k): c for k,c in p}
[63]:
{[2, 4, 3, 1, 5, 6]: 1, [3, 4, 6, 1, 2, 5]: -1, [4, 3, 1, 2, 5, 6]: 2}
[ ]:
[ ]:
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
- Maria Gillespie’s lecture notes on Schubert Calculus Variations
- Stephen Griffeth’s lecture notes on Algebraic combinatorics and representations of Cherednik algebras
- The official SageMath thematic tutorials.
- More SageMath thematic tutorials.
- The book Calcul Mathématique avec Sage, and its ongoing translations into English and German.
- Sage’s tutorials on
Symmetric functions
andQuasi symmetric functions
- An in-the-work improved symmetric functions tutorial (prepared by Pauline Hubert and Mélodie Lapointe)
- A tutorial on integer partitions (prepared by Pauline Hubert and Mélodie Lapointe)
- A compendium of symmetric function formulas (prepared by Nancy Wallace)
- A compendium of rectangular combinatorics formulas (prepared by Étienne Tétreault)
- A brief Demontration: Computing with ideals using Singular (early draft)
- Chow, a SageMath library for computations in intersection theory, by Christophe Sorger and Manfred Lehn.
- multipolynomial_bases, a SageMath library for computing with various bases of multivariate polynomials: Schubert polynomials, Grothendieck polynomials, Demazure characters, by Viviane Pons
- code and comments on how to compute the bicharacter of diagonal harmonics up to 5 rows of 6 variables, by Bergeron and Thiéry
- Other Computational sessions at the CRM thematic semester: Algebra and Words in Combinatorics.
Brief introduction to Sage
Joint exploration of symmetric function features available in SageMath (notebook);
Help desk, work in small groups on pen&paper and/or computational exercises or personal problems.
For suggestions of exercises, see the lecturers notes and computational exercises
- Informal help desk
Other demos as requested
Help desk, work in small groups on pen&paper and/or computational exercises or personal problems.
For suggestions of exercises, see the lecturers notes and computational exercises
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
This sheet contains a few additional exercises related to the lectures.
Exercise: parabola in projective space
Plot a parabola in 3D, and illustrate that it degenerates into an ellipse when looking tangentially.
Hint: see parametric_plot3d()
and the options
aspect_ratio
, frame
, and viewer='threejs'
of
show()
.
A solution:
sage: var('u')
sage: p = parametric_plot3d((u, -u^2, 0), (u,-40,40), boundary_style=None)
sage: p.show(viewer="threejs", frame=False)
Research problem
Define the operators
acting on the polynomial ring \(\mathbb{Q}[x_1,\dots,x_n]\). At \(q=0\), the operators degenerate to the symmetric powersums, seen as differential operators. Their joint zeroes form the space of harmonic polynomials, which is of dimension \(n!\), carries the graded regular representation of \(S_n\), etc.
Conjecture [Wood with successive refinements by Hivert & T., D’Aderrio & Mocci, Bergeron & Borie & T.]:
- The same holds for \(q\)-harmonic polynomials, defined as the joint zeroes of the operators \(D_{q,k}, k\geq 1\).
- Exceptions: \(q=-a/b\) for \(a,b \in \mathbb{NN}\) with \(1\leq a \leq n \leq b\).
- This extends to Coxeter groups \(G(m,p,n)\) and diagonal harmonics.
Many things have been tried, but I (Nicolas) believe nobody tried to use the Cherednik algebra to tackle this problem.
References:
- arXiv:1010.4985 On a conjecture of Hivert and Thiéry about Steenrod operators Michele D’Adderio, Luca Moci
- arXiv:1011.3654 Deformed diagonal h`armonic polynomials for complex reflection groups François Bergeron, Nicolas Borie, Nicolas M.Thiéry
- arXiv:0812.3566 Harmonics for Deformed Steenrod Operators Francois Bergeron, Adriano Garsia, Nolan Wallach
- arXiv:0812.3056 Deformation of symmetric functions and the rational Steenrod algebra Florent Hivert, Nicolas M. Thiéry
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
This is part of a series of Computational sessions at the CRM thematic semester: Algebra and Words in Combinatorics.
- Joint exploration of reflection group features available in SageMath (see: Demo: reflection groups (draft))
- Help desk, work in small groups on computational exercises or personal problems.
- Software installation;
- Joint exploration of Sage features
- (see Symmetric functions, Posets related to coxeter groups);
- Help desk, work in small groups.
- Help desk, work in small groups.
- Demo by Cédric of computing with Cherednik algebras on Magma
- Demo by Vic + N. illustrating Vic’s course (see Computing Molien-type sums for reflection groups);
- Pen and paper exercise sessions by Cédric, Nathan, Vic:
- Vic’s exercise sheet;
- Computational exercises.
- Help desk, work in small groups.
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
This sheet contains some computational exercises related to the lectures.
Exercise (basic computations + explore the classification)
For all finite Coxeter groups \(W\) (just a few of them for the infinite families):
#. Compute the cardinality of `W`
- Compute the length of the longest element of \(W\)
See CoxeterGroup()
, samples()
Exercise (pictures)
- Construct the root lattice for type \(G_2\) and plot it (see Root Systems, Tutorial: visualizing root systems).
- Draw more pictures, for finite and affine Weyl groups!
Exercise (computing with roots)
Check on examples the property that \(ws_i\) is longer than \(w\) if and only if \(w.\alpha_i\) is a positive root.
Two options with the current implementation in Sage:
- In the crystalographic case, build the root lattice and its Weyl group
- Use the permutation representation
Exercise (enumerative combinatorics for reduced words)
- Count the number of reduced words for the longest element in
\(S_n\) and retrieve the sequence from the Online Encyclopedia
of Integer Sequences, for example by using
oeis
. - Check on computer that this matches with OEIS’s suggestion
about
standard Young tableaux
). - The bijection is known as Edelman-Green’s insertion. Search for
its implementation is Sage (see
search_src()
). - Try with other types.
Exercises
- Draw the (truncated) Cayley graph for Gamma = 3,3,3
- Implement the twist operation
- Implement the twist-rigidity test
- Implement listing all applicable twists
- Compute all Coxeter systems that can be obtained from a given
Coxeter system by applying twists (see
RecursivelyEnumeratedSet
) - Implement the (truncated) Davis complex
Exercise (product formula for inversions)
- Check the product formula for the inversions statistic in the
- symmetric group;
- Retrieve the analogue product formula for some other reflection groups.
Exercise (other product formula)
- Implement a function that, given a polynomial \(\prod(1-q^{d_i})\) in expanded form, recovers the \(d_i\) (see exercise 2 in Vic’s exercise sheet);
- Use it to recover the degrees, exponents, and coexponents for a couple reflection groups from their Molien formula, and check the product formula of the lectures (see Computing Molien-type sums for reflection groups).
[1]:
%display latex
[2]:
S = SymmetricFunctions(QQ)
[3]:
S.inject_shorthands()
/opt/sage-git2/local/lib/python2.7/site-packages/sage/combinat/sf/sf.py:1416: RuntimeWarning: redefining global value `e`
inject_variable(shorthand, getattr(self, shorthand)())
[4]:
(s[2,1] + 3 * m[2,1]) * (e[3,1]+1)
[4]:
[5]:
print latex(s[4,3,2] * s[10,5,3])
s_{10,5,4,3,3,2} + s_{10,5,4,4,2,2} + s_{10,5,4,4,3,1} + s_{10,5,5,3,2,2} + s_{10,5,5,3,3,1} + s_{10,5,5,4,2,1} + s_{10,5,5,4,3} + s_{10,6,3,3,3,2} + 2s_{10,6,4,3,2,2} + 2s_{10,6,4,3,3,1} + 2s_{10,6,4,4,2,1} + s_{10,6,4,4,3} + s_{10,6,5,2,2,2} + 3s_{10,6,5,3,2,1} + 2s_{10,6,5,3,3} + s_{10,6,5,4,1,1} + 2s_{10,6,5,4,2} + s_{10,6,6,2,2,1} + s_{10,6,6,3,1,1} + 2s_{10,6,6,3,2} + s_{10,6,6,4,1} + s_{10,7,3,3,2,2} + s_{10,7,3,3,3,1} + s_{10,7,4,2,2,2} + 3s_{10,7,4,3,2,1} + 2s_{10,7,4,3,3} + s_{10,7,4,4,1,1} + 2s_{10,7,4,4,2} + 2s_{10,7,5,2,2,1} + 2s_{10,7,5,3,1,1} + 4s_{10,7,5,3,2} + 2s_{10,7,5,4,1} + s_{10,7,6,2,1,1} + 2s_{10,7,6,2,2} + 3s_{10,7,6,3,1} + s_{10,7,6,4} + s_{10,7,7,2,1} + s_{10,7,7,3} + s_{10,8,3,3,2,1} + s_{10,8,3,3,3} + s_{10,8,4,2,2,1} + s_{10,8,4,3,1,1} + 3s_{10,8,4,3,2} + s_{10,8,4,4,1} + s_{10,8,5,2,1,1} + 2s_{10,8,5,2,2} + 3s_{10,8,5,3,1} + s_{10,8,5,4} + 2s_{10,8,6,2,1} + 2s_{10,8,6,3} + s_{10,8,7,2} + s_{10,9,3,3,2} + s_{10,9,4,2,2} + s_{10,9,4,3,1} + s_{10,9,5,2,1} + s_{10,9,5,3} + s_{10,9,6,2} + s_{11,5,3,3,3,2} + 2s_{11,5,4,3,2,2} + 2s_{11,5,4,3,3,1} + 2s_{11,5,4,4,2,1} + s_{11,5,4,4,3} + s_{11,5,5,2,2,2} + 3s_{11,5,5,3,2,1} + 2s_{11,5,5,3,3} + s_{11,5,5,4,1,1} + 2s_{11,5,5,4,2} + 2s_{11,6,3,3,2,2} + 2s_{11,6,3,3,3,1} + 2s_{11,6,4,2,2,2} + 6s_{11,6,4,3,2,1} + 3s_{11,6,4,3,3} + 2s_{11,6,4,4,1,1} + 3s_{11,6,4,4,2} + 4s_{11,6,5,2,2,1} + 4s_{11,6,5,3,1,1} + 7s_{11,6,5,3,2} + 3s_{11,6,5,4,1} + 2s_{11,6,6,2,1,1} + 3s_{11,6,6,2,2} + 4s_{11,6,6,3,1} + s_{11,6,6,4} + s_{11,7,3,2,2,2} + 3s_{11,7,3,3,2,1} + 2s_{11,7,3,3,3} + 4s_{11,7,4,2,2,1} + 4s_{11,7,4,3,1,1} + 7s_{11,7,4,3,2} + 3s_{11,7,4,4,1} + 4s_{11,7,5,2,1,1} + 6s_{11,7,5,2,2} + 8s_{11,7,5,3,1} + 2s_{11,7,5,4} + s_{11,7,6,1,1,1} + 6s_{11,7,6,2,1} + 4s_{11,7,6,3} + s_{11,7,7,1,1} + 2s_{11,7,7,2} + s_{11,8,3,2,2,1} + s_{11,8,3,3,1,1} + 3s_{11,8,3,3,2} + 2s_{11,8,4,2,1,1} + 4s_{11,8,4,2,2} + 5s_{11,8,4,3,1} + s_{11,8,4,4} + s_{11,8,5,1,1,1} + 6s_{11,8,5,2,1} + 4s_{11,8,5,3} + 2s_{11,8,6,1,1} + 4s_{11,8,6,2} + s_{11,8,7,1} + s_{11,9,3,2,2} + s_{11,9,3,3,1} + 2s_{11,9,4,2,1} + s_{11,9,4,3} + s_{11,9,5,1,1} + 2s_{11,9,5,2} + s_{11,9,6,1} + s_{12,5,3,3,2,2} + s_{12,5,3,3,3,1} + s_{12,5,4,2,2,2} + 3s_{12,5,4,3,2,1} + 2s_{12,5,4,3,3} + s_{12,5,4,4,1,1} + 2s_{12,5,4,4,2} + 2s_{12,5,5,2,2,1} + 2s_{12,5,5,3,1,1} + 4s_{12,5,5,3,2} + 2s_{12,5,5,4,1} + s_{12,6,3,2,2,2} + 3s_{12,6,3,3,2,1} + 2s_{12,6,3,3,3} + 4s_{12,6,4,2,2,1} + 4s_{12,6,4,3,1,1} + 7s_{12,6,4,3,2} + 3s_{12,6,4,4,1} + 4s_{12,6,5,2,1,1} + 6s_{12,6,5,2,2} + 8s_{12,6,5,3,1} + 2s_{12,6,5,4} + s_{12,6,6,1,1,1} + 5s_{12,6,6,2,1} + 3s_{12,6,6,3} + 2s_{12,7,3,2,2,1} + 2s_{12,7,3,3,1,1} + 4s_{12,7,3,3,2} + 4s_{12,7,4,2,1,1} + 6s_{12,7,4,2,2} + 8s_{12,7,4,3,1} + 2s_{12,7,4,4} + 2s_{12,7,5,1,1,1} + 10s_{12,7,5,2,1} + 6s_{12,7,5,3} + 4s_{12,7,6,1,1} + 6s_{12,7,6,2} + 2s_{12,7,7,1} + s_{12,8,3,2,1,1} + 2s_{12,8,3,2,2} + 3s_{12,8,3,3,1} + s_{12,8,4,1,1,1} + 6s_{12,8,4,2,1} + 4s_{12,8,4,3} + 4s_{12,8,5,1,1} + 6s_{12,8,5,2} + 4s_{12,8,6,1} + s_{12,8,7} + s_{12,9,3,2,1} + s_{12,9,3,3} + s_{12,9,4,1,1} + 2s_{12,9,4,2} + 2s_{12,9,5,1} + s_{12,9,6} + s_{13,5,3,3,2,1} + s_{13,5,3,3,3} + s_{13,5,4,2,2,1} + s_{13,5,4,3,1,1} + 3s_{13,5,4,3,2} + s_{13,5,4,4,1} + s_{13,5,5,2,1,1} + 2s_{13,5,5,2,2} + 3s_{13,5,5,3,1} + s_{13,5,5,4} + s_{13,6,3,2,2,1} + s_{13,6,3,3,1,1} + 3s_{13,6,3,3,2} + 2s_{13,6,4,2,1,1} + 4s_{13,6,4,2,2} + 5s_{13,6,4,3,1} + s_{13,6,4,4} + s_{13,6,5,1,1,1} + 6s_{13,6,5,2,1} + 4s_{13,6,5,3} + 2s_{13,6,6,1,1} + 3s_{13,6,6,2} + s_{13,7,3,2,1,1} + 2s_{13,7,3,2,2} + 3s_{13,7,3,3,1} + s_{13,7,4,1,1,1} + 6s_{13,7,4,2,1} + 4s_{13,7,4,3} + 4s_{13,7,5,1,1} + 6s_{13,7,5,2} + 4s_{13,7,6,1} + s_{13,7,7} + 2s_{13,8,3,2,1} + 2s_{13,8,3,3} + 2s_{13,8,4,1,1} + 4s_{13,8,4,2} + 4s_{13,8,5,1} + 2s_{13,8,6} + s_{13,9,3,2} + s_{13,9,4,1} + s_{13,9,5} + s_{14,5,3,3,2} + s_{14,5,4,2,2} + s_{14,5,4,3,1} + s_{14,5,5,2,1} + s_{14,5,5,3} + s_{14,6,3,2,2} + s_{14,6,3,3,1} + 2s_{14,6,4,2,1} + s_{14,6,4,3} + s_{14,6,5,1,1} + 2s_{14,6,5,2} + s_{14,6,6,1} + s_{14,7,3,2,1} + s_{14,7,3,3} + s_{14,7,4,1,1} + 2s_{14,7,4,2} + 2s_{14,7,5,1} + s_{14,7,6} + s_{14,8,3,2} + s_{14,8,4,1} + s_{14,8,5}
[6]:
s[3,2].coproduct()
[6]:
[7]:
tensor([s[3,2], (p[2,1]+p[3])])
[7]:
[8]:
p[3](s[2,1])
[8]:
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
SLC 64: Sage and Sage-Combinat demo¶
Sage demo¶
sage: %hide
sage: pretty_print_default()
sage: 1 + 1
sage: plot(sin(x), -pi, pi, fill = 'axis')
sage: @interact
....: def _(a=(0,2)):
....: show(plot(sin(x*(1+a*x)), (x,0,6)))
sage: @interact
....: def plottaylor(order=(1..15)):
....: f = sin(x) * e^(-x)
....: g = f.taylor(x, 0, order)
....: html('$f(x)\;=\;%s$'%latex(f))
....: html('$\hat{f}(x;%s)\;=\;%s+\mathcal{O}(x^{%s})$'%(0,latex(g),order+1))
....: F = plot(f,-1, 5, thickness=2)
....: G = plot(g,-1, 5, color='green', thickness=2)
....: show(F+G, ymin = -.5, ymax = 1)
sage: var('y')
....: f = sin(x) - cos(x*y) + 1 / (x^3+1)
....: f
sage: f.integrate(x)
sage: f.i
sage: f.is_idempotent
sage: f.is_idempotent
Elementary combinatorics¶
sage: p = Partition([3,3,2,1])
sage: p
sage: p.pp()
sage: p.conjugate().pp()
sage: s = Permutation([5,3,2,6,4,8,9,7,1])
sage: s
sage: (p,q) = s.robinson_schensted()
sage: p.pp()
1 4 7 9
2 6 8
3
5
sage: q.pp()
1 4 6 7
2 5 8
3
9
sage: G = p.row_stabilizer()
sage: G
Permutation Group with generators [(), (7,9), (6,8), (4,7), (2,6), (1,4)]
sage: G.
sage: P5 = Partitions(5)
sage: P5
Partitions of the integer 5
sage: P5.list()
[[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]]
sage: P5.cardinality()
7
sage: Partitions(100000).cardinality()
27493510569775696512677516320986352688173429315980054758203125984302147328114964173055050741660736621590157844774296248940493063070200461792764493033510116079342457190155718943509725312466108452006369558934464248716828789832182345009262853831404597021307130674510624419227311238999702284408609370935531629697851569569892196108480158600569421098519
sage: Permutations(20).random_element()
[15, 6, 8, 14, 17, 16, 4, 7, 11, 3, 10, 5, 19, 9, 12, 2, 20, 18, 1, 13]
sage: Compositions(10).unrank(100) # TODO: non stupid algorithm
[1, 1, 3, 1, 2, 1, 1]
sage: for p in StandardTableaux([3,2]):
....: print "-----------------------------"
....: p.pp()
-----------------------------
1 3 5
2 4
-----------------------------
1 2 5
3 4
-----------------------------
1 3 4
2 5
-----------------------------
1 2 4
3 5
-----------------------------
1 2 3
4 5
ToDo
Summary:
- Every mathematical object (element, set, category, …) is modeled by a Python object</li>
- All combinatorial classes share a uniform interface</li>
sage: C = DisjointUnionEnumeratedSets( [ Compositions(4), Permutations(3)] )
sage: C
Union of Family (Compositions of 4, Standard permutations of 3)
sage: C.cardinality()
14
sage: C.list()
[[1, 1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 3], [2, 1, 1], [2, 2], [3, 1], [4], [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
sage: C = CartesianProduct(Compositions(8), Permutations(20))
sage: C
Cartesian product of Compositions of 8, Standard permutations of 20
sage: C.cardinality()
311411457046609920000
sage: F = Family(NonNegativeIntegers(), Permutations)
sage: F
Lazy family (Permutations(i))_{i in Set of non negative integers}
sage: F[1000]
Standard permutations of 1000
sage: U = DisjointUnionEnumeratedSets(F)
sage: U.cardinality()
+Infinity
sage: for p in U:
....: print p
[]
[1]
[1, 2]
[2, 1]
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
...
Summary:
- Basic combinatorial classes + constructions give a flexible toolbox
- This is made possible by uniform interfaces
- Lazy algorithms and data structures for large / infinite sets (iterators, …)
Integer lists:
sage: IntegerVectors(10, 3, min_part = 2, max_part = 5, inner = [2, 4, 2]).list()
[[4, 4, 2], [3, 5, 2], [3, 4, 3], [2, 5, 3], [2, 4, 4]]
sage: Compositions(5, max_part = 3, min_length = 2, max_length = 3).list()
[[1, 1, 3], [1, 2, 2], [1, 3, 1], [2, 1, 2], [2, 2, 1], [2, 3], [3, 1, 1], [3, 2]]
sage: Partitions(5, max_slope = -1).list()
[[5], [4, 1], [3, 2]]
sage: IntegerListsLex(10, length=3, min_part = 2, max_part = 5, floor = [2, 4, 2]).list()
[[4, 4, 2], [3, 5, 2], [3, 4, 3], [2, 5, 3], [2, 4, 4]]
sage: IntegerListsLex(5, min_part = 1, max_part = 3, min_length = 2, max_length = 3).list()
[[3, 2], [3, 1, 1], [2, 3], [2, 2, 1], [2, 1, 2], [1, 3, 1], [1, 2, 2], [1, 1, 3]]
sage: IntegerListsLex(5, min_part = 1, max_slope = -1).list()
[[5], [4, 1], [3, 2]]
sage: c = Compositions(5)[1]
sage: c
[1, 1, 1, 2]
sage: c = IntegerListsLex(5, min_part = 1)[1]
sage: from sage.combinat.species.library import *
sage: o = var("o")
Fibonacci words:
sage: Eps = EmptySetSpecies()
sage: Z0 = SingletonSpecies()
sage: Z1 = Eps*SingletonSpecies()
sage: FW = CombinatorialSpecies()
sage: FW.define(Eps + Z0*FW + Z1*Eps + Z1*Z0*FW)
sage: FW
sage: L = FW.isotype_generating_series().coefficients(15)
sage: L
sage: sloane_find(L)
Searching Sloane's online database...
[[45, 'Fibonacci numbers: F(n) = F(n-1) + F(n-2), F(0) = 0, F(1) = 1, F(2) = 1, ...', [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169]], [24595, 'a(n) = s(1)t(n) + s(2)t(n-1) + ... + s(k)t(n+1-k), where k = [ (n+1)/2 ], s = (F(2), F(3), ...), t = A023533.', [1, 0, 0, 1, 2, 3, 5, 0, 0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1598, 2586, 4184, 6770, 10954, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28658, 46370, 75028, 121398, 196426]], [25109, 'a(n) = s(1)t(n) + s(2)t(n-1) + ... + s(k)t(n-k+1), where k = [ n/2 ], s = (F(2), F(3), F(4), ...), t = A023533.', [0, 0, 1, 2, 3, 0, 0, 0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1598, 2586, 4181, 6770, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28658, 46370, 75028, 121398, 196426, 317824, 514250]], [132636, 'Fib(n) mod n^3.', [0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 1685, 7063, 4323, 4896, 12525, 15937, 19271, 10483, 2060, 22040, 5674, 15621, 2752, 3807, 9340, 432, 46989, 19305, 11932, 62155, 31899, 12088, 22273, 3677, 32420]], [132916, 'a(0)=0; a(1)=1; a(n) = Sum a(n-k), k= 1 ... [n^(1/3)] for n>=2.', [0, 1, 1, 1, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 21892, 39603, 72441, 133936, 245980, 452357, 832273, 1530610, 2815240, 5178123, 9523973, 17517336, 32219432, 59260741, 108997509, 200477682]], [147316, 'A000045 Fibonacci mirror sequence Binet: f(n)=(1/5)*2^(-n) ((5 - 2 *Sqrt[5]) (1 + Sqrt[5])^n + (1 - Sqrt[5])^n(5 + 2 * Sqrt[5])).', [1597, -987, 610, -377, 233, -144, 89, -55, 34, -21, 13, -8, 5, -3, 2, -1, 1, 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597]], [39834, 'a(n+2)=-a(n+1)+a(n) (signed Fibonacci numbers); or Fibonacci numbers (A000045) extended to negative indices.', [1, 1, 0, 1, -1, 2, -3, 5, -8, 13, -21, 34, -55, 89, -144, 233, -377, 610, -987, 1597, -2584, 4181, -6765, 10946, -17711, 28657, -46368, 75025, -121393, 196418, -317811, 514229, -832040, 1346269, -2178309, 3524578, -5702887, 9227465, -14930352, 24157817]], [152163, 'a(n)=a(n-1)+a(n-2), n>1 ; a(0)=1, a(1)=-1 .', [1, -1, 0, -1, -1, -2, -3, -5, -8, -13, -21, -34, -55, -89, -144, -233, -377, -610, -987, -1597, -2584, -4181, -6765, -10946, -17711, -28657, -46368, -75025, -121393, -196418, -317811, -514229, -832040, -1346269, -2178309, -3524578, -5702887]]]
sage: BT = CombinatorialSpecies()
sage: Leaf = SingletonSpecies()
sage: BT.define(Leaf+(BT*BT))
sage: BT5 = BT.isotypes([o]*5)
sage: BT5.list()
[o*(o*(o*(o*o))), o*(o*((o*o)*o)), o*((o*o)*(o*o)), o*((o*(o*o))*o), o*(((o*o)*o)*o), (o*o)*(o*(o*o)), (o*o)*((o*o)*o), (o*(o*o))*(o*o), ((o*o)*o)*(o*o), (o*(o*(o*o)))*o, (o*((o*o)*o))*o, ((o*o)*(o*o))*o, ((o*(o*o))*o)*o, (((o*o)*o)*o)*o]
sage: %hide
sage: def pbt_to_coordinates(t):
....: e = {}
....: queue = [t]
....: while queue:
....: z = queue.pop()
....: if not isinstance(z[0], int):
....: e[z[1]._labels[0]-1] = z
....: queue.extend(z)
....: coord = [(len(e[i][0]._labels) * len(e[i][1]._labels))
....: for i in range(len(e))]
....: return sage.geometry.polyhedra.Polytopes.project_1(coord)
....:
sage: K4 = Polyhedron(vertices=[pbt_to_coordinates(t) for t in BT.isotypes(range(5))])
sage: K4.show(fill=True).show(frame=False)
sage: A=random_matrix(ZZ,3,6,x=7)
sage: L=LatticePolytope(A)
sage: L.plot3d()
sage: L.npoints() # should be cardinality!
28
This example used PALP and J-mol.
sage: show(graphs(5, lambda G: G.size() <= 4))
Words¶
An infinite periodic word:
sage: p = Word([0,1,1,0,1,0,1]) ^ Infinity
sage: p
word: 0110101011010101101010110101011010101101...
The fixed point of a morphism:
sage: m = WordMorphism('a->acabb,b->bcacacbb,c->baba')
sage: w = m.fixed_point('a')
sage: w
word: acabbbabaacabbbcacacbbbcacacbbbcacacbbac...
Predefined algebraic structures¶
sage: L = RootSystem(['A',2,1]).weight_space()
sage: L.plot(size=[[-1..1],[-1..1]],alcovewalks=[[0,2,0,1,2,1,2,0,2,1]])
sage: W = WeylGroup(["B", 3])
sage: W.cayley_graph(side = "left").plot3d(color_by_label = True)
sage: print W.character_table() # Thanks GAP!
CT1
2 4 4 3 3 4 3 1 1 3 4
3 1 . . . . . 1 1 . 1
1a 2a 2b 4a 2c 2d 6a 3a 4b 2e
X.1 1 1 1 1 1 1 1 1 1 1
X.2 1 1 1 -1 -1 -1 -1 1 1 -1
X.3 1 1 -1 -1 1 -1 1 1 -1 1
X.4 1 1 -1 1 -1 1 -1 1 -1 -1
X.5 2 2 . . -2 . 1 -1 . -2
X.6 2 2 . . 2 . -1 -1 . 2
X.7 3 -1 1 1 1 -1 . . -1 -3
X.8 3 -1 -1 -1 1 1 . . 1 -3
X.9 3 -1 -1 1 -1 -1 . . 1 3
X.10 3 -1 1 -1 -1 1 . . -1 3
sage: rho = SymmetricGroupRepresentation([3, 2], "orthogonal"); rho
Orthogonal representation of the symmetric group corresponding to [3, 2]
sage: rho([1, 3, 2, 4, 5])
1 & 0 & 0 & 0 & 0 \\
0 & -\frac{1}{2} & \frac{1}{2} \, \sqrt{3} & 0 & 0 \\
0 & \frac{1}{2} \, \sqrt{3} & \frac{1}{2} & 0 & 0 \\
0 & 0 & 0 & -\frac{1}{2} & \frac{1}{2} \, \sqrt{3} \\
0 & 0 & 0 & \frac{1}{2} \, \sqrt{3} & \frac{1}{2}
Classical basis:
sage: Sym = SymmetricFunctions(QQ)
sage: Sym
Symmetric Functions over Rational Field
sage: s = Sym.schur()
sage: h = Sym.complete()
sage: e = Sym.elementary()
sage: m = Sym.monomial()
sage: p = Sym.powersum()
sage: m(( ( h[2,1] * ( 1 + 3 * p[2,1]) ) + s[2](s[3])))
Macdonald polynomials:
sage: J = MacdonaldPolynomialsJ(QQ)
sage: P = MacdonaldPolynomialsP(QQ)
sage: Q = MacdonaldPolynomialsQ(QQ)
sage: J
Macdonald polynomials in the J basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field
sage: f = P(J[2,2] + 3 * Q[3,1])
sage: f
(q^2*t^6-q^2*t^5-q^2*t^4-q*t^5+q^2*t^3+2*q*t^3+t^3-q*t-t^2-t+1)*McdP[2, 2] + ((3*q^3*t^5-6*q^3*t^4+3*q^3*t^3-3*q^2*t^4+6*q^2*t^3-3*q^2*t^2-3*q*t^3+6*q*t^2-3*q*t+3*t^2-6*t+3)/(q^7*t-2*q^6*t+2*q^4*t-q^4-q^3*t+2*q^3-2*q+1))*McdP[3, 1]
sage: Sym = SymmetricFunctions(J.base_ring())
sage: s = Sym.s()
sage: s(f)
A demonstration of Sage + GAP4 + GAP3 + Chevie + Semigroupe¶
Let us create the Coxeter group W:
sage: W = CoxeterGroup(["H",4])
It is constructed as a group of permutations, from root data given by GAP3+Chevie (thanks to Franco’s interface):
sage: W._gap_group
CoxeterGroup("H",4)
sage: (W._gap_group).parent()
Gap3
with operations on permutations implemented in Sage:
sage: W.an_element()^3
(1,5)(2,62)(3,7)(6,9)(8,12)(11,15)(13,17)(16,20)(18,22)(21,25)(26,29)(28,31)(30,33)(32,35)(34,37)(36,39)(38,41)(42,45)(46,48)(47,49)(50,52)(55,56)(57,58)(61,65)(63,67)(66,69)(68,72)(71,75)(73,77)(76,80)(78,82)(81,85)(86,89)(88,91)(90,93)(92,95)(94,97)(96,99)(98,101)(102,105)(106,108)(107,109)(110,112)(115,116)(117,118)
and group operations implemented in GAP:
sage: len(W.conjugacy_classes_representatives())
34
sage: W.cardinality()
14400
Now, assume we want to do intensive computations on this group, requiring heavy access to the left and right Cayley graphs (e.g. Bruhat interval calculations, representation theory, …). Then we can use Jean-Eric Pin’s Semigroupe, a software written in C:
sage: S = semigroupe.AutomaticSemigroup(W.semigroup_generators(), W.one(), category = FiniteCoxeterGroups())
The following triggers the full expansion of the group and its Cayley graph in memory:
sage: S.cardinality()
14400
And we can now iterate through the elements, in length-lexicographic order w.r.t. their reduced word:
sage: sum( x^p.length() for p in S)
x^60 + 4*x^59 + 9*x^58 + 16*x^57 + 25*x^56 + 36*x^55 + 49*x^54 + 64*x^53 + 81*x^52 + 100*x^51 + 121*x^50 + 144*x^49 + 168*x^48 + 192*x^47 + 216*x^46 + 240*x^45 + 264*x^44 + 288*x^43 + 312*x^42 + 336*x^41 + 359*x^40 + 380*x^39 + 399*x^38 + 416*x^37 + 431*x^36 + 444*x^35 + 455*x^34 + 464*x^33 + 471*x^32 + 476*x^31 + 478*x^30 + 476*x^29 + 471*x^28 + 464*x^27 + 455*x^26 + 444*x^25 + 431*x^24 + 416*x^23 + 399*x^22 + 380*x^21 + 359*x^20 + 336*x^19 + 312*x^18 + 288*x^17 + 264*x^16 + 240*x^15 + 216*x^14 + 192*x^13 + 168*x^12 + 144*x^11 + 121*x^10 + 100*x^9 + 81*x^8 + 64*x^7 + 49*x^6 + 36*x^5 + 25*x^4 + 16*x^3 + 9*x^2 + 4*x + 1
sage: S[0:10]
[[], [0], [1], [2], [3], [0, 1], [0, 2], [0, 3], [1, 0], [1, 2]]
sage: S[-1]
[0, 1, 0, 1, 0, 2, 0, 1, 0, 1, 2, 0, 1, 0, 2, 3, 2, 0, 1, 0, 1, 2, 0, 1, 0, 2, 3, 2, 0, 1, 0, 1, 2, 0, 1, 0, 2, 3, 2, 0, 1, 0, 1, 2, 0, 1, 0, 2, 3, 2, 0, 1, 0, 1, 2, 0, 1, 0, 2, 3]
The elements of S are handles to C objects from Semigroupe
:
sage: x = S.an_element()
sage: x
[0, 1, 2, 3]
Products are calculated by Semigroupe
:
sage: x * x
[0, 1, 0, 2, 0, 1, 3, 2]
Powering operations are handled by Sage:
sage: x^3
[0, 1, 0, 2, 0, 1, 0, 2, 3, 2, 0, 1]
sage: x^(10^10000)
Altogether, S is a full fledged Sage Coxeter group, which passes all the generic tests:
sage: TestSuite(S).run(verbose = True, skip = "_test_associativity")
And of course it works for general semigroups too, like the 0-Hecke monoid, and can further compute much more information about those, like the (Knuth-Bendix completion of the) relations between the generators:
sage: S.print_relations()
aa = 1
bb = 1
cb = bc
cc = 1
da = ad
db = bd
dd = 1
cac = aca
dcd = cdc
...
dcababcabacdcababcabacdcababcabacdcababcabacdc = cdcababcabacdcababcabacdcababcabacdcababcabacd
which contains the usual commutation + braid relations:
sage: from sage.combinat.j_trivial_monoids import *
sage: S = semigroupe.AutomaticSemigroup(W.simple_projections(), W.one(), by_action = True)
sage: S.cardinality()
48
sage: S.print_relations()
aa = a
bb = b
ca = ac
cc = c
bab = aba
cbcb = bcbc
cbacba = bcbacb
abacbacbc = 0
sage: W = CoxeterGroup(["A",3])
sage: S = semigroupe.AutomaticSemigroup(W.simple_projections(), W.one(), by_action = True, category = FiniteJTrivialMonoids())
sage: H = S.algebra(QQ)
sage: H.orthogonal_idempotents()
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Sage Days 20.5: Representation theory of finite monoids demo¶
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
LAGA, Villetaneuse, 2010/06/11: Sage demo¶
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Joint Sage-Combinat & Chevie workshop, Orsay: Sage and Sage-Combinat demo¶
- Demonstration: Basics
Category
- Demonstration: Documentation
- Demonstration: Algebraic constructions and categories
- Demonstration: Sage combines the power of multiple software
- Demonstration: Databases
- Demonstration: Cython: Python -> C
- Demonstration: Sage + GAP4 + GAP3 + Chevie + Semigroupe (experimental)
- Demonstration: Algebraic Combinatorics
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Affine Schubert Calculus Workshop, Toronto: Sage and Sage-Combinat demo¶
- Demonstration: Basics
- Demonstration: Plots (short version)
- Demonstration: Documentation
- Demonstration: Algebraic constructions and categories
- Demonstration: Sage + GAP4 + GAP3 + Chevie + Semigroupe (experimental)
- Demonstration: Cython: Python -> C
- Demonstration: Sage-Combinat
Tutorials¶
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
FPSAC‘10, San Francisco: Sage and Sage-Combinat demo¶
- Demonstration: Basics
- Demonstration: Plots (short version)
- Demonstration: Documentation
- Demonstration: Algebraic constructions and categories
- Demonstration: Sage + GAP4 + GAP3 + Chevie + Semigroupe (experimental)
- Demonstration: Cython: Python -> C
- Demonstration: Sage-Combinat
Tutorials¶
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
LACIM 2010, Montreal: Sage and Sage-Combinat demo¶
- Demonstration: Basics
- Demonstration: Plots (short version)
- Demonstration: Documentation
- Demonstration: Algebraic constructions and categories
- Demonstration: Cython: Python -> C
- Demonstration: Sage-Combinat
- Demonstration: A real life example, parallel testing of a conjecture on J-Trivial monoids using MuPAD (experimental)
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Nikolaus Conference 2010, Aachen: Sage-Combinat demo¶
sage: %hide
sage: pretty_print_default(False)
Tableaux and the like¶
sage: s = Permutation([5,3,2,6,4,8,9,7,1])
sage: s
sage: (p,q) = s.robinson_schensted()
sage: p.pp()
1 4 7 9
2 6 8
3
5
sage: p.row_stabilizer()
Permutation Group with generators [(), (7,9), (6,8), (4,7), (2,6), (1,4)]
Counting & the like¶
sage: Partitions(100000).cardinality()
Species:
sage: from sage.combinat.species.library import *
sage: o = var("o")
sage: BT = CombinatorialSpecies()
sage: Leaf = SingletonSpecies()
sage: BT.define(Leaf+(BT*BT))
sage: BT.isotypes([o]*5).list()
[o*(o*(o*(o*o))), o*(o*((o*o)*o)), o*((o*o)*(o*o)), o*((o*(o*o))*o), o*(((o*o)*o)*o), (o*o)*(o*(o*o)), (o*o)*((o*o)*o), (o*(o*o))*(o*o), ((o*o)*o)*(o*o), (o*(o*(o*o)))*o, (o*((o*o)*o))*o, ((o*o)*(o*o))*o, ((o*(o*o))*o)*o, (((o*o)*o)*o)*o]
Words¶
sage: m = WordMorphism('a->acabb,b->bcacacbb,c->baba')
sage: m.fixed_point('a')
word: acabbbabaacabbbcacacbbbcacacbbbcacacbbac...
Lattice points of polytopes¶
sage: A=random_matrix(ZZ,3,6,x=7)
sage: L=LatticePolytope(A)
sage: L.plot3d()
sage: L.npoints() # should be cardinality!
28
This example used PALP and J-mol
Graphs up to an isomorphism¶
sage: show(graphs(5, lambda G: G.size() <= 4))
Symmetric functions¶
Usual bases:
sage: Sym = SymmetricFunctions(QQ); Sym
Symmetric Functions over Rational Field
sage: Sym.inject_shorthands()
sage: m(( ( h[2,1] * ( 1 + 3 * p[2,1]) ) + s[2](s[3])))
Macdonald polynomials:
sage: J = MacdonaldPolynomialsJ(QQ)
sage: P = MacdonaldPolynomialsP(QQ)
sage: Q = MacdonaldPolynomialsQ(QQ)
sage: J
Macdonald polynomials in the J basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field
sage: P(J[2,2] + 3 * Q[3,1])
Root systems¶
sage: L = RootSystem(['A',2,1]).weight_space()
sage: L.plot(size=[[-1..1],[-1..1]],alcovewalks=[[0,2,0,1,2,1,2,0,2,1]])
sage: W = WeylGroup(["B", 3])
sage: W.cayley_graph(side = "left").plot3d(color_by_label = True)
GAP at work¶
sage: print W.character_table() # Thanks GAP!
CT1
2 4 4 3 3 4 3 1 1 3 4
3 1 . . . . . 1 1 . 1
1a 2a 2b 4a 2c 2d 6a 3a 4b 2e
X.1 1 1 1 1 1 1 1 1 1 1
X.2 1 1 1 -1 -1 -1 -1 1 1 -1
X.3 1 1 -1 -1 1 -1 1 1 -1 1
X.4 1 1 -1 1 -1 1 -1 1 -1 -1
X.5 2 2 . . -2 . 1 -1 . -2
X.6 2 2 . . 2 . -1 -1 . 2
X.7 3 -1 1 1 1 -1 . . -1 -3
X.8 3 -1 -1 -1 1 1 . . 1 -3
X.9 3 -1 -1 1 -1 -1 . . 1 3
X.10 3 -1 1 -1 -1 1 . . -1 3
sage: type(W.character_table())
sage: G = gap(W); G
sage: G.Ch
sage: T = G.CharacterTable(); T
sage: T.Irr()[10,10]
Coxeter3 at work¶
sage: W3 = CoxeterGroup(W, implementation="coxeter3")
sage: KL = matrix([ [ W3.kazhdan_lusztig_polynomial(u,v) if u.bruhat_le(v) else 0 for u in W3 ]
....: for v in W3])
sage: show(KL)
sage: W = WeylGroup(["C", 3, 1])
sage: W
Weyl Group of type ['C', 3, 1] (as a matrix group acting on the root space)
sage: W.from_reduced_word([1,2,3,0,3,0,3,2,1,3,3,2]).stanley_symmetric_function()
256*m[1, 1, 1, 1, 1, 1] + 128*m[2, 1, 1, 1, 1] + 64*m[2, 2, 1, 1] + 32*m[2, 2, 2] + 48*m[3, 1, 1, 1] + 24*m[3, 2, 1] + 8*m[3, 3] + 16*m[4, 1, 1] + 8*m[4, 2] + 4*m[5, 1]
Crystals¶
sage: latex.jsmath_avoid_list(['tikzpicture'])
sage: K = KirillovReshetikhinCrystal(['A',3,1], 2,2)
sage: G = K.digraph()
sage: G.set_latex_options(format = "dot2tex", edge_labels = True, color_by_label = {0:"black", 1:"blue", 2:"red", 3:"green"}, edge_options = lambda (u,v,label):({"backward":label ==0}))
sage: view(G, viewer="pdf", tightpage=True)
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Orsay, January 17-19 of 2011: Sage Demo and Tutorials¶
Main Sage Demonstration¶
- Demonstration: Basics
- Demonstration: Plots (short version)
- Demonstration: Documentation
- Demonstration: Sage combines the power of multiple software
- Demonstration: Databases
- Demonstration: Algebraic constructions and categories
- Demonstration: Cython: Python -> C
- Demonstration: Combinatorics (short)
- Demonstration: Algebraic Combinatorics
Other demonstrations¶
Tutorials¶
- Tutorial: Using the Sage notebook, navigating the help system, first exercises
- Tutorial: Enumerated sets
- Tutorial: Undegraduate Algebra and Calculus (Version française)
- Tutorial: Programming in Python and Sage
- Tutorial: Using Free Modules and Vector Spaces
- Tutorial: Implementing Algebraic Structures
- How to contribute to Sage
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
How to contribute to Sage¶
Sébastien Labbé¶
-
class
center
¶ - If the slide text doesn’t fit in your browser window, try decreasing the text size.Type
T
if you have trouble viewing this presentation.Sage Days 28
Orsay, France, January 17-19th 2011
Sage is distributed under the terms of the GNU General Public License version 2 (GPLv2) which provides four kinds of freedom:
- Freedom to run the program
- Freedom to access the code
- Freedom to redistribute the program to anyone
- Freedom to improve the software
While all users of Sage make use of the first freedom, in this talk, we will see how to appropriate the other three.
-
class
borderless
¶ - Find a bug
- Build the documentation
- Sage trac server
- Update the current patch
- Create a ticket
- Export a patch
- Clone your Sage
- Verify the patch
- Mercurial
- Upload the patch
- Enable Mercurial queues
- More on Mercurial queues
- Create an empty patch
- Dowload a patch
- Fix the bug
- Edit the series file
- View your changes
- Reviewing a patch
- Test the changes
- Positive review or Needs work
- Run tests
- Do some cleaning
That’s the easiest part. Choose one amongst this
made for Sage Days 28 or browse the
During this talk, instead of fixing a bug I am going to introduce one in the inverse method of a permutation.
In Sage, modifications are tracked on a web site called Sage trac. Every bug gets assigned a number. For instance, the number #10484 refers to the bug called Chinese remainder code raises an error when called with Python ints. On the ticket, one can see that:
- The bug was reported and solved by David Loeffler (UK) in December 2010.
- The ticket was positively reviewed by Robert Bradshaw (USA) and Mike Hansen.
- The solution was merged in
sage-4.6.2
by Jeroen Demeyer (Belgium) on January 11th 2011.
One can also look at the solution, download it, test it, etc.
In order to create a ticket:
- Create an account on http://trac.sagemath.org/sage_trac/register
- Login to your account
- Make sure the ticket does not already exists.
- Create ticket
- In the description field, explain how should someone else understand and/or reproduce the bug.
I create the imaginary ticket #12345 for introducing a useless print
statement in the method that computes the inverse of a permutation.
- Clone Sage and create your branch (Do it right now because it might take some time)
sage -clone slabbe
This creates a new directory called sage-slabbe
in the devel
repository:
slabbe@pol ~/Applications/sage-4.6.1/devel $ ls -l
drwxr-xr-x 2 slabbe staff 68 14 jan 03:59 old/
lrwxr-xr-x 1 slabbe staff 9 18 jan 15:01 sage -> sage-main/
drwxr-xr-x 23 slabbe staff 782 18 jan 01:42 sage-main/
drwxr-xr-x 24 slabbe staff 816 17 jan 01:50 sage-slabbe/
lrwxr-xr-x 1 slabbe staff 11 14 jan 03:42 sagenb -> sagenb-main/
drwxr-xr-x 21 slabbe staff 714 14 jan 03:41 sagenb-main/
-
class
borderless
Build the main branch | Build my branch slabbe | Print the current branch |
sage -b main |
sage -b slabbe |
sage -branch |
Sage uses the program Mercurial ( hg or sage -hg ) to manage all of its source code. Mercurial stores the evolution of every single file of Sage since the beginning.
Since I am too lazy to write sage -hg everytime I use Mercurial, I added
the following line to my ~/.bashrc
file:
alias hg='sage -hg'
I verify that it works:
slabbe@pol ~ $ hg --version
Mercurial Distributed SCM (version 1.6.4)
Copyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- hg log
- Print the revision history of the specified files or the entire project.
slabbe@pol ~/Applications/sage-4.6.1/devel/sage-main $ hg log
changeset: 15205:f24ce048fa66
tag: tip
user: Jeroen Demeyer
date: Tue Jan 11 08:10:26 2011 +0100
summary: 4.6.1
...
changeset: 0:039f6310c6fe
user: tornaria
date: Sat Feb 11 01:13:08 2006 +0000
summary: [project @ original sage-0.10.12]
- hg update
- Update the repository’s working directory to the specified changeset.
Mercurial queues is an extension to Mercurial that allows one to easily
work with collections of patches. To allow Mercurial queues, edit (or create)
the file ~/.hgrc
and make sure it contains the line hgext.mq = in
the extensions section:
[ui]
username = Sebastien Labbe <hidden adress email>
[extensions]
hgext.mq =
color =
[alias]
qstatus = status --rev -2:.
Warning
The line hgext.mq=
is an indispensable for the next steps.
cd to your branch:
cd Applications/sage-4.6.1/devel/sage-slabbe
Create a new empty patch:
hg qnew trac_12345-add_useless_print-sl.patch
Find the file containing the bug
Personnally I found the file permutation.py
here:
sage-4.6.1/devel/sage-slabbe/sage/combinat/permutation.py
Find the solution to the bug
Edit the source code accordingly, save and quit
Now, you may use the following Mercurial commands to look at your local changes.
hg status shows changed files since last hg qnew (or hg qrefresh):
slabbe@pol ~/Applications/sage-4.6.1/devel/sage-combinat/sage/combinat $ hg status
M sage/combinat/permutation.py
hg diff shows differences since last hg qnew (or hg qrefresh)
slabbe@pol ~/Applications/sage-4.6.1/devel/sage-combinat/sage/combinat $ hg diff
diff --git a/sage/combinat/permutation.py b/sage/combinat/permutation.py
--- a/sage/combinat/permutation.py
+++ b/sage/combinat/permutation.py
@@ -1208,6 +1208,7 @@ class Permutation_class(CombinatorialObj
sage: Permutation([2, 4, 1, 5, 3]).inverse()
[3, 1, 5, 2, 4]
"""
+ print "YO !!!! Let's inverse some permutations !!!"
w = range(len(self))
for i,j in enumerate(self):
w[j-1] = i+1
- Build sage
sage -b
- Verify the effects of the modification
- Run
sage
sage: p = Permutation([4,3,2,5,1])
sage: p.inverse()
YO !!!! Let's inverse some permutations !!!
[5, 3, 2, 1, 4]
That’s great: we are now able to modify Sage.
- Make sure that all examples in the source code still work
sage -t <files>
slabbe@pol ~/Applications/sage-4.6.1/devel/sage-slabbe/sage/combinat $ sage -t permutation.py
sage -t "devel/sage-slabbe/sage/combinat/permutation.py"
**********************************************************************
File "/Users/slabbe/Applications/sage-4.6.1/devel/sage-slabbe/sage/combinat/permutation.py", line 1206:
sage: Permutation([3,8,5,10,9,4,6,1,7,2]).inverse()
Expected:
[8, 10, 1, 6, 3, 7, 9, 2, 5, 4]
Got:
YO !!!! Let's inverse some permutations !!!
[8, 10, 1, 6, 3, 7, 9, 2, 5, 4]
----------------------------------------------------------------------
The following tests failed:
sage -t "devel/sage-slabbe/sage/combinat/permutation.py"
Total time for all tests: 10.4 seconds
If tests failed, one should edit files again…
Build the documentation and make sure there are no errors or warnings:
sage -b && sage -docbuild reference html
Open the html version of documentation in your browser and make sure the documentation looks OK:
open ~/Applications/sage-4.6.1/devel/sage/doc/output/html/en/reference/sage/combinat/permutation.html
When the bug is fixed, once we made sure every tests pass and that the documentation builds fine, then we can update the current patch with hg qrefresh to reflect the changes:
hg qrefresh
No changes are shown anymore by hg status or hg diff:
hg status
hg diff
Modifications are now in the patch. See hg qstatus or hg qdiff:
hg qstatus
hg qdiff
Add a commit message to the patch:
hg qrefresh -m "#12345: add a useless print in the inverse method of a permutation"
Export the patch with hg export:
hg export trac_12345-add_useless_print-sl.patch >
~/Documents/tmp/trac_12345-add_useless_print-sl.patch
The command hg export also adds informations in the patch (author name, date, …).
Note
Personnaly, I added the following alias to my ~/.bashrc
:
alias qtoptotmp='hg export `hg qtop` > ~/Documents/tmp/`hg qtop`'
Here is an example of a patch exported by Mercurial for the imaginary ticket #12345. It contains information about the author, the date, the commit message we just wrote and finally the complete diff.
trac_12345-add_useless_print-sl.patch:
# HG changeset patch
# User Sebastien Labbe <hidden adress email>
# Date 1295311529 -3600
# Node ID 4a6379cf0c965e1ce309846cbcb9f864932a3b6c
# Parent 83e5e45a8935ac627c45ed14042bbebafeb1a800
#12345: add a useless print in the inverse method of a permutation
diff --git a/sage/combinat/permutation.py b/sage/combinat/permutation.py
--- a/sage/combinat/permutation.py
+++ b/sage/combinat/permutation.py
@@ -1208,6 +1208,7 @@ class Permutation_class(CombinatorialObj
sage: Permutation([2, 4, 1, 5, 3]).inverse()
[3, 1, 5, 2, 4]
"""
+ print "YO !!!! Let's inverse some permutations !!!"
w = range(len(self))
for i,j in enumerate(self):
w[j-1] = i+1
Upload the patch on Sage trac
You can mention things like “tested on sage-4.6.1” in the text box when uploading the ticket.
Make sure the patch was correctly uploaded by looking at it directly on the web page.
Set the ticket to needs review
You may ask somebody to review your ticket.
Other useful Mercurial commands when patches multiplies:
- hg qnew
- Create a new patch
- hg qnew
- …
- hg qpop
- Move a patch from the applied stack to the unapplied one
- hg qpush
- Move a patch from the unapplied stack to the applied one
- hg qtop
- Show the current patch
- hg qseries
- Print all of the patches in order
A feature available on a Sage Trac ticket interests you? You want to review a ticket?
Download a patch!
Insert a patch into the series after the last applied patch with hg qimport:
hg qimport ~/Downloads/trac_65321-nice-feature-AA.patch
Warning
Do NOT use the command hg import as it will import the changes in the current patch.
You can change the order in which the patches are applied. To do so, simply edit the series file:
slabbe@pol ~/Applications/sage-4.6.1/devel/sage-slabbe $ cd .hg/patches/
slabbe@pol ~/Applications/sage-4.6.1/devel/sage-slabbe/.hg/patches $ vim series
Make sure the patch you are reviewing is the first patch to be applied:
slabbe@pol ~/Applications/sage-4.6.1/devel/sage-slabbe/.hg/patches $ cat series
trac_65321-nice-feature-AA.patch
A.patch
B.patch
C.patch
Warning
Visit the Reviewing a patch Section of the Sage Developer’s Guide. Also, make sure you read William Stein’s blog post about reviewing a Sage trac ticket.
- Make sure the patch applies on Sage without conflicts:
hg qpush
andhg qpop
Experiment the functionality proposed in the patch.
- Make sure the bug described in the ticket is fixed.
- Make sure the patch does not introduce any new bug.
- Run tests on the affected files.
sage -t <affected_files>
- Test the entire Sage library.
sage --testall --long
- Ensure that the documentation builds fine:
sage -docbuild reference html
- Check for full 100% doctest coverage:
sage -coverage <file>
Once you’ve tested the patch, report any failures on the Trac page for the ticket. Make suggestions about simplifying the code or fixing typos you noticed.
Three cases may happen:
- Needs work
- Mark it as needs work if there is anything to do.
- Positive review
- Otherwise, mark it as positive review, and mention in a comment all the things you checked.
- Delegate
- If you don’t feel experienced enough for that, add a comment on the Trac page explaining what you have checked, what the results were, and that you think someone more experienced should take a look.
Note
Delete an (unapplied) patch from the queue:
hg qdelete trac_65321-nice-feature-AA.patch
Erase your branch. Of course, do this only if you don’t care about your local changes:
rm -rf sage-slabbe
Reviewing a Sage trac ticket, William Stein’s blog post, October 31, 2010.
This talk was generated
- by Docutils
- from ReStructuredText source
- to a Simple Standards-based Slide Show System (S5) format.
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Wolfville, May 2-6 of 2011: Sage Demo and Tutorials¶
Main Sage Demonstration¶
Other demonstrations¶
Tutorials¶
- Tutorial: Using the Sage notebook, navigating the help system, first exercises
- Tutorial: Enumerated sets
- Tutorial: Undegraduate Algebra and Calculus (Version française)
- Tutorial: Programming in Python and Sage
- Tutorial: Using Free Modules and Vector Spaces
- Tutorial: Implementing Algebraic Structures
- Tutorial: How to contribute to Sage (outdated)
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
SMAI 2011, Guidel, 23 mai 2011: Sage pour le calcul symbolique, algébrique et combinatoire¶
Cet exposé complète le tutoriel d’introduction à Sage en explorant plus en avant les possibilités de Sage pour le calcul symbolique, algébrique et combinatoire. Nous mettrons en particulier en avant l’intérêt d’avoir une large palette d’outils à l’intérieur d’un même système de calcul, ainsi que la démarche de Sage visant à modéliser les mathématiques au plus près. On peut par exemple manipuler non seulement des matrices, mais ausi des systèmes d’équations, des morphismes ou des sous-espaces vectoriels. On peut aussi modéliser des connaissances mathématiques comme: «L’anneau des polynômes en une variable sur un corps est euclidien». Cette modélisation s’appuie sur un pont naturel entre théorie des catégories d’un côté et programmation orientée objet (un des multiples paradigmes de programmation du langage Python) de l’autre.
Si le temps le permet, nous montrerons un exemple réel dans un contexte de recherche illustrant des constructions avancées et la combinaison de multiples outils de calculs.
Calcul symbolique¶
Le coeur des systèmes comme Maple et Maxima est le calcul sur les expressions, avec sa simplicité pour les nouveaux venus et sa souplesse. Modulo la déclaration explicite des variables et des petites variantes de syntaxe, l’utilisateur casuel retrouvera ses petits.
Une expression:
sage: f = cos(x)^6 + sin(x)^6 + 3 * sin(x)^2 * cos(x)^2; f
sin(x)^6 + cos(x)^6 + 3*sin(x)^2*cos(x)^2
Simplifions-la:
sage: f.simplify_trig()
1
Une sommation définie:
sage: var('n,k')
(n, k)
sage: sum(binomial(n, k) * factorial(k) / factorial(n+1+k), k, 0, n)
1/2*sqrt(pi)/factorial(n + 1/2)
sage: pretty_print(_)
<html><span class="math">\newcommand{\Bold}[1]{\mathbf{#1}}\frac{\sqrt{\pi}}{2 \, \left(n + \frac{1}{2}\right)!}</span></html>
Calcul de \(\lim\limits_{x\rightarrow \frac{\pi}{4} }\dfrac{\cos\left(\frac{\pi}{4}-x \right)-\tan x }{1-\sin\left(\frac{\pi}{4}+x \right)}\):
sage: f(x) = (cos(pi/4-x)-tan(x)) / (1-sin(pi/4 + x))
sage: limit(f(x), x = pi/4, dir='minus')
+Infinity
Calcul, selon la valeur de \(x\), de \(\int_0^{\infty} \frac{x \cos u}{u^2+x^2} du\):
sage: var('u')
u
sage: f = x * cos(u) / (u^2 + x^2)
sage: assume(x>0)
sage: f.integrate(u, 0, infinity)
1/2*pi*e^(-x)
sage: forget(); assume(x<0); f.integrate(u, 0, infinity)
-1/2*pi*e^x
L’arithmétique est gérée en interne (pynac) et le reste est délégué à Maxima. En relatif, cet aspect reste un des points faibles de Sage.
Polynômes¶
Chaque fois que possible, Sage privilégie la modélisation explicite de la structure algébrique dans laquelle on souhaite mener le calcul. Cela permet de travailler naturellement et efficacement dans des constructions algébriques plus avancées:
sage: Z2 = GF(2); Z2
Finite Field of size 2
sage: P = Z2['x']; P
Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL)
sage: M = MatrixSpace(P, 3); M
Full MatrixSpace of 3 by 3 dense matrices over Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL)
sage: m = M.random_element(); m # random
[ x + 1 x^2 x^2]
[ x x^2 + x x + 1]
[ x^2 + 1 x^2 + x + 1 x^2]
sage: m.parent()
Full MatrixSpace of 3 by 3 dense matrices over Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL)
sage: m * (m-1) # random
[ x^4 + x^3 + x x^3 + x^2 x^4 + x^2]
[ x^2 + x + 1 x^4 + x + 1 x^3 + x^2 + 1]
[ x^4 x^4 + x^3 + x^2 + 1 x^3 + 1]
sage: m.det() # random
x^6 + x^5 + x^3 + x^2 + x + 1
et d’y traiter rigoureusement, par exemple, les questions de factorisation:
sage: p = 54*x^4+36*x^3-102*x^2-72*x-12
sage: p.factor()
6*(3*x + 1)^2*(x^2 - 2)
sage: for A in [ZZ, QQ, ComplexField(16), QQ[sqrt(2)], GF(5)]:
....: print A, ":"; print A['x'](p).factor()
Integer Ring :
2 * 3 * (3*x + 1)^2 * (x^2 - 2)
Rational Field :
(54) * (x + 1/3)^2 * (x^2 - 2)
Complex Field with 16 bits of precision :
(54.00) * (x - 1.414) * (x + 0.3333)^2 * (x + 1.414)
Number Field in sqrt2 with defining polynomial x^2 - 2 :
(54) * (x - sqrt2) * (x + sqrt2) * (x + 1/3)^2
Finite Field of size 5 :
(4) * (x + 2)^2 * (x^2 + 3)
Algèbre linéaire¶
Dans les exemples ci-dessous, nous ferons de l’algèbre linéaire sur le corps fini \(\ZZ/7\ZZ\):
sage: K = GF(7); K
Finite Field of size 7
sage: list(K)
[0, 1, 2, 3, 4, 5, 6]
Nous avons choisi ce corps à titre d’illustration pour avoir des résultats lisibles. On aurait pu prendre des coefficients entiers, rationnels, ou numériques à plus ou moins haute précision. Les aspects numériques seront abordés plus en détail dans l’exposé suivant. Notons au passage que, même en calcul exact, il est possible de manipuler de relativement grosses matrices:
sage: M = random_matrix(K, 10000, sparse=True, density=3/10000)
sage: M.rank() # random
9278
Définissons donc une matrice à coefficients dans \(\ZZ/7\ZZ\):
sage: A = matrix(K, 4, [5,5,4,3,0,3,3,4,0,1,5,4,6,0,6,3]); A
[5 5 4 3]
[0 3 3 4]
[0 1 5 4]
[6 0 6 3]
Calculons le polynôme caractéristique de cette matrice:
sage: P = A.characteristic_polynomial(); P
x^4 + 5*x^3 + 6*x + 2
On vérifie le théorème de Cayley-Hamilton sur cet exemple:
sage: P(A)
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
Notons que l’information sur le corps de base est préservée:
sage: P.parent()
Univariate Polynomial Ring in x over Finite Field of size 7
ce qui influe directement sur la factorisation de ce polynôme:
sage: factor(P)
(x + 3) * (x + 6) * (x + 5)^2
sage: factor(x^4 + 5*x^3 + 6*x + 2)
x^4 + 5*x^3 + 6*x + 2
Le calcul ci-dessus nous donne les valeurs propres: -3=4,-6=1 et -5=2. Quels sont les espaces propres?
sage: A.eigenspaces_left()
[
(4, Vector space of degree 4 and dimension 1 over Finite Field of size 7
User basis matrix:
[1 4 6 1]),
(1, Vector space of degree 4 and dimension 1 over Finite Field of size 7
User basis matrix:
[1 3 3 4]),
(2, Vector space of degree 4 and dimension 2 over Finite Field of size 7
User basis matrix:
[1 0 2 3]
[0 1 6 0])
]
Récupérons ces espaces propres:
sage: E = dict(A.eigenspaces_left())
sage: E[2]
Vector space of degree 4 and dimension 2 over Finite Field of size 7
User basis matrix:
[1 0 2 3]
[0 1 6 0]
E[2]
n’est pas une liste de vecteurs ni une matrice, mais un
objet qui modélise l’espace propre \(E_2\), comme le sous-espace de
\((\ZZ/7\ZZ)^4\) décrit par sa base échelon réduite. On peut donc lui
poser des questions:
sage: E[2].dimension()
2
sage: E[2].basis()
[
(1, 0, 2, 3),
(0, 1, 6, 0)
]
sage: V = E[2].ambient_vector_space(); V
Vector space of dimension 4 over Finite Field of size 7
Voire faire des calculs avec:
sage: E[2] + E[4]
Vector space of degree 4 and dimension 3 over Finite Field of size 7
Basis matrix:
[1 0 0 0]
[0 1 0 5]
[0 0 1 5]
sage: v = V([1,2,0,3])
sage: v in E[2]
True
sage: E[2].echelon_coordinates(v)
[1, 2]
sage: E[2].is_subspace(E[4])
False
sage: E[2].is_subspace(V)
True
sage: Q = V/E[2]; Q
Vector space quotient V/W of dimension 2 over Finite Field of size 7 where
V: Vector space of dimension 4 over Finite Field of size 7
W: Vector space of degree 4 and dimension 2 over Finite Field of size 7
User basis matrix:
[1 0 2 3]
[0 1 6 0]
sage: Q( V([0,0,0,1]) )
(2, 4)
On veut maintenant manipuler \(A\) comme un morphisme sur \(V\):
sage: phi = End(V)(A); phi
Free module morphism defined by the matrix
[5 5 4 3]
[0 3 3 4]
[0 1 5 4]
[6 0 6 3]
Domain: Vector space of dimension 4 over Finite Field of size 7
Codomain: Vector space of dimension 4 over Finite Field of size 7
sage: v = V.an_element()
sage: v
(1, 0, 0, 0)
sage: phi(v)
(5, 5, 4, 3)
sage: (phi^-1)(v)
(1, 2, 3, 4)
sage: phi^4 + 5*phi^3 + 6*phi + 2
Free module morphism defined by the matrix
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
Domain: Vector space of dimension 4 over Finite Field of size 7
Codomain: Vector space of dimension 4 over Finite Field of size 7
sage: (phi - 1).image()
Vector space of degree 4 and dimension 3 over Finite Field of size 7
Basis matrix:
[1 0 0 0]
[0 1 0 5]
[0 0 1 5]
sage: (phi - 1).kernel() == E[1]
True
sage: phi.restrict(E[2])
Free module morphism defined by the matrix
[2 0]
[0 2]
Domain: Vector space of degree 4 and dimension 2 over Finite Field of ...
Codomain: Vector space of degree 4 and dimension 2 over Finite Field of ...
- « Mathematics is the art of reducing any problem to linear algebra » William Stein
- Il serait en principe suffisant d’implanter l’algèbre linéaire sur les matrices
- Le pari de Sage: modéliser au plus près les mathématiques, pour que l’utilisateur ou le programmeur puisse s’exprimer dans le langage adapté au problème considéré.
Combinatoire¶
Selon le même principe, lorsque l’on demande toutes les partitions de l’entier 5, le résultat est un objet qui modélise cet ensemble:
sage: P = Partitions(5); P
Partitions of the integer 5
Pour obtenir la liste de ces objets, il faut le demander explicitement:
sage: P.list()
[[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]]
Cela permet de manipuler formellement des grands ensembles:
sage: Partitions(100000).cardinality()
27493510569775696512677516320986352688173429315980054758203125984302147328114964173055050741660736621590157844774296248940493063070200461792764493033510116079342457190155718943509725312466108452006369558934464248716828789832182345009262853831404597021307130674510624419227311238999702284408609370935531629697851569569892196108480158600569421098519
Et de calculer paresseusement avec. Ici, on tire au hasard une main de cinq cartes à jouer:
sage: Symboles = Set(["Coeur", "Carreau", "Pique", "Trefle"])
sage: Valeurs = Set([2, 3, 4, 5, 6, 7, 8, 9, 10, "Valet", "Dame", "Roi", "As"])
sage: Cartes = CartesianProduct(Valeurs, Symboles).map(tuple)
sage: Mains = Subsets(Cartes, 5)
sage: Mains.cardinality()
2598960
sage: Mains.random_element() # random
{(2, 'Coeur'), (6, 'Pique'), (10, 'Carreau'), ('As', 'Pique'), ('Valet', 'Coeur')}
et là on manipule un mot infini défini comme point fixe d’un morphisme:
sage: m = WordMorphism('a->acabb,b->bcacacbb,c->baba')
sage: m.fixed_point('a')
word: acabbbabaacabbbcacacbbbcacacbbbcacacbbac...
Probas?¶
Une session rêvée:
sage: X = random_variable(BernouilliDistribution(1/2))
sage: Y = random_variable(BinomialDistribution(3, 1/3))
sage: Z = X + 2*Y
sage: Z.mean()
sage: Z.variance()
sage: plot(Z.distribution())
sage: event = ( Z <= 1 )
sage: event.probability()
- Ce type de modélisation serait-il utile?
- Pour l’enseignement?
- Pour fournir des modèles exacts pour des tests statistiques? (à la StatXact)
- Implantable à partir des fondamentaux de Sage? (combinatoire, intégration, …)?
Combinatoire algébrique¶
Et pour faire joli, un système de racine affine et un groupe de Weyl:
sage: L = RootSystem(['A',2,1]).weight_space()
sage: L.plot(size=[[-1..1],[-1..1]], alcovewalks=[[0,2,0,1,2,1,2,0,2,1]])
sage: W = WeylGroup(["B", 3])
sage: W.cayley_graph(side = "left").plot3d(color_by_label = True)
Graphes¶
Nous montrons maintenant quelques fonctionnalités de Sage autour des graphes:
sage: g = graphs.ChvatalGraph()
sage: g.show()
sage: c = g.hamiltonian_cycle()
sage: g.show(edge_colors = {"red": c.edges()} )
Grâce à GAP et à (un port de) Nauty, on peut étudier de près les questions de symétries et d’isomorphisme dans les graphes. Voici tous les graphes simples sur cinq sommets avec moins de quatre arêtes:
sage: show(graphs(5, lambda G: G.size() <= 4))
Le groupe de symétries (automorphismes) du graphe de Petersen:
sage: petersen = graphs.PetersenGraph()
sage: petersen.show()
sage: group = petersen.automorphism_group(); group
Permutation Group with generators [(3,7)(4,5)(8,9), (2,6)(3,8)(4,5)(7,9), (1,4,5)(2,3,8,6,9,7), (1,10)(2,4,6,5)(3,9,8,7)]
Et quelques-unes de ses propriétés:
sage: group.cardinality()
120
sage: group.character_table()
[ 1 1 1 1 1 1 1]
[ 1 -1 1 -1 1 -1 1]
[ 4 -2 0 1 1 0 -1]
[ 4 2 0 -1 1 0 -1]
[ 5 1 1 1 -1 -1 0]
[ 5 -1 1 -1 -1 1 0]
[ 6 0 -2 0 0 0 1]
sage: [N.cardinality() for N in group.normal_subgroups()]
[1, 60, 120]
sage: group.is_isomorphic(SymmetricGroup(5))
True
Calculons quelques propriétés classiques de ce graphe. Il faut trois couleurs pour le colorier:
sage: petersen.chromatic_number()
3
sage: petersen.show(partition=petersen.coloring())
Mais ce n’est cependant pas un graphe parfait:
sage: petersen.is_perfect()
False
Tant que l’on ne supprime pas plus de quatre sommets ou quatre arêtes, le graphe reste connexe:
sage: petersen.vertex_connectivity()
3
sage: petersen.edge_connectivity()
3
Programmation linéaire¶
La plupart des calculs précédents se ramènent à de la programmation linéaire en entiers. Pour commencer, nous montrons comment résoudre le programme linéaire suivant:
\(\begin{array}{lrrrl}\text{Max : } & x&+ y &- 3z\\\text{Tel que : }& x&+2y &&\leq 4 \\ & &- y &+ 5z &\leq 8\\ \end{array}\)
à l’aide de Sage:
sage: p = MixedIntegerLinearProgram()
sage: x, y, z = p['x'], p['y'], p['z']
sage: p.set_objective ( x + y + 3*z )
sage: p.add_constraint( x + 2*y <= 4 )
sage: p.add_constraint( - y + 5*z <= 8 )
sage: p.solve()
8.800000000...
sage: p.get_values(x), p.get_values(y), p.get_values(z)
Nous resolvons maintenant le même système en imposant que \(z\) soit entier:
sage: p.set_integer(z)
sage: p.solve()
8.0
sage: p.get_values(x), p.get_values(y), p.get_values(z)
Maintenant, nous montrons comment Sage calcule un ensemble indépendant maximal du graphe de Petersen:
sage: I = petersen.independent_set(); I
[0, 3, 6, 7]
sage: petersen.show(vertex_colors = {'red' : I})
La recherche d’un ensemble indépendant maximal peut s’encoder en le programme linéaire en nombres entiers suivant:
\(\begin{array}{ll}\text{Max : } & \displaystyle\sum_{v\in E(G)} b_v \\\text{Tel que : } & \forall u,v\in E(G),\ b_u+b_v \leq 1 \\ & b_v\text{ variable binaire }\end{array}\)
Ce qui en Sage donne:
sage: LP = MixedIntegerLinearProgram(maximization=True)
sage: b = LP.new_variable()
sage: LP.set_objective(sum([b[v] for v in petersen]))
sage: for (u,v) in petersen.edges(labels=None): # For any edge, we define a constraint
....: LP.add_constraint(b[u]+b[v],max=1)
sage: LP.set_binary(b)
On trouve alors un indépendant de taille quatre:
sage: LP.solve()
4.0
sage: b_sol = LP.get_values(b)
sage: print b_sol
{0: 0.0, 1: 1.0, 2: 0.0, 3: 0.0, 4: 1.0, 5: 0.0, 6: 0.0, 7: 1.0, 8: 1.0, 9: 0.0}
sage: I = [ v for v in petersen.vertices() if b_sol[v] ]; I
[1, 4, 7, 8]
sage: petersen.show(vertex_colors = {'red' : I})
Pour finir, on manipule l’ensemble de tous les points entiers d’un polytope:
sage: A = random_matrix(ZZ,3,6,x=7)
sage: L = LatticePolytope(A)
sage: L.plot3d()
Un grand merci au passage à Nathann Cohen qui a fourni une bonne part des exemples et fonctionnalités ci-dessus.
Catégories¶
Comme on l’a vu, Sage a une large gamme de fonctionnalités, développées par des enseignants, chercheurs et volontaires d’horizons très différents. Il intègre de plus des outils dont les approches sont variées. Comment s’assurer qu’il conserve une certaine cohérence interne?
Revenons sur notre corps fini:
sage: K = GF(7); K
Finite Field of size 7
Toujours dans l’idée de modéliser les mathématiques au plus près, Sage a des informations sur la structure mathématique de \(K\):
sage: K.category()
Category of finite fields
Voilà ce qu’il peut en déduire:
sage: graph = K.category().category_graph()
sage: graph.set_latex_options(format="dot2tex")
sage: view(graph, viewer="pdf", tightpage=True)
En quoi est-ce utile?
Cohérence des spécifications:
sage: K.cardinality() 7 sage: Partitions(10).cardinality() 42 sage: EllipticCurve([GF(5)(0),0,1,-1,0]).cardinality() 8
Cela n’est cependant pas encore parfaitement au point:
sage: LatticePolytope(A).npoints() # random 4
Partage de code générique:
sage: K.multiplication_table(names = 'elements') * 0 1 2 3 4 5 6 +-------------- 0| 0 0 0 0 0 0 0 1| 0 1 2 3 4 5 6 2| 0 2 4 6 1 3 5 3| 0 3 6 2 5 1 4 4| 0 4 1 5 2 6 3 5| 0 5 3 1 6 4 2 6| 0 6 5 4 3 2 1 sage: K.multiplication_table.__module__ 'sage.categories.magmas'
La hierarchie de catégorie est traduite automatiquement en une hierarchie de classes:
sage: for cls in K.__class__.mro(): ....: print cls <class 'sage.rings.finite_rings.finite_field_prime_modn.FiniteField_prime_modn_with_category'> ... <class 'sage.categories.finite_fields.FiniteFields.parent_class'> <class 'sage.categories.fields.Fields.parent_class'> <class 'sage.categories.euclidean_domains.EuclideanDomains.parent_class'> <class 'sage.categories.principal_ideal_domains.PrincipalIdealDomains.parent_class'> <class 'sage.categories.unique_factorization_domains.UniqueFactorizationDomains.parent_class'> <class 'sage.categories.gcd_domains.GcdDomains.parent_class'> ... <class 'sage.categories.magmas.Magmas.parent_class'> ... <class 'sage.categories.finite_sets.FiniteSets.parent_class'> ... <type 'object'>
Partage de tests génériques:
sage: TestSuite(K).run(verbose=True) running ._test_additive_associativity() . . . pass running ._test_an_element() . . . pass running ._test_associativity() . . . pass running ._test_category() . . . pass running ._test_distributivity() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass running ._test_eq() . . . pass running ._test_not_implemented_methods() . . . pass running ._test_pickling() . . . pass pass running ._test_elements_eq() . . . pass running ._test_enumerated_set_contains() . . . pass running ._test_enumerated_set_iter_cardinality() . . . pass running ._test_enumerated_set_iter_list() . . . pass running ._test_eq() . . . pass running ._test_len() . . . pass running ._test_not_implemented_methods() . . . pass running ._test_one() . . . pass running ._test_pickling() . . . pass running ._test_prod() . . . pass running ._test_some_elements() . . . pass running ._test_zero() . . . pass
A demonstration of Sage + GAP4 + GAP3 + Chevie + Semigroupe¶
Let us create the Coxeter group W:
sage: W = CoxeterGroup(["H",4]); W
Permutation Group with generators [(3,8)(4,64)(7,12)(10,14)(11,16)(13,18)(15,20)(17,22)(19,23)(21,26)(24,27)(25,29)(28,30)(31,33)(34,36)(37,39)(40,43)(42,46)(45,48)(47,50)(49,52)(51,53)(59,60)(63,68)(67,72)(70,74)(71,76)(73,78)(75,80)(77,82)(79,83)(81,86)(84,87)(85,89)(88,90)(91,93)(94,96)(97,99)(100,103)(102,106)(105,108)(107,110)(109,112)(111,113)(119,120), (2,7)(3,63)(4,8)(5,10)(6,11)(9,13)(15,17)(19,21)(20,24)(22,27)(23,28)(26,30)(29,32)(33,35)(36,38)(37,40)(39,42)(41,45)(43,46)(44,47)(52,54)(53,55)(58,59)(62,67)(64,68)(65,70)(66,71)(69,73)(75,77)(79,81)(80,84)(82,87)(83,88)(86,90)(89,92)(93,95)(96,98)(97,100)(99,102)(101,105)(103,106)(104,107)(112,114)(113,115)(118,119), (1,5)(2,62)(3,7)(6,9)(8,12)(11,15)(13,17)(16,20)(18,22)(21,25)(26,29)(28,31)(30,33)(32,35)(34,37)(36,39)(38,41)(42,45)(46,48)(47,49)(50,52)(55,56)(57,58)(61,65)(63,67)(66,69)(68,72)(71,75)(73,77)(76,80)(78,82)(81,85)(86,89)(88,91)(90,93)(92,95)(94,97)(96,99)(98,101)(102,105)(106,108)(107,109)(110,112)(115,116)(117,118), (1,61)(2,6)(5,9)(7,11)(10,13)(12,16)(14,18)(15,19)(17,21)(20,23)(22,26)(24,28)(27,30)(31,34)(33,36)(35,38)(41,44)(45,47)(48,50)(49,51)(52,53)(54,55)(56,57)(62,66)(65,69)(67,71)(70,73)(72,76)(74,78)(75,79)(77,81)(80,83)(82,86)(84,88)(87,90)(91,94)(93,96)(95,98)(101,104)(105,107)(108,110)(109,111)(112,113)(114,115)(116,117)]
It is constructed as a group of permutations, from root data given by GAP3+Chevie (thanks to Franco’s interface):
sage: W._gap_group
CoxeterGroup("H",4)
sage: (W._gap_group).parent()
Gap3
with operations on permutations implemented in Sage:
sage: W.an_element()^3
(3,8)(4,64)(7,12)(10,14)(11,16)(13,18)(15,20)(17,22)(19,23)(21,26)(24,27)(25,29)(28,30)(31,33)(34,36)(37,39)(40,43)(42,46)(45,48)(47,50)(49,52)(51,53)(59,60)(63,68)(67,72)(70,74)(71,76)(73,78)(75,80)(77,82)(79,83)(81,86)(84,87)(85,89)(88,90)(91,93)(94,96)(97,99)(100,103)(102,106)(105,108)(107,110)(109,112)(111,113)(119,120)
and group operations implemented in GAP 4:
sage: len(W.conjugacy_classes_representatives())
34
sage: W.cardinality()
14400
Now, assume we want to do intensive computations on this group, requiring heavy access to the left and right Cayley graphs (e.g. Bruhat interval calculations, representation theory, …). Then we can use Jean-Eric Pin’s Semigroupe, a software written in C:
sage: S = semigroupe.AutomaticSemigroup(W.semigroup_generators(), W.one(),
....: category = CoxeterGroups().Finite())
The following triggers the full expansion of the group and its Cayley graph in memory:
sage: S.cardinality()
14400
And we can now iterate through the elements, in length-lexicographic order w.r.t. their reduced word:
sage: var('t')
t
sage: sum( t^p.length() for p in S)
t^60 + 4*t^59 + 9*t^58 + 16*t^57 + 25*t^56 + 36*t^55 + 49*t^54 + 64*t^53 + 81*t^52 + 100*t^51 + 121*t^50 + 144*t^49 + 168*t^48 + 192*t^47 + 216*t^46 + 240*t^45 + 264*t^44 + 288*t^43 + 312*t^42 + 336*t^41 + 359*t^40 + 380*t^39 + 399*t^38 + 416*t^37 + 431*t^36 + 444*t^35 + 455*t^34 + 464*t^33 + 471*t^32 + 476*t^31 + 478*t^30 + 476*t^29 + 471*t^28 + 464*t^27 + 455*t^26 + 444*t^25 + 431*t^24 + 416*t^23 + 399*t^22 + 380*t^21 + 359*t^20 + 336*t^19 + 312*t^18 + 288*t^17 + 264*t^16 + 240*t^15 + 216*t^14 + 192*t^13 + 168*t^12 + 144*t^11 + 121*t^10 + 100*t^9 + 81*t^8 + 64*t^7 + 49*t^6 + 36*t^5 + 25*t^4 + 16*t^3 + 9*t^2 + 4*t + 1
sage: S[0:10]
[[], [1], [2], [3], [4], [1, 2], [1, 3], [1, 4], [2, 1], [2, 3]]
sage: S[-1]
[1, 2, 1, 2, 1, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3, 4, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3, 4, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3, 4, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3, 4, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3, 4]
The elements of S are handles to C objects from Semigroupe
:
sage: x = S.an_element()
sage: x
[1, 2, 3, 4]
Products are calculated by Semigroupe
:
sage: x * x
[1, 2, 1, 2, 3, 2, 4, 3]
Powering operations are handled by Sage:
sage: x^3
[1, 2, 1, 2, 3, 2, 1, 2, 3, 4, 3, 2]
sage: x^(10^10000)
[1, 2, 1, 2, 3, 2, 1, 2, 1, 3, 2, 4, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3, 4, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3, 4, 3, 2, 1, 2, 3, 4]
Altogether, S is a full fledged Sage Coxeter group, which passes all the generic tests:
sage: TestSuite(S).run(verbose = True, skip = "_test_associativity")
running ._test_an_element() . . . pass
...
running ._test_has_descent() . . . pass
...
running ._test_reduced_word() . . . pass
...
And of course it works for general semigroups too, and can further compute much more information about those, like the (Knuth-Bendix completion of the) relations between the generators:
sage: W = CoxeterGroup(["A",3])
sage: S = semigroupe.AutomaticSemigroup(W.simple_reflections(), W.one())
sage: S.print_relations()
aa = 1
bb = 1
ca = ac
cc = 1
bab = aba
cbc = bcb
cbac = bcba
which contains the usual commutation + braid relations.
Let’s try now the 0-Hecke monoid:
sage: from sage.combinat.j_trivial_monoids import *
sage: S = semigroupe.AutomaticSemigroup(W.simple_projections(), W.one(), by_action = True)
sage: S.cardinality()
24
sage: S.print_relations()
aa = a
bb = b
ca = ac
cc = c
bab = aba
cbc = bcb
cbac = bcba
abacba = 0
sage: S.cardinality()
24
sage: S = semigroupe.AutomaticSemigroup(W.simple_projections(), W.one(), by_action = True,
....: category = JTrivialMonoids().Finite())
sage: H = S.algebra(QQ)
sage: H._repr_term = lambda x: '['+''.join(str(i) for i in x.reduced_word())+']'
sage: for x in H.orthogonal_idempotents():
....: print x
[121321]
-[121321] - [21321] + [2132] + [13] - [12132] + [2321] - [132] + [1213] - [12321] + [1232] + [1321] - [213]
[232] - [2321] - [1232] + [12321]
[121321] - [21321] + [32] + [12] - [12132] - [2] + [] - [121] + [2321] - [12321] - [321] + [23] + [2132] + [13] - [232] - [123] + [21] - [213] + [1213] - [3] + [1232] + [1321] - [1] - [132]
[121] - [1213] + [12321] - [1321]
-[12] + [21321] - [2132] - [232] + [12132] + [2] - [21] + [121] + [132] - [1213] - [1321] + [213]
-[121321] + [21321] - [32] + [12132] - [2321] + [12321] + [321] - [23] - [2132] - [13] + [232] + [123] + [213] - [1213] + [3] - [1232] - [1321] + [132]
-[121] - [13] + [1213] - [12321] + [1321] + [1]
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
4th SCIence Workshop, December 13th of 2011: Sage & Interfaces¶
Main Sage Demonstration¶
- Demonstration: Basics
- Demonstration: Plots (short version)
- Demonstration: Documentation
- Demonstration: Interfaces
- Demonstration: Sage combines the power of multiple software
- Demonstration: Databases
- Demonstration: Cython: Python -> C
- Demonstration: Combinatorics (short)
- Demonstration: Algebraic Combinatorics
- Demonstration: Sage + GAP4 + GAP3 + Chevie + Semigroupe (experimental)
- Demonstration: A real life example, parallel testing of a conjecture on J-Trivial monoids using MuPAD (experimental)
Other demonstrations¶
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Groupe d’utilisateur de Sage, 16/02/2012: Démonstration rapide de Sage¶
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Journée de l’Informatique Libre, 28/04/2012: Développement collaboratif libre d’applications métier; étude de cas: Sage pour les Mathématiques¶
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Counting¶
sage: Partitions(100000).cardinality()
Random generation¶
sage: M = BinaryTrees(100)
sage: t = M.random_element()
sage: view(t, tightpage=True)
Species¶
sage: from sage.combinat.species.library import *
sage: BT = CombinatorialSpecies()
sage: Leaf = SingletonSpecies()
sage: BT.define(Leaf+(BT*BT))
sage: o = var("o")
sage: BT.isotypes([o]*5).list()
[o*(o*(o*(o*o))), o*(o*((o*o)*o)), o*((o*o)*(o*o)), o*((o*(o*o))*o), o*(((o*o)*o)*o), (o*o)*(o*(o*o)), (o*o)*((o*o)*o), (o*(o*o))*(o*o), ((o*o)*o)*(o*o), (o*(o*(o*o)))*o, (o*((o*o)*o))*o, ((o*o)*(o*o))*o, ((o*(o*o))*o)*o, (((o*o)*o)*o)*o]
sage: S = BT.isotype_generating_series()
sage: S
x + x^2 + 2*x^3 + 5*x^4 + 14*x^5 + O(x^6)
sage: S[100]
227508830794229349661819540395688853956041682601541047340
sage: S
x + x^2 + 2*x^3 + 5*x^4 + 14*x^5 + 42*x^6 + 132*x^7 + 429*x^8 + 1430*x^9 + ... + O(x^101)
Words¶
sage: m = WordMorphism('a->acabb,b->bcacacbb,c->baba')
sage: m.fixed_point('a')
word: acabbbabaacabbbcacacbbbcacacbbbcacacbbac...
Lattice points of polytopes¶
sage: A = random_matrix(ZZ, nrows=6,ncols=3,x=7)
sage: L = LatticePolytope(A)
sage: L.plot3d()
sage: L.npoints() # should be cardinality!
28
This example used PALP and J-mol
Graphs up to an isomorphism¶
sage: show(graphs(5, lambda G: G.size() <= 4))
Guessing and the like¶
sage: data = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
sage: oeis(data)
0: A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1.
sage: from ore_algebra import guess, OreAlgebra
sage: guess(data, OreAlgebra(ZZ['x'], 'Dx'))
(x^2 + x - 1)*Dx + 2*x + 1
sage: L = _
sage: x = var('x')
sage: F = function('F')(x)
sage: equation = sum( L[i] * diff(F, x, i) for i in range(L.order()+1) )
sage: equation
(2*x + 1)*F(x) + (x^2 + x - 1)*D[0](F)(x)
sage: solution = desolve(equation, F, ics=[0,1])
sage: solution
-1/(x^2 + x - 1)
sage: solution.series(x)
1 + 1*x + 2*x^2 + 3*x^3 + 5*x^4 + 8*x^5 + 13*x^6 + 21*x^7 + 34*x^8 + 55*x^9 + 89*x^10 + 144*x^11 + 233*x^12 + 377*x^13 + 610*x^14 + 987*x^15 + 1597*x^16 + 2584*x^17 + 4181*x^18 + 6765*x^19 + Order(x^20)
sage: L.rational_solutions()
[(1/(x^2 + x - 1),)]
sage: L.generalized_series_solutions(15)
[1 + x + 2*x^2 + 3*x^3 + 5*x^4 + 8*x^5 + 13*x^6 + 21*x^7 + 34*x^8 + 55*x^9 + 89*x^10 + 144*x^11 + 233*x^12 + 377*x^13 + 610*x^14 + O(x^15)]
sage: L.generalized_series_solutions(1000)
[1 + x + 2*x^2 + 3*x^3 + 5*x^4 + 8*x^5 + ... + O(x^1000)]
sage: L = guess(data, OreAlgebra(ZZ['n'], 'Sn'))
sage: L
-Sn^2 + Sn + 1
sage: L.generalized_series_solutions()
[(1.618033988749895?)^n*(1 + O(n^(-5))), (-0.618033988749895?)^n*(1 + O(n^(-5)))]
sage: s = L.generalized_series_solutions()[0]
sage: s.exponential_part()
1.618033988749895?
sage: s.exponential_part().minpoly()
x^2 - x - 1
sage: data = [1, 1, 2, 5, 14]
sage: oeis(data)
0: A000108: Catalan numbers: C(n) = binomial(2n,n)/(n+1) = (2n)!/(n!(n+1)!). Also called Segner numbers.
1: A120588: G.f. satisfies: 3*A(x) = 2 + x + A(x)^2, with a(0) = 1.
2: A080937: Number of Catalan paths (nonnegative, starting and ending at 0, step +/-1) of 2*n steps with all values <= 5.
sage: data = _[0].first_terms()
sage: data
(1, 1, 2, 5, 14, 42, 132, ...)
sage: L = guess(data, OreAlgebra(ZZ['x'], 'Dx'))
sage: L
(4*x^2 - x)*Dx^2 + (10*x - 2)*Dx + 2
sage: L.power_series_solutions(10)
[1 + x + 2*x^2 + 5*x^3 + 14*x^4 + 42*x^5 + 132*x^6 + 429*x^7 + 1430*x^8 + O(x^9)]
sage: L.power_series_solutions(1000)
[1 + x + 2*x^2 + 5*x^3 + 14*x^4 + 42*x^5 + 132*x^6 + 429*x^7 + ... + O(x^1000)]
sage: equation = sum( L[i] * diff(F, x, i) for i in range(L.order()+1) ); equation
2*(5*x - 1)*D[0](F)(x) + (4*x^2 - x)*D[0, 0](F)(x) + 2*F(x)
sage: res = desolve(equation, F)
sage: res
sage: res = res.subs(_K2=I/2, _K1=-1)
sage: res.series(x)
1 + 1*x + 2*x^2 + 5*x^3 + 14*x^4 + 42*x^5 + 132*x^6 + ... + 1767263190*x^19 + Order(x^20)
sage: res.canonicalize_radical()
1/2*(I*sqrt(4*x - 1) + 1)/x
sage: M = guess(data, OreAlgebra(ZZ['n'], 'Sn'))
sage: M
(-n - 2)*Sn + 4*n + 2
Asymptotic expansion:
sage: M.generalized_series_solutions()
[4^n*n^(-3/2)*(1 - 9/8*n^(-1) + 145/128*n^(-2) - 1155/1024*n^(-3) + 36939/32768*n^(-4) + O(n^(-5)))]
sage: M.generalized_series_solutions(10)
[4^n*n^(-3/2)*(1 - 9/8*n^(-1) + 145/128*n^(-2) - 1155/1024*n^(-3) + 36939/32768*n^(-4) - 295911/262144*n^(-5) + 4735445/4194304*n^(-6) - 37844235/33554432*n^(-7) + 2421696563/2147483648*n^(-8) - 19402289907/17179869184*n^(-9) + O(n^(-10)))]
Symmetric functions¶
sage: S = SymmetricFunctions(QQ)
sage: S.inject_shorthands()
Arithmetic:
sage: s[2,1] * s[2,1]
s[2, 2, 1, 1] + s[2, 2, 2] + s[3, 1, 1, 1] + 2*s[3, 2, 1] + s[3, 3] + s[4, 1, 1] + s[4, 2]
sage: p( m[1] * ( e[3]*s[2] + 1 ))
p[1] + 1/12*p[1, 1, 1, 1, 1, 1] - 1/6*p[2, 1, 1, 1, 1] - 1/4*p[2, 2, 1, 1] + 1/6*p[3, 1, 1, 1] + 1/6*p[3, 2, 1]
Hopf structure:
sage: s[3,2,1].coproduct()
s[] # s[3, 2, 1] + s[1] # s[2, 2, 1] + s[1] # s[3, 1, 1] + s[1] # s[3, 2] + s[1, 1] # s[2, 1, 1] + s[1, 1] # s[2, 2] + s[1, 1] # s[3, 1] + s[1, 1, 1] # s[2, 1] + s[2] # s[2, 1, 1] + s[2] # s[2, 2] + s[2] # s[3, 1] + s[2, 1] # s[1, 1, 1] + 2*s[2, 1] # s[2, 1] + s[2, 1] # s[3] + s[2, 1, 1] # s[1, 1] + s[2, 1, 1] # s[2] + s[2, 2] # s[1, 1] + s[2, 2] # s[2] + s[2, 2, 1] # s[1] + s[3] # s[2, 1] + s[3, 1] # s[1, 1] + s[3, 1] # s[2] + s[3, 1, 1] # s[1] + s[3, 2] # s[1] + s[3, 2, 1] # s[]
Plethysms:
sage: p[3](p[6])
sage: s[3,2](s[2,1]+p[3])
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques¶
Todo
Add dates
- Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Introduction
- Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Tris et complexité
- Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Codes correcteurs
- Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Produits rapides
- Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Groupe Symétrique et groupes de permutations
- Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Algèbre linéaire, formes normales et applications
- Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Algèbre linéaire sur un anneau
- Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Algèbre linéaire rapide
- Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Programmation linéaire
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Algèbre linéaire¶
Mathematics is the art of reducing any problem to linear algebra
- William Stein.
On se place dans un corps \(K\) quelconque
Définition
Une matrice est sous forme échelon (en lignes) si le nombre de zéros précédant la première valeur non nulle d’une ligne augmente ligne par ligne jusqu’à ce qu’il ne reste plus que des zéros:
Les colonnes caractéristiques sont les colonnes contenant les pivots (soulignés ci-dessus), c’est-à-dire les premiers coefficients non nul d’une ligne.
Une matrice est sous forme échelon réduite si les pivots valent 1 et si les autres coefficients dans les colonnes des pivots sont nuls:
Exemple
sage: M = random_matrix(QQ, 4, 8, algorithm='echelon_form', num_pivots=3); M # random
[ 1 -3 0 -2 0 3 1 0]
[ 0 0 1 -5 0 -2 -1 -1]
[ 0 0 0 0 1 -1 3 1]
[ 0 0 0 0 0 0 0 0]
sage: M.pivots() # random
(0, 2, 4)
Remarque
L’algorithme du pivot de Gauß-Jordan transforme une matrice jusqu’à ce qu’elle soit sous forme échelon (réduite).
Exercice: matrices à deux lignes
Soit \(M\) une matrice générique à deux lignes. Écrire sous forme de multiplication à gauche par une matrice \(2\times 2\) le pivot de Gauß appliqué à \(M\).
Todo
Le faire à la place sur trois matrices typiques à deux lignes
Remarque
Si \(M\) est obtenue de \(N\) par l’algorithme du pivot de Gauß, alors \(M=PN\) où \(P\) est une matrice inversible (éventuellement de déterminant \(1\)).
Disons ici que deux matrices \(M\) et \(N\) de \(M_{n,m}(K)\) sont équivalentes (modulo l’action de \(GL_n(K)\) à gauche) s’il existe une matrice inversible \(P\) telle que \(M=PN\).
Exercice:
Vérifier que cela définit une relation d’équivalence!
Question
La remarque précédente dit que si deux matrices \(M\) et \(N\) donnent la même forme échelon réduite par Gauß, alors elles sont équivalentes.
Réciproque?
Démonstration opératoire de la réciproque
Montrer pas à pas que si \(M\) et \(N\) sont réduits et \(M=PN\), alors \(P\) est essentiellement l’identité (à part les dernières colonnes correspondant aux lignes nulles de \(M\) et \(N\)).
Todo
Digression sur les formes normales
Théorème
On considère les matrices \(n\times m\) à coefficients dans un corps \(K\). La forme échelon réduite donne une forme normale pour les matrices modulo l’action de \(GL_n(K)\) à gauche.
Décrire un objet comme étant le résultat d’un algorithme est opératoire, mais pas très conceptuel. Peut-on faire mieux?
Exercice
Soient \(M\) et \(N\) deux matrices de \(M_{n,m}(K)\), que l’on voit comme deux paquets de \(n\) vecteurs de \(K^m\). Montrer que \(M\) et \(N\) sont équivalentes (modulo l’action de \(GL_n(K)\) à gauche) si et seulement si les vecteurs engendrent le même sous-espace vectoriel de \(K^m\).
Todo
Rédiger la démonstrationn
Corollaire
L’ensemble quotient \(GL_n(K) \backslash M_{n,m}(K)\) représente l’ensemble des sous-espaces vectoriels de dimension au plus \(n\) dans \(K^m\). Cet ensemble est naturellement muni d’une structure de variété appelée variété Grassmanienne.
Todo
Poser le problème
Prendre une matrice spécifique
Interprétation des lignes de la forme normale réduite?
Pourquoi est-elle indépendante de l’ordre de calcul?
Rappel: groupes de permutations
Pour manipuler un sous-groupe \(G\) du groupe symétrique \(S_n\), on avait considéré le sous-groupe \(G_{n-1}\) des éléments fixant \(n\), puis ceux fixant \(n\) et \(n-1\), et ainsi de suite récursivement.
Formellement, on avait considéré la suite des groupes symétriques emboîtés:
et la suite induite des groupes emboîtés \(G_i:=G \cap S_i\):
L’étude de \(G\) se ramenait alors à l’étude des quotients successifs \(G_i/G_{i-1}\).
Appliquons le même programme.
Définition: Drapeau
Un drapeau complet d’un espace vectoriel \(V\) de dimension \(n\) est une suite maximale de sous-espaces strictement emboîtés:
Définition: Drapeau canonique
À chaque base ordonnée, on peut associer naturellement un drapeau complet. Ici on considérera principalement le drapeau canonique associé à la base canonique \(e_1,\cdots,e_m\) de \(V=K^m\):
Note: on prend les éléments dans cet ordre pour que cela colle avec nos petites habitudes de calcul du pivot de Gauß. Et pour alléger les notations, on utilisera plutôt:
Formes échelon et bases adaptées
Dans ce formalisme, qu’est-ce qu’une matrice sous forme échelon?
C’est une base d’un espace vectoriel \(E\) adaptée à un drapeau complet donné. C’est-à-dire une base sur laquelle on peut lire immédiatement les sous espaces \(E_i:=E\cap \overline V_i\).
Le pivot de Gauß est un algorithme de calcul de base adaptée.
Définition intrinsèque des colonnes caractéristiques
Remarque: en passant de \(E_{i+1}\) à \(E_i\), la dimension croît de \(0\) ou de \(1\).
Cela permet de donner une définition intrinsèque de la notion de colonnes caractéristiques d’un sous espace vectoriel \(E\): les \(i\) tels que la dimension de \(E_i\) croît strictement. Cela décrit la position de \(E\) par rapport à un drapeau complet fixé.
Évidemment, sur une forme échelon pour \(E\), cela correspond aux colonnes \(i\) pour lesquelles on a un vecteur de la forme \(e_i+\cdots\).
Formes échelon réduites
Considérons deux bases adaptées d’un même espace vectoriel \(E\). Pour \(i\) une colonne caractéristique, on note \(a_i\) et \(b_i\) les vecteurs de la forme \(a_i=e_i+\cdots\) et \(b_i=e_i+\cdots\).
Alors \(a_i-b_i\in V_{i+1}\); autrement dit \(a_i=b_i\) dans le quotient \(E_i/E_{i+1}\).
Prendre une forme échelon réduite, c’est faire un choix d’un représentant (relativement canonique) \(a_i\) dans chaque quotient \(E_i/E_{i+1}\): celui qui a des zéros aux autres colonnes caractéristiques.
Ce formalisme montre que le vecteur \(a_i\) est intrinsèque à \(E\) (et au choix du drapeau complet). En particulier il est clair qu’il est complètement indépendant des autres coefficients de la forme échelon réduite, même si opératoirement le calcul de \(a_i\) par Gauß passe par ceux-ci.
Remarque
La permutation \(P\) apparaissant dans le calcul de l’algorithme de Gauß a une interprétation géométrique naturelle (position du drapeau \(\langle v_1\rangle, \langle v_1,v_2\rangle\) par rapport au drapeau canonique).
Les variétés Grassmaniennes et ses variantes (variétés de drapeaux, …) et leur multiples généralisations sont l’objet d’études approfondies en géométrie. La combinatoire y joue un rôle important: l’apparition d’une permutation \(P\) dans le pivot de Gauß est le prototype du type de lien.
Exercice: résolution d’équations linéaires
Soit \(E\) un ensemble d’équations linéaires/affines. Retrouver les algorithmes usuels de résolution: existence de solution, dimension, base et paramétrisation de l’espace des solutions.
Exercice: calcul avec les sous espaces vectoriels
On considère des sous espaces \(E\), \(F\), … de \(V=K^n\) donnés par des générateurs ou des équations. Donner des algorithmes (et leur complexité!) pour:
Déterminer une base de \(E\).
Tester si un vecteur appartient à \(E\).
Tester si \(E=F\).
Tester si deux vecteurs \(x\) et \(y\) de \(V\) sont égaux modulo \(E\)
Calculer l’orthogonal d’un sous-espace vectoriel
Calculer la somme \(E+F\) et l’intersection \(E\cap F\) de deux espaces vectoriels
Calculer la sous-algèbre de \(V\) engendrée par \(E\) (en supposant \(V\) muni d’une structure d’algèbre \((V,+,.,*)\))
Plus généralement: clôture de \(E\) sous des opérations linéaires
Calculer dans l’espace quotient \(E/F\)
Cas de la dimension infinie?
Exercice: calcul avec les morphismes
Soit \(\phi\) une application linéaire entre deux espaces vectoriels \(E\) et \(F\) de dimension fini. Donner des algorithmes pour:
- Calculer le noyau de \(\phi\)
- Calculer l’image de \(\phi\)
- Calculer l’image réciproque par \(\phi\) d’un vecteur \(f\) de \(F\)
- Arithmétique: composition, combinaison linéaires, inverse
- Calculer le polynôme caractéristique
- Calculer les valeurs propres de \(\phi\)
- Calculer les espaces propres de \(\phi\)
On considère maintenant l’anneau \(\ZZ\). On est maintenant en train de travailler avec des \(\ZZ\)-modules au lieu d’espaces vectoriels. Peut-on procéder comme précédemment?
Exercice: matrices à deux lignes
Exemple:
sage: M = matrix([[10,1,2], [6,2,-1]]); M
[10 1 2]
[ 6 2 -1]
Quel candidat pour une forme échelon?
Interprétation en terme de multiplication par une matrice?
Interprétation en terme de sous-espace engendré?
Cette forme échelon est elle réduite?
sage: M.echelon_form() [ 2 3 -4] [ 0 7 -11]
Description du quotient?
Remarque clef
Soit \(\begin{pmatrix}a\\b\end{pmatrix}\) un vecteur de \(\ZZ^2\), et \(r, u,v\) les résultats du pgcd étendu de \(a\) et \(b\): \(r = a\wedge b = ua+bv\). Posons:
alors: \(M\begin{pmatrix}a\\b\end{pmatrix} = \begin{pmatrix}r\\0\end{pmatrix}\) et \(M\in GL(\ZZ)\)!
Moralité: la majeure partie de ce que l’on a vu précédemment s’applique mutatis-mutandis. L’algèbre linéaire sur \(\ZZ\) n’est pas foncièrement plus compliquée ou coûteuse que sur un corps.
Il y a juste quelques points techniques à traiter, qui apparaissent déjà en dimension \(1\):
Exercice: Résolution
Déterminer l’ensemble des solutions entières de l’équation \(6x+4y+10z=18\).
Exercice: Torsion
Donner un exemple de quotient d’un module libre \(\ZZ^n\) qui n’est pas isomorphe à un module libre.
Donner un exemple de drapeau infini
Existe-t’il des drapeaux croissants infinis?
Exemple:
sage: V = ZZ^6 sage: I = V.zero_submodule(); I Free module of degree 6 and rank 0 over Integer Ring Echelon basis matrix: []
sage: I = I + V.submodule([V.random_element(prob=.3)]); I # random Free module of degree 6 and rank 1 over Integer Ring Echelon basis matrix: [ 0 19 0 0 24 0]
Généralisations
Tout ce que l’on vient de dire se généralise immédiatement pour un anneau principal quelconque comme \(A=\QQ[x]\); à condition bien entendu que \(A\) soit constructif, et en particulier, qu’il y ait un algorithme pour calculer le PGCD étendu.
Exercice
Soit \(G\) un groupe additif abélien engendré par un nombre fini \(n\) d’éléments, mettons \(a\), \(b\), \(c\), avec \(n=3\).
- Que peut-on dire sur l’ensemble des relations entre ces éléments?
- En déduire la structure de \(G\).
Soit \(A\) un anneau. Par exemple un anneau de polynômes multivariés \(A=\QQ[x,y]\). Qu’est-ce qui subsiste de tout ce que l’on a vu?
Un \(A\)-sous-module de \(A^1\) est juste un idéal de \(A\).
Exemple: \(\langle x^2y, xy^2\rangle\)
Calcul avec les idéaux et sous-modules: bases de Gröbner
Combinatoire sous-jacente: idéaux monomiaux!
Explorons un exemple:
sage: %display latex
sage: A = QQ['a']
sage: a = A.gen()
sage: M = matrix(A, random_matrix(ZZ, 3, 8)); M[0,0] = a; M
[ a 2 0 0 0 2 2 2]
[ 1 2 0 -2 0 0 1 2]
[ 1 -2 1 -2 -1 -2 -2 -2]
sage: N = copy(M)
sage: M[1] = M[0,0] * M[1] - M[1,0] * M[0]
sage: M[2] = M[0,0] * M[2] - M[2,0] * M[0]
sage: M
sage: M[2] = M[1,1] * M[2] - M[2,1] * M[1]
sage: M
Revenons sur notre exemple:
sage: M
On constate que \(a\) divise la troisième ligne; on peut donc diviser de manière exacte par \(a\):
sage: M[2] = M[2] // a
sage: M
De plus, le coefficient M[3,3]
est le déterminant de la matrice d’origine:
sage: N[:,:3]
sage: N[:,:3].det()
sage:
Ce phénomène est général et peut être utilisé récursivement:
Algorithme de Gauß-Bareiss
On procède comme pour Gauß sans fractions, y compris pour traiter les lignes avec un coefficient nul dans la colonne du pivot. Cependant, avant de traiter les colonnes \(\geq i+2\) on divise tout le quadrant inférieur composé des lignes et colonnes \(\geq i+2\), par \(M[i,i]\).
Le fonctionnement de l’algorithme repose sur la propriété suivante:
Proposition
Soit \(M\) une matrice sur un anneau intègre. Après avoir traité les \(i\) premières colonnes, \(M_{i,i}\) est le déterminant du \(i\)-ème mineur dominant de la matrice d’origine (correspondant aux \(i\) premières lignes et colonnes). De plus après avoir traité la colonne \(i+1\), ce déterminant divise toutes les coefficients \(M_{i',j'}\) avec \(i',j'\geq i+2\).
Exercice
Vérifier la proposition dans le cas d’une matrice triangulaire supérieure.
Remarque
Pour simplifier, on a supposé ci-dessus que la matrice était carrée et que tous les mineurs dominants étaient non nuls. Modulo les détails techniques usuels (forme échelon réduite plutôt que uni triangulaire supérieure), l’algorithme se généralise à des matrices quelconques sur un anneau intègre.
Le coeur de l’algèbre linéaire est l’étude des matrices modulo des relations d’équivalences (équivalence, conjugaison, similitude), et ce sur les différents types d’anneaux.
Dans chaque cas, on introduit une notion d’ordre (plus conceptuellement de drapeau) qui permet de définir simultanément une forme normale et un algorithme d’élimination permettant de calculer cette forme normale.
Voir par exemple [Storjohan.2004] pour une présentation d’ensemble.
Exercice: Du calcul matriciel au calcul sur les sous espace vectoriels
Soit \(V\) une liste de vecteurs dans \(E=\QQ^10\), comme par exemple:
sage: V = random_matrix(QQ, 4, 10, algorithm='echelonizable', rank=3).rows() # random sage: V [(1, 4, -5, 3, -19, 2, -56, -19, -5, -43), (4, 16, -20, -11, 75, 8, 229, 52, 26, 153), (5, 20, -25, -19, 121, 10, 368, 87, 43, 251), (0, 0, 0, -2, 13, 0, 39, 11, 4, 28)]
On veut calculer une base du sous-espace vectoriel engendré par \(V\). On peut l’obtenir simplement avec les outils déjà présents:
sage: E = QQ^10 sage: E.span(V) Vector space of degree 10 and dimension 3 over Rational Field Basis matrix: [ 1 4 -5 0 0 2 1 -3 1 -2] [ 0 0 0 1 0 0 0 1 -2 -1] [ 0 0 0 0 1 0 3 1 0 2]
Implanter votre propre fonction
baseSEV(V)
qui calcule une telle base en se ramenant à du calcul matriciel.Soit \(V\) une liste de vecteurs et \(u\) un autre vecteur. On veut tester si \(u\) est dans le sous espace vectoriel engendré par \(V\):
sage: u = E([1, 2, 5, 3, 0, 1, 6, 3, 0, 5]) sage: u in V False
Comme ci-dessus, implanter votre propre fonction
appartient(V,v)
qui se ramène à du calcul matriciel.Implanter votre propre fonction
SEV_egaux(U, V)
qui teste si deux listes deux vecteurs engendrent le même sous espace vectoriel.Implanter votre propre fonction
SEV_orthogonal(V)
pour calculer une base de l’orthogonal de \(\langle V\rangle\), c’est-à-dire l’ensemble des vecteurs \(u\) du dual de \(E\) tel que \(\langle u,v\rangle=0\).Quel rapport avec la résolution d’équations?
Implanter votre propre fonction
SEV_somme(U, V)
qui calcule une base de la somme des deux sous-espaces vectoriels \(\langle U\rangle\) et \(\langle V\rangle\).De même implanter
SEV_intersection(U,V)
etSEV_en_somme_directe(U,V)
.
Exercice: application aux codes cycliques
On oubliera ici que les codes cycliques sont naturellement représentés par des idéaux dans \(\ZZ_2[X] / X^n-1\), et on ne fera que de l’algèbre linéaire.
Soit \(E\) un espace vectoriel sur un corps fini; typiquement:
sage: F2 = GF(2)
sage: E = F2^7; E
Vector space of dimension 7 over Finite Field of size 2
On considère l’opération cycle(v)
qui prend un vecteur et
décale ses coordonnées d’un cran vers la droite (modulo \(n\)). On
rappelle qu’un code cyclique est un sous-espace vectoriel de \(E\)
qui est stable par l’opération cycle
.
- Implanter l’opération
cycle
. - Implanter une fonction
code_cyclique(v)
qui renvoie le plus petit code cyclique \(C\) contenant \(v\). - Implanter une fonction qui renvoie la matrice de contrôle du code \(C\), c’est à dire une matrice \(M\) telle que \(Mv=0\) si et seulement si \(v\) est dans \(C\).
- Implanter le décodage par syndrome pour le code cyclique engendré par \(v=\) (voir le cours Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Codes correcteurs).
Exercice: algorithme de Gauß-Bareiss
Dans tout cet exercice, on pourra supposer que la matrice d’entrée est inversible, voire que ses \(n\) premiers mineurs sont non nuls (pas de permutation des lignes nécessaire).
(Échauffement) Écrire une fonction qui met une matrice à coefficients dans un corps sous forme échelon à l’aide de l’algorithme de Gauß. Vérifier votre programme pour:
sage: M = matrix([[2, 1, 3], [1, 4, 9], [1, 8, 27]]); M
Écrire une fonction qui met une matrice à coefficients entiers sous forme échelon à l’aide de l’algorithme de Gauß-Bareiss. Vérifier votre programme pour la matrice ci-dessus, puis sur une matrice aléatoire de grande taille.
Évaluer la complexité pratique en prenant des matrices aléatoire de taille \(n=2^k\). Comparer avec ce que l’on obtient avec Gauß, et avec Gauß sur un corps fini.
Qu’en pensez-vous?
En déduire une fonction qui calcule le déterminant d’une matrice à coefficients entiers.
Faire la même chose pour des matrices à coefficients polynomiaux univariés.
En déduire une fonction qui calcule le polynôme caractéristique d’une matrice.
Algèbre linéaire, représentations des monoïdes et Chaînes de Markov
Voir: La bibliothèque de Tsetlin.
Ce texte est à approcher comme les textes de l’agrégation: il s’agit d’un menu à la carte; vous pouvez choisir d’étudier certains points, pas tous, pas nécessairement dans l’ordre, et de façon plus ou moins fouillée. Vous pouvez aussi vous poser d’autres questions que celles indiquées plus bas. L’objectif final est de concevoir un mini-développement de 5 minutes comportant une partie traitée sur ordinateur et, si possible, des représentations graphiques de vos résultats.
[Storjohan.2004] | Algorithms for Matrix Canonical Forms, Arne Storjohan, PhD Thesis, Department of Computer Science, Swiss Federal Institute of Technology – ETH, 2000 |
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Algèbre linéaire sur un anneau¶
\(\def\suchthat{\ \mid\ }\) \(\def\ZZ{\bf Z}\) \(\def\QQ{\bf Q}\) \(\def\RR{\bf R}\)
Todo
Exercices de motivation
- Décrire \(<u,v>\) pour \(u,v=...\in\ZZ^3\)
- Résoudre le système d’équations …
- Décrire le groupe abélien de présentation …
Exprimer les corrections en terme de morphismes
On souhaite faire de l’algèbre linéaire sur l’anneau \(\ZZ\). On va donc travailler avec des \(\ZZ\)-modules, ou de manière équivalente des groupes abéliens. Peut-on procéder comme sur un corps?
Exercices
- Donner des exemples de \(\ZZ\)-modules (ensemble satisfaisant les même axiomes qu’un espace vectoriel, où \(\ZZ\) joue le rôle du corps de base)
- Dans chacun des cas, essayer de déterminer une base.
Todo
Déplacer ici la résolution d’une équation et la mise sous forme échelon?
Solution
- \(\ZZ\), \(2\ZZ\), \(p\ZZ\)
- plus généralement tout idéal de \(\ZZ\)
- \(\ZZ/2\ZZ\)
- plus généralement, tout quotient d’un \(\ZZ\)-module par un sous \(\ZZ\)-module.
- \(\ZZ^n\)
- \(\ZZ/2\ZZ \times \ZZ/3\ZZ \times \ZZ/8\ZZ\)
- plus généralement, tout produit cartésien (ou équivalent somme directe) de \(\ZZ\)-module
- \(\QQ\), \(\RR\)
- Groupes abéliens (notés additivement)
Exercice
Considérons la matrice suivante:
sage: %display latex
sage: M = matrix([[14,19,-10], [10,14,-7]]); M
[ 14 19 -10]
[ 10 14 -7]
- Comment mettre \(M\) sous forme échelon? Indication: on veut que les lignes de la forme échelon engendrent le même sous-module \(F\) de \(E\) que celles de \(M\).
- Interprétation en terme de multiplication par une matrice?
- La forme échelon est elle réduite?
- Décrire l’ensemble quotient \(E/F\).
Solution
Oublions les deux dernières colonnes pour le moment:
sage: v = M[:,0]; v
[14]
[10]
Dans ce cas, le sous-module engendré par les deux lignes est juste un idéal de \(\ZZ\), en l’occurrence \(14\ZZ + 10\ZZ = 2\ZZ\):
sage: a = 14; b = 10
sage: gcd(a,b)
2
On veut donc renvoyer la forme échelon \(\begin{pmatrix}2\\0\end{pmatrix}\), et en obtenir les coefficients par combinaisons linéaires de \(a\) et de \(b\). Calculons les coefficients de Bezout:
sage: r, u, v = xgcd(a,b)
sage: r, u, v
(2, -2, 3)
On a alors:
sage: a*u + b*v
2
sage: a*(-b/r) + b*(a/r)
0
Posons:
sage: T = matrix([[ u, v ],
....: [ -b/r,a/r ]]); T
[-2 3]
[-5 7]
On peut mettre \(M\) sous forme échelon en la multipliant par \(T\):
sage: T * M
[ 2 4 -1]
[ 0 3 1]
La matrice \(T\) est de déterminant \(1\):
sage: det(T)
1
Donc cette opération est inversible: les lignes de la forme échelon de \(M\) engendrent bien le même sous-module que les lignes de \(M\).
Cette forme échelon n’est pas encore réduite; on peut la réduire avec une étape de plus, et on obtient:
sage: M.echelon_form()
[ 2 1 -2]
[ 0 3 1]
Généralisons cela.
Remarque clef
Soit \(\begin{pmatrix}a\\b\end{pmatrix}\) un vecteur de \(\ZZ^2\), et \(r, u,v\) les résultats du pgcd étendu de \(a\) et \(b\): \(r = a\wedge b = ua+bv\). Posons:
alors: \(M\begin{pmatrix}a\\b\end{pmatrix} = \begin{pmatrix}r\\0\end{pmatrix}\) et \(M\in GL(\ZZ)\) (\(\det(M)=1\))!
Théorème: forme de Hermite
Soit \(M\) une matrice à coefficient dans \(\ZZ\). Alors il existe une matrice \(T\) inversible telle que \(TM\) est sous forme échelon.
Comme pour un corps, il existe une forme réduite canonique, dite forme de Hermite. Les pivots y sont entiers positifs, et les coefficients dans la colonne d’un pivot \(r\) sont réduits modulo \(r\).
Exemples:
sage: random_matrix(ZZ, 6, 8).echelon_form() # random
[ 1 0 0 1 0 2140209 401777 10460194]
[ 0 1 0 1 0 871600 163624 4259915]
[ 0 0 1 0 0 1534726 288111 7500917]
[ 0 0 0 2 0 2166811 406771 10590213]
[ 0 0 0 0 1 663131 124488 3241027]
[ 0 0 0 0 0 3155946 592459 15424568]
Moralité
La majeure partie de ce que l’on a vu précédemment va s’appliquer mutatis-mutandis en utilisant la forme de Hermite comme forme échelon réduite. L’algèbre linéaire sur \(\ZZ\) n’est pas foncièrement plus compliquée ou coûteuse que sur un corps.
Il y a juste quelques points techniques à traiter. Elles apparaîtront en fait déjà en dimension \(1\).
Corollaire
Tout sous-module \(F\) d’un \(\ZZ\)-module libre \(E\) de dimension finie \(n\) est libre de dimension finie \(\leq n\).
Début de la démonstration
Si \(F\) admet un système générateur fini, le mettre sous forme échelon. Alors \(F=\ZZ v_1 \oplus \cdots \oplus \ZZ v_k\), où \(v_1,\ldots,v_k\) sont les lignes de la forme échelon.
Mais sinon?
Exercice
- Donner un exemple de drapeau infini décroissant; c’est-à-dire une suite infinie \(F_0\supsetneq F_1\supsetneq \cdots\) de sous-modules strictement emboîtés.
- Existe-t’il des drapeaux croissants infinis dans \(\ZZ^n\)
Solution
Un drapeau infini décroissant:
Remarque: contrairement aux espaces vectoriels, deux sous-modules emboîtés de même dimension ne sont pas forcément égaux; tous les modules du drapeau ci-dessus sont de même dimension!
Pour un drapeau croissant infini, il serait tentant de prendre:
Mais il ne vivrait pas dans \(\ZZ^n\).
Faisons croître un sous-module de \(\ZZ^6\) en lui rajoutant progressivement des vecteurs aléatoires:
sage: V = ZZ^6
sage: I = V.zero_submodule(); I
Free module of degree 6 and rank 0 over Integer Ring
Echelon basis matrix:
[]
sage: I = I + V.submodule([V.random_element(prob=.3)]); I # random
Free module of degree 6 and rank 1 over Integer Ring
Echelon basis matrix:
[ 0 19 0 0 24 0]
À chaque étape, soit la dimension augmente, soit un des pivots diminue strictement.
Il s’ensuit qu’une suite croissante infinie de sous-module se stabilise forcément.
Exercice: Une équation affine
Déterminer l’ensemble des solutions entières de chacune des équations suivantes:
- \(6x+4y+10z=15\)
- \(6x+4y+10z=18\)
Todo
Solution
Exercice: noyau
- Soit \(M\) une matrice sous forme échelon. Décrire son noyau à gauche \(K=\{v, vM =0\}\).
- Soit \(M\) une matrice quelconque de dimension \(n\times m\). Décrire son noyau à gauche.
Solution
Soit \(T\) inversible telle que \(TM\) est sous forme échelon. Soit \(k\) son rang. Alors les \(n-k\) dernières lignes de \(T\) forment une base de \(K\).
Todo
Considérer les lignes de \(T\) comme une base de l’espace. \(TM\) est alors la matrice de \(M\) dans cette base. On est ramené au cas précédent.
Corollaire
Le noyau d’un \(\ZZ\)-morphisme entre deux modules libres est libre.
L’ensemble des solutions entières d’un système d’équations à coefficients entiers est un module libre.
Note
Plus généralement, le noyau d’un \(\ZZ\)-morphisme d’un \(\ZZ\)-module libre dans un \(\ZZ\)-module quelconque est un sous-module d’un module libre, donc libre. Le corollaire précédent est surtout intéressant algorithmiquement.
Exercice: Torsion
Donner un exemple de quotient d’un module libre \(\ZZ^n\) qui n’est pas isomorphe à un \(\ZZ\)-module libre.
Décrire le quotient de \(\ZZ^4\) par le sous-module engendré par les lignes de:
sage: M = diagonal_matrix([2,6,12,0]) [ 2 0 0 0] [ 0 6 0 0] [ 0 0 12 0] [ 0 0 0 0]
Todo
Solution
On s’intéresse aux groupes (additifs) abéliens engendrés par un nombre fini d’éléments.
Exercice
- Donner des exemples de tels groupes
- Que peut-on dire sur l’ensemble \(F\) des relations entre les générateurs \(g_1,\ldots,g_n\) d’un groupe abélien \(G\)?
Solution
\(\ZZ\), \(\ZZ/k\ZZ\), produits directs de ceux-ci.
Y’en a t’il d’autres? L’exemple suivant est-il de la forme ci-dessus?
L’ensemble \(F\) des relations entre les générateurs \(g_1,\cdots,g_n\) est un sous-module (libre!) de \(\ZZ^n\). \(G\) est isomorphe à \(\ZZ^n / F\).
Exercice
Soit \(M\) une matrice \(n\times m\) vue comme famille génératrice d’un sous \(\ZZ\)-module de \(\ZZ^m\). Soient \(S\) et \(T\) matrices inversibles de dimensions appropriées.
Montrer que le sous-module correspondant à \(S M T\) est isomorphe à \(F\).
Exemple: prendre le sous-module engendré par notre matrice \(M\) préférée; à quel sous-module très simple est-il isomorphe?:
sage: M = matrix([[14,19,-10], [10,14,-7]]); M [ 14 19 -10] [ 10 14 -7]
Qu’en déduire sur notre groupe \(H\) ci-dessus?
Solution
Multiplier par \(S\) change les générateurs, sans changer le sous-module. Multiplier par \(T\) revient à changer la base dans lequel on exprime les générateurs du sous-module.
Faisons une forme échelon sur les lignes puis sur les colonnes:
sage: N = M.echelon_form(); N
[ 2 1 -2]
[ 0 3 1]
sage: N.transpose().echelon_form().transpose()
[1 0 0]
[0 1 0]
Donc \(M\) est isomorphe au sous-module engendré par \(e_1\) et \(e_2\).
Il s’ensuit que le groupe \(H\) est isomorphe à
Théorème
Correspondance:
- matrices modulo la double action de \(GL_n(\ZZ)\) à gauche et \(GL_m(\ZZ)\) à droite.
- sous-modules de rang \(\leq n\) de \(\ZZ^m\) à isomorphisme près
- quotients de \(\ZZ^m\) par un sous-module de rang \(\leq n\) à isomorphisme près
- groupes abéliens engendrés par \(n\) éléments à isomorphisme près
Problème
On a vu que la forme échelon (ligne) donne une forme normale pour les matrices modulo l’action à gauche de \(GL_n(\ZZ)\).
Équivalent pour les matrices modulo la double action de \(GL_n(\ZZ)\) et \(GL_m(\ZZ)\)?
Exemple
sage: A = matrix([[ 4134, 11016, 52074, 159720, -462804, 1027050,-1807692],
....: [-18014,-47944,-226778,-695548, 2015364,-4472474, 7872162],
....: [-11584,-30896,-145972,-447728, 1297368,-2879104, 5067330],
....: [ 7516, 20072, 94768, 290684, -842328, 1869292,-3289908],
....: [-19264,-51392,-242776,-744644, 2157744,-4788448, 8427786]]);
sage: A
sage: _.echelon_form()
[ 2 16 2 4 8436 1598 11904]
[ 0 24 0 0 4296 888 6108]
[ 0 0 12 0 9288 1632 13098]
[ 0 0 0 12 6432 1200 9006]
[ 0 0 0 0 9744 1704 13764]
sage: _.transpose().echelon_form().transpose()
[ 2 0 0 0 0 0 0]
[ 0 12 0 0 0 0 0]
[ 0 6 12 0 0 0 0]
[ 0 6 0 12 0 0 0]
[ 0 12 0 0 24 0 0]
sage: _echelon_form()
[ 2 0 0 0 0 0 0]
[ 0 6 0 12 0 0 0]
[ 0 0 12 12 0 0 0]
[ 0 0 0 24 0 0 0]
[ 0 0 0 0 24 0 0]
sage: _.transpose().echelon_form().transpose()
[ 2 0 0 0 0 0 0]
[ 0 6 0 0 0 0 0]
[ 0 0 12 0 0 0 0]
[ 0 0 0 24 0 0 0]
[ 0 0 0 0 24 0 0]
Note: statistiquement, la procédure termine en deux voire une étapes. Pour trouver l’exemple ci-dessus, on a essayé plein d’exemples aléatoires avec les fonctions du fichier suivant.
Forme normale de Smith
Soit \(M\) une matrice \(n\times m\) à coefficients dans \(\ZZ\).
Alors \(M\) est équivalente à une matrice diagonale dont les coefficients non nuls \(d_1,\dots,d_k\) se divisent successivement.
Démonstration
- La procédure utilisée dans l’exemple termine
- Si deux coefficients diagonaux successifs ne se divisent pas, on peut effectuer un pgcd par multiplication à droite et à gauche par des matrices de transvection.
Corollaire: classification des groupes abéliens
Tout groupe abélien est isomorphe de façon canonique à un produit direct
où les \(d_1,\dots,d_k\) se divisent successivement.
Pour les détails et aller plus loin, voir Wikipedia: Théorème des facteurs invariants.
Tout ce que l’on vient de dire se généralise immédiatement pour un anneau principal quelconque comme \(A=\QQ[x]\); à condition bien entendu que \(A\) soit constructif, et en particulier, qu’il y ait un algorithme pour calculer le PGCD étendu.
Soit \(A\) un anneau. Par exemple un anneau de polynômes multivariés \(A=\QQ[x,y]\). Qu’est-ce qui subsiste de tout ce que l’on a vu?
Un \(A\)-sous-module de \(A^1\) est juste un idéal de \(A\).
Exemple: \(\langle x^3y, x^2y^2, xy^3\rangle\)
Calcul avec les idéaux et sous-modules: bases de Gröbner
Combinatoire sous-jacente: idéaux monomiaux!
Todo
faire un dessin d’idéal monomial
Explorons un exemple:
sage: A = QQ['a']
sage: a = A.gen()
sage: M = matrix(A, random_matrix(ZZ, 3, 8)); M[0,0] = a; M # random
[ a 2 0 0 0 2 2 2]
[ 1 2 0 -2 0 0 1 2]
[ 1 -2 1 -2 -1 -2 -2 -2]
sage: N = copy(M)
sage: M[1] = M[0,0] * M[1] - M[1,0] * M[0]
sage: M[2] = M[0,0] * M[2] - M[2,0] * M[0]
sage: M # random
[ a -2 2 -1 -1 -1 -38 0]
[ 0 4*a + 24 -2*a - 24 -a + 12 a + 12 -2*a + 12 a + 456 -2*a]
[ 0 11*a 6*a -a 0 -12*a 0 -7*a]
sage: M[2] = M[1,1] * M[2] - M[2,1] * M[1]
sage: M # random
[ a -2 2 -1 -1 -1 -38 0]
[ 0 4*a + 24 -2*a - 24 -a + 12 a + 12 -2*a + 12 a + 456 -2*a]
[ 0 0 46*a^2 + 408*a 7*a^2 - 156*a -11*a^2 - 132*a -26*a^2 - 420*a -11*a^2 - 5016*a -6*a^2 - 168*a]
Revenons sur notre exemple:
sage: M # random
[ a -2 2 -1 -1 -1 -38 0]
[ 0 4*a + 24 -2*a - 24 -a + 12 a + 12 -2*a + 12 a + 456 -2*a]
[ 0 0 46*a^2 + 408*a 7*a^2 - 156*a -11*a^2 - 132*a -26*a^2 - 420*a -11*a^2 - 5016*a -6*a^2 - 168*a]
On constate que \(a\) divise la troisième ligne; on peut donc diviser de manière exacte par \(a\):
sage: M[2] = M[2] / a
sage: M # random
[ a -2 2 -1 -1 -1 -38 0]
[ 0 4*a + 24 -2*a - 24 -a + 12 a + 12 -2*a + 12 a + 456 -2*a]
[ 0 0 46*a^2 + 408*a 7*a^2 - 156*a -11*a^2 - 132*a -26*a^2 - 420*a -11*a^2 - 5016*a -6*a^2 - 168*a]
Todo
il serait plus naturel de faire M[2]//a, mais cela ne fonctionne plus Sage >= 6.10.
De plus, le coefficient M[2,2]
est le déterminant de la matrice d’origine:
sage: N[:,:3] # random
[ a -2 2]
[12 4 -2]
[ 0 11 6]
sage: N[:,:3].det() # random
46*a + 408
sage: M[2,2] # random
46*a + 408
Ce phénomène est général et peut être utilisé récursivement:
Algorithme de Gauß-Bareiss
On procède comme pour Gauß sans fractions, y compris pour traiter les lignes avec un coefficient nul dans la colonne du pivot. Cependant, avant de traiter les colonnes \(\geq i+2\) on divise tout le quadrant inférieur composé des lignes et colonnes \(\geq i+2\), par \(M[i,i]\).
Le fonctionnement de l’algorithme repose sur la propriété suivante:
Proposition
Soit \(M\) une matrice sur un anneau intègre. Après avoir traité les \(i\) premières colonnes, \(M_{i,i}\) est le déterminant du \(i\)-ème mineur dominant de la matrice d’origine (correspondant aux \(i\) premières lignes et colonnes). De plus après avoir traité la colonne \(i+1\), ce déterminant divise toutes les coefficients \(M_{i',j'}\) avec \(i',j'\geq i+2\).
Exercice
Vérifier la proposition dans le cas d’une matrice triangulaire supérieure.
Remarque
Pour simplifier, on a supposé ci-dessus que la matrice était carrée et que tous les mineurs dominants étaient non nuls. Modulo les détails techniques usuels (forme échelon réduite plutôt que uni triangulaire supérieure), l’algorithme se généralise à des matrices quelconques sur un anneau intègre.
Le coeur de l’algèbre linéaire est l’étude des matrices modulo des relations d’équivalences (équivalence, conjugaison, similitude), et ce sur les différents types d’anneaux.
Dans chaque cas, on introduit une notion d’ordre (plus conceptuellement de drapeau) qui permet de définir simultanément une forme normale et un algorithme d’élimination permettant de calculer cette forme normale.
Voir par exemple [Storjohan.2004] pour une présentation d’ensemble.
Modulo quelques difficultés supplémentaires (gestion de la torsion, etc.), la plupart des algorithmes de l’algèbre linéaire sur les corps peuvent être adaptés aux anneaux principaux sans changement majeur de complexité, la forme normale de Hermite remplaçant la forme échelon.
Sur les anneaux plus généraux, il reste possible de faire certains calcul (déterminant, …) avec des analogues du pivot de Gauß, mais la plupart des opérations (résolution d’équation, …) nécessitent de nouveaux outils comme les bases de Gröbner.
Algorithme de Gauß-Bareiss
Dans tout cet exercice, on pourra supposer pour simplifier le code que les mineurs calés en haut à gauche sont tous non nuls; autrement dit, au moment de traiter la colonne \(i\), le coefficient \(M_{i,i}\) est non nul, ce qui évite d’avoir à permuter des lignes ou gérer les colonnes caractéristiques.
De manière générale, pour produire des illustrations, ne pas hésiter à faire des hypothèses simplificatrices pour évacuer les détails techniques et se concentrer sur le phénomène ou algorithme à illustrer. «As simple as possible, but no simpler».
(Échauffement) Écrire une fonction qui met une matrice à coefficients dans un corps sous forme échelon à l’aide de l’algorithme de Gauß. Vérifier votre programme pour:
sage: M = matrix([[2, 1, 3], [1, 4, 9], [1, 8, 27]]); M
Écrire une fonction qui met une matrice à coefficients entiers sous forme échelon à l’aide de l’algorithme de Gauß-Bareiss. Vérifier votre programme pour la matrice ci-dessus, puis sur une matrice aléatoire de grande taille.
Évaluer la complexité pratique en prenant des matrices aléatoire de taille \(n=1,2,...\). Comparer avec ce que l’on obtient avec Gauß, et avec Gauß sur un corps fini. Qu’en pensez-vous?
En déduire une fonction qui calcule le déterminant d’une matrice à coefficients entiers.
Faire la même chose pour des matrices à coefficients polynomiaux univariés.
En déduire une fonction qui calcule le polynôme caractéristique d’une matrice.
Noyau
Illustrer sur un exemple bien choisi le calcul du noyau d’une matrice \(M\) à coefficients entiers (ou dans \(\QQ[x]\)!).
Groupes abéliens
Illustrer le théorème de classification des groupes abéliens de type fini sur un exemple bien choisi de groupe abélien \(H\) défini par \(n\) générateurs et relations.
On pourra:
- Réaliser les relations comme sous-module \(R\) de \(F=\ZZ^n\) donné par sa base \(B\) sous forme échelon réduite.
- Réaliser le groupe comme quotient \(F/R\); on pourra par exemple implanter le calcul de la forme normale d’un élément du groupe par réduction modulo la base \(B\)
- Calculer la forme de Smith pour obtenir les diviseurs du groupe; en déduire sa structure.
- Tenter de construire explicitement le générateur correspondant à chaque facteur dans \(F/R\).
- Construire explicitement l’isomorphisme entre \(H\) et sa réalisation comme quotient \(F/R\) en utilisant les matrices qui donnent la forme de Smith.
Exercice: forme de Hermite et de Smith
- Implanter l’algorithme de calcul de la forme de Hermite d’une matrice
- Implanter l’algorithme de calcul de la forme de Smith d’une matrice
- Application à la résolution d’un système
Todo
Un exemple rigolo de système d’équations Diophantiennes
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Algèbre linéaire, formes normales et applications¶
Mathematics is the art of reducing any problem to linear algebra
- William Stein.
Soit \(E\) en ensemble muni d’une relation d’équivalence \(\rho\). Une fonction \(f: E\mapsto E\) donne une forme normale pour \(\rho\) si, pour chaque classe d’équivalence \(C\), tous les élements de \(C\) sont envoyé sur le même élément \(c\) de \(C\). L’élément \(c\) est alors appelé la forme normale des éléments de \(C\).
Exemples
\(E=\ZZ\)
\(\rho\): égalité modulo \(p\)
\(f: x \mapsto x \mod p\)
\(E=\ZZ\times \ZZ\)
\(\rho\): \((a,b) \rho (c,d)\) si \(ad=bc\)
\(f\): ???
…
Quel intérêt?
Les formes normales permettent de représenter les classes d’équivalence et donc de calculer dans le quotient.
Question
Formes normales pour les matrices?
Exercice
Résoudre le système d’équations suivant sur \(GF(5)\):
On donnera une paramétrisation et une base de l’ensemble des solutions.
Solution partielle:
sage: K = GF(5)
sage: M = matrix(K, [[0,0,3,1,4], [3,1,4,2,1], [4,3,2,1,3]]); M
[0 0 3 1 4]
[3 1 4 2 1]
[4 3 2 1 3]
sage: v = vector(SR.var('x1,x2,x3,x4,x5'))
sage: [(eq == 0) for eq in matrix(ZZ,M)*v]
sage: M.echelon_form()
[1 2 0 3 3]
[0 0 1 2 3]
[0 0 0 0 0]
Calcul du pivot de Gauß à la main:
sage: N = copy(M)
sage: N
[0 0 3 1 4]
[3 1 4 2 1]
[4 3 2 1 3]
sage: N[0],N[1] = N[1],N[0]
sage: N
[3 1 4 2 1]
[0 0 3 1 4]
[4 3 2 1 3]
sage: N[0] = N[0] / K(3)
sage: N
[1 2 3 4 2]
[0 0 3 1 4]
[4 3 2 1 3]
sage: N[2] = N[2] - 4*N[0]
sage: N
[1 2 3 4 2]
[0 0 3 1 4]
[0 0 0 0 0]
sage: N[1] = N[1] / K(3)
sage: N
[1 2 3 4 2]
[0 0 1 2 3]
[0 0 0 0 0]
sage: N[0] = N[0] - 3*N[1]
sage: N
[1 2 0 3 3]
[0 0 1 2 3]
[0 0 0 0 0]
Calcul d’une base des solutions:
sage: M.right_kernel()
Vector space of degree 5 and dimension 3 over Finite Field of size 5
Basis matrix:
[1 0 0 4 4]
[0 1 0 3 3]
[0 0 1 1 4]
Remarque Sage
Le système ci-dessus a été fabriqué avec:
sage: random_matrix(GF(5),3,5, algorithm='echelonizable', rank=2); M # random
[0 0 3 1 4]
[3 1 4 2 1]
[4 3 2 1 3]
On se place dans un corps \(K\) quelconque
Définition
Une matrice est sous forme échelon (en lignes) si le nombre de zéros précédant la première valeur non nulle d’une ligne augmente ligne par ligne jusqu’à ce qu’il ne reste plus que des zéros:
Les colonnes caractéristiques sont les colonnes contenant les pivots (soulignés ci-dessus), c’est-à-dire les premiers coefficients non nul d’une ligne.
Une matrice est sous forme échelon réduite si les pivots valent 1 et si les autres coefficients dans les colonnes des pivots sont nuls:
Exemple
sage: M2 = random_matrix(QQ, 4, 8, algorithm='echelon_form', num_pivots=3); M2 # random
[ 1 -3 0 -2 0 3 1 0]
[ 0 0 1 -5 0 -2 -1 -1]
[ 0 0 0 0 1 -1 3 1]
[ 0 0 0 0 0 0 0 0]
sage: M2.pivots() # random
(0, 2, 4)
Remarque
L’algorithme du pivot de Gauß-Jordan transforme une matrice jusqu’à ce qu’elle soit sous forme échelon (réduite).
Exercice
Revenons à notre matrice:
sage: M
[0 0 3 1 4]
[3 1 4 2 1]
[4 3 2 1 3]
Déterminer si les vecteurs suivants sont des combinaisons linéaires des lignes de \(M\):
sage: u = vector([1, 2, 4, 1, 0])
sage: v = vector([2, 1, 4, 0, 1])
Solution
Sur \(M\), ce n’est pas évident. Par contre, si on part de sa forme échelon \(N\):
sage: N = M.echelon_form(); N
On voit aisément que \(u\) est combinaison linéaire des lignes de \(N\):
sage: u
(1, 2, 4, 1, 0)
sage: u - N[0]
(0, 0, 4, 3, 2)
sage: u - N[0] - 4*N[1]
(0, 0, 0, 0, 0)
Mais pas \(v\):
sage: v
(2, 1, 4, 0, 1)
sage: v - 2*N[0]
(0, 2, 4, 4, 0)
sage: v - 2*N[0] - 4*N[1]
(0, 2, 0, 1, 3)
Théorème-Définition: réduction modulo forme échelon
Soit \(N\) une matrice sous forme échelon, et \(u\) un vecteur, Alors, on peut écrire de manière unique \(u = q N + r\), où \(qN\) est une combinaison linéaire de lignes de \(N\) et \(r\) a des coefficients nuls dans les colones caractéristiques de \(N\).
(moralement, on ajoute \(u\) en dernière ligne de \(N\) et on finit le pivot de Gauß).
On appelle \(r\) la réduction de \(u\) modulo \(N\).
Exercice
Considérons les deux polynômes suivants:
sage: x = QQ['x'].gen()
sage: P = x^2 - 2*x + 1
sage: U = x^5 - x + 2
Considérer la base canonique \(x^5, x^4, \ldots, 1\) des polynômes de degré inférieur à 5, et écrire la matrice \(N\) des polynômes \(x^3P,x^2P,xP,P\), vus comme vecteurs dans cette base. De même écrire le vecteur \(u\) représentant le polynôme \(U\) dans cette base. Calculer la réduction de \(u\) module \(N\).
Que constatez-vous?
Solution
Construisons N et u:
sage: N = matrix([[1,-2,1,0,0,0],[0,1,-2,1,0,0],[0,0,1,-2,1,0],[0,0,0,1,-2,1]])
sage: u = vector([1, 0, 0, 0, -1, 2])
Calculons la réduction:
sage: u - N[0]
(0, 2, -1, 0, -1, 2)
sage: u - N[0] - 2*N[1]
(0, 0, 3, -2, -1, 2)
sage: u - N[0] - 2*N[1] - 3*N[2]
(0, 0, 0, 4, -4, 2)
sage: u - N[0] - 2*N[1] - 3*N[2] -4*N[2]
(0, 0, -4, 12, -8, 2)
sage: u - N[0] - 2*N[1] - 3*N[2] -4*N[3]
(0, 0, 0, 0, 4, -2)
Comparons cela avec la division Euclidienne:
sage: U % P
4*x - 2
sage: U // P
x^3 + 2*x^2 + 3*x + 4
Conclusion
La division Euclidienne est un cas particulier de réduction d’un vecteur modulo une forme échelon. Le vecteur \(q\) donne la résultat de la division et \(r\) le reste.
Exercice: matrices à deux lignes
Pour chacunes des matrices suivantes, écrire la première étape du pivot de Gauß sous forme de multiplication à gauche par une matrice \(P\) de taille \(2\times 2\)
sage: var('a1,b1,c1,a2,b2,c2')
Échange lignes \(1\) et \(2\) pour:
sage: M1 = matrix([[0,b1,c1],[1,b2,c2]]); M1
Renormalisation \(L_1 = \frac{1}{a_1} L_1\) pour:
sage: M2 = matrix([[a1,b1,c1],[0,b2,c2]]); M2
Pivot \(L_2 = L_2 -\frac{a_2}{a_1}L_1\) pour:
sage: M3 = matrix([[a1,b1,c1],[a2,b2,c2]]); M3
Solutions:
sage: P = matrix([[0,1],[1,0]]); P, P*M1
sage: P = matrix([[1/a1,0],[0,1]]); P, P*M2
sage: P = matrix([[1,0],[-a2/a1,1]]); P, P*M3
Remarques
- Les opérations sur les lignes peuvent être implantées par multiplication à gauche par des matrices inversibles.
- Si \(N\) est obtenue de \(M\) par l’algorithme du pivot de Gauß, alors \(N=PM\) où \(P\) est une matrice inversible, éventuellement de déterminant \(1\) (le produit des matrices ci-dessus).
- S’il n’y a pas de permutation à effectuer, alors on peut écrire \(M\) sous la forme \(M=LU\), où \(U=N\) est triangulaire supérieure (upper triangular), et \(L=P^{-1}\) est triangulaire inférieure (lower triangular): le produit des inverses des matrices ci-dessus. On appelle cela la décomposition `LU`.
Exercice
Déterminer la décomposition \(M=LU\) de notre matrice favorite.
Solution:
sage: M.LU()
Disons ici que deux matrices \(M\) et \(M'\) de \(M_{n,m}(K)\) sont équivalentes (modulo l’action de \(GL_n(K)\) à gauche) s’il existe une matrice inversible \(P\) telle que \(M=PM'\).
Exercice:
Vérifier que cela définit une relation d’équivalence!
Question
La remarque précédente dit que si deux matrices \(M\) et \(M'\) donnent la même forme échelon réduite par Gauß, alors elles sont équivalentes.
Réciproque?
Démonstration de la réciproque
Soient \(M\) et \(M'\) deux matrices équivalentes, et \(N\) et \(N'\) leurs formes échelons réduites. On veut montrer que \(N=N'\).
On note que \(N\) et \(N'\) sont équivalentes: on peut prendre \(P\) telle que \(N=PN'\).
Remarque: notons \(N_k\) la sous-matrice composée des \(k\) premières colonnes de \(N\) et de même pour \(N'\); elles sont encore sous forme échelon. Comme \(P\) est inversible, elles sont de même rang, et donc ont le même nombre de lignes non nulles.
Conclusion: les colonnes caractéristiques de \(N\) et \(N'\) coïncident.
En regardant ce qui se passe au niveau des pivots, on déduit que les \(rang(N')\) premières colonnes de \(P\) sont celles de l’identité. Il s’ensuit que \(N=N'\).
Théorème
On considère les matrices \(n\times m\) à coefficients dans un corps \(K\). La forme échelon réduite donne une forme normale pour les matrices modulo l’action de \(GL_n(K)\) à gauche.
Corollaire
Il y a une certaine liberté dans l’ordre d’exécution des opérations du pivot de Gauß. Le théorème précédent garanti que le résultat final ne dépend pas de l’ordre des calculs.
Reprenons notre matrice:
sage: M = matrix(GF(5), [[0,0,3,1,4], [3,1,4,2,1], [4,3,2,1,3]]); M
et sa forme échelon:
sage: M.echelon_form()
Pour le moment, cette forme échelon est décrite comme le résultat d’un calcul: l’application du pivot de Gauß. C’est opératoire, mais pas très conceptuel.
Peut-on faire mieux?
Exercice
Soient \(M\) et \(M'\) deux matrices de \(M_{n,m}(K)\), que l’on voit comme deux paquets de \(n\) vecteurs de \(K^m\). Montrer que \(M\) et \(M'\) sont équivalentes (modulo l’action de \(GL_n(K)\) à gauche) si et seulement si les vecteurs engendrent le même sous-espace vectoriel de \(K^m\).
Solution
Si les matrices sont équivalentes, la multiplication à gauche par la matrice inversible permet d’exprimer les vecteurs de l’une en fonction de l’autre, et réciproquement. Ils engendrent donc le même sous-espace vectoriel.
Réciproquement, supposons que les vecteurs engendrent le même espace vectoriel \(F\). S’ils forment une base, il suffit de prendre la matrice \(P\) qui exprime la première base en fonction de la deuxième (\(P\) est inversible!), de sorte que \(M=PM'\). Sinon on remplace \(M\) et \(M'\) par leurs formes échelon (qui leurs sont équivalentes); et on prend la matrice \(P\) pour les lignes non nulles (qui forment une base), et on la complète par l’identité pour les lignes nulles.
Corollaire
L’ensemble quotient \(GL_n(K) \backslash M_{n,m}(K)\) représente l’ensemble des sous-espaces vectoriels de dimension au plus \(n\) dans \(K^m\). Cet ensemble est naturellement muni d’une structure de variété appelée variété Grassmanienne.
Corollaire
La forme échelon réduite donne une forme normale pour les sous-espaces vectoriels!
Exercice
Compter le nombre de sous espaces vectoriels de rang \(2\) d’un espace de dimension \(4\) sur \(GL(5)\).
Exercice
- Compter le nombre de points, droites, plans et hyperplans dans \(GF(q)^3\) en fonction de leur rang.
- On se place maintenant dans \(\RR^3\). Décrire géométriquement, en fonction de leur forme échelon, comment ces sous espaces vectoriels se positionnent dans l’espace.
Todo
Solutions
Exercice
Soit \((e_1,\dots, e_5)\) la base canonique de \(K^5\), et soit \(E\) le sous espace vectoriel de \(K^5\) engendré par les lignes de notre matrice favorite \(M\):
sage: M
Pour \(i\) de \(1\) à \(5\), calculer la dimension de l’espace vectoriel
Puis décrire les quotients successifs \(E_i / E_{i+1}\).
Digression: lien avec les groupes de permutations
Pour manipuler un sous-groupe \(G\) du groupe symétrique \(S_n\), on avait considéré le sous-groupe \(G_{n-1}\) des éléments fixant \(n\), puis ceux fixant \(n\) et \(n-1\), et ainsi de suite récursivement.
Formellement, on avait considéré la suite des groupes symétriques emboîtés:
et la suite induite des groupes emboîtés \(G_i:=G \cap S_i\):
L’étude de \(G\) se ramenait alors à l’étude des quotients successifs \(G_i/G_{i-1}\).
Appliquons le même programme.
Définition: Drapeau
Un drapeau complet d’un espace vectoriel \(V\) de dimension \(n\) est une suite maximale de sous-espaces strictement emboîtés:
Définition: Drapeau canonique
À chaque base ordonnée, on peut associer naturellement un drapeau complet. Ici on considérera principalement le drapeau canonique associé à la base canonique \(e_1,\cdots,e_m\) de \(V=K^m\):
Note: on prend les éléments dans cet ordre pour que cela colle avec nos petites habitudes de calcul du pivot de Gauß. Et pour alléger les notations, on utilisera plutôt:
Formes échelon et bases adaptées
Dans ce formalisme, qu’est-ce qu’une matrice sous forme échelon?
C’est une base \(B\) d’un espace vectoriel \(E\) adaptée à un drapeau complet donné. C’est-à-dire une base sur laquelle on peut lire immédiatement les sous espaces \(E_i:=E\cap \overline V_i\):
Le pivot de Gauß est un algorithme de calcul de base adaptée.
Définition intrinsèque des colonnes caractéristiques
Remarque: en passant de \(E_{i+1}\) à \(E_i\), la dimension croît de \(0\) ou de \(1\).
Cela permet de donner une définition intrinsèque de la notion de colonnes caractéristiques d’un sous espace vectoriel \(E\): les \(i\) tels que la dimension de \(E_i\) croît strictement. Cela décrit la position de \(E\) par rapport à un drapeau complet fixé.
Évidemment, sur une forme échelon pour \(E\), cela correspond aux colonnes \(i\) pour lesquelles on a un vecteur de la forme \(e_i+\cdots\).
Formes échelon réduites
Considérons deux bases adaptées d’un même espace vectoriel \(E\). Pour \(i\) une colonne caractéristique, on note \(a_i\) et \(b_i\) les vecteurs de la forme \(a_i=e_i+\cdots\) et \(b_i=e_i+\cdots\).
Alors \(a_i-b_i\in V_{i+1}\); autrement dit \(a_i=b_i\) dans le quotient \(E_i/E_{i+1}\).
Prendre une forme échelon réduite, c’est faire un choix d’un représentant (relativement canonique) \(a_i\) dans chaque quotient \(E_i/E_{i+1}\): celui qui a des zéros aux autres colonnes caractéristiques.
Ce formalisme montre que le vecteur \(a_i\) est intrinsèque à \(E\) (et au choix du drapeau complet). En particulier il est clair qu’il est complètement indépendant des autres coefficients de la forme échelon réduite, même si opératoirement le calcul de \(a_i\) par Gauß passe par ceux-ci.
Remarque
La permutation \(P\) apparaissant dans le calcul de l’algorithme de Gauß a une interprétation géométrique naturelle (position du drapeau \(\langle v_1\rangle, \langle v_1,v_2\rangle\) par rapport au drapeau canonique).
Les variétés Grassmaniennes et ses variantes (variétés de drapeaux, …) et leur multiples généralisations sont l’objet d’études approfondies en géométrie. La combinatoire y joue un rôle important: l’apparition d’une permutation \(P\) dans le pivot de Gauß est le prototype du type de lien.
Todo
Faire un résumé ici
Todo
vérifier / homogénéiser les notations
Exercice: résolution d’équations linéaires
Soit \(E\) un ensemble d’équations linéaires/affines. Retrouver les algorithmes usuels de résolution: existence de solution, dimension, base et paramétrisation de l’espace des solutions.
Exercice: calcul avec les sous espaces vectoriels
On considère des sous espaces \(E\), \(F\), … de \(V=K^n\) donnés par des générateurs ou des équations. Donner des algorithmes (et leur complexité!) pour:
- Déterminer une base de \(E\).
- Tester si un vecteur \(x\) appartient à \(E\).
- Tester si \(E=F\).
- Tester si deux vecteurs \(x\) et \(y\) de \(V\) sont égaux modulo \(E\).
- Calculer l’orthogonal d’un sous-espace vectoriel.
- Calculer la somme \(E+F\) et l’intersection \(E\cap F\) de deux espaces vectoriels.
- Calculer la sous-algèbre de \(V\) engendrée par \(E\) (en supposant \(V\) muni d’une structure d’algèbre \((V,+,.,*)\)).
- Plus généralement: clôture de \(E\) sous des opérations linéaires.
- Calculer dans l’espace quotient \(E/F\).
- Cas de la dimension infinie?
Exercice: calcul avec les morphismes
Soit \(\phi\) une application linéaire entre deux espaces vectoriels \(E\) et \(F\) de dimension finie. Donner des algorithmes pour:
- Calculer le noyau de \(\phi\).
- Calculer l’image de \(\phi\).
- Calculer l’image réciproque par \(\phi\) d’un vecteur \(f\) de \(F\).
- Arithmétique: composition, combinaison linéaires, inverse.
- Calculer le polynôme caractéristique.
- Calculer les valeurs propres de \(\phi\).
- Calculer les espaces propres de \(\phi\).
Todo
- Décomposition LU, exercice en TD ou TP
- Le cours est un peu long; décider quoi déplacer en TP
La forme échelon d’une matrice joue un rôle central en algèbre linéaire car:
- Il existe des algorithmes relativement peu coûteux pour la calculer (par exemple Gauß: \(O(n^3)\)).
- La plupart des problèmes en algèbre linéaire sur un corps se traitent aisément sur cette forme échelon.
- La forme échelon a un sens algébrique: c’est une forme normale pour la relation d’équivalence induite par l’action à gauche du groupe linéaire.
- La forme échelon a un sens géométrique: c’est une forme normale pour un sous-espace vectoriel; elle décrit sa position par rapport au drapeau canonique.
Nous verrons d’autres formes normales pour d’autres classes d’équivalences de matrices.
Soit \(V\) une liste de vecteurs dans \(E=\QQ^{10}\), comme par exemple:
sage: V = random_matrix(QQ, 4, 10, algorithm='echelonizable', rank=3).rows() # random
sage: V
[(1, 4, -5, 3, -19, 2, -56, -19, -5, -43),
(4, 16, -20, -11, 75, 8, 229, 52, 26, 153),
(5, 20, -25, -19, 121, 10, 368, 87, 43, 251),
(0, 0, 0, -2, 13, 0, 39, 11, 4, 28)]
On veut calculer une base du sous-espace vectoriel engendré par \(V\). On peut l’obtenir simplement avec les outils déjà présents:
sage: E = QQ^10
sage: E.span(V)
Vector space of degree 10 and dimension 3 over Rational Field
Basis matrix:
[ 1 4 -5 0 0 2 1 -3 1 -2]
[ 0 0 0 1 0 0 0 1 -2 -1]
[ 0 0 0 0 1 0 3 1 0 2]
Implanter votre propre fonction baseSEV(V)
qui calcule une
telle base en se ramenant à du calcul matriciel.
Indications:
Utiliser la méthode
echelon_form
des matrices. Si vous n’avez pas encore eu l’occasion d’implanter un pivot de Gauß, faites le au préalable, en faisant pour simplifier l’hypothèse que toutes les colonnes sont caractéristiques, de sorte que le résultat est triangulaire supérieur avec pivots sur la diagonale.Essayez les commandes suivantes:
sage: M = matrix(V) sage: list(M) sage: M[1].is_zero() sage: [ n^2 for n in range(20) if n.is_prime() ]
Soit \(V\) une liste de vecteurs et \(u\) un autre vecteur. On veut tester si \(u\) est dans le sous espace vectoriel engendré par \(V\):
sage: u = E([1, 2, 5, 3, 0, 1, 6, 3, 0, 5])
sage: u in V
False
Comme ci-dessus, implanter votre propre fonction appartient(V,u
qui se ramène à du calcul matriciel. On pourra par exemple supposer
que \(V\) est sous forme échelon, et calculer la réduction de \(u\) par
rapport à \(V\).
Indication: mettre \(V\) sous forme de matrice \(M\) et utiliser \(M.pivots()\) pour en récupérer les colonnes caractéristiques.
Version avancée: calculer \(q\) et \(r\) tels que \(u=qV + r\).
Implanter votre propre fonction SEV_egaux(U, V)
qui teste
si deux listes deux vecteurs engendrent le même sous espace
vectoriel.
Implanter votre propre fonction SEV_orthogonal(V)
pour
calculer une base de l’orthogonal de \(\langle V\rangle\),
c’est-à-dire l’ensemble des vecteurs \(u\) du dual de \(E\) tel
que \(\langle u,v\rangle=0\).
Quel rapport avec la résolution d’équations?
Implanter votre propre fonction SEV_somme(U, V)
qui calcule une
base de la somme des deux sous-espaces vectoriels \(\langle U\rangle\)
et \(\langle V\rangle\).
De même implanter SEV_intersection(U,V)
et
SEV_en_somme_directe(U,V)
.
Todo
Proposer des exercices d’illustration à base d’interact: par exemple une mini application de calcul guidé de pivot de Gauß, ou de réduction d’un vecteur par rapport à une matrice échelonnée.
Voir: La bibliothèque de Tsetlin
Ce texte est à approcher comme les textes de l’agrégation: il s’agit d’un menu à la carte; vous pouvez choisir d’étudier certains points, pas tous, pas nécessairement dans l’ordre, et de façon plus ou moins fouillée. Vous pouvez aussi vous poser d’autres questions que celles indiquées plus bas. L’objectif final est de concevoir un mini-développement de 5 minutes comportant une partie traitée sur ordinateur et, si possible, des représentations graphiques de vos résultats.
[Storjohan.2004] | Algorithms for Matrix Canonical Forms, Arne Storjohan, PhD Thesis, Department of Computer Science, Swiss Federal Institute of Technology – ETH, 2000 |
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Algèbre linéaire rapide¶
Dans ce cours, nous explorons quelques limites de l’algorithme de Gauß en terme de performances, et donnons des approches pour les contourner.
Todo
ajouter des #random où utile; mettre les %display latex juste là où c’est utile
Todo
ajouter des exercices pour les faire manipuler
On a vu que l’algorithme de Gauß était de complexité \(O(n^3)\), au moins dans son implantation naïve. Vérifions cela expérimentalement avec les petits outils du fichier gauss.py:
sage: %runfile media/gauss.py
sage: m = matrice_inversible(3, GF(7)); m # random
[1 0 0]
[0 6 2]
[2 5 0]
sage: gauss(m) # random
[3 1 6]
[0 2 6]
[0 0 2]
Commençons par un corps fini:
sage: import functools
sage: construit_donnee = functools.partial(matrice_inversible, corps=GF(7))
sage: construit_donnee(3) # random
[5 4 5]
[6 2 2]
[1 2 5]
sage: temps(gauss, 10, construit_donnee) # random
0.00010991096496582031
sage: temps(gauss, 20, construit_donnee) # random
0.0004279613494873047
sage: t = [temps(gauss, 2^k, construit_donnee) for k in range(9)]; t # random
[1.2874603271484375e-05,
2.193450927734375e-05,
3.0994415283203125e-05,
7.295608520507812e-05,
0.00026416778564453125,
0.0012059211730957031,
0.007287025451660156,
0.04861617088317871,
0.3510730266571045]
sage: [ t[i+1]/t[i] for i in range(len(t)-1) ] # random
[1.7037037037037037,
1.4130434782608696,
2.353846153846154,
3.6209150326797386,
4.564981949458484,
6.04270462633452,
6.671607119486978,
7.221322047363801]
Un peu plus de valeurs supplémentaires calculées au préalable:
sage: t = [2.8848648071289062e-05, 2.9087066650390625e-05, 4.1961669921875e-05, 9.298324584960938e-05, 0.0003731250762939453, 0.0017020702362060547, 0.010125160217285156, 0.04890704154968262, 0.3750150203704834, 2.7238361835479736, 20.545907974243164, 182.26634407043457, 1334.3144991397858]
sage: [ t[i+1]/t[i] for i in range(len(t)-1) ]
[1.0082644628099173, 1.4426229508196722, 2.215909090909091, 4.012820512820513, 4.561661341853035, 5.948732315450343, 4.830248657812941, 7.667914649662898, 7.263272230688392, 7.543004274023845, 8.871174946316705, 7.320685044432319]
sage: points(enumerate(_))
C’est plausible.
Prenons maintenant le corps des rationnels:
sage: construit_donnee = functools.partial(matrice_inversible, corps=QQ)
sage: t = [temps(gauss, 2^k, construit_donnee) for k in range(8)]; t # random
[2.7179718017578125e-05, 3.910064697265625e-05, 0.00010395050048828125, 0.0005209445953369141, 0.003559112548828125, 0.028071880340576172, 0.25052881240844727, 2.8525619506835938]
sage: t = [6.389617919921875e-05, 0.00010395050048828125, 0.000308990478515625, 0.001764059066772461, 0.012479066848754883, 0.09727597236633301, 0.8789999485015869, 9.599533081054688, 127.58634281158447, 2059.1530270576477]
sage: [ t[i+1]/t[i] for i in range(len(t)-1) ]
[1.62686567164179, 2.97247706422018, 5.70910493827161, 7.07406406271118, 7.79513192334881, 9.03614661585030, 10.9209711529777, 13.2908904770988, 16.13929031650778]
Bof …
Avec la matrice de Hilbert:
sage: def hilbert(n):
....: return matrix(QQ, n, n, lambda i,j: 1/(1+i+j))
....: hilbert(3)
[ 1 1/2 1/3]
[1/2 1/3 1/4]
[1/3 1/4 1/5]
sage: [temps(gauss, 2^k, hilbert) for k in range(8)] # random
[9.393692016601562e-05, 4.887580871582031e-05, 0.000102996826171875, 0.0005269050598144531, 0.003654003143310547, 0.028528928756713867, 0.23932909965515137, 2.2389848232269287]
sage: t = [2.193450927734375e-05, 3.4809112548828125e-05, 9.202957153320312e-05, 0.0004980564117431641, 0.003587961196899414, 0.029154062271118164, 0.2275228500366211, 2.2509679794311523, 25.5708429813385, 328.3195171356201]
sage: [ t[i+1]/t[i] for i in range(len(t)-1) ]
[1.58695652173913, 2.64383561643836, 5.41191709844560, 7.20392532312111, 8.12552329058409, 7.80415600134117, 9.89337105731950, 11.3599319115150, 12.8396047551200]
Bof!
Todo
Ces bancs d’essais suggèrent que la complexité n’est pas pire que \(O(n^4)\), ce qui n’est guère mieux que ce que l’on obtient en modulaire ou multimodulaire. Trouver quelque chose de plus frappant.
Prenons un corps de fractions rationnelles:
sage: K = QQ['x'].fraction_field()
sage: construit_donnee = functools.partial(random_matrix, K)
sage: construit_donnee(2)
[ (-3/8*x + 3/25)/(-1/13*x^2 + 1/3*x) (-1/3*x - 1)/(-2*x^2 + x + 1)]
[ (4/169*x^2 + 1/9*x)/(x^2 + 9*x - 1/5) (-1/2*x^2 + 1/2*x + 207)/(2/7*x^2 + 3/2*x + 1/2)]
sage: t = [temps(gauss, n, construit_donnee) for n in range(10)]; t
[1.3113021850585938e-05, 2.193450927734375e-05, 0.00019097328186035156, 0.0006430149078369141, 0.0026559829711914062, 0.0067059993743896484, 0.01826310157775879, 0.04449200630187988, 0.11454296112060547, 0.6179559230804443]
sage: t = [1.1920928955078125e-05, 1.9073486328125e-05, 0.00018310546875, 0.0007388591766357422, 0.002237081527709961, 0.007543087005615234, 0.021083831787109375, 0.08204507827758789, 0.15540504455566406, 0.9841179847717285, 22.702343940734863]
sage: [ t[i+1]/t[i] for i in range(len(t)-1) ]
[1.60000000000000, 9.60000000000000, 4.03515625000000, 3.02775088738303, 3.37184269423425,
2.79511979265440, 3.89137416319884, 1.89414219375687, 6.33259999754532, 23.0687217305563]
Pourquoi est-ce que notre analyse de complexité est si éloignée de la réalité?
Parce que l’on a un mauvais modèle!
On a mesuré la complexité arithmétique de l’algorithme de Gauß, la métrique étant donnée par le nombre d’opérations arithmétiques.
Or, comme l’a constaté toute personne ayant calculé un pivot de Gauß à la main, les coefficients ont tendance à grossir:
sage: %display latex # not tested
sage: gauss(matrice_inversible(10)) # random
[ 1 0 -2 -1/2 1 2 1 0 -1 -1]
[ 0 1 0 -5/2 5 0 3 2 0 -3]
[ 0 0 1 -5/4 11/2 -1 2 3/2 3/2 -5/2]
[ 0 0 0 1 -14/9 8/9 -10/9 -2/3 -2/9 2/3]
[ 0 0 0 0 1 -19/31 -11/62 -3/62 -2/31 -21/31]
[ 0 0 0 0 0 1 5/6 -5/9 2/81 38/27]
[ 0 0 0 0 0 0 1 -13/3 -74/27 7/9]
[ 0 0 0 0 0 0 0 1 194/495 -94/165]
[ 0 0 0 0 0 0 0 0 1 1467/436]
[ 0 0 0 0 0 0 0 0 0 1]
sage: def max_coeff(m):
....: return max([c.numer() for row in m.rows() for c in row])
sage: t = [ max_coeff(gauss(matrice_inversible(2^k))) for k in range(7) ] # random
sage: t
[1, 1, 1, 19, 4238342698, 99340450694580511972871852, 49519664469784658153819267407199333624664412533859761535203139]
Considérer que le temps nécessaire à une opération arithmétique est constant est donc abusif!
Une meilleure mesure est donc la complexité en bits, puisque les opérations arithmétiques sont de complexité polynomiale en \(n\) (en fait en gros \(n\log n\)) où \(n\) est le nombre de bits:
sage: tt = [x.ndigits(2) for x in t]
sage: [float(tt[i+1]/tt[i]) for i in range(len(t)-1)]
[1.0, 1.0, 2.0, 5.0, 2.6, 2.3846153846153846]
Cela suggère expérimentalement que, pour les rationnels, le nombre de bits est borné par un petit polynôme en \(n\).
Todo
Donner les complexités explicites, quitte à ne pas les démontrer
Exemple
Entrée: une matrice \(M\) à coefficients entiers
Sortie: son déterminant
C’est un problème typique: on a un résultat qui est relativement petit (un nombre) mais son calcul direct nécessite de manipuler pleins de gros coefficients.
Algorithme modulaire
- Déterminer une borne \(b\) sur le déterminant (par ex: borne de Hadamard)
- Choisir un grand nombre premier \(p>2b\)
- Calculer \(\det(M)\) modulo \(p\):
- En déduire \(\det(M)\).
Algorithme multimodulaire
- Comme ci-dessus
- Choisir plusieurs (combien?) petits nombres premiers tels que \(p_1\cdots p_k>2b\)
- Calculer \(\det(M)\) modulo \(p_i\) pour chaque \(i\)
- Utiliser le lemme chinois pour reconstruire \(\det(M)\).
Intérêt du multimodulaire?
- Calculer avec des corps finis dont les éléments tiennent dans un entier machine. Chaque opération arithmétique sur \(GF(p_i)\) correspond à un petit nombre d’opérations du processeurs.
- Voire dans un flottant machine (seul intérêt: les processeurs actuels sont plus optimisés pour manipuler des flottants …).
- Parallélisation
Remarque
La clef des algorithmes précédents est que l’on avait un morphisme du corps de base dans un/plusieurs corps où l’arithmétique était plus efficace, avec la possibilité d’inverser localement ce morphisme à la fin.
La même technique s’adapte à chaque fois que l’on a une explosion des coefficients intermédiaires, et un morphisme dans un (ou plusieurs) corps où l’on maîtrise la croissance des coefficients intermédiaires.
Exemple de problème
Entrée: une matrice \(M\) à coefficients polynomiaux
Sortie: son déterminant
Algorithme
- Déterminer une borne \(k\) sur le degré du déterminant.
- Choisir \(k\) éléments du corps de base.
- Prendre le morphisme d’évaluation en ces points:
- Calculer \(\phi(\det(M))\) en se ramenant au calcul de \(k\) déterminants de matrices à coefficients dans le corps de base.
- Reconstruire \(\det(M)\), par exemple par interpolation de Lagrange, FFT inverse, …
Exercice
Donner une borne de complexité pour le calcul du polynôme caractéristique d’une matrice dans \(GF(p)\).
Exemple de problème
Entrée: une matrice \(M\) carrée inversible à coefficients rationnels
Sortie: l’inverse de \(M\)
Que se passe-t’il si on prend \(M\) modulo \(p\)? modulo \(p^k\)
Exercice: Expansion \(3\)-adique d’entiers
Voici quelques expansions \(3\)-adiques d’entiers:
sage: K = Zp(3)
sage: %display latex
sage: K(1)
1 + O(3^20)
sage: K(3)
3 + O(3^21)
sage: K(3^2)
3^2 + O(3^22)
sage: K(3^3)
3^3 + O(3^23)
sage: K(24)
2*3 + 2*3^2 + O(3^21)
sage: K(25)
1 + 2*3 + 2*3^2 + O(3^20)
- Calculer l’expansion \(3\)-adique de \(15\) et de \(-1\).
- Calculer le produit de \(1+2\cdot3 +2\cdot3^2+\cdots\) par \(4\).
Solutions
sage: K(15)
2*3 + 3^2 + O(3^21)
sage: K(-1)
2 + 2*3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^5 + 2*3^6 + 2*3^7 + 2*3^8 + 2*3^9 + 2*3^10 + 2*3^11 + 2*3^12 + 2*3^13 + 2*3^14 + 2*3^15 + 2*3^16 + 2*3^17 + 2*3^18 + 2*3^19 + O(3^20)
sage: 1 / K(4)
1 + 2*3 + 2*3^3 + 2*3^5 + 2*3^7 + 2*3^9 + 2*3^11 + 2*3^13 + 2*3^15 + 2*3^17 + 2*3^19 + O(3^20)
sage: 1 / K(4) * K(4)
1 + O(3^20)
Tronquer revient à considèrer le morphisme partiel \(\phi\) de \(\QQ\) dans \(\ZZ/p^k\ZZ\):
- Ce morphisme n’est définit que pour les rationnels \(x\) dont le dénominateur n’est pas divisible par \(p\)
- Si on connaît \(\phi(x)\) pour \(k\) suffisamment grand, on peut retrouver \(x\) par reconstruction rationnelle. Rappel: encore une conséquence d’Euclide étendu!
Algorithme
- Prendre un nombre premier \(p\) qui ne divise pas le déterminant de \(M\).
- Une bonne stratégie est de choisir \(p\) au hasard! Statistiquement il sera bon, et sinon on s’en rendra compte et on recommencera.
- Calculer l’inverse \(N\) de \(M\) modulo \(p\).
- Raffiner itérativement cette solution: - Supposons que l’on ait \(N\) tel que \(MN=1\) modulo \(p^k\) - Prendre \(R\) tel que \(MN = 1+p^k R\) - Poser \(N':=N(1-p^kR)\) - Alors \(MN'=1-p^{2k} R\)
- \(k\) double à chaque itération!!!
- Lorsque \(k\) est suffisamment grand, on retrouve \(M^{-1}\) par reconstruction rationnelle de chacun de ses coefficients.
Mise en contexte: on a écrit notre matrice \(M\) comme une série:
où chaque \(M_i\) est essentiellement une matrice mod \(p\), et on a utilisé la technique classique de l’itération de Newton pour calculer une solution de plus en plus précise de l’équation \(MN=1\).
Considérons une matrice creuse:
sage: M = random_matrix(GF(7), 19, sparse=True, density=1/3)
sage: M
[2 0 0 0 2 2 0 2 0 0 0 4 0 0 3 5 0 0 0]
[2 0 0 0 0 3 0 6 0 0 3 0 6 0 3 5 6 0 5]
[3 6 2 0 2 2 0 3 2 1 1 0 1 6 6 0 0 1 0]
[0 4 6 2 0 1 0 0 0 6 0 5 0 0 2 2 2 1 0]
[0 0 0 6 2 0 0 1 0 5 2 0 0 0 0 0 4 1 0]
[0 0 4 0 0 0 0 0 0 4 0 0 0 2 0 0 2 0 0]
[0 0 0 0 2 0 0 2 0 0 3 5 0 4 0 1 0 0 0]
[6 0 0 0 0 6 0 0 0 4 5 0 0 3 6 4 0 0 4]
[0 0 0 0 0 0 2 0 0 1 4 0 0 0 6 0 0 2 0]
[0 1 4 0 0 6 5 0 5 0 6 3 3 0 0 0 0 0 2]
[2 0 0 2 0 2 0 0 0 0 0 4 0 6 0 0 4 0 0]
[4 0 1 0 4 0 0 0 0 0 3 5 0 0 3 0 0 2 0]
[0 4 0 6 0 0 0 0 5 6 0 0 1 5 5 4 0 5 3]
[0 0 5 0 6 0 2 2 5 5 2 1 3 0 0 4 0 0 5]
[0 0 5 0 5 0 0 4 0 0 0 0 5 4 0 2 0 0 0]
[0 0 0 0 0 0 0 4 0 0 0 0 0 5 0 0 0 0 3]
[0 1 3 4 0 1 0 0 0 4 0 0 0 0 0 1 5 6 0]
[6 0 5 0 0 0 0 0 0 0 1 0 6 0 0 0 0 0 0]
[5 0 0 2 0 0 6 6 0 6 0 5 0 0 0 0 0 0 0]
Et appliquons un pivot de Gauß partiel:
sage: gauss(M,10)
[1 0 0 0 1 1 0 1 0 0 0 2 0 0 5 6 0 0 0]
[0 1 5 0 1 1 0 0 5 6 6 6 6 1 2 4 0 6 0]
[0 0 1 0 0 0 0 0 0 1 0 0 0 4 0 0 4 0 0]
[0 0 0 1 5 2 0 0 4 5 2 1 2 5 4 0 1 6 0]
[0 0 0 0 1 3 0 5 0 0 2 2 4 0 0 0 4 0 1]
[0 0 0 0 0 2 0 1 4 3 4 1 2 5 4 0 5 0 0]
[0 0 0 0 0 1 0 6 0 0 6 1 6 4 0 1 6 0 5]
[0 0 0 0 0 4 0 3 0 4 3 0 3 3 4 3 3 0 3]
[0 0 0 0 0 0 2 0 0 1 4 0 0 0 6 0 0 2 0]
[0 0 0 0 0 1 5 5 0 2 2 6 1 3 5 3 1 1 3]
[0 0 0 0 0 4 0 2 6 4 6 1 2 3 3 2 1 2 5]
[0 0 0 0 0 3 0 3 0 6 3 4 0 3 4 4 3 2 0]
[0 0 0 0 0 2 0 2 3 0 4 3 3 2 1 2 0 1 2]
[0 0 0 0 0 3 2 0 5 0 4 3 0 1 0 4 5 0 6]
[0 0 0 0 0 6 0 0 0 2 4 4 6 5 0 2 2 0 2]
[0 0 0 0 0 0 0 4 0 0 0 0 0 5 0 0 0 0 3]
[0 0 0 0 0 6 0 0 0 1 0 4 0 1 3 4 2 4 0]
[0 0 0 0 0 5 0 3 0 2 6 0 2 1 5 6 4 0 6]
[0 0 0 0 0 1 6 6 6 3 5 2 0 4 2 5 2 2 1]
Regardons à plus grande échelle:
sage: M = random_matrix(GF(7), 200, sparse=True, density=1/10)
sage: len(M.nonzero_positions())
3806
sage: len(gauss(M, 50).nonzero_positions())
23497
Problème
Beaucoup de matrices apparaissant dans les problèmes pratiques sont structurées:
- Matrices par bandes
- Matrices companion
- Matrices très creuses
L’algorithme de Gauß ne préserve pas ces structures!
Et pourtant:
sage: M = random_matrix(GF(7), 10000, sparse=True, density=3/10000)
sage: M.rank()
9263
Comment cela marche-t-il???
On cherche des algorithmes dont la complexité soit contrôlée non seulement par la taille \(n\) de la matrice, mais aussi par son nombre \(m\) de coefficients non nuls.
Problème
Calculer le polynôme minimal d’une matrice
Exercice
Soit \(P\) le polynôme minimal d’une matrice carrée \(M\).
Soient \(U\) et \(V\) deux vecteurs.
Montrer que la suite de nombre \(u_k = U M^k V\) satisfait une relation de récurence donnée par les coefficients de \(P\).
Solution
Todo
rédiger
Rappel: Algorithme de Berlekamp-Massey
L’algorithme de Berlekamp-Massey permet, étant donné une suite \(s_{1},\dots,s_{n}\) d’éléments d’un corps de trouver la plus petite relation de récurrence satisfaite par cette suite. Les coefficients de cette relation de récurrence sont traditionnellement encodés sous la forme d’un polynôme. Encore une conséquence d’Euclide étendu …
Voir TP pour les détails.
Algorithme de Wiedemann
- Prendre des vecteurs \(U\) et \(V\) aléatoires
- Déterminer les premiers termes de la suite \(u_k\) en calculant itérativement \(V, MV, M^2V, \ldots\)
- En déduire par Berlekamp-Massey la relation de récurence minimale qu’elle satisfait
- Cette relation divise le polynôme minimal \(P\) de \(M\)
- Réitérer «suffisamment de fois»
Remarques
- On n’a eu besoin de calculer que des produits \(MV\)
- On voit \(M\) comme un endomorphisme
- Complexité mémoire bornée par \(n\)
Exercice
Supposer que le polynôme minimal de \(M\) soit \(X^3-2X+1\).
Déterminer l’inverse de \(M\).
Voir TP.
http://en.wikipedia.org/wiki/Newton%27s_identities
Todo
Rédiger:
- Étape 1: matrice triangulaire
- Étape 2: sur un corps algébriquement clos, on triangularise
- Étape 3: généralisation à un corps quelconque, avec extension implicite du corps is besoin
Rappeler les identités de Newton, avec démo Sage
Todo
Il reste 1/2h; soit attaquer le TP, soit prendre le temps ci-dessus
Todo
rajouter un exo sur Faddeev-Leverrier
Exercice: Berlekamp-Massey
L’algorithme de Berlekamp-Massey permet, étant donné une suite \(s_{1},\dots,s_{n}\) d’éléments d’un corps de trouver la plus petite relation de récurrence satisfaite par cette suite. Les coefficients de cette relation de récurrence sont traditionnellement encodés sous la forme d’un polynôme.
Cette algorithme est implanté dans Sage par la fonction
berlekamp_masse()
. Vous pouvez au choix faire quelques
essais avec cette fonction et passer à l’exercice suivant ou …
Implanter l’algorithme de Berlekamp-Massey, soit en vous servant de [Massey.1969]: Shift-register synthesis and BCH Decoding James L. Massey, IEEE transactions on information theory, 1969, ou via l’algorithme d’Euclide étendu décrit dans le texte sur Wiedemann ou, avec plus de détails dans le texte sur le code de Goppa.
Exercice: Polynome minimal et Wiedemann sur un exemple
- Prendre \(n=10\). Construire une matrice carrée \(M\) aléatoire de
dimension \(n\) dont les valeurs propres sont dans \(\{0,1,2\}\)
avec des multiplicités quelconques; on pourra soit le faire à
la main, soit utiliser
random_matrix()
avec l’algorithmediagonalizable
; on pourra tirer les multiplicités au hasard avecIntegerVectors
. - Calculer son polynôme minimal avec la méthode
minimal_polynomial
. - Construire un vecteur aléatoire colonne \(v\) et un vecteur aléatoire ligne \(w\) de tailles \(n\). Calculer la suite \((w\times M^{k}\times v)_{k=0,\dots,2n}\).
- Vérifier sur machine qu’elle suit une relation de récurence dont les coefficients sont donnés par le polynôme minimal de \(M\) (attention: les coefficients apparaissent dans l’ordre inverse de la convention utilisée dans [Massey.1969]). Le prouver.
- Réciproquement, utiliser l’algorithme de Berlekamp-Massey pour retrouver le polynôme minimal de \(M\).
Exercice: Implantation de l’algorithme de Wiedemann
- Écrire une procédure
endomorphisme
qui prend une matrice \(M\) en argument, et renvoie l’endomorphisme correspondant, c’est-à-dire la fonction qui à un vecteur \(v\) associe le vecteur \(M\times v\). - Finir d’implanter une procédure
wiedemann(f, V)
qui prend un endomorphisme \(f\) et l’espace sur lequel il agit, et calcule son polynôme minimal en utilisant l’algorithme de Wiedemann. - Vérifier le résultat sur la matrice précédente.
- Écrire la fonction de multiplication par une matrice diagonale, la fonction de multiplication par une matrice tridiagonale. Utiliser Wiedemann pour calculer le polynôme minimal de quelques matrices de ce type.
- Évaluer la complexité expérimentale de ces calculs. Comparer avec la méthode \(minpoly\) du système.
Exercice: Calcul du rang par préconditionnement par produit de matrices diagonales.
- Fabriquer des matrices carrées raisonablement aléatoires de rang environ \(\frac{n}{2}\), dont les valeurs propres sont dans \(\{0,1,2\}\) (cf. l’exercice sur Wiedemann pour des indications).
- Tester expérimentalement le théorème 2 page 7 de Computing the rank of large sparse matrices over finite fields Jean-Guillaume Dumas et Gilles Villard, Computer Algebra in Scientific Computing, 2002.
On s’intéresse aux graphes simples (pas de boucles, pas d’arêtes multiples, pas d’orientation, …) à isomorphie près (les sommets ne portent pas d’étiquette permettant de les distinguer). Une forêt est un graphe acyclique; un arbre est une forêt connexe.
Exercice
Fabriquer la liste de toutes les forêts à \(6\) sommets et \(4\) arêtes, à isomorphie près. Que constatez-vous?
Indication: essayer:
sage: for g in graphs(5): show(g)
puis utiliser les options property
et size
et la méthode
is_forest
.
Fixons un entier \(n\). On va considérer la matrice \(T_n\) dont
- Les colonnes sont indexées par les arbres à \(n\) sommets (et donc \(n-1\) arêtes);
- Les lignes sont indexées par les forêts à \(n\) sommets et \(n-2\) arêtes;
- Le coefficient \(c_{f,t}\) compte le nombre d’arêtes de \(t\) telles que si l’on supprime cette arête on obtient un graphe isomorphe à \(f\).

Exercice
Écrire une fonction qui construit la matrice \(T_n\).
Indications:
Construire les deux listes de graphes
Par défaut, les graphes sont mutables, et on ne peut pas les mettre dans un dictionnaire:
sage: G = Graph([[1,2]]) sage: {G: 1} Traceback (most recent call last) ... TypeError: graphs are mutable, and thus not hashable
Pour corriger cela, il faut rendre le graphe immutable:
sage: G = G.copy(immutable=True) Graph on 2 vertices sage: {G: 1} {Graph on 2 vertices: 1}
Numéroter les graphes dans les deux listes en utilisant
sage.combinat.ranker.from_list()
.Utiliser les méthodes
copy
etdelete_edge
pour obtenir \(f\) par suppression d’une arête de \(t\). Puis utiliser la méthodecanonical_label
pour mettre \(f\) sous forme canonique à isomorphie près.
Exercice
Explorer les propriétés de la matrice \(T_n\).
Pour les curieux, voir arXiv:0912.2619, p. 21.
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
La bibliothèque de Tsetlin¶
Il est connu depuis quelques dizaines années que la théorie des représentations des groupes peut faciliter l’étude de systèmes dont l’évolution est aléatoire (chaînes de Markov), en les décomposant en systèmes plus simples. Plus récemment on a réalisé qu’en généralisant un peu le cadre (en remplaçant l’axiome d’inversibilité des groupes par d’autres axiomes) on pouvait expliquer le comportement particulièrement simple d’autres chaînes de Markov.
Dans ce texte, nous étudions une chaîne de Markov simple, la bibliothèque de Tsetlin, afin d’illustrer ce propos. C’est l’occasion de connecter entre eux quelques points clés du programme de l’agrégation: combinatoire, algèbre linéaire, représentations, chaînes de Markov, exploration informatique, sans demander de bagage théorique lourd.
Considérons un rayon d’une bibliothèque contenant \(n\) livres tous distincts. Lorsque l’on emprunte un livre pour le consulter, le règlement intérieur stipule que l’on doit le redéposer tout à la droite du rayon. C’est ce que l’on fait naturellement avec sa pile de chemises dans le placard: après usage et nettoyage d’une chemise, on la range en haut de la pile.
Ce mode d’organisation a l’intérêt d’être d’auto-adaptatif:
- Les livres les plus souvent utilisés s’accumulent en bout de rayon, et sont donc très rapidement retrouvés.
- Si l’usage évolue dans le temps, le système s’adapte.
De fait, ce type de stratégie est utilisé non seulement dans la vie courante, mais aussi dans des systèmes informatiques. Les questions naturelles qui se posent sont:
- L’état stationnaire: Vers quel(s) état(s) converge le système? Cela afin, entre autres, d’évaluer le temps moyen d’accès à un livre.
- La vitesse de convergence: à quelle vitesse le système s’adapte à un changement d’environnement.
Formalisons cela un peu. La bibliothèque de Tsetlin est la chaîne de Markov discrète (temps discret, espace d’état discret) décrite par:
- Un ensemble \(\Omega_n\) d’états donné par les permutations des \(n\) livres.
- Un ensemble d’opérations \(\tau_i: \Omega_n\mapsto \Omega_n\). Appliquer \(\tau_i\) à une permutation \(\sigma\) consiste à déplacer la valeur \(i\) à la fin de la permutation.
- Des paramètres \(x_i\geq 0\), avec \(\sum_i x_i=1\), donnant à chaque itération la probabilité d’appliquer l’opération \(\tau_i\).
On peut représenter l’ensemble \(\Omega_n\) muni des opérations \(\tau_i\) par un digraphe (essentiellement un automate); voici ce que l’on obtient pour \(n=3\):

Todo
Préciser que sigma(i): livre en position i
Pour des raisons techniques, il sera pratique de numéroter les livres par \(0,1,\cdots,n-1\), et de représenter chaque état de l’étagère par une permutation de \(\{0,\dots,n-1\}\) sous forme de tuple. Construire \(\Omega_n\) avec:
sage: map(tuple, Permutations(range(3)))
[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
Écrire une fonction tau(sigma, i)
qui implante l’opération
\(\tau_i\) en prenant un tuple sigma
et renvoyant un tuple
sigma
. Il peut être pratique d’utiliser les opérations
d’extractions de sous-tuples (sigma[i:j]
) et de
concaténation.
Todo
mettre les bons paramètres à plot pour que ce soit moins moche
Écrire une fonction tsetlin_digraph(n)
qui construit le
(multi digraphe) comme ci-dessus. Pour cela, on pourra
construire la liste des arêtes au moyen d’une compréhension,
et la passer à DiGraph
, en vous inspirant de
l’exemple suivant:
sage: edges = [[i,i^2 % 10,"x"] for i in range(10)]
sage: G = DiGraph(edges, loops=True)
sage: G.plot(edge_labels=True)
Vérifier pour quelques valeurs de \(n\) que ce digraphe est fortement connexe. Cela indique que la chaîne de Markov est irréductible.
On souhaite non seulement manipuler des états, mais des combinaisons linéaires d’états, les coefficients représentant la probabilité d’être dans un état donné. Pour cela, on se fixe un corps \(K\) contenant les paramètres \(x_i\), et on considère l’espace vectoriel \(K\Omega_n\) dont la base est indexée par \(\Omega_n\).
On veut construire la matrice de transition qui décrit comment un élément de \(K\Omega_n\) est transformé à chaque itération. Autrement dit, c’est la matrice de l’opérateur \(\sum x_i \tau_i\), où chaque \(\tau_i\) est étendu par linéarité à tout \(K\Omega_n\).
Implanter une fonction tsetlin_transition_matrix(n, x)
qui
étant donné \(n\) et une liste de \(n\) paramètres construit la
matrice de transition.
Mathématiquement, les lignes et colonnes de cette matrice sont
indexées par des permutations. Cependant Sage ne permet de
construire que des matrices aux lignes et colonnes indexées
par des entiers. Pour passer d’une indexation à l’autre, on
pourra avantageusement utiliser la fonction
sage.combinat.ranker.rank_from_list()
:
sage: r = sage.combinat.ranker.rank_from_list(['a',1,x])
sage: r('a')
0
sage: r(1)
1
sage: r(x)
2
Indication: selon les paramètres, on voudra construire une matrice à coefficients entiers, rationnels, symboliques. On pourra supposer que tous les paramètres vivent dans le même anneau, donné par \(x[0].parent()\), et construire la matrice en conséquence.
Tester votre fonction pour \(n=3\) en prenant comme liste de paramètres:
sage: x = var(['x%i'%i for i in range(3)])
(x0, x1, x2)
Vérifier que, en prenant \(x_i=1\) pour tout \(i\), on retrouve la matrice d’adjacence du graphe de transition.
Vérifier que la matrice est stochastique.
Vérifier que \(\sum_i x_i\) (c’est-à-dire \(1\) en principe) est valeur propre de multiplicité \(1\) et calculer le vecteur propre associé. Que représente ce vecteur propre? Pourquoi la multiplicité doit être \(1\)?
Jusqu’où peut-on aller? Quelle est la difficulté?
Pour tester plus loin, prendre par exemple \(x_i = 1/n\). Jusqu’où peut-on aller? Quelle est la difficulté?
Combien y-a-t’il de coefficients non nuls dans la matrice de transition? Et après application du pivot de Gauß?
Comment pousser plus loin?
Calculer les valeurs propres de la matrice de transition
(méthode eigenvalues
).
Que remarquez-vous?
Quelles stratégies peut-on appliquer pour pousser le calcul aussi loin que possible?
On rappelle qu’un monoïde est un ensemble muni d’une loi associative admettant un élément neutre. L’ensemble \(T_n\) des fonctions de \(\Omega_n\) dans \(\Omega_n\) est un monoïde pour la composition. On appelle monoïde de transition le sous-monoïde \(M_n\) engendré par les \(\tau_i\).
Construire le monoïde \(T_n\) des fonctions de \(\Omega_n\) dans
\(\Omega_n\) en utilisant FiniteSetMaps
. Choisir une
action à gauche pour avoir la loi de composition dans l’ordre
usuel. Construire aussi la fonction identité avec la méthode
one
.
Construire chaque \(\tau_i\) comme un élément de \(T_n\). Les stocker dans
une liste tau
.
Indication: que font les commandes suivantes:
sage: import functools
sage: f = functools.partial(tau, i=2)
sage: f( (2, 1, 3) )
En utilisant TransitiveIdeal
, construire la liste des
éléments du monoïde \(M_n\) comme le plus petit ensemble contenant
l’identité de \(T_n\) et stable par multiplication à gauche par les
\(\tau_i\).
Indication: définir une fonction suivants(m)
qui étant
donné un élément \(m\) de \(T_n\) renvoie la liste de tous les
produits \(m \tau_i\).
Construire le graphe de Cayley à droite de \(M_n\) (voir Wikipedia article Graphe_de_Cayley). C’est-à-dire le digraphe ayant comme sommets les éléments de \(M_n\) et comme arêtes les \(m \stackrel{i}{\rightarrow} m\tau_i\).
Vérifier, pour de petites valeurs de \(n\), que le graphe de Cayley de \(M_n\) est acyclique. C’est la propriété de \(R\)-trivialité.
En terme de théorie des représentations le fait que le monoïde \(M_n\) soit \(R\)-trivial implique que ses modules simples sont de dimension \(1\). Considérons alors le \(M_n\)-module \(K\Omega\). Il existe une suite de composition maximale pour \(K\Omega\); c’est-à-dire une suite de \(M_n\)-modules emboîtés:
telle que \(V_i/V_{i-1}\) est un module simple. Ceux ci étant de dimension \(1\), les \(V_i\) forment un drapeau complet dans \(K\Omega_n\) stabilisé par \(M_n\).
Plus prosaïquement, cela se traduit par l’existence d’une base adaptée de \(K\Omega_n\) dans laquelle tous les éléments de \(M_n\) sont triangulaires supérieurs (c’est un analogue de la diagonalisation simultanée d’un ensemble de matrices commutant entre elles). Cette base n’est pas forcément aisée à construire, mais nous avons uniquement besoin de son existence!
Déduire de la \(R\)-trivialité de \(M\) que les valeurs propres de la matrice de transition sont toutes de la forme \(\sum_{i\in S} x_i\), où \(S\) est un sous-ensemble de \(\{1,\dots,n\}\). Indication: vérifier que chaque opérateur \(\tau_i\) est idempotent, et en déduire ses valeurs propres.
Prendre comme paramètres \(x_i = 2^i\) et choisir un nombre premier \(p\) strictement supérieur à \(2^n\).
Construire la matrice de transition, avec ces paramètres, et dans le corps \(\ZZ/p\ZZ\). Calculer ses valeurs propres avec leur multiplicités.
Montrer que ce calcul est suffisant pour déterminer les valeurs propres de la matrice de transition pour des paramètres formels.
Calculer les multiplicités obtenues pour quelques valeurs de \(n\), les regarder en détail, et formuler une conjecture. Indication: utiliser l’Encyclopédie en Ligne des Séquences d’Entiers.
Nous allons retrouver combinatoirement les valeurs propres et leur multiplicités. Le principe est que, la matrice étant triangulaire, il suffit de connaître ses coefficients diagonaux, c’est-à-dire comment elle agit sur les quotients \(V_i/V_{i-1}\). Autrement dit, on a uniquement besoin de connaître la multiplicité des modules simples dans \(K\Omega\), et ceci peut se faire, comme pour les groupes, par théorie des caractère: on va compter des points fixes puis inverser par la table des caractères.
Il se trouve que, pour un monoïde \(R\)-trivial, la table des caractères est uni-triangulaire supérieure à coefficients 0-1: c’est la matrice d’incidence d’un ordre partiel \(P\). L’inverser revient donc à une inversion de Möbius par rapport à \(P\). Pour le monoïde \(M_n\) l’ordre partiel est simplement le treillis booléen des sous-ensembles de \(\{0,\dots,n\}\) et l’inversion de Möbius est donc juste une inclusion exclusion.
Pour \(S\) un sous-ensemble de \(\{0,\dots,n\}\), on définit l’opérateur \(\tau_S:=\prod_i \tau_i\), où le produit est pris dans l’ordre croissant (par exemple). Ainsi, \(\tau_{\{1,3\}}=\tau_1\circ\tau_3\).
Les éléments \(\tau_S\) jouent le rôle des représentants des classes de conjugaison.
Chacun des points suivant est à effectuer au choix théoriquement par ordinateur sur des exemples, ou pour \(n\) quelconque.
- Vérifier que \(\tau_S\) est idempotent.
- Compter le nombre \(c_S\) de points fixes de chaque \(\tau_S\).
- Appliquer l’inclusion-exclusion \(m_S = \sum_{S'\supseteq S} c_S\) et constater que \(m_S\) redonne la multiplicité des valeurs propres \(\sum_{i\in S} x_i\) de la conjecture.
Les prémisses de cette approche des chaînes de Markov remontent à l’étude de la bibliothèque de Tsetlin par [Bidigare_1997], [Brown_2000] … Cela a fortement contribué à l’engouement récent pour l’étude de la théorie des représentations des monoïdes. On pourra par exemple se référer à [ASST_2014] pour une liste de références, ainsi qu’une étude un peu systématique de cette approche dans le cas R-trivial et son application à l’étude de plusieurs familles de chaînes de Markov; cela inclus des modèles dans la mouvance des «tas de sable» qui modélisent en physique statistique des phénomènes comme les avalanches [ASST_2013].
[Bidigare_1997] | Thomas Patrick Bidigare. Hyperplane arrangement face algebras and their associated Markov chains. ProQuest LLC, Ann Arbor, MI, 1997. Thesis (Ph.D.)–University of Michigan. |
[Brown_2000] | Kenneth S. Brown. Semigroups, rings, and Markov chains. J. Theoret. Probab., 13(3):871–938, 2000. |
[ASST_2013] | Directed nonabelian sandpile models on trees Ayyer, Arvind and Schilling, Anne and Steinberg, Benjamin and Thiéry, Nicolas M. arXiv:1305.1697 |
[ASST_2014] | Markov chains, \(R\)-trivial monoids and representation theory Ayyer, Arvind and Schilling, Anne and Steinberg, Benjamin and Thiéry, Nicolas M. arXiv:1401.4250 |
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Codes correcteurs¶
Référence: Wikipedia: Codes correcteurs
Un expéditeur \(A\) transmet un message \(m\) à \(B\) sur un canal bruité.
Problématique
- Comment \(B\) peut-il détecter l’existence d’erreurs de transmission
- Comment \(B\) peut-il corriger des erreurs éventuelles
Note
Contrairement à la cryptographie, la problématique n’est pas de se protéger d’un tiers malicieux, mais d’un bruit aléatoire.
- NASA/CNES/…: communication avec des sondes et satellites
- CD / DVD
- Transfert de données par Internet (TCP, CRC, MD5 checksum)
- Téléphones portables
Quelles sont les contraintes spécifiques à chacune de ces applications?
Syntaxe: orthographe, grammaire
Anglais: \(500000\) mots de longueur moyenne \(10\) sur en gros \(26^{10}\), soit une proportion de \(10^{-9}\).
Exemple: pomme, abrucot, poime (pomme, poire, prime, poème)
Sémantique: sens, contexte, …
Définitions
Un code \(C\) est un sous-ensemble de mots dans \(M:=A^{n}\), où
- \(A\) est un alphabet, comme \(A:=\mathbb{Z}/q\mathbb{Z}\). Typiquement \(q=2\) (codes binaires).
- \(n\) est un entier, la dimension du code
Codage: on transforme le message envoyé \(m\) en un mot \(c\) du code.
Transmission: en passant à travers le canal, \(c\) devient \(c'\).
Détection d’erreur: on essaye de déterminer si \(c=c'\).
Correction d’erreur: on essaye de retrouver \(c\) à partir de \(c'\).
Décodage: on retrouve le message \(m\) à partir de \(c\).
Todo
Illustration sur un exemple en utilisant les codes de Sage
Définition
Distance de Hamming entre deux mots: nombre de lettres qui diffèrent.
Stratégie:
- Détection d’erreur: est-ce que \(c'\) est dans \(C\)?
- Correction d’erreur par distance minimale: on renvoie le mot de \(C\) le plus proche de \(c'\).
Exercice: Est-ce raisonnable?
On suppose que lors de la transmission chaque lettre a une probabilité \(p\) d’être corrompue, indépendemment des autres.
Calculer la probabilité qu’un mot de longueur \(n\) arrive intact? Avec moins d’une erreur? Avec moins de deux erreurs?
Application numérique:
sage: n = 7; p = 0.1
sage: (1-p)^(n-1)
0.478296900000000
sage: (1-p)^n + n*p*(1-p)^(n-1)
0.850305600000000
sage: (1-p)^n + n*p*(1-p)^(n-1) + binomial(n,2) * p^2*(1-p)^(n-2)
0.974308500000000
sage: n = 7; p = 0.01
sage: (1-p)^(n-1)
0.932065347906990
sage: (1-p)^n + n*p*(1-p)^(n-1)
0.997968958365060
sage: (1-p)^n + n*p*(1-p)^(n-1) + binomial(n,2) * p^2*(1-p)^(n-2)
0.999966037469850
Définitions
- Capacité de détection: \(D(c)\) nombre maximal d’erreurs que l’on est sûr de détecter
- Capacité de correction: \(e(C)\) nombre maximal d’erreurs que l’on est sûr de corriger
- Distance \(d(C)\) du code: distance minimale entre deux points distincts du code
Pour formuler cela formellement, il est pratique d’introduire la notion de boule naturellement associée à une métrique; étant donné \(x\in M\), et un entier \(k\geq 0\), la boule de centre \(x\) et de rayon \(k\) est:
Alors:
Cas dégénérés: lorsque \(|C|\leq 1\), on prendra par convention \(d(C)=+\infty\). Cela peut paraître plus naturel en prenant la définition alternative:
Exercice: En petite dimension:
- Trouver tous les codes de \((\mathbb{Z}/2\mathbb{Z})^{n}\) pour \(n=1\), \(n=2\), \(n=0\).
- Pour chacun d’entre eux,, donner la distance \(D(C)\), la capacité de détection \(D(C)\), la capacité de correction \(e(C)\). Dessiner les boules de centres dans \(C\) et de rayon \(e(C)\).
- Permettent-t’ils de corriger une erreur?
- Donner un code de \((\mathbb{Z}/2\mathbb{Z})^{3}\) permettant de corriger une erreur.
- Peut-on faire mieux?
Proposition
Capacité de détection: \(D(C) = d(C) - 1\).
Capacité de correction: \(e(C) = \llcorner\frac{d(C)-1}2\lrcorner\).
Problème: Kepler discret
On se fixe un alphabet \(A\) avec \(q=|A|\), une longueur \(n\) et une capacité de correction \(e\). Combien de mot peut on coder au maximum?
De manière équivalente: combien de boules non intersectantes de rayon \(e\) peut-on faire rentrer dans \(M\)?
Exemples: visualisation des boules de rayon \(e\) autour de quelques codes binaires
Chargement de quelques fonctions, et configuration des plots 3D:
sage: %run "media/codes_correcteurs.py"
sage: from sage.plot.plot3d.base import SHOW_DEFAULTS
sage: SHOW_DEFAULTS['frame'] = False
sage: SHOW_DEFAULTS['aspect_ratio'] = [1,1,1]
sage: SHOW_DEFAULTS['viewer'] = 'threejs'
Les boules dans \(\ZZ/q\ZZ^3\):
sage: @interact
....: def _(r=slider(0,3,1), q=slider(2,7,1)):
....: K = IntegerModRing(q)
....: V = K^3
....: return dessin_boules([V.zero()], r)
Le code de triple répétition sur \(\ZZ/2\ZZ\):
sage: K = GF(2)
....: V = K^3
....: C = V.subspace([[1,1,1]])
....: dessin_boules(C,1)
et sur \(\ZZ/3\ZZ\):
sage: K = GF(3)
sage: V = K^3
sage: C = V.subspace([[1,1,1]])
sage: dessin_boules(C,1)
Le code de Hamming:
sage: V = K^7
....: C = codes.HammingCode(GF(2),3)
....: dessin_boules(C, 1, projection=projection_7_3)
Todo
- The above example does not work with thebelab because the file is not available for %run; how to fix that?
- Generalize projection_7_3 to projection(n, 3), and make it the default value
Exercice: Borne de Hamming sur \(|C|\).
Soit \(A=\ZZ/q\ZZ\).
- Taille de la boule \(B(x,e):=\{y,\quad d(x,y)\leq e\}\) de \(A^n\) de centre \(x\) et de rayon \(e\)? Indication: commencer par \(q=2\) et \(x=0\cdots0\).
- Taille de \(A^n\)?
- Conclusion?
Solution
Application numérique: \(n=6,q=2,d=3\): \(|C|\leq?\).
Todo
faire un interact pour l’application numérique
Définition: code parfait
Un code \(C\) est parfait si \(|C| |B(x,e(C))| = |A^n|\), i.e.
Exemples
Dans tous les exemples vus jusqu’ici, les seuls codes parfaits sont les codes triviaux, le code de triple répétition sur un alphabet à deux lettres et le code de Hamming.
Problème
Algorithmes de codage? de décodage?
Principe: on rajoute de la structure pour rendre les algorithmes plus efficaces.
Définition
Un code linéaire est un sous-espace vectoriel de \(A^n\), où \(A\) est un corps fini.
Commençons par un petit échauffement.
Exercice: algèbre linéaire sur \(\mathbb{Z}/2\mathbb{Z}\), à la main
Soit \(H\) la matrice:
sage: A = GF(2); A
Finite Field of size 2
sage: H = matrix(A, [[0,1,1,1, 1,0,0],
....: [1,0,1,1, 0,1,0],
....: [1,1,0,1, 0,0,1]]); H
- Calculer le noyau de \(H\).
- Est-ce que les vecteurs \((1,1,0,0,1,1,0)\) et \((1,0,1,1,1,0,1)\) sont dans le sous-espace vectoriel engendré par les lignes de \(H\)?
- Conclusion?
Exemple: bit de parité
Sept bits plus un huitième bit dit de parité tel que le nombre total de bit à \(1\) est pair.
Exemple: code de Hamming \(H(7,4)\).
Quatre bits \(\left(a_{1},a_{2},a_{3},a_{4}\right)\) plus trois bits de redondance \(\left(a_{5},a_{6},a_{7}\right)\) définis par:
Comment tester si un mot appartient au code?
Avec Sage:
sage: A = GF(2); A
Finite Field of size 2
sage: n = 7
sage: V = A^7; V
Vector space of dimension 7 over Finite Field of size 2
Matrice de contrôle:
sage: H = matrix(A, [[0,1,1,1, 1,0,0],
....: [1,0,1,1, 0,1,0],
....: [1,1,0,1, 0,0,1]])
Test d’appartenance au code:
sage: mot_du_code = V([1,0,1,1,0,1,0]);
sage: H * mot_du_code
(0, 0, 0)
sage: mot_quelconque = V([1,1,0,1,0,1,1]);
sage: H * mot_quelconque
(0, 1, 0)
Refaites le à la main!
Le code lui-même est le noyau de \(H\):
sage: C = H.right_kernel()
Vector space of degree 7 and dimension 4 over Finite Field of size 2
Basis matrix:
[1 0 0 0 0 1 1]
[0 1 0 0 1 0 1]
[0 0 1 0 1 1 0]
[0 0 0 1 1 1 1]
sage: mot_du_code in C
True
sage: mot_quelconque in C
False
Refaites le à la main!
Est-ce que l’on pourrait trouver \(C\) encore plus rapidement?
Oui:
sage: MatrixSpace(A,4,4)(1).augment(H[:,0:4].transpose())
[1 0 0 0 0 1 1]
[0 1 0 0 1 0 1]
[0 0 1 0 1 1 0]
[0 0 0 1 1 1 1]
Combien y-a-t’il de mots dans le code de Hamming \(H(7,4)\)?
Calculer la distance de ce code (indice: se ramener en zéro!)
Quelle est sa capacité de détection? de correction? Est-il parfait?
Solution:
sage: sage: C.cardinality()
16
sage: def poids(c): return len([i for i in c if i])
sage: poids(V([0,1,0,0,0,0,0]))
1
sage: poids(V([1,0,1,1,0,1,0]))
4
sage: min(poids(m) for m in C if m)
3
Comment coder un mot?
Matrice génératrice:
sage: G = C.matrix(); G
[1 0 0 0 0 1 1]
[0 1 0 0 1 0 1]
[0 0 1 0 1 1 0]
[0 0 0 1 1 1 1]
sage: M = A^4
sage: m = M([1,0,1,0])
sage: c = m * G; c
(1, 0, 1, 0, 1, 0, 1)
Exercice
- Partir du mot zéro, le coder, et faire alternativement une erreur sur chacun des bits. Noter le résultat après multiplication par la matrice de contrôle.
- Prendre un mot à 4 bits de votre choix, le coder, faire une erreur sur un des 7 bits, corriger et décoder. Vérifier le résultat.
- Que se passe-t’il s’il y a deux erreurs?
Principe: encore plus de structure pour être encore plus efficace.
Définition
Un code \(C\) est cyclique s’il est stable par rotation des mots:
Les praticiens ont noté que les codes cycliques avaient de bonnes propriétés.
Donnons une structure d’anneau quotient à \(A^n\) en l’identifiant avec \(A[X]/(X^n-1)\).
Sous cette identification, les mots ci-dessus correspondent à
\[1 + X^2 + X^5, X+X^3+X^6, 1+X^2+X^4\]
Remarque
Dans \(A[X]/(X^n-1)\), décalage = multiplication par \(X\).
Par exemple, pour \(A[X]/(X^7-1)\):
Codes cycliques \(\longleftrightarrow\) idéaux dans \(A[X]/(X^n-1)\).
Soit \(g\) un diviseur de \(X^n-1\), et \(h\) tel que \(gh=X^n-1\).
- Code: idéal engendré par \(g\)
- Codage: \(m\mapsto mg\)
- Détection d’erreur: \(c*h=0\)
- Décodage: division par \(g\) modulo \(X^n-1\) (par ex. par Euclide étendu)
Codes BCH
On peut construire des codes cycliques de capacité de correction déterminée à l’avance. Pour en savoir plus, voir Wikipedia, Codes BCH.
Exercice (secret partagé)
Un vieux pirate est sur son lit de mort. Dans sa jeunesse il a enfoui un Fabuleux Trésor dans la lagune de l’Ile de la Tortue, quelque part à l’est du Grand Cocotier. Il a réuni ses dix lieutenants préférés pour leur transmettre l’information secrète indispensable: la distance entre le Grand Cocotier et le Trésor. Connaissant bien ses lieutenants, et dans un étonnant dernier sursaut de justice, il ne voudrait pas qu’une conjuration de quelques uns d’entre eux assassine les autres pour empocher seuls le trésor. En tenant cependant compte de la mortalité habituelle du milieu, il souhaite donner une information secrète à chacun de ses lieutenants pour que huit quelconques d’entre eux puissent retrouver ensemble le trésor, mais pas moins. Comment peut-il s’y prendre?
Application au codage: CIRC
Todo
Faire la figure
Découpage de l’information en blocs, interprétés comme des polynômes \(P_1,\dots,P_k\) dans \(GF(q)[X]\).
Points d’évaluation \(x_1,\ldots,x_l\).
Premier étage: évaluation et entrelacement.
Deuxième étage: codage de chacun des \(l\) blocs avec un code permettant de détecter les erreurs.
Exercice préliminaire
- Sage contient de nombreuses fonctionalités autour du codage. Un
point d’entrée est
codes?
ainsi que le tutoriel thématique Coding Theory in Sage. Y jeter un coup d’oeil.
Essayer l’exemple suivant et consulter la documentation de
@interact
:sagenb.notebook.interact.interact()
; voir aussi la documentation de jupyter:sage: @interact ....: def f(x=slider(1,10,1)): ....: return x^2
Choisir à la carte parmi les exercices suivants.
Exercice: illustrer un cours sur le codage
Mettre au point une illustration sur ordinateur d’un point d’un cours sur le codage. On pourra par exemple:
Illustrer visuellement les liens entre distance, capacité de correction et de détection, ainsi que les notions de distance de Hamming, boules, …
Déterminer en quelles (petites) dimensions on peut espérer l’existence de codes parfaits non triviaux?
Indications:
- implanter une fonction pour calculer la borne de Hamming
- utiliser
@interact
pour explorer rapidement les valeurs qu’elle prend en fonction de \(q\), \(n\), \(e\).
Déterminer empiriquement quels paramètres de code (dimension, distance, …) seraient souhaitables pour différentes applications (par ex. transmission satellite depuis Voyager). On pourra par exemple calculer, en fonction de la dimension, de la capacité de correction, et du taux d’erreur, la probabilité qu’un message erroné ne soit pas détecté ou pas corrigé. Puis jouer avec les paramètres jusqu’à trouver des paramètres potentiels plausibles.
Indication: comme ci-dessus
Simuler, avec les outils existant dans Sage une chaîne complète: codage, transmission, détection. Estimer empiriquement la probabilité qu’un message soit transmis incorrectement et non détecté. Comparer avec la théorie.
Implanter toute la chaîne: codage, transmission, détection, correction, décodage.
Implanter des fonctions de calcul de distance et test de perfection.
Pour ces derniers points, on pourra considérer des codes:
- décrits par une liste exhaustive de mots
- linéaires
- cycliques (voir ci-dessous)
- par interpolation
- code à deux étages avec entrelacement, comme le code CIRC utilisé dans les CDs.
Exercice: codes cycliques
On oubliera ici que les codes cycliques sont naturellement représentés par des idéaux dans \(A[X] / X^n-1\), et on ne fera que de l’algèbre linéaire.
Soit \(E\) un espace vectoriel sur un corps fini; typiquement:
sage: F2 = GF(2)
sage: E = F2^7; E
Vector space of dimension 7 over Finite Field of size 2
On considère l’opération cycle(v)
qui prend un vecteur et
décale ses coordonnées d’un cran vers la droite (modulo \(n\)). On
rappelle qu’un code cyclique est un sous-espace vectoriel de \(E\)
qui est stable par l’opération cycle
.
- Implanter l’opération
cycle
. - Implanter une fonction
code_cyclique(v)
qui renvoie une base du plus petit code cyclique \(C\) contenant \(v\). - Implanter une fonction qui renvoie la matrice de contrôle du code \(C\), c’est à dire une matrice \(M\) telle que \(Mv=0\) si et seulement si \(v\) est dans \(C\).
- Implanter le décodage par syndrome pour le code cyclique engendré par \(v\).
Exercice: Le tour de magie
Implanter le tour de prestidigitation du texte Codes Correcteurs d’Erreurs, Agreg 2005.
Un petit exemple d’utilisation des composants visuels interactifs de Sage:
sage: @interact
sage: def magie(step=slider([1..5])):
....: return matrix(4,4,[i for i in srange(0,32) if i.digits(base=2,padto=6)[5-step]])
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Groupe Symétrique et groupes de permutations¶
Définition
Soit \(E\) un ensemble.
On appelle groupe symétrique de \(E\) l’ensemble des applications bijectives de \(E\) sur \(E\) muni de la composition d’applications.
On le note \(S_E\).
Exemple:
sage: G = SymmetricGroup(['a', 'b', 'c'])
sage: G.list()
[(), ('b','c'), ('a','b'), ('a','b','c'), ('a','c','b'), ('a','c')]
Pour voir ses éléments comme des fonctions:
sage: F = FiniteSetMaps(['a','b','c'])
sage: F.domain()
{'a', 'b', 'c'}
sage: F.codomain()
{'a', 'b', 'c'}
sage: for sigma in G: print F(sigma)
map: a -> a, b -> b, c -> c
map: a -> a, b -> c, c -> b
map: a -> b, b -> a, c -> c
map: a -> b, b -> c, c -> a
map: a -> c, b -> a, c -> b
map: a -> c, b -> b, c -> a
Un cas particulier courant est le cas où \(E\) est l’ensemble fini \(\left\{ 1,\dots,n\right\}\), \(n\) étant un entier naturel strictement positif. On note alors \(S_n\) le groupe symétrique de cet ensemble. Les éléments de \(S_n\) sont appelés permutations et \(S_n\) est appelé groupe des permutations d’ordre \(n\), ou groupe symétrique d’ordre \(n\).
Exemple:
sage: S3 = SymmetricGroup(3)
Maintenant, si \(E\) est un ensemble à \(n\) éléments, alors on sait que \(S_E\) est isomorphe à \(S_n\):
sage: G.is_isomorphic(S3)
True
En conséquence, il suffit de connaître les propriétés du groupe \(S_n\) pour en déduire celles du groupe \(S_E\).
Proposition
Le groupe \(S_n\) est d’ordre \(n!\) .
Exemple:
sage: SymmetricGroup(3).cardinality()
6
sage: SymmetricGroup(100).cardinality()
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
- Une transposition \((i,j)\) est une permutation qui échange \(i\) et \(j\) et laisse les autres éléments inchangés.
- Une transposition élémentaire est une transposition de la forme \((i,i+1)\).
- Un cycle \((c_{1},c_{2},\dots,c_{k})\) est une permutation qui envoie \(c_{1}\) sur \(c_{2}\), \(c_{2}\) sur \(c_{3}\), et \(c_{k}\) sur \(c_{1}\).
sage: G = SymmetricGroup(8)
sage: sigma = G.random_element()
Mot:
sage: [sigma(i) for i in range(1,9)] [7, 8, 3, 2, 5, 4, 1, 6] sage: sigma.domain() # raccourci mal nommé! [7, 8, 3, 2, 5, 4, 1, 6]
Bimot:
sage: [(i,sigma(i)) for i in range(1,9)] [(1, 7), (2, 8), (3, 3), (4, 2), (5, 5), (6, 4), (7, 1), (8, 6)]
Graphe:
sage: DiGraph([(i,sigma(i)) for i in range(1,9)], loops=True).plot() sage: DiGraph([(i,sigma(i)) for i in range(1,9)], loops=True).plot(talk=True)
Matrice:
sage: sigma.matrix() [0 0 0 0 0 0 1 0] [0 0 0 0 0 0 0 1] [0 0 1 0 0 0 0 0] [0 1 0 0 0 0 0 0] [0 0 0 0 1 0 0 0] [0 0 0 1 0 0 0 0] [1 0 0 0 0 0 0 0] [0 0 0 0 0 1 0 0]
Produit de cycles (voir ci-dessous):
sage: sigma (1,7)(2,8,6,4)
Le produit dans le groupe symétrique est donné par la composition de fonctions: \(\sigma\tau = \sigma\circ\tau\). Parfois on préfère l’ordre inverse et on définit: \(\sigma \tau = \tau \circ \sigma\).
Exercice
Calculer le produit des permutations suivantes:
sage: G = SymmetricGroup(3)
sage: sigma = G([2,3,1])
sage: tau = G([2,1,3])
Solution
sage: (sigma * tau).domain()
[1, 3, 2]
sage: (tau * sigma).domain()
[3, 2, 1]
Note
Dans Sage, le produit sigma * tau
désigne la composée \(\tau
\circ \sigma\). Sage suit en cela la convention utilisée par le
logiciel GAP, inclus dans Sage et à qui Sage délègue de nombreux
calculs sur les groupes.
Propositions
Dans le produit \(\sigma\tau\), on peut considérer que \(\tau\) permute les positions de \(\sigma\), tandis que dans le produit \(\tau\sigma\), \(\tau\) permute les valeurs de \(\sigma\):
sage: G = SymmetricGroup(8) sage: tau = G([(3,5)]) sage: sigma = G([1,5,4,6,8,2,7,3]) sage: sigma [1, 5, 4, 6, 8, 2, 7, 3] sage: (sigma * tau).domain() [1, 3, 4, 6, 8, 2, 7, 5] sage: (tau * sigma).domain() [1, 5, 8, 6, 4, 2, 7, 3]
Deux cycles disjoints commutent.
Toute permutation se décompose de manière unique comme un produit de cycles (à l’ordre près).
Exercice
- Comment calculer l’inverse d’une permutation? Complexité?
- Calcul de la décomposition en cycles? Complexité?
Le type cyclique d’une permutation est la partition de \(n\) donnée par les longueurs de ses cycles.
Exemple
sage: sigma = G.random_element(); sigma
sage: sigma.cycle_type()
Exercices
Que se passe-t-il lorsque l’on conjugue une permutation \(\tau\) donnée sous forme de décomposition en cycles par une permutation \(\sigma\) (avec pour résultat \(\sigma\tau\sigma^{-1}\))? Exemple: prendre \(\sigma = (1,2,3,4,5,6,7,8)\) et \(\tau=(2,5,3)\).
sage: sigma = G([(1,2,3,4,5,6,7,8)]) sage: tau = G([(2,5,3)]) sage: ~sigma * tau * sigma
Quelles sont les classes de conjugaisons du groupe symétrique?
Solution
- Chaque cycle \((i_1,\dots,i_k)\) de \(\tau\) contribue un cycle \((\sigma(i_1),\dots,\sigma(i_k))\) dans \(\sigma\tau\sigma^{-1}\).
- Deux permutations sont dans la même classe de conjugaison si et seulement si elles ont même type cyclique. Les classes de conjugaisons sont donc indexées par les partitions.
Conséquence: les représentations du groupe symétrique sont indexées par les partitions.
Proposition
- \(S_n\) est engendré par les cycles.
- \(S_n\) est engendré par les transpositions.
- \(S_n\) est engendré par les transpositions élémentaires.
- \(S_n\) est engendré par la transposition \((1,2)\) et le cycle \((1,\dots,n)\).
Générateurs: \(\tau_{i}=(i,i+1)\).
Relations:
- \(\tau_{i}^{2}=1\),
- \(\tau_{i}\tau_{i+1}\tau_{i}=\tau_{i+1}\tau_{i}\tau_{i+1}\),
- \(\tau_{i}\tau_{j}=\tau_{j}\tau_{i}\) si \(\left|i-j\right|>1\).

Le permutoèdre pour \(S_3\)

Le permutoèdre pour \(S_4\)
sage: q = QQ['q'].gen()
sage: 1 * (1+q) * (1+q+q^2)
sage: expand( 1 * (1+q) * (1+q+q^2) )
q^3 + 2*q^2 + 2*q + 1
sage: expand( 1 * (1+q) * (1+q+q^2) * (1+q+q^2+q^3) )
q^6 + 3*q^5 + 5*q^4 + 6*q^3 + 5*q^2 + 3*q + 1
sage: sage.combinat.q_analogues.q_factorial(4)
q^6 + 3*q^5 + 5*q^4 + 6*q^3 + 5*q^2 + 3*q + 1
Les \(q\)-factorielles apparaissent aussi naturellement dans le comptage de sous-espaces vectoriels ou d’applications inversibles sur un corps fini \(\mathbb F_q\).
Un groupe de permutations est un groupe donné comme sous-groupe d’un groupe symétrique.
Groupe trivial \(id_n\).
Groupe cyclique \(C_n\):
sage: C5 = CyclicPermutationGroup(5); C5 Cyclic group of order 4 as a permutation group sage: C5.group_generators() Family ((1,2,3,4,5),)
Groupe diédral \(D_n\):
sage: D5 = DihedralGroup(5); D5 Dihedral group of order 10 as a permutation group sage: D5.group_generators() Family ((1,2,3,4,5), (1,5)(2,4))
Groupe alterné \(A_n\):
sage: A5 = AlternatingGroup(5); A5 Alternating group of order 5!/2 as a permutation group sage: A5.group_generators() Family ((3,4,5), (1,2,3,4,5)) sage: A5.is_simple()
Tout groupe fini! (théorème de Cayley)
Exercice
Construire le groupe des symétries du cube:
. 7-----8
. /| /|
. 5-----6 |
. | | | |
. | 3---|-4
. |/ |/
. 1-----2
Solution
sage: G = PermutationGroup([...])
- Groupes de symétries d’objets discrets.
- Comptage d’objets à isomorphie près (Énumération de Pólya; voir TP).
- Étude des groupes finis.
- Étude du groupe des permutations des racines d’un polynôme. C’est l’origine du concept de groupe par Évariste Galois.
Problème: Soit \(G\subset S_n\) un groupe de permutation; \(G\) est typiquement très gros.
- Comment le représenter? Le manipuler?
- Calculer son nombre d’éléments?
- Tester si un élément est dedans?
- Exprimer un élément en fonction des générateurs?
- Déterminer ses sous-groupes?
- Est-il abélien, simple, résoluble, … ?
Exercice
Soit \(G\) un groupe de permutations de \(\{1,\dots,n\}\). Par exemple, le groupe des symétries du cube (\(n=8\)).
Soit \(H\) le sous groupe des éléments de \(G\) qui fixent \(n\).
- Supposons \(|H|\) connu. Comment en déduire \(|G|\)?
- Comment obtenir des représentants des classes de \(G/H\)?
- Supposons que l’on sache tester si une permutation est dans \(H\). Comment tester si une permutation est dans \(G\)?
Solution
Rappel: \(\quad\sigma H=\tau H \quad\Longleftrightarrow\quad \sigma^{-1}\tau\in H \quad\Longleftrightarrow\quad \sigma(n)=\tau(n)\)
Du coup, la fonction:
induit un isomorphisme entre les classes à droite \(\sigma H\) et les éléments de l’orbite \(G.n\) de \(n\) sous l’action de \(G\).
- \(|G| = |H|\ |G.x|\)
- Il suffit de choisir pour chaque \(y\) dans \(G.n\) une permutation \(\sigma_{n,y}\) telle que \(\sigma_{n,y}(n)=y\).
- Soit \(\tau\) une permutation. Si \(\sigma(n)\not\in G.n\), alors \(\sigma\not\in G\). Sinon, \(\sigma_{n,\tau(n)}^{-1} \sigma\) fixe \(n\). Donc \(\sigma \in G \Longleftrightarrow \sigma_{n,\tau(n)}^{-1}\sigma\in H\).
On a une bonne idée? Appliquons la récursivement.
Définition
On considère la tour de groupes
où \(G_i:=G\cap S_i\) est le sous-groupe des éléments de \(G\) qui fixent \(\left\{i+1,\dots,n\right\}\).
Pour décrire \(G\), il suffit de décrire chacune des inclusions.
Un système générateur fort est composé des représentants \(\sigma_{i,y}\) des classes de \(G_{i}/G_{i-1}\) pour chaque \(i\).
On abrège système générateur fort en SGS (pour strong generating system).
Remarque
Un système générateur fort est un système générateur \(S\) adapté à la tour \(S_0 \subset S_1 \subset \cdots \subset S_n\):
C’est l’analogue des bases sous forme échelon d’un espace vectoriel \(E\) qui sont adaptées à un drapeau.
Exemple
\(S_n\) engendré par toutes les transpositions.
Proposition
La connaissance d’un système générateur fort permet de résoudre tous les problèmes ci-dessus:
- Calcul du nombre d’éléments
- Tester si un élément est dedans
- …
Exercices
- Construire à la main un système générateur fort pour:
- le groupe trivial \(Id_n\)
- le groupe cyclique \(C_{4}\)
- le groupe alterné \(A_{4}\)
- le groupe symétrique \(S_n\)
- le groupe dihédral \(D_{8}\)
- le groupe des symétries du cube agissant sur les sommets.
- Donner une borne sur la taille d’un système générateur fort. Comparer avec la taille du groupe.
Solution partielle
sage: PermutationGroup([], domain=[1,2,3,4]).strong_generating_system(base_of_group=[4,3,2,1])
[[()], [()], [()], [()]]
sage: CyclicPermutationGroup(4).strong_generating_system(base_of_group=[4,3,2,1])
[[(1,2,3,4), (1,4,3,2), (), (1,3)(2,4)], [()], [()], [()]]
sage: AlternatingGroup(4).strong_generating_system(base_of_group=[4,3,2,1])
[[(), (1,4,2), (1,4,3), (1,2,4)], [(), (1,2,3), (1,3,2)], [()], [()]]
sage: DihedralGroup(4).strong_generating_system(base_of_group=[4,3,2,1])
[[(1,2,3,4), (1,4,3,2), (), (1,3)(2,4)], [(), (1,3)], [()], [()]]
sage: SymmetricGroup(4).strong_generating_system(base_of_group=[4,3,2,1])
[[(), (1,4), (2,4), (3,4)], [(), (1,2,3), (1,3,2)], [(), (1,2)], [()]]
Notons \(h_i=|G_i|/|G_{i-1}\). Alors la taille d’un système générateur fort est \(h_1+\cdots+h_n \leq n(n+1)/2\) alors que la taille de \(G\) est \(h_1\cdots h_n\leq n!\).
Définition
Un sous-ensemble \(B\) est une base de \(G\) si tout élément \(g\) dans le groupe est caractérisé par \(g(b)\) pour \(b\) dans \(B\).
Ci-dessus, on a utilisé \(B:=\{n,\dots,1\}\), mais la définition de système générateur fort se généralise relativement à n’importe quelle base \(B\).
Exercices
- Vérifier que \(\left\{5,4,3\right\}\) est une base pour \(A_{5}\).
Comment calculer un système générateur fort?
- Calculer l’orbite \(G.n\) de \(n\) (comment on fait?)
- Les permutations \(\sigma_{n,y}\) qui envoient \(n\) sur \(y\), \(y\) dans
- \(G.n\) donnent des représentants des classes de \(G/G_n\)
- Calculer les générateurs de \(G_n\) avec le Lemme de Schreier (voir ci-dessous).
- Réitérer récursivement
Todo
Donner la complexité
Soit \(G\) un groupe et \(H\) un sous-groupe. Soient \(A\) un ensemble de générateurs de \(G\) et \(U\) des représentants des \(H\)-classes à droite:
Alors:
Démonstration
Soit \(g\in G\). On l’exprime en fonction des générateurs: \(g = a_1\cdots a_k\) avec les \(a_i\) dans \(A\).
Pour tout \(i\), prenons l’unique \(u_i\) tel que \(a_i\cdots a_k \in u_i H\). Alors:
On note que chacun des facteurs est dans l’ensemble sus-mentionné. Ce dernier engendre donc \(H\).
Exercice:
Utiliser l’algorithme de Schreier-Sims pour retrouver un SGS pour le groupe des symétries du cube, sachant qu’il est engendré par \(\left(0,1,3,7,6,4\right)\left(2,5\right)\) et \(\left(0,1,3,2\right)\left(4,5,7,6\right)\).
Note
On peut calculer incrémentalement et efficacement un système générateur fort à partir d’un système générateur quelconque.
Algorithmes dérivés de petite complexité (typiquement \(O(n\log(|G|))\)). On peut manipuler des groupes de permutations d’ordre plusieurs centaines de milliers.
Exemple:
sage: S3 = SymmetricGroup(3)
sage: S3.subgroups()
[Permutation Group with generators [()], Permutation Group with generators [(2,3)], Permutation Group with generators [(1,2)], Permutation Group with generators [(1,3)], Permutation Group with generators [(1,2,3)], Permutation Group with generators [(1,2), (1,3,2)]]
Ce que l’on vient de voir est une idée très générale en calcul algébrique:
On a une structure algébrique:
- une algèbre de polynômes (univariée/multivariée),
- un espace vectoriel,
- un groupe symétrique…
On veut pouvoir calculer avec ses sous-structures \(I\) (idéaux, sous-espaces vectoriels, groupes de permutations):
- Test d’appartenance d’un élément à \(I\),
- Test d’égalité de \(I\) et de \(J\),
- Calcul de «taille» de \(I\),
- …
Pour cela, on se donne:
- Un ordre
- Un drapeau de sous-structures vis à vis de cet ordre
- Un procédé de division: Euclide, …
- Une notion de système générateur fort: PGCD, base de Gröbner, forme échelon, système fort de générateurs,
- Un algorithme de calcul d’un tel système: algorithme d’Euclide, de Buchberger, de Gauss, de Schreier-Sims, …
Le fichier GroupeSymetrique.py vous donne un point de départ pour les différentes fonctions que vous aurez à implanter dans ce TP. Le fichier GroupeSymetrique-correction.py contient une correction partielle.
La formule d’énumération de Pólya permet de dénombrer des objets discrets considérés modulo certaines symétries. Un des cas les plus simples concerne le dénombrement des colliers à \(n\) perles rouges ou bleues, considérés à une rotation près. Par exemple, voilà trois colliers à \(n=8\) perles. Les deux premiers sont identiques, mais pas le troisième (on pourrait autoriser le retournement, mais on ne le fera pas dans un premier temps pour simplifier).
Note
Pour refabriquer un de ces dessins, on peut utiliser:
sage: G = graphs.CycleGraph(8)
sage: G.plot(vertex_colors={"red": [0,2,3,4,5], "blue": [1,6,7]})
Nous allons énoncer cette formule dans le cas général, en l’illustrant au fur et à mesure sur cet exemple.
Exercice préliminaire
Vérifier, en les dessinant tous à la main, qu’il y a \(8\) colliers à \(n=5\) perles rouges ou bleues. Préciser combien d’entre eux ont \(0,1,2,\dots\) perles rouges.
Comparer vos colliers avec les listes produites par
IntegerVectorsModPermutationGroup
:
sage: C5 = CyclicPermutationGroup(5)
sage: I = IntegerVectorsModPermutationGroup(C5, max_part=1)
sage: I.list()
[[0, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[1, 1, 0, 0, 0],
[1, 0, 1, 0, 0],
[1, 1, 1, 0, 0],
[1, 1, 0, 1, 0],
[1, 1, 1, 1, 0],
[1, 1, 1, 1, 1]]
sage: I.cardinality()
8
sage: I = IntegerVectorsModPermutationGroup(CyclicPermutationGroup(5), max_part=1, sum=3)
sage: I.list()
[[1, 1, 1, 0, 0], [1, 1, 0, 1, 0]]
sage: I.cardinality()
2
Soit \(E\) un ensemble fini (ici \(E:=\left\{ 1,\dots,5\right\}\)), et \(F\) un autre ensemble (ici \(F:=\left\{ Rouge,Bleu\right\}\)), typiquement fini ou dénombrable. Les objets discrets qui nous intéressent sont les fonctions de \(E\) dans \(F\) (ici les colliers où on a fixé la première perle). Pour modéliser des symétries sur \(E\) (ici on veut considérer que deux colliers qui sont identiques à rotation près sont identiques), on introduit un sous-groupe \(G\) du groupe symétrique \(S_E\) (ici le groupe cyclique \(G:=C_{5}=\left\langle (1,\dots,5)\right\rangle\)). Ce groupe agit sur l’ensemble des fonctions \(F^{E}\) par \(\sigma\cdot f:=f\circ\sigma^{-1}\), où \(\sigma\in G\) et \(f\in F^{E}\). Deux fonctions \(f\) et \(g\) sont dites isomorphes s’il existe une permutation \(\sigma\) dans \(G\) telle que \(f=\sigma.g\) (ici, deux colliers sont isomorphes s’ils sont identiques à rotation près).
Notre objectif est de compter le nombres de classes d’isomorphie. Cela peut être fait via le Lemme de Burnside. Nous allons directement énoncer une version raffinée de cette formule, due à Pólya, afin de compter les colliers selon leur nombre de perles rouges. Pour cela, nous allons associer à chaque élément \(c\) de \(F\) un poids \(w(c)\) multiplicatif, et associer à chaque fonction \(f\) dans \(F^{E}\) le poids \(w\left(f\right)=\prod_{e\in E}w(f(e))\). Ce poids est constant sur une classe d’isomorphie \(\overline{f}\), ce qui permet de définir \(w\left(\overline{f}\right)\). Considérons maintenant la somme \(\sum_{\overline{f}}w\left(\overline{f}\right)\) des poids de toutes les classes d’isomorphie. Si \(w\left(c\right)=1\) pour tout \(c\) dans \(F\), cette somme donne le nombre de classes d’isomorphies, c’est-à-dire \(8\) dans notre exemple. Si \(w(Rouge)=1\) et \(w(Bleu)=q\), on obtient:
qui indique en particulier qu’il y a deux colliers avec respectivement deux ou trois perles rouges, et un collier avec respectivement zéro, une, quatre, ou cinq perles rouges. On notera que le rôle joué par les éléments de \(F\) (ici les couleurs rouges et bleues) sont parfaitement symétriques; cela rend relativement naturelle l’introduction des polynômes symétriques suivantes:
qui énumèrent les objets de \(F\) répétés \(k\) fois.
Nous pouvons maintenant énoncer la fameuse formule de Pólya. La seule information dont l’on a besoin sur le groupe est en fait le type cyclique \(l(c)\) de chacun de ses éléments:
Précision: dans le produit \(\prod_{k\in l(\sigma)} p_k\), on tient compte des répétitions; si \(\sigma\) a trois cycles de longueur \(k\), alors \(p_k\) est élevé à la puissance trois.
Indication pour l’ensemble des exercices: Sage (comme MuPAD ou Maple)
contiennent un certain nombre de fonctions prédéfinies pour manipuler
les groupes de permutations (voir PermutationGroup()
), dont la
formule de Pólya; à vous de choisir ce que vous réimplantez ou pas
selon ce que vous avez le plus besoin de comprendre.
Écrire une fonction
p(k,poids)
qui calcule \(p_{k}\) à partir de la liste des poids des éléments de \(F\).La formule de Pólya requiers de calculer le type cyclique d’une permutation.
Option 1: (Sage >= 7.5) utilisez directement la méthode
sigma.cycle_type()
et passer directement à la suite.Option 2: Implanter une fonction
type_cyclique(sigma)
qui calcule le type cyclique d’une permutationsigma
à partir de la méthodecycle_tuples()
des permutations.Option 3: Implanter l’algorithme de recherche des cycles, mais en stockant uniquement leur taille. Indications:
sage: G = DihedralGroup(10) sage: g = G.an_element(); g (1,2,3,4,5,6,7,8,9,10) sage: g.parent().domain() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
et utiliser un ensemble (
set
) pour noter les éléments du domaine déjà croisés.
Lister les permutations de \(C_{5}\).
Écrire la formule ci-dessus pour \(poids=[1,1]\).
Écrire une fonction
Polya(G, poids)
implantant la formule ci-dessus pour un groupe \(G\) et des poids quelconques.Compter le nombre de colliers bicolores à dix perles selon leur nombre de perles rouges.
Compter le nombre de colliers à dix perles de trois couleurs.
Variante sur l’exercice précédent: on veut maintenant aussi considérer comme identiques deux colliers qui ne diffèrent que d’un retournement. Compter le nombre de tels colliers à trois perles bleues et deux perles rouges.
Indication: considérer le groupe diédral \(D_{5}\) des symétries du pentagone.
Compter le nombre de cubes que l’on peut obtenir en peignant leurs faces en au plus trois couleurs.
Indications:
- Numéroter les faces, considérer le groupe des isométries positives du cube, comme groupe de permutations de ses faces.
- Déterminer les générateurs de ce groupe (par exemple sous forme de produit de cycles).
- Construire le groupe dans Sage en utilisant
PermutationGroup()
. - Poursuivre comme ci-dessus.
Construire à la main les \(11\) graphes simples non orientés sur \(4\) sommets non étiquetés. Puis recalculer leur nombre grâce à la formule de Pólya. Compter le nombre de graphes simples à \(5,6,7,8,9,10,\ldots\) sommets.
Indications:
- Un graphe simple non orienté sur \(n\) sommets peut être considéré comme une fonction allant de l’ensemble des paires \(\{i,j\}\) de \(\{1,\dots,n\}\) dans \(\{0,1\}\) (\(1\) s’il y a une arête entre \(i\) et \(j\), et \(0\) sinon).
- On numérote les paires \(\{i,j\}\) de \(1\) à \(\binom{n}{2}\). Le groupe \(G\) est le groupe des permutation des paires induites par les \(n!\) permutations des sommets dans \(S_n\). On peut donc rechercher quelles permutations des paires sont induites par l’échange des sommets \(1\) et \(2\) et par la permutation cyclique \((1,2,3,\dots,n)\) des sommets; le groupe \(G\) est alors engendré par ces deux permutations, et l’on peut poursuivre comme dans l’exercice précédent.
- Au delà de \(n=7\) le calcul devient long à cause de la somme sur le groupe. Pour aller plus loin, on peut regrouper dans la formule de Pólya les permutations ayant le même type cyclique. Pour cela, il faut pouvoir compter le nombre de permutations dans \(S_n\) ayant un type cyclique donné, et pouvoir calculer le type cyclique d’une permutation des arêtes dans \(G\), connaissant le type cyclique de la permutation des sommets correspondant dans \(S_n\).
Un multigraphe est un graphe dans lequel il peut y avoir un nombre quelconque d’arêtes entre deux sommets. Calculer la série génératrice par nombre d’arêtes des graphes sur 4,5,6 sommets. Indication: ici, \(F\) est composé des entiers \(\left\{0,1,2,\dots\right\}\) auxquels on peut attribuer les poids \(\left\{ 1,q,q^{2},\dots\right\}\); on peut alors mettre \(p_{k}:=1^{k}+q^{k}+q^{2k}+\cdots\) sous la forme \(p_{k}=\frac{1}{1-q^{k}}\).
- Consulter la documentation et le code de la méthode
cycle_index()
des groupes de permutations. C’est l’un de vos prédécesseurs qui l’a implantée! - Utilisez-la pour recalculer les exemples précédents.
- Est-elle plus ou moins performante que votre implantation?
- Comment fonctionne-t-elle?
On supposera pour simplifier que l’on travaille avec un groupe de permutations \(G\) de \(\{1,\dots,n\}\) et que la base est \(n,n-1,\dots,1\).
On représentera un système générateur fort de \(G\) sous la forme d’une liste \(l\) telle que \(l[i-1]\) contient des représentants des classes de \(G_i/G_{i-1}\). Ces représentants seront représenté sous la forme d’un dictionnaire associant à chaque élément \(y\) de l’orbite de \(i\) sous \(G_i\) une permutation \(\sigma_{i,y}\) de \(G_i\) telle que \(\sigma_{i,y}(i)=y\).
Pour le groupe symétrique \(S_3\), cela donnerait:
sage: S = SymmetricGroup(3)
sage: sgf = [ {1: S.one()},
....: {1: S([(1,2)]), 2: S.one()},
....: {1: S([(1,3)]), 2: S([(2,3)]), 3: S.one()} ]
Exercice
Construisez dans Sage les systèmes générateurs forts des groupes \(C_4\), \(D_4\), \(A_4\), et du groupe des symétries du cube.
Comparez avec le système générateur fort calculé par Sage (en fait GAP).
Exercice: Utilisation des systèmes générateurs forts
Implanter des procédures qui, étant donné un système générateur fort d’un groupe \(G\), permettent de:
- Calculer la taille du groupe,
- Calculer la liste des éléments du groupe,
- Indication: récursion
- Variante (avancé): implanter un itérateur
- Tester si une permutation donnée appartient au groupe.
Exercice: Calcul des systèmes générateurs forts
Implanter l’algorithme de Schreier-Sims pour calculer un système générateur fort d’un groupe de permutations donné par des générateurs.
Indication: Implanter d’abord une méthode
transversal(generateurs, i)
qui calcule l’orbite de \(i\) sous
l’action des générateurs avec, pour chaque élément \(i\) de l’orbite,
une permutation envoyant \(i\) sur \(y\).
[Sagan] | The Symmetric Group, Bruce Sagan. |
[Knuth] | The Art of Computer Programming, Sorting algorithms, Donald E. Knuth. |
[Wikipedia] | http://en.wikipedia.org/wiki/Symmetric_group |
[Seress] | Permutation Group Algorithms, Ákos Seress. http://www.cambridge.org/uk/catalogue/catalogue.asp?isbn=0511060165 |
[Kreher-Stinson] | Combinatorial Algorithms: Generation, Enumeration, and Search, Donald L. Kreher et Douglas Stinson. http://www.math.mtu.edu/~kreher/cages.html |
[Gap] | Le système de calcul formel GAP http://www-groups.dcs.st-and.ac.uk/~gap/ |
[Magma] | Le système de calcul formel Magma http://magma.maths.usyd.edu.au/magma/ |
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Introduction¶
- Savoir illustrer un enseignement:
- Présentation d’exemples de tailles plus conséquentes
- Évacuation des détails des calculs sans intérêt pédagogique
- Interactivité, gestion du rythme, expérimentation avec la classe
- Possibilité pour l’élève de refaire les calculs, et d’expérimenter avec
- Comprendre le fonctionnement général des outils de calcul formel
- Aborder l’algèbre avec un autre point de vue, plus constructif
- Manipuler de la bibliographie
Moyens:
- Expérimentation avec les outils de calcul formel
- Programmation d’algorithmes classiques pour bien les comprendre
- Étude de textes
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Calcul Formel: Qu’est-ce?¶
Premiers calculs avec Sage
:
sage: 1 + 1
2
sage: ( 1 + 2 * (3 + 5) ) * 2
34
sage: 2^3
8
sage: 2**3
8
sage: 20/6
10/3
sage: 2^10
1024
sage: 2^100
1267650600228229401496703205376
sage: 2^1000
10715086071862673209484250490600018105614048117055336074437\
50388370351051124936122493198378815695858127594672917553146\
82518714528569231404359845775746985748039345677748242309854\
21074605062371141877954182153046474983581941267398767559165\
54394607706291457119647768654216766042983165262438683720566\
8069376
sage: 20.0 / 14
1.42857142857143
sage: numerical_approx(20/14)
1.42857142857143
sage: numerical_approx(2^1000)
1.071508607186267e301
sage: numerical_approx(20/14, digits=60)
1.42857142857142857142857142857142857142857142857142857142857
Premier exemple d’instabilité numérique:
sage: (1 + 10^50) - 10^50
1
sage: (1.0 + 10^50) - 10^50
0.000000000000000
En exigeant suffisamment de précision:
sage: a = numerical_approx(1, digits=49)
sage: (x+10^50)-10^50
1.000000000000000000000000000000000000000000000000
sage: a = numerical_approx(1, digits=48)
sage: (x+10^50)-10^50
0.000000000000000000000000000000000000000000000000
Quelques exemples supplémentaires:
sage: factorial(100)
93326215443944152681699238856266700490715968264381621\
46859296389521759999322991560894146397615651828625369\
7920827223758251185210916864000000000000000000000000
sage: factor(2^(2^5)+1)
641 * 6700417
Calcul formel avec des fonctions et constantes usuelles:
sage: arccos(sin(pi/3))
arccos(1/2*sqrt(3))
sage: sqrt(2)
sqrt(2)
sage: exp(I*pi/6)
e^(1/6*I*pi)
sage: simplify(arccos(sin(pi/3)))
1/6*pi
sage: simplify(exp(i*pi/6))
1/2*sqrt(3) + 1/2*I
sage: numerical_approx( 6*arccos( sin(pi/3)), digits=60 )
3.14159265358979323846264338327950288419716939937510582097494
sage: numerical_approx( sqrt(2), digits=60 )
1.41421356237309504880168872420969807856967187537694807317668
Calcul modulo \(4\):
sage: m = 7 % 4; m
3
sage: 3 * m + 1
10
Et si l’on veut faire tout les calculs suivants modulo \(4\):
sage: Z4 = IntegerModRing(4); Z4
Ring of integers modulo 4
sage: m = Z4(7); m
3
Par la suite, tous les calculs faisant intervenir m
sont fait
modulo \(4\). Ainsi, dans l’exemple suivants, \(3\) et \(1\) sont
automatiquement convertis dans \(\ZZ/n\ZZ\):
sage: 3 * m + 1
2
Corps finis:
sage: Z3 = GF(3); Z3
Finite Field of size 3
sage: a = matrix(QQ, [[1,2,3],[2,4,8],[3,9,27]])
sage: (a^2 + 1) * a^(-1)
[ -5 13/2 7/3]
[ 7 1 25/3]
[ 2 19/2 27]
sage: P = QQ['x']; P
Univariate Polynomial Ring in x over Rational Field
sage: F = P.fraction_field(); F
Fraction Field of Univariate Polynomial Ring in x over Rational Field
sage: p = P(x+1) * P(x); p
x^2 + x
sage: p + 1/p
(x^4 + 2*x^3 + x^2 + 1)/(x^2 + x)
sage: parent(p + 1/p)
Fraction Field of Univariate Polynomial Ring in x over Rational Field
sage: k.<a> = NumberField(x^3 + x + 1)
sage: a^3
-a - 1
sage: a^4+3*a
-a^2 + 2*a
Calcul formel =
- Arithmétique (nombres, …)
- Calcul algébrique (matrices, polynômes, séries, groupes)
- Calcul symbolique (intégration, …)
Calcul mathématique (computational mathematics) =
- Calcul formel
- Combinatoire, graphes
- Calcul numérique
- Recherche opérationnelle
- …
L’option Algèbre et Calcul Formel¶
- Arithmétique
- Algèbre linéaire
- Factorisation
- Polynômes et systèmes polynomiaux
- Groupes, combinatoire, …
- En filigrane: algorithmique et complexité
- Cryptographie
- Codage
- Solveurs exacts (linéaire, …) pour les sciences de l’ingénieur
- Robotique
- Diviser pour mieux régner
- Élimination (Gauß, Euclide, Gröbner, SGS)
- Évaluation (Fourier)
- Changements de représentation
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Modélisation mathématique¶
Sage
est orienté objet¶Python
et Sage
utilisent fortement la programmation orientée
objet. Même si cela reste relativement transparent pour une
utilisation occasionnelle, il est utile d’en savoir un minimum,
d’autant que ce fait est très naturel dans un contexte mathématique.
Le paradigme de la programmation orientée objet s’appuie sur un
principe: «toute entité du monde physique ou mathématique que l’on
souhaite manipuler avec l’ordinateur est modélisé par un objet»; de
plus cette objet est une instance d’une classe. Ainsi, le nombre
rationnel \(o=12/35\) est modélisé par un objet qui est une
instance de la classe Rational
:
sage: o = 12/35
sage: type(o)
<type 'sage.rings.rational.Rational'>
Noter que cette classe est vraiment associée à l’objet \(12/35\),
et non seulement à la variable o
qui le contient:
sage: type(12/35)
<type 'sage.rings.rational.Rational'>
Précisons les définitions. Un objet est une portion de la mémoire de l’ordinateur qui contient l’information nécessaire pour représenter l’entité qu’il modélise. La classe quant à elle définit deux choses:
- la structure de données d’un objet, c’est-à-dire comment
l’information est organisée dans le bloc mémoire. Par exemple, la
classe
Rational
stipule qu’un nombre rationel comme \(12/35\) est représenté, en gros, par deux nombres entiers: son numérateur et son dénominateur. - son comportement, et en particulier les opérations sur cet objet:
par exemple comment on extrait le numérateur d’un nombre rationel,
comment on calcule sa valeur absolue, comment on multiplie ou
additionne deux nombres rationels, etc. Chacune de ces opération est
implantée par une méthode (respectivement
numer
,abs
, {__mult__}, {__add__}, …).
Pour factoriser un nombre entier \(o\), on va donc appeller la
méthode factor
avec la syntaxe suivante:
sage: o = 720
sage: o.factor()
2^4 * 3^2 * 5
que l’on peut lire comme: «prendre la valeur de o
et lui
appliquer la méthode factor
sans autre argument». Sous le capot,
effectue le calcul suivant:
sage: type(o).factor(o)
2^4 * 3^2 * 5
De gauche à droite: «demander à la classe de (la valeur de) o
(type(o)
) la méthode appropriée de factorisation
(type(o).factor
), et l’appliquer à o
».
Notons au passage que l’on peut appliquer une opération à une valeur sans passer par une variable:
sage: 720.factor()
2^4 * 3^2 * 5
et donc en particulier enchaîner les opérations, de la gauche vers la droite. Ici, on prend le numérateur d’un nombre rationnel, que l’on factorise ensuite:
sage: o = 720 / 133
sage: o.numerator().factor()
2^4 * 3^2 * 5
En quoi cela nous concerne-t-il? Tout d’abord, l’orientation objet
permet le polymorphisme: quelque soit l’objet o
que l’on veut
factoriser, on peut toujours utiliser la notation o.factor()
(ou
son raccourci factor(o)
). De même, calquant les notations
mathématiques usuelles, le produit de deux objets a
et b
peut
toujours être noté a*b
même si l’algorithme utilisé dans chaque
cas est différent (Pour une opération arithmétique binaire comme le
produit, la procédure de sélection de la méthode appropriée est un peu
plus compliquée que ce qui a été décrit précédemment. En effet elle
doit gérer des opérations mixtes comme la somme \(2 + 3/4\) d’un entier
et d’un nombre rationnel. En l’occurence, \(2\) sera converti en nombre
rationnel \(2/1\) avant l’addition. C’est le modèle de coercion de
Sage
qui est en charge de cela.). Voici un produit de deux nombres
entiers:
sage: 3 * 7
21
un produit de deux nombres rationnels, obtenu par produit des numérateurs et dénominateurs puis réduction:
sage: (2/3) * (6/5)
4/5
Un produit de deux nombres complexes, utilisant \(I^2=-1\):
sage: (1 + I) * (1 - I)
2
des produits commutatifs formels de deux expressions:
sage: (x + 2) * (x + 1)
(x + 1)*(x + 2)
sage: (x + 1) * (x + 2)
(x + 1)*(x + 2)
Outre la simplicité de notation, cela permet d’écrire des programmes génériques comme:
sage: def puissance_quatre(a):
....: a = a * a
....: a = a * a
....: return a
qui s’appliquent à tout objet admettant les opérations utilisées (ici la multiplication):
sage: puissance_quatre(2)
16
sage: puissance_quatre(3/2)
81/16
sage: puissance_quatre(I)
1
sage: puissance_quatre(x+1)
(x + 1)^4
sage: M = matrix([[0,-1],[1,0]]); M
[ 0 -1]
[ 1 0]
sage: puissance_quatre(M)
[1 0]
[0 1]
Plus prosaïquement, l’orientation objet permet l’introspection: on peut ainsi accéder à l’aide en ligne spécifique à la factorisation des nombres entiers avec:
sage: o = 720
sage: x.factor?
...
Definition: o.factor(self, algorithm='pari', proof=None, ...)
Docstring:
Return the prime factorization of this integer as a list of
pairs (p, e), where p is prime and e is a positive integer.
...
voire à l’implantation de cette fonction, précédée de son aide en ligne:
sage: o.factor?
...
def factor(self, algorithm='pari', proof=None, ...)
...
if algorithm == 'pari':
...
elif algorithm in ['kash', 'magma']:
...
En passant au dessus des détails techniques, on distingue bien que Sage
délègue le calcul à d’autres logiciels (Pari
, Kash
, …).
Enfin, on peut utiliser la complétion automatique pour demander
interactivement à un objet o
quelles sont toutes les opérations que
l’on peut lui appliquer:
sage: o.<tab>
o.N o.__abs__
o.__add__ o.__and__
...
Ici, il y en a beaucoup; voici celles qui commencent par n
:
sage: o.n<tab>
o.n o.nbits o.ndigits
o.next_prime o.next_probable_prime o.nth_root
o.numerator o.numerical_approx
Dans la section précédente, nous avons vu la notion technique de
classe d’un objet. Dans la pratique, il est suffisant de savoir que
cette notion existe; on a rarement besoin de regarder explicitement le
type d’un objet. En revanche Sage
introduit une contrepartie plus
conceptuelle de cette notion que nous allons aborder maintenant: celle
du parent d’un objet.
Supposons par exemple que l’on veuille déterminer si un élément \(a\) est inversible. La réponse ne va pas seulement dépendre de l’élément lui-même, mais de l’ensemble \(A\) auquel il est considéré appartenir. Par exemple, le nombre \(5\) n’est pas inversible dans l’ensemble \(\ZZ\) des entiers, son inverse \(1/5\) n’étant pas un entier:
sage: a = 5; a
5
sage: a.is_unit()
False
En revanche, il est inversible dans l’ensemble des rationnels:
sage: a = 5/1; a
5
sage: a.is_unit()
True
Comme nous l’avons vu dans la section précédente, Sage
répond
différemment à ces deux questions car les éléments \(5\) et
\(5/1\) sont dans des classes différentes:
sage: type(5)
<type 'sage.rings.integer.Integer'>
sage: type(5/1)
<type 'sage.rings.rational.Rational'>
Dans certains systèmes de calcul formel orientés objet, tels que
MuPAD
ou Axiom
l’ensemble \(X\) auquel \(x\) est
considéré appartenir (ici \(\ZZ\) ou \(\QQ\)) est simplement
modélisé par la classe de \(x\). Sage
suit l’approche de
Magma
, et modélise \(X\) par un objet supplémentaire associé à
\(x\), et appelé son parent:
sage: parent(5)
Integer Ring
sage: parent(5/1)
Rational Field
On peut retrouver ces deux ensembles avec les raccourcis:
sage: ZZ
Integer Ring
sage: QQ
Rational Field
et les utiliser pour convertir aisément un élément de l’un à l’autre lorsque cela a un sens:
sage: QQ(5).parent()
Rational Field
sage: ZZ(5/1).parent()
Integer Ring
sage: ZZ(1/5)
Traceback (most recent call last):
...
TypeError: no conversion of this rational to integer
Voici \(1\) en tant qu’entier \(1\in\ZZ\), nombre rationnel \(1\in\QQ\), et approximations flottantes \(1{,}0\in\RR\) et \(1{,}0+0{,}0i \in\CC\):
sage: ZZ(1), QQ(1), RR(1), CC(1)
(1, 1, 1.00000000000000, 1.00000000000000)
Selon le même principe, lorsque l’on demande toutes les partitions de l’entier 5, le résultat est un objet qui modélise cet ensemble:
sage: P = Partitions(5); P
Partitions of the integer 5
Pour obtenir la liste de ces objets, il faut le demander explicitement:
sage: P.list()
[[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]]
Cela permet de manipuler formellement des grands ensembles:
sage: Partitions(100000).cardinality()
27493510569775696512677516320986352688173429315980054758203125984302147328114964173055050741660736621590157844774296248940493063070200461792764493033510116079342457190155718943509725312466108452006369558934464248716828789832182345009262853831404597021307130674510624419227311238999702284408609370935531629697851569569892196108480158600569421098519
Et de calculer paresseusement avec. Ici, on tire au hasard une main de cinq cartes à jouer:
sage: Symboles = Set(["Coeur", "Carreau", "Pique", "Trefle"])
sage: Valeurs = Set([2, 3, 4, 5, 6, 7, 8, 9, 10, "Valet", "Dame", "Roi", "As"])
sage: Cartes = cartesian_product([Valeurs, Symboles])
sage: Mains = Subsets(Cartes, 5)
sage: Mains.cardinality()
2598960
sage: Mains.random_element() # random
{(2, 'Coeur'), (6, 'Pique'), (10, 'Carreau'), ('As', 'Pique'), ('Valet', 'Coeur')}
et là on manipule un mot infini défini comme point fixe d’un morphisme:
sage: m = WordMorphism('a->acabb,b->bcacacbb,c->baba')
sage: m.fixed_point('a')
word: acabbbabaacabbbcacacbbbcacacbbbcacacbbac...
Les parents étant eux-même des objets, on peut leur appliquer des opérations. Ainsi, on peut construire le produit cartésien \(\QQ^2\):
sage: cartesian_product([QQ, QQ])
The cartesian product of (Rational Field, Rational Field)
retrouver \(\QQ\) comme corps des fractions de \(\ZZ\):
sage: ZZ.fraction_field()
Rational Field
construire l’anneau des polynômes en \(x\) à coefficients dans \(\ZZ\):
sage: ZZ['x']
Univariate Polynomial Ring in x over Integer Ring
Par empilement successif, on peut construire des structure algébriques avancées comme l’espace des matrices \(3\times 3\) à coefficients polynomiaux sur un corps fini:
sage: Z5 = GF(5); Z5
Finite Field of size 5
sage: P = Z5['x']; P
Univariate Polynomial Ring in x over Finite Field of size 5
sage: M = MatrixSpace(P, 3, 3); M
Full MatrixSpace of 3 by 3 dense matrices over
Univariate Polynomial Ring in x over Finite Field of size 5
dont voici un élément:
sage: m = M.random_element(); # random
[2*x^2 + 3*x + 4 4*x^2 + 2*x + 2 4*x^2 + 2*x]
[ 3*x 2*x^2 + x + 3 3*x^2 + 4*x]
[ 4*x^2 + 3 3*x^2 + 2*x + 4 2*x + 4]
sage: m.det()
Dans les exemples ci-dessous, nous ferons de l’algèbre linéaire sur le corps fini \(\ZZ/7\ZZ\):
sage: K = GF(7); K
Finite Field of size 7
sage: list(K)
[0, 1, 2, 3, 4, 5, 6]
Nous avons choisi ce corps à titre d’illustration pour avoir des résultats lisibles. On aurait pu prendre des coefficients entiers, rationnels, ou numériques à plus ou moins haute précision. Les aspects numériques seront abordés plus en détail dans l’exposé suivant. Notons au passage que, même en calcul exact, il est possible de manipuler de relativement grosses matrices:
sage: n = 500
sage: M = random_matrix(K, n, sparse=True, density=3/n)
sage: M.visualize_structure() # not tested
sage: n = 10000
sage: M = random_matrix(K, n, sparse=True, density=3/n)
sage: M.rank() # random
9278
Définissons donc une matrice à coefficients dans \(\ZZ/7\ZZ\):
sage: A = matrix(K, 4, [5,5,4,3,0,3,3,4,0,1,5,4,6,0,6,3]); A
[5 5 4 3]
[0 3 3 4]
[0 1 5 4]
[6 0 6 3]
Calculons le polynôme caractéristique de cette matrice:
sage: P = A.characteristic_polynomial(); P
x^4 + 5*x^3 + 6*x + 2
On vérifie le théorème de Cayley-Hamilton sur cet exemple:
sage: P(A)
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
Notons que l’information sur le corps de base est préservée:
sage: P.parent()
Univariate Polynomial Ring in x over Finite Field of size 7
ce qui influe directement sur la factorisation de ce polynôme:
sage: factor(P)
(x + 3) * (x + 6) * (x + 5)^2
sage: factor(x^4 + 5*x^3 + 6*x + 2)
x^4 + 5*x^3 + 6*x + 2
Le calcul ci-dessus nous donne les valeurs propres: -3=4,-6=1 et -5=2. Quels sont les espaces propres?
sage: A.eigenspaces_left()
[
(4, Vector space of degree 4 and dimension 1 over Finite Field of size 7
User basis matrix:
[1 4 6 1]),
(1, Vector space of degree 4 and dimension 1 over Finite Field of size 7
User basis matrix:
[1 3 3 4]),
(2, Vector space of degree 4 and dimension 2 over Finite Field of size 7
User basis matrix:
[1 0 2 3]
[0 1 6 0])
]
Récupérons ces espaces propres:
sage: E = dict(A.eigenspaces_left())
sage: E[2]
Vector space of degree 4 and dimension 2 over Finite Field of size 7
User basis matrix:
[1 0 2 3]
[0 1 6 0]
E[2]
n’est pas une liste de vecteurs ni une matrice, mais un
objet qui modélise l’espace propre \(E_2\), comme le sous-espace de
\((\ZZ/7\ZZ)^4\) décrit par sa base échelon réduite. On peut donc lui
poser des questions:
sage: E[2].dimension()
2
sage: E[2].basis()
[
(1, 0, 2, 3),
(0, 1, 6, 0)
]
sage: V = E[2].ambient_vector_space(); V
Vector space of dimension 4 over Finite Field of size 7
Voire faire des calculs avec:
sage: E[2] + E[4]
Vector space of degree 4 and dimension 3 over Finite Field of size 7
Basis matrix:
[1 0 0 0]
[0 1 0 5]
[0 0 1 5]
sage: v = V([1,2,0,3])
sage: v in E[2]
True
sage: E[2].echelon_coordinates(v)
[1, 2]
sage: E[2].is_subspace(E[4])
False
sage: E[2].is_subspace(V)
True
sage: Q = V/E[2]; Q
Vector space quotient V/W of dimension 2 over Finite Field of size 7 where
V: Vector space of dimension 4 over Finite Field of size 7
W: Vector space of degree 4 and dimension 2 over Finite Field of size 7
User basis matrix:
[1 0 2 3]
[0 1 6 0]
sage: Q( V([0,0,0,1]) )
(2, 4)
On veut maintenant manipuler \(A\) comme un morphisme sur \(V\):
sage: phi = End(V)(A); phi
Free module morphism defined by the matrix
[5 5 4 3]
[0 3 3 4]
[0 1 5 4]
[6 0 6 3]
Domain: Vector space of dimension 4 over Finite Field of size 7
Codomain: Vector space of dimension 4 over Finite Field of size 7
sage: v = V.an_element()
sage: v
(1, 0, 0, 0)
sage: phi(v)
(5, 5, 4, 3)
sage: (phi^-1)(v)
(1, 2, 3, 4)
sage: phi^4 + 5*phi^3 + 6*phi + 2
Free module morphism defined by the matrix
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
Domain: Vector space of dimension 4 over Finite Field of size 7
Codomain: Vector space of dimension 4 over Finite Field of size 7
sage: (phi - 1).image()
Vector space of degree 4 and dimension 3 over Finite Field of size 7
Basis matrix:
[1 0 0 0]
[0 1 0 5]
[0 0 1 5]
sage: (phi - 1).kernel() == E[1]
True
sage: phi.restrict(E[2])
Free module morphism defined by the matrix
[2 0]
[0 2]
Domain: Vector space of degree 4 and dimension 2 over Finite Field of ...
Codomain: Vector space of degree 4 and dimension 2 over Finite Field of ...
- « Mathematics is the art of reducing any problem to linear algebra » William Stein
- Il serait en principe suffisant d’implanter l’algèbre linéaire sur les matrices
- Le pari de Sage: modéliser au plus près les mathématiques, pour que l’utilisateur ou le programmeur puisse s’exprimer dans le langage adapté au problème considéré.
Un parent n’a, en général, pas lui-même un parent, mais une catégorie qui indique ses propriétés:
sage: C = QQ.category(); C
Category of quotient fields
De fait Sage
sait que \(\QQ\) est un corps:
sage: QQ in Fields()
True
et donc, par exemple, un groupe additif commutatif:
sage: QQ in CommutativeAdditiveGroups()
True
Voici tous les axiomes satisfaits par \(\QQ\):
sage: C.axioms()
et les catégories de \(\QQ\):
sage: G = C.category_graph()
sage: G.set_latex_options(format="dot2tex")
sage: view(G, tightpage=True, viewer="pdf")
Sage
en déduit que \(\QQ[x]\) est un anneau euclidien:
sage: QQ['x'].category()
Category of euclidean domains
En général, il peut combiner des axiomes et des structures:
sage: Magmas().Associative() & Magmas().Unital().Inverse() & Sets().Finite()
Category of finite groups
Et appliquer par exemple le théorème de Wedderburn:
sage: Rings().Division() & Sets().Finite()
Category of finite fields
Toutes ces propriétés sont utilisées pour calculer rigoureusement et plus efficacement sur les éléments de ces ensembles.
Dans cette section, nous donnons quelques exemples typiques pour lesquels il est important de contrôler le domaine de calcul. En première lecture, on peut passer rapidement sur les exemples plus avancés pour arriver directement à la synthèse de fin de section.
Soit \(c\) une expression un tout petit peu compliquée:
sage: a = var('a')
sage: c = (a+1)^2 - (a^2+2*a+1)
et cherchons à résoudre l’équation en \(x\) donnée par \(cx=0\):
sage: eq = c * x == 0
L’utilisateur imprudent pourrait être tenté de simplifier cette équation par \(c\) avant de la résoudre:
sage: eq2 = eq / c; eq2
x == 0
sage: solve(eq2, x)
[x == 0]
Heureusement, Sage
ne fait pas cette erreur:
sage: solve(eq, x)
[x == x]
Ici, Sage
a pu résoudre correctement le système car le coefficient
\(c\) est une expression polynomiale. Il est donc facile de tester
si \(c\) est nul; il suffit de le développer:
sage: expand(c)
0
Et d’utiliser le fait que deux polynômes sous forme développée identiques sont égaux. On dit que la forme développée d’un polynôme est une forme normale.
En revanche, sur un exemple à peine plus compliqué, Sage
commet une
erreur:
sage: c = cos(a)^2 + sin(a)^2 - 1
sage: eq = c*x == 0
sage: solve(eq, x)
[x == 0]
alors même qu’il sait faire la simplification et même le test à zéro correctement:
sage: c.simplify_trig()
0
sage: c.is_zero()
True
Cet exemple illustre l’importance du test de nullité, et plus généralement des formes normales, dans un domaine de calcul. Sans lui, tout calcul faisant intervenir une division devient hasardeux. Les algorithmes comme le pivot de Gauss en algèbre linéaire sont particulièrement sensibles à ces considérations.
Construisons l’anneau \(\QQ[x_1,x_2,x_3,x_4]\) des polynômes en \(4\) variables:
sage: R = QQ['x1,x2,x3,x4']; R
Multivariate Polynomial Ring in x1, x2, x3, x4 over Rational Field
sage: x1, x2, x3, x4 = R.gens()
Les éléments de \(R\) sont automatiquement représentés sous forme développée:
sage: x1 * (x2 - x3)
x1*x2 - x1*x3
qui comme nous l’avons vu est une forme normale. On dit alors que \(R\) est à représentation normale. En particulier le test à zéro y est immédiat:
sage: (x1+x2)*(x1-x2) - (x1^2 -x2^2)
0
Mais ce n’est pas toujours un avantage. Par exemple, si l’on construit le déterminant de Vandermonde \(\prod_{1\leq i < j \leq n} (x_i-x_j)\):
sage: prod( (a-b) for (a,b) in Subsets([x1,x2,x3,x4],2) )
x1^3*x2^2*x3 - x1^2*x2^3*x3 - x1^3*x2*x3^2 + x1*x2^3*x3^2
+ x1^2*x2*x3^3 - x1*x2^2*x3^3 - x1^3*x2^2*x4 + x1^2*x2^3*x4
+ x1^3*x3^2*x4 - x2^3*x3^2*x4 - x1^2*x3^3*x4 + x2^2*x3^3*x4
+ x1^3*x2*x4^2 - x1*x2^3*x4^2 - x1^3*x3*x4^2 + x2^3*x3*x4^2
+ x1*x3^3*x4^2 - x2*x3^3*x4^2 - x1^2*x2*x4^3 + x1*x2^2*x4^3
+ x1^2*x3*x4^3 - x2^2*x3*x4^3 - x1*x3^2*x4^3 + x2*x3^2*x4^3
on obtient \(4!=24\) termes. Alors que la même construction avec une expression reste sous forme factorisée qui est ici beaucoup plus compacte et lisible:
sage: x1, x2, x3, x4 = var('x1, x2, x3, x4')
sage: prod( (a-b) for (a,b) in Subsets([x1,x2,x3,x4],2) )
(x3 - x4)*(x2 - x4)*(x2 - x3)*(x1 - x4)*(x1 - x3)*(x1 - x2)
De même, une représentation factorisée ou partiellement factorisée permet des calculs de { pgcd} bien plus rapides. Réciproquement, il ne serait pas judicieux de mettre automatiquement tout polynôme sous forme factorisée, même s’il s’agit aussi d’une forme normale, car la factorisation est coûteuse et non compatible avec l’addition.
De manière générale, selon le type de calcul voulu, la représentation idéale d’un élément n’est pas toujours sa forme normale. Cela amène les systèmes de calcul formel à un compromis avec les expressions. Un certain nombre de simplifications basiques, comme la réduction des rationnels ou la multiplication par zéro, y sont effectuées automatiquement; les autres transformations sont laissées à l’initiative de l’utilisateur auquel des commandes spécialisées sont proposées.
Considérons la factorisation de l’expression polynomiale suivante:
sage: x = var('x')
sage: p = 54*x^4+36*x^3-102*x^2-72*x-12
sage: factor(p)
6*(3*x + 1)^2*(x^2 - 2)
Cette réponse est-elle satisfaisante? Il s’agit bien d’une factorisation
de \(p\), mais son optimalité dépend fortement du contexte! Pour
le moment Sage
considère p
comme une expression symbolique, qui se
trouve être polynomiale. Il ne peut pas savoir si l’on souhaite
factoriser \(p\) en tant que produit de polynômes à coefficients
entiers ou à coefficients rationnels (par exemple). Pour prendre le
contrôle, nous allons préciser dans quel ensemble (domaine de calcul?)
nous souhaitons considérer \(p\). Pour commencer, nous allons
considérer \(p\) comme un polynôme à coefficient entiers. Nous
définissons donc l’anneau \(R=\ZZ[x]\) de ces polynômes:
sage: R = ZZ['x']; R
Univariate Polynomial Ring in x over Integer Ring
Puis nous convertissons \(p\) dans cet anneau:
sage: q = R(p); q
54*x^4 + 36*x^3 - 102*x^2 - 72*x - 12
À l’affichage on ne voit pas de différence, mais \(q\) sait qu’il est un élément de \(R\):
sage: parent(q)
Univariate Polynomial Ring in x over Integer Ring
Du coup, sa factorisation est sans ambiguïté:
sage: factor(q)
2 * 3 * (3*x + 1)^2 * (x^2 - 2)
On procède de même sur le corps des rationels:
sage: R = QQ['x']; R
Univariate Polynomial Ring in x over Rational Field
sage: q = R(p); q
54*x^4 + 36*x^3 - 102*x^2 - 72*x - 12
sage: factor(R(p))
(54) * (x + 1/3)^2 * (x^2 - 2)
Dans ce nouveau contexte, la factorisation est encore non ambiguë; mais
différente de précédemment. Notons au passage que Sage
sait que
\(R\) est un anneau euclidien:
sage: R.category()
Category of euclidean domains
et donc en particulier un anneau où la factorisation est unique (voir Figure {fig:premierspas:catégories}).
Cherchons maintenant une factorisation complète sur les nombres complexes. Une première option est de s’autoriser une approximation numérique des nombres complexes avec 16 bits de précision:
sage: R = ComplexField(16)['x']; R
Univariate Polynomial Ring in x over Complex Field
with 16 bits of precision
sage: q = R(p); q
54.00*x^4 + 36.00*x^3 - 102.0*x^2 - 72.00*x - 12.00
sage: factor(R(p))
(54.00) * (x - 1.414) * (x + 0.3333)^2 * (x + 1.414)
Une autre est d’agrandir un peu le corps des rationnels; ici, on va rajouter \(\sqrt{2}\).
sage: R = QQ[sqrt(2)]['x']; R
Univariate Polynomial Ring in x over Number Field in sqrt2
with defining polynomial x^2 - 2
sage: q = R(p); q
54*x^4 + 36*x^3 - 102*x^2 - 72*x - 12
sage: factor(R(p))
(54) * (x - sqrt2) * (x + sqrt2) * (x + 1/3)^2
Enfin, peut-être souhaite-t’on que les coefficients soient considérés modulo \(5\)?
sage: R = GF(5)['x']; R
Univariate Polynomial Ring in x over Finite Field of size 5
sage: q = R(p); q
4*x^4 + x^3 + 3*x^2 + 3*x + 3
sage: factor(R(p))
(4) * (x + 2)^2 * (x^2 + 3)
Dans les exemples précédents, nous avons illustré comment l’utilisateur peut contrôler le niveau de rigueur dans ses calculs. D’un côté il peut utiliser les expressions symboliques. Ces expressions vivent dans l’anneau des expressions symboliques:
sage: parent(sin(x))
Symbolic Ring
que l’on peut aussi obtenir avec:
sage: SR
Symbolic Ring
Les propriétés de cet anneau sont assez floues; il est commutatif:
sage: SR.category()
Category of commutative rings
et les règles de calcul font en gros l’hypothèse que toutes les
variables symboliques sont à valeur dans \(\CC\). Le domaine de
calcul (expressions polynomiale? rationnelles? trigonométriques?)
n’étant pas spécifié explicitement, le résultat d’un calcul nécessite le
plus souvent des transformations manuelles pour être mis sous la forme
désirée (voir {sec:calculus:simplifications}), en utilisant par exemple
expand
, combine
, collect
et simplify
. Pour bien utiliser
ces fonctions, il faut savoir quel type de transformations elles
effectuent et à quel domaine de calcul ces transformations s’appliquent.
Ainsi, l’usage aveugle de la fonction simplify
peut conduire à des
résultats faux. Des variantes de simplify
permettent alors de
préciser la simplification à effectuer.
D’un autre côté, l’utilisateur peut construire un parent qui va spécifier explicitement le domaine de calcul. Cela est particulièrement intéressant lorsque ce parent est à forme normale: c’est-à-dire que deux objets éléments sont mathématiquement égaux si et seulement si ils ont la même représentation.
Pour résumer, la souplesse est l’avantage principal des expressions:
- pas de déclaration explicite du domaine de calcul;
- ajout au vol de nouvelles variables ou fonctions symboliques;
- changement au vol du domaine de calcul (par exemple lorsque l’on prend le sinus d’une expression polynomiale);
- utilisation de toute la gamme des outils d’analyse (intégration, etc.).
Les avantages de la déclaration explicite du domaine de calcul sont:
- vertus pégagogiques: réfléchir au préalable à l’univers où vivent les objets;
- rigueur: les résultats obtenus sont garantis corrects (
Sage
n’est pas un système de calcul certifié; il peut donc toujours y avoir un bogue informatique; mais il n’y aura pas d’utilisation d’hypothèse implicite); - mise sous forme normale automatique (le plus souvent) — cela peut aussi être un inconvénient ! — ;
- constructions avancées qui seraient délicates avec des expressions (calculs sur un corps fini ou une extension algébrique de \(\QQ\), dans un anneau non commutatif…).
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Systèmes de calculs et SageMath¶
- Arithmétique: entiers longs, corps finis, …
- Polynômes, fractions rationnelles, matrices, …
- Sommations, intégration, dérivation, limites symbolique
- Solveurs (linéaire, polynômiaux, équations différentielles, …)
- Lien calcul numérique
- Bases de données (nombres premiers, groupes classiques, …)
- Langage de programmation et structures de données Multiparadigme: impératif / objet / fonctionnel Pourquoi programmer?
- Gestion de mémoire
- Interface avec d’autres systèmes
- Interface utilisateur
Systèmes généralistes:
- Mathematica
- Maple
- MuPAD (était pas trop cher)
- Axiom (libre)
- Sage (libre)
Systèmes spécialisés:
- Magma
- GAP (groupes)
- Linbox (algèbre linéaire exacte)
- Pari, NTL, … (théorie des nombres)
- R (statistiques)
- Macsima (calcul symbolique, libre)
- Matlab (calcul numérique)
- Scilab (calcul numérique)
- Python scientifique (calcul numérique)
sage: var('a,b,c,d,e,f,g')
sage: F = a + b * c + d * e * sin(f)^g
sage: F.operands()
[sin(f)^g*d*e, b*c, a]
Que se passe-t’il lorsque l’on fait:
sage: F = 0
(comptage de références ou glaneur de cellule)
Listes, ensembles et tables d’association:
sage: liste = [sin(1+x), 3, sin(1+x)]; liste
sage: ensemble = { sin(1+x), 3, sin(1+x) }; ensemble
sage: tableAssociative = { sin(1+x) : 1, 3 : 2 }
sage: tableAssociative[3]
2
sage: tableAssociative[sin(1+x)]
1
Exécution conditionnelle, boucles, fonctions, …
Début de l’utilisation de l’ordinateur comme outil pour la recherche en mathématique:
- Exploration informatique (analogue du télescope des astronomes)
- Démonstration du théorème des quatre couleurs, …
- Besoin de mise en commun des développements
- Besoin de langages de programmation de plus haut niveau
- Apparition de systèmes spécialisés libres (GAP, …)
- Apparition de systèmes généralistes commerciaux (Maple, …)
- Utilisation pour l’enseignement
- Besoin d’un système généraliste libre
- Besoin d’un système basé sur un langage de programmation généraliste (écosystème, outils de développements, paradigmes de programmation modernes, …)
- Besoin d’un système réutilisant et combinant les composants spécialisés libres (ex. Python scientifique)
- 2005: William Stein lance le projet
SageMath
- 2017:
SageMath
est développé par 300 enseignants, chercheurs, ingénieurs dans le monde entier
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Travaux pratiques¶
L’objectif de la séance est de prendre en main le logiciel Sage
. À
vous d’explorer ses fonctionnalités selon vos goûts et connaissances
préalables, et de préparer une mini-illustration de trois minutes que
vous présenterez en fin de séance.
Si vous n’avez pas encore encore eu l’occasion de le faire:
- Lire le premier chapitre de Calcul Mathématique avec Sage. À noter: la version anglaise est plus récente (2018).
- Suivre le Tutorial: Comprehensions, Iterators, and Iterables
- Suivre le Tutorial: Programming in Python and Sage
Voici quelques pistes pour la suite:
- Reparcourir les notes d’introduction
- Faire le maximum de problèmes du Projet Euler (Version en Français)
Instructions pour les mini-illustrations:
- Préparer une feuille de travail Jupyter, nommée \(<Prenom>-<Nom>\) (pour moi, cela donnerait Nicolas-Thiéry)
- La sauvegarder; cela donne un fichier comme Nicolas-Thiéry.ipynb
- Envoyer ce fichier à Nicolas.Thiery@u-psud.fr dans un mail ayant comme sujet «Illustration agrégation».
- Site principal sur Sage
- Site principal sur Sage en Français (tutoriels, …)
- D’autres tutoriaux Sage
- Calcul Mathématique avec Sage
- Programmation Python pour les mathématiques
- Cours Python de Bob Cordeau
- Guide du calcul avec les logiciels libres
- A First Course in Linear Algebra de Rob Beezer
- Utilisation du système de calcul formel libre XCAS pour l’agreg
- A Computational Introduction to Number Theory and Algebra, by Victor Shoup
- Poly d’introduction à la programmation scientifique avec MuPAD
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Produits rapides¶
Exercice: matrices \(2\times 2\) génériques
Soit \(M=\begin{pmatrix}a&b\\c&d\end{pmatrix}\).
- Calculer \(M^{-1}\) en utilisant les cofacteurs.
- Calculer \(M^{-1}\) par pivot de Gauß.
Solution
La matrice inverse:
sage: %display latex
sage: a,b,c,d = QQ['a,b,c,d'].fraction_field().gens()
sage: M = matrix([[a,b],[c,d]]); M
[a b]
[c d]
sage: M^-1
[ d/(-b*c + a*d) (-b)/(-b*c + a*d)]
[(-c)/(-b*c + a*d) a/(-b*c + a*d)]
Par pivot de Gauß:
sage: I2 = matrix(2,2,1); I2
[1 0]
[0 1]
sage: M = M.augment(I2, subdivide=True); M
[a b|1 0]
[c d|0 1]
sage: M[1] = a*M[1] - c *M[0]; M
[ a b| 1 0]
[ 0 -b*c + a*d| -c a]
sage: M[1] = M[1]/M[1,1]; M
[ a b| 1 0]
[ 0 1|(-c)/(-b*c + a*d) a/(-b*c + a*d)]
sage: M[0] = M[0] - b * M[1]; M
[ a 0| a*d/(-b*c + a*d) (-a*b)/(-b*c + a*d)]
[ 0 1| (-c)/(-b*c + a*d) a/(-b*c + a*d)]
sage: M[0] = M[0]/a; M
[ 1 0| d/(-b*c + a*d) (-b)/(-b*c + a*d)]
[ 0 1|(-c)/(-b*c + a*d) a/(-b*c + a*d)]
Théorème: formule d’inversion de matrice par blocs
Soit \(M=\begin{pmatrix}A&B\\C&D\end{pmatrix}\) une matrice par blocs, où \(A\) et \(D\) sont carrées. On suppose de plus que \(A\) et \(\Delta=(D-CA^{-1}B)\) sont inversibles. Alors,
Voir: https://en.wikipedia.org/wiki/Block_matrix#Block_matrix_inversion
Exercice
- Vérifier que l’on retrouve bien la formule d’inversion des matrices \(2\times 2\) à partir de la formule d’inversion par blocs.
- Vérifier que l’on obtient bien l’inverse de \(M\).
Théorème: «pour les matrices, l’inversion ne coûte pas plus que la multiplication»
Soient respectivement \(c_n\) et \(d_n\) les complexités de la multiplication et de l’inversion de matrices de taille \(n\).
Si \(c_n=O(n^\omega)\) avec \(\omega\geq 2\), alors \(d_n=O(n^\omega)\).
Démonstration (simplifiée)
Soit \(a\) tel que \(c_n\leq a n^\omega\), pour tout \(n\). On va chercher \(b\) tel que \(d_n \leq bc_n=ba n^\omega\), pour tout \(n\).
Supposons que \(n\) et \(b\) sont tels que \(d_n \leq ba n^\omega\).
Alors, en utilisant la formule par blocs ci-dessus, et en comptant les additions avec les multiplications (\(\omega \geq 2\)),
Donc, à condition de prendre \(b\) suffisamment grand, \(d_{2n} \leq ba(2n)^\omega\), comme voulu.
Cela donne par récurrence la complexité voulue pour \(n\) une puissance de \(2\). Quitte à gérer proprement les parties entières, le même argument marche pour tout \(n\).
Exercice
Soit \(f(x)\) une fonction suffisamment gentille dont on recherche une racine \(a\).
On suppose que l’on dispose d’une approximation \(a_0\) de \(a\), et on pose:
- Calculer \(f(a)\) par développement de Taylor de \(f\) en \(a_0\).
- Qu’en déduire sur \(a_1-a\) par rapport à \(a_0-a\)?
- Quelle conclusion peut-on en tirer? Sous quelles hypothèses?
Pour les détails, voir l’article de la Wikipedia.
Exercice
Soit \(B(z)\) une série avec un terme constant non nul. Elle admet alors une unique série inverse \(A(z)\).
Soit \(C(z)\) une série. Vérifier que:
On suppose que l’on dispose d’une approximation \(A_0(z)\) de l’inverse \(A(z)\) de \(B(z)\):
et on pose:
Que peut on dire de cette nouvelle approximation?
Proposer un analogue pour l’inversion des séries du théorème sur l’inversion de matrices ci-dessus.
On verra en TP que l’expression ci-dessus pour \(A_1(z)\) peut être obtenue par itération de Newton.
Solution
Pour \(\Longrightarrow\):
Pour \(\Longleftarrow\):
Posons maintenant \(C(z)\) tel que \(A_0(z)B(z) = 1 + C(z)\), et calculons \(A_1(z)B(z)\):
La précision double!
Notons \(c_n\) la complexité de la multiplication et \(d_n\) la complexité de la division de séries. Soit \(f(n)\geq 0\) croissante; par exemple \(f(n)=n^\omega\) avec \(\omega>0\) ou \(f(n)=\log(n)\).
Proposition: Si \(c_n=O\left(nf(n)\right)\), alors \(d_n=O\left(nf(n)\right)\).
Soit \(a\) tel que \(c_n\leq a nf(n)\), pour tout \(n\). On va chercher \(b\) tel que \(d_n \leq ba nf(n)\), pour tout \(n\).
On conclue comme précédemment.
Problème: calcul de la racine d’une série
Soit \(B(z)\) une série. On souhaite calculer sa racine, c’est-à-dire la série \(A(z)\) telle que \(A(z)^2-B(z)=0\).
Comment procéder?
Exercice
Soit \(F(X)\) un polynôme à coefficients dans \(\QQ[[z]]\). Par exemple: \(F(X)=X^2 - B(z)\).
On cherche une série \(A(z)\) telle que \(F(A(z))=0\).
On suppose que l’on dispose d’une approximation \(A_0(z)\) de \(A(z)\).
- En vous inspirant de la méthode de Newton usuelle, proposer une meilleure approximation \(A_1(z)\) de \(A(z)\).
- Quelle est la vitesse de convergence?
- Quelles opérations sont nécessaires lors d’une itération?
- Proposer un analogue pour la résolution des équations implicites du théorème sur l’inversion de matrices ci-dessus.
Exercice
- En déduire un algorithme pour calculer la racine carrée d’une série \(B(z)\). Que faut-il comme hypothèse sur \(B(z)\)?
- Que se passe-t’il si l’on essaye de calculer l’inverse d’une série de cette manière?
Remarque
On peut en fait traiter de manière similaire des équations différentielles linéaires \(F(A(z))=0\).
Application: connaissant \(B(z)\), calculer \(A(z)=\exp(B(z))\)
Indication: Prendre \(F(A(z)) = A(z)'-B(z)'A(z)\)
Soit \(F=F(z)\) et \(G(z)\) deux polynômes. On veut déterminer \(Q\) et \(R\) avec \(\deg R < \deg G\) tels que
Récrivons ceci sous la forme:
Que se passe-t’il si \(z\) est «grand»?
Idée:
- Envoyer l’infini sur zéro en prenant la réciproque des polynômes concernés: \(\overline P = z^{\deg P} P(\frac1z)\)
- Obtenir \(\overline Q\) en calculant les premiers termes de \(\frac{\overline F}{\overline G}\). C’est une inversion de série: itération de Newton, …
- Calculer \(R=F-GQ\)
Pour les détails, voir page 83 de [AECF]
Dans la suite, on considère un anneau \(K\) et deux polynômes dans \(K[z]\):
L’objectif est de calculer les coefficients \(c_k\) du polynôme \(C(z) = A(z)B(z)\).
Algorithme naïf
On se contente d’utiliser la formule \(c_k = \sum_{i+j=k} a_i b_j\).
Exercice
Quelle est la complexité du calcul du produit des polynômes \(A(z)\) et \(B(z)\) par l’algorithme naïf?
Exercice
Donner des formules pour calculer les coefficients du polynôme \((a_0+a_1z)(b_0+b_1z)\) en fonction de \(a_0,a_1,b_0,b_1\) utilisant un nombre minimal de produits.
Nous allons maintenant appliquer les deux principes suivants:
- «Si vous avez une bonne idée, appliquez la par récurrence, vous obtiendrez une meilleure idée.»
- Diviser pour régner!
Étape de récurrence
Supposons que \(n=m=2l\), et écrivons
où \(A_0=A_0(z)\), \(A_1=A_1(z)\), \(B_0=B_0(z)\) et \(B_1=B_1(z)\) sont de degré \(\leq l\).
On peut calculer \(AB\) en calculant récursivement quatre produits de polynômes de degré \(l\):
Ou seulement avec trois:
L’algorithme de Karatsuba consiste à calculer le produits de polynômes de degré \(2^r\) en appliquant récursivement l’étape précédente.
Complexité
L’algorithme de multiplication de Karatsuba est de complexité \(O(n^{\log_2(3)})\approx O(n^{1.59})\).
Démonstration
On suppose d’abord que \(n=2^r\), et on ne compte que le nombre \(f(r)\) de multiplications requises dans \(K\). Clairement:
Pour calculer le produit de deux polynômes de degré \(n\), on les complète en polynômes de degré \(2^{\lceil \log_2(n)\rceil}\). Le nombre de multiplications dans \(K\) est alors borné par:
Il est clair que le nombre d’additions est négligeable (de l’ordre de \(O(4n\log_2 n)\)).
En pratique: implantation
L’algorithme de Karatsuba, étant plus compliqué en particulier à cause de la récursion, est moins performant en petit degré que l’algorithme naïf. Aussi les implantations utilisent l’étape de récurrence en haut degré, et basculent sur un produit naïf en deçà d’un certain seuil.
Ce seuil est déterminé expérimentalement par bancs d’essais. Dans certains cas la détermination du seuil optimal pour une architecture donnée est effectuée automatiquement à la compilation.
C’est un principe très général. On l’avait déjà vu avec les tris, et on le retrouve par exemple en algèbre linéaire avec la bibliothèque ATLAS (Automatically Tuned Linear Algebra Software)
En pratique: usage
L’algorithme de Karatsuba requiert des soustractions:
- Il ne s’applique pas aux polynômes sur des semi-anneaux (par exemple \(\NN[x]\), algèbre tropicale, …)
- Il peut poser des problèmes de stabilité numérique en calcul approché (flottants, …)
Remarque stupide
Si \(x_0\) est un élément de \(K\), et \(C(z) = A(z)B(z)\) alors:
Corollaire
Soient \(x_1,\dots,x_n\) des éléments de \(K\) et munissons \(K^n\) de l’addition et de la multiplication point à point.
L’application d’évaluation:
est un morphisme d’algèbre.
C’est même un isomorphisme si on se restreint à l’ensemble \(K[z]_n\) des polynômes de degré \(<n\).
Le produit dans \((K^n,+,.)\) est de complexité \(n\). Donc il est tentant d’utiliser cet isomorphisme pour calculer les produits:
Problème
Rentable si le calcul de \(\Phi\) (évaluation) et de \(\Phi^{-1}\) (par ex. interpolation) est peu coûteux. Pour des points quelconques, c’est au moins du \(O(n^2)\).
Comment choisir de bons points d’évaluation?
Proposition
Supposons que l’anneau \(K\) contienne une racine primitive \(\omega\) de l’unité. Alors le morphisme d’algèbre:
induit un isomorphisme d’algèbre de \(K[z] / (z^n-1)\).
Démonstration
Regarder le noyau + dimension.
Remarque
On retrouve la même algèbre que dans les codes cycliques; entre autres, la multiplication par \(x\) donne une action du groupe cyclique \(C_n\).
Exercice
- \(DFT_\omega\) est une application linéaire. Donner sa matrice.
- Donner la matrice inverse.
Indication: \(\sum_{k=0}^{n-1} \omega^{ik} = \begin{cases}n&\text{si $i\equiv 0[n]$}\\0&\text{sinon}\end{cases}\)
Proposition
La transformée de Fourier discrète inverse est encore une transformée de Fourier discrète:
Remarque: lien avec la théorie des représentations
La matrice de \(DFT_\omega\) est aussi la table des caractères du groupe cyclique \(C_n\). Le fait qu’elle soit unitaire à un scalaire près est un cas particulier d’une proposition générale sur les tables de caractères. L’espace \(K[z]/(z^n-1)\) se décompose en \(n\) modules simples de dimension \(1\) et la transformation \(DFT_\omega\) correspond à la décomposition d’un polynôme dans ces modules simples.
Il existe des notions de transformées de Fourier discrètes pour d’autres groupes.
Il reste à calculer efficacement la transformée de Fourier discrète.
Diviser pour régner
Supposons que \(P\) soit un polynôme de degré au plus \(n=2k\).
Noter que \(z^{2k} - 1 = (z^k-1) (z^k+1)\).
Du coup, la moitié des racines \(2k\)-ièmes sont des racines \(k\)-èmes de l’unité, racines de \(z^k-1=0\). On peut donc utiliser la transformée de Fourier discrète pour évaluer \(P(z)\) dessus. Plus précisément, on calcule
(ce calcul est léger!) et on utilise \(DFT_{\omega^2}(P_+(z))\) pour retrouver l’évaluation de \(P(z)\) aux racines \(k\)-èmes de l’unité.
L’autre moitié des racines \(2k\)-ièmes sont les racines \(k\)-ème de l’unité décalées par un facteur \(\omega\), racines de \(z^k+1\). On calcule alors
et on peut donc utiliser \(DFT_{\omega^2}(P_-(\omega z))\).
Algorithme de multiplication par FFT
On considère une racine \(2^k\)-ème de l’unité, et on applique récursivement l’idée précédente.
Complexité: \(O(n\log n)\), comme pour les tris.
Problème
Et s’il n’y a pas de racine primitive de l’unité dans \(K\)?
On la rajoute!
Exemple: les corps cyclotomiques obtenus par extension algébrique de \(\QQ\) par un polynôme cyclotomique:
sage: K = CyclotomicField(6)
sage: omega = K.gen()
sage: omega^6
Souci: ces corps cyclotomiques nécessitent de calculer dans des extensions de corps de haut degré; donc un bon produit; cela pourrait boucler!
Algorithme de Schönhage et Strassen: \(O(n\log n\log\log n)\)
Autre souci: on a divisé par \(n=2^k\); ce n’est pas forcément possible, par exemple en caractéristique \(2\)!
Même principe que pour les polynômes; juste plus technique à cause de la gestion des retenues. On retrouve le produit par Karatsuba, par FFT, …
Ce que l’on a remarqué pour les séries s’applique aux calculs sur les nombres réels à précision arbitraire.
Algorithme de Strassen
Même principe que Karatsuba!
- Pour multiplier deux matrices \(2\times 2\), il existe des formules n’utilisant que 7 produits au lieu de 8.
- On découpe les matrices de taille \(2^k\) en \(4\) blocs de taille \(2^{k-1}\) et on utilise les formules ci-dessus récursivement.
Complexité: \(O(n^{\log_2 7}) \approx O(n^{2,8})\)
Améliorations: pour un \(k\) donné (ci-dessus \(k=2\)), chercher systématiquement l’algorithme optimal. Puis l’appliquer récursivement en découpant les matrices en \(k\times k\) blocs.
Algorithmes de Coppersmith-Winograd et suivants
Complexité: \(O(n^{2.3755\cdots})\) (1990), \(O(n^{2.374})\) (2010), \(O(n^{2,3728\cdots})\) (2014), …
Inutilisables en pratique …
- «Tout se ramène aux produits» - calcul d’inverse - division Euclidienne - résolution d’équation (différentielle)
- Algorithmes récursifs (diviser pour régner): - Structures récursives: matrices et polynômes par blocs - Itération de Newton - FFT
Parcourir les exercices suivants et en piocher un pour préparer une démonstration courte (5 minutes). Ensuite, jouer avec les exercices de votre choix. En fin de séance (vers 11h30), chacun d’entre vous présentera sa démonstration aux autres.
Exercice: Karatsuba
- Implanter l’algorithme naïf pour multiplier deux polynômes
- Implanter l’algorithme de Karatsuba pour multiplier deux polynômes
- Faire un banc d’essai pour ces deux algorithmes, et tracer un graphe permettant de comparer simultanément leur complexité pratique entre elles et avec leur complexité théorique.
- Avec votre implantation, à partir de quel seuil est-il préférable d’utiliser l’algorithme de Karatsuba?
Prolongements possibles:
- Implanter un algorithme mixte Karatsuba/naïf qui tienne compte du seuil obtenu. Comparer.
- Comparer la complexité pratique de votre implantation du produit avec celle de la bibliothèque de Sage.
- Deviner, d’après sa complexité pratique, le ou les algorithmes utilisés par Sage.
- Implanter le produit de deux entiers par Karatsuba; comparer avec l’implantation pour les polynômes.
Transformée de Fourier rapide
Voir ce sujet de TP.
Exercice: Illustration de Newton numérique
Réaliser une animation similaire à celle de l’article de la Wikipedia.
Exercice: Convergence de Newton numérique
- Choisir une équation de la forme \(f(x) = 0\) et calculer des approximations successives \(x_0, x_1, \dots,\) de l’une de ses solutions à l’aide d’une itération de Newton.
- Tracer le graphe du nombre de décimales correctes en fonction du nombre d’itérations.
Exercice: Inversion de séries formelle par itération de Newton
Soit \(B(z)\) une série formelle dans \(K[[z]]\) dont on veut calculer l’inverse \(A(z)=B^{-1}(z)\). En particulier, on supposera que son terme constant \(b_0=B(0)\) est inversible dans \(K\).
On pose la fonction \(F(X) = B(z) - 1/X\), de sorte que \(A(z)\) satisfait l’équation fonctionnelle implicite \(F(A(z))=0\).
- Choisir \(A_0(z)\) tel que \(A_0(z)\equiv A(z) [z]\)
- Supposer que l’on ait trouvé \(A_i(z)\) tel que \(A_i(z)\equiv A(z)[z^k]\). Appliquer une itération de Newton pour retrouver l’expression de \(A_{i+1}\) vue en cours, et donner sa précision (i.e. combien de termes de \(A(z)\) sont obtenus).
Exercice: Comptage des arbres par itération de Newton
Cet exercice est un complément pour la section 15.1.2 «Dénombrement d’arbres par séries génératrices» du livre «Calcul Mathématique avec Sage».
On rappelle que l’ensemble \(C\) des arbres binaires complets est défini récursivement en spécifiant qu’un arbre binaire complet est soit une feuille, soit consiste en une racine à laquelle sont attachés un sous-arbre gauche et un sous-arbre droit. Soit \(C(z)\) la série génératrices des arbres binaires complets comptés par nombres de feuilles.
- Écrire l’équation ensembliste satisfaite par \(C\).
- La traduire en équation algébrique satisfaite par \(C(z)\).
- Choisir \(C_0(z)\) tel que \(C_0(z)\equiv C(z) [z]\).
- Par itération de Newton, calculer successivement \(C_1(z)\),
\(C_2(z)\), … et indiquer le nombre de termes de \(C(z)\)
obtenus à chaque étape.
Indication: on pourra au choix représenter les \(C_i(z)\) par:
- Des fractions rationnelles (représentées par des
expressions), en utilisant la commande
taylor()
pour les développer en série entière. - Des séries tronquées à l’ordre approprié (éventuellement représentées par des polynômes), en utilisant l’exercice précédent pour les calculs d’inverse.
- Des fractions rationnelles (représentées par des
expressions), en utilisant la commande
[Riou] | Notes de cours de Joël Riou |
[AECF] | Algorithmes Efficaces pour le Calcul Formel |
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Programmation linéaire¶
Ce support de cours est principalement inspiré de l’excellent livre «Linear Programming» de Vašek Chvátal [Chvatal_LP]. C’est un extrait mis à jour d’un cours de Recherche Opérationnelle et Optimisation Discrète donné dans le cadre du M1 de Mathématiques et Ingénierie Mathématique de l’Université Lyon I en 2001-2003. Dans le cadre de la préparation à l’agrégation, l’objectif est de donner un aperçu rapide de la programmation linéaire et de ses applications en combinatoire polyhédrale.
Exemple ([Chvatal_LP] p. 3)
Besoins journaliers:
- Énergie: 2000 kcal
- Protéines: 55g
- Calcium: 800 mg
Nourriture disponible:
Portion Énergie (kcal) Protéines (g) Calcium (mg) Prix/portion Céréales 28g 110 4 2 3 Poulet 100g 205 32 12 24 Oeufs 2 gros 160 13 54 13 Lait entier 237cc 160 8 285 9 Tarte 170g 420 4 22 20 Porc et haricots 260g 260 14 80 19
Quels menu pour Pauline?
Contraintes:
- Céréales: au plus 4 portions par jour
- Poulet: au plus 3 portions par jour
- Oeufs: au plus 2 portions par jour
- Lait: au plus 8 portions par jour
- Tarte: au plus 2 portions par jour
- Porc et haricots: au plus 2 portions par jour
- Pauline peut-elle trouver une solution ?
- Comment formaliser le problème ? (modélisation)
- Qu’est-ce qui fait la spécificité du problème ?
- Savez-vous résoudre des problèmes similaires ?
Modélisation et résolution avec Sage
sage: p = MixedIntegerLinearProgram(maximization=False)
sage: cereales = p['cereales']
sage: poulet = p['poulet']
sage: oeufs = p['oeufs']
sage: lait = p['lait']
sage: tarte = p['tarte']
sage: porc = p['porc']
sage: p.add_constraint( cereales <= 4)
sage: p.add_constraint( poulet <= 3)
sage: p.add_constraint( oeufs <= 2)
sage: p.add_constraint( lait <= 8)
sage: p.add_constraint( tarte <= 2)
sage: p.add_constraint( porc <= 2)
sage: p.add_constraint( 110*cereales + 205*poulet + 160*oeufs + 160*lait + 420*tarte + 260*porc >= 2000)
sage: p.add_constraint( 4*cereales + 32*poulet + 13*oeufs + 8*lait + 4*tarte + 14*porc >= 55)
sage: p.add_constraint( 2*cereales + 12*poulet + 54*oeufs + 285*lait + 22*tarte + 80*porc >= 800)
sage: p.set_objective( 3*cereales + 24*poulet + 13*oeufs + 9*lait + 20*tarte + 19*porc)
sage: p.solve()
92.5
sage: p.get_values(cereales)
4.0
sage: p.get_values(cereales), p.get_values(poulet), p.get_values(oeufs), p.get_values(lait), p.get_values(tarte), p.get_values(porc)
(4.0, 0.0, 0.0, 4.5, 2.0, 0.0)
sage: #
Exemples ([Chvatal_LP] p. 5)
Maximiser: 5*x1 + 4*x2 + 3*x3
Sous les contraintes: 2*x1 + 3*x2 + x3 <= 5
4*x1 + x2 + 2*x3 <= 11
3*x1 + 4*x2 + 2*x3 <= 8
x1, x2, x3 >= 0
Minimiser: 3*x1 - x2
Sous les contraintes: - x1 + 6*x2 - x3 + x4 >= -3
7*x2 + 2*x4 = 5
x1 + x2 + x3 = 1
x3 + x4 <= 2
x2, x3 >= 0
Définitions
Programme linéaire sous forme standard:
Maximiser:
Sous les contraintes:
Un choix des variables \((x_1,\ldots,x_n)\) est appelé solution du problème.
Une solution est faisable si elle vérifie les contraintes.
\(z\) est appelé fonction objective. À chaque solution elle associe une valeur.
Une solution est optimale si elle est faisable et maximize la fonction objective.
Exercice
Peut-on mettre sous forme standard les exemples précédents ?
Exercice ([Chvatal_LP] p. 7)
On considère les quatre programmes linéaires standard suivants, écrits
avec la syntaxe du système de calcul formel MuPAD
:
Chvatal7a = [[ x1 <= 3,
x2 <= 7],
3 + x1 + x2,
NonNegative]
Chvatal7b = [[ x1 +x2 <= 2,
-2*x1-2*x2 <= -10 ],
3*x1 -x2,
NonNegative]
Chvatal7c = [[-2*x1 +x2 <= -1,
-x1-2*x2 <= -2],
x1 -x2,
NonNegative]
extra = [[x1 + x2 <= 1],
x1 + x2,
NonNegative]
Déterminer pour ces quatre problèmes les solutions faisables, les solutions optimales. Illustrer sur un dessin au tableau.
Solution
- Premier cas: une solution optimale unique;
- Deuxième cas: pas de solution faisable;
- Troisième cas: pas de solution optimale: on peut faire tendre la fonction objective vers l’infini avec des solutions faisables;
- Quatrième cas: une infinité de solutions optimales.
Problème
Considérons le système suivant:
s1 + 2*x1 + 3*x2 + x3 = 5
s2 + 4*x1 + x2 + 2*x3 = 11
s3 + 3*x1 + 4*x2 + 2*x3 = 8
Que peut-on dire dessus?
Solution
C’est un système affine à 6 inconnues et 3 équations.
L’ensemble des solutions est un sous espace affine de dimension \(3\) de \(\mathbb{R}^3\), que l’on peut décrire en prenant comme paramètres \(x_1\), \(x_2\) et \(x_3\).
En effet, vu la forme échelon réduite, \(s_1\), \(s_2\) et \(s_3\) s’expriment en fonction de \(x_1\), \(x_2\) et \(x_3\).
En particulier, on lit immédiatement les valeurs de \(s_1,s_2,s_3\) au point de coordonnées \(x_1=x_2=x_3=0\).
Exercice
Transformer le système pour prendre comme paramètres \(s_1\), \(s_2\) et \(x_1\).
Solution
sage: s = var('s1,s2,s3')
sage: x = var('x1,x2,x3')
sage: A = matrix([[2,3,1],[4,1,2],[3,4,2]])
sage: B = vector([5,11,8]).column()
sage: M = block_matrix([[matrix([[s1,s2,s3]]), matrix([[x1,x2,x3]]), 0], [1,A,B]]); M
[s1 s2 s3|x1 x2 x3| 0]
[--------+--------+--]
[ 1 0 0| 2 3 1| 5]
[ 0 1 0| 4 1 2|11]
[ 0 0 1| 3 4 2| 8]
sage: M.swap_columns(4,0)
sage: M.swap_columns(5,1); M
[x2 x3 s3|x1 s1 s2| 0]
[--------+--------+--]
[ 3 1 0| 2 1 0| 5]
[ 1 2 0| 4 0 1|11]
[ 4 2 1| 3 0 0| 8]
sage: M[1:] = M[1:].echelon_form()
sage: M
[ x2 x3 s3| x1 s1 s2| 0]
[-----------------+-----------------+-----]
[ 1 0 0| 0 2/5 -1/5| -1/5]
[ 0 1 0| 2 -1/5 3/5| 28/5]
[ 0 0 1| -1 -6/5 -2/5|-12/5]
sage: #
Considérons le système suivant:
sage: x1,x2,x3 = var('x1,x2,x3')
sage: Chvatal13 = [[2*x1 + 3*x2 + x3 <= 5,
....: 4*x1 + x2 + 2*x3 <= 11,
....: 3*x1 + 4*x2 + 2*x3 <= 8],
....: 5*x1 + 4*x2 + 3*x3]
Questions
Solution faisable ?
Amélioration de la solution ?
Idée: on transforme le problème en un système d’équations affines avec des contraintes de positivité:
Maximiser: z = 5*x1 + 4*x2 + 3*x3
Sous les contraintes: s1 + 2*x1 + 3*x2 + x3 = 5
s2 + 4*x1 + x2 + 2*x3 = 11
s3 + 3*x1 + 4*x2 + 2*x3 = 8
s1,...,x3 >=0
Amélioration locale: en augmentant \(x_1\) jusqu’à \(5/2\), on fait tomber \(s_1\) à zéro.
On transforme le système pour se ramener à une situation similaire à la précédente, où l’on a trois variables et la fonction objective qui s’expriment en fonction des autres variables:
Maximiser: z = - 7/2 x2 + 1/2*x3 - 5/2*s1 + 25/2
Sous les contraintes: x1 + 3/2*x2 + 1/2*x3 + 1/2*s1 = 5/2
s2 - 5*x2 - 2*s1 = 1
s3 - 1/2*x2 + 1/2*x3 - 3/2*s1 = 1/2
On appelle cette opération un pivot.
Le nom n’est pas un accident. On a effectué cette opération au moyen d’une substitution. Mais on a vu en résolvant les systèmes linéaires qu’une substitution n’est qu’un cas particulier de pivot de Gauß, et qu’il est généralement plus puissant de se mettre dans un cadre matriciel.
Pour cela, nous chargeons un petit fichier annexe:
sage: load("~/Enseignement/Agregation/media/programmation_lineaire.py")
qui contient quelques utilitaires comme:
def matrice_systeme(systeme, variables):
"""
Renvoie une matrice par block représentant un programme linéaire sous forme standard.
INPUT::
- ``systeme`` -- Un programme linéaire sous forme standard
- ``variables`` -- La liste des variables du système
EXAMPLES::
sage: x = x1,x2,x3 = var('x1,x2,x3')
sage: Chvatal13 = [[2*x1 + 3*x2 + x3 <= 5,
....: 4*x1 + x2 + 2*x3 <= 11,
....: 3*x1 + 4*x2 + 2*x3 <= 8],
....: 5*x1 + 4*x2 + 3*x3]
sage: m = matrice_systeme(Chvatal13, x); m
[ z|s1 s2 s3|x1 x2 x3| 0]
[--+--------+--------+--]
[ 1| 0 0 0|-5 -4 -3| 0]
[--+--------+--------+--]
[ 0| 1 0 0| 2 3 1| 5]
[ 0| 0 1 0| 4 1 2|11]
[ 0| 0 0 1| 3 4 2| 8]
"""
def liste_coeffs(expression):
return [expression.coeff(v) for v in variables]
inequations = systeme[0]
m = matrix([liste_coeffs(ineq.lhs()) for ineq in inequations])
rhs = vector(ineq.rhs() for ineq in inequations).column()
slack = SR.var(','.join("s%s"%i for i in range(1,len(inequations)+1)))
z = SR.var('z')
return block_matrix([[z, matrix([slack]), matrix([variables]), ZZ(0)],
[ZZ(1), ZZ(0), -matrix([liste_coeffs(systeme[1])]), ZZ(0)],
[ZZ(0), ZZ(1), m, rhs ]])
Mettons notre système sous forme matricielle:
sage: m = matrice_systeme(Chvatal13, (x1,x2,x3)); m
[ z|s1 s2 s3|x1 x2 x3| 0]
[--+--------+--------+--]
[ 1| 0 0 0|-5 -4 -3| 0]
[--+--------+--------+--]
[ 0| 1 0 0| 2 3 1| 5]
[ 0| 0 1 0| 4 1 2|11]
[ 0| 0 0 1| 3 4 2| 8]
Noter les signes négatifs pour \(z\): pour plus de cohérence avec les variables d’écart, on a écrit l’équation définissant la fonction objective sous la forme:
Rejouons maintenant le pivot. On veut remplacer le paramètre \(x_1\) par \(s_1\). Pour cela on échange les colonnes correspondantes:
sage: t = copy(m)
sage: t.swap_columns(1,4); t
[ z|x1 s2 s3|s1 x2 x3| 0]
[--+--------+--------+--]
[ 1|-5 0 0| 0 -4 -3| 0]
[--+--------+--------+--]
[ 0| 2 0 0| 1 3 1| 5]
[ 0| 4 1 0| 0 1 2|11]
[ 0| 3 0 1| 0 4 2| 8]
et on remets sous forme échelon:
sage: t[1:] = t[1:].echelon_form()
sage: t
[ z| x1 s2 s3| s1 x2 x3| 0]
[----+--------------+--------------+----]
[ 1| 0 0 0| 5/2 7/2 -1/2|25/2]
[----+--------------+--------------+----]
[ 0| 1 0 0| 1/2 3/2 1/2| 5/2]
[ 0| 0 1 0| -2 -5 0| 1]
[ 0| 0 0 1|-3/2 -1/2 1/2| 1/2]
sage: #
On automatise l’opération de pivot avec:
Ce qui donne:
sage: m = matrice_systeme(Chvatal13, (x1,x2,x3)); m
[ z|s1 s2 s3|x1 x2 x3| 0]
[--+--------+--------+--]
[ 1| 0 0 0|-5 -4 -3| 0]
[--+--------+--------+--]
[ 0| 1 0 0| 2 3 1| 5]
[ 0| 0 1 0| 4 1 2|11]
[ 0| 0 0 1| 3 4 2| 8]
sage: pivot(m, 1, 4)
[ z| x1 s2 s3| s1 x2 x3| 0]
[----+--------------+--------------+----]
[ 1| 0 0 0| 5/2 7/2 -1/2|25/2]
[----+--------------+--------------+----]
[ 0| 1 0 0| 1/2 3/2 1/2| 5/2]
[ 0| 0 1 0| -2 -5 0| 1]
[ 0| 0 0 1|-3/2 -1/2 1/2| 1/2]
sage: m = _
Et on réitère: on augmente \(x_3\) jusqu’à \(1\), ce qui fait tomber \(s_3\) à 0:
sage: pivot(m, 3, 6)
[ z|x1 s2 x3|s1 x2 s3| 0]
[--+--------+--------+--]
[ 1| 0 0 0| 1 3 1|13]
[--+--------+--------+--]
[ 0| 1 0 0| 2 2 -1| 2]
[ 0| 0 1 0|-2 -5 0| 1]
[ 0| 0 0 1|-3 -1 2| 1]
sage: m = _
sage: #
Et maintenant, que fait-on?
Est-ce que l’introduction de ces variables change le problème ?
Définition: tableau initial
Tableau initial:
Ou sous forme matricielle:
Exercice
Mettre sous forme matricielle le problème suivant:
Chvatal19 = [[ x1 + 3*x2 + x3 <= 3,
-x1 + 3*x3 <= 2,
2*x1 + 3*x2 - x3 <= 2,
2*x1 - x2 + 2*x3 <= 4],
5*x1 + 5*x2 + 3*x3,
NonNegative]
Solution
sage: m = matrice_systeme(Chvatal19, (x1,x2,x3)); m
[ z|s1 s2 s3 s4|x1 x2 x3| 0]
[--+-----------+--------+--]
[ 1| 0 0 0 0|-5 -5 -3| 0]
[--+-----------+--------+--]
[ 0| 1 0 0 0| 1 3 1| 3]
[ 0| 0 1 0 0|-1 0 3| 2]
[ 0| 0 0 1 0| 2 3 -1| 2]
[ 0| 0 0 0 1| 2 -1 2| 4]
sage: #
Définitions: tableaux
De manière générale, un tableau est un ensemble d’équations de la forme:
z + 5/2 x2 - 11/2 x3 + 5/2 s4 = 5
x1 + 3/2 x2 - 1/2 x3 + 1/2 s4 = 4
s1 + 3/2 x2 + 3/2 x3 - 1/2 s4 = 2
s2 + 3/2 x2 + 5/2 x3 + 1/2 s4 = 3
s3 - 4 x2 + 3 x3 - s4 = 2
\(x_1,s_1,s_2,s_3\) sont les variables basiques; \(\{x_1,s_1,s_2,s_3\}\) est la base.
\(x_2,x_3,s_4\) sont les variables non basiques.
Notes de terminologie
On utilise dans ce cours les tableaux, plutôt que les dictionnaires utilisés par exemple dans [Chvatal_LP]. La différence est minime: on fait juste passer les variables non basiques d’un côté ou de l’autre des équations. D’autre part, on utilise \(s_1,s_2,s_3,s_4\) plutôt que \(x_4,x_5,x_6,x_7\) comme noms pour les variables d’écarts.
Voici le dictionnaire correspondant au tableau précédent:
x1 = 1 - 3/2 x2 + 1/2 x3 - 1/2 x7
x4 = 2 - 3/2 x2 - 3/2 x3 + 1/2 x7
x5 = 3 - 3/2 x2 - 5/2 x3 - 1/2 x7
x6 = 2 + 4 x2 - 3 x3 + x7
z = 5 - 5/2 x2 + 11/2 x3 - 5/2 x7
À noter aussi que, afin d’utiliser commodément la forme échelon sur les tableaux représentés par des matrices par bloc Sage, on a choisi de faire passer l’expression de \(z\) de l’autre côté, ce qui peut différer des conventions utilisées dans certains systèmes (ex. MuPAD).
La caractéristique essentielle d’un tableau est que, connaissant les variables non-basiques, on peut immédiatement calculer les variables basiques et la fonction objective (d’où le terme de dictionnaire). Le calcul devient même immédiat si toutes les variables non-basiques sont nulles.
Remarques
- Les équations d’un tableau décrivent un sous-espace affine \(E\) de \(\mathbb{R}^{n+m}\).
- Un point \(p\) de cet espace est caractérisé par ses coordonnées dans les variables non-basiques.
- L’opération de pivot préserve ce sous-espace affine.
Exercice
Calculer directement le tableau correspondant aux variables non-basiques \(x_1,s_2,s_3\) du programme linéaire suivant:
Chvatal13 = [[2*x1 + 3*x2 + x3 <= 5,
4*x1 + x2 + 2*x3 <= 11,
3*x1 + 4*x2 + 2*x3 <= 8],
5*x1 + 4*x2 + 3*x3,
NonNegative]
Conclusion: un tableau est déterminé par le choix des variables non basiques.
Remarques
- Chaque choix de variables non-basiques correspond à une base affine de ce sous-espace.
- Chaque choix met en valeur le comportement du sous-espace au voisinage d’un point particulier: celui de coordonnées nulles dans les variables non-basiques.
Définitions
Le point de coordonnées \((0,\ldots,0)\) dans les variables non-basiques est appellé solution basique du tableau.
Un tableau est faisable si la solution basique est une solution faisable.
De manière équivalente, un tableau est faisable si les constantes dans \(B\) sont toutes positives (ou nulles).
Un tableau est optimal si la solution basique est une solution optimale.
Todo
Cas de la dimension \(2\)
Exercice (en TP)
Revenons à notre exemple:
sage: m = matrice_systeme(Chvatal19, (x1,x2,x3)); m
[ z|s1 s2 s3 s4|x1 x2 x3| 0]
[--+-----------+--------+--]
[ 1| 0 0 0 0|-5 -5 -3| 0]
[--+-----------+--------+--]
[ 0| 1 0 0 0| 1 3 1| 3]
[ 0| 0 1 0 0|-1 0 3| 2]
[ 0| 0 0 1 0| 2 3 -1| 2]
[ 0| 0 0 0 1| 2 -1 2| 4]
sage: pivot(m, 5, 1)
[ z|x1 s2 s3 s4|s1 x2 x3| 0]
[--+-----------+--------+--]
[ 1| 0 0 0 0| 5 10 2|15]
[--+-----------+--------+--]
[ 0| 1 0 0 0| 1 3 1| 3]
[ 0| 0 1 0 0| 1 3 4| 5]
[ 0| 0 0 1 0|-2 -3 -3|-4]
[ 0| 0 0 0 1|-2 -7 0|-2]
Oups, mauvais pivot! Essayons plutôt:
sage: pivot(m, 5, 3)
[ z| s1 s2 x1 s4| s3 x2 x3| 0]
[-----+-----------------------+-----------------+-----]
[ 1| 0 0 0 0| 5/2 5/2 -11/2| 5]
[-----+-----------------------+-----------------+-----]
[ 0| 1 0 0 0| -1/2 3/2 3/2| 2]
[ 0| 0 1 0 0| 1/2 3/2 5/2| 3]
[ 0| 0 0 1 0| 1/2 3/2 -1/2| 1]
[ 0| 0 0 0 1| -1 -4 3| 2]
sage: m = _
sage: pivot(m, 7, 4)
[ z| s1 s2 x1 x3| s3 x2 s4| 0]
[-----+-----------------------+-----------------+-----]
[ 1| 0 0 0 0| 2/3 -29/6 11/6| 26/3]
[-----+-----------------------+-----------------+-----]
[ 0| 1 0 0 0| 0 7/2 -1/2| 1]
[ 0| 0 1 0 0| 4/3 29/6 -5/6| 4/3]
[ 0| 0 0 1 0| 1/3 5/6 1/6| 4/3]
[ 0| 0 0 0 1| -1/3 -4/3 1/3| 2/3]
sage: m = _
sage: pivot(m, 6, 2)
[ z| s1 x2 x1 x3| s3 s2 s4| 0]
[------+---------------------------+--------------------+------]
[ 1| 0 0 0 0| 2 1 1| 10]
[------+---------------------------+--------------------+------]
[ 0| 1 0 0 0|-28/29 -21/29 3/29| 1/29]
[ 0| 0 1 0 0| 8/29 6/29 -5/29| 8/29]
[ 0| 0 0 1 0| 3/29 -5/29 9/29| 32/29]
[ 0| 0 0 0 1| 1/29 8/29 3/29| 30/29]
sage: m = _
sage: #
Exercice (TP)
Utilisez l’algorithme du simplexe pour résoudre les programmes linéaires suivants:
Chvatal26_21a = [[ x1 + x2 + 2*x3 <= 4,
2*x1 + 3*x3 <= 5,
2*x1 + x2 + 3*x3 <= 7],
3*x1 + 2*x2 + 4*x3,
NonNegative]
Chvatal26_21c = [[2*x1 + 3*x2 <= 3,
x1 + 5*x2 <= 1,
2*x1 + x2 <= 4,
4*x1 + x2 <= 5],
2*x1 + x2,
NonNegative]
Exemple
Essayons d’appliquer l’algorithme du simplexe aux programmes linéaires de [Chvatal_LP] (p. 7). Que se passe-t’il ?
sage: m = matrice_systeme(Chvatal7a, (x1,x2)); m
[ z|s1 s2|x1 x2| 0]
[--+-----+-----+--]
[ 1| 0 0|-1 -1| 0]
[--+-----+-----+--]
[ 0| 1 0| 1 0| 3]
[ 0| 0 1| 0 1| 7]
sage: m = pivot(m, 3, 1); m
[ z|x1 s2|s1 x2| 0]
[--+-----+-----+--]
[ 1| 0 0| 1 -1| 3]
[--+-----+-----+--]
[ 0| 1 0| 1 0| 3]
[ 0| 0 1| 0 1| 7]
sage: m = pivot(m, 4, 2)
sage: m
[ z|x1 x2|s1 s2| 0]
[--+-----+-----+--]
[ 1| 0 0| 1 1|10]
[--+-----+-----+--]
[ 0| 1 0| 1 0| 3]
[ 0| 0 1| 0 1| 7]
sage: m = matrice_systeme(Chvatal7b, (x1,x2)); m
[ z| s1 s2| x1 x2| 0]
[---+-------+-------+---]
[ 1| 0 0| -3 1| 0]
[---+-------+-------+---]
[ 0| 1 0| 1 1| 2]
[ 0| 0 1| -2 -2|-10]
sage: m = matrice_systeme(Chvatal7c, (x1,x2)); m
[ z|s1 s2|x1 x2| 0]
[--+-----+-----+--]
[ 1| 0 0|-1 1| 0]
[--+-----+-----+--]
[ 0| 1 0|-2 1|-1]
[ 0| 0 1|-1 -2|-2]
sage: pivot(m, 3, 2)
[ z|s1 x1|s2 x2| 0]
[--+-----+-----+--]
[ 1| 0 0|-1 3| 2]
[--+-----+-----+--]
[ 0| 1 0|-2 5| 3]
[ 0| 0 1|-1 2| 2]
sage: m = matrice_systeme(extra, (x1,x2)); m
[ z|s1|x1 x2| 0]
[--+--+-----+--]
[ 1| 0|-1 -1| 0]
[--+--+-----+--]
[ 0| 1| 1 1| 1]
sage: pivot(m, 2, 1)
[ z|x1|s1 x2| 0]
[--+--+-----+--]
[ 1| 0| 1 0| 1]
[--+--+-----+--]
[ 0| 1| 1 1| 1]
sage: #
On a un algorithme qui marche sur quelques exemples.
Il faut vérifier trois points pour savoir s’il marche en général:
- Initialisation
- Itération
- Terminaison
Proposition
Étant donné un tableau faisable, on peut toujours effectuer l’une des opérations suivantes:
- Conclure que le système a une solution optimale unique, la calculer et la certifier;
- Conclure que le système a une infinité de solutions optimales, les calculer et les certifier;
- Conclure que le système est non borné, et le certifier en décrivant une demi-droite de solutions sur laquelle \(z\) prend des valeurs aussi grandes que voulu.
- Trouver une variable entrante, une variable sortante, et effectuer un pivot. Par construction, le tableau obtenu est équivalent au tableau précédent, et est encore faisable. De plus, \(z\) a augmenté au sens large (i.e. la constante \(z^*\) dans la nouvelle expression de \(z\) est supérieure ou égale à l’ancienne).
Démonstration
Il suffit d’analyser le tableau faisable. Notons \(S_1,\ldots,S_m\) les variables basiques, \(X_1,\ldots,X_n\) les variables non-basiques, et \(C_1,\ldots,C_n,z^*\) les coefficients tels que \(z=z^*+\sum C_iX_i\).
Par exemple, dans le tableau final du problème[probleme:simplexe1], on a \(X_1=x_2\), \(X_2=s_1\), \(X_3=s_2\), \(S_1=x_1\), \(S_2=x_3\), \(S_3=s_3\), \(C_1=-3\), \(C_2=-1\), \(C_3=-1\) et \(z^*=13\).
- Si \(C_i<0\), pour tout \(i\), alors la solution basique du tableau, de coordonnées \(X_1^*=\cdots=X_n^*=0\) est l’unique solution optimale. Vérifiez le en prouvant qu’une toute solution faisable quelconque de coordonnées \(X_1,\ldots,X_n\) donnant la même valeur \(z=z^*\) à la fonction objective est égale à la solution basique du tableau.
- Si \(C_i\leq0\) pour tout \(i\), la solution basique du tableau est optimale, et l’ensemble des solutions optimales est décrit par les inéquations linéaires du système et l’annulation des variables non-basiques \(X_i\) pour lesquelles on a \(C_i<0\). Les détails sont similaires au 1.
- Sinon, on peut prendre \(X_i\), variable non-basique avec un coefficient \(C_i>0\). Si les équations du tableau n’imposent pas de limite sur \(X_i\), le système est non borné: la demi-droite décrite par \((0,\ldots,0,X_i,0,\ldots,0)\) pour \(X_i\geq0\) est composée de solutions faisables qui donnent des valeurs aussi grandes que voulu à \(z\).
- Autrement, une des variables basiques \(S_j\) tombe à zéro, et on peut faire un pivot entre la variable entrante \(X_i\) et la variable sortante \(S_j\). Par construction, la nouvelle solution basique correspond à une solution faisable \((0,\ldots,0,X_i,0,\ldots,0)\) pour un \(X_i\geq0\). En particulier le nouveau tableau est faisable, et comme \(C_i\geq0\), la constante \(z^*\) a augmenté au sens large.
Exemple ([Chvatal_LP] p. 29)
Système où \(z\) n’augmente pas strictement lors du pivot:
sage: m = matrice_systeme(Chvatal29, (x1,x2, x3)); m
[ z|s1 s2 s3|x1 x2 x3| 0]
[--+--------+--------+--]
[ 1| 0 0 0|-2 1 -8| 0]
[--+--------+--------+--]
[ 0| 1 0 0| 0 0 2| 1]
[ 0| 0 1 0|-1 3 4| 2]
[ 0| 0 0 1| 2 -4 6| 3]
sage: m = pivot(m, 6, 1); m
[ z| x3 s2 s3| x1 x2 s1| 0]
[---+-----------+-----------+---]
[ 1| 0 0 0| -2 1 4| 4]
[---+-----------+-----------+---]
[ 0| 1 0 0| 0 0 1/2|1/2]
[ 0| 0 1 0| -1 3 -2| 0]
[ 0| 0 0 1| 2 -4 -3| 0]
sage: m = pivot(m, 4, 3); m
[ z| x3 s2 x1| s3 x2 s1| 0]
[----+--------------+--------------+----]
[ 1| 0 0 0| 1 -3 1| 4]
[----+--------------+--------------+----]
[ 0| 1 0 0| 0 0 1/2| 1/2]
[ 0| 0 1 0| 1/2 1 -7/2| 0]
[ 0| 0 0 1| 1/2 -2 -3/2| 0]
sage: m = pivot(m, 5, 2); m
[ z| x3 x2 x1| s3 s2 s1| 0]
[-----+-----------------+-----------------+-----]
[ 1| 0 0 0| 5/2 3 -19/2| 4]
[-----+-----------------+-----------------+-----]
[ 0| 1 0 0| 0 0 1/2| 1/2]
[ 0| 0 1 0| 1/2 1 -7/2| 0]
[ 0| 0 0 1| 3/2 2 -17/2| 0]
sage: m = pivot(m, 6, 1); m
[ z| s1 x2 x1| s3 s2 x3| 0]
[----+--------------+--------------+----]
[ 1| 0 0 0| 5/2 3 19|27/2]
[----+--------------+--------------+----]
[ 0| 1 0 0| 0 0 2| 1]
[ 0| 0 1 0| 1/2 1 7| 7/2]
[ 0| 0 0 1| 3/2 2 17|17/2]
sage: #
Lorsque \(z\) n’augmente pas, on est forcément dans une situation de dégénérescence: le pivot change le tableau, mais pas la solution basique décrite par le tableau.
Problème
Peut-on garantir que l’algorithme va finir par s’arrêter ?
Proposition
Si l’algorithme du simplexe ne cycle pas, il termine en au plus \(\binom{n+m}m\) itérations.
Résumé de démonstration
Chaque itération correspond à un tableau faisable.
Un tableau faisable est entièrement caractérisé par le choix des variables basiques.
Il n’y a que \(C(n+m,m)\) choix possibles de variables basiques.
Remarque
L’algorithme ne peut cycler qu’en présence de dégénérescence.
Exemple ([Chvatal_LP] p. 31)
Avec une stratégie incorrecte, l’algorithme du simplexe peut cycler éternellement:
Système cyclant en 6 itérations avec la stratégie:
- Choix de la variable entrante avec le coefficient dans l’expression de \(z\) le plus fort
- Choix de la variable sortante avec le plus petit index
sage: m = matrice_systeme(Chvatal31, (x1, x2, x3, x4)); m
[ z| s1 s2 s3| x1 x2 x3 x4| 0]
[----+--------------+-------------------+----]
[ 1| 0 0 0| -10 57 9 24| 0]
[----+--------------+-------------------+----]
[ 0| 1 0 0| 0.5 -5.5 -2.5 9| 0]
[ 0| 0 1 0| 0.5 -1.5 -0.5 1| 0]
[ 0| 0 0 1| 1 0 0 0| 1]
sage: m = pivot(m, 4, 1); m
[ z| x1 s2 s3| s1 x2 x3 x4| 0]
[-----+-----------------+-----------------------+-----]
[ 1| 0.0 0 0| 20.0 -53.0 -41.0 204.0| 0]
[-----+-----------------+-----------------------+-----]
[ 0| 1.0 0 0| 2.0 -11.0 -5.0 18.0| 0]
[ 0| 0.0 1 0| -1.0 4.0 2.0 -8.0| 0]
[ 0| 0.0 0 1| -2.0 11.0 5.0 -18.0| 1]
sage: m = pivot(m, 5, 2); m
[ z| x1 x2 s3| s1 s2 x3 x4| 0]
[-----+-----------------+-----------------------+-----]
[ 1| 0 0.0 0| 6.75 13.25 -14.5 98.0| 0]
[-----+-----------------+-----------------------+-----]
[ 0| 1.0 0.0 0|-0.75 2.75 0.5 -4.0| 0]
[ 0| 0.0 1.0 0|-0.25 0.25 0.5 -2.0| 0]
[ 0| 0.0 0.0 1| 0.75 -2.75 -0.5 4.0| 1]
sage: m = pivot(m, 6, 1); m
[ z| x3 x2 s3| s1 s2 x1 x4| 0]
[-----+-----------------+-----------------------+-----]
[ 1| 0.0 0 0|-15.0 93.0 29.0 -18.0| 0]
[-----+-----------------+-----------------------+-----]
[ 0| 1.0 0 0| -1.5 5.5 2.0 -8.0| 0]
[ 0| 0.0 1.0 0| 0.5 -2.5 -1.0 2.0| 0]
[ 0| 0.0 0.0 1| 0 0 1.0 0| 1]
sage: m = pivot(m, 7, 2); m
[ z| x3 x4 s3| s1 s2 x1 x2| 0]
[-----+-----------------+-----------------------+-----]
[ 1| 0 0.0 0|-10.5 70.5 20.0 9.0| 0]
[-----+-----------------+-----------------------+-----]
[ 0| 1.0 0.0 0| 0.5 -4.5 -2.0 4.0| 0]
[ 0| 0.0 1.0 0| 0.25 -1.25 -0.5 0.5| 0]
[ 0| 0.0 0 1| 0 0 1.0 0| 1]
sage: m = pivot(m, 4, 1); m
[ z| s1 x4 s3| x3 s2 x1 x2| 0]
[-----+-----------------+-----------------------+-----]
[ 1| 0.0 0 0| 21.0 -24.0 -22.0 93.0| 0]
[-----+-----------------+-----------------------+-----]
[ 0| 1.0 0 0| 2.0 -9.0 -4.0 8.0| 0]
[ 0| 0.0 1.0 0| -0.5 1.0 0.5 -1.5| 0]
[ 0| 0 0 1| 0 0 1.0 0| 1]
sage: m = pivot(m, 5, 2); m
[ z| s1 s2 s3| x3 x4 x1 x2| 0]
[-----+-----------------+-----------------------+-----]
[ 1| 0 0.0 0| 9.0 24.0 -10.0 57.0| 0]
[-----+-----------------+-----------------------+-----]
[ 0| 1.0 0.0 0| -2.5 9.0 0.5 -5.5| 0]
[ 0| 0.0 1.0 0| -0.5 1.0 0.5 -1.5| 0]
[ 0| 0 0 1| 0 0 1.0 0| 1]
sage: #
Problème
Comment garantir que l’algorithme ne cyclera pas ?
L’algorithme du simplexe ne peut cycler qu’en présence de dégénérescence.
Problème
Comment se débarasser des dégénérescences ?
Idée: supprimer les dégénérescences en perturbant légèrement le système
Exemple ([Chvatal_LP] p. 34, 35)
On introduit des constantes \(\varepsilon_1>>\cdots>>\varepsilon_n\):
sage: m = matrice_systeme(Chvatal35, (x1, x2, x3, x4)); m
[ z| s1 s2 s3| x1 x2 x3 x4| 0]
[----+--------------+-------------------+----]
[ 1| 0 0 0| -10 57 9 24| 0]
[----+--------------+-------------------+----]
[ 0| 1 0 0| 0.5 -5.5 -2.5 9| e1]
[ 0| 0 1 0| 0.5 -1.5 -0.5 1| e2]
[ 0| 0 0 1| 1 0 0 0| e3]
sage: m = pivot(m, 4, 1); m
[ z| x1 s2 s3| s1 x2 x3 x4| 0]
[------------+--------------------------------------+---------------------------------------------------+------------]
[ 1| 0.0 0 0| 20.0 -53.0 -41.0 204.0| 20.0*e1]
[------------+--------------------------------------+---------------------------------------------------+------------]
[ 0| 1.0 0 0| 2.0 -11.0 -5.0 18.0| 2.0*e1]
[ 0| 0.0 1 0| -1.0 4.0 2.0 -8.0| -e1 + e2]
[ 0| 0.0 0 1| -2.0 11.0 5.0 -18.0|-2.0*e1 + e3]
sage: m = pivot(m, 5, 2); m
[ z| x1 x2 s3| s1 s2 x3 x4| 0]
[----------------------+--------------------------------------------------------------------+-------------------------------------------------------------------------------------------+----------------------]
[ 1| 0 0.0 0| 6.75 13.25 -14.5 98.0| 6.75*e1 + 13.25*e2]
[----------------------+--------------------------------------------------------------------+-------------------------------------------------------------------------------------------+----------------------]
[ 0| 1.0 0.0 0| -0.75 2.75 0.5 -4.0| -0.75*e1 + 2.75*e2]
[ 0| 0.0 1.0 0| -0.25 0.25 0.5 -2.0| -0.25*e1 + 0.25*e2]
[ 0| 0.0 0.0 1| 0.75 -2.75 -0.5 4.0|0.75*e1 - 2.75*e2 + e3]
sage: #
Inconvénient: solution approchée, ou introduction de calcul symbolique
Théorème
L’algorithme du simplexe termine si, lorsqu’il y a ambiguïté sur le choix de la variable entrante ou sortante, on choisit toujours la variable de plus petit index.
Cette méthode est simple et élégante.
Par contre, elle empêche toute stratégie pour faire converger l’algorithme plus vite.
Stratégie au choix, mais si \(z\) n’augmente pas pendant plus d’un certain nombre d’itérations, on bascule sur la stratégie du plus petit index jusqu’à ce que l’on soit sorti de la dégénérescence.
Pour le moment, l’algorithme du simplexe nécessite de partir d’un tableau faisable.
Problème
Dans le cas général, comment se ramener à un tableau faisable?
Le système pourrait même ne pas avoir de solution!
Exemple ([Chvatal_LP] p. 39)
Système \(P_1\):
Maximiser: \(x_1-x_2+x_3\)
Sous les contraintes:
\(2x_1-x_2+2x_3\leq4\)
\(2x_1-3x_2+x_3\leq-5\)
\(-x_1+x_2-2x_3\leq-1\)
\(x_1,x_2,x_3\geq0\)
Introduction d’un système auxiliaire \(P_0\) pour déterminer si \(P\) est faisable:
Maximiser: \(-x_0\)
Sous les contraintes:
\(2x_1-x_2+2x_3-x_0\leq4\)
\(2x_1-3x_2+x_3-x_0\leq-5\)
\(-x_1+x_2-2x_3-x_0\leq-1\)
\(x_0,x_1,x_2,x_3\geq0\)
Remarques:
- \(P_0\) est faisable (prendre \(x_0\) suffisamment grand);
- Les solutions faisables de \(P\) correspondent aux solutions faisables de \(P_0\) avec \(x_0=0\);
- \(P\) est faisable si et seulement si \(P_0\) a une solution faisable avec \(x_0=0\).
Étudions ce nouveau système:
sage: m = matrice_systeme(Chvatal40, (x1,x2,x3,x0)); m
[ z|s1 s2 s3|x1 x2 x3 x0| 0]
[--+--------+-----------+--]
[ 1| 0 0 0| 0 0 0 1| 0]
[--+--------+-----------+--]
[ 0| 1 0 0|-1 1 -2 -1|-1]
[ 0| 0 1 0| 2 -3 1 -1|-5]
[ 0| 0 0 1| 2 -1 2 -1| 4]
sage: m = pivot(m, 7, 2); m
[ z|s1 x0 s3|x1 x2 x3 s2| 0]
[--+--------+-----------+--]
[ 1| 0 0 0| 2 -3 1 1|-5]
[--+--------+-----------+--]
[ 0| 1 0 0|-3 4 -3 -1| 4]
[ 0| 0 1 0|-2 3 -1 -1| 5]
[ 0| 0 0 1| 0 2 1 -1| 9]
sage: m = pivot(m, 5, 1); m
[ z| x2 x0 s3| x1 s1 x3 s2| 0]
[----+--------------+-------------------+----]
[ 1| 0 0 0|-1/4 3/4 -5/4 1/4| -2]
[----+--------------+-------------------+----]
[ 0| 1 0 0|-3/4 1/4 -3/4 -1/4| 1]
[ 0| 0 1 0| 1/4 -3/4 5/4 -1/4| 2]
[ 0| 0 0 1| 3/2 -1/2 5/2 -1/2| 7]
sage: m = pivot(m, 6, 2); m
[ z| x2 x3 s3| x1 s1 x0 s2| 0]
[----+--------------+-------------------+----]
[ 1| 0 0 0| 0 0 1 0| 0]
[----+--------------+-------------------+----]
[ 0| 1 0 0|-3/5 -1/5 3/5 -2/5|11/5]
[ 0| 0 1 0| 1/5 -3/5 4/5 -1/5| 8/5]
[ 0| 0 0 1| 1 1 -2 0| 3]
sage: #
Maintenant, nous savons que le système \(P\) est faisable.
En fait, en éliminant \(x_0\) on obtient même un tableau faisable pour \(P\) (après pivotage de la fonction objective)!
Todo
Illustrer avec Sage; il faut juste permuter correctement les colonnes de \(z\).
Algorithme du simplexe en deux phases
Entrée: un problème \(P\) sous forme standard Sortie: une description complète des solutions optimales de \(P\)
Phase I:
Si \((0,\ldots,0)\) est solution faisable de \(P\), on passe directement à la phase II.
Définir un problème auxiliaire \(P_0\).
Le premier tableau pour \(P_0\) est infaisable.
Le rendre faisable par un pivot approprié de \(x_0\).
Appliquer le simplexe habituel:
Si à une étape donnée, \(x_0\) peut sortir de la base, le faire en priorité:
En effet, il y a une solution faisable avec \(x_0=0\), et on peut passer en phase II.
Si à une étape donnée on atteint une solution optimale:
Si \(x_0\) n’est pas basique:
Il y a une solution faisable avec \(x_0=0\). On peut donc passer en phase II.
Si \(x_0\) est basique et \(z_0<0\):
\(P\) est infaisable, et on s’arrête.
Sinon \(x_0\) est basique et \(z_0=0\):
Situation impossible si on fait toujours sortir \(x_0\) en priorité de la base.
Tirer de \(P_0\) un tableau faisable pour \(P\).
Phase II:
- Appliquer le simplexe habituel à partir du tableau donné par \(P_0\).
Exercice ([Chvatal_LP] ex 3.9a p. 44) (TP)
Résoudre à l’aide de l’algorithme du simplexe en deux phase le programme linéaire suivant:
Chvatal44_39a = [[ x1 - x2 <= -1,
- x1 - x2 <= -3,
2*x1 + x2 <= 4],
3*x1 + x2,
NonNegative]
Solution
sage: m = matrice_systeme(Chvatal44_39a, (x1,x2)); m
[ z|s1 s2 s3|x1 x2| 0]
[--+--------+-----+--]
[ 1| 0 0 0|-3 -1| 0]
[--+--------+-----+--]
[ 0| 1 0 0| 1 -1|-1]
[ 0| 0 1 0|-1 -1|-3]
[ 0| 0 0 1| 2 1| 4]
sage: m = matrice_systeme(Chvatal44_39a0, (x1, x2, x0)); m
[ z|s1 s2 s3|x1 x2 x0| 0]
[--+--------+--------+--]
[ 1| 0 0 0| 0 0 1| 0]
[--+--------+--------+--]
[ 0| 1 0 0| 1 -1 -1|-1]
[ 0| 0 1 0|-1 -1 -1|-3]
[ 0| 0 0 1| 2 1 -1| 4]
sage: m = pivot(m, 6, 2); m
[ z|s1 x0 s3|x1 x2 s2| 0]
[--+--------+--------+--]
[ 1| 0 0 0|-1 -1 1|-3]
[--+--------+--------+--]
[ 0| 1 0 0| 2 0 -1| 2]
[ 0| 0 1 0| 1 1 -1| 3]
[ 0| 0 0 1| 3 2 -1| 7]
sage: m = pivot(m, 4, 1); m
[ z| x1 x0 s3| s1 x2 s2| 0]
[----+--------------+--------------+----]
[ 1| 0 0 0| 1/2 -1 1/2| -2]
[----+--------------+--------------+----]
[ 0| 1 0 0| 1/2 0 -1/2| 1]
[ 0| 0 1 0|-1/2 1 -1/2| 2]
[ 0| 0 0 1|-3/2 2 1/2| 4]
sage: m = pivot(m, 5, 2); m
[ z| x1 x2 s3| s1 x0 s2| 0]
[----+--------------+--------------+----]
[ 1| 0 0 0| 0 1 0| 0]
[----+--------------+--------------+----]
[ 0| 1 0 0| 1/2 0 -1/2| 1]
[ 0| 0 1 0|-1/2 1 -1/2| 2]
[ 0| 0 0 1|-1/2 -2 3/2| 0]
sage: m = matrice_systeme(Chvatal44_39a, (x1, x2)); m
[ z|s1 s2 s3|x1 x2| 0]
[--+--------+-----+--]
[ 1| 0 0 0|-3 -1| 0]
[--+--------+-----+--]
[ 0| 1 0 0| 1 -1|-1]
[ 0| 0 1 0|-1 -1|-3]
[ 0| 0 0 1| 2 1| 4]
sage: m = pivot(m, 4, 1)
sage: m = pivot(m, 5, 2); m
[ z| x1 x2 s3| s1 s2| 0]
[----+--------------+---------+----]
[ 1| 0 0 0| 1 -2| 5]
[----+--------------+---------+----]
[ 0| 1 0 0| 1/2 -1/2| 1]
[ 0| 0 1 0|-1/2 -1/2| 2]
[ 0| 0 0 1|-1/2 3/2| 0]
sage: m = pivot(m, 5, 3); m
[ z| x1 x2 s2| s1 s3| 0]
[----+--------------+---------+----]
[ 1| 0 0 0| 1/3 4/3| 5]
[----+--------------+---------+----]
[ 0| 1 0 0| 1/3 1/3| 1]
[ 0| 0 1 0|-2/3 1/3| 2]
[ 0| 0 0 1|-1/3 2/3| 0]
Conclusion: il existe une unique solution optimale de coordonnées \(x_1=1\) et \(x_2=2\). La fonction objective y vaut \(z=5\).
sage: #
L’algorithme du simplexe, comme l’algorithme de Gauß, est intéressant non seulement d’un point de vue pratique, mais aussi à cause de ses conséquences théoriques.
Théorème
Tout programme linéaire \(P\) sous forme standard a l’une des propriétés suivantes:
- Si \(P\) n’a pas de solutions optimales, alors \(P\) est infaisable ou non borné;
- Si \(P\) a une solutions faisable, alors \(P\) a une solution basique faisable;
- Si \(P\) a une solution optimale, alors \(P\) a une solution basique optimale.
D’un point de vue géométrique: l’ensemble des solutions faisables est un polyèdre convexe, et s’il existes une solution optimale, alors il en existe une sur un des sommets du polyèdre convexe.
Pour une discussion complète sur ce thème, nous renvoyons au livre de référence [Chvatal_LP], ainsi qu’à l’excellente Foire Aux Questions http://rutcor.rutgers.edu/~mnk/lp-faq.html pour les évolutions récentes.
En très bref:
- L’algorithme du simplexe est de complexité exponentielle en théorie, mais quasi-linéaire dans les problèmes pratiques.
- Résoudre un programme linéaire est un problème de complexité polynomiale (ex: algorithme de l’Ellipsoïde)
Exemple
On considère le problème suivant:
Maximiser: \(z =4x_1+x_2+5x_3+3x_4\)
Sous les contraintes:
\(x_1-x_2-x_3+3x_4\leq1\)
\(5x_1+x_2+3x_3+8x_4\leq55\)
\(-x_1+2x_2+3x_3-5x_4\leq3\)
\(x_1,x_2,x_3,x_4\geq0\)
- Borne inférieure sur la valeur optimale \(z^*\)?
- Borne supérieure sur la valeur optimale \(z^*\)?
D’après la seconde contrainte:
En utilisant la somme de la deuxième et troisième contrainte:
Problème
Comment faire cela de manière systématique ?
On recherche des combinaisons linéaires des contraintes:
- \(y_1\) fois la première contrainte: \(x_1-x_2-x_3+3x_4\leq1\)
- \(y_2\) fois la seconde contrainte: \(5x_1+x_2+3x_3+8x_4\leq55\)
- \(y_3\) fois la troisième contrainte: \(-x_1+2x_2+3x_3-5x_4\leq3\)
Ce qui donne:
Quelles sont les contraintes pour obtenir une borne sur \(z^*\) ?
Pour garder le sens des inégalités: \(y_1,y_2,y_3\geq0\)
Pour obtenir une majoration de \(z=4x_1+x_2+5x_3+3x_4\):
- \(y_1+5y_2-y_3\geq4\)
- \(-y_1+y_2+2y_3\geq1\)
- \(-y_1+3y_2+3y_3\geq5\)
- \(3y_1+8y_2-5y_3\geq3\)
Si \(y_1,y_2,y_3\) satisfont ces conditions, on obtient la borne \(z\leq y_1+55y_2+3y_3\).
On veut donc minimiser \(y_1+55y_2+3y_3\)!
Par exemple, en prenant \(y_1=0\) et \(y_2=y_3=1\), on retrouve l’inégalité \(z\leq58\).
Définition
Soit \(P\) un programme linéaire sous forme standard:
Maximiser:
Sous les contraintes:
Le dual de \(P\) est le problème:
Minimiser:
Sous les contraintes:
\(P\) est appelé problème primal.
Proposition
Si \(x_1,\ldots,x_n\) est une solution faisable du problème primal et \(y_1,\ldots,y_m\) une solution faisable du problème dual, alors \(z\leq w\), i.e.
Démonstration
Il suffit d’appliquer les inégalités qui définissent les solutions faisables:
En particulier:
Si, comme dans l’exemple précédent, on connaît une solution faisable du problème dual, on obtient une borne sur le problème primal et réciproquement!
Si on connaît une solution faisable du problème primal et une solution faisable du problème dual telles que \(z=w\), i.e.
\[\sum_{j=1}^nc_j\ x_j=\sum_{i=1}^mb_i\ y_i,\]alors on sait que ces deux solutions sont optimales!
Exercice (TP)
Prouver que les solutions faisables \(x_1=0\) , \(x_2=14\), \(x_3=0\), \(x_4=5\) et \(y_1=11\), \(y_2=0\), \(y_3=6\) du problème original et de son dual sont optimales.
La donnée de \((y_1,y_2,y_3)\) donne un certificat de l’optimalité de la solution \((x_1,x_2,x_3,x_4)\):
Quelqu’un qui veut faire une vérification peut le faire quasiment sans calcul: il suffit de tester que les solutions sont faisables et que \(z=w\)!
Problème
Est-il toujours possible de trouver un tel certificat ?
La réponse est oui, et c’est le théorème central de la programmation linéaire.
Théorème
Si le problème primal a une solution optimale \((x_1^*,\ldots,x_n^*)\), alors le problème dual a une solution optimale \((y_1^*,\ldots,y_m^*)\) telle que \(w^*=z^*\), i.e.
Ce théorème nous assure de l’existence d’un certificat.
Mais y-a-t’il une technique pour le calculer ?
Oui, car la preuve va être constructive: son principe va précisément être de construire une solution optimale, en utilisant le tableau final obtenu par l’algorithme du simplexe.
Exemple
Faisons un peu de magie. Le tableau initial est:
sage: m = matrice_systeme(Chvatal54, (x1, x2, x3, x4)); m
[ z|s1 s2 s3|x1 x2 x3 x4| 0]
[--+--------+-----------+--]
[ 1| 0 0 0|-4 -1 -5 -3| 0]
[--+--------+-----------+--]
[ 0| 1 0 0| 1 -1 -1 3| 1]
[ 0| 0 1 0| 5 1 3 8|55]
[ 0| 0 0 1|-1 2 3 -5| 3]
L’algorithme du simplexe donne comme tableau final:
sage: m = pivot(m, 7, 1)
sage: m = pivot(m, 5, 3); m
[ z| x4 s2 x2| x1 s3 x3 s1| 0]
[---+-----------+---------------+---]
[ 1| 0 0 0| 1 6 2 11| 29]
[---+-----------+---------------+---]
[ 0| 1 0 0| 1 1 1 2| 5]
[ 0| 0 1 0| -5 -11 -9 -21| 1]
[ 0| 0 0 1| 2 3 4 5| 14]
sage: #
Ce calcul donne la solution optimale: \((x_1^*:=0,\ x_2^*:=14,\ x_3^*:=0)\).
Ce calcul donne aussi un certificat, mais pour le vérifier, il faut refaire tout le calcul!
Sortons le lapin du chapeau …
La variable \(y_1\) est associée à la première contrainte, qui elle même est associée à la variable d’écart \(s_1\). Hop, on prends pour \(y_1^*\) l’opposé du coefficient de \(s_1\) dans l’expression de \(z\) dans le tableau final. De même pour \(y_2^*\) et \(y_3^*\):
\((y_1^*,y_2^*,y_3^*)\) est une solution faisable du problème dual.
Par «miracle», on obtient \(w^*=z^*\).
On a donc pu lire le certificat voulu directement sur le tableau final!
Todo
Faire le calcul avec Sage
Voyons maintenant pourquoi cela marche dans le cas général.
Démonstration du théorème de dualité
Il suffit de construire une solution faisable \((y_1^*,\ldots,y_m^*)\) vérifiant \(w^*=z^*\).
On applique l’algorithme du simplexe au problème initial, en introduisant comme d’habitude les variables d’écart \(s_1,\ldots,s_m\). Dans le tableau final, \(z\) est de la forme
où les \(\overline{c_j}\) et \(d_i\) sont des coeffs nuls pour les variables basiques, et négatifs pour les autres.
On pose comme dans l’exemple:
Il ne reste plus qu’à vérifier que \((y_1^*,\ldots,y_m^*)\) est faisable et donne \(w^*=z^*\).
C’est un calcul fastidieux mais direct (surtout sous forme matricielle!):
Pour une solution quelconque \((x_1,\ldots,x_n)\), on a par définition:
En remplaçant dans l’expression ci-dessus, on obtient
Cette égalité étant vérifiée quel que soit le choix de \((x_1,\ldots,x_n)\), il doit y avoir égalité des coefficients des \(x_j\) de part et d’autre. On en déduit d’une part que
comme voulu, et d’autre part que
c’est-à-dire que \((y_1^*,\ldots,y_m^*)\) est une solution faisable du problème dual.
Proposition
Le dual du dual d’un problème \(P\) est le problème \(P\) lui-même.
(matriciellement: on transpose \(A\) et on échange \(B\) et \(C\))
Exercice
Vérifiez-le sur un exemple.
Il s’ensuit:
Théorème
On a les relations suivantes entre un problème \(P\) et son dual \(Q\):
- \(P\) admet une solution optimale si et seulement si \(Q\) en admet une.
- Si \(P\) est faisable, alors \(Q\) est borné; si \(Q\) est faisable, alors \(P\) est borné.
Exemple
Un problème et son dual peuvent être simultanément infaisables:
Maximiser: 2*x1-x2
Sous les contraintes: x1-x2 <= 1
-x1+x2 <= -2
x1, x2 >= 0
Le tableau suivant résume les possibilités (nb: un problème non borné est faisable!):
primaldual optimal infaisable non borné optimal possible impossible impossible infaisable impossible possible possible non borné impossible possible impossible
Todo
Introduire les notations matricielles. Vérifier que prendre le dual revient à transposer et à multiplier par \(-1\). En déduire que le dual du dual de \(P\) est \(P\). Redémontrer la proposition et le théorème en utilisant les notations matricielles.
Todo
explication intuitive en 5 minutes pour l’agreg
Problème
Supposons que l’on connaisse la solution optimale \((x_1^*,\ldots,x_n^*)\) du problème, mais pas le tableau final dans l’algorithme du simplexe. Peut-on retrouver la solution optimale \((y_1^*,\ldots,y_m^*)\) du problème dual de façon à obtenir un certificat ?
Pour voir cela, on va raffiner l’inégalité \(w\geq z\) sur des solutions \(x_j\) et \(y_i\) faisables en utilisant les variables d’écart pour mesurer la différence \(w-z\).
Exercice
On veut introduire des variables d’écart \(t_i\) pour le problème dual:
Donner une formule raisonable pour \(t_i\).
Exprimer \(w-z\) en fonction des \(x_i,\ y_i,\ s_i,\ t_i\).
Solution
Par définition des variables d’écart \(s_i\), on a
et donc
De même, par définition des variables d’écart \(t_j\) pour le problème dual, on a
que l’on utilise pour exprimer \(c_j\)
En remplaçant dans l’expression de \(w-z\), on obtient
Qui se simplifie en:
Problème
Que peut-on déduire de cette égalité ?
Théorème (Complémentarité des variables d’écart)
Si \((x_1^*,\ldots,x_n^*)\) est solution optimale du problème primal et \((y_1^*,\ldots,y_m^*)\) est solution optimale du problème dual, alors:
Problème
Et maintenant ? Comment utiliser ce théorème pour trouver \((y_1^*,\ldots,y_m^*)\)?
Exercice ([Chvatal_LP] p. 64-65)
Si \((x_1^*,\ldots,x_n^*)\) est une solution basique non dégénérée, alors les équations que l’on tire du théorème de complémentarité ont une unique solution.
Donc, lorsque la solution optimale du problème est non dégénérée, la technique que l’on a utilisée dans les exercices permet toujours d’obtenir un certificat, pour le prix de la résolution d’un système de \(m\) équations linéaires en \(m\) variables.
Exercice
Maximiser \(x_1+x_2\)
Sous les contraintes
\(2x_1+x_2\leq14\)
\(-x_1+x_2\leq8\)
\(2x_1-x_2\leq10\)
\(x_1,x_2\geq0.\)
- Faire une figure dans le plan de la région des solutions faisables.
- Donner le problème dual.
- Prendre \(y_1=y_2=1,y_3=0\). Donner l’inégalité sur les \(x_i\) correspondante, et représenter la région qu’elle délimite dans le plan.
- Donner quelques solutions faisables du problème dual.
- Tracer sur la figure les régions délimitées par les inégalités correspondantes.
- Calculer la solution optimale du primal et du dual.
- Les tracer sur la figure.
- Essayer d’interpréter géométriquement les théorèmes que l’on a rencontrés.
Todo
faire un interact illustrant le phénomène
Todo
explication intuitive en 5 minutes pour l’agreg (en s’appuyant sur le problème du Bucheron?)
Faire un interact?
Problème
Modèle économique d’une usine dont on veut maximiser le profit.
Une papetterie produit et vend différents types de papier: du papier kraft vendu au rouleau, du papier recyclé vendu à la ramette et du papier velin vendu à la feuille. Pour celà, elle dispose en début de mois d’un certain stock de matière première: de l’eau (à l’hectolitre), du chlore (au litre) du bois (à la tonne), du vieux papier (au kilo), des fibres textiles (au ballot). Remplacer les stocks en fin de mois à un certain coût. Chaque type de papier nécessite une certaine proportion de chaque matière première. Par exemple, le chlore sert à blanchir le papier; il n’y en a pas besoin pour le papier kraft; le papier velin est essentiellement produit à partir de bois et de fibres textiles, etc. Le but est de prévoir, pour le mois qui vient, quelle quantité de chaque papier il faut produire pour maximiser le profit de la papetterie.
- Modéliser ce problème sous forme de programme linéaire sous forme standard.
\(x_j\) : quantité de produit \(j\) fabriquée
\(c_j\) : prix de vente unitaire du produit \(j\)
\(a_{ij}\): quantité de ressource \(i\) consommée par unité de produit \(j\) fabriquée
\(b_i\): limites sur la disponibilité de la ressource \(i\)
Maximiser:
\[z=\sum_{j=1}^nc_jx_j\]Sous les contraintes:
\[\sum_{j=1}^na_{ij}x_j\leq b_i,\textrm{ pour }i=1,\ldots,m;\]\[x_j\geq0,\textrm{ pour }j=1,\ldots,n.\]
- Quelle dimension (au sens physique) ont les variables \(x_j\) , \(b_i\) , \(c_j\) , \(a_{ij}\)?
- On voudrait trouver une interprétation pour les variables \(y_i\) dans le problème dual. Quelle dimension physique ont-elles? Qu’est-ce que cela suggère ?
Cela suggère que \(y_i\) mesure la valeur intrinsèque de la ressource \(i\) pour l’usine.
Théorème
S’il y a au moins une solution optimale \((x_1^*,\ldots,x_m^*)\) non dégénérée, alors il existe \(\varepsilon\) strictement positif tel que lorsque \(|t_i|\leq\varepsilon\) pour tout \(i\), le programme linéaire relaxé:
Maximiser:
Sous les contraintes:
a une solution optimale, et la valeur optimale est
où \(z^*\) est la valeur optimale du problème original et \((y_1^*,\ldots,y_m^*)\) est la solution optimale du dual.
Autrement dit, on peut mesurer l’espérance de gain au voisinage d’une solution optimale lorsque l’on relaxe certaines des contraintes: \(y_i^*\) décrit le gain que l’usine peut espérer en augmentant la quantité de ressource \(i\) disponible.
Exercice
Utiliser le théorème de dualité pour vérifier les solutions des problèmes de programmation linéaire que vous avez résolu jusqu’ici.
Exercice
Un bûcheron a 100 hectares de bois de feuillus. Couper un hectare de bois et laisser la zone se régénérer naturellement coûte 10 kF par hectares, et rapporte 50 kF. Alternativement, couper un hectare de bois, et replanter avec des pins coûte 50 kF par hectares, et rapporte à terme 120 kF. Sachant que le bûcheron n’a que 4000 kF en caisse au début de l’opération, déterminer la meilleure stratégie à adopter et le profit escomptable.
Maintenant, le bûcheron a aussi l’option d’emprunter pour augmenter son capital initial, et ce pour un taux d’intérêt total de \(S\)% sur la durée de l’opération. Alternativement, il peut décider d’investir son capital dans une autre activité rapportant \(T\)% sur la durée de l’opération. Déterminer, selon les valeurs de \(S\) et \(T\), la meilleure stratégie à adopter.
Exercice
Pouvez vous interpréter les conditions de complémentarité des variables d’écart en termes économiques ?
Exercice
L’objectif est de démontrer l’un des sens du théorème d’interprétation économique des variables duales. L’autre sens est plus technique, et ne sera pas abordé ici; voir les références pour les détails.
Soit \(z^*\) la valeur optimale du problème primal et \((y_1^*,\ldots,y_m^*)\) une solution optimale quelconque du problème dual. Montrer que pour toute solution faisable \((x_1,\ldots,x_n)\) du problème primal où l’on a relaxé chaque contrainte \(i\) de la quantité \(t_i\), on a
Solution
Exprimons le fait que \((x_1,\ldots,x_n)\) est solution faisable du problème avec les contraintes relaxées:
\[\sum_{j=1}^na_{ij}x_j\leq b_i+t_i\]Donc:
\[\sum_{i=1}^my_i^*\left(\sum_{j=1}^na_{ij}x_j\right)\leq\sum_{i=1}^my_i^*b_i+\sum_{i=1}^my_i^*t_i=w^*+\sum_{i=1}^my_i^*t_i=z^*+\sum_{i=1}^my_i^*t_i\]On a trouvé le terme de droite voulu.
Reste à trouver le terme de gauche, ce que l’on fait avec une inversion de somme similaire à celle qui a été utilisée dans les démonstrations précédentes.
Exercice
Construire un exemple montrant que la conclusion du théorème est fausse si l’hypothèse de non dégénérescence de la solution optimale est omise.
Todo
Applications de la programmation linéaire et autres points abordés (méthodes alternatives) dans ProgrammationLinéaire.tex.
Todo
Finir de traduire en ReST la section sur Ford-Fulkerson & co dans RechercheOpérationnelle.tex, et mettre un lien depuis ici.
Todo
Note sur le corps/anneau de base
On a vu plusieurs modèles généraux pour faire de l’optimisation:
Programmation linéaire
- Algorithme du simplexeEfficace en pratique (quasiment linéaire), non polynomial en théorie
- Algorithme de l’ellipsoïdePolynomial, mais non efficace en pratique
- Méthode des points intérieursPlus ou moins efficace que le simplexe selon les cas
Théorème de dualité \(\Longrightarrow\) Certification, optimisation, coûts marginaux, …
Mais: solutions dans \(\mathbb{Q}\)
Problèmes de flots
- Algorithme de Ford-FulkersonPolynomial \(O(n^3)\). Plus efficace que le simplexe.
Théorème de dualité (flots/coupes)
- Théorème d’intégralité\(\Longrightarrow\) Algorithmes et théorèmes min-max sur des problèmes discrets.
Réseaux de transports
- Algorithme du simplexe pour les réseaux
- Théorème de dualité (coûts marginaux)
- Théorème d’intégralité
Exercice: Algorithme du simplexe
- Télécharger le fichier annexe contenant les utilitaires et exemples du cours.
- Effectuer avec Sage les exercices marqués “TP” ci-dessus.
Exercice: Complexité au pire de l’algorithme du simplexe
Expérimentez avec le programme linéaire de Klee Minty. Pour la description précise du programme linéaire, voir par exemple la référence externe de Greenberg, Harvey J.
Ajouter une fonction construisant ce programme linéaire serait un bon mini-projet pour une première contribution à Sage.
Programmes linéaires mixtes et problèmes SAT
On considère une formule booléenne, comme par exemple:
On voudrait savoir si \(F\) est satisfiable (c’est-à-dire si l’on peut choisir des valeurs Vrai/Faux pour A, B, C, D telles que la formule devienne vraie).
- Peut-on se ramener à la résolution d’un programme linéaire? D’un programme linéaire mixte?
- Quelle est la complexité?
- Tester la satisfiabilité de \(F\) au moyen de
MixedIntegerLinearProgram
. On pourra voir [CMS_LP] pour des exemples d’utilisation.
Note: pour résoudre de tel système, le plus naturel est d’utiliser depuis Sage des solveurs spécialisés de formules booléennes. Voir la section SAT Solvers du manuel de référence.
Exercice: flot maximal
Modélisation: on considère le problème (dit de flot) suivant:
On a un réseau de canalisations d’eau entre les noeuds \(a, b, \ldots{}, i\), où le nombre sur chaque arête indique le débit maximal pouvant passer par cette canalisation. L’eau rentre par le sommet \(a\) et ressort par le sommet \(i\), sans perte ni création dans les noeuds intermédiaires. Quel débit d’eau maximal peut on faire passer entre \(a\) et \(i\)?
Indication: utiliser la fonction Digraph.flow()
.
Exercice: couplage maximal dans les graphes bipartis
Un couplage d’un graphe est un ensemble d’arêtes de ce graphe deux à deux disjointes. On recherche un couplage de taille maximale du graphe biparti suivant:
- Modéliser ce problème sous la forme d’un programme linéaire et le résoudre. Quelle est la complexité de cette méthode?
- Modéliser ce problème sous la forme d’un problème de flot et le résoudre. Quelle est la complexité de cette méthode?
Exercice: couplage maximal dans les graphes
On recherche maintenant un couplage dans un graphe quelconque, comme:
Modéliser ce problème sous la forme d’un programme linéaire et le résoudre. Quelle est la complexité de cette méthode?
Comme dans l’exemple précédent, de très nombreux problèmes (durs) sur les graphes (coloration, recherche de clique, …) se modélisent naturellement sous forme de programmes linéaires (en entiers). Cela fait de la programmation linéaire un outil de choix en théorie des graphes, que ce soit en théorie qu’en pratique. Pour explorer plus ce thème, voir [CMS_LP].
Une matrice \(X=[x_{ij}]\) de taille \(n\times n\) est bistochastique si les coefficients \(x_{ij}\) sont positifs et si la somme des coefficients sur chaque ligne et chaque colonne vaut \(1\):
\(X\) est une matrice de permutation si sur chaque ligne et chaque colonne il y a exactement un \(1\) et \(n-1\) zéros:
La matrice précédente correspond à la permutation \((3,1,2)\).
Clairement une matrice de permutation est une matrice bistochastique. Réciproquement, les matrices de permutations sont les matrices bistochastiques à coefficients entiers.
Exemple
En dimension \(n=2\), quelles sont les matrices bistochastiques? quelles sont les matrices de permutations?
Théorème (Birkhoff-Von Neumann)
Toute matrice bistochastique est une combinaison linéaire convexe de matrices de permutations.
Exercice
Écrire la matrice bistochastique ci-dessus comme combinaison linéaire convexe de matrices de permutations.
Démontrer le lemme suivant en utilisant un réseau de transport adéquat:
Pour toute matrice \(X=(x_{ij})\) bistochastique, on peut trouver une matrice de permutation \(Y=(y_{ij})\) de façon à ce que si \(x_{ij}=0\) alors \(y_{ij}=0\).
En déduire une démonstration constructive du théorème de Birkhoff-Von Neumann, que vous écrirez sous la forme d’un programme.
Tester votre programme sur des matrices bistochastiques aléatoires de grande taille (comment en fabriquer?)
Problème des visites guidées.
Une compagnie propose \(7\) visites guidées dans la journée, notées \(a,b,c,d,e,f,g\), dont les horaires et durées sont fixées. Si une visite (par ex. \(a\)) termine suffisament avant une autre (par exemple \(c\)), le guide de la première visite peut enchaîner sur la deuxième; on notera alors \(a\rightarrow c\). En l’occurence, voici tous les enchaînements possibles:
\(a\rightarrow c, a\rightarrow d, a\rightarrow f, a\rightarrow g, b\rightarrow c, b\rightarrow g, d\rightarrow g, e\rightarrow f, e\rightarrow g\).
- Combien faut-il de guides au minimum dans cet exemple ?
- Comment trouver le nombre minimum de guides nécessaires dans le cas général ?
Définitions
Soit \(P=(E,<)\) un ordre partiel.
Une chaîne \(C\) de \(P\) est un ensemble de sommets de \(P\) deux-à-deux comparables:
Une antichaîne \(A\) de \(P\) est un ensemble de sommets deux-à-deux incomparables.
Une couverture en chaînes de \(P\) est un ensemble \(C_1,\ldots,C_k\) de chaînes, de sorte que tout sommet de \(P\) est dans une unique chaîne \(C_i\).
Une couverture en antichaînes de \(P\) est un ensemble \(A_1,\ldots,A_k\) d’antichaînes, de sorte que tout sommet de \(P\) est dans une unique antichaîne \(A_i\).
Exercice
Trouver dans l’ordre partiel \(P\) précédent:
- Une chaîne de taille maximale
- Une antichaîne de taille maximale
- Une couverture en chaînes de \(P\) de taille minimale
- Une couverture en antichaînes de \(P\) de taille minimale
Que remarquez vous ?
Y-aurait-il un théorème min-max reliant la taille de la plus grande chaîne et la taille de la plus petite couverture en antichaînes ? Et un autre reliant la taille de la plus grande antichaîne et celle de la plus petite couverture en chaînes ?
Exercice
Soit \(P\) un ordre partiel quelconque.
Soit \(C\) une chaîne de \(P\) et \(A_1,\ldots,A_k\) une couverture de \(P\) en antichaînes.
Montrer que \(\left|C\right|\leq k\).
Soit \(A\) une antichaîne de \(P\) et \(C_1,\ldots,C_k\) une couverture de \(P\) en chaînes.
Montrer que \(\left|A\right|\leq k\).
Proposition
Soit \(P\) un ordre partiel. La taille de la plus grande chaîne de \(P\) est égale à la taille de la plus petite couverture en antichaînes de \(P\).
Exercice
Prouvez la démonstration précédente!
Le théorème dans l’autre sens est plus difficile et bien plus profond. Il n’y a pas de construction élémentaire de l’antichaîne et de la couverture en chaîne idoine. On va en fait se ramener au théorème de dualité de la programation linéaire (surprise).
Théorème (Dilworth)
Soit \(P\) un ordre partiel. La taille de la plus grande antichaîne de \(P\) est égale à la taille de la plus petite couverture en chaînes de \(P\).
On note \(n\) le nombre de sommets de \(P\).
Choisir une couverture en chaîne de \(P\) est équivalent à sélectionner un certain nombre d’arcs dans \(P\), de sorte que chaque sommet ait au plus un arc sortant de sélectionné, et un arc rentrant de sélectionné.
Remarque: s’il y a \(k\) chaînes, il y a \(n-k\) arcs sélectionnés.
Cela ressemble à un problème de couplage maximal dans un graphe biparti.
On construit un graphe biparti \(B\) dans lequel chaque sommet \(x\) de \(P\) est dupliqué en \((x,1)\) et \((x,2)\).
Chaque fois que \(x<y\) dans \(P\), on relie \((x,1)\) et \((y,2)\).
Qu’est-ce qu’un couplage dans \(B\)?
Un ensemble d’arcs de \(P\) vérifiant exactement les conditions voulues.
Une couverture de \(P\) en \(k\) chaînes correspond à un couplage de \(B\) de taille \(n-k\).
Prenons une couverture de \(P\) de taille \(k\) minimale.
Cela donne un couplage de taille max \(n-k\) de \(B\).
Le théorème min-max pour les graphes bipartis indique qu’il y a une couverture de \(B\) de même taille: \(n-k\) sommets de \(B\) qui touchent tous les arcs.
Dans \(P\) cela correspond à au plus \(n-k\) sommets qui touchent tous les arcs.
Soit \(A\) l’ensemble des sommets restants qui est de taille au moins \(k\).
Il ne peut pas y avoir d’arcs entre deux sommets de \(A\).
Conclusion: \(A\) est une antichaîne de taille au moins \(k\).
Exercice
- Suivez le déroulement de la preuve sur l’ordre partiel précédent.
- Cette démonstration du théorème de Dilworth est constructive! En déduire un algorithme pour calculer une antichaîne de taille maximale et une couverture minimale en chaînes d’un ordre partiel.
- Quelle est la complexité de cet algorithme? Comparer avec la recherche de clique maximale et de colorations minimales dans un graphe.
[Chvatal_LP] | (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) Linear Programming, Vašek Chvátal |
[Vanderbie] | Linear Programming; Foundations and Extensions R. Vanderbie |
[LPFAQ] | Linear Programming FAQ |
[Wikipedia] | Linear Programming |
[CMS_LP] | (1, 2) Le chapitre Programmation Linéaire de Calcul Mathématique avec Sage (version anglaise: Sage’s Mixed Integer Linear Programming thematic tutorial) |
[LP1] | A basic introduction to linear programming with a graphical example in 2D |
[LP2] | Un ticket pour l’algorithme du simplexe itératif avec des tableaux |
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Tris et complexité¶
- Quel est le meilleur algorithme pour trouver un nom dans un annuaire?
- Quelle est la meilleure méthode pour calculer le déterminant d’une matrice?
- Comment prédire le temps que va mettre un programme pour s’exécuter?
- Comment savoir, entre deux algorithmes, lequel est le plus efficace?
- Comment savoir si un algorithme est optimal?
- Comment déterminer si un problème est insoluble en pratique?
Je recherche le nom «Zorro» dans un annuaire en utilisant la méthode suivante:
- Je pars du début de l’annuaire;
- Je compare le nom avec «Zorro»;
- Si oui, j’ai terminé;
- Si non, je recommence en 2 avec le nom suivant.
Problème
Combien est-ce que cela va me prendre de temps?
On s’est donné un problème (rechercher un mot dans un dictionnaire), un algorithme pour le résoudre (recherche exhaustive). Puis on a introduit un modèle de calcul:
- Choix de la mesure de la taille d’une instance du problème (le nombre de mots d’un dictionnaire donné)
- Choix des opérations élémentaires (comparer deux mots)
Dans ce modèle, on a cherché le nombre d’opérations élémentaires effectuées par l’algorithme pour un problème de taille \(n\). C’est ce que l’on appelle la complexité de l’algorithme.
En fait, on a vu deux variations:
- Complexité au pire (\(n\) opérations)
- Complexité en moyenne (\(\frac{n}{2}\) opérations)
À partir de cette information, et en connaissant le temps nécessaire pour de petites instances du problème on peut évaluer le temps nécessaire pour résoudre n’importe quelle instance du problème.
Autres variations:
- Complexité en mémoire
- Complexité vis-à-vis de toute autre ressource (bande passante, …)
Donner des algorithmes et leur complexité au pire et en moyenne pour les problèmes suivants:
Calculer la somme de deux matrices carrées
Calculer le produit de deux matrices carrées
Calculer la somme de deux entiers
Calculer le produit de deux entiers
Calculer l’inverse d’une matrice
Rechercher un élément dans une liste
Calculer le plus grand élément d’une liste
sage: def plus_grand_element(liste): ....: """ ....: Renvoie le plus grand élément de la liste ....: EXAMPLES:: ....: sage: plus_grand_element([7,3,1,10,4,10,2,9]) ....: 10 ....: sage: plus_grand_element([7]) ....: 7 ....: """ ....: resultat = liste[0] ....: for i in range(1, len(liste)-1): ....: # Invariant: ....: # resultat est le plus grand element de liste[:i] ....: assert resultat in liste[:i] ....: assert all(resultat >= x for x in liste[:i]) ....: if liste[i] > resultat: ....: resultat = liste[i] ....: return resultat sage: plus_grand_element([7,3,1,10,4,10,2,9]) 10
Digression: invariants, preuve et test
Rechercher un élément dans une liste triée
Insérer un élément dans une liste triée
sage: var('n')
sage: xmax=10^9
sage: ymax=10^19
sage: op_per_seconds=10^9
sage: funs = [n^0, log(n), sqrt(n), n, 1000*n, n*(log(n)), n^log(3,2), n^2, n^(2.3727.n(digits=5)), n^log(7,2), n^3, 2^n, 5^n, factorial(n), n^n]
sage: colors = rainbow(len(funs))
sage: def time_label(s, t): return text(s, (1,t), horizontal_alignment = "left")
sage: time_labels = sum(time_label(t,s)
....: for t,s in [["seconde", 1], ["minute", 60], ["jour",24*3600],
....: [u"année",365*24*3600], [u"siècle",100*365*24*3600],[u"âge de l'univers",14*10^9*365*24*3600]])
sage: def legend(f, color="black"):
....: label = "$" + latex(f) + "$"
....: options = {"fontsize": 14}
....: if f(n=100)/op_per_seconds >= ymax:
....: xshift=1.3^(len(funs)-2-funs.index(f))
....: return text(label, ((f/op_per_seconds-ymax).find_root(1,100)*xshift, 3*ymax), horizontal_alignment="center", **options)
....: return text(label, (1.1*xmax, f(n=xmax)/10^9), horizontal_alignment="left", **options)
sage: p = sum( plot(f/op_per_seconds,
....: xmin=1, xmax=(100 if f(n=100)>ymax else xmax),
....: ymax=ymax,
....: scale="loglog", gridlines=True, gridlinesstyle = {"color":'LightGray'},
....: color=color) + legend(f, color=color)
....: for f,color in zip(funs, colors)) + time_labels
sage: p
Exercice
On dispose d’un ordinateur pouvant exécuter \(10^{9}\) opérations élémentaires par seconde (1GHz). On a un problème (par exemple, chercher un mot dans une liste, calculer le déterminant d’une matrice). Enfin, on a plusieurs algorithmes pour résoudre ce problème, dont on connaît les complexités respectives: \(O(\log n)\), \(O(n)\), \(O(n\log n)\), \(O(n^{2})\), \(O(n^{3})\), \(O(n^{10})\), \(O(2^{n})\), \(O(n!)\), \(O(n^{n})\). Évaluer la taille de problème que l’on peut traiter en une seconde? en un an?
La plupart du temps, il suffit d’avoir un ordre de grandeur du nombre d’opérations: les constantes sont sans grande importance. Un algorithme en \(1000\log_{2}n+50\) sera meilleur qu’un algorithme en \(\frac{n}{1000}\) dès que l’on s’intéressera à des instances suffisamment grandes.
Mais voir aussi l’article Constant Time Factors do Matter
Définition
Soient \(f\) et \(g\) deux fonctions de \(\NN\) dans \(\NN\) (par exemple les complexités de deux algorithmes).
On note \(f=O(g)\) si, asymptotiquement, \(f\) est au plus du même ordre de grandeur que \(g\); formellement: il existe une constante \(a\) et un entier \(N\) tels que \(f(n)\leq ag(n)\) pour \(n\geq N\).
On note \(f=o(g)\) si, assymptotiquement, \(f\) est négligeable devant \(g\); formellement: pour toute constante \(a\) il existe \(N\) tel que \(f(n)\leq ag(n)\) pour \(n\geq N\).
Proposition
Quelques règles de calculs sur les \(O()\):
- \(O(4n+3)=O(n)\)
- \(O(\log n)+O(\log n)=O(\log n)\)
- \(O(n^{2})+O(n)=O(n^{2})\)
- \(O(n^{3})O(n^{2}\log n)=O(n^{5}\log n)\)
Exercice (Règles mixtes)
Simplifier les expressions suivantes:
- \(O(n^3\log n) o(\log n)\)
- \(O(1/n) + o(1)\)
Exercice
Donner quelques algorithmes et leur complexité pour le calcul du déterminant d’une matrice
Note
Digression: Complexité arithmétique versus complexité binaire
Exemple
On a vu un algorithme en \(O(n)\) pour rechercher le plus grand élément d’une liste de nombres.
Existe-t-il un meilleur algorithme?
Définition
La complexité d’un problème est la complexité du meilleur algorithme pour le résoudre.
On dit qu’un algorithme est optimal si sa complexité coïncide avec celle du problème.
Exercices
- Les algorithmes vus précédemment sont-ils optimaux?
- Démontrer que la recherche d’un élément dans une liste triée de taille \(n\) est un problème de complexité \(O(\log n)\).
On a une liste que l’on veut trier, mettons \([7,8,4,2,5,9,3,5]\).
- On échange le premier élément avec le plus petit des éléments: \(2,8,4,7,5,9,3,5\)
- On échange le deuxième élément avec le plus petit des éléments restants: \(2,3,4,7,5,9,8,5\)
- Etc.
- Au bout de \(k\) étapes, les \(k\) premiers éléments sont triés; on échange alors le \(k+1\)-ième élément avec le plus petit des éléments restants.
- À la fin, la liste est triée: \(2,3,4,5,5,7,8,9\).
- On groupe les éléments par paquets de deux, et on trie chacun de ces paquets: \((7,8),(2,4),(5,9),(3,5)\).
- On groupe les éléments par paquets de quatre, et on trie chacun de ces paquets: \((2,4,7,8),(3,5,5,9)\).
- …
- Au bout de \(k\) étapes, les paquets de \(2^{k}\) éléments sont triés; on les regroupe par paquets de \(2^{k+1}\) que l’on trie.
- À la fin, tous les éléments sont dans le même paquet et sont triés: \((2,3,4,5,5,7,8,9)\).
- On choisit une valeur \(p\) dans la liste que l’on appelle pivot.
- On fait des échanges judicieux jusqu’à ce que toutes les valeurs strictement plus petites que \(p\) soient placées avant \(p\), et les valeurs plus grandes soient placées après.
- On applique récursivement l’algorithme sur les éléments avant et après \(p\).
Problèmes
Quelle est le meilleur algorithme de tri?
Les algorithmes de tris en \(O(n\log n)\) sont-ils optimaux?
Théorème
Le tri d’une liste de taille \(n\) est un problème de complexité \(O(n\log n)\).
Exercices
Évaluer au mieux la complexité des problèmes suivants:
- Calcul du \(n\)-ième nombre de Fibonacci;
- Calcul du déterminant d’une matrice;
- Calcul du rang d’une matrice;
- Calcul de l’inverse d’une matrice;
- Calcul d’un vecteur \(x\) solution de \(Ax=b\), où \(A\) est une matrice et \(b\) un vecteur;
- Calcul du pgcd de deux nombres;
- Test de primalité de \(n\);
- Recherche du plus court chemin entre deux stations de métro à Paris;
- Calcul de la \(n\)-ième décimale de \(\sqrt{2}\);
- Calcul de l’inverse d’un nombre modulo \(3\);
- Recherche d’un échec et mat en \(4\) coups à partir d’une position donnée aux échecs.
- Problème du sac à dos: étant donné un ensemble d’objets de hauteur et de poids variables, et un sac à dos de hauteur donnée, charger au maximum le sac à dos?
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Option Algèbre et Calcul Formel de l’Agrégation de Mathématiques: Tris et complexité, Travaux Pratiques¶
Les exercices suivants sont à faire dans l’ordre à l’occasion des deux séances de TP. Pour l’un d’entre eux, préparer une illustration de deux minutes sur un point spécifique de votre choix. En fin de chaque séance, deux ou trois étudiants présenterons leurs illustrations au reste du groupe (cf. TP de la première semaine pour les instructions pour envoyer votre feuille de travail).
L’objectif de ce TP est d’acquérir de bonnes pratiques de programmation, notamment en vue de préparer efficacement des illustrations logicielles robustes le jour de l’oral.
- Écrire des fonctions dans un fichier séparé et les charger dans Jupyter;
- Écrire des fonctions avec documentations et tests;
- Écrire du code modulaire.
- Cahier des charges d’une fonction: Préconditions, postconditions et invariants de boucles;
- Exécuter les tests de manière automatique,
- Mesurer un nombre d’opérations, un temps d’exécution;
- Représenter la complexité dans un graphique.
Implanter une fonction
recherche(liste, valeur)
renvoyant la première position de valeur dans la liste, ouNone
si valeur n’est pas dans la liste.Tester votre fonction avec les exemples ci dessous:
sage: recherche([9,20,3,40,37,42,69,65,21,66,1,74,50], 21) 9 sage: recherche([9,20,3,40,37,42,69,65,21,66,1,74,50], 69) 7 sage: recherche([9,20,3,40,37,42,69,65,21,66,1,74,50], 5)
Note: on remarquera que, comme ci-dessus, l’objet
None
n’est pas affiché par Python:sage: None
On peut vérifier que c’est bien
None
qui est renvoyé avec:sage: recherche([9,20,3,40,37,42,69,65,21,66,1,74,50], 5) == None True
ou, plus rapide:
sage: recherche([9,20,3,40,37,42,69,65,21,66,1,74,50], 5) is None True
Indication: utiliser les tests suivants:
sage: recherche([],1) sage: recherche([2],1) sage: recherche([2],2) 1 sage: recherche([9,20,3,40,37,42,69,65,21,66,1,74,50], 21) 9 sage: recherche([9,20,3,40,37,42,69,65,21,66,1,74,50], 69) 7 sage: recherche([9,20,3,40,37,42,69,65,21,66,1,74,50], 5) sage: recherche([1,3,9,20,21,37,40,42,50,65,66,69,74], 21) 5 sage: recherche([1,3,9,20,21,37,40,42,50,65,66,69,74], 69) 12 sage: recherche([1,3,9,20,21,37,40,42,50,65,66,69,74], 5)
Télécharger le fichier annexe recherche.py et l’enregistrer dans un dossier de votre choix, comme par exemple
~/Agregation/OptionC/TP2/tris.py
.L’ouvrir avec l’éditeur de texte de votre choix (par exemple
gedit
, ou l’éditeur intégré dans Jupyter.Compléter / modifier le squelette qui y est fourni afin de mettre en pratique les deux premiers objectifs du TP:
documenter votre fonction recherche,
incorporer les tests effectués «à la main» dans la question précédente,
écrire en commentaire les pré et post conditions ainsi que l’invariant de boucle pour votre fonction recherche.
chaque fois que possible, traduire ces commentaires sous forme exécutable par la machine, en utilisant la commande:
assert <condition>
Charger le fichier
recherche.py
dans une feuille de travail Jupyter à l’aide de la commande:sage: %run recherche.py
Attention, cela présuppose que
SageMath
a été lancé dans le même répertoire:cd ~/Agregation/OptionC/TP2/ sage -notebook jupyter
ou au moins que la feuille de travail soit dans ce même répertoire.
Vérifier que vous pouvez maintenant utiliser les fonctions présentes dans recherche.py.
Tester votre fonction de recherche: dans un terminal, aller dans le dossier, et lancer les tests du fichier tris.py avec:
cd ~/Agregation/OptionC/TP2/ sage -t recherche.py
Expérimenter avec cette fonctionalité; notamment ajouter des tests faux dans la documentation de votre fonction.
Reprendre toutes les étapes précédentes avec la recherche dichotomique, en supposant que la liste en argument est triée. Prenez le temps de bien écrire votre invariant de boucle, cela va s’avérer crucial. Utilisez deux bornes
inf
etsup
, vérifiant à chaque étape l’invariantinf <= i < sup
, oùi
est la première position de la valeur dans la liste, si elle y est présente.
Utiliser la fonctionalité de
Python
pour mesurer le temps d’exécution de vos fonctions recherche sur diverses entrées:sage: %time recherche([1,2,3],5);
Lancer cette commande plusieurs fois; que constatez vous?
Pour obtenir un temps moyenné automatiquement sur plusieurs exécutions, vous pouvez aussi utiliser:
sage: %timeit recherche([1,2,3],5);
Ces deux commandes ont l’inconvénient d’afficher leur résultat plutôt que de le renvoyer, ce qui ne permet pas de récupérer les valeurs automatiquement pour, par exemple, en faire un graphique.
Pour automatiser le processus, il faut donc en revenir à la fonction \(time.time\) qui renvoie l’heure actuelle (en secondes depuis le premier janvier 1970):
sage: import time sage: avant = time.time(); recherche([1,2,3], 5); apres = time.time() sage: apres-avant # random
Dans l’exercice 5 on verra une bibliothèque plus avancée pour automatiser le processus.
Seconde méthode de mesure: instrumenter vos fonctions de recherche en insérant un compteur pour le nombre de comparaisons effectuées lors d’un appel.
Indication: essayer l’exemple suivant:
sage: def f(): ....: global compteur ....: compteur = 0 ....: for i in range(10): ....: compteur += 1 ....: return 42 sage: f() 42 sage: compteur 10
Votre programme ainsi modifié contient une variable globale et doit donc être chargé avec:
sage: %run -i recherche.py
(voir la documentation de \(%run\) pour les détails).
Complexité pratique: faire quelques statistiques sur le nombre de comparaisons en moyenne et au pire utilisées par vos fonctions de recherche, en fonction de la taille de la liste; représenter graphiquement les résultats. Comparer l’efficacité des deux méthodes de recherche en les présentant dans un même graphique.
Indications:
Voir
randint()
pour créer une liste aléatoire.Définir une fonction
complexite_recherche(n)
qui lancerecherche
sur un échantillon de listes de longueur \(n\), et renvoie le nombre de comparaisons en moyenne et au pire.Voir
point()
pour afficher un nuage de points. Que fait l’exemple suivant?sage: point( [ [i, i^2] for i in range(10) ] )
Pour trier une liste:
sage: sorted(['c', 'b', 'a']) ['a', 'b', 'c']
Évaluer la taille maximale d’une liste dans laquelle on peut faire une recherche en moins d’une heure et d’une semaine.
Le but de cet exercice est de mettre en pratique les compétences acquises dans les exercices précédents, dans un cadre un peu plus élaboré.
Pour chaque algorithme de tri, bien prendre le temps de spécifier les invariants, tracer des courbes statistiques de complexité au pire et en moyenne. Comparer avec les courbes théoriques et comparer l’efficacité relative des différents algorithmes.
Vous pouvez partir du fichier annexe tris.py.
Ce premier tri est décrit par son invariant de boucle, à vous de trouver l’algorithme! Cela devrait vous convaincre qu’une fois le bon invariant écrit, la programmation en découle assez simplement.
L’invariant est: «à l’étape \(k\), les \(k\) premiers éléments de la liste sont les \(k\) plus petits éléments de la liste originale, et sont triés».
Le tri à bulle porte ce nom en référence à l’intuition derrière l’algorithme: les éléments légers (plus petits) remontent tels des bulles dans un liquide plus lourd. On peut aussi le voir dans l’autre sens: les éléments les plus lourds (plus grands) coulent au fond de la liste.
Plus formellement, on parcourt la liste, et dès que l’on trouve une paire successive mal ordonnée, on la réarrange, et on repart du début de la liste.
Ce nouveau tri, ainsi que le suivant utilisent le principe de diviser pour régner. Ce paradigme de programmation consiste en 3 étapes:
- Diviser le problème en sous-problèmes plus simples à résoudre;
- Résoudre les sous-problèmes;
- Reconstruire la solution au problème de départ à partir des solutions aux sous-problèmes.
Dans le cas du tri, l’étape 1 consiste à couper la liste en plusieurs morceaux, l’étape 2 consiste à trier chaque morceau, et pour la dernière étape on recolle les morceaux de liste comme il faut pour que le tout reste trié. Cette dernière étape dépend évidement de la façon dont on a coupé la liste à l’étape 1.
Pour le tri fusion, l’étape \(1\) est brutale: on coupe la liste à la moitié. En supposant les deux sous-listes triées, comment les fusionner pour maintenir le tri ? Cette étape de fusion doit être réalisée en \(|L_1|+|L_2|\) opérations, où \(L_1\) et \(L_2\) sont les listes triées à fusionner.
Indication: utiliser une fonction récursive; si nécessaire, s’entraîner en implantant au préalable une fonction récursive pour calculer \(n!\)
Ici c’est l’inverse, on souhaite que l’étape 3 soit la plus simple possible: on veut qu’il suffise de concaténer les listes. Pour cela, on choisit un élément dit «pivot» dans la liste de départ, et nos deux sous-listes sont obtenues respectivement à partir des éléments strictement plus petits et plus grands que le pivot.
Pour les plus rapides, vous pouvez implanter les tris suivant:
- tri insertion en place,
- tri par tas. Indication: utiliser le module heapq de Python,
- tri par insertion dans un Arbre Binaires de Recherche. Indications:
- consulter la documentation de
LabelledBinaryTree
pour trouver comment construire des arbres binaires étiquetés. - Définir une fonction récursive
insere(arbre, i)
qui insère un nombrei
dans un arbre binaire de recherche.
- consulter la documentation de
Estimer la complexité de la fonction suivante:
sage: def fusion(l1, l2):
....: return sorted(l1+l2)
lorsque elle est appliquée à des listes aléatoires, respectivement triées.
Que peut-on en déduire?
Pour en savoir plus, voir l’article sur Tim sort
Des collègues sont en train d’implanter une bibliothèque pour faire très facilement des bancs d’essais, en particulier pour l’enseignement. C’est encore expérimental, mais ils sont preneurs de retour. En l’état, il n’est pas clair s’il sera possible d’avoir cette bibliothèque le jour du concours.
Si vous êtes partant pour essayer cette bibliothèque, télécharger le fichier bleachermark.py et le mettre dans le même répertoire que votre feuille de travail.
Voici un exemple d’utilisation dans lequel on fait un banc d’essai
pour la fonction sorted
de Python pour différentes tailles de
listes. On commence par écrire un générateur de listes aléatoires de
taille donnée:
sage: from random import randint
sage: def random_list(n):
....: return [randint(0, n) for i in range(n)]
On construit le banc d’essai:
sage: from bleachermark import *
sage: BB = SimpleBleachermark(random_list, sorted, sizes=[2^k for k in range(10)])
On le lance:
sage: BB.run()
On peut l’interrompre à tout moment et le relancer ultérieurement.
Ensuite on peut accéder à la moyenne du temps de calcul pour sorted
pour chaque taille:
sage: BB.averages() # random
{1: 4.870000000005703e-06,
2: 5.19999999995413e-06,
4: 6.820000000002935e-06,
8: 7.3599999999807154e-06,
16: 1.0719999999997399e-05,
32: 1.774000000003717e-05,
64: 3.4700000000000843e-05,
128: 7.322999999999524e-05,
256: 0.00015710000000003,
512: 0.00034635999999997223}
Voici comment en faire un graphique:
sage: points( BB.averages().items() ) # not tested
De même, on peut accéder au min, max, ainsi qu’à l’intégralité des temps de calculs avec:
sage: BB.mins() # not tested
sage: BB.maxes() # not tested
sage: BB.timings() # not tested
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Sage + GAP4 + GAP3 + Chevie + Semigroupe (experimental)¶
Requirements
- some experimental code that has not yet been migrated from the Sage-Combinat queue to the sage-semigroups package.
- gap3
- the Semigroupe package
Todo
update to use libsemigroups
Let us create the Coxeter group W:
sage: W = CoxeterGroup(["H",4]); W
It is constructed as a group of permutations, from root data given by GAP3+Chevie (thanks to Franco’s interface):
sage: W._gap_group
CoxeterGroup("H",4)
sage: (W._gap_group).parent()
Gap3
with operations on permutations implemented in Sage:
sage: W.an_element()^3
(1,5)(2,62)(3,7)(6,9)(8,12)(11,15)(13,17)(16,20)(18,22)(21,25)(26,29)(28,31)(30,33)(32,35)(34,37)(36,39)(38,41)(42,45)(46,48)(47,49)(50,52)(55,56)(57,58)(61,65)(63,67)(66,69)(68,72)(71,75)(73,77)(76,80)(78,82)(81,85)(86,89)(88,91)(90,93)(92,95)(94,97)(96,99)(98,101)(102,105)(106,108)(107,109)(110,112)(115,116)(117,118)
and group operations implemented in GAP 4:
sage: len(W.conjugacy_classes_representatives())
34
sage: W.cardinality()
14400
Now, assume we want to do intensive computations on this group, requiring heavy access to the left and right Cayley graphs (e.g. Bruhat interval calculations, representation theory, …). Then we can use Jean-Eric Pin’s Semigroupe, a software written in C:
sage: S = semigroupe.AutomaticSemigroup(W.semigroup_generators(), W.one(),
....: category = CoxeterGroups().Finite())
The following triggers the full expansion of the group and its Cayley graph in memory:
sage: S.cardinality()
14400
And we can now iterate through the elements, in length-lexicographic order w.r.t. their reduced word:
sage: sum( x^p.length() for p in S)
x^60 + 4*x^59 + 9*x^58 + 16*x^57 + 25*x^56 + 36*x^55 + 49*x^54 + 64*x^53 + 81*x^52 + 100*x^51 + 121*x^50 + 144*x^49 + 168*x^48 + 192*x^47 + 216*x^46 + 240*x^45 + 264*x^44 + 288*x^43 + 312*x^42 + 336*x^41 + 359*x^40 + 380*x^39 + 399*x^38 + 416*x^37 + 431*x^36 + 444*x^35 + 455*x^34 + 464*x^33 + 471*x^32 + 476*x^31 + 478*x^30 + 476*x^29 + 471*x^28 + 464*x^27 + 455*x^26 + 444*x^25 + 431*x^24 + 416*x^23 + 399*x^22 + 380*x^21 + 359*x^20 + 336*x^19 + 312*x^18 + 288*x^17 + 264*x^16 + 240*x^15 + 216*x^14 + 192*x^13 + 168*x^12 + 144*x^11 + 121*x^10 + 100*x^9 + 81*x^8 + 64*x^7 + 49*x^6 + 36*x^5 + 25*x^4 + 16*x^3 + 9*x^2 + 4*x + 1
sage: S[0:10]
[[], [0], [1], [2], [3], [0, 1], [0, 2], [0, 3], [1, 0], [1, 2]]
sage: S[-1]
[0, 1, 0, 1, 0, 2, 0, 1, 0, 1, 2, 0, 1, 0, 2, 3, 2, 0, 1, 0, 1, 2, 0, 1, 0, 2, 3, 2, 0, 1, 0, 1, 2, 0, 1, 0, 2, 3, 2, 0, 1, 0, 1, 2, 0, 1, 0, 2, 3, 2, 0, 1, 0, 1, 2, 0, 1, 0, 2, 3]
The elements of S are handles to C objects from Semigroupe
:
sage: x = S.an_element()
sage: x
[0, 1, 2, 3]
Products are calculated by Semigroupe
:
sage: x * x
[0, 1, 0, 2, 0, 1, 3, 2]
Powering operations are handled by Sage:
sage: x^3
[0, 1, 0, 2, 0, 1, 0, 2, 3, 2, 0, 1]
sage: x^(10^10000)
Altogether, S is a full fledged Sage Coxeter group, which passes all the generic tests:
sage: TestSuite(S).run(verbose = True, skip = "_test_associativity")
And of course it works for general semigroups too, and can further compute much more information about those, like the (Knuth-Bendix completion of the) relations between the generators:
sage: S.print_relations()
aa = 1
bb = 1
cb = bc
cc = 1
da = ad
db = bd
dd = 1
cac = aca
dcd = cdc
...
dcbabacbabcdcbabacbabcdcbabacbabcdcbabacbabcdc = cdcbabacbabcdcbabacbabcdcbabacbabcdcbabacbabcd
which contains the usual commutation + braid relations.
Let’s try now the 0-Hecke monoid:
sage: from sage.combinat.j_trivial_monoids import *
sage: S = semigroupe.AutomaticSemigroup(W.simple_projections(), W.one(), by_action = True)
sage: S.cardinality()
14400
sage: S.print_relations()
aa = a
bb = b
ca = ac
cc = c
da = ad
db = bd
dd = d
cbc = bcb
dcd = cdc
...
ababacbabacbabcdcbabacbabcdcbabacbabcdcbabacbabcdcbabacbabcd = 0
Let us throw in more mathematical information:
sage: W = CoxeterGroup(["A",3])
sage: S = semigroupe.AutomaticSemigroup(W.simple_projections(), W.one(), by_action = True,
....: category = JTrivialMonoids().Finite())
sage: S.cardinality()
sage: H = S.algebra(QQ)
sage: H.orthogonal_idempotents()
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Basics¶
Arithmetic:
sage: 1 + 1
sage: 1 + 3
sage: ( 1 + 2 * (3 + 5)^2 ) * 2
258
sage: 20/14
10/7
sage: 2^1000
107...376
sage: numerical_approx(20/14)
1.42857142857143
sage: 20.0/14
sage: numerical_approx(pi, 10000)
3.1415926535897932384626...
Editing the worksheet!
Polynomials:
sage: factor(x^100 - 1)
(x - 1)*(x + 1)*(x^2 + 1)*(x^4 - x^3 + x^2 - x + 1)*(x^4 + x^3 + x^2 + x + 1)*(x^8 - x^6 + x^4 - x^2 + 1)*(x^20 - x^15 + x^10 - x^5 + 1)*(x^20 + x^15 + x^10 + x^5 + 1)*(x^40 - x^30 + x^20 - x^10 + 1)
sage: %display latex
sage: factor(x^100 - 1)
(x - 1)*(x + 1)*(x^2 + 1)*(x^4 - x^3 + x^2 - x + 1)*(x^4 + x^3 + x^2 + x + 1)*(x^8 - x^6 + x^4 - x^2 + 1)*(x^20 - x^15 + x^10 - x^5 + 1)*(x^20 + x^15 + x^10 + x^5 + 1)*(x^40 - x^30 + x^20 - x^10 + 1)
Symbolic calculations:
sage: var('x,y')
sage: f = sin(x) - cos(x*y) + 1 / (x^3+1)
sage: f
sage: f.integrate(x)
sage: expr = sin(x) + sin(2 * x) + sin(3 * x)
sage: solve(expr, x)
[sin(3*x) == -sin(2*x) - sin(x)]
sage: find_root(expr, 0.1, pi)
2.0943951023931957
Todo
arbitrary precision numerical approximation of the solution
sage: f = expr.simplify_trig(); f
2*(2*cos(x)^2 + cos(x))*sin(x)
sage: solve(f, x)
[x == 0, x == 2/3*pi, x == 1/2*pi]
Statistics:
sage: print r.summary(r.c(1,2,3,111,2,3,2,3,2,5,4))
Todo
other examples from MuPAD-Combinat/lib/DOC/demo/mupad.tex
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Algebraic constructions and categories¶
sage: Px.<x> = QQ[]
sage: Fx = Px.fraction_field()
sage: for category in Fx.categories(): print category
sage: g = sage.categories.category.category_graph()
sage: g.set_latex_options(format = "dot2tex")
sage: view(g, tightpage = True, viewer = "pdf")
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Cython: Python -> C¶
Here is a function that computes \(\sum_{k=0}^N k\) in pure Python:
sage: def mysum(N):
....: s = int(0)
....: for k in range(1,N):
....: s += k
....: return s
sage: %time mysum(10^7)
Let us compare this with the Cython version:
sage: %%cython
sage: def mysum_cython(N):
....: cdef long long s = 0
....: cdef int k
....: for k in range(1,N):
....: s += k
....: return s
sage: %time mysum_cython(10^7)
A function to count the number of integer partitions with parts in a given set:
sage: def buying(coins, total):
....: vlist = [ [0] * len(coins) for _ in range(total + 1) ]
....: for i in range(total + 1):
....: for j, coin in enumerate(coins):
....: if j == 0:
....: if i % coin == 0:
....: vlist[i][j] = 1
....: else:
....: k = 0
....: while i - k >= 0:
....: vlist[i][j] += vlist[i - k][j - 1]
....: k += coin
....: return vlist[total][len(coins) - 1]
sage: [buying([1,2,5,10], i) for i in [1..20]]
[1, 2, 2, 3, 4, 5, 6, 7, 8, 11, 12, 15, 16, 19, 22, 25, 28, 31, 34, 40]
sage: [1,3..10]
[1, 3, 5, 7, 9]
Let us see how long it takes to find the number of partitions of 500 into odd parts:
sage: %time buying([1,3..500], 500)
732986521245024
Time: CPU 3.05 s, Wall: 3.05 s
Make two changes:
sage: %%cython
sage: def cybuying(coins, total):
....: cdef int i, j, k, coin
....: vlist = [ [0] * len(coins) for _ in range(total + 1) ]
....: for i in range(total + 1):
....: for j, coin in enumerate(coins):
....: if j == 0:
....: if i % coin == 0:
....: vlist[i][j] = 1
....: else:
....: k = 0
....: while i - k >= 0:
....: vlist[i][j] += vlist[i - k][j - 1]
....: k += coin
....: return vlist[total][len(coins) - 1]
Surely two tiny changes in some Python code cannot make it much faster:
sage: %time cybuying([1,3..500], 500)
732986521245024L
Time: CPU 0.08 s, Wall: 0.09 s
sage: 3.05/0.08
38.1250000000000
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Documentation¶
Type plot(
and then press the TAB
key:
sage: plot(
Click on Help at the top of this page
There is also Live Documentation in which you can evaluate the examples.
Introspection:
sage: m = matrix([[1/2,1],[2,1]]); m
sage: m.
sage: g = Graph(); g
sage: g.
Looking at the sources:
sage: m.det
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demontration: Computing with ideals using Singular (early draft)¶
Status: this sheet is the script of a brief interactive demo during the Material and activities linked to the CRM-2017 school and workshop on “Equivariant Combinatorics”.
Let us define an ideal:
sage: P = QQ['a,b,c,d,e']
sage: P.inject_variables()
Defining a, b, c, d, e
sage: p1 = 3*c^2 - 4*b*d + a*e
sage: p2 = -2*b*c*d + 3*a*d^2 + 3*b^2*e - 4*a*c*e
sage: p3 = 8*b^2*d^2 - 9*a*c*d^2 - 9*b^2*c*e + 9*a*c^2*e + 2*a*b*d*e - a^2*e^2
sage: I = Ideal([p1, p2, p3])
sage: a in I
False
sage: (p1*a - b * p2) in I
True
sage: I.dimension()
3
The calculations are actually carried out by Singular. Many more advanced features are not directly exposed in Sage, in which case one need to call singular directly. Here we follow the instructions from Singular’s manual to compute a free resolution of this ideal:
sage: res = I._singular_().mres(0); res
[1]:
_[1]=3*c^2-4*b*d+a*e
_[2]=2*b*c*d-3*a*d^2-3*b^2*e+4*a*c*e
[2]:
_[1]=2*b*c*d*gen(1)-3*a*d^2*gen(1)-3*b^2*e*gen(1)+4*a*c*e*gen(1)-3*c^2*gen(2)+4*b*d*gen(2)-a*e*gen(2)
[3]:
_[1]=0
[4]:
_[1]=gen(1)
[5]:
_[1]=0
And its Betti numbers:
sage: res.betti()
1 0 0
0 1 0
0 1 0
0 0 1
TODO: explore how to get the nice pretty printing provided by Singular:
sage: res.betti().print("betti") # todo: not implemented
File "<ipython-input-19-cd5e12c00f
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Interfaces¶
sage: %gap
sage: ConjugacyClasses(TransitiveGroup(12,20))
sage: x = gap(1)
sage: y = gap(2)
sage: type(x)
sage: type(y)
sage: type(x+y)
sage: z = maxima(3) + gap(2) - 5
sage: z
sage: type(z)
sage: Sage(z)
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Modeling mathematics¶
Dans la tradition d’Axiom, MuPAD ou Magma, voire poussant le bouchon, Sage privilégie chaque fois que possible la modélisation explicite de la structure algébrique dans laquelle on souhaite mener le calcul.
Par exemple, Sage connaît les structures algébriques usuelles:
sage: NN
Non negative integer semiring
sage: ZZ
Integer Ring
sage: QQ['x']
Univariate Polynomial Ring in x over Rational Field
Et leurs propriétés:
sage: ZZ.category()
Category of euclidean domains
sage: sorted( ZZ.category().axioms() )
['AdditiveAssociative', 'AdditiveCommutative', 'AdditiveInverse', 'AdditiveUnital',
'Associative', 'Commutative',
'Distributive', 'NoZeroDivisors', 'Unital']
sage: G = ZZ.category().category_graph()
sage: G.set_latex_options(format="dot2tex")
sage: view(G, viewer="pdf", tightpage=True) # not tested
Cette modélisation permet tout d’abord de travailler naturellement et efficacement dans des constructions algébriques avancées:
sage: Z2 = GF(2); Z2
Finite Field of size 2
sage: P = Z2['x']; P
Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL)
sage: M = MatrixSpace(P, 3); M
Full MatrixSpace of 3 by 3 dense matrices over Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL)
sage: m = M.random_element(); m # random
[ x + 1 x^2 x^2]
[ x x^2 + x x + 1]
[ x^2 + 1 x^2 + x + 1 x^2]
sage: m.parent()
Full MatrixSpace of 3 by 3 dense matrices over Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL)
sage: m * (m-1) # random
[ x^4 + x^3 + x x^3 + x^2 x^4 + x^2]
[ x^2 + x + 1 x^4 + x + 1 x^3 + x^2 + 1]
[ x^4 x^4 + x^3 + x^2 + 1 x^3 + 1]
sage: m.det() # random
x^6 + x^5 + x^3 + x^2 + x + 1
sage: m.det().parent()
Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL)
sage: Z2.coerce_map_from(ZZ)
Natural morphism:
From: Integer Ring
To: Finite Field of size 2
sage: P.coerce_map_from(Z2)
Polynomial base injection morphism:
From: Finite Field of size 2
To: Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL)
sage: M.coerce_map_from(P)
Call morphism:
From: Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL)
To: Full MatrixSpace of 3 by 3 dense matrices over Univariate Polynomial Ring in x over Finite Field of size 2 (using NTL)
sage: M.coerce_map_from(QQ)
Example: factorisation dans les anneaux de polynômes¶
Cette modélisation permet aussi de traiter rigoureusement, par exemple, les questions de factorisation:
sage: p = 54*x^4+36*x^3-102*x^2-72*x-12
sage: p.factor()
6*(x^2 - 2)*(3*x + 1)^2
sage: for K in [ZZ, QQ, ComplexField(16), QQ[sqrt(2)], GF(5)]:
....: print K, ":"; print K['x'](p).factor()
Integer Ring :
2 * 3 * (3*x + 1)^2 * (x^2 - 2)
Rational Field :
(54) * (x + 1/3)^2 * (x^2 - 2)
Complex Field with 16 bits of precision :
(54.00) * (x - 1.414) * (x + 0.3333)^2 * (x + 1.414)
Number Field in sqrt2 with defining polynomial x^2 - 2 :
(54) * (x - sqrt2) * (x + sqrt2) * (x + 1/3)^2
Finite Field of size 5 :
(4) * (x + 2)^2 * (x^2 + 3)
Exemples en Algèbre linéaire¶
Dans les exemples ci-dessous, nous ferons de l’algèbre linéaire sur le corps fini \(\ZZ/7\ZZ\):
sage: K = GF(7); K
Finite Field of size 7
sage: list(K)
[0, 1, 2, 3, 4, 5, 6]
Nous avons choisi ce corps à titre d’illustration pour avoir des résultats lisibles. On aurait pu prendre des coefficients entiers, rationnels, ou numériques à plus ou moins haute précision. Les aspects numériques seront abordés plus en détail dans l’exposé suivant. Notons au passage que, même en calcul exact, il est possible de manipuler de relativement grosses matrices:
sage: M = random_matrix(K, 10000, sparse=True, density=3/10000)
sage: M.rank() # random
9278
sage: M.visualize_structure('/tmp/structure.png') # not tested
sage: os.system(sage.misc.viewer.png_viewer()+' '+'/tmp/structure.png') # not tested
Définissons donc une matrice à coefficients dans \(\ZZ/7\ZZ\):
sage: A = matrix(K, 4, [5,5,4,3,0,3,3,4,0,1,5,4,6,0,6,3]); A
[5 5 4 3]
[0 3 3 4]
[0 1 5 4]
[6 0 6 3]
Calculons le polynôme caractéristique de cette matrice:
sage: P = A.characteristic_polynomial(); P
x^4 + 5*x^3 + 6*x + 2
On vérifie le théorème de Cayley-Hamilton sur cet exemple:
sage: P(A)
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
Notons que l’information sur le corps de base est préservée:
sage: P.parent()
Univariate Polynomial Ring in x over Finite Field of size 7
ce qui influe directement sur la factorisation de ce polynôme:
sage: factor(P)
(x + 3) * (x + 6) * (x + 5)^2
sage: factor(x^4 + 5*x^3 + 6*x + 2)
x^4 + 5*x^3 + 6*x + 2
Le calcul ci-dessus nous donne les valeurs propres: -3=4,-6=1 et -5=2. Quels sont les espaces propres?
sage: A.eigenspaces_left()
[
(4, Vector space of degree 4 and dimension 1 over Finite Field of size 7
User basis matrix:
[1 4 6 1]),
(1, Vector space of degree 4 and dimension 1 over Finite Field of size 7
User basis matrix:
[1 3 3 4]),
(2, Vector space of degree 4 and dimension 2 over Finite Field of size 7
User basis matrix:
[1 0 2 3]
[0 1 6 0])
]
Récupérons ces espaces propres:
sage: E = dict(A.eigenspaces_left())
sage: E[2]
Vector space of degree 4 and dimension 2 over Finite Field of size 7
User basis matrix:
[1 0 2 3]
[0 1 6 0]
E[2]
n’est pas une liste de vecteurs ni une matrice, mais un
objet qui modélise l’espace propre \(E_2\), comme le sous-espace de
\((\ZZ/7\ZZ)^4\) décrit par sa base échelon réduite. On peut donc lui
poser des questions:
sage: E[2].dimension()
2
sage: E[2].basis()
[
(1, 0, 2, 3),
(0, 1, 6, 0)
]
sage: V = E[2].ambient_vector_space(); V
Vector space of dimension 4 over Finite Field of size 7
Voire faire des calculs avec:
sage: E[2] + E[4]
Vector space of degree 4 and dimension 3 over Finite Field of size 7
Basis matrix:
[1 0 0 0]
[0 1 0 5]
[0 0 1 5]
sage: v = V([1,2,0,3])
sage: v in E[2]
True
sage: E[2].echelon_coordinates(v)
[1, 2]
sage: E[2].is_subspace(E[4])
False
sage: E[2].is_subspace(V)
True
sage: Q = V/E[2]; Q
Vector space quotient V/W of dimension 2 over Finite Field of size 7 where
V: Vector space of dimension 4 over Finite Field of size 7
W: Vector space of degree 4 and dimension 2 over Finite Field of size 7
User basis matrix:
[1 0 2 3]
[0 1 6 0]
sage: Q( V([0,0,0,1]) )
(2, 4)
On veut maintenant manipuler \(A\) comme un morphisme sur \(V\):
sage: phi = End(V)(A); phi
Vector space morphism represented by the matrix:
[5 5 4 3]
[0 3 3 4]
[0 1 5 4]
[6 0 6 3]
Domain: Vector space of dimension 4 over Finite Field of size 7
Codomain: Vector space of dimension 4 over Finite Field of size 7
sage: v = V.an_element()
sage: v
(1, 0, 0, 0)
sage: phi(v)
(5, 5, 4, 3)
sage: (phi^-1)(v)
(1, 2, 3, 4)
sage: P(phi) # todo: not implemented
sage: phi^4 + 5*phi^3 + 6*phi + 2
Vector space morphism represented by the matrix:
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
Domain: Vector space of dimension 4 over Finite Field of size 7
Codomain: Vector space of dimension 4 over Finite Field of size 7
sage: (phi - 1).image()
Vector space of degree 4 and dimension 3 over Finite Field of size 7
Basis matrix:
[1 0 0 0]
[0 1 0 5]
[0 0 1 5]
sage: (phi - 1).kernel() == E[1]
True
sage: phi.restrict(E[2])
Vector space morphism represented by the matrix:
[2 0]
[0 2]
Domain: Vector space of degree 4 and dimension 2 over Finite Field of size 7
User basis matrix:
[1 0 2 3]
[0 1 6 0]
Codomain: Vector space of degree 4 and dimension 2 over Finite Field of size 7
User basis matrix:
[1 0 2 3]
[0 1 6 0]
En résumé¶
- « Mathematics is the art of reducing any problem to linear algebra » William Stein
- Il serait en principe suffisant d’implanter l’algèbre linéaire sur les matrices
- Le pari de Sage: modéliser au plus près les mathématiques, pour que l’utilisateur ou le programmeur puisse s’exprimer dans le langage adapté au problème considéré.
Exemples en combinatoire¶
Selon le même principe, lorsque l’on demande toutes les partitions de l’entier 5, le résultat est un objet qui modélise cet ensemble:
sage: P = Partitions(5); P
Partitions of the integer 5
Pour obtenir la liste de ces objets, il faut le demander explicitement:
sage: P.list()
[[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]]
Cela permet de manipuler formellement des grands ensembles:
sage: Partitions(100000).cardinality()
27493510569775696512677516320986352688173429315980054758203125984302147328114964173055050741660736621590157844774296248940493063070200461792764493033510116079342457190155718943509725312466108452006369558934464248716828789832182345009262853831404597021307130674510624419227311238999702284408609370935531629697851569569892196108480158600569421098519
Et de calculer paresseusement avec. Ici, on tire au hasard une main de cinq cartes à jouer:
sage: Symboles = Set(["Coeur", "Carreau", "Pique", "Trefle"])
sage: Valeurs = Set([2, 3, 4, 5, 6, 7, 8, 9, 10, "Valet", "Dame", "Roi", "As"])
sage: Cartes = CartesianProduct(Valeurs, Symboles).map(tuple)
sage: Mains = Subsets(Cartes, 5)
sage: Mains.cardinality()
2598960
sage: Mains.random_element() # random
{(2, 'Coeur'), (6, 'Pique'), (10, 'Carreau'), ('As', 'Pique'), ('Valet', 'Coeur')}
et là on manipule un mot infini défini comme point fixe d’un morphisme:
sage: m = WordMorphism('a->acabb,b->bcacacbb,c->baba')
sage: m.fixed_point('a')
word: acabbbabaacabbbcacacbbbcacacbbbcacacbbac...
Further reading¶
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Calculations with character rings of the biHecke monoid (experimental)¶
Warning
This demonstration requires experimental code that has not yet been migrated from the Sage-Combinat queue to the sage-semigroups package.
sage: %hide
sage: pretty_print_default()
sage: attach /home/nthiery/work/frg/Articles/Hivert_Schilling_Thiery_HeckeMonoid/main.sage
sage: M = BiHeckeMonoid(["A",3])
sage: G = M.character_ring()
sage: E = G.E(); T = G.T(); P = G.P()
sage: M0 = M.fix_w0_monoid()
sage: G0 = M0.character_ring()
sage: S0 = G0.S(); P0 = G0.P()
sage: for e in P0.basis():
....: print "Ind(",e, ")=",P(G.induce_from_M0(S0(e))) # indirect doctest
Ind( P[1234] )= P[1234]
Ind( P[2134] )= P[2134] + P[2314] + P[2341] + P[2413] + P[4213]
Ind( P[2314] )= P[2314] + P[2341]
Ind( P[3214] )= P[3214] + P[3241] + P[3421]
Ind( P[2341] )= P[2341]
Ind( P[3241] )= P[3241] + P[3421]
Ind( P[3421] )= P[3421]
Ind( P[4321] )= P[4321]
Ind( P[2431] )= P[2431] + P[4231]
Ind( P[4231] )= P[4231]
Ind( P[1324] )= P[1324] + P[1342] + P[3124] + P[3142] + P[3241] + P[3412] + P[4132]
Ind( P[3124] )= P[3124] + P[3142] + P[3241] + P[3412]
Ind( P[1342] )= P[1342] + P[3142] + P[3412] + P[4132]
Ind( P[3142] )= P[3142] + P[3412]
Ind( P[3412] )= P[3412]
Ind( P[4312] )= P[4312]
Ind( P[1432] )= P[1432] + P[4132] + P[4312]
Ind( P[4132] )= P[4132] + P[4312]
Ind( P[1243] )= P[1243] + P[1423] + P[2413] + P[2431] + P[4123]
Ind( P[2143] )= P[2143] + P[2413] + P[2431] + P[4213] + P[4231]
Ind( P[2413] )= P[2413] + P[2431] + P[4213] + P[4231]
Ind( P[4213] )= P[4213] + P[4231]
Ind( P[1423] )= P[1423] + P[4123]
Ind( P[4123] )= P[4123]
Behind the scene, it uses the cutting poset (to convert between translation modules to simple modules), the character formula for projective modules of J-Trivial monoids, the property that simple modules for M0 are induced to translation modules for M, etc. Plus inversion by matrix of linear morphism between finite dimensional vector spaces. It also uses the expansion of the character of a projective module for the BiHecke monoid in term of simple module, but this one is hard-coded for type A3 (currently too expensive to recalculate it). The framework supports q-characters; but few of the rules above are implemented for them, since we do not know them yet
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Computational representation theory for finite monoids (experimental)¶
Requirements
This demonstration requires experimental code that has not yet been migrated from the Sage-Combinat queue to the sage-semigroups package.
Running example¶
Let us consider an \(H\)-trivial monoid:
sage: O3 = HTrivialMonoids().Finite().example(action = "right"); O3
The finite H-trivial monoid of order preserving maps on {1, .., 3}
sage: O3.rename("O3")
Its generators:
sage: pi = O3.monoid_generators(); pi
Finite family {1: 113, 2: 122, -2: 133, -1: 223}
Its right Cayley graph:
sage: view(O3.cayley_graph(side="right"))
Its right classes:
sage: O3.r_classes()
[{123}, {233, 133, 122}, {113, 112, 223}, {111, 222, 333}]
Representation theory¶
The Cartan matrix of \(O_3\):
sage: O3.cartan_matrix()
[1 0 0]
[1 1 0]
[0 1 1]
A larger example:
sage: O = HTrivialMonoids().Finite().example(5); O
The finite H-trivial monoid of order preserving maps on {1, .., 5}
sage: O.cardinality()
126
sage: O.cartan_matrix()
[1 1 0 0 0]
[0 1 1 0 0]
[0 0 1 1 0]
[0 0 0 1 1]
[0 0 0 0 1]
sage: O = HTrivialMonoids().Finite().example(6)
sage: O.cardinality()
462
sage: O.cartan_matrix()
[1 1 0 0 0 0]
[0 1 1 0 0 0]
[0 0 1 1 0 0]
[0 0 0 1 1 0]
[0 0 0 0 1 1]
[0 0 0 0 0 1]
sage: O = HTrivialMonoids().Finite().example(7)
sage: O.cardinality()
1716
sage: O.cartan_matrix()
[1 1 0 0 0 0 0]
[0 1 1 0 0 0 0]
[0 0 1 1 0 0 0]
[0 0 0 1 1 0 0]
[0 0 0 0 1 1 0]
[0 0 0 0 0 1 1]
[0 0 0 0 0 0 1]
Constructing modules¶
Its regular right class modules:
sage: right_class_modules = [O3.lr_regular_class_module(i, side="right") for i in range(3)]; right_class_modules
[Free module generated by {123} endowed with an action of O3 over Rational Field,
Free module generated by {113, 112, 223} endowed with an action of O3 over Rational Field,
Free module generated by {111, 222, 333} endowed with an action of O3 over Rational Field]
Its simple modules:
sage: simple_modules = [O3.simple_module(i) for i in range(3)]; simple_modules
[A quotient of Free module generated by {123} endowed with an action of O3 over Rational Field,
A quotient of Free module generated by {113, 112, 223} endowed with an action of O3 over Rational Field,
A quotient of Free module generated by {111, 222, 333} endowed with an action of O3 over Rational Field]
sage: simple_modules[0].dimension()
1
sage: simple_modules[1].dimension()
2
sage: simple_modules[2].dimension()
1
Characters¶
As for groups, characters provide a convenient computational tool to recover the composition factors of a module \(V\) (although those composition factors are not sufficient to completely describe the structure of \(V\)). Let us take for example the right regular representation:
sage: V = O3.regular_representation(side="right", base_ring = QQ)
sage: V.character()
10*p[0] + 6*p[1] + 3*p[2]
It encodes the trace of appropriate idempotents of \(O_3\) when acting on \(V\). In particular, the identity is of trace \(10\) since \(V\) is of dimension \(10\).
Here are the characters of right class modules:
sage: right_class_modules[0].character()
p[0]
sage: right_class_modules[1].character()
3*p[0] + p[1]
sage: right_class_modules[2].character()
3*p[0] + 2*p[1] + p[2]
Here are the characters of the simple modules, a.k.a. the character table:
sage: simple_modules[0].character()
p[0]
sage: simple_modules[1].character()
2*p[0] + p[1]
sage: simple_modules[2].character()
p[0] + p[1] + p[2]
Note that it is unitriangular, and therefore invertible. Hence we may recover the composition factors of a module from its characters.
The characters ring¶
A convenient device is to use the character ring of \(V\); its elements model formal ZZ linear combinations of modules (aka virtual characters):
sage: G = O3.character_ring(QQ, side="right"); G
The right-character ring of O3 over Rational Field
and, like is done for the symmetric group, with symmetric functions, to play with changes of bases in this ring. For example, the character table is the change of bases from the S basis (analogue of Schur) to the C basis (analogue of powersum):
sage: S = G.S(); S
The right-character ring of O3 over Rational Field in the basis of characters of simple right modules
sage: p = G.C(); p
The right-character ring of O3 over Rational Field in the basis of characters of right-class functions modules
sage: p(S[0])
p[0]
sage: p(S[1])
2*p[0] + p[1]
sage: p(S[2])
p[0] + p[1] + p[2]
One can compute the composition factors of a module by using the inverse change of basis. Here are the composition factors of the right regular representation:
sage: V = O3.regular_representation()
sage: S(V.character())
3*S[0] + 3*S[1] + S[2]
and those of the right class modules:
sage: S(right_class_modules[0].character())
S[0]
sage: S(right_class_modules[1].character())
S[0] + S[1]
sage: S(right_class_modules[2].character())
S[1] + S[2]
sage: for cls in S.__class__.mro(): print cls ….:
Change of bases in the characters ring¶
This ring admits several bases:
sage: C = G.C(); C
The left-character ring of O3 over Rational Field in the basis of characters of left-class functions modules
sage: S = G.S(); S
The left-character ring of O3 over Rational Field in the basis of characters of simple left modules
sage: P = G.P(); P
The left-character ring of O3 over Rational Field in the basis of characters of projective indecomposable left modules
sage: T = G.T(); T
The left-character ring of O3 over Rational Field in the basis of characters of regular left-class modules
Here are the composition factors of projective modules and left class modules:
sage: for chi in P.basis():
....: print "%s = %s"%(chi, S(chi))
P[0] = S[0] + S[1]
P[1] = S[1] + S[2]
P[2] = S[2]
sage: for chi in T.basis():
....: print "%s = %s"%(chi, S(chi))
T[0] = S[0]
T[1] = S[0] + S[1]
T[2] = S[1] + S[2]
This shows that \(V\) could possibly have a composition series in term of right class modules (and here, it does!):
sage: T(V.character())
T[0] + 2*T[1] + T[2]
By changing bases one can calculate how a module could possibly decompose in term of various modules. For example, here are the composition factors of the regular representation:
sage: S(V.character())
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: A real life example, parallel testing of a conjecture on J-Trivial monoids using MuPAD (experimental)¶
Requirements
This demonstration requires experimental code that has not yet been migrated from the Sage-Combinat queue to the sage-semigroups package, as well as MuPAD and MuPAD-Combinat.
sage: from sage.combinat.j_trivial_monoids import *
sage: def pij(j): return lambda i: i if i != j+1 else j
sage: pi2 = pij(2)
sage: pi2(1), pi2(2), pi2(3), pi2(4)
(1, 2, 2, 4)
sage: class NDPFMonoid(AutomaticMonoid):
....: def __init__(self, n):
....: ambient_monoid = DiscreteFunctions(range(1,n+1), action="right")
....: pi = Family(range(1, n), lambda j: ambient_monoid(pij(j)))
....: AutomaticMonoid.__init__(self, pi, one = ambient_monoid.one(),
....: category = (SubFiniteMonoidsOfFunctions(),
....: JTrivialMonoids().Finite()))
sage: Mon = NDPFMonoid(3)
sage: Mon.cardinality()
5
sage: Mon.list()
[[], [1], [2], [1, 2], [2, 1]]
sage: [ NDPFMonoid(n).cardinality() for n in range(6)]
[1, 1, 2, 5, 14, 42]
sage: MuMon = mupad(Mon); MuMon
/ +- -+ \
| | 0, 1, 2, 3, 4 | |
| | | |
| | 1, 1, 4, 4, 4 | |
| | | |
Dom::MMonoid| | 2, 3, 2, 3, 4 | |
| | | |
| | 3, 3, 4, 4, 4 | |
| | | |
| | 4, 4, 4, 4, 4 | |
\ +- -+ /
sage: MuMon.count()
5
sage: MuAlg = mupad.Dom.MonoidAlgebra(MuMon); MuAlg
sage: MuCMat = MuAlg.cartanInvariantsMatrix(); MuCMat
+- -+
| 1, 0, 0, 0 |
| |
| 0, 1, 1, 0 |
| |
| 0, 0, 1, 0 |
| |
| 0, 0, 0, 1 |
+- -+
sage: MuCMat.sage()
[1 0 0 0]
[0 1 1 0]
[0 0 1 0]
[0 0 0 1]
sage: M4 = NDPFMonoid(4)
sage: var('q')
q
sage: cartconj = M4.cartan_matrix(q); cartconj
[ 1 0 0 0 0 0 0 0]
[ 0 1 q q^2 0 0 0 0]
[ 0 0 1 q 0 0 0 0]
[ 0 0 0 1 0 0 0 0]
[ 0 0 0 0 1 0 q 0]
[ 0 0 0 0 q 1 q^2 0]
[ 0 0 0 0 0 0 1 0]
[ 0 0 0 0 0 0 0 1]
sage: cart = M4.cartan_matrix_mupad(q); cart
[ 1 0 0 0 0 0 0 0]
[ 0 1 0 0 q 0 0 0]
[ 0 q 1 0 q^2 0 0 0]
[ 0 0 0 1 0 q^2 q 0]
[ 0 0 0 0 1 0 0 0]
[ 0 0 0 0 0 1 0 0]
[ 0 0 0 0 0 q 1 0]
[ 0 0 0 0 0 0 0 1]
sage: def is_isomorphic_matrices(m1, m2):
....: coeffs1 = set([ c for row in m1 for c in row ])
....: coeffs2 = set([ c for row in m2 for c in row ])
....: if coeffs1 != coeffs2:
....: return False
....: f = sage.combinat.ranker.rank_from_list(sorted(coeffs1))
....: def graph(m):
....: m = matrix([[f(m[i,j]) for j in range(m.ncols()) ] for i in range(m.nrows())])
....: return DiGraph(m, multiple_edges = True)
....: return graph(m1).is_isomorphic(graph(m2))
sage: is_isomorphic_matrices(cart, cartconj)
True
sage: P4 = Posets(4); P4
Posets containing 4 vertices
sage: P4.cardinality()
16
sage: Pos = P4[9]; Pos.cover_relations()
[[0, 2], [1, 2], [2, 3]]
sage: #Pos.plot()
sage: MP = NDPFMonoidPoset(Pos); MP
NDPF monoid of Poset ([[0, 2], [1, 2], [2, 3]])
sage: is_isomorphic_matrices(MP.cartan_matrix(q), MP.cartan_matrix_mupad(q))
True
sage: @parallel()
....: def check_conj_parallel(Pos):
....: MP = NDPFMonoidPoset(Pos)
....: return is_isomorphic_matrices(MP.cartan_matrix(q),
....: MP.cartan_matrix_mupad(q))
sage: for (((poset,), _), res) in check_conj_parallel(Posets(3).list()): print poset.cover_relations(), res
sage: all(x[1] for x in check_conj_parallel(Posets(4).list()))
True
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Montreal Python: Sage Demo¶
Two important (but minor) differences between Sage language and Python¶
Integer division in Python :
sage: %python
sage: 2/3 + 4/5 + 1/7
0
in Sage:
sage: 2/3 + 4/5 + 1/7
169/105
Exponent (^
) in Python :
sage: %python
sage: 10^14 #exclusive OR
4
in Sage :
sage: 10^14
100000000000000
The preparser
sage: preparse('2/3 + 2^3 + 3.0')
"Integer(2)/Integer(3) + Integer(2)**Integer(3) + RealNumber('3.0')"
sage: preparse('2^3')
'Integer(2)**Integer(3)'
2D Plots¶
sage: f = sin(1/x)
sage: P = plot(f, -10, 10, color='red')
sage: P
sage: Q = line([(3,0.9), (7,0.9), (7,1.1), (3,1.1), (3,0.9)], color='green')
sage: Q
sage: R = text('$f(x) = \\sin(\\frac{1}{x})$', (5,1))
sage: R
sage: Q + R + P
L’outil interact (exemples tirés du wiki de Sage : http://wiki.sagemath.org/)¶
Curves of Pursuit¶
by Marshall Hampton.
sage: %hide
sage: npi = RDF(pi)
sage: from math import cos,sin
sage: def rot(t):
....: return matrix([[cos(t),sin(t)],[-sin(t),cos(t)]])
sage: def pursuit(n,x0,y0,lamb,steps = 100, threshold = .01):
....: paths = [[[x0,y0]]]
....: for i in range(1,n):
....: rx,ry = list(rot(2*npi*i/n)*vector([x0,y0]))
....: paths.append([[rx,ry]])
....: oldpath = [x[-1] for x in paths]
....: for q in range(steps):
....: diffs = [[oldpath[(j+1)%n][0]-oldpath[j][0],oldpath[(j+1)%n][1]-oldpath[j][1]] for j in range(n)]
....: npath = [[oldpath[j][0]+lamb*diffs[j][0],oldpath[j][1]+lamb*diffs[j][1]] for j in range(n)]
....: for j in range(n):
....: paths[j].append(npath[j])
....: oldpath = npath
....: return paths
sage: html('<h3>Curves of Pursuit</h3>')
sage: @interact
sage: def curves_of_pursuit(n = slider([2..20],default = 5, label="# of points"),steps = slider([floor(1.4^i) for i in range(2,18)],default = 10, label="# of steps"), stepsize = slider(srange(.01,1,.01),default = .2, label="stepsize"), colorize = selector(['BW','Line color', 'Filled'],default = 'BW')):
....: outpaths = pursuit(n,0,1,stepsize, steps = steps)
....: mcolor = (0,0,0)
....: outer = line([q[0] for q in outpaths]+[outpaths[0][0]], rgbcolor = mcolor)
....: polys = Graphics()
....: if colorize=='Line color':
....: colors = [hue(j/steps,1,1) for j in range(len(outpaths[0]))]
....: elif colorize == 'BW':
....: colors = [(0,0,0) for j in range(len(outpaths[0]))]
....: else:
....: colors = [hue(j/steps,1,1) for j in range(len(outpaths[0]))]
....: polys = sum([polygon([outpaths[(i+1)%n][j+1],outpaths[(i+1)%n][j], outpaths[i][j+1]], rgbcolor = colors[j]) for i in range(n) for j in range(len(outpaths[0])-1)])
....: #polys = polys[0]
....: colors = [(0,0,0) for j in range(len(outpaths[0]))]
....: nested = sum([line([q[j] for q in outpaths]+[outpaths[0][j]], rgbcolor = colors[j]) for j in range(len(outpaths[0]))])
....: lpaths = [line(x, rgbcolor = mcolor) for x in outpaths]
....: show(sum(lpaths)+nested+polys, axes = False, figsize = [5,5], xmin = -1, xmax = 1, ymin = -1, ymax =1)
Factor Trees¶
by William Stein
sage: %hide
sage: import random
sage: def ftree(rows, v, i, F):
....: if len(v) > 0: # add a row to g at the ith level.
....: rows.append(v)
....: w = []
....: for i in range(len(v)):
....: k, _, _ = v[i]
....: if k is None or is_prime(k):
....: w.append((None,None,None))
....: else:
....: d = random.choice(divisors(k)[1:-1])
....: w.append((d,k,i))
....: e = k//d
....: if e == 1:
....: w.append((None,None))
....: else:
....: w.append((e,k,i))
....: if len(w) > len(v):
....: ftree(rows, w, i+1, F)
sage: def draw_ftree(rows,font):
....: g = Graphics()
....: for i in range(len(rows)):
....: cur = rows[i]
....: for j in range(len(cur)):
....: e, f, k = cur[j]
....: if not e is None:
....: if is_prime(e):
....: c = (1,0,0)
....: else:
....: c = (0,0,.4)
....: g += text(str(e), (j*2-len(cur),-i), fontsize=font, rgbcolor=c)
....: if not k is None and not f is None:
....: g += line([(j*2-len(cur),-i), ((k*2)-len(rows[i-1]),-i+1)],
....: alpha=0.5)
....: return g
sage: @interact
sage: def factor_tree(n=100, font=(10, (8..20)), redraw=['Redraw']):
....: n = Integer(n)
....: rows = []
....: v = [(n,None,0)]
....: ftree(rows, v, 0, factor(n))
....: show(draw_ftree(rows, font), axes=False)
Illustrating the prime number theorem¶
by William Stein
sage: @interact
sage: def _(N=(100,(2..2000))):
....: html("<font color='red'>$\pi(x)$</font> and <font color='blue'>$x/(\log(x)-1)$</font> for $x < %s$"%N)
....: show(plot(prime_pi, 0, N, rgbcolor='red') + plot(x/(log(x)-1), 5, N, rgbcolor='blue'))
Stock Market data, fetched from Yahoo and Google¶
by William Stein
sage: %hide
sage: import urllib
sage: class Day:
....: def __init__(self, date, open, high, low, close, volume):
....: self.date = date
....: self.open=float(open); self.high=float(high); self.low=float(low); self.close=float(close)
....: self.volume=int(volume)
....: def __repr__(self):
....: return '%10s %4.2f %4.2f %4.2f %4.2f %10d'%(self.date, self.open, self.high,
....: self.low, self.close, self.volume)
sage: class Stock:
....: def __init__(self, symbol):
....: self.symbol = symbol.upper()
....: def __repr__(self):
....: return "%s (%s)"%(self.symbol, self.yahoo()['price'])
....:
....: def yahoo(self):
....: url = 'http://finance.yahoo.com/d/quotes.csv?s=%s&f=%s' % (self.symbol, 'l1c1va2xj1b4j4dyekjm3m4rr5p5p6s7')
....: values = urllib.urlopen(url).read().strip().strip('"').split(',')
....: data = {}
....: data['price'] = values[0]
....: data['change'] = values[1]
....: data['volume'] = values[2]
....: data['avg_daily_volume'] = values[3]
....: data['stock_exchange'] = values[4]
....: data['market_cap'] = values[5]
....: data['book_value'] = values[6]
....: data['ebitda'] = values[7]
....: data['dividend_per_share'] = values[8]
....: data['dividend_yield'] = values[9]
....: data['earnings_per_share'] = values[10]
....: data['52_week_high'] = values[11]
....: data['52_week_low'] = values[12]
....: data['50day_moving_avg'] = values[13]
....: data['200day_moving_avg'] = values[14]
....: data['price_earnings_ratio'] = values[15]
....: data['price_earnings_growth_ratio'] = values[16]
....: data['price_sales_ratio'] = values[17]
....: data['price_book_ratio'] = values[18]
....: data['short_ratio'] = values[19]
....: return data
....: def historical(self):
....: try:
....: return self.__historical
....: except AttributeError:
....: pass
....: symbol = self.symbol
....: def get_data(exchange):
....: name = get_remote_file('http://finance.google.com/finance/historical?q=%s:%s&output=csv'%(exchange, symbol.upper()),
....: verbose=False)
....: return open(name).read()
....: R = get_data('NASDAQ')
....: if "Bad Request" in R:
....: R = get_data("NYSE")
....: R = R.splitlines()
....: headings = R[0].split(',')
....: self.__historical = []
....: try:
....: for x in reversed(R[1:]):
....: date, opn, high, low, close, volume = x.split(',')
....: self.__historical.append(Day(date, opn,high,low,close,volume))
....: except ValueError:
....: pass
....: self.__historical = Sequence(self.__historical,cr=True,universe=lambda x:x)
....: return self.__historical
....: def plot_average(self, spline_samples=10):
....: d = self.historical()
....: if len(d) == 0:
....: return text('no historical data at Google Finance about %s'%self.symbol, (0,3))
....: avg = list(enumerate([(z.high+z.low)/2 for z in d]))
....: P = line(avg) + points(avg, rgbcolor='black', pointsize=4) + \
....: text(self.symbol, (len(d)*1.05, d[-1].low), horizontal_alignment='right', rgbcolor='black')
....: if spline_samples > 0:
....: k = 250//spline_samples
....: spl = spline([avg[i*k] for i in range(len(d)//k)] + [avg[-1]])
....: P += plot(spl, (0,len(d)+30), color=(0.7,0.7,0.7))
....: P.xmax(260)
....: return P
....: def plot_diff(self):
....: d = self.historical()
....: if len(d) == 0:
....: return text('no historical data at Google Finance about %s'%self.symbol, (0,3))
....: diff = []
....: for i in range(1, len(d)):
....: z1 = d[i]; z0 = d[i-1]
....: diff.append((i, (z1.high+z1.low)/2 - (z0.high + z0.low)/2))
....: P = line(diff,thickness=0.5) + points(diff, rgbcolor='black', pointsize=4) + \
....: text(self.symbol, (len(d)*1.05, 0), horizontal_alignment='right', rgbcolor='black')
....: P.xmax(260)
....: return P
sage: symbols = ['bsc', 'vmw', 'sbux', 'aapl', 'amzn', 'goog', 'wfmi', 'msft', 'yhoo', 'ebay', 'java', 'rht', ]; symbols.sort()
sage: stocks = dict([(s,Stock(s)) for s in symbols])
sage: @interact
sage: def data(symbol = symbols, other_symbol='', spline_samples=(8,[0..15])):
....: if other_symbol != '':
....: symbol = other_symbol
....: S = Stock(symbol)
....: html('<h1 align=center><font color="darkred">%s</font></h1>'%S)
....: S.plot_average(spline_samples).save('avg.png', figsize=[10,2])
....: S.plot_diff().save('diff.png', figsize=[10,2])
....: Y = S.yahoo()
....: k = Y.keys(); k.sort()
....: html('Price during last 52 weeks:<br>Grey line is a spline through %s points (do not take seriously!):<br> <img src="cell://avg.png">'%spline_samples)
....: html('Difference from previous day:<br> <img src="cell://diff.png">')
....: html('<table align=center>' + '\n'.join('<tr><td>%s</td><td>%s</td></tr>'%(k[i], Y[k[i]]) for i in range(len(k))) + '</table>')
Cryptography¶
The Diffie-Hellman Key Exchange Protocol¶
by Timothy Clemans and William Stein
sage: @interact
sage: def diffie_hellman(bits=slider(8, 513, 4, 8, 'Number of bits', False),
....: button=selector(["Show new example"],label='',buttons=True)):
....: maxp = 2 ^ bits
....: p = random_prime(maxp)
....: k = GF(p)
....: if bits > 100:
....: g = k(2)
....: else:
....: g = k.multiplicative_generator()
....: a = ZZ.random_element(10, maxp)
....: b = ZZ.random_element(10, maxp)
....: print """
sage: <html>
sage: <style>
sage: .gamodp, .gbmodp {
sage: color:#000;
sage: padding:5px
sage: }
sage: .gamodp {
sage: background:#846FD8
sage: }
sage: .gbmodp {
sage: background:#FFFC73
sage: }
sage: .dhsame {
sage: color:#000;
sage: font-weight:bold
sage: }
sage: </style>
sage: <h2 style="color:#000;font-family:Arial, Helvetica, sans-serif">%s-Bit Diffie-Hellman Key Exchange</h2>
sage: <ol style="color:#000;font-family:Arial, Helvetica, sans-serif">
sage: <li>Alice and Bob agree to use the prime number p = %s and base g = %s.</li>
sage: <li>Alice chooses the secret integer a = %s, then sends Bob (<span class="gamodp">g<sup>a</sup> mod p</span>):<br/>%s<sup>%s</sup> mod %s = <span class="gamodp">%s</span>.</li>
sage: <li>Bob chooses the secret integer b=%s, then sends Alice (<span class="gbmodp">g<sup>b</sup> mod p</span>):<br/>%s<sup>%s</sup> mod %s = <span class="gbmodp">%s</span>.</li>
sage: <li>Alice computes (<span class="gbmodp">g<sup>b</sup> mod p</span>)<sup>a</sup> mod p:<br/>%s<sup>%s</sup> mod %s = <span class="dhsame">%s</span>.</li>
sage: <li>Bob computes (<span class="gamodp">g<sup>a</sup> mod p</span>)<sup>b</sup> mod p:<br/>%s<sup>%s</sup> mod %s = <span class="dhsame">%s</span>.</li>
sage: </ol></html>
....: """ % (bits, p, g, a, g, a, p, (g^a), b, g, b, p, (g^b), (g^b), a, p,
....: (g^ b)^a, g^a, b, p, (g^a)^b)
Plot3d¶
Dessiner une fonction \(\mathbb{R}^2\mapsto \mathbb{R}\) : la commande plot3d
sage: def f(x, y):
....: return x^2 + y^2
sage: plot3d(f, (-10,10), (-10,10), viewer='tachyon')
Animations¶
sage: a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.3)], xmin=0, xmax=2*pi, figsize=[2,1])
sage: a.show()
La commande complex_plot pour les fonctions complexe¶
sage: f(x) = x^4 - 1
sage: complex_plot(f, (-2,2), (-2,2))
sage: def newton(f, z, precision=0.001) :
....: while abs(f(x=z)) >= precision:
....: z = z - f(x=z) / diff(f)(x=z)
....: return z
sage: complex_plot(lambda z : newton(f, z), (-1,1), (-1,1))
Utilisation du Notebook : Écriture, édition et évaluation d’une saisie¶
- Pour évaluer une saisie dans le Notebook de Sage, tapez la saisie dans une cellule et faites shift-entrée ou cliquer le lien evaluate
- . Essayez-le maintenant avec une expression simple (e.g., 2 + 2 ). La première évaluation d’une cellule prend plus de temps que les fois suivantes, car un processus commence.
sage: 2+3
5
sage: 4+5
9
Créez de nouvelles cellules de saisie en cliquant sur la ligne bleue qui apparaît entre les cellules lorsque vous déplacez la souris. Essayez-le maintenant.
Vous pouvez rééditer n’importe quelle cellule en cliquant dessus (ou en utilisant les flèches du clavier). Retournez plus haut et changez votre 2 + 2 en un 3 + 3 et réévaluez la cellule.
Vous pouvez aussi éditer ce texte-ci en double cliquant dessus ce qui fera apparaître un éditeur de texte TinyMCE Javascript. Vous pouvez même ajouter des expressions mathématiques telles que \(\sin(x) - y^3\) comme avec LaTeX.
Comment consulter l’aide contextuelle et obtenir de la documentation¶
Vous trouvez la liste des fonctions que vous pouvez appelez sur un objet X en tappant X.<touche de tabulation> .
sage: X = 2009
- Écrivez X.
- et appuyez sur la touche de tabulation.
sage: X.factor()
7^2 * 41
Une fois que vous avez sélectionné une fonction, disons factor, tappez X.factor(<touche de tabulation> ou X.factor?<touche de tabulation> pour *obtenir de l’aide et des exemples* d’utilisation de cette fonction. Essayez-le maintenant avec X.factor .
sage: 4+5
9
- Pour obtenir l’aide complète et un tutoriel plus exhaustif, cliquez sur le lien Help
- en haut à droite de cette page, et cliquer ensuite sur Fast Static Versions of the Documentation.
Résolution d’équations polynomiales¶
sage: a,b,c,d,X = var('a,b,c,d,X')
sage: s = solve(a*X^2 + b*X + c == 0, X)
sage: show(s)
sage: s = solve(a*X^3 + b*X^2 + c*X + d == 0, X)
sage: show(s[0])
Algèbre linéaire¶
sage: A = matrix(3, [9,4,2,4,6,1,6,4,3,2,3,4,2,7,8,6,5,3]); A
[9 4 2 4 6 1]
[6 4 3 2 3 4]
[2 7 8 6 5 3]
sage: show(A)
sage: latex(A)
\left(\begin{array}{rrrrrr}
9 & 4 & 2 & 4 & 6 & 1 \\
6 & 4 & 3 & 2 & 3 & 4 \\
2 & 7 & 8 & 6 & 5 & 3
\end{array}\right)
sage: r = random_matrix(ZZ, 200)
sage: r[0]
(6, 1, -4, 1, 3, 2, 0, 4, 1, 2, 1, -2, 0, 3, 1, 5, 0, 0, 3, -4, 68, 4, -1, -29, 2, 0, 1, 2, 4, -1, 1, 0, 1, 0, -22, 0, -2, 0, -1, -1, -3, -1, 0, 1, 1, 1, -32, 1, -1, -1, 0, 5, -1, -13, 0, 2, -1, -50, -1, 0, 16, 1, 1, -5, 0, -5, -3, -1, 1, 0, 1, -6, 0, -1, 1, 1, 0, 3, 0, -2, 1, 3, 0, 2, 5, -5, 3, 0, -9, 3, -1, 5, 0, -1, -1, 3, 0, 2, 0, 1, 0, 3, -1, 0, 0, 1, 0, -1, 0, 0, -7, 1, 0, 0, -3, -1, 12, 1, 0, -74, 1, 1, 0, 1, 1164, 21, -109, -5, -2, 1, 1, 3, -30, 17, -28, 1, 1, 161, -4, 1, 10, 2, -1, -1, 4, -6, 0, 17, 0, 25, -1, -1, -1, 0, -2, -1, -1, -1, 1, -6, -1, -2, 1, 2, -1, 0, -6, 1, -3, -1, 6, 0, -3, 0, -4, -1, 1, 1, 12, -7, -1, 1, -1, -1, 1, 2, 2, -25, -2, -1, 0, -1, 2, 3, 1, -3, 12, -10, 1, 0)
sage: time r.determinant()
-1529834725553757938763159502025548590567911254662803196770598603331067849864395053736435397051765374245101197807489393057663130380141963203671083430967372792929619229867512126727684265591250414807452250453734959591879530432065001775694429765051483913590921267567927871370268065203061006918276079882798699436138525602103991441803398564880661084453659955387439288542429758896771118012008221672140101768416901702596791928059352838737552772934612946211933401613477671553715592
Time: CPU 0.45 s, Wall: 0.73 s
sage: r.determinant?
Théorie des graphes¶
sage: D = graphs.DodecahedralGraph()
sage: D.show()
sage: D.show3d(viewer='tachyon')
sage: D.chromatic_polynomial()
x^20 - 30*x^19 + 435*x^18 - 4060*x^17 + 27393*x^16 - 142194*x^15 + 589875*x^14 - 2004600*x^13 + 5673571*x^12 - 13518806*x^11 + 27292965*x^10 - 46805540*x^9 + 68090965*x^8 - 83530946*x^7 + 85371335*x^6 - 71159652*x^5 + 46655060*x^4 - 22594964*x^3 + 7171160*x^2 - 1111968*x
sage: graph_editor(D);
sage: D.show()
Recherche dans l’encyclopédie de séquences en ligne de Sloane¶
sage: sloane_find([1,5,29,169],1)
Searching Sloane's online database...
[]
sage: sloane_find([1,2,3,4,5,6],1)
Searching Sloane's online database...
[]
Cython¶
The Sage notebook allows transparently editing and compiling Cython code simply by typing %cython at the top of a cell and evaluate it. Variables and functions defined in a Cython cell are imported into the running session.
Example 1, pure Python¶
Here is some simple Python code to numerically integrate the function \(f(x) = x^2\).
sage: from math import sin
sage: def f(x):
....: return sin(x**2)
....:
sage: def integrate_f_py(a, b, N):
....: s = 0
....: dx = (b-a)/N
....: for i in range(N):
....: s += f(a+i*dx)
....: return s * dx
sage: timeit('integrate_f_py(0, 1, 1000)', number=50)
50 loops, best of 3: 18.5 ms per loop
Example 1, compiled with Cython (no other changes)¶
Simply compiling this in Cython gives a speedup.
sage: %cython
sage: from math import sin
sage: def f(x):
....: return sin(x**2)
....:
sage: def integrate_f_cy0(a, b, N):
....: s = 0
....: dx = (b-a)/N
....: for i in range(N):
....: s += f(a+i*dx)
....: return s * dx
sage: timeit('integrate_f_cy0(0, 1, 1000)', number=50)
50 loops, best of 3: 16.7 ms per loop
Example 1, typed and compiled with Cython¶
Adding some static type declarations makes a much greater difference.
sage: %cython
sage: from math import sin
sage: def f(double x):
....: return sin(x**2)
....:
sage: def integrate_f_cy(double a, double b, int N):
....: cdef int i
....: cdef double s, dx
....: s = 0
....: dx = (b-a)/N
....: for i in range(N):
....: s += f(a+i*dx)
....: return s * dx
sage: timeit('integrate_f_cy(0, 1, 1000)')
625 loops, best of 3: 489 µs per loop
sage: 18500 /489.0
37.8323108384458
Example 2, pure Python¶
Here is a Python function that computes the sum of the first \(n\) positive integers.
sage: def mysum_py(n):
....: s = 0
....: for k in range(n):
....: s += k
....: return s
sage: time mysum_py(10^6)
499999500000
Time: CPU 2.09 s, Wall: 2.16 s
Example 2, just compiled with Cython¶
Simply compiling this function with Cython provides a speedup.
sage: %cython
sage: def mysum_cy0(n):
....: s = 0
....: for k in range(n):
....: s += k
....: return s
sage: time mysum_cy0(10^6)
499999500000L
Time: CPU 0.25 s, Wall: 0.27 s
sage: 2.09 / 0.25
8.36000000000000
Example 2, typed and compiled with Cython¶
Adding some static type declarations makes a much greater difference.
sage: %cython
sage: def mysum_cy1(n):
....: cdef int k
....: cdef long long s
....:
....: s = 0
....: for k in range(n):
....: s += k
....: return s
sage: time mysum_cy1(10^6)
499999500000L
Time: CPU 0.00 s, Wall: 0.00 s
sage: 2.09 / 0.00
+infinity
sage: timeit('mysum_cy1(10^6)')
125 loops, best of 3: 1.57 ms per loop
sage: 2.09/0.00157
1331.21019108280
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Sage combines the power of multiple software¶
(taken from a talk from William Stein)
Construct an elliptic curve using John Cremona’s table:
sage: E = EllipticCurve('389a')
Use matplotlib to plot it:
sage: plot(E,thickness=3)
Use mwrank to do a 2-descent:
sage: print E.mwrank()
Curve [0,1,1,-2,0] : Rank = 2
PARI to compute Fourier coefficients \(a_n\):
sage: E.anlist(15)
[0, 1, -2, -2, 2, -3, 4, -5, 0, 1, 6, -4, -4, -3, 10, 6]
lcalc to compute zeros in the critical strip of the L-series:
sage: E.lseries().zeros(5)
[0.000000000, 0.000000000, 2.87609907, 4.41689608, 5.79340263]
sympow to compute the modular degree:
sage: E.modular_degree()
40
Magma to compute the rank of the 3-selmer group:
sage: magma(E).ThreeSelmerGroup()
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Origamis, torus coverings and combinatorics of permutations¶
TODO: move this demo to sage.dynamics.demo?
Introduction¶
Studying geometric coverings from the viewpoint of permutations is a long story. Hurwitz [Hur1891] proved that the space of coverings of the sphere with prescribed simple ramifications is connected.
In Teichmüller theory, torus coverings play a central role. They were used to compute the volume of moduli space, provide explicit examples of Teichmüller curves, …
References¶
[EskMasSch03] | A. Eskin and H. Masur and M. Schmoll, Billards in rectangles with barriers, Duke Math. J., (2003) |
[HubLel06] | (1, 2) Hubert P. and Lelièvre S., Prime Arithmetic Teichmüller discs in \(\mathcal{H}(2)\), Isr. J. Math., (2006) |
[Hur1891] | Hurwitz A., Ueber Riemann’sche Flachen mit gegebenzen Verzweigungspunkten, Math. Ann, 39 (1891), 1-61. |
[LelRoy06] | (1, 2) S. Lelièvre and E. Royer, Orbitwise countings in H(2) and quasimodular forms, IMRN (2006) |
[McMul05] | (1, 2) C. T. McMullen, Teichmüller curves in genus two: Discriminant and spin, Math. Ann., 333, p. 87-130, (2005) |
[Sch05] | G. Schmithuesen, Veech Groups of Origamis, PhD Thesis Karlsruhe (2005) http://www.math.kit.edu/iag3/~schmithuesen/en |
[Vee89] | W. A. Veech, Teichmüller curves in moduli space, Eisenstein series and an application to triangular billiards , Inventiones Mathematicae, (1989) |
Three definitions¶
definition 1: An origami is a couple \((r,u)\) of permutations such that the group generated by \(r\) and \(u\) acts transitively.:
sage: o = Origami('(1,2)','(1,3)')
sage: o
(1,2)(3)
(1,3)(2)
Two origamis \((r,u) \in S_n \times S_n\) and \((r',u') \in S_n \times S_n\) are isomorphic if there exists a permutation \(\sigma \in S_n\) such that
definition 2: An origami is a connected cover of the torus ramified over one point.
Two origamis \(\pi: X \rightarrow \mathbb{T}^2\) and \(\pi': X' \rightarrow \mathbb{T}^2\) are isomorphics if there exists an homeomorphism \(f: X \rightarrow X'\) such that \(f \circ \pi' = \pi\).:
sage: o = Origami('(1,2)','(1,3)')
sage: o.show()
Starting from the topological definition 2, we get two permutations by looking at the monodromy action. We have a third definition
definition 3: An origami is a subgroup of finite index of the free group \(F_2\) generated by two elements. Two origamis \(H \subset F_2\) and \(H' \subset F_2\) are isomorphic if they are conjugate in \(F_2\).
Exercise: the three definitions and isomorphisms are equivalent.
Primitive and reduced origamis¶
- An origami is primitive, if either of the following equivalent statements holds:
- the group generated by the permutations \(r\) and \(u\) is primitive (there is no non trivial block),
- there is no intermediate cover \(\pi: X \rightarrow \mathbb{T}^2\),
- the subgroup \(H \subset F_2\) is maximal.
An origami \(\pi: X \rightarrow \mathbb{T}^2\) is reduced if there is no intermediate cover over a bigger torus which is also ramified over one point.
A primitive origami is reduced.
The escalators are always cover of a torus with two squares:
sage: esc3 = origamis.escalator(3)
sage: esc3.show()
sage: esc3.is_primitive()
False
but are ramified over two points above this \(2\)-torus, assuming they have at least \(3\) steps:
sage: esc3 = origamis.escalator(3)
sage: esc3.is_reduced()
True
Here is an example of an origami called a L-shaped origami:
sage: o = Origami('(1,2,3)','(1,4)')
sage: o.show()
sage: o.is_primitive()
True
Any non-reduced origami is, in a unique way, the composition of a reduced origami and an isogeny (Not yet implemented). The maximal torus above which the reduced origami is defined as a ramified cover over one point is obtained from the lattice of periods:
sage: o=Origami('(1,2,3)(4,5,6)','(1,4,7,8)(2,5)(3,6)')
sage: o.is_reduced()
False
sage: o.lattice_of_periods()
(1, 0, 2)
\(SL(2,\ZZ)\) action and Veech group of an origami¶
The group \(SL(2,\ZZ)\) acts on isomorphism classes of origamis. The standard form \(dz\) on the torus gives a canonical flat structures to any origami. The group \(SL(2,\RR)\) acts on flat surfaces.
The matrices act as follows on permutations (beware that you must consider origamis up to conjugacy):
- \(S \cdot (r,u) = (r^{-1}, u)\) (rotation by \(\pi/2\))
- \(L \cdot (r,u) = (r, u\ r^{-1}))\) (horizontal twist)
- \(R \cdot (r,u) = (r\ u^{-1}, u)\) (vertical twist)
- \(V \cdot (r,u) = (r^{-1}, u)\) (reflection wrt vertical axis)
- \(H \cdot (r,u) = (r, u^{-1})\) (reflection wrt horizontal axis)
The subgroup of \(SL(2,\ZZ)\) that fixes an origami is called the Veech of this origami. As an example, all escalators have Veech group the Theta group:
sage: origamis.escalator(3).veech_group()
Arithmetic subgroup corresponding to permutations
S=(2,3)
T=(1,2,3)
L=(1,2)
R=(1,3)
sage: origamis.escalator(4).veech_group()
Arithmetic subgroup corresponding to permutations
S=(2,3)
T=(1,2,3)
L=(1,2)
R=(1,3)
sage: origamis.escalator(5).veech_group()
Arithmetic subgroup corresponding to permutations
S=(2,3)
T=(1,2,3)
L=(1,2)
R=(1,3)
The Teichmüller curve of an origami is isometric to the quotient \(SL(2,\ZZ) / \Gamma\) where \(\Gamma\) is the Veech group.
The eierlegende Wollmilchsau has Veech group the full modular group \(SL(2,\ZZ)\):
sage: e = origamis.eierlegende_wollmilchsau()
sage: e
(1,2,3,4)(5,6,7,8)
(1,5,3,7)(2,8,4,6)
sage: e.veech_group()
Arithmetic subgroup corresponding to permutations
S=()
T=()
L=()
R=()
Schmithüesen in her thesis [Sch05] wrote an algorithm to compute the Veech group of an origami based on Rademacher-Schreier algorithm. Her algorithm is implemented in Sage.
Hubert, Lelièvre, McMullen classification in \(\mathcal{H}(2)\)¶
Hubert, Lelièvre [HubLel06] and McMullen [McMul05] gives a classification of orbits of origamis in \(\mathcal{H}(2)\) under the action of \(SL(2,\ZZ)\). A stratum \(\mathcal{H}(\kappa_1,\ldots,\kappa_l)\) of moduli space is the set of flat surfaces with given singularity degrees \(\kappa_1, \ldots, \kappa_l\). In the context of origamis, it corresponds to the conjugacy class, as an integer partition, of the commutator \(r\,u\,r^{-1}\,u^{-1}\) of the permutations \(r\) and \(u\) that define the origami. The stratum \(\mathcal{H}(2)\) corresponds to the integer partitions \((3,1^k)\) where \(k\) is an integer.
Any origami can be decomposed into horizontal cylinders. These cylinders are bounded by horizontal saddle connections (horizontal geodesics connecting singularities). The combinatorics of gluings of these cylinders along saddle connections is known as a cylinder diagram. Algebraically, it corresponds to a pair \((bot,top)\) of permutations with the same number of cycles in their cycle decompositions and a pairing of cycles of \(bot\) with cycles of \(top\).
There are only two cylinder diagrams in \(\mathcal{H}(2)\) which can be built as follows:
sage: a = AbelianStratum(2)
sage: cyls = a.cylinder_diagrams()
sage: c0, c1 = cyls
sage: c0
Cylinder diagram (0,1)-(1,2) (2)-(0)
sage: c1
Cylinder diagram (0,1,2)-(0,1,2)
The origamis in \(\mathcal{H}(2)\) can then be generated using those two cylinder diagrams by specifying the lengths of each separatrix, the height of each cylinder and an (optionnal) twist paramater:
sage: c0,c1 = AbelianStratum(2).cylinder_diagrams()
sage: o = c0.cylcoord_to_origami([1,1,1],[1,1])
sage: o
(1,2)(3)
(1,3)(2)
sage: o.stratum()
H(2)
sage: o = c1.cylcoord_to_origami([2,3,1],[1])
sage: o
(1,2,3,4,5,6)
(1,6,2,3,4,5)
sage: o.stratum()
H(2)
theorem ([HubLel06], [McMul05]): Let \(E_n\) be the set of primitive origamis in \(\mathcal{H}(2)\) with \(n\) squares. If \(n = 3\) or \(n\) even, then \(E_n\) is an \(SL(2,\ZZ)\) orbit. If \(n \geq 5\) and odd, then \(E_n\) is a union of two \(SL(2,\ZZ)\) orbits \(A_n\) and \(B_n\) which can be distinguished by the number of integer Weierstrass points.
The method .orientation_cover_list returns the stratum of Quadratic differential which corresponds to the cover together with the Weierstrass points partition which consist in the number of integer Weierstrass points of the cover and the triple of half-integers points.
sage: c0,c1 = AbelianStratum(2).cylinder_diagrams()
sage: o5a = c0.cylcoord_to_origami([1,2,1],[1,2])
sage: o5a.orientation_cover_list()
[(Q(1, -1^5), (3, (1, 1, 1)))]
sage: o5a.veech_group().index()
9
sage: o5b = c0.cylcoord_to_origami([1,3,1],[1,1])
sage: o5b.orientation_cover_list()
[(Q(1, -1^5), (1, (3, 1, 1)))]
sage: o5b.veech_group().index()
18
We now prove that we have everything:
sage: c0,c1 = AbelianStratum(2).cylinder_diagrams()
sage: l0 = set(map(lambda x: x.standard_form(),c0.origami_iterator(5)))
sage: len(l0)
17
sage: l1 = set(map(lambda x: x.standard_form(),c1.origami_iterator(5)))
sage: len(l1)
10
sage: 18 + 9 == 17 + 10
True
theorem ([EskMasSch03], [LelRoy06]): Let \(e_n\), \(a_n\) and \(b_n\) denote the cardinalities of \(E_n\), \(A_n\) and \(B_n\). Then
These numbers are related to coefficients of quasimodular forms (see [LelRoy06]).:
sage: e_n = lambda n: 3*n**2*(n-2)/8*prod(1-1/p**2 for p in prime_factors(n))
sage: a_n = lambda n: 3*n**2*(n-1)/16*prod(1-1/p**2 for p in prime_factors(n))
sage: b_n = lambda n: 3*n**2*(n-3)/16*prod(1-1/p**2 for p in prime_factors(n))
sage: a_n(5)
18
sage: b_n(5)
9
sage: e_n(5)
27
sage: c0,c1 = AbelianStratum(2).cylinder_diagrams()
sage: l0 = set(map(lambda x: x.standard_form(), c0.origami_iterator(7)))
sage: l1 = set(map(lambda x: x.standard_form(), c1.origami_iterator(7)))
sage: len(l0) + len(l1)
90
sage: c0,c1 = AbelianStratum(2).cylinder_diagrams()
sage: o7a = c0.cylcoord_to_origami([1,4,1],[1,2])
sage: o7a.orientation_cover_list()
[(Q(1, -1^5), (3, (1, 1, 1)))]
sage: o7a.veech_group().index()
36
sage: o7b = c0.cylcoord_to_origami([1,5,1],[1,1])
sage: o7b.orientation_cover_list()
[(Q(1, -1^5), (1, (3, 1, 1)))]
sage: o7b.veech_group().index()
54
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Plots (short version)¶
Some nice plots:
sage: plot(sin(x), -2*pi, 2*pi, fill = 'axis')
Taylor approximation:
sage: f = sin(x)
sage: g = f.taylor(x,0,3)
sage: plot(g, -2*pi, 2*pi)
All the way to a full featured applet:
sage: %hide
sage: var('x')
sage: @interact
sage: def g(f=sin(x), c=0, n=(1..30),
....: xinterval=range_slider(-10, 10, 1, default=(-8,8), label="x-interval"),
....: yinterval=range_slider(-50, 50, 1, default=(-3,3), label="y-interval")):
....: x0 = c
....: degree = n
....: xmin,xmax = xinterval
....: ymin,ymax = yinterval
....: p = plot(f, xmin, xmax, thickness=4)
....: dot = point((x0,f(x=x0)),pointsize=80,rgbcolor=(1,0,0))
....: ft = f.taylor(x,x0,degree)
....: pt = plot(ft, xmin, xmax, color='red', thickness=2, fill=f)
....: show(dot + p + pt, ymin=ymin, ymax=ymax, xmin=xmin, xmax=xmax)
....: html('$f(x)\;=\;%s$'%latex(f))
....: html('$P_{%s}(x)\;=\;%s+R_{%s}(x)$'%(degree,latex(ft),degree))
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
A micro demo of profiling Sage / Python code (draft)¶
This is a brief demo presented at Sage Days 86. See also Profiling.
We will analyze the performances of integer partitions of \(45\). Here is how many of them there are:
sage: P = Partitions(45)
sage: P.cardinality()
89134
The next command measures how much time it takes to list them all. Before running it, try to estimate the result:
sage: %time x = list(P)
CPU times: user 1.95 s, sys: 40 ms, total: 1.99 s
Wall time: 1.94 s
One can get statistics on how much time is used in each subfunction call:
sage: %prun x = list(P)
This is not so easy to analyze. A graphical visualization would be much nicer!
Graphical visualization with \(snakeviz\)¶
Installation:
sage -pip install snakeviz
This works locally only; we can hope for a tighter integration in the notebook in the long run.
We now load the extension in the notebook:
sage: %load_ext snakeviz
Let’s use it:
sage: %snakeviz x = list(P)
*** Profile stats marshalled to file u'/tmp/...'.
Graphical visualization with \(runsnake\)¶
I find the output easier to intepret with \(runsnake\); but this may just be a bias from having used it quite some. On the other hand it’s not integrated in the browser and harder to install.
Installation on Linux:
sage: apt install runsnakerun
For other systems, see the web page.
Let’s use it:
sage: runsnake("list(P)")
Todo
Add a demo of using the Python debugger to trace through the code.
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demo: reflection groups (draft)¶
\(\def\QQ{\mathbb{Q}}\def\CC{\mathbb{C}}\def\Hilb{\operatorname{Hilb}}\)
This demonstration review some of the features of Coxeter and reflection groups (basic manipulations, related posets, calculation of Molien type series). It grew out of a live demo improvised with the participants during interactive sessions at the CRM workshop on reflection groups on May 29th of 2017.
Requirements
This demonstration requires gap3 to be installed.
sage: %display latex # not tested
Constructing Coxeter groups¶
Let’s build the Coxeter group of type \(E_8\), and do some sample calculations with it:
sage: W = CoxeterGroup(["E",8])
sage: W
Finite Coxeter group over Integer Ring with Coxeter matrix:
[1 2 3 2 2 2 2 2]
[2 1 2 3 2 2 2 2]
[3 2 1 3 2 2 2 2]
[2 3 3 1 3 2 2 2]
[2 2 2 3 1 3 2 2]
[2 2 2 2 3 1 3 2]
[2 2 2 2 2 3 1 3]
[2 2 2 2 2 2 3 1]
sage: W.cardinality()
696729600
By default, this Coxeter group is constructed as a matrix group:
sage: W.an_element()
[ 0 1 0 0 0 0 0 -1]
[ 0 0 1 0 0 0 0 -1]
[ 1 1 0 0 0 0 0 -1]
[ 0 1 1 0 0 0 0 -1]
[ 0 0 0 1 0 0 0 -1]
[ 0 0 0 0 1 0 0 -1]
[ 0 0 0 0 0 1 0 -1]
[ 0 0 0 0 0 0 1 -1]
Instead, it’s possible to construct it as a permutation group, namely the group of permutation of its roots:
sage: W = CoxeterGroup(["E",8], implementation="permutation")
sage: w = W.an_element(); w
(1,121)(3,13)(9,22)(18,27)(19,23)(25,30)(29,35)(31,42)(34,40)(36,37)(38,45)(41,47)(43,49)(44,56)(48,51)(50,52)(54,59)(55,62)(57,64)(60,63)(68,73)(74,78)(96,98)(100,102)(101,106)(104,107)(108,110)(109,112)(111,113)(123,133)(129,142)(138,147)(139,143)(145,150)(149,155)(151,162)(154,160)(156,157)(158,165)(161,167)(163,169)(164,176)(168,171)(170,172)(174,179)(175,182)(177,184)(180,183)(188,193)(194,198)(216,218)(220,222)(221,226)(224,227)(228,230)(229,232)(231,233)
Finite and affine Coxeter groups can be specified as above using their classification, by providing their Cartan type. Here is a sample of all available Cartan types:
sage: CartanType.samples()
[['A', 1], ['A', 5], ['B', 1], ['B', 5], ['C', 1], ['C', 5],
['D', 2], ['D', 3], ['D', 5],
['E', 6], ['E', 7], ['E', 8],
['F', 4], ['G', 2], ['I', 5], ['H', 3], ['H', 4],
['A', 1, 1], ... ['BC', 5, 2]^*]
It contains all exceptional types, and a couple representatives of each infinite families.
Reduced words¶
Let’s play with elements and reduced words. One can construct an element from the Coxeter generators (also called simple reflections) with:
sage: s = W.simple_reflections()
sage: w = s[1] * s[3] * s[2]; w
(1,133,3)(2,122)(4,18,22,10,9,27)(13,123,121)(15,25,23,21,19,30)(17,36,35,28,29,37)(26,41,42,33,31,47)(32,48,45,39,38,51)(34,46,40)(43,53,49)(44,61,56)(50,58,52)(54,67,59)(55,66,62)(57,69,64)(60,70,63)(65,72)(68,77,73)(71,75)(74,83,78)(76,84)(79,81)(80,87)(82,88)(85,91)(86,90)(89,96,98)(92,94)(93,100,102)(95,99)(97,101,106)(103,104,107)(105,108,110)(109,115,112,111,114,113)(124,138,142,130,129,147)(135,145,143,141,139,150)(137,156,155,148,149,157)(146,161,162,153,151,167)(152,168,165,159,158,171)(154,166,160)(163,173,169)(164,181,176)(170,178,172)(174,187,179)(175,186,182)(177,189,184)(180,190,183)(185,192)(188,197,193)(191,195)(194,203,198)(196,204)(199,201)(200,207)(202,208)(205,211)(206,210)(209,216,218)(212,214)(213,220,222)(215,219)(217,221,226)(223,224,227)(225,228,230)(229,235,232,231,234,233)
Here is a short hand (note: the word need not be reduced):
sage: W.from_reduced_word([1,3,2])
(1,133,3)(2,122)(4,18,22,10,9,27)(13,123,121)(15,25,23,21,19,30)(17,36,35,28,29,37)(26,41,42,33,31,47)(32,48,45,39,38,51)(34,46,40)(43,53,49)(44,61,56)(50,58,52)(54,67,59)(55,66,62)(57,69,64)(60,70,63)(65,72)(68,77,73)(71,75)(74,83,78)(76,84)(79,81)(80,87)(82,88)(85,91)(86,90)(89,96,98)(92,94)(93,100,102)(95,99)(97,101,106)(103,104,107)(105,108,110)(109,115,112,111,114,113)(124,138,142,130,129,147)(135,145,143,141,139,150)(137,156,155,148,149,157)(146,161,162,153,151,167)(152,168,165,159,158,171)(154,166,160)(163,173,169)(164,181,176)(170,178,172)(174,187,179)(175,186,182)(177,189,184)(180,190,183)(185,192)(188,197,193)(191,195)(194,203,198)(196,204)(199,201)(200,207)(202,208)(205,211)(206,210)(209,216,218)(212,214)(213,220,222)(215,219)(217,221,226)(223,224,227)(225,228,230)(229,235,232,231,234,233)
sage: w.reduced_word()
[1, 2, 3]
sage: w.reduced_words()
[[1, 3, 2], [2, 1, 3], [1, 2, 3]]
Computing Molien-type sums for reflection groups¶
Let’s start by exploring the Shephard-Todd reflection group G_4
:
sage: W = ReflectionGroup(4); W
Irreducible complex reflection group of rank 2 and type ST4
sage: W.cardinality()
24
sage: W.is_isomorphic(SymmetricGroup(4))
False
It is constructed as a permutation group:
sage: w = W.an_element(); w
(1,3,9)(2,4,7)(5,10,18)(6,11,16)(8,12,19)(13,15,20)(14,17,21)(22,23,24)
Here is how to recover the matrix action on \(V\) and \(V^*\):
sage: m = w.to_matrix(); m
[ 1 0]
[ 0 E(3)]
sage: w.to_matrix("dual")
[ 1 0]
[ 0 E(3)^2]
The Hilbert series of the invariant ring and degrees of its generators¶
Let’s use Molien’s formula to compute the Hilbert series \(H=\Hilb(\CC[V]^W,q)\) of the invariant ring \(\CC[V]^W=S(V^*)^W\):
sage: QQq = QQ['q'].fraction_field()
sage: q = QQq.gen()
sage: H = 1/W.cardinality() * sum( 1/det(1-q*w.to_matrix()) for w in W );
sage: H
1/(q^10 - q^6 - q^4 + 1)
We know that this should factor as \(\frac{1}{\prod 1-q^{d_i}}\).
Frustrating as it is, Sage can’t factor the above fraction as is:
sage: H.factor()
Traceback (most recent call last):
...
NotImplementedError
That’s because it looks like a fraction in \(\QQ(q)\) but it is in fact a fraction in the Universal Cyclotomic Field (the extension of \(\QQ\) containing all roots of unity):
sage: H.parent()
Fraction Field of Univariate Polynomial Ring in q over Universal Cyclotomic Field
To proceed, we first convert \(H\) into \(\QQ(q)\):
sage: H = QQq(H)
sage: H.parent()
Fraction Field of Univariate Polynomial Ring in q over Rational Field
and then can finally factor it:
sage: factor(H.denominator())
(q - 1)^2 * (q + 1)^2 * (q^2 - q + 1) * (q^2 + 1) * (q^2 + q + 1)
This is a product of cyclotomic polynomials, and by manual inspection, one we can recover the desired form for the denominator of \(H\):
sage: H.denominator() == (1-q^4)*(1-q^6)
True
This is telling us that the invariant ring is generated by two invariants of degree \(4\) and \(6\). Let’s double check this.
Sage can compute generators of an invariant ring of a finite matrix
group, but only over reasonably simple fields, which does not include
the Universal Cyclotomic Field. So we are going to convert our group
into a matrix group WM
over the Cyclotomic Field of degree \(3\):
sage: K = CyclotomicField(3)
sage: WM = MatrixGroup( [ matrix(K, w.to_matrix()) for w in W.gens()])
sage: WM
Matrix group over Cyclotomic Field of order 3 and degree 2 with 2 generators (
[ 1 0] [2/3*zeta3 + 1/3 1/3*zeta3 - 1/3]
[ 0 zeta3], [2/3*zeta3 - 2/3 1/3*zeta3 + 2/3]
)
sage: WM.invariant_generators()
[x1^4 - x1*x2^3, x1^6 + 5/2*x1^3*x2^3 - 1/8*x2^6]
Computation of exponents and coexponents¶
We will use that \(V\) and \(V^*\) are irreducible representations together with the following relations between the Hilbert series of the corresponding isotypic components in the polynomial ring \(\CC[V]^W\) with the exponents \(e_1,\ldots,e_n\) and coexponents \(e_1^*,\ldots,e_n^*\):
sage: 1/W.cardinality() * sum( w.to_matrix().trace()/det(1-q*w.to_matrix()) for w in W ) / H
q^5 + q^3
sage: 1/W.cardinality() * sum( w.to_matrix("dual").trace()/det(1-q*w.to_matrix()) for w in W ) / H
q^3 + q
Let’s do a consistency check with the degrees (which are the \(e_i+1\)) and the codegrees (which are the \(e_i^*-1\)):
sage: W.degrees()
(4, 6)
sage: W.codegrees()
(2, 0)
Solomon’s formula¶
Exercise
Compute the Hilbert series of \((\CC[V]\otimes \bigwedge^\cdot V^*)^W\) via a Molien-type calculation:
and then compare it to the prediction of Solomon’s formula, namely:
Solution
sage: QQqt = QQ['q,t'].fraction_field()
sage: q,t = QQqt.gens()
sage: Solomon = 1/W.cardinality() * sum( det(1+t*w.to_matrix()) / det(1-q*w.to_matrix()) for w in W )
sage: QQqt(Solomon) / H
q^8*t^2 + q^5*t + q^3*t + 1
sage: _.factor()
(q^3*t + 1) * (q^5*t + 1)
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Démontration: Calcul symbolique¶
Le coeur des systèmes comme Maple et Maxima est le calcul sur les expressions, avec sa simplicité pour les nouveaux venus et sa souplesse. Modulo la déclaration explicite des variables et des petites variantes de syntaxe, l’utilisateur casuel retrouvera ses petits.
Une expression:
sage: f = cos(x)^6 + sin(x)^6 + 3 * sin(x)^2 * cos(x)^2; f
sin(x)^6 + cos(x)^6 + 3*sin(x)^2*cos(x)^2
La même après avoir configuré les affichages en latex:
sage: %display latex
sage: f
Simplifions-la:
sage: f.simplify_trig()
1
Variables symboliques:
sage: k
Traceback (most recent call last):
...
NameError: name 'k' is not defined
sage: var('n,k')
(n, k)
Une sommation définie:
sage: sum(binomial(n, k) * factorial(k) / factorial(n+1+k), k, 0, n)
1/2*sqrt(pi)/factorial(n + 1/2)
Calcul de \(\lim\limits_{x\rightarrow \frac{\pi}{4} }\dfrac{\cos\left(\frac{\pi}{4}-x \right)-\tan x }{1-\sin\left(\frac{\pi}{4}+x \right)}\):
sage: f(x) = (cos(pi/4-x)-tan(x)) / (1-sin(pi/4 + x))
sage: limit(f(x), x = pi/4, dir='minus')
+Infinity
Calcul, selon la valeur de \(x\), de l’intégrale \(\int_0^{\infty} \frac{x \cos u}{u^2+x^2} du\):
sage: var('u')
u
sage: f = x * cos(u) / (u^2 + x^2)
sage: assume(x>0)
sage: f.integrate(u, 0, infinity)
1/2*pi*e^(-x)
sage: forget(); assume(x<0); f.integrate(u, 0, infinity)
-1/2*pi*e^x
L’arithmétique est gérée en interne (pynac) et le reste est délégué à Maxima. En relatif, cet aspect reste un des points faibles de Sage.
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Symmetric functions¶
- First step when using any new Sage functionality… ask Sage what to do!
sage: SymmetricFunctions?
sage: S = SymmetricFunctions(QQ) # The ring of symmetric functions over the rational numbers
sage: # Typing 'objectname.<tab>' gives a lot of information about whant
sage: # you can do with the object
sage: S.
sage: # The usual bases for symmetric functions
sage: p = S.powersum(); s = S.schur(); m = S.monomial(); h = S.homogeneous(); e = S.elementary()
sage: # The 'forgotten basis' is dual to the elementary basis
sage: f = e.dual_basis()
sage: # Different ways of entering symmetric functions
sage: p[2,1] == p([2,1]) and p[2,1] == p(Partition([2,1]))
True
sage: # Changing bases
sage: p(s[2,1])
1/3*p[1, 1, 1] - 1/3*p[3]
sage: # Sums of different bases are automatically converted to a single basis
sage: h[3] + s[3] + e[3] + p[3]
2*h[1, 1, 1] - 5*h[2, 1] + 6*h[3]
sage: # Littlewood-Richardson coefficients are relatively fast
sage: timeit('s[10]^4')
5 loops, best of 3:..
sage: # Changing bases
sage: time h(s[10]^4);
h[10, 10, 10, 10]
Time: CPU 1.07 s, Wall: 1.08 s
sage: # We get an arbitrary symmetric function to demonstrate some functionality
sage: foo = h.an_element()
sage: foo
1/2*h[] + 3*h[1, 1, 1] + 2*h[2, 1, 1]
sage: foo.omega() # The omega involution
1/2*h[] + 3*h[1, 1, 1] + 2*h[1, 1, 1, 1] - 2*h[2, 1, 1]
sage: e(foo.omega())
1/2*e[] + 3*e[1, 1, 1] + 2*e[2, 1, 1]
sage: foo.scalar(s[3,1]) # The Hall scalar product
4
sage: foo.is_schur_positive()
True
sage: foo.skew_by(e[2,1])
9*h[] + 10*h[1]
sage: # We can define skew partition directly
sage: mu = Partition([3,2])/Partition([2,1])
sage: mu
[[3, 2], [2, 1]]
sage: s(mu)
s[1, 1] + s[2]
sage: # We can expand a symmetric function in monomials
sage: s(mu).expand(3)
x0^2 + 2*x0*x1 + x1^2 + 2*x0*x2 + 2*x1*x2 + x2^2
sage: # Or we can choose our alphabet
sage: s(mu).expand(3,alphabet=['a','b','c'])
a^2 + 2*a*b + b^2 + 2*a*c + 2*b*c + c^2
sage: mu = Partition([32,18,16,4,1])/Partition([14,3,2,1])
sage: la = Partition([33,19,17,4,1])/Partition([15,4,3,1])
sage: (s(la) - s(mu)).is_schur_positive()
True
sage: foo.kronecker_product(foo)
1/4*h[] + 54*h[1, 1, 1] + 20*h[1, 1, 1, 1] + 8*h[2, 1, 1]
sage: foo.plethysm(h[3])
1/2*h[] + 3*h[3, 3, 3] + 2*h[4, 3, 3, 2] - 2*h[5, 3, 3, 1] + 2*h[6, 3, 3]
sage: foo.inner_plethysm?
sage: # The transition matrix from the Schur basis to the power basis
sage: # Try s.transition_matrix? for more information
sage: s.transition_matrix(m,5)
[1 1 1 1 1 1 1]
[0 1 1 2 2 3 4]
[0 0 1 1 2 3 5]
[0 0 0 1 1 3 6]
[0 0 0 0 1 2 5]
[0 0 0 0 0 1 4]
[0 0 0 0 0 0 1]
sage: # The sum of degree 6 Schur functions whose first part is even
sage: foo = sum([s[mu] for mu in Partitions(6) if mu[0]%2 == 0])
sage: foo
s[2, 1, 1, 1, 1] + s[2, 2, 1, 1] + s[2, 2, 2] + s[4, 1, 1] + s[4, 2] + s[6]
sage: def remove_last_part(mu):
....: r""" Remove the last part from a partition """
....: return Partition(mu[:-1])
sage: # We can apply this map to all the partitions appearing in 'foo'
sage: foo.map_support(remove_last_part)
s[] + s[2, 1, 1, 1] + s[2, 2] + s[2, 2, 1] + s[4] + s[4, 1]
sage: # Warning! This gives different results depending on the basis in which foo is expressed
sage: h(foo).map_support(remove_last_part)
3*h[] + h[2, 1, 1, 1] + h[2, 2] - 2*h[2, 2, 1] - 2*h[3, 1, 1] + 2*h[3, 2] - 2*h[4] + 4*h[4, 1] - 4*h[5]
sage: foo.map_support(remove_last_part) == h(foo).map_support(remove_last_part)
False
sage: # We can easily get specific coefficients
sage: foo.coefficient([4,2])
1
sage: # There are many forms of symmetric functions in sage.
sage: # They do not (yet) all appear under 'SymmetricFunctions'
sage: # These are the ~H[X;q,t] often called the 'modified Macdonald polynomials'
sage: Ht = MacdonaldPolynomialsHt(QQ)
sage: s(Ht([3,2]))
Traceback (most recent call last):
...
TypeError
sage: Ht.base_ring()
Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field
sage: S.base_ring()
Rational Field
sage: q
Traceback (most recent call last):
...
NameError: name 'q' is not defined
sage: # The following is a shortcut notation (based on Magma).
sage: # It defines R to be the polynomial ring in the variables
sage: # 'q' and 't' over the rational numbers, and makes these variables
sage: # available for use
sage: R.<q,t> = Frac(ZZ['q','t'])
sage: S = SymmetricFunctions(R)
sage: p = S.powersum(); s = S.schur(); m = S.monomial(); h = S.homogeneous(); e = S.elementary();
sage: Ht = MacdonaldPolynomialsHt(R)
sage: s(Ht([3,2]))
q^4*t^2*s[1, 1, 1, 1, 1] + (q^4*t+q^3*t^2+q^3*t+q^2*t^2)*s[2, 1, 1, 1] + (q^4+q^3*t+q^2*t^2+q^2*t+q*t^2)*s[2, 2, 1] + (q^3*t+q^3+2*q^2*t+q*t^2+q*t)*s[3, 1, 1] + (q^3+q^2*t+q^2+q*t+t^2)*s[3, 2] + (q^2+q*t+q+t)*s[4, 1] + s[5]
sage: latex(_)
q^{4} t^{2}s_{1,1,1,1,1} + \left(q^{4} t + q^{3} t^{2} + q^{3} t + q^{2} t^{2}\right)s_{2,1,1,1} + \left(q^{4} + q^{3} t + q^{2} t^{2} + q^{2} t + q t^{2}\right)s_{2,2,1} + \left(q^{3} t + q^{3} + 2 q^{2} t + q t^{2} + q t\right)s_{3,1,1} + \left(q^{3} + q^{2} t + q^{2} + q t + t^{2}\right)s_{3,2} + \left(q^{2} + q t + q + t\right)s_{4,1} + s_{5}
sage: s(Ht([3,2])).coefficient([2,1,1,1]).subs({q:q^(-1), t:t^(-1)}) *q^5 * t^5
q^3*t^3 + q^2*t^4 + q^2*t^3 + q*t^4
sage: # We can also create the ring of Macdonald Polynomials
sage: # using different parameters
sage: A.<a,b> = QQ[]
sage: P = MacdonaldPolynomialsP(FractionField(A),a,b)
sage: sa = SymmetricFunctions(FractionField(A)).schur()
sage: sa(P[2,1])
((a*b-b^2+a-b)/(-a*b^2+1))*s[1, 1, 1] + s[2, 1]
sage: # Press <tab> after the following to see the different
sage: # variants of Macdonald polynomials in sage
sage: MacdonaldPolynomials
Traceback (most recent call last):
...
NameError: name 'MacdonaldPolynomials' is not defined
sage: # Press <tab> after the following to see the different
sage: # variants of Jack polynomials in sage
sage: JackPolynomials
sage: # Press <tab> after the following to see the different
sage: # variants of Hall-Littlewood polynomials in sage
sage: HallLittlewood
sage: ks2 = kSchurFunctions(R,2,t=R(t))
sage: s = SymmetricFunctions(R).schur()
sage: s(ks2[2,2,1])
s[2, 2, 1] + t*s[3, 1, 1] + (t^2+t)*s[3, 2] + (t^3+t^2)*s[4, 1] + t^4*s[5]
sage: ks2(s[1])
ks2[1]
sage: ks2(s[3])
Traceback (most recent call last):
...
ValueError: s[3] is not in the space spanned by k-Schur Functions at level 2 over Multivariate Polynomial Ring in q, t over Rational Field.
sage: # Warning: Not well supported yet!
sage: SchubertPolynomialRing
sage: # Warning: Not well supported yet!
sage: LLT
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Representation theory of monoids and Markov chains: generalized Tsetlin library (experimental)¶
Requirements
This demonstration requires experimental code that has not yet been migrated from the Sage-Combinat queue to the sage-semigroups package.
In a first step, we construct a poset, its set of linear extensions, and endow this set with the promotion action:
sage: P = Poset([[1,2,3,4], [[1,2], [3,4]]], linear_extension=True)
sage: view(P)
sage: L = P.linear_extensions(); L
The set of all linear extensions of Finite poset containing 4 elements
sage: L.cardinality()
6
sage: list(L)
[[1, 2, 3, 4], [1, 3, 2, 4], [1, 3, 4, 2], [3, 1, 2, 4], [3, 1, 4, 2], [3, 4, 1, 2]]
sage: G = L.markov_chain_digraph(labeling="source")
sage: view(G)
sage: M = G.transition_monoid(); M
The transition monoid of Looped multi-digraph on 6 vertices
sage: M.is_r_trivial()
False
sage: M.is_l_trivial()
True
sage: M = G.transition_monoid(category=LTrivialMonoids())
sage: V = G.transition_module(monoid=M).algebra(QQ); V
sage: V.character()
6*C[()] + C[(1, 2, 3, 4)] + 3*C[(2,)] + 2*C[(2, 4)] + 3*C[(4,)]
sage: V.composition_factors()
2*S[()] + S[(1, 2, 3, 4)] + S[(2,)] + S[(2, 4)] + S[(4,)]
One can read off the eigenvalues of the generators of the monoid and of the transition matrix!
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Tutorial: Editing the Sage sources¶
Prerequisite: this tutorial assumes that you can open a terminal, and
that you can run sage
by typing in:
> sage
----------------------------------------------------------------------
| Sage Version 4.6.2, Release Date: 2011-02-25 |
| Type notebook() for the GUI, and license() for information. |
----------------------------------------------------------------------
sage: 1 + 1
2
1. Choose a function that you want to modify¶
In this tutorial, you will edit the code and documentation of some
Sage
function. If you found a typo in the Sage
documentation,
or have some simple function that you want to add, please go ahead for
it! You can also pick one of the New Beginner Tickets.
As an example, we will play around with the inverse method of
permutations:
sage: P = Permutation([3,1,2])
sage: P.inverse()
[2, 3, 1]
2. Find the sources¶
Use ?
to fetch the documentation of the chosen method. Up to some
exceptions, the file containing the source code of this method will
appear in the first lines:
sage: P.inverse?
Type: instancemethod
Base Class: <type 'instancemethod'>
String Form: <bound method Permutation_class.inverse of [3, 1, 2]>
Namespace: Interactive
File: /opt/sage/local/lib/python2.6/site-packages/sage/combinat/permutation.py
Definition: P.inverse(self)
Docstring:
Returns the inverse of a permutation
EXAMPLES:
sage: Permutation([3,8,5,10,9,4,6,1,7,2]).inverse()
[8, 10, 1, 6, 3, 7, 9, 2, 5, 4]
sage: Permutation([2, 4, 1, 5, 3]).inverse()
[3, 1, 5, 2, 4]
Hence, the sources are in
/opt/sage/local/lib/python2.6/site-packages/sage/combinat/permutation.py
.
On the computer where this tutorial has been written, /opt/sage
is
the root directory of Sage
, which is usually called
SAGE_ROOT
. Please adapt all the examples below to your particular
setup. Then, local/lib/python2.6/site-packages/
is the
subdirectory where Python
code gets installed. Finaly
sage/combinat/permutation.py
is the file containing the Python
module sage.combinat.permutation
where this method is
implemented:
sage: P.__module__
'sage.combinat.permutation'
3. A first modification (cheaty variant)¶
Warning
as a first step, we will cheat a bit. Please make sure to continue on to the next steps!
3.1 Open the source file¶
Open the file with your favorite text editor, typically via the file
browser. Here, we use the text editory gedit
which is installed by
default on most distributions of Linux, as well as on the Sage
windows install. We call it from the terminal:
> gedit /opt/sage/local/lib/python2.6/site-packages/sage/combinat/permutation.py
Search for the method definition in the file:
def inverse(self):
r"""
Returns the inverse of a permutation
...
"""
w = range(len(self))
...
3.1 Edit the code¶
Insert “Hi there!” somewhere in the documentation string of the method.
3.2 Check your modification¶
Rerun Sage. In the notebook, you can do this via Action -> Restart
worksheet
. Then, check that “Hi there!” indeed appears in the
documentation of inverse:
sage: P = Permutation([3,2])
sage: P.inverse?
...
Returns the inverse of a permutation
Hi there!
EXAMPLES:
...
4. A real modification¶
We are now hitting a little annoyance in the Sage workflow which
should disappear at some point. The file we modified is in fact not
the original source file. To do things properly, we need to modify
instead /opt/sage/src/sage/combinat/permutation.py
:
> gedit /opt/sage/local/lib/python2.6/site-packages/sage/combinat/permutation.py
Edit the documentation of inverse
to add an example showing that
the inverse of the empty partition is the empty partition. Once this
is done, rebuild sage with:
> sage -b
Warning
Depending on the state of your Sage installation, this step may recompile some bits of Sage, requiring the standard development tools (compiler, …) to be installed on your machine. See the Sage source installation instructions.
Rerun sage, and check that your example shows up in the documentation.
5. Test the modifications¶
Are you sure your modifications are correct? Really sure?
Make sure that all the examples in the source code still work.
> cd /opt/sage/src/ > sage -t sage/combinat/permutation.py
If some tests failed, edit the file again.
6. Rebuild the documentation¶
Build the documentation and make sure there are no errors or warnings:
> sage -b && sage -docbuild reference html
Open the html version of the documentation in your browser and make sure it looks OK:
> open /opt/sage/src/doc/output/html/en/reference/sage/combinat/permutation.html
7. Oops, what did I modify?¶
Warning
Everything below needs to be updated to git
Do not worry about editing the Sage sources. Sage uses the version
control system Mercurial
( hg or sage -hg ) to manage all
of its source code. Mercurial
stores the evolution of every single
file of Sage
since the beginning. At any point, you can track
your modifications to the original sources:
> **cd /opt/sage/src/**
> **sage -hg status**
M sage/combinat/permutation.py
> **sage -hg diff**
diff --git a/sage/combinat/permutation.py b/sage/combinat/permutation.py
--- a/sage/combinat/permutation.py
+++ b/sage/combinat/permutation.py
@@ -1207,6 +1207,8 @@ class Permutation_class(CombinatorialObj
[8, 10, 1, 6, 3, 7, 9, 2, 5, 4]
sage: Permutation([2, 4, 1, 5, 3]).inverse()
[3, 1, 5, 2, 4]
+ sage: Permutation([]).inverse()
+ []
"""
w = range(len(self))
for i,j in enumerate(self):
And even revert your modifications. Try it now! Make a random
modification to the code of inverse
. Rebuild Sage
and run the
tests to check that you actually broke this method. Then, use:
> **sage -hg revert --all**
Warning
This really reverts all your modifications! Use with care!
8. Streamlining the process¶
In case Mercurial
is installed on your machine, you may use hg
as a shortcut for sage -hg. You can also add the following line to
your ~/.bashrc
file:
**alias hg='sage -hg'**
I verify that it works:
> hg –version Mercurial Distributed SCM (version 1.6.4)
Copyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
To learn more about mercurial (highly recommended), see the Mercurial tutorial.
9. Conclusion¶
Congratulations, you can now adapt Sage
to your taste! Go ahead,
explore the Sage
sources. Play around with them. Modify them. They
are all yours.
We will see in a later tutorial how you can then share your
modifications with others or contribute them back to Sage
.
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Tutorial: How to contribute to Sage (outdated)¶
Prerequisites:
Warning
This tutorial is outdated since 2013 (Sage 6.0), when Sage development’s workflow was deeply refactored, including a switch to the version control system git instead of Mercurial.
See instead the Developers Guide.
GNU General Public Licence¶
Sage is distributed under the terms of the GNU General Public License version 2 (GPLv2) which provides four kinds of freedom:
- Freedom to run the program
- Freedom to study the code
- Freedom to change the code
- Freedom to redistribute your changes to anyone, improve the software
All users of Sage make use of the first freedom. In * Tutorial: Editing the Sage sources we used the second and third. Here we will see how to use the last one.
Sixteen Easy Steps¶
-
class
borderlesstable
¶ - Find a bug
- Verify the content the patch
- Sage trac server
- Upload the patch on Sage trac
- Create a ticket
- More on Mercurial queues
- Edit the sage sources
- Download a patch
- Enable Mercurial queues
- Edit the series file
- Create a patch
- Reviewing a patch
- Update the current patch
- Positive review or Needs work
- Export a patch
- Advanced tricks
1. Find a bug¶
That’s the easiest part :-) If you don’t have one, you may browse the Open Beginner Tickets.
Here, we will fix a typo in the documentation of
sage.modular.modform.element.ModularForm_abstract.qexp()
.
Todo
extract this to a separate tutorial “trac server / reporting a bug”?
2. Sage trac server¶
In Sage, modifications are tracked on a web site called Sage trac. Every bug gets assigned a number. For instance, the number #10484 refers to the bug called Chinese remainder code raises an error when called with Python ints. On the ticket, one can see that:
- The bug was reported and solved by David Loeffler (UK) in December 2010.
- The ticket was positively reviewed by Robert Bradshaw (USA) and Mike Hansen.
- The solution was merged in
sage-4.6.2
by Jeroen Demeyer (Belgium) on January 11th 2011.
One can also look at the solution, download it, test it, etc.
3. Create a ticket¶
In order to create a ticket:
- Get an account, following the instructions on http://trac.sagemath.org/sage_trac/.
- Make sure the ticket does not already exists.
- Login to your account
- Create ticket
- In the description field, explain how someone else should understand and/or reproduce the bug.
Here, we create the ticket #11299 for fixing the documentation of
sage.modular.modform.element.ModularForm_abstract.qexp()
.
4. Edit the sage sources¶
5. Enable Mercurial queues¶
Mercurial queues is an extension to Mercurial that allows one to
easily work with collections of patches. To allow Mercurial queues,
edit (or create) the file ~/.hgrc
and make sure it contains your
user name, and the line hgext.mq = in the extensions
section:
[ui]
username = Sebastien Labbe <hidden adress email>
[extensions]
hgext.mq =
color =
[alias]
qstatus = status --rev -2:.
If you plan on joining the Sage-Combinat community, you may as well create at once a full featured .hgrc
6. Create a patch¶
Create a patch:
hg qnew trac_11299-fix_modform_element_qexp_documentation-nt.patch
Note
Often one starts instead by creating an empty patch, and then puts the modifications in there.
No changes are shown anymore by hg status or hg diff:
> **hg status**
> **hg diff**
Modifications are now in the patch. See hg qstatus or hg qdiff:
> **hg qstatus**
> **hg qdiff**
7. Update the current patch¶
Anytime one is happy with the current modifications, one may update the current patch with hg qrefresh to reflect the changes:
> **hg qrefresh**
After that, hg status and hg diff will report changes with respect to the last hg qrefresh.
8. Export a patch¶
When the bug is fixed, once we made sure every tests pass and that the documentation builds fine, then we can export the current patch. First we Add a commit message to the patch:
> **hg qrefresh -m "#11299: fix the documentation of ..."**
Export the patch with hg export:
> **hg export trac_11299-fix_modform_element_qexp_documentation-nt.patch > /tmp/trac_11299-fix_modform_element_qexp_documentation-nt.patch**
The command hg export also adds informations in the patch (author name, date, …).
Note
Personally, I added the following alias to my ~/.bashrc
:
alias qtoptotmp='hg export `hg qtop` > ~/Documents/tmp/`hg qtop`'
9. Verify the content the patch¶
Here is an example of a patch exported by Mercurial
for the ticket
#11299. It contains information about the author, the date, the
commit message we just wrote and finally the complete diff:
> **cat /tmp/trac_11299-fix_modform_element_qexp_documentation-nt.patch**
# HG changeset patch
# User Nicolas M. Thiery <nthiery@users.sf.net>
# Date 1304605845 10800
# Node ID deaba508575826bc715e019f77e7ce0d2bbe285c
# Parent 361a4ad7d52c69b64ae2e658ffd0820af0d87e93
#11299: Fix the documentation of modform_element.qexp
diff --git a/sage/modular/modform/element.py b/sage/modular/modform/element.py
--- a/sage/modular/modform/element.py
+++ b/sage/modular/modform/element.py
@@ -199,17 +199,17 @@ class ModularForm_abstract(ModuleElement
def qexp(self, prec=None):
"""
- Same as self.q_expansion(prec).
+ Same as ``self.q_expansion(prec)``.
- .. seealso: :meth:`q_expansion`
+ .. seealso:: :meth:`q_expansion`
EXAMPLES::
-
+
sage: CuspForms(1,12).0.qexp()
- q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 + O(q^6)
+ q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 + O(q^6)
"""
return self.q_expansion(prec)
-
+
def __eq__(self, other):
"""
10. Upload the patch on Sage trac¶
From the ticket page, upload the patch on Sage trac. You can mention things like “tested on sage-4.6.2” in the text box when uploading the ticket.
Make sure the patch was correctly uploaded by looking at it directly on the web page.
Set the ticket to needs review
You may ask somebody to review your ticket, typically by adding his trac login in the CC field.
11. More on Mercurial queues¶
Other useful Mercurial commands when managing several patches:
- hg qnew
- Create a new patch
- hg qnew
- …
- hg qpop
- Move a patch from the applied stack to the unapplied one
- hg qpush
- Move a patch from the unapplied stack to the applied one
- hg qtop
- Show the current patch
- hg qseries
- Print all of the patches in order
- hg qapplied
- Print the applied stack
- hg qunapplied
- Print the unapplied stack
- hg qdelete trac_65321-nice-feature-AA.patch
- Delete an (unapplied) patch from the queue
- hg log
Print the revision history of the specified files or the entire project:
> cd /opt/sage/ > hg log changeset: 15205:f24ce048fa66 tag: tip user: Jeroen Demeyer date: Tue Jan 11 08:10:26 2011 +0100 summary: 4.6.1 ... changeset: 0:039f6310c6fe user: tornaria date: Sat Feb 11 01:13:08 2006 +0000 summary: [project @ original sage-0.10.12]
- hg update …
- Update the repository’s working directory to the specified changeset.
12. Download a patch¶
Todo
move this just before uploading to trac
Todo
extract this to a tutorial “using someone’s else patch / reviewing a patch”?
A feature available on a Sage Trac ticket is of interest to you? You want to review a ticket?
Download a patch!
Insert a patch into the series after the last applied patch with hg qimport, and then apply it with hg qpush:
> hg qimport ~/Downloads/trac_65321-nice-feature-AA.patch
> hg qpush
Applying trac_65321-nice-feature-AA.patch
Currently at : trac_10056-new_oeis_address-tm.patch
Warning
Do NOT use the command hg import as it will import the changes in the current patch.
13. Edit the series file¶
You can change the order in which the patches are applied. To do so, simply edit the series file:
/opt/sage/devel/sage/.hg/patches/series
Make sure the patch you are reviewing is the first patch to be applied:
> cd /opt/sage/devel/sage/.hg/patches/
> cat series
trac_65321-nice-feature-AA.patch
A.patch
B.patch
C.patch
Warning
Patches might not commute, for example if they edit the exact same line. If conflicts occur after editing the series file and doing hg qpush, simply edit the series file and try again.
14. Reviewing a patch¶
Visit the Reviewing a patch Section of the Sage Developer’s Guide. Also, make sure you read William Stein’s blog post about reviewing a Sage trac ticket.
Make sure the patch applies on Sage without conflicts:
> hg qpush
Experiment the functionality proposed in the patch.
- Make sure the bug described in the ticket is fixed.
- Make sure the patch does not introduce any new bug.
Run tests on the affected files:
> sage -t <affected_files>
Test the entire Sage library.
> sage –testall –long
Ensure that the documentation builds fine:
> sage -docbuild reference html
Check for full 100% doctest coverage:
> sage -coverage <file>
Once you’ve tested the patch, report any failures on the Trac page for the ticket. Make suggestions about simplifying the code or fixing typos you noticed.
Note
The experimental Sage patch buildbot automatizes some of the steps.
15. Positive review or Needs work¶
Three cases may happen:
- Needs work
- If there is anything to do, describe it precisely in a comment, and change the status of the ticket to needs work.
- Positive review
- Otherwise, mark it as positive review, and mention in a comment all the things you checked.
- Delegate
If you feel unqualified for some aspects of the review, add a comment on the ticket explaining what you have checked, what the results were, and that you think someone more experienced should take a look at.
Feedback on tickets is always useful!
Note
In Sage, a negative review does not exist! There is always room for work and improvement!
16. Advanced tricks¶
19.1 Clone your version of Sage¶
- Clone Sage and create your branch (Do it right now because it might take some time)
sage -clone slabbe
This creates a new directory called sage-slabbe
in the devel
repository:
slabbe@pol /opt/sage/devel $ ls -l
drwxr-xr-x 2 slabbe staff 68 14 jan 03:59 old/
lrwxr-xr-x 1 slabbe staff 9 18 jan 15:01 sage -> sage-main/
drwxr-xr-x 23 slabbe staff 782 18 jan 01:42 sage-main/
drwxr-xr-x 24 slabbe staff 816 17 jan 01:50 sage-slabbe/
lrwxr-xr-x 1 slabbe staff 11 14 jan 03:42 sagenb -> sagenb-main/
drwxr-xr-x 21 slabbe staff 714 14 jan 03:41 sagenb-main/
cd to your branch:
> cd /opt/sage/devel/sage-slabbe
-
class
borderlesstable
Build the main branch | Build my branch slabbe | Print the current branch |
sage -b main |
sage -b slabbe |
sage -branch |
19.2. Do some cleaning¶
Delete an (unapplied) patch from the queue:
> hg qdelete trac_65321-nice-feature-AA.patch
Erase your branch. Of course, do this only if you don’t care about your local changes:
> sage -b main
> rm -rf /opt/sage/devel/sage-slabbe
References¶
Reviewing a Sage trac ticket, William Stein’s blog post, October 31, 2010.
This talk was generated
- by Docutils
- from ReStructuredText source
- to a Simple Standards-based Slide Show System (S5) format.
Mélodie Lapointe (lapointe.melodie@courrier.uqam.ca) and Pauline Hubert (hubert.pauline@courrier.uqam.ca)
Partitions¶
Recall that a partition \(\mu\) of \(n\), one writes \(\mu\vdash n\) or \(n=|\mu|\), is a sequence of integers \((\mu_0,\mu_1,\ldots, \mu_{k-1})\) (the \(m_i\)’s are the parts of \(\mu\)) with \(\mu_0\geq \mu_1\geq\,\cdots\,\geq\mu_{k-1}>0\), and \(n=\mu_0+\mu_1+\ldots+\mu_{k-1}\). The number \(\ell(\mu):=k\) of parts of \(\mu\) is said to be its length. A partition \(\mu\) may also be described as a Ferrers diagram, which is the \(n\)-subset of \(\mathbb{N}\times \mathbb{N}\):
This set is also denoted \(\mu\), and its elements are the cells of \(\mu\). The conjuguate of \(\mu\), is the partition \(\mu'\) such that
Parts of \(\mu\) are the lengths of the rows of its diagram, and parts of \(\mu'\) are the lengths of its columns.
For more, see https://en.wikipedia.org/wiki/Partition_(number_theory)
We mostly follow the notation conventions of Macdonald’s book: Symmetric Functions and Hall Polynomials, Second Edition, Oxford Mathematical Monographs, 1998.
Here are a few preliminary declarations just to make outputs nicer, and diagrams to printout with the French convention. Partitions parts are glued together, with parts of size larger that \(9\) ending with a “dot” so that there is no confusion.
[93]:
%display latex
def mystr(i):
if i<10:
return str(i)
else:
return ''.join([str(i),"."])
def compact(mu):
return (''.join(mystr(i) for i in mu))
Partition._latex_= compact
Handling partitions in SAGE¶
Partition can be created/declared in SAGE the following way:
[3]:
mu=Partition([10,10,2,2,1]); mu
[3]:
Nice format versus normal one¶
with \(\mu\) here printed out in a nicer format than the usual:
[4]:
print(Partition([10,10,2,2,1]))
[10, 10, 2, 2, 1]
Listing partitions of \(n\)¶
One can also list all partitions of a given integer.
[5]:
Partitions(4).list()
[5]:
Number of partitions¶
Or simply calculate the number of partitions of \(n\).
(We underline that this function does not actually generate the partitions of \(n\) in order to count them; hence it is amazingly fast.)
[6]:
Partitions(3000).cardinality()
[6]:
[7]:
print(Partitions(100000).cardinality())
27493510569775696512677516320986352688173429315980054758203125984302147328114964173055050741660736621590157844774296248940493063070200461792764493033510116079342457190155718943509725312466108452006369558934464248716828789832182345009262853831404597021307130674510624419227311238999702284408609370935531629697851569569892196108480158600569421098519
Partitions with constraints¶
One may add constraints on partitions; for instance, to get partitions of 5 of length 2:
[8]:
p = Partitions(5,length=2)
p.list()
[8]:
or get all partitions of 6 with length between 3 and 5.
[14]:
p = Partitions(6,min_length=3,max_length=5)
p.list()
[14]:
Ferrers diagram¶
By default SAGE uses the English convention, but it has become the tradition in recent years to use the more natural (cartesian coordinates) French notation. Here is how to set this
[33]:
Partitions.options.convention="french"
[32]:
mu = Partition([8,5,5,5,4,3,3,2])
print(mu.ferrers_diagram())
********
*****
*****
*****
****
***
***
**
Cells¶
The list of cells of \(m\) my be obtained as follows
[34]:
mu.cells()
[34]:
and printed out in nice format as
[36]:
map(compact,mu.cells())
[36]:
If one insists on using the English convention, rather than akwardly reading this notebook “… upside down, in a mirror …” (as Macdonald would say), one could globally switch back as follows
[31]:
Partitions.options(convention='english')
print(mu.ferrers_diagram())
********
*****
*****
*****
****
***
***
**
Partition containment¶
A partition \(\mu\) is said to be included in a partition \(\lambda\) if \(\mu_i \leq \lambda_i\), for all \(i\). In other words, the diagram of \(\mu\) is a subset of the diagram of \(\lambda\). For example, one can list all partitions \(\lambda\) of \(5\) such that the partition \([2,1]\) is included in \(\lambda\).
[26]:
p = Partitions(5,inner= [2,1])
p.list()
[26]:
Or all partitions of 5 included in the partition \([4,3,2,1]\).
[27]:
p = Partitions(5,outer=[4,3,2,1])
p.list()
[27]:
The default (total) order on partitions is the lexicographic order.
[29]:
mu = Partition([4,3,3])
nu = Partition([4,4,1])
mu < nu
[29]:
Exercise:¶
[83]:
q=var('q')
mu=[15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]
sum(Partitions(i,outer=mu).cardinality()*q^i for i in range(20))
[83]:
Young Tableaux¶
An \(A\)-valued Young tableaux of shape \(\mu\) is a “filling” the cells of the Ferrers diagram of \(\mu\) with elements of an ordered set \(A\). Hence it is a function \(\tau:\mu\rightarrow A\). A tableau is said to standard if it is bijective (hence \(A\) has cardinality equal to the number of cells of \(\mu\)), and its entries on each row (and each column) are stricly increasing from left to right (from bottom to top in french convention). A tableau (not necessarily bijective) is said to be semistandard if its entries are weakly increasing from left to right on each row, and strictly increasing on each column. These object may be constructed in the following way.
[38]:
t = SemistandardTableau([[1,2,4],[3,3,6],[5,7],[8]])
t.pp()
print('')
s = StandardTableau([[1,2,4],[3,6],[5,7],[8]])
s.pp()
8
5 7
3 3 6
1 2 4
8
5 7
3 6
1 2 4
The function pp(\(\ \)) (“pp” stands for pretty print) gives a nicer display for Young tableaux. Observe that if you set options (like French vs English convention) for partitions, these will also apply to Young tableaux.
It is also possible to list all semistandard and standard Young tableaux of a given partition.
[39]:
x = SemistandardTableaux([4,3,3,2,1])
print(x.cardinality())
y = StandardTableaux([4,3,3,2,1])
print(y.cardinality())
390780390
15015
The functions for partitions, such as display, options, cardinality, and list, may also be used on Young tableaux.
Exercise:¶
Verify that the number of standard Young tableaux of shape :math:`[n,n]` is equal to the Catalan number for :math:`0 leq n leq 20`. (The function catalan_number(:math:`n`) returns the nth catalan number).
[40]:
all(catalan_number(i)==StandardTableaux([i,i]).cardinality() for i in range(1,10))
[40]:
Exercise:¶
Compute the sum of all monomials of degree 5 in three variables using partitions and standard tableaux.
[41]:
var('x y z')
young_tableaux = []
monomials = []
for i in Partitions(5).list():
young_tableaux.extend(SemistandardTableaux(i,max_entry=3).list())
for j in young_tableaux:
k = reduce(operator.add,j)
monomials.append(x^k.count(1)*y^k.count(2)*z^k.count(3))
show(sum(monomials))
Hook formula for the number of standard tableaux of shape \(\mu\)¶
The classical hook formula
[47]:
def hook_formula(mu):
mu=Partition(mu)
return factorial(add(k for k in mu))/prod(mu.hook_length(i,j) for i,j in mu.cells())
[48]:
hook_formula(Partition([4,3,1,1]))
[48]:
Here are some functions on partitions often used in the theory of Macdonald polynomials¶
(Sometimes one may want a more functional notation for combinatorial calculations on diagrams.)
where \(a(c)\) et \(l(c)\) respectively denote the arm and the leg of a cell \(c\) in \(\mu\). We have
[114]:
q,t=var('q,t')
def ell(mu):
mu=Partition(mu)
return mu.length()
def arm(c,mu):
mu=Partition(mu)
return mu.arm_length(c[0],c[1])
def leg(c,mu):
mu=Partition(mu)
return mu.leg_length(c[0],c[1])
def corner_cells(mu):
mu=Partition(mu)
return mu.corners()
def zee(mu):
mu=Partition(mu)
return mu.aut()
def n(mu): return add(i for i,j in mu.cells())
def B(mu):
return add(t^i*q^j for i,j in mu.cells())
def T(mu):
return prod(t^i*q^j for i,j in mu.cells())
def Pi(mu):
return prod(1-t^i*q^j for i,j in mu.cells() if [i,j]<>[0,0])
def epsilon(mu):
return prod((q^(arm(c,mu))-t^(1+leg(c,mu)))*(t^(leg(c,mu))-q^(1+arm(c,mu))) for c in mu.cells())
[115]:
mu=Partition([4,3,3,1,1])
[116]:
vector([n(mu),n(mu.conjugate())])== add(vector(c) for c in mu.cells())
[116]:
[117]:
B(mu)
[117]:
[118]:
Pi(mu)
[118]:
[119]:
k=var('k')
factor(add(k^ell(mu)/zee(mu) for mu in Partitions(6)))
[119]:
[120]:
factor(add((-k)^ell(mu)/zee(mu) for mu in Partitions(6)))
[120]:
[ ]:
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Tutorial: Testing a conjecture in parallel (draft)¶
In this tutorial, we illustrate how to test a conjecture in parallel
on a multicore machine using the @parallel
decorator.
See also
sage.parallel
Todo
expand and move to sage.parallel.tutorial?
For illustration purpose, we take a stupid conjecture, namely that the number \(n=49\) has no divisor in the range \(2,...,9\). Let us start with a little function that checks the conjecture on a given \(n\) and \(i\):
sage: def check_conjecture(i, n):
....: return not i.divides(n)
Shoot, the conjecture is false:
sage: n = 49
sage: all( check_conjecture(i, n) for i in srange(2,10) )
False
We can for example recover the counter example with exists()
:
sage: n = 49
sage: exists( ((i,n) for i in srange(2,10)),
....: lambda (i,n): not check_conjecture(i,n))
(True, (7, 49))
We want to find a counter-example in parallel. To this end, we define
a variant of check_conjecture
that checks the conjecture in
parallel on a bunch of inputs:
sage: @parallel
....: def check_par(i, n):
....: return not i.divides(n)
Here is how we can use it to test the conjecture in parallel for three
pairs (i, n)
:
sage: list( check_par( [ (2,11), (3, 9), (4,7) ] ) )
[(((2, 11), {}), True), (((3, 9), {}), False), (((4, 7), {}), True)]
Each output is of the form (input, result)
, where input
describes the arguments and optional arguments passed down to the
python function. We now run the check for \(n=49\) and the range
2,…,9:
sage: n = 49
sage: sorted(check_par( (i,n) for i in srange(2,10) ))
[(((2, 49), {}), True), (((3, 49), {}), True), (((4, 49), {}), True), (((5, 49), {}), True), (((6, 49), {}), True), (((7, 49), {}), False), (((8, 49), {}), True), (((9, 49), {}), True)]
If we just want to know whether \(n\) has no divisor in the range, we can do:
sage: all( result for (input, result) in check_par( (i,n) for i in srange(2,10) ))
Killing any remaining workers...
False
Note the information message just before the answer: the computation was stopped as soon as a counter-example was found.
Let us print all counter-examples:
sage: for (input, result) in check_par( (i,n) for i in srange(2,10) ):
....: if not result: print input
((7, 49), {})
Here is a powerful idiom to recover a counter-example:
sage: for (input, result) in check_par( (i,n) for i in srange(2,10) ):
....: assert result
Traceback (most recent call last):
...
AssertionError
There is no output if there is no counter-example. Otherwise, an error is thrown:
sage: for (input, result) in check_par( (i,n) for i in srange(2,10) ):
....: assert result
Traceback (most recent call last):
...
AssertionError
Then one can use the Python debugger to recover the counter-example by post mortem introspection on the stack:
sage: import pdb
sage: pdb.pm() # not tested
> <ipython console>(2)<module>()
(Pdb) p input
((7, 49), {})
This may sound a bit of an overkill, but this is a very general technique, and it is handy when the input is a complicated object that we want to explore. In particular, one can use the following hack to insert the counter-example in the global name space:
sage: pdb.pm() # not tested
> <ipython console>(2)<module>()
(Pdb) p input[0][0]
7
(Pdb) import __main__
(Pdb) __main__.my_counter_example = input[0][0]
Now we can play with it:
sage: my_counter_example # not tested
7
sage: my_counter_example.divides(49) # not tested
True
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Partitions and Young tableaux tutorial¶
Mélodie Lapointe (lapointe.melodie@courrier.uqam.ca) and Pauline Hubert (hubert.pauline@courrier.uqam.ca)
Partitions¶
Recall that a partition \(\mu\) of \(n\), one writes \(\mu \vdash n\) or \(n = |\mu|\), is an sequence of intergers \((\mu_0,\mu_1,\dots,\mu_{k-1})\) (the \(m_i\)’s are the parts of \(\mu\)) with \(\mu_0 \geq \mu_1 \geq \dots \geq \mu_{k-1} \geq 0\) and \(n = \mu_0 + \mu_1 + \dots + \mu_{k-1}\). The number \(\ell(\mu):= k\) of parts of \(\mu\) is said to be its length. A partition \(\mu\) may also be described as a Ferrers diagram, which is the \(n\)-subset of \(\mathbb{N}\times \mathbb{N}\) :
This set is also denoted \(\mu\), and its elements are the cells of \(\mu\). The conjugate of \(\mu\), is the partition \(\mu'\) such that
Parts of \(\mu\) are the lengths of the rows of its diagram, and parts of \(\mu\) are the lengths of its columns.
For more, see https://en.wikipedia.org/wiki/Partition_(number_theory)
We mostly follow the notation convention of Macdonald’s book: Symmetric Functions and Hall Polynomials, Second Edition, Oxford Mathematical Monographs, 1998.
Partition can be created/declared in SAGE the following way:
sage: Partition([10,10,5,2,2,1])
Listing partitions of \(n\)¶
One can also list all partitions of a given integer.
sage: Partitions(4).list()
Number of partitions¶
Or simply count them.
(We underline that this function does not actually generate the partitions of \(n\) in order to count them; hence it is amazingly fast.)
sage: Partitions(3000).cardinality()
sage: Partitions(100000).cardinality()
Partitions with constraints¶
One may add constraints on partitions; for instance, to get partitions of 5 of length 2.
sage: p = Partitions(5,length=2)
sage: p.list()
or get all partitions of 6 with length between 3 and 5.
sage: p = Partitions(6,min_length=3,max_length=5)
sage: p.list()
Ferrers diagram¶
By default SAGE uses the English convention, but it has become the tradition in recent years to use the more natural (cartesian coordinates) French notation. Here is how to set this
sage: Partitions.options(convention='french')
sage: mu = Partition([8,5,5,5,4,3,3,2])
sage: print(mu.ferrers_diagram())
Cells¶
The list of cells of \(m\) may be obtained as follows
sage: mu.cells()
If one insists on using the English convention, one could globally switch back as follows
sage: Partitions.options(convention='english')
sage: print(mu.ferrers_diagram())
Partition containment¶
A partition \(\mu\) is included in a partition \(\lambda\) if \(\mu_i \leq \lambda_i, \forall i\). In other words, the diagram of \(\mu\) is a subset of the diagram of \(\lambda\). For example, one can list all partitions \(\lambda\) of 5 such that the partition \([2,1]\) is included in \(\lambda\).
sage: p = Partitions(5,inner= [2,1])
sage: p.list()
Or all partitions of 5 included in the partition \([4,3,2,1]\).
sage: p = Partitions(5,outer=[4,3,2,1])
sage: p.list()
The default (total) order on partitions is the lexicographic order.
sage: mu = Partition([4,3,3])
sage: nu = Partition([4,4,1])
sage: mu < nu
*Exercise:*
Let \(\lambda\) be the partition \([15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]\). Compute:
sage: q = var('q')
sage: mu = [15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]
sage: show(sum(Partitions(i,outer=mu).cardinality()*q^i for i in range(20)))
Young Tableaux¶
An A-valued Young tableaux of shape \(\mu\) is a “filling” of the cells of a Ferrers diagram of \(\mu\) with elements of an ordered set A. Hence, it is a function \(\tau:\mu \rightarrow A\). A tableau is said to be standard if it is bijective (hence A has cardinality equal to the number of cells of \(\mu\)), and its entries on each row (and each column) are stricly increasing from left to right (from bottom to top in french convention). A tableau (not necessarily bijective) is said to be semistandard if its entries are weakly increasing from left to right on each row, and strictly increasing on each column. These object can be constructed in the following way.
sage: t = SemistandardTableau([[1,2,4],[3,3,6],[5,7],[8]])
sage: t.pp()
sage: print('')
sage: s = StandardTableau([[1,2,4],[3,6],[5,7],[8]])
sage: s.pp()
The function pp() (“pp” stands for pretty print) gives a nicer display for Young tableaux. Observe that if you set options (like French vs English convention) for partitions, these will also apply to Young tableaux.
It is possible to list all semistandard and standard Young tableaux of a given partition.
sage: x = SemistandardTableaux([4,3,3,2,1])
sage: print(x.cardinality())
sage: y = StandardTableaux([4,3,3,2,1])
sage: print(y.cardinality())
The functions for partitions, such as display, options, cardinality, and list, are also found in Young tableaux.
*Exercise:*
Verify that the number of standard Young tableaux of shape :math:`[n,n]` is equal to the Catalan number for :math:`0 leq n leq 20`. (The function catalan_number(:math:`n`) returns the nth catalan number).
sage: all(catalan_number(i)==StandardTableaux([i,i]).cardinality() for i in range(1,10))
*Exercise:*
Compute the sum of all monomials of degree 5 in three variables using partitions and standard tableaux.
sage: var('x y z')
sage: young_tableaux = []
sage: monomials = []
sage: for i in Partitions(5).list():
sage: young_tableaux.extend(SemistandardTableaux(i,max_entry=3).list())
sage: for j in young_tableaux:
sage: k = reduce(operator.add,j)
sage: monomials.append(x^k.count(1)*y^k.count(2)*z^k.count(3))
sage: show(sum(monomials))
Hook formula for the number of standard tableaux of shape \(mu\)¶
The classical hook formula
with \(h((i,j),\mu) := \mu_i + \mu'_j -i -j - 1\), may be coded as
sage: def hook_formula(mu):
sage: return factorial(add(k for k in mu))/prod(mu.hook_length(i,j) for i,j in mu.cells())
sage: hook_formula(Partition([4,3,1,1]))
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Start here!¶
About SageMath and this document¶
SageMath (Sage
for short) is a general
purpose computational mathematics system developed by a worldwide
community of hundreds of researchers, teachers and engineers. It’s
based on the Python programming language and includes GAP, PARI/GP,
Singular, and dozens of other specialized libraries.
This live document will guide you through the first steps of using Sage, and provide pointers to explore and learn further.
In the following, we will be assuming that you are reading this
document as a Jupyter notebook (Jupyter is the primary user interface
for Sage). If instead you are reading this document as a web page, you can click
on Run on mybinder.org
to get access to the notebook online. If
you have Sage already installed on your machine, you may instead
download this page As Jupyter notebook
. If you just want to try
out a few things, you may also just click the Activate
button on
the upper right corner to play with the examples.
Todo
- credits on the many sources of inspiration
Entering, editing, and evaluating input¶
A first calculation¶
Sage can be used as a pocket calculator: you type in some expression
to be calculated, Sage evaluates it, and prints the result; and
repeat. This is called the Read-Eval-Print-Loop. In the Jupyter
notebook, you type the expression in an input cell, or code
cell. This is the rectangle below this paragraph containing \(1+1\)
(if instead you see sage: 1+1
, you are reading this document as a
web page and won’t be able to play with the examples). Click on the
cell to select it, and press shift-enter
to evaluate it:
sage: 1 + 1
2
You may instead click the play button in the tool bar or use the
Cell
menu:

Sage prints out its response in an output cell just below the
input cell (that’s the 2
, so Sage confirms that \(1+1=2\)).
Click again in the cell, replace \(1+1\) by \(2+2\),
and evaluate it. Notice how much quicker it is now? That’s because a
Sage process had to be started the first time, and then stayed ready.
Congratulations, you have done your first calculations with Sage
.
Using the Jupyter Notebook¶
Now take some time to explore the Help
menu. We specifically
recommend taking the User Interface Tour
, and coming back to
Keyboard shortcuts
every now and then to get fast at Jupyter.
The Jupyter developers also maintain a tutorial notebook
which you may find useful.
For now we just review the basics. Use the menu item Insert ->
Insert Cell Below
to create a new input cell below this paragraph,
then calculate any simple expression of your liking.
You can move around and edit any cell by clicking in it. Go back and change your \(2+2\) above to \(3+3\) and re-evaluate it.
You can also edit any text cell by double clicking on it. Try it now! The text you see is using the Markdown markup language. Do some changes to the text, and evaluate it again to rerender it. Markdown supports a fair amount of basic formatting, such as bold, underline, basic lists, and so forth. Thanks to the TeX rendering engine MathJax, you may embed mathematical formulae such as $sin(x) - y^3$ just like with LaTeX. It can be fun to type in fairly complicated math, like this:
If you mess everything up, you can use the menu Kernel ->
Restart
to restart Sage. You can also use the menu File -> Save and
Checkpoint
to save notebook, and File -> Revert to Checkpoint -> ...
to reset to any previously saved version.
More interactions¶
We are now done with basic interaction with Sage. Much richer
interactions are possible thanks to Jupyter’s interactive widgets.
That will be the topic for a later tutorial; here is just a teaser for
now. Try clicking on the sliders to illustrate multiplication below.
Also, you can try changing the slider ranges to something different by
editing the input cell (make sure to also change xmax
, ymax
):
sage: @interact # not tested
....: def f(n=(1 .. 15), m=(1 .. 15)):
....: print("n * m = {} = {}".format(n * m, factor(n * m)))
....: P = polygon([(0, 0), (0, n), (m, n), (m, 0)])
....: P.show(aspect_ratio=1, gridlines='minor', figsize=[3, 3], xmax=14, ymax=14)
A brief tour¶
Sage covers many areas of mathematics:
2D/3D Graphics, Categories, Basic Rings and Fields: Integers and Rational Numbers, Real and Complex Numbers, Finite Rings and Fields, Polynomials, Formal Power Series, p-Adic Numbers, Quaternion Algebras, Linear Algebra: Matrices and Spaces of Matrices, Vectors and Modules, Tensors on Free Modules of Finite Rank, Calculus and Analysis: Symbolic Calculus, Mathematical Constants, Elementary and Special Functions, Asymptotic Expansions, Numerical Optimization, Probability and Statistics, Probability , Statistics, Quantitative Finance, Mathematical Structures: Sets, Monoids, Groups, Semirings, Rings, Algebras, Discrete Mathematics Combinatorics, Graph Theory, Quivers, Matroid Theory, Discrete Dynamics, Coding Theory, Cryptography, Game Theory, Symbolic Logic, SAT solvers, Geometry and Topology: Combinatorial and Discrete Geometry, Hyperbolic Geometry, Cell Complexes and their Homology, Differential Forms, Manifolds, Parametrized Surfaces, Knot Theory, Number Fields and Function Fields, Number Theory: Diophantine approximation, Quadratic Forms, L-Functions, Arithmetic Subgroups of \(SL_2(Z)\), General Hecke Algebras and Hecke Modules, Modular Symbols, Modular Forms, Modular Forms for Hecke Triangle Groups, Modular Abelian Varieties, Algebraic and Arithmetic Geometry: Schemes, Plane, Elliptic and Hyperelliptic Curves, Databases, Games
We now engage in a brief tour.
Todo
- Better formatting of the above list of areas, with links to relevant pieces of the documentation.
- Insert more striking examples (including Sage-Manifolds!)
- Insert Read More links
Calculus¶
sage: %display latex
sage: x,y = var('x,y')
sage: f = (cos(pi/4-x)-tan(x)) / (1-sin(pi/4 + x)); f
-(cos(1/4*pi - x) - tan(x))/(sin(1/4*pi + x) - 1)
sage: limit(f, x = pi/4, dir='minus')
+Infinity
sage: solve([x^2+y^2 == 1, y^2 == x^3 + x + 1], x, y)
[[x == -1/2*I*sqrt(3) - 1/2, y == -sqrt(-1/2*I*sqrt(3) + 3/2)],
[x == -1/2*I*sqrt(3) - 1/2, y == sqrt(-1/2*I*sqrt(3) + 3/2)],
[x == 1/2*I*sqrt(3) - 1/2, y == -sqrt(1/2*I*sqrt(3) + 3/2)],
[x == 1/2*I*sqrt(3) - 1/2, y == sqrt(1/2*I*sqrt(3) + 3/2)],
[x == 0, y == -1], [x == 0, y == 1]]
sage: plot3d(sin(pi*sqrt(x^2+y^2)) / sqrt(x^2+y^2), (x,-5,5), (y,-5,5), viewer="threejs")
Graphics3d Object
sage: contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi),
....: contours=[-8,-4,0,4,8], colorbar=True)
Graphics object consisting of 1 graphics primitive
Algebra¶
sage: factor(x^100 - 1)
(x^40 - x^30 + x^20 - x^10 + 1)*(x^20 + x^15 + x^10 + x^5 + 1)*(x^20 - x^15 + x^10 - x^5 + 1)*(x^8 - x^6 + x^4 - x^2 + 1)*(x^4 + x^3 + x^2 + x + 1)*(x^4 - x^3 + x^2 - x + 1)*(x^2 + 1)*(x + 1)*(x - 1)
sage: p = 54*x^4+36*x^3-102*x^2-72*x-12
sage: p.factor()
6*(x^2 - 2)*(3*x + 1)^2
sage: for K in [ZZ, QQ, ComplexField(16), QQ[sqrt(2)], GF(5)]:
....: print(K, ":"); print(K['x'](p).factor())
Integer Ring :
2 * 3 * (3*x + 1)^2 * (x^2 - 2)
Rational Field :
(54) * (x + 1/3)^2 * (x^2 - 2)
Complex Field with 16 bits of precision :
(54.00) * (x - 1.414) * (x + 0.3333)^2 * (x + 1.414)
Number Field in sqrt2 with defining polynomial x^2 - 2 :
(54) * (x - sqrt2) * (x + sqrt2) * (x + 1/3)^2
Finite Field of size 5 :
(4) * (x + 2)^2 * (x^2 + 3)
sage: ZZ.category()
Join of Category of euclidean domains and Category of infinite enumerated sets and Category of metric spaces
sage: sorted( ZZ.category().axioms() )
['AdditiveAssociative', 'AdditiveCommutative', 'AdditiveInverse', 'AdditiveUnital',
'Associative', 'Commutative', 'Distributive',
'Enumerated', 'Infinite',
'NoZeroDivisors',
'Unital']
Linear algebra¶
sage: A = matrix(GF(7), 4, [5,5,4,3,0,3,3,4,0,1,5,4,6,0,6,3]); A
[5 5 4 3]
[0 3 3 4]
[0 1 5 4]
[6 0 6 3]
sage: P = A.characteristic_polynomial(); P
x^4 + 5*x^3 + 6*x + 2
sage: P(A)
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
sage: A.eigenspaces_left()
[
(4, Vector space of degree 4 and dimension 1 over Finite Field of size 7
User basis matrix:
[1 4 6 1]),
(1, Vector space of degree 4 and dimension 1 over Finite Field of size 7
User basis matrix:
[1 3 3 4]),
(2, Vector space of degree 4 and dimension 2 over Finite Field of size 7
User basis matrix:
[1 0 2 3]
[0 1 6 0])
]
Computing the rank of a large sparse matrix:
sage: M = random_matrix(GF(7), 10000, sparse=True, density=3/10000)
sage: M.rank() # random
9263
Geometry¶
sage: polytopes.truncated_icosidodecahedron().plot(viewer="threejs")
Graphics3d Object
Programming and plotting¶
sage: n, l, x, y = 10000, 1, 0, 0
sage: p = [[0, 0]]
sage: for k in range(n):
....: theta = (2 * pi * random()).n(digits=5)
....: x, y = x + l * cos(theta), y + l * sin(theta)
....: p.append([x, y])
sage: g = line(p, thickness=.4) + line([p[n], [0, 0]], color='red', thickness=2)
sage: g.show(aspect_ratio=1)
Interactive plots¶
sage: x = var('x')
sage: @interact # not tested
....: def g(f=x*sin(1/x),
....: c=slider(-1, 1, .01, default=-.5),
....: n=(1..30),
....: xinterval=range_slider(-1, 1, .1, default=(-8,8), label="x-interval"),
....: yinterval=range_slider(-1, 1, .1, default=(-3,3), label="y-interval")):
....: x0 = c
....: degree = n
....: xmin,xmax = xinterval
....: ymin,ymax = yinterval
....: p = plot(f, xmin, xmax, thickness=4)
....: dot = point((x0,f(x=x0)),pointsize=80,rgbcolor=(1,0,0))
....: ft = f.taylor(x,x0,degree)
....: pt = plot(ft, xmin, xmax, color='red', thickness=2, fill=f)
....: show(dot + p + pt, ymin=ymin, ymax=ymax, xmin=xmin, xmax=xmax)
....: html('$f(x)\;=\;%s$'%latex(f))
....: html('$P_{%s}(x)\;=\;%s+R_{%s}(x)$'%(degree,latex(ft),degree))
Graph Theory¶
Coloring graphs:
sage: g = graphs.PetersenGraph(); g
Petersen graph: Graph on 10 vertices
sage: g.plot(partition=g.coloring())
Graphics object consisting of 26 graphics primitives
Combinatorics¶
Fast counting:
sage: Partitions(100000).cardinality()
27493510569775696512677516320986352688173429315980054758203125984302147328114964173055050741660736621590157844774296248940493063070200461792764493033510116079342457190155718943509725312466108452006369558934464248716828789832182345009262853831404597021307130674510624419227311238999702284408609370935531629697851569569892196108480158600569421098519
Playing poker:
sage: Suits = Set(["Hearts", "Diamonds", "Spades", "Clubs"])
sage: Values = Set([2, 3, 4, 5, 6, 7, 8, 9, 10, "Jack", "Queen", "King", "Ace"])
sage: Cards = cartesian_product([Values, Suits])
sage: Hands = Subsets(Cards, 5)
sage: Hands.random_element() # random
{(5, 'Pique'), (4, 'Coeur'), (8, 'Trefle'), ('As', 'Trefle'), (10, 'Carreau')}
sage: Hands.cardinality()
2598960
Algebraic Combinatorics¶
Drawing an affine root systems:
sage: L = RootSystem(["G", 2, 1]).ambient_space()
sage: p = L.plot(affine=False, level=1)
sage: p.show(aspect_ratio=[1, 1, 2], frame=False)
Number Theory¶
sage: E = EllipticCurve('389a')
sage: plot(E, thickness=3)
Graphics object consisting of 2 graphics primitives
Games¶
Sudoku solver:
sage: S = Sudoku('5...8..49...5...3..673....115..........2.8..........187....415..3...2...49..5...3'); S
+-----+-----+-----+
|5 | 8 | 4 9|
| |5 | 3 |
| 6 7|3 | 1|
+-----+-----+-----+
|1 5 | | |
| |2 8| |
| | | 1 8|
+-----+-----+-----+
|7 | 4|1 5 |
| 3 | 2| |
|4 9 | 5 | 3|
+-----+-----+-----+
sage: list(S.solve())
[+-----+-----+-----+
|5 1 3|6 8 7|2 4 9|
|8 4 9|5 2 1|6 3 7|
|2 6 7|3 4 9|5 8 1|
+-----+-----+-----+
|1 5 8|4 6 3|9 7 2|
|9 7 4|2 1 8|3 6 5|
|3 2 6|7 9 5|4 1 8|
+-----+-----+-----+
|7 8 2|9 3 4|1 5 6|
|6 3 5|1 7 2|8 9 4|
|4 9 1|8 5 6|7 2 3|
+-----+-----+-----+]
Help system¶
We review the three main ways to get help in Sage:
- navigating through the documentation,
- tab-completion,
- contextual help.
Completion and contextual documentation¶
Start typing something and press the Tab
key. The interface tries to
complete it with a command name. If there is more than one completion, then
they are all presented to you. Remember that Sage is case sensitive, i.e. it
differentiates upper case from lower case. Hence the Tab
completion of
klein
won’t show you the KleinFourGroup
command that builds the group
\(\ZZ/2 \times \ZZ/2\) as a permutation group. Try pressing the Tab
key in the following cells:
sage: klein
sage: Klein
To see documentation and examples for a command, type a question mark
?
at the end of the command name and evaluate the cell:
sage: KleinFourGroup?
sage:
Exercise A
What is the largest prime factor of \(600851475143\)?
sage: factor?
sage:
Digression: assignments and methods¶
In the above manipulations we did not store any data for
later use. This can be done in Sage with the =
symbol as in:
sage: a = 3
sage: b = 2
sage: a + b
5
This can be understood as Sage evaluating the expression to the right
of the =
sign and creating the appropriate object, and then
associating that object with a label, given by the left-hand side (see
the foreword of Tutorial: Objects and Classes in Python and Sage for
details). Multiple assignments can be done at once:
sage: a, b = 2, 3
sage: a
2
sage: b
3
This allows us to swap the values of two variables directly:
sage: a, b = 2, 3
sage: a, b = b, a
sage: a, b
(3, 2)
We can also assign a common value to several variables simultaneously:
sage: c = d = 1
sage: c, d
(1, 1)
sage: d = 2
sage: c, d
(1, 2)
Note that when we use the word variable in the computer-science sense we mean “a label attached to some data stored by Sage”. Once an object is created, some methods apply to it. This means functions but instead of writing f(my_object) you write my_object.f():
sage: p = 17
sage: p.is_prime()
True
See Tutorial: Objects and Classes in Python and Sage for details.
Method discovery with tab-completion¶
Todo
Replace the examples below by less specialized ones
To know all methods of an object you can once more use tab-completion.
Write the name of the object followed by a dot and then press Tab
:
sage: a.
Exercise B
Create the permutation 51324 and assign it to the variable p
.
sage: Permutation?
sage:
What is the inverse
of p
?
sage: p.inv
sage:
Does p
have the pattern
123? What about 1234? And 312? (even if you don’t
know what a pattern is, you should be able to find a command that does this).
sage: p.pat
sage:
Exercises¶
Linear algebra¶
Exercise C
Use the matrix()
command to create the following matrix.
sage: matrix?
sage:
Then, using methods of the matrix,
- Compute the determinant of the matrix.
- Compute the echelon form of the matrix.
- Compute the eigenvalues of the matrix.
- Compute the kernel of the matrix.
- Compute the LLL decomposition of the matrix (and lookup the documentation for what LLL is if needed!)
sage:
sage:
Now that you know how to access the different methods of matrices,
- Create the vector \(v = (1, -1, -1, 1)\).
- Compute the two products: \(M \cdot v\) and \(v \cdot M\). What mathematically borderline operation is Sage doing implicitly?
sage: vector?
sage:
Note
Vectors in Sage can be used as row vectors or column vectors.
A method such as eigenspaces
might not
return what you expect, so it is best to specify eigenspaces_left
or
eigenspaces_right
instead. Same thing for kernel (left_kernel
or
right_kernel
), and so on.
Plotting¶
The plot()
command allows you to draw plots of functions. Recall
that you can access the documentation by pressing the Tab
key
after writing plot?
in a cell:
sage: plot?
sage:
Here is a simple example:
sage: var('x') # make sure x is a symbolic variable
x
sage: plot(sin(x^2), (x, 0, 10))
Graphics object consisting of 1 graphics primitive
Here is a more complicated plot. Try to change every single input to the plot command in some way, evaluating to see what happens:
sage: P = plot(sin(x^2), (x, -2, 2), rgbcolor=(0.8, 0, 0.2), thickness=3, linestyle='--', fill='axis')
sage: show(P, gridlines=True)
Above we used the show()
command to show a plot after it was created. You can
also use P.show
instead:
sage: P.show(gridlines=True)
Try putting the cursor right after P.show(
and pressing Tab
to get a list of
the options for how you can change the values of the given inputs.
sage: P.show(
Plotting multiple functions at once is as easy as adding the plots together:
sage: P1 = plot(sin(x), (x, 0, 2*pi))
sage: P2 = plot(cos(x), (x, 0, 2*pi), rgbcolor='red')
sage: P1 + P2
Graphics object consisting of 2 graphics primitives
Symbolic Expressions¶
Here is an example of a symbolic function:
sage: f(x) = x^4 - 8*x^2 - 3*x + 2
sage: f(x)
x^4 - 8*x^2 - 3*x + 2
sage: f(-3)
20
This is an example of a function in the mathematical variable \(x\). When Sage starts, it defines the symbol \(x\) to be a mathematical variable. If you want to use other symbols for variables, you must define them first:
sage: x^2
x^2
sage: u + v
Traceback (most recent call last):
...
NameError: name 'u' is not defined
sage: var('u v')
(u, v)
sage: u + v
u + v
Still, it is possible to define symbolic functions without first defining their variables:
sage: f(w) = w^2
sage: f(3)
9
In this case those variables are defined implicitly:
sage: w
w
Exercise D
Define the symbolic function \(f(x) = x \sin(x^2)\). Plot \(f\) on the
domain \([-3, 3]\) and color it red. Use the find_root()
method to
numerically approximate the root of \(f\) on the interval \([1, 2]\):
sage:
Compute the tangent line to \(f\) at \(x = 1\):
sage:
Plot \(f\) and the tangent line to \(f\) at \(x = 1\) in one image:
sage:
Exercise E (Advanced)
Solve the following equation for \(y\):
There are two solutions, take the one for which \(\lim_{x\to0} y(x) = 1\). (Don’t forget to create the variables \(x\) and \(y\)!).
sage:
Expand \(y\) as a truncated Taylor series around \(0\) containing \(n = 10\) terms.
sage:
Do you recognize the coefficients of the Taylor series expansion? You might
want to use the On-Line Encyclopedia of Integer Sequences, or better yet, Sage’s class OEIS
which
queries the encyclopedia:
sage: oeis?
sage:
Congratulations for completing your first Sage tutorial!
Exploring further¶
Accessing Sage¶
The Sage cell service lets you evaluate individual Sage commands.
In general, Sage computations can be embedded in any web page using Thebelab or the Sage-cell server.
Binder is a service that lets you run Jupyter online on top of an arbitrary software stack. Sessions are free, anonymous, and temporary. You can use one of the existing repositories, or create your own.
Todo
add links about both
Cocalc (Collaborative Calculation in the Cloud) is an online service that gives access to a wealth of computational systems, including Sage, with extra goodies for teaching. It’s free for basic usage.
JupyterHub lets you (or your institution or …) deploy a multi-user Jupyter service.
The Sage Debian Live USB key let’s you run Linux with Sage and many other goodies on your computer without having to install them.
Sage can be installed on most major operating systems (Linux, macOS, Windows), through usual package managers or installers, or by compiling from source.
Ways to use Sage¶
There are many ways beyond the Jupyter Notebook to use Sage: interactive command line, program scripts, … See the Sage tutorial.
Note
Sage used to have its own legacy notebook system, which has been phased out in favor of Jupyter. If you have old notebooks, here is how to migrate them.
Resources¶
- Sage’s web page: https://www.sagemath.org
- Ask Sage: https://ask.sagemath.org
- Bug Tracker: https://trac.sagemath.org
- The open book Computational Mathematics with Sage (originally written in French; also translated in German)
- Sage’s main tutorial
- Sage’s official thematic tutorials
- More Sage tutorials
- Sage’s quick reference cards
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Symmetric Functions Tutorial¶
The aim of this tutorial is to present what it is possible to do in Sage on symmetric functions. We suppose that the reader knows the basics about symmetric functions.
Caveat: in this tutorial, the term symmetric “functions” will mostly stand for “abstract” symmetric polynomials, in which variables are not made explicit. Indeed for most practical calculations variables need not appear. Moreover, one may show that this does not cause any trouble in the calculations. \(\def\QQ{mathbb{QQ}}\)
Outputs printed in latex mode:
sage: %display latex # not tested
For the impatient¶
Before going into details with symmetric functions in sage, here is a quick example of what we can do in sage.
We recall that the complete homogeneous symmetric functions \(h_d\) are defined in terms of the power sum symmetric functions
\(p_{\mu}\) by the formula :
where \(z_\mu\) is the number of “automorphisms” of a permutation having cycle structure \(\mu\).
Here is how to obtain both sides of this equality in the ring of symmetric function “\(\mathrm{Sym}\)” over \(\mathbb{Q}\):
sage: Sym = SymmetricFunctions(QQ)
sage: Sym.inject_shorthands()
Defining e as shorthand for Symmetric Functions over Rational Field in the elementary basis
Defining f as shorthand for Symmetric Functions over Rational Field in the forgotten basis
Defining h as shorthand for Symmetric Functions over Rational Field in the homogeneous basis
Defining m as shorthand for Symmetric Functions over Rational Field in the monomial basis
Defining p as shorthand for Symmetric Functions over Rational Field in the powersum basis
Defining s as shorthand for Symmetric Functions over Rational Field in the Schur basis
sage: p(h[6])
1/720*p[1, 1, 1, 1, 1, 1] + 1/48*p[2, 1, 1, 1, 1] + 1/16*p[2, 2, 1, 1] + 1/48*p[2, 2, 2] + 1/18*p[3, 1, 1, 1] + 1/6*p[3, 2, 1] + 1/18*p[3, 3] + 1/8*p[4, 1, 1] + 1/8*p[4, 2] + 1/5*p[5, 1] + 1/6*p[6]
sage: sum((1/Partition(i).aut())*p(i) for i in Partitions(6).list())
1/720*p[1, 1, 1, 1, 1, 1] + 1/48*p[2, 1, 1, 1, 1] + 1/16*p[2, 2, 1, 1] + 1/48*p[2, 2, 2] + 1/18*p[3, 1, 1, 1] + 1/6*p[3, 2, 1] + 1/18*p[3, 3] + 1/8*p[4, 1, 1] + 1/8*p[4, 2] + 1/5*p[5, 1] + 1/6*p[6]
Abstract symmetric functions¶
We first describe how to manipulate “variable free” symmetric functions (with coefficients in the ring of rational coefficient fractions in \(q\) and \(t\)). Such functions are linear combinations of one of the six classical bases of symmetric functions; all indexed by interger partitions \(\mu=\mu_1\mu_2\cdots \mu_k\).
- The power sum symmetric functions \(p_\mu=p_{\mu_1}p_{\mu_2}\cdots p_{\mu_2}\)
- The (complete) homogeneous symmetric functions \(h_\mu=h_{\mu_1}h_{\mu_2}\cdots h_{\mu_2}\)
- The elementary symmetric functions \(e_\mu=e_{\mu_1}e_{\mu_2}\cdots e_{\mu_2}\)
- The monomial functions \(m_{\mu}\)
- The Schur functions \(s{\mu}\)
- The forgotten symmetric functions \(f_{\mu}\)
sage: from sage.combinat.q_analogues import *
sage: from sage.combinat.sf.sfa import *
sage: F = QQ['q','t'].fraction_field()
sage: F.inject_variables()
Defining q, t
sage: Symqt = SymmetricFunctions(F)
sage: Symqt.inject_shorthands(verbose=False)
Another often used coefficient ring is \(\mathbb{Q}(q,t)\).
Thus, declaring first this ring (and “injecting” variables \(q\) and
\(t\) to make them available), one may introduce the ring of symmetric
functions over \(\mathbb{Q}(q,t)\) as follows. The Symqt.inject_shorthands()
command makes the “usual” short names (as in Macdonald book) available
(with Sage < 8.0, it will display a warning message you can ignore.).
The keyword \(verbose\) allows you to make the injection quiet.
sage: h[2,1],p[2,1]
(h[2, 1], p[2, 1])
sage: (q+t)*s[2,1,1]
(q+t)*s[2, 1, 1]
Now that we have acces to all the bases we need, we can start to manipulate them. Symmetric functions are indexed by partitions \(\mu\), with integers considered as partitions having size one (don’t forget the brackets!):
sage: s[101,14,13,11]
s[101, 14, 13, 11]
sage: e[3,2,1]
e[3, 2, 1]
The ring structure¶
Note that for the multiplicative bases (ie: \(e\), \(h\) and \(p\)), products are replaced by the corresponding partition indexed expression:
sage: p([2,1,1])*p([5,2])==p([5,2,2,1,1])
True
For the non-multiplicative bases, such as the Schur functions, multiplication are expanded as linear combinations in the same (linear) basis:
sage: s([5])^2*s([1,1,1])
s[5, 5, 1, 1, 1] + s[6, 4, 1, 1, 1] + 2*s[6, 5, 1, 1] + s[6, 6, 1] + s[7, 3, 1, 1, 1] + 2*s[7, 4, 1, 1] + s[7, 5, 1] + s[8, 2, 1, 1, 1] + 2*s[8, 3, 1, 1] + s[8, 4, 1] + s[9, 1, 1, 1, 1] + 2*s[9, 2, 1, 1] + s[9, 3, 1] + 2*s[10, 1, 1, 1] + s[10, 2, 1] + s[11, 1, 1]
sage: m([3,1])*m([2,2])
m[3, 2, 2, 1] + 2*m[3, 3, 2] + m[5, 2, 1] + m[5, 3]
These calculations are relatively fast as illustrated in the following, showing only the length of the output rather than printing it out in all its glory:
sage: len(s[10,5,5,3]*s[12,5,2])
2986
When we mix different bases, the result will be expressed in one of the bases, usually the first basis encountered in the expression:
sage: s([2,1])*m([1,1])+p([2,2])
s[1, 1, 1, 1] - s[2, 1, 1] + s[2, 1, 1, 1] + 2*s[2, 2] + s[2, 2, 1] - s[3, 1] + s[3, 1, 1] + s[3, 2] + s[4]
sage: m([1,1])*s([2,1])+p([2,2])
20*m[1, 1, 1, 1, 1] + 9*m[2, 1, 1, 1] + 2*m[2, 2] + 4*m[2, 2, 1] + 2*m[3, 1, 1] + m[3, 2] + m[4]
sage: p([2,2])+m([1,1])*s([2,1])
1/6*p[1, 1, 1, 1, 1] - 1/6*p[2, 1, 1, 1] + p[2, 2] - 1/6*p[3, 1, 1] + 1/6*p[3, 2]
Concrete symmetric functions¶
Our above abstract symmetric functions represent (possibly very large) concrete multivariate polynomials that are invariant upon any permutation of their variables. Simple examples include
To expand a symmetric function into a concrete polynomial in the set of variables \(x_0, x_1, \dots, x_{n-1}\), one proceeds as follows:
sage: p[3].expand(3)
x0^3 + x1^3 + x2^3
sage: h[3].expand(3)
x0^3 + x0^2*x1 + x0*x1^2 + x1^3 + x0^2*x2 + x0*x1*x2 + x1^2*x2 + x0*x2^2 + x1*x2^2 + x2^3
sage: e[3].expand(3)
x0*x1*x2
sage: s[3,1,1].expand(4)
x0^3*x1*x2 + x0^2*x1^2*x2 + x0*x1^3*x2 + x0^2*x1*x2^2 + x0*x1^2*x2^2 + x0*x1*x2^3 + x0^3*x1*x3 + x0^2*x1^2*x3 + x0*x1^3*x3 + x0^3*x2*x3 + 3*x0^2*x1*x2*x3 + 3*x0*x1^2*x2*x3 + x1^3*x2*x3 + x0^2*x2^2*x3 + 3*x0*x1*x2^2*x3 + x1^2*x2^2*x3 + x0*x2^3*x3 + x1*x2^3*x3 + x0^2*x1*x3^2 + x0*x1^2*x3^2 + x0^2*x2*x3^2 + 3*x0*x1*x2*x3^2 + x1^2*x2*x3^2 + x0*x2^2*x3^2 + x1*x2^2*x3^2 + x0*x1*x3^3 + x0*x2*x3^3 + x1*x2*x3^3
sage: m[3,1,1].expand(4)
x0^3*x1*x2 + x0*x1^3*x2 + x0*x1*x2^3 + x0^3*x1*x3 + x0*x1^3*x3 + x0^3*x2*x3 + x1^3*x2*x3 + x0*x2^3*x3 + x1*x2^3*x3 + x0*x1*x3^3 + x0*x2*x3^3 + x1*x2*x3^3
sage: f[3,1,1].expand(4)
3*x0^5 + 2*x0^4*x1 + x0^3*x1^2 + x0^2*x1^3 + 2*x0*x1^4 + 3*x1^5 + 2*x0^4*x2 + x0^3*x1*x2 + x0*x1^3*x2 + 2*x1^4*x2 + x0^3*x2^2 + x1^3*x2^2 + x0^2*x2^3 + x0*x1*x2^3 + x1^2*x2^3 + 2*x0*x2^4 + 2*x1*x2^4 + 3*x2^5 + 2*x0^4*x3 + x0^3*x1*x3 + x0*x1^3*x3 + 2*x1^4*x3 + x0^3*x2*x3 + x1^3*x2*x3 + x0*x2^3*x3 + x1*x2^3*x3 + 2*x2^4*x3 + x0^3*x3^2 + x1^3*x3^2 + x2^3*x3^2 + x0^2*x3^3 + x0*x1*x3^3 + x1^2*x3^3 + x0*x2*x3^3 + x1*x2*x3^3 + x2^2*x3^3 + 2*x0*x3^4 + 2*x1*x3^4 + 2*x2*x3^4 + 3*x3^5
For sure, one may use any other set of variables via the optional “alphabet”:
sage: g = s[2,1]
sage: g.expand(3, alphabet =['x','y','z'])
x^2*y + x*y^2 + x^2*z + 2*x*y*z + y^2*z + x*z^2 + y*z^2
Exercise
Let \(e_k(n) = e_k(x_0,x_1, \dots , x_{n-1})\) and similarly for the homogeneous functions. Then we have the following recursion relations for \(n \geq 1\) :
where \(\delta_{k,0}\) is the Kronecker delta.
Check these relations for \(k=3\) and \(2 \leq n \leq 5\).
Solution
sage: k=3
sage: R = PolynomialRing(QQ,'x',5)
sage: R.inject_variables()
Defining x0, x1, x2, x3, x4
sage: l = list(R.gens())
sage: for xn, n in zip(l[1:], range(2,6)):
....: f1 = e([k]).expand(n)
....: print(f1)
....: f2 = e([k]).expand(n-1,l[:n-1])+xn*(e([k-1]).expand(n-1,l[:n-1]))
....: print(f2)
....: g1 = h([k]).expand(n)
....: print(g1)
....: g2 = h([k]).expand(n-1,l[:n-1])+xn*(h([k-1]).expand(n,l[:n]))
....: print(g2)
0
0
x0^3 + x0^2*x1 + x0*x1^2 + x1^3
x0^3 + x0^2*x1 + x0*x1^2 + x1^3
x0*x1*x2
x0*x1*x2
x0^3 + x0^2*x1 + x0*x1^2 + x1^3 + x0^2*x2 + x0*x1*x2 + x1^2*x2 + x0*x2^2 + x1*x2^2 + x2^3
x0^3 + x0^2*x1 + x0*x1^2 + x1^3 + x0^2*x2 + x0*x1*x2 + x1^2*x2 + x0*x2^2 + x1*x2^2 + x2^3
x0*x1*x2 + x0*x1*x3 + x0*x2*x3 + x1*x2*x3
x0*x1*x2 + x0*x1*x3 + x0*x2*x3 + x1*x2*x3
x0^3 + x0^2*x1 + x0*x1^2 + x1^3 + x0^2*x2 + x0*x1*x2 + x1^2*x2 + x0*x2^2 + x1*x2^2 + x2^3 + x0^2*x3 + x0*x1*x3 + x1^2*x3 + x0*x2*x3 + x1*x2*x3 + x2^2*x3 + x0*x3^2 + x1*x3^2 + x2*x3^2 + x3^3
x0^3 + x0^2*x1 + x0*x1^2 + x1^3 + x0^2*x2 + x0*x1*x2 + x1^2*x2 + x0*x2^2 + x1*x2^2 + x2^3 + x0^2*x3 + x0*x1*x3 + x1^2*x3 + x0*x2*x3 + x1*x2*x3 + x2^2*x3 + x0*x3^2 + x1*x3^2 + x2*x3^2 + x3^3
x0*x1*x2 + x0*x1*x3 + x0*x2*x3 + x1*x2*x3 + x0*x1*x4 + x0*x2*x4 + x1*x2*x4 + x0*x3*x4 + x1*x3*x4 + x2*x3*x4
x0*x1*x2 + x0*x1*x3 + x0*x2*x3 + x1*x2*x3 + x0*x1*x4 + x0*x2*x4 + x1*x2*x4 + x0*x3*x4 + x1*x3*x4 + x2*x3*x4
x0^3 + x0^2*x1 + x0*x1^2 + x1^3 + x0^2*x2 + x0*x1*x2 + x1^2*x2 + x0*x2^2 + x1*x2^2 + x2^3 + x0^2*x3 + x0*x1*x3 + x1^2*x3 + x0*x2*x3 + x1*x2*x3 + x2^2*x3 + x0*x3^2 + x1*x3^2 + x2*x3^2 + x3^3 + x0^2*x4 + x0*x1*x4 + x1^2*x4 + x0*x2*x4 + x1*x2*x4 + x2^2*x4 + x0*x3*x4 + x1*x3*x4 + x2*x3*x4 + x3^2*x4 + x0*x4^2 + x1*x4^2 + x2*x4^2 + x3*x4^2 + x4^3
x0^3 + x0^2*x1 + x0*x1^2 + x1^3 + x0^2*x2 + x0*x1*x2 + x1^2*x2 + x0*x2^2 + x1*x2^2 + x2^3 + x0^2*x3 + x0*x1*x3 + x1^2*x3 + x0*x2*x3 + x1*x2*x3 + x2^2*x3 + x0*x3^2 + x1*x3^2 + x2*x3^2 + x3^3 + x0^2*x4 + x0*x1*x4 + x1^2*x4 + x0*x2*x4 + x1*x2*x4 + x2^2*x4 + x0*x3*x4 + x1*x3*x4 + x2*x3*x4 + x3^2*x4 + x0*x4^2 + x1*x4^2 + x2*x4^2 + x3*x4^2 + x4^3
Convert a concrete symmetric polynomial into an abstract symmetric function¶
Conversely, a “concrete” symmetric polynomial, i.e.: explicitly expressed in the variables, maybe written as a formal symmetric function in any chosen basis.
sage: pol1 = (p([2])+e([2,1])).expand(3)
sage: pol1
x0^2*x1 + x0*x1^2 + x0^2*x2 + 3*x0*x1*x2 + x1^2*x2 + x0*x2^2 + x1*x2^2 + x0^2 + x1^2 + x2^2
sage: n = 3
sage: R = PolynomialRing(FractionField(QQ['q','t']),'x',n)
sage: X=R.gens()
sage: R.inject_variables()
Defining x0, x1, x2
sage: Discr=mul(mul((X[k]-X[j])^2 for j in range(k)) for k in range(1,n))
sage: Discr
x0^4*x1^2 + (-2)*x0^3*x1^3 + x0^2*x1^4 + (-2)*x0^4*x1*x2 + 2*x0^3*x1^2*x2 + 2*x0^2*x1^3*x2 + (-2)*x0*x1^4*x2 + x0^4*x2^2 + 2*x0^3*x1*x2^2 + (-6)*x0^2*x1^2*x2^2 + 2*x0*x1^3*x2^2 + x1^4*x2^2 + (-2)*x0^3*x2^3 + 2*x0^2*x1*x2^3 + 2*x0*x1^2*x2^3 + (-2)*x1^3*x2^3 + x0^2*x2^4 + (-2)*x0*x1*x2^4 + x1^2*x2^4
sage: e.from_polynomial(Discr)
e[2, 2, 1, 1] - 4*e[2, 2, 2] - 4*e[3, 1, 1, 1] + 18*e[3, 2, 1] - 27*e[3, 3] - 8*e[4, 1, 1] + 24*e[4, 2]
The pol
input of the function from_polynomial(pol)
is assumed to
lie in a polynomial ring over the same base field as that used for the symmetric
functions, which thus has to be delared beforehand.
sage: n = 3
sage: R = PolynomialRing(FractionField(QQ['q','t']),'y',n)
sage: R.inject_variables()
Defining y0, y1, y2
Here, we will work with three variables (\(y_0, y_1\) and \(y_2\)). Finally, we can declare our polynomial and convert it into a symmetric function
in the monomial basis for example.
sage: pol2 = y0^2*y1 + y0*y1^2 + y0^2*y2 + 2*y0*y1*y2 + y1^2*y2 + y0*y2^2 + y1*y2^2
sage: m.from_polynomial(pol2)
2*m[1, 1, 1] + m[2, 1]
- In the preceeding example, the base ring of polynomials is the same as the base
- ring of symmetric polynomials considered, as checked by the following.
sage: print(s.base_ring())
Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field
sage: print(pol2.base_ring())
Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field
Thus a concrete symmetric polynomial over \(\mathbb{Q}(q,t)\) may be transformed into an abstract symmetric function in any basis.
sage: R = PolynomialRing(QQ['q','t'],'y',3)
sage: R.inject_variables()
Defining y0, y1, y2
sage: pol2 = 1+(y0*y1+y0*y2+y1*y2)*(q+t)+(y0*y1*y2)*(q*t)
sage: s.from_polynomial(pol2)
s[] + (q+t)*s[1, 1] + q*t*s[1, 1, 1]
Changes of bases¶
Many calculations on symmetric functions involve a change of (linear) basis.
For example, here we compute \(p_{22}+m_{11}s_{21}\) in the elementary basis.
sage: e(p([2,2])+m([1,1])*s([2,1]))
e[1, 1, 1, 1] - 4*e[2, 1, 1] + 4*e[2, 2] + e[2, 2, 1] - e[3, 2]
Exercise
Print all the Schur functions on partitions of size 5 and convert them into the elementary basis.
sage: for mu in Partitions(5):
....: print(s(mu))
....: print(e(s(mu)))
s[5]
e[1, 1, 1, 1, 1] - 4*e[2, 1, 1, 1] + 3*e[2, 2, 1] + 3*e[3, 1, 1] - 2*e[3, 2] - 2*e[4, 1] + e[5]
s[4, 1]
e[2, 1, 1, 1] - 2*e[2, 2, 1] - e[3, 1, 1] + 2*e[3, 2] + e[4, 1] - e[5]
s[3, 2]
e[2, 2, 1] - e[3, 1, 1] - e[3, 2] + e[4, 1]
s[3, 1, 1]
e[3, 1, 1] - e[3, 2] - e[4, 1] + e[5]
s[2, 2, 1]
e[3, 2] - e[4, 1]
s[2, 1, 1, 1]
e[4, 1] - e[5]
s[1, 1, 1, 1, 1]
e[5]
Exercise
Compute the sum of the homogeneous functions on partitions of size 4 in the power sum basis.
sage: p(sum(h(mu) for mu in Partitions(4)))
47/24*p[1, 1, 1, 1] + 7/4*p[2, 1, 1] + 3/8*p[2, 2] + 2/3*p[3, 1] + 1/4*p[4]
Exercise
It is well konwn that \(h_n(X) = \sum \limits_{\mu \vdash n} \dfrac{p_{\mu}(x)}{z_{\mu}}\). Verify this result for \(n \in \{1,2,3,4\}\)
Note that there exists a function zee()
which takes a partition \(\mu\) and gives back the value of \(z_{\mu}\). To use this function, you should import it from* sage.combinat.sf.sfa
.
sage: from sage.combinat.sf.sfa import *
sage: zee([4,4,2,1])
64
sage: for n in range (1,5) :
....: print(p(h([n])) == sum(p(mu)/zee(mu) for mu in Partitions(n)))
True
True
True
True
Other well-known bases¶
Other important bases are implemented in SAGE.
- The forgotten symmetric functions
- The Hall-littlewood basis
- The Jack basis
- The orthogonal basis
- The symplectic basis
- The Witt basis
- The zonal basis
The well known Macdonald symmetric functions are also implemented in sage. For more details, you can consult the following sage reference : http://doc.sagemath.org/html/en/reference/combinat/sage/combinat/sf/macdonald.html
Here are some examples involving the “combinatorial” Macdonald symmetric functions. These are eigenfunctions of the operator \(\nabla\). (See below for more informations about \(\nabla\).)
sage: H = Symqt.macdonald().Ht()
sage: s(H([2,1]))
q*t*s[1, 1, 1] + (q+t)*s[2, 1] + s[3]
sage: H(s[2,1])
((-q)/(-q*t^2+t^3+q^2-q*t))*McdHt[1, 1, 1] + ((q^2+q*t+t^2)/(-q^2*t^2+q^3+t^3-q*t))*McdHt[2, 1] + (t/(-q^3+q^2*t+q*t-t^2))*McdHt[3]
sage: [H(mu).nabla() for mu in Partitions(4)]
[q^6*McdHt[4],
q^3*t*McdHt[3, 1],
q^2*t^2*McdHt[2, 2],
q*t^3*McdHt[2, 1, 1],
t^6*McdHt[1, 1, 1, 1]]
More basic commands on symmetric functions¶
We can see that the terms of a calculation are always given in a precise order on the partitions. This order can be changed.
First, the function get_print_style()
applied to a basis gives us the order used on the partitions for this basis. Then, with set_print_style()
we can set another printing order. The possible orders are :
lex
: lexicographic order.length
: by length of the partitions, and for partitions of same length by lexicographic order.maximal_part
: by the value of the biggest part of the partition.
sage: s.get_print_style()
'lex'
sage: s.set_print_style('lex')
sage: s(p[4,1,1])
-s[1, 1, 1, 1, 1, 1] - s[2, 1, 1, 1, 1] + s[2, 2, 1, 1] + s[2, 2, 2] - s[3, 3] - s[4, 2] + s[5, 1] + s[6]
sage: s.set_print_style('length')
sage: s(p[4,1,1])
s[6] - s[3, 3] - s[4, 2] + s[5, 1] + s[2, 2, 2] + s[2, 2, 1, 1] - s[2, 1, 1, 1, 1] - s[1, 1, 1, 1, 1, 1]
sage: s.get_print_style()
'length'
sage: s.set_print_style('maximal_part')
sage: s(p[4,1,1])
-s[1, 1, 1, 1, 1, 1] + s[2, 2, 2] - s[2, 1, 1, 1, 1] + s[2, 2, 1, 1] - s[3, 3] - s[4, 2] + s[5, 1] + s[6]
The function coefficient()
returns the coefficient associated to a given partition.
sage: f = s[5,2,2,1]
sage: e(f)
e[4, 3, 1, 1, 1] - 2*e[4, 3, 2, 1] + e[4, 3, 3] - e[4, 4, 1, 1] + e[4, 4, 2] - e[5, 2, 1, 1, 1] + 2*e[5, 2, 2, 1] - e[5, 3, 2] + e[5, 4, 1] + e[6, 2, 1, 1] - e[6, 2, 2] - e[6, 4] - e[7, 2, 1] + e[8, 2]
sage: e(f).coefficient([4,3,2,1])
-2
The function degree()
gives the degree of a symmetric function.
sage: f.degree()
10
Finally, the function support()
returns the list of partitions that appear in a given symmetric function. The result will depend on the basis of the function. In the following example, we also use the function sorted()
to get an ordered list.
sage: print(f.support())
[[5, 2, 2, 1]]
sage: print(sorted(h(f).support()))
[[5, 2, 2, 1], [5, 3, 1, 1], [5, 3, 2], [5, 4, 1], [6, 2, 1, 1], [6, 3, 1], [6, 4], [7, 1, 1, 1], [7, 2, 1], [8, 1, 1], [8, 2]]
The omega involution¶
The \(\omega\) involution is the linear extension of the map which sends \(e_\lambda\) to \(h_{\lambda}\).
sage: f = s[2]^2; f
s[2, 2] + s[3, 1] + s[4]
sage: h(f)
h[2, 2]
sage: e(f.omega())
e[2, 2]
sage: [(s(mu),s(mu).omega()) for mu in Partitions(5)]
[(s[5], s[1, 1, 1, 1, 1]),
(s[4, 1], s[2, 1, 1, 1]),
(s[3, 2], s[2, 2, 1]),
(s[3, 1, 1], s[3, 1, 1]),
(s[2, 2, 1], s[3, 2]),
(s[2, 1, 1, 1], s[4, 1]),
(s[1, 1, 1, 1, 1], s[5])]
Scalar Products¶
The Hall scalar product is the standard scalar product on the algebra of symmetric functions. It makes the Schur functions into an orthonormal basis. The value of the scalar product between \(p_{\mu}\) and \(p_{\lambda}\) is given by \(z_{\mu}\) if \(\mu = \lambda\) or zero otherwise. In formula,
Or, yet again, we have
Thus, we get
sage: p([2,2,1]).scalar(p([2,2,1]))
8
sage: Matrix([[p(mu).scalar(p(nu)/zee(mu)) for nu in Partitions(5)] for mu in Partitions(5)])
[1 0 0 0 0 0 0]
[0 1 0 0 0 0 0]
[0 0 1 0 0 0 0]
[0 0 0 1 0 0 0]
[0 0 0 0 1 0 0]
[0 0 0 0 0 1 0]
[0 0 0 0 0 0 1]
Other scalar products, such as the \(q,t\)-scalar product¶
One may specify an optional argument which is a function on partitions giving the value for the scalar product between \(p_{\mu}\) and \(p_{\mu}\). Power sums remain orthogonal for the resulting scalar product. By default, this value is \(z_{\mu}\), but other interesting cases include:
This is already refined as \(scalar_qt()\):
sage: Matrix([[p(mu).scalar_qt(p(nu)/zee(mu)) for nu in Partitions(3)] for mu in Partitions(3)])
[ (-q^3 + 1)/(-t^3 + 1) 0 0]
[ 0 (q^3 - q^2 - q + 1)/(t^3 - t^2 - t + 1) 0]
[ 0 0 (-q^3 + 3*q^2 - 3*q + 1)/(-t^3 + 3*t^2 - 3*t + 1)]
Schur Positivity¶
When computing with symmetric functions, one often wants to check a given symmetric function is Schur positive or not. In our current setup, this means that coefficients polynomials in \(\mathbb{N}[q,t]\). The following function
returnsTrue
if the given symmetric function is Schur positive andFalse
if not.
sage: f = s([4,1])+s([3,2])
sage: print(f.is_schur_positive())
True
sage: g = s([4,1])-s([3,2])
sage: print(g.is_schur_positive())
False
- For example, we can verify the well-known Schur positivity of product of Schur
- functions.
sage: for mu in Partitions(2) :
....: for nu in Partitions(3) :
....: if (s(mu)*s(nu)).is_schur_positive() :
....: print('The product of ', s(mu),' and ',s(nu),' is Schur positive.')
....: else :
....: print('The product of ', s(mu),' and ',s(nu),'is not Schur positive.')
The product of s[2] and s[3] is Schur positive.
The product of s[2] and s[2, 1] is Schur positive.
The product of s[2] and s[1, 1, 1] is Schur positive.
The product of s[1, 1] and s[3] is Schur positive.
The product of s[1, 1] and s[2, 1] is Schur positive.
The product of s[1, 1] and s[1, 1, 1] is Schur positive.
Exercise
Its representation theoretic signification implies that \(\nabla (e_n)\) is Schur positive. Verify this for \(1 \leq n \leq 6\).
sage: for n in range(1,7) :
....: print(e([n]).nabla().is_schur_positive())
True
True
True
True
True
True
Schur positivity is a rare phenomena in general, but symmetric functions that come from representation theory are Schur positive. One can show that the probability that a degree \(n\) monomial positive is Schur positive is equal to
with \(K_{\mu,\nu}\) the Kostka numbers. Recall that these occur in the expansion of the Schur functions in terms of the monomial functions:
For instance, we have
sage: m(s[3,2])
5*m[1, 1, 1, 1, 1] + 3*m[2, 1, 1, 1] + 2*m[2, 2, 1] + m[3, 1, 1] + m[3, 2]
hence defining
sage: def K(mu,nu):
....: return s(mu).scalar(h(nu))
so that the above expression is indeed seen to be
sage: add(K([3,2],nu)*m(nu) for nu in Partitions(5))
5*m[1, 1, 1, 1, 1] + 3*m[2, 1, 1, 1] + 2*m[2, 2, 1] + m[3, 1, 1] + m[3, 2]
Now, we set
sage: def k(mu):
....: n=add(j for j in mu)
....: return add(K(mu,nu) for nu in Partitions(n))
so that the above probability is calculated by the function
sage: def prob_Schur_positive(n): return 1/mul(k(mu) for mu in Partitions(n))
One can then illustrate how very rare Schur-positivity is, as a function of the degree:
sage: [prob_Schur_positive(n) for n in range(1,8)]
[1, 1/2, 1/9, 1/560, 1/480480, 1/1027458432000, 1/2465474364698304960000]
Plethysm¶
As its name strongly suggests, the plethysm()
function computes the plethysm \(f\circ g\), of two symmetric functions \(f\) and \(g\). Recall that this is the operation characterized by the properties
- \((f_1+f_2)\circ g =(f_1\circ g)+(f_2\circ g)\),
- \((f_1\cdot f_2)\circ g =(f_1\circ g)\cdot (f_2\circ g)\),
- \(p_k\circ(g_1+g_2) =(p_k\circ g_1)+(p_k\circ g_2)\),
- \(p_k\circ (g_1\cdot g_2) =(p_k\circ g_1)+(p_k\circ g_2)\),
- \(p_k\circ p_n =p_{kn}\),
- \(p_k\circ x =x^k\), if \(x\) is a variable
- \(p_k\circ c =c\), if \(c\) is a constant
One may specify a list of SAGE-variables to be treated as variables
in a plethysm, using the option include=[x1,x2,...,xk]
, and/or a list
of SAGE-variables to be considered as constants, using the option
exclude=[c1,c2,...,ck]
. Here are some examples.
sage: p([3,2]).plethysm(h([3,1]))
1/36*p[3, 3, 3, 3, 2, 2, 2, 2] + 1/12*p[4, 3, 3, 3, 3, 2, 2] + 1/12*p[6, 3, 3, 2, 2, 2, 2] + 1/18*p[6, 3, 3, 3, 3, 2] + 1/4*p[6, 4, 3, 3, 2, 2] + 1/6*p[6, 6, 3, 3, 2] + 1/18*p[9, 3, 2, 2, 2, 2] + 1/6*p[9, 4, 3, 2, 2] + 1/9*p[9, 6, 3, 2]
sage: g = p([1]) + t*s([2,1])
sage: p([2]).plethysm(g,include=[t])
p[2] + 1/3*t^2*p[2, 2, 2] + (-1/3*t^2)*p[6]
sage: p([2]).plethysm(g,exclude=[t])
p[2] + 1/3*t*p[2, 2, 2] + (-1/3*t)*p[6]
It is costumary to also write \(f[g]\) for \(f\circ g\) in mathematical texts, but SAGE uses the shorthand notation \(f(g)\) for better compatibility with python. For instance, the plethysm \(s_4\circ s_2\), may also be computed as
sage: s[4](s[2])
s[2, 2, 2, 2] + s[4, 2, 2] + s[4, 4] + s[6, 2] + s[8]
To have nice expressions for plethystic substitutions, one may set aliases for the symmetric function on the empty partition (i.e. \(s_0, m_0, \dots\), all equal to the constant 1), and the symmetric function (unique up to a scalar) of degree 1.
sage: One = s([])
sage: X = s[1]
sage: s[3](s[4](One*(1+q)))
(q^12+q^11+2*q^10+3*q^9+4*q^8+4*q^7+5*q^6+4*q^5+4*q^4+3*q^3+2*q^2+q+1)*s[]
One should compare this with
sage: q_binomial(7,3)
q^12 + q^11 + 2*q^10 + 3*q^9 + 4*q^8 + 4*q^7 + 5*q^6 + 4*q^5 + 4*q^4 + 3*q^3 + 2*q^2 + q + 1
sage: s[4](X*(1+q))
q^2*s[2, 2] + (q^3+q^2+q)*s[3, 1] + (q^4+q^3+q^2+q+1)*s[4]
sage: s[4](X/(1-q)).map_coefficients(factor)
((q-1)^-4*(q+1)^-2*q^6*(q^2+1)^-1*(q^2+q+1)^-1)*s[1, 1, 1, 1] + ((q-1)^-4*(q+1)^-2*q^2*(q^2+q+1)^-1)*s[2, 2] + ((q-1)^-4*(q+1)^-2*q^3*(q^2+1)^-1)*s[2, 1, 1] + ((q-1)^-4*(q+1)^-2*q*(q^2+1)^-1)*s[3, 1] + ((q-1)^-4*(q+1)^-2*(q^2+1)^-1*(q^2+q+1)^-1)*s[4]
sage: s[3](s[4])-s[2](s[6])
s[4, 4, 4] + s[6, 4, 2] + s[7, 4, 1] + s[8, 2, 2] + s[9, 3]
Suggests that we have the following positive coefficient polynomial
sage: q_binomial(7,3)-q_binomial(8,2)
q^9 + q^8 + q^7 + q^6 + q^5 + q^4 + q^3
Some interesting operators on symmetric functions¶
Operators on symmetric functions may be found in SAGE. Among these, the nabla operator is characterized as having the combinatorial Macdonald symmetric functions \(H_{\mu}=H_{\mu}(\mathbf{x};q,t)\) as eigenfunctions:
where \(\mu\) is a partition, \(\mu'\) its conjugate, and \(n(\mu)\) is set to be equal to \(\sum_i (i-1)\mu_i\). This operator \(\nabla\) is thus defined over symmetric functions with
coefficients in the fraction field \(\mathbb{Q}[q,t]\), as is declared above.
It has been shown by Haiman that \(\nabla(e_n)\) is the Frobenius transform of the bigraded character of the \(\mathbb{S}_n\)-module of diagonal harmonic
polynomials. Recall that the Frobernius transform encodes irreducible as Schur functions.
sage: s(e[3].nabla())
(q^3+q^2*t+q*t^2+t^3+q*t)*s[1, 1, 1] + (q^2+q*t+t^2+q+t)*s[2, 1] + s[3]
The global dimension of this module is \((n+1)^{n-1}\), and the dimension of its alternating component (see exercise below) is the Catalan number \(C_n=\frac{1}{n+1}\binom{2n}{n}\). And there are many other interesting properties of the bigraded version.
sage: Hilb_qt=s(e[3].nabla()).scalar(p[1]^3); Hilb_qt
q^3 + q^2*t + q*t^2 + t^3 + 2*q^2 + 3*q*t + 2*t^2 + 2*q + 2*t + 1
sage: Hilb_qt.substitute({q:1,t:1})
16
There are also interesting conjectures on the effect of \(\nabla\) on Schur functions.
sage: (-s([2,2,1])).nabla()
(q^6*t^3+q^5*t^4+q^4*t^5+q^3*t^6)*s[1, 1, 1, 1, 1] + (q^5*t^2+2*q^4*t^3+2*q^3*t^4+q^2*t^5)*s[2, 2, 1] + (q^6*t^2+2*q^5*t^3+2*q^4*t^4+2*q^3*t^5+q^2*t^6+q^4*t^3+q^3*t^4)*s[2, 1, 1, 1] + (q^4*t^2+q^3*t^3+q^2*t^4)*s[3, 2] + (q^5*t^2+q^4*t^3+q^3*t^4+q^2*t^5+q^4*t^2+2*q^3*t^3+q^2*t^4)*s[3, 1, 1] + (q^3*t^2+q^2*t^3)*s[4, 1]
Exercise
We have the following relation between \(\nabla (e_n)\) and the q,t-Catalan numbers :
Check this relation for \(1 \leq n \leq 5\)
Note that the n-th q,t-Catalan number can be computed by using the command ``qt_catalan_number(n)`` which has to be imported from sage.combinat.q_analogues
if it hasn’t already been done*.
sage: from sage.combinat.q_analogues import *
sage: for n in range (1,6) :
....: print((n,qt_catalan_number(n)))
(1, 1)
(2, q + t)
(3, q^3 + q^2*t + q*t^2 + t^3 + q*t)
(4, q^6 + q^5*t + q^4*t^2 + q^3*t^3 + q^2*t^4 + q*t^5 + t^6 + q^4*t + q^3*t^2 + q^2*t^3 + q*t^4 + q^3*t + q^2*t^2 + q*t^3)
(5, q^10 + q^9*t + q^8*t^2 + q^7*t^3 + q^6*t^4 + q^5*t^5 + q^4*t^6 + q^3*t^7 + q^2*t^8 + q*t^9 + t^10 + q^8*t + q^7*t^2 + q^6*t^3 + q^5*t^4 + q^4*t^5 + q^3*t^6 + q^2*t^7 + q*t^8 + q^7*t + 2*q^6*t^2 + 2*q^5*t^3 + 2*q^4*t^4 + 2*q^3*t^5 + 2*q^2*t^6 + q*t^7 + q^6*t + q^5*t^2 + 2*q^4*t^3 + 2*q^3*t^4 + q^2*t^5 + q*t^6 + q^4*t^2 + q^3*t^3 + q^2*t^4)
sage: for n in range (1,6) :
....: print((n,e([n]).nabla().scalar(e([n])).substitute({q:1,t:1})))
(1, 1)
(2, 2)
(3, 5)
(4, 14)
(5, 42)
sage: for n in range (1,6) :
....: print((n,factor(e([n]).nabla().scalar(e([n])).substitute({t:1/q}))))
(1, 1)
(2, q^-1 * (q^2 + 1))
(3, q^-3 * (q^2 - q + 1) * (q^4 + q^3 + q^2 + q + 1))
(4, q^-6 * (q^2 - q + 1) * (q^4 + 1) * (q^6 + q^5 + q^4 + q^3 + q^2 + q + 1))
(5, q^-10 * (q^4 + 1) * (q^4 - q^3 + q^2 - q + 1) * (q^6 + q^3 + 1) * (q^6 + q^5 + q^4 + q^3 + q^2 + q + 1))
sage: for n in range (1,6) :
....: print(e([n]).nabla().scalar(e([n])) == qt_catalan_number(n))
True
True
True
True
True
\(k\)-Schur functions¶
The \(k\)-Schur functions live in the \(k\)-bounded subspace of the ring of symmetric functions. It is possible to compute in the \(k\)-bounded subspace directly:
sage: Sym = SymmetricFunctions(QQ)
sage: ks = Sym.kschur(3,1)
sage: f = ks[2,1]*ks[2,1]
sage: print(f)
ks3[2, 2, 1, 1] + ks3[2, 2, 2] + ks3[3, 1, 1, 1]
or to lift to the ring of symmetric functions:
sage: f.lift()
s[2, 2, 1, 1] + s[2, 2, 2] + s[3, 1, 1, 1] + 2*s[3, 2, 1] + s[3, 3] + s[4, 1, 1] + s[4, 2]
However, it is not always possible to convert a symmetric function to the \(k\)-bounded subspace:
sage: s = Sym.schur()
sage: ks(s[2,1])
ks3[2, 1]
The \(k\)-Schur functions are more generally defined with a parameter \(t\) and they are a basis of the subspace spanned by the Hall-Littlewood \(Qp\) symmetric functions indexed by partitions whose first part is less than or equal to \(k\):
sage: Sym = SymmetricFunctions(QQ['t'].fraction_field())
sage: SymS3 = Sym.kBoundedSubspace(3) # default t='t'
sage: ks = SymS3.kschur()
sage: Qp = Sym.hall_littlewood().Qp()
sage: print(ks(Qp[2,1,1,1]))
ks3[2, 1, 1, 1] + (t^2+t)*ks3[2, 2, 1] + (t^3+t^2)*ks3[3, 1, 1] + t^4*ks3[3, 2]
The subspace spanned by the \(k\)-Schur functions with a parameter \(t\) are not known to form a natural algebra. However it is known that the product of a \(k\)-Schur function and an \(\ell\)-Schur function is in the linear span of the \(k+\ell\)-Schur functions:
sage: ks(ks[2,1]*ks[1,1]) # not tested
sage: ks[2,1]*ks[1,1]
s[2, 1, 1, 1] + s[2, 2, 1] + s[3, 1, 1] + s[3, 2]
sage: ks6 = Sym.kBoundedSubspace(6).kschur()
sage: print(ks6(ks[3,1,1]*ks[3]))
ks6[3, 3, 1, 1] + ks6[4, 2, 1, 1] + (t+1)*ks6[4, 3, 1] + t*ks6[4, 4]
+ ks6[5, 1, 1, 1] + ks6[5, 2, 1] + t*ks6[5, 3] + ks6[6, 1, 1]
The \(k\)-split basis is a second basis of the ring spanned by the \(k\)-Schur functions with a parameter \(t\). The \(k\)-split basis has the property that \(Q'_\lambda[X;t]\) expands positively in the \(k\)-split basis and the \(k\)-split basis conjecturally expands positively in the \(k\)-Schur functions.:
sage: ksp3 = SymS3.ksplit()
sage: print(ksp3(Qp[2,1,1,1]))
ksp3[2, 1, 1, 1] + t^2*ksp3[2, 2, 1] + (t^3+t^2)*ksp3[3, 1, 1] + t^4*ksp3[3, 2]
sage: print([ks(ksp3(la)) for la in ksp3(Qp[2,1,1,1]).support()])
[ks3[2, 2, 1], ks3[2, 1, 1, 1] + t*ks3[2, 2, 1], ks3[3, 2], ks3[3, 1, 1]]
Dual \(k\)-Schur functions¶
The dual space to the subspace spanned by the \(k\)-Schur functions is most naturally realized as a quotient of the ring of symmetric functions by an ideal. When \(t=1\) the ideal is generated by the monomial symmetric functions indexed by partitions whose first part is greater than \(k\):
sage: Sym = SymmetricFunctions(QQ)
sage: SymQ3 = Sym.kBoundedQuotient(3,t=1)
sage: km = SymQ3.kmonomial()
sage: print(km[2,1]*km[2,1])
4*m3[2, 2, 1, 1] + 6*m3[2, 2, 2] + 2*m3[3, 2, 1] + 2*m3[3, 3]
sage: F = SymQ3.affineSchur()
sage: print(F[2,1]*F[2,1])
2*F3[1, 1, 1, 1, 1, 1] + 4*F3[2, 1, 1, 1, 1] + 4*F3[2, 2, 1, 1] + 4*F3[2, 2, 2]
+ 2*F3[3, 1, 1, 1] + 4*F3[3, 2, 1] + 2*F3[3, 3]
When \(t\) is not equal to \(1\), the subspace spanned by the \(k\)-Schur functions is realized as a quotient of the ring of symmetric functions by the ideal generated by the Hall-Littlewood symmetric functions in the P basis indexed by partitions with first part greater than \(k\).
sage: Sym = SymmetricFunctions(FractionField(QQ['t']))
sage: SymQ3 = Sym.kBoundedQuotient(3)
sage: kHLP = SymQ3.kHallLittlewoodP()
sage: print(kHLP[2,1]*kHLP[2,1])
(t^2+2*t+1)*HLP3[2, 2, 1, 1] + (t^3+2*t^2+2*t+1)*HLP3[2, 2, 2]
+ (-t^4-t^3+t+1)*HLP3[3, 1, 1, 1] + (-t^2+t+2)*HLP3[3, 2, 1] + (t+1)*HLP3[3, 3]
sage: HLP = Sym.hall_littlewood().P()
sage: print(kHLP(HLP[3,1]))
HLP3[3, 1]
sage: kHLP(HLP[4])
0
In this space, the basis which is dual to the \(k\)-Schur functions conjecturally expands positively in the \(k\)-bounded Hall-Littlewood functions and has positive structure coefficients.
sage: dks = SymQ3.dual_k_Schur()
sage: print(kHLP(dks[2,2]))
(t^4+t^2)*HLP3[1, 1, 1, 1] + t*HLP3[2, 1, 1] + HLP3[2, 2]
sage: print(dks[2,1]*dks[1,1])
(t^2+t)*dks3[1, 1, 1, 1, 1] + (t+1)*dks3[2, 1, 1, 1] + (t+1)*dks3[2, 2, 1]
+ dks3[3, 1, 1] + dks3[3, 2]
At \(t=1\) the \(k\)-bounded Hall-Littlewood basis is equal to the \(k\)-bounded monomial basis and the dual \(k\)-Schur elements are equal to the affine Schur basis. The \(k\)-bounded monomial basis and affine Schur functions are faster and should be used instead of the \(k\)-bounded Hall-Littlewood P basis and dual \(k\)-Schur functions when \(t=1\).
sage: SymQ3 = Sym.kBoundedQuotient(3,t=1)
sage: dks = SymQ3.dual_k_Schur()
sage: F = SymQ3.affineSchur()
sage: F[3,1]==dks[3,1]
True
Representation theory of the symmetric group¶
- The Schur functions \(s_\lambda\) can also be interpreted as irreducible characters
of the symmetric group \(S_n\), where \(n\) is the size of the partition \(\lambda\). Since the Schur functions of degree \(n\) form a basis of the symmetric functions of degree \(n\), it follows that an arbitrary symmetric function (homogeneous of degree \(n\)) may be interpreted as a function on the symmetric group. In this interpretation the power sum symmetric function \(p_\lambda\) is the characteristic function of the conjugacy class with shape \(\lambda\), multiplied by the order of the centralizer of an element.
Hence the irreducible characters can be computed as follows.
sage: M = Matrix([[s[mu.conjugate()].scalar(p[nu.conjugate()]) for nu in Partitions(5)] for mu in Partitions(5)])
sage: M
[ 1 -1 1 1 -1 -1 1]
[ 4 -2 0 1 1 0 -1]
[ 5 -1 1 -1 -1 1 0]
[ 6 0 -2 0 0 0 1]
[ 5 1 1 -1 1 -1 0]
[ 4 2 0 1 -1 0 -1]
[ 1 1 1 1 1 1 1]
We can indeed check that this agrees with the character table of \(S_5\), modulo our reordering by
sage: SymmetricGroup(5).character_table() == M
True
Inner plethysm¶
The operation of inner plethysm f.inner_plethysm(g)
models the
composition of the \(S_n\) representation represented by \(g\) with the
\(GL_m\) representation whose character is \(f\). See the documentation of
inner_plethysm
, for more information.
sage: g = s[2]^2
sage: g.inner_plethysm(s[2])
s[2]
sage: Matrix([[s(mu).inner_plethysm(s(nu)) for nu in Partitions(4)] for mu in Partitions(3)])
[ s[4] s[2, 1, 1] + 2*s[3, 1] + s[4] s[1, 1, 1, 1] + s[2, 2] + s[4] s[1, 1, 1, 1] + 2*s[2, 1, 1] + s[3, 1] s[1, 1, 1, 1]]
[ 0 s[2, 1, 1] + s[2, 2] + s[3, 1] s[2, 2] s[2, 1, 1] + s[2, 2] + s[3, 1] 0]
[ 0 s[1, 1, 1, 1] 0 s[4] 0]
More specific applications¶
The first part of this tutorial was meant to present general use of symmetric functions in Sage. Here are now more specific applications.
Sage knows certain categorical information about this algebra.
sage: Sym.category()
Join of Category of hopf algebras over Fraction Field of Univariate Polynomial Ring in t over Rational Field
and Category of graded algebras over Fraction Field of Univariate Polynomial Ring in t over Rational Field
and Category of monoids with realizations
and Category of coalgebras over Fraction Field of Univariate Polynomial Ring in t over Rational Field with realizations
Let us explore the other operations of \(p\). We can ask for the mathematical properties of \(p\).
sage: p.categories()
[Category of graded bases of Symmetric Functions over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of filtered bases of Symmetric Functions over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of bases of Symmetric Functions over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of graded hopf algebras with basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of filtered hopf algebras with basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of hopf algebras with basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of realizations of hopf algebras over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of hopf algebras over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of graded algebras with basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of filtered algebras with basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of bialgebras with basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of algebras with basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of graded algebras over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of commutative algebras over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of filtered algebras over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of bialgebras over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of algebras over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of commutative rings,
Category of rings,
Category of associative algebras over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of rngs,
Category of semirings,
Category of associative additive commutative additive associative additive unital distributive magmas and additive magmas,
Category of unital algebras with basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of magmatic algebras with basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of unital algebras over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of magmatic algebras over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of additive commutative additive associative additive unital distributive magmas and additive magmas,
Category of additive commutative additive associative distributive magmas and additive magmas,
Category of additive associative distributive magmas and additive magmas,
Category of distributive magmas and additive magmas,
Category of magmas and additive magmas,
Category of commutative monoids,
Category of monoids,
Category of semigroups,
Category of realizations of unital magmas,
Category of realizations of magmas,
Category of commutative magmas,
Category of unital magmas,
Category of magmas,
Category of graded modules with basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of filtered modules with basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of coalgebras with basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of vector spaces with basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of modules with basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of graded modules over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of realizations of coalgebras over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of filtered modules over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of coalgebras over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of vector spaces over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of modules over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of bimodules over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field on the left and Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field on the right,
Category of right modules over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of left modules over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of commutative additive groups,
Category of additive groups,
Category of additive inverse additive unital additive magmas,
Category of commutative additive monoids,
Category of additive monoids,
Category of additive unital additive magmas,
Category of commutative additive semigroups,
Category of additive commutative additive magmas,
Category of additive semigroups,
Category of additive magmas,
Category of realizations of Symmetric Functions over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field,
Category of realizations of sets,
Category of sets,
Category of sets with partial maps,
Category of objects]
To start with, \(p\) is a graded algebra, the grading being induced by the size of the partitions. Due to this, the one is the basis element indexed by the empty partition:
sage: p.one()
p[]
Note also that it is a good idea to use:
sage: s.one()
s[]
sage: s.zero()
0
instead of \(s(1)\) and \(s(0)\) within programs where speed is important, in order to prevent unnecessary coercions.
Hopf structure and important identities¶
Many important identities between symmetric functions can be linked to “the” Hopf algebra structure on the ring of symmetric function. In part, this means that we have a coproduct on symmetric functions
that may be described in either of the two forms:
For instance, we have
sage: One=s[0]
sage: X=s[1]
sage: Y=tensor([X,One])
sage: Z=tensor([One,X])
sage: s[3](Y+Z)
s[] # s[3] + s[1] # s[2] + s[2] # s[1] + s[3] # s[]
sage: s[3,2,1].coproduct()
s[] # s[3, 2, 1] + s[1] # s[2, 2, 1] + s[1] # s[3, 1, 1] + s[1] # s[3, 2] + s[1, 1] # s[2, 1, 1] + s[1, 1] # s[2, 2] + s[1, 1] # s[3, 1] + s[1, 1, 1] # s[2, 1] + s[2] # s[2, 1, 1] + s[2] # s[2, 2] + s[2] # s[3, 1] + s[2, 1] # s[1, 1, 1] + 2*s[2, 1] # s[2, 1] + s[2, 1] # s[3] + s[2, 1, 1] # s[1, 1] + s[2, 1, 1] # s[2] + s[2, 2] # s[1, 1] + s[2, 2] # s[2] + s[2, 2, 1] # s[1] + s[3] # s[2, 1] + s[3, 1] # s[1, 1] + s[3, 1] # s[2] + s[3, 1, 1] # s[1] + s[3, 2] # s[1] + s[3, 2, 1] # s[]
sage: s[3,2,1](Y+Z)
s[] # s[3, 2, 1] + s[1] # s[2, 2, 1] + s[1] # s[3, 1, 1] + s[1] # s[3, 2] + s[1, 1] # s[2, 1, 1] + s[1, 1] # s[2, 2] + s[1, 1] # s[3, 1] + s[1, 1, 1] # s[2, 1] + s[2] # s[2, 1, 1] + s[2] # s[2, 2] + s[2] # s[3, 1] + s[2, 1] # s[1, 1, 1] + 2*s[2, 1] # s[2, 1] + s[2, 1] # s[3] + s[2, 1, 1] # s[1, 1] + s[2, 1, 1] # s[2] + s[2, 2] # s[1, 1] + s[2, 2] # s[2] + s[2, 2, 1] # s[1] + s[3] # s[2, 1] + s[3, 1] # s[1, 1] + s[3, 1] # s[2] + s[3, 1, 1] # s[1] + s[3, 2] # s[1] + s[3, 2, 1] # s[]
Skew Schur fonctions¶
arise when one considers the effect of coproduct on Schur functions themselves
Skew Schur functions are also implemented in SAGE. For instance, we have the skew Schur \(s_{321/2}\).
sage: Sym = SymmetricFunctions(QQ)
sage: Sym.inject_shorthands(verbose=false)
sage: s[3,2,1].skew_by(s[2])
s[2, 1, 1] + s[2, 2] + s[3, 1]
Thus we get the same result as above.
sage: add(tensor([s[3,2,1].skew_by(s(mu)),s(mu)]) for k in range(7) for mu in Partitions(k))
s[] # s[3, 2, 1] + s[1] # s[2, 2, 1] + s[1] # s[3, 1, 1] + s[1] # s[3, 2] + s[1, 1] # s[2, 1, 1] + s[1, 1] # s[2, 2] + s[1, 1] # s[3, 1] + s[1, 1, 1] # s[2, 1] + s[2] # s[2, 1, 1] + s[2] # s[2, 2] + s[2] # s[3, 1] + s[2, 1] # s[1, 1, 1] + 2*s[2, 1] # s[2, 1] + s[2, 1] # s[3] + s[2, 1, 1] # s[1, 1] + s[2, 1, 1] # s[2] + s[2, 2] # s[1, 1] + s[2, 2] # s[2] + s[2, 2, 1] # s[1] + s[3] # s[2, 1] + s[3, 1] # s[1, 1] + s[3, 1] # s[2] + s[3, 1, 1] # s[1] + s[3, 2] # s[1] + s[3, 2, 1] # s[]
In particular, we get
sage: h[4].coproduct()
h[] # h[4] + h[1] # h[3] + h[2] # h[2] + h[3] # h[1] + h[4] # h[]
sage: h[4](Y+Z)
h[] # h[4] + h[1] # h[3] + h[2] # h[2] + h[3] # h[1] + h[4] # h[]
sage: tensor([h,e])(h[4](Y-Z))
h[] # e[4] - h[1] # e[3] + h[2] # e[2] - h[3] # e[1] + h[4] # e[]
sage: s[3,1](Y-Z)
s[] # s[2, 1, 1] - s[1] # s[1, 1, 1] - s[1] # s[2, 1] + s[1, 1] # s[1, 1] + s[2] # s[1, 1] + s[2] # s[2] - s[2, 1] # s[1] - s[3] # s[1] + s[3, 1] # s[]
Cauchy kernel formula¶
The Cauchy kernel is the expression
written here using plethystic notation. Its degree \(n\) homogeneous component plays a crucial role in the description of “dual bases” with respect to the scalar product. We have
- where one “thinks” \(\mathbf{x}=s_1\otimes \mathbb{1}\) and
\(\mathbf{y}= \mathbb{1}\otimes s_1\). One says that \(\{F_\mu\}_\mu\) and \(\{G_\lambda\}_\lambda\) are dual bases.
Schur functions are self dual, the dual of the \(h_{\mu}\) are the \(m_\mu\), that of the \(p_\mu\) are the \(p_{\mu}/z_{\mu}\). The “forgotten” symmetric function \(f_{\mu}\) appear as the dual of the \(e_{\mu}\).
sage: h4xy=add(tensor([s(mu),s(mu)]) for mu in Partitions(4)); h4xy
s[1, 1, 1, 1] # s[1, 1, 1, 1] + s[2, 1, 1] # s[2, 1, 1] + s[2, 2] # s[2, 2] + s[3, 1] # s[3, 1] + s[4] # s[4]
sage: s[4](Y*Z)
s[1, 1, 1, 1] # s[1, 1, 1, 1] + s[2, 1, 1] # s[2, 1, 1] + s[2, 2] # s[2, 2] + s[3, 1] # s[3, 1] + s[4] # s[4]
sage: tensor([h,m])(h4xy)
h[1, 1, 1, 1] # m[1, 1, 1, 1] + h[2, 1, 1] # m[2, 1, 1] + h[2, 2] # m[2, 2] + h[3, 1] # m[3, 1] + h[4] # m[4]
sage: tensor([e,h])(h4xy)
e[1, 1, 1, 1] # h[4] + e[2, 1, 1] # h[3, 1] - 4*e[2, 1, 1] # h[4] + e[2, 2] # h[2, 2] - 2*e[2, 2] # h[3, 1] + 2*e[2, 2] # h[4] + e[3, 1] # h[2, 1, 1] - 2*e[3, 1] # h[2, 2] - e[3, 1] # h[3, 1] + 4*e[3, 1] # h[4] + e[4] # h[1, 1, 1, 1] - 4*e[4] # h[2, 1, 1] + 2*e[4] # h[2, 2] + 4*e[4] # h[3, 1] - 4*e[4] # h[4]
sage: tensor([p,p])(h4xy)
1/24*p[1, 1, 1, 1] # p[1, 1, 1, 1] + 1/4*p[2, 1, 1] # p[2, 1, 1] + 1/8*p[2, 2] # p[2, 2] + 1/3*p[3, 1] # p[3, 1] + 1/4*p[4] # p[4]
The coproduct, being cocommutative on the generators, is cocommutative everywhere:
sage: p[2, 1].coproduct()
p[] # p[2, 1] + p[1] # p[2] + p[2] # p[1] + p[2, 1] # p[]
This coproduct, along with the counit which sends every symmetric function to its 0-th homogeneous component, makes the ring of symmetric functions into a graded connected bialgebra. It is known that every graded connected bialgebra has an antipode. For the ring of symmetric functions, the antipode can be characterized explicitly: The antipode is an anti-algebra morphism (thus an algebra morphism, since our algebra is commutative) which sends \(p_{\lambda}\) to \((-1)^{\mathrm{length}(\lambda)} p_{\lambda}\) for every partition \(\lambda\). Thus, in particular, it sends the generators on the \(p\) basis to their opposites:
sage: p[3].antipode()
-p[3]
sage: p[3](-X)
-p[3]
sage: s[3,1,1,1,1].antipode()
-s[5, 1, 1]
sage: s[3,1,1,1,1](-X)
-s[5, 1, 1]
The graded connected bialgebra of symmetric functions over a \(\mathbb{Q}\)-algebra has a rather simply-understood structure: It is (isomorphic to) the symmetric algebra of its space of primitives (which is spanned by the power-sum symmetric functions).
Here are further examples:
sage: g = s[2]^2
sage: g.antipode()
s[1, 1, 1, 1] + s[2, 1, 1] + s[2, 2]
sage: g.coproduct()
s[] # s[2, 2] + s[] # s[3, 1] + s[] # s[4] + 2*s[1] # s[2, 1] + 2*s[1] # s[3] + s[1, 1] # s[1, 1] + s[1, 1] # s[2] + s[2] # s[1, 1] + 3*s[2] # s[2] + 2*s[2, 1] # s[1] + s[2, 2] # s[] + 2*s[3] # s[1] + s[3, 1] # s[] + s[4] # s[]
sage: g.coproduct().apply_multilinear_morphism( lambda x,y: x*y.antipode() )
0
In this interpretation of symmetric functions as characters on the symmetric group, the multiplication and comultiplication are interpreted as induction (from \(S_n\times S_m\) to \(S_{n+m}\)) and restriction, respectively. The Schur functions can also be interpreted as characters of \(GL_n\).
The Kronecker product¶
As in the section on the Representation theory of the symmetric group, a symmetric function may be considered as a class function on the symmetric group where the elements \(p_\mu/z_\mu\) are the indicators of a permutation having cycle structure \(\mu\). The Kronecker product of two symmetric functions corresponds to the pointwise product of these class functions.
Since the Schur functions are the irreducible characters of the symmetric group under this identification, the Kronecker product of two Schur functions corresponds to the internal tensor product of two irreducible symmetric group representations.
Under this identification, the Kronecker product of \(p_\mu/z_\mu\) and \(p_\nu/z_\nu\) is \(p_\mu/z_\mu\) if \(\mu=\nu\), and the result is equal to \(0\) otherwise.
internal_product
, kronecker_product
, inner_tensor
and
itensor
are different names for the same function.
sage: g
s[2, 2] + s[3, 1] + s[4]
sage: g.kronecker_product(g)
s[1, 1, 1, 1] + 3*s[2, 1, 1] + 4*s[2, 2] + 5*s[3, 1] + 3*s[4]
sage: g.kronecker_product(s[4])
s[2, 2] + s[3, 1] + s[4]
sage: g.kronecker_product(e[4])
s[1, 1, 1, 1] + s[2, 1, 1] + s[2, 2]
sage: g.omega()
s[1, 1, 1, 1] + s[2, 1, 1] + s[2, 2]
sage: Matrix([[p(mu).kronecker_product(p(nu)/zee(nu)) for nu in Partitions(5)] for mu in Partitions(5)])
[ p[5] 0 0 0 0 0 0]
[ 0 p[4, 1] 0 0 0 0 0]
[ 0 0 p[3, 2] 0 0 0 0]
[ 0 0 0 p[3, 1, 1] 0 0 0]
[ 0 0 0 0 p[2, 2, 1] 0 0]
[ 0 0 0 0 0 p[2, 1, 1, 1] 0]
[ 0 0 0 0 0 0 p[1, 1, 1, 1, 1]]
Implementing new bases¶
In order to implement a new symmetric function basis, Sage will need to know at a minimum how to change back and forth between at least one other basis (although they do not necessarily have to be the same basis). All of the standard functions associated with the basis will have a default implementation (although a more specific implementation may be more efficient).
To present an idea of how this is done, we will create here the example of how to implement the basis \(s_\mu[X(1-t)]\).
To begin, we import the class
sage.combinat.sf.sfa.SymmetricFunctionAlgebra_generic()
. Our
new basis will inherit all of the default methods from this class:
sage: from sage.combinat.sf.sfa import SymmetricFunctionAlgebra_generic as SFA_generic
Now the basis we are creating has a parameter \(t\) which is possible
to specialize. In this example we will convert to and from the Schur
basis. For this we implement methods _self_to_s
and _s_to_self
.
By registering these two functions as coercions, Sage then knows
automatically how it possible to change between any two bases for
which there is a path of changes of bases.
sage: from sage.categories.morphism import SetMorphism
sage: class SFA_st(SFA_generic):
....: def __init__(self, Sym, t):
....: SFA_generic.__init__(self, Sym, basis_name=
....: "Schur functions with a plethystic substitution of X -> X(1-t)",
....: prefix='st')
....: self._s = Sym.s()
....: self.t = Sym.base_ring()(t)
....: cat = HopfAlgebras(Sym.base_ring()).WithBasis()
....: self.register_coercion(
....: SetMorphism(Hom(self._s, self, cat), self._s_to_self))
....: self._s.register_coercion(
....: SetMorphism(Hom(self, self._s, cat), self._self_to_s))
....: def _s_to_self(self, f):
....: # f is a Schur function and the output is in the st basis
....: return self._from_dict(f.theta_qt(0,self.t)._monomial_coefficients)
....: def _self_to_s(self, f):
....: # f is in the st basis and the output is in the Schur basis
....: return self._s.sum(cmu*self._s(mu).theta_qt(self.t,0) for mu,cmu in f)
....: class Element(SFA_generic.Element):
....: pass
An instance of this basis is created by calling it with a symmetric
function ring Sym
and a parameter t
which is in the base ring
of Sym
. The Element
class inherits all of the methods from
sage.combinat.sf.sfa.SymmetricFunctionAlgebra_generic_Element
.
In Macdonald’s work, this basis is denoted
\(S_\lambda(x;t)\) and the change of basis coefficients of the
Macdonald J
basis are the coefficients \(K_{\lambda\mu}(q,t)\).
Here is an example of its use:
sage: QQqt = QQ['q','t'].fraction_field()
sage: (q,t) = QQqt.gens()
sage: st = SFA_st(SymmetricFunctions(QQqt),t)
sage: st
Symmetric Functions over Fraction Field of Multivariate Polynomial
Ring in q, t over Rational Field in the Schur functions with a
plethystic substitution of X -> X(1-t) basis
sage: st[2,1] * st[1]
st[2, 1, 1] + st[2, 2] + st[3, 1]
sage: st([2]).coproduct()
st[] # st[2] + st[1] # st[1] + st[2] # st[]
sage: J = st.symmetric_function_ring().macdonald().J()
sage: st(J[2,1])
q*st[1, 1, 1] + (q*t+1)*st[2, 1] + t*st[3]
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Sage-Combinat¶
sage: %hide
sage: pretty_print_default()
Elementary combinatorics¶
Combinatorial objects¶
sage: p = Partition([3,3,2,1])
sage: p
sage: p.pp()
sage: p.conjugate().pp()
sage: p.conjugate
sage: s = Permutation([5,3,2,6,4,8,9,7,1])
sage: s
sage: (p,q) = s.robinson_schensted()
sage: p.pp()
1 4 7 9
2 6 8
3
5
sage: q.pp()
1 4 6 7
2 5 8
3
9
sage: p.row_stabilizer()
Permutation Group with generators [(), (7,9), (6,8), (4,7), (2,6), (1,4)]
Enumerated sets (combinatorial classes)¶
sage: P5 = Partitions(5)
sage: P5
Partitions of the integer 5
sage: P5.list()
[[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]]
sage: P5.cardinality()
7
sage: Partitions(100000).cardinality()
27493510569775696512677516320986352688173429315980054758203125984302147328114964173055050741660736621590157844774296248940493063070200461792764493033510116079342457190155718943509725312466108452006369558934464248716828789832182345009262853831404597021307130674510624419227311238999702284408609370935531629697851569569892196108480158600569421098519
sage: Permutations(20).random_element()
[15, 6, 8, 14, 17, 16, 4, 7, 11, 3, 10, 5, 19, 9, 12, 2, 20, 18, 1, 13]
sage: Compositions(10).unrank(100) # TODO: non stupid algorithm
[1, 1, 3, 1, 2, 1, 1]
sage: for p in StandardTableaux([3,2]):
....: print("-" * 29)
....: p.pp()
-----------------------------
1 3 5
2 4
-----------------------------
1 2 5
3 4
-----------------------------
1 3 4
2 5
-----------------------------
1 2 4
3 5
-----------------------------
1 2 3
4 5
Trees¶
ToDo
Summary:
- Every mathematical object (element, set, category, …) is modeled by a Python object</li>
- All combinatorial classes share a uniform interface</li>
Constructions¶
sage: C = DisjointUnionEnumeratedSets( [ Compositions(4), Permutations(3)] )
sage: C
Union of Family (Compositions of 4, Standard permutations of 3)
sage: C.cardinality()
14
sage: C.list()
[[1, 1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 3], [2, 1, 1], [2, 2], [3, 1], [4], [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
sage: C = CartesianProduct(Compositions(8), Permutations(20))
sage: C
Cartesian product of Compositions of 8, Standard permutations of 20
sage: C.cardinality()
311411457046609920000
sage: C.random_element() # todo: broken
sage: F = Family(NonNegativeIntegers(), Permutations)
sage: F
Lazy family (Permutations(i))_{i in Set of non negative integers}
sage: F[1000]
Standard permutations of 1000
sage: U = DisjointUnionEnumeratedSets(F)
sage: U.cardinality()
+Infinity
sage: p = iter(U)
sage: for i in range(12):
....: print(next(p))
[]
[1]
[1, 2]
[2, 1]
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
[1, 2, 3, 4]
[1, 2, 4, 3]
sage: for p in U:
....: print(p)
[]
[1]
[1, 2]
[2, 1]
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
...
Summary:
- Basic combinatorial classes + constructions give a flexible toolbox
- This is made possible by uniform interfaces
- Lazy algorithms and data structures for large / infinite sets (iterators, …)
See also sage.combinat.tutorial_enumerated_sets
.
Enumeration kernels¶
Integer lists:
sage: IntegerVectors(10, 3, min_part = 2, max_part = 5, inner = [2, 4, 2]).list()
[[4, 4, 2], [3, 5, 2], [3, 4, 3], [2, 5, 3], [2, 4, 4]]
sage: Compositions(5, max_part = 3, min_length = 2, max_length = 3).list()
[[1, 1, 3], [1, 2, 2], [1, 3, 1], [2, 1, 2], [2, 2, 1], [2, 3], [3, 1, 1], [3, 2]]
sage: Partitions(5, max_slope = -1).list()
[[5], [4, 1], [3, 2]]
sage: IntegerListsLex(10, length=3, min_part = 2, max_part = 5, floor = [2, 4, 2]).list()
[[4, 4, 2], [3, 5, 2], [3, 4, 3], [2, 5, 3], [2, 4, 4]]
sage: IntegerListsLex(5, min_part = 1, max_part = 3, min_length = 2, max_length = 3).list()
[[3, 2], [3, 1, 1], [2, 3], [2, 2, 1], [2, 1, 2], [1, 3, 1], [1, 2, 2], [1, 1, 3]]
sage: IntegerListsLex(5, min_part = 1, max_slope = -1).list()
[[5], [4, 1], [3, 2]]
sage: c = Compositions(5)[1]
sage: c
[1, 1, 1, 2]
sage: c = IntegerListsLex(5, min_part = 1)[1]
Species / decomposable classes¶
sage: from sage.combinat.species.library import *
sage: o = var("o")
Fibonacci words:
sage: Eps = EmptySetSpecies()
sage: Z0 = SingletonSpecies()
sage: Z1 = Eps*SingletonSpecies()
sage: FW = CombinatorialSpecies()
sage: FW.define(Eps + Z0*FW + Z1*Eps + Z1*Z0*FW)
sage: FW
sage: L = FW.isotype_generating_series().coefficients(15)
sage: L
sage: sloane_find(L)
Searching Sloane's online database...
[[45, 'Fibonacci numbers: F(n) = F(n-1) + F(n-2), F(0) = 0, F(1) = 1, F(2) = 1, ...', [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169]], [24595, 'a(n) = s(1)t(n) + s(2)t(n-1) + ... + s(k)t(n+1-k), where k = [ (n+1)/2 ], s = (F(2), F(3), ...), t = A023533.', [1, 0, 0, 1, 2, 3, 5, 0, 0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1598, 2586, 4184, 6770, 10954, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28658, 46370, 75028, 121398, 196426]], [25109, 'a(n) = s(1)t(n) + s(2)t(n-1) + ... + s(k)t(n-k+1), where k = [ n/2 ], s = (F(2), F(3), F(4), ...), t = A023533.', [0, 0, 1, 2, 3, 0, 0, 0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1598, 2586, 4181, 6770, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28658, 46370, 75028, 121398, 196426, 317824, 514250]], [132636, 'Fib(n) mod n^3.', [0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 1685, 7063, 4323, 4896, 12525, 15937, 19271, 10483, 2060, 22040, 5674, 15621, 2752, 3807, 9340, 432, 46989, 19305, 11932, 62155, 31899, 12088, 22273, 3677, 32420]], [132916, 'a(0)=0; a(1)=1; a(n) = Sum a(n-k), k= 1 ... [n^(1/3)] for n>=2.', [0, 1, 1, 1, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 21892, 39603, 72441, 133936, 245980, 452357, 832273, 1530610, 2815240, 5178123, 9523973, 17517336, 32219432, 59260741, 108997509, 200477682]], [147316, 'A000045 Fibonacci mirror sequence Binet: f(n)=(1/5)*2^(-n) ((5 - 2 *Sqrt[5]) (1 + Sqrt[5])^n + (1 - Sqrt[5])^n(5 + 2 * Sqrt[5])).', [1597, -987, 610, -377, 233, -144, 89, -55, 34, -21, 13, -8, 5, -3, 2, -1, 1, 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597]], [39834, 'a(n+2)=-a(n+1)+a(n) (signed Fibonacci numbers); or Fibonacci numbers (A000045) extended to negative indices.', [1, 1, 0, 1, -1, 2, -3, 5, -8, 13, -21, 34, -55, 89, -144, 233, -377, 610, -987, 1597, -2584, 4181, -6765, 10946, -17711, 28657, -46368, 75025, -121393, 196418, -317811, 514229, -832040, 1346269, -2178309, 3524578, -5702887, 9227465, -14930352, 24157817]], [152163, 'a(n)=a(n-1)+a(n-2), n>1 ; a(0)=1, a(1)=-1 .', [1, -1, 0, -1, -1, -2, -3, -5, -8, -13, -21, -34, -55, -89, -144, -233, -377, -610, -987, -1597, -2584, -4181, -6765, -10946, -17711, -28657, -46368, -75025, -121393, -196418, -317811, -514229, -832040, -1346269, -2178309, -3524578, -5702887]]]
sage: FW3 = FW.isotypes([o]*4); FW3
Isomorphism types for Combinatorial species with labels [o, o, o, o]
sage: FW3.list()
[o*(o*(o*(o*{}))), o*(o*(o*(({}*o)*{}))), o*(o*((({}*o)*o)*{})), o*((({}*o)*o)*(o*{})), o*((({}*o)*o)*(({}*o)*{})), (({}*o)*o)*(o*(o*{})), (({}*o)*o)*(o*(({}*o)*{})), (({}*o)*o)*((({}*o)*o)*{})]
Binary trees:
sage: BT = CombinatorialSpecies()
sage: Leaf = SingletonSpecies()
sage: BT.define(Leaf+(BT*BT))
sage: BT5 = BT.isotypes([o]*5)
sage: BT5.list()
[o*(o*(o*(o*o))), o*(o*((o*o)*o)), o*((o*o)*(o*o)), o*((o*(o*o))*o), o*(((o*o)*o)*o), (o*o)*(o*(o*o)), (o*o)*((o*o)*o), (o*(o*o))*(o*o), ((o*o)*o)*(o*o), (o*(o*(o*o)))*o, (o*((o*o)*o))*o, ((o*o)*(o*o))*o, ((o*(o*o))*o)*o, (((o*o)*o)*o)*o]
sage: %hide
sage: def pbt_to_coordinates(t):
....: e = {}
....: queue = [t]
....: while queue:
....: z = queue.pop()
....: if not isinstance(z[0], int):
....: e[z[1]._labels[0]-1] = z
....: queue.extend(z)
....: coord = [(len(e[i][0]._labels) * len(e[i][1]._labels))
....: for i in range(len(e))]
....: return sage.geometry.polyhedra.Polytopes.project_1(coord)
....:
sage: K4 = Polyhedron(vertices=[pbt_to_coordinates(t) for t in BT.isotypes(range(5))])
sage: K4.show(fill=True).show(frame=False)
Juggling automaton:
sage: F = SingletonSpecies()
sage: state_labels = map(tuple, Permutations([0,0,1,1,1]).list())
sage: states = dict((i, CombinatorialSpecies()) for i in state_labels)
sage: def successors(state):
....: newstate = state[1:]+(0,)
....: if state[0] == 0:
....: return [(0, newstate)]
....: return [(i+1, newstate[0:i] + (1,) + newstate[i+1:])
....: for i in range(0, len(state)) if newstate[i] == 0]
...
sage: for state in state_labels:
....: states[state].define(
....: sum( [states[target]*F
....: for (height, target) in successors(state)], Eps ))
....:
sage: states
{(1, 1, 0, 1, 0): Combinatorial species, (0, 1, 1, 0, 1): Combinatorial species, (1, 1, 1, 0, 0): Combinatorial species, (1, 0, 1, 0, 1): Combinatorial species, (0, 1, 0, 1, 1): Combinatorial species, (1, 0, 0, 1, 1): Combinatorial species, (0, 1, 1, 1, 0): Combinatorial species, (1, 0, 1, 1, 0): Combinatorial species, (0, 0, 1, 1, 1): Combinatorial species, (1, 1, 0, 0, 1): Combinatorial species}
sage: states[(1,1,1,0,0)].isotypes([o]*5).cardinality()
165
Lattice points of polytopes¶
sage: A=random_matrix(ZZ,3,6,x=7)
sage: L=LatticePolytope(A)
sage: L.plot3d()
sage: L.npoints() # should be cardinality!
28
This example used PALP and J-mol
Graphs up to an isomorphism¶
sage: show(graphs(5, lambda G: G.size() <= 4))
Words¶
TODO: link to sage.combinat.words.demo
, and possibly move/merge
there the material here.
An infinite periodic word:
sage: p = Word([0,1,1,0,1,0,1]) ^ Infinity
sage: p
word: 0110101011010101101010110101011010101101...
The Fibonacci word:
sage: f = words.FibonacciWord()
sage: f
word: 0100101001001010010100100101001001010010...
The Thue-Morse word:
sage: t = words.ThueMorseWord()
sage: t
word: 0110100110010110100101100110100110010110...
A random word over the alphabet [0, 1] of length 1000:
sage: r = words.RandomWord(1000,2)
sage: r
word: 0010101011101100110000000110000111011100...
The fixed point of a morphism:
sage: m = WordMorphism('a->acabb,b->bcacacbb,c->baba')
sage: w = m.fixed_point('a')
sage: w
word: acabbbabaacabbbcacacbbbcacacbbbcacacbbac...
Their prefixes of length 1000:
sage: pp = p[:1000]
sage: ff = f[:1000]
sage: tt = t[:1000]
sage: ww = w[:1000]
A comparison of their complexity function:
sage: A = list_plot([pp.number_of_factors(i) for i in range(100)], color='red')
sage: B = list_plot([ff.number_of_factors(i) for i in range(100)], color='blue')
sage: C = list_plot([tt.number_of_factors(i) for i in range(100)], color='green')
sage: D = list_plot([r.number_of_factors(i) for i in range(100)], color='black')
sage: E = list_plot([ww.number_of_factors(i) for i in range(100)], color='orange')
sage: A + B + C + D + E
Construction of a permutation and builds its associated Rauzy diagram:
sage: p = iet.Permutation('a b c d', 'd c b a')
sage: p
a b c d
d c b a
sage: r = p.rauzy_diagram()
sage: r
Rauzy diagram with 7 permutations
sage: r.graph().plot()
Let us now construct a self-similar interval exchange transformation associated to a loop in the Rauzy diagram:
sage: g0 = r.path(p, 't', 't', 'b', 't')
sage: g1 = r.path(p, 'b', 'b', 't', 'b')
sage: g = g0 + g1
sage: m = g.matrix()
sage: v = m.eigenvectors_right()[-1][1][0]
sage: T = iet.IntervalExchangeTransformation(p, v)
We can plot it and all its power:
sage: T.plot()
sage: (T*T).plot()
sage: (T*T*T).plot()
Check the self similarity of T:
sage: T.rauzy_diagram(iterations=8).normalize(T.length()) == T
True
And get the symbolic coding of 0 using the substitution associated to the path:
sage: s = g.orbit_substitution()
sage: s.fixed_point('a')
word: adbdadcdadbdbdadcdadbdadcdadccdadcdadbda...
Predefined algebraic structures¶
Root systems, Coxeter groups, …¶
sage: L = RootSystem(['A',2,1]).weight_space()
sage: L.plot(size=[[-1..1],[-1..1]],alcovewalks=[[0,2,0,1,2,1,2,0,2,1]])
sage: CartanType.samples()
[['A', 1], ['A', 5], ['B', 1], ['B', 5], ['C', 1], ['C', 5], ['D', 2], ['D', 3], ['D', 5], ['E', 6], ['E', 7], ['E', 8], ['F', 4], ['G', 2], ['I', 5], ['H', 3], ['H', 4], ['A', 1, 1], ['A', 5, 1], ['B', 1, 1], ['B', 5, 1], ['C', 1, 1], ['C', 5, 1], ['D', 3, 1], ['D', 5, 1], ['E', 6, 1], ['E', 7, 1], ['E', 8, 1], ['F', 4, 1], ['G', 2, 1], ['B', 5, 1]^*, ['C', 4, 1]^*, ['F', 4, 1]^*, ['G', 2, 1]^*, ['BC', 1, 2], ['BC', 5, 2]]
sage: T = CartanType(["E", 8, 1])
sage: print(T.dynkin_diagram())
O 2
|
|
O---O---O---O---O---O---O---O
1 3 4 5 6 7 8 0
E8~
sage: T.is_simply_laced(), T.is_finite(), T.is_crystalographic()
(True, False, True)
sage: W = WeylGroup(["B", 3])
sage: W
Weyl Group of type ['B', 3] (as a matrix group acting on the ambient space)
sage: W.cayley_graph(side = "left").plot3d(color_by_label = True)
sage: print(W.character_table()) # Thanks GAP!
CT1
2 4 4 3 3 4 3 1 1 3 4
3 1 . . . . . 1 1 . 1
1a 2a 2b 4a 2c 2d 6a 3a 4b 2e
X.1 1 1 1 1 1 1 1 1 1 1
X.2 1 1 1 -1 -1 -1 -1 1 1 -1
X.3 1 1 -1 -1 1 -1 1 1 -1 1
X.4 1 1 -1 1 -1 1 -1 1 -1 -1
X.5 2 2 . . -2 . 1 -1 . -2
X.6 2 2 . . 2 . -1 -1 . 2
X.7 3 -1 1 1 1 -1 . . -1 -3
X.8 3 -1 -1 -1 1 1 . . 1 -3
X.9 3 -1 -1 1 -1 -1 . . 1 3
X.10 3 -1 1 -1 -1 1 . . -1 3
sage: rho = SymmetricGroupRepresentation([3, 2], "orthogonal"); rho
Orthogonal representation of the symmetric group corresponding to [3, 2]
sage: rho([1, 3, 2, 4, 5])
1 & 0 & 0 & 0 & 0 \\
0 & -\frac{1}{2} & \frac{1}{2} \, \sqrt{3} & 0 & 0 \\
0 & \frac{1}{2} \, \sqrt{3} & \frac{1}{2} & 0 & 0 \\
0 & 0 & 0 & -\frac{1}{2} & \frac{1}{2} \, \sqrt{3} \\
0 & 0 & 0 & \frac{1}{2} \, \sqrt{3} & \frac{1}{2}
Affine Weyl groups:
sage: W = WeylGroup(["C", 3, 1])
sage: W
Weyl Group of type ['C', 3, 1] (as a matrix group acting on the root space)
sage: W.category()
Category of affine weyl groups
sage: W.an_element()
[-1 1 0 0]
[ 0 1 0 0]
[ 0 0 1 0]
[ 0 0 0 1]
sage: W.from_reduced_word([1,2,3,0,3,0,3,2,1,3,3,2]).stanley_symmetric_function()
256*m[1, 1, 1, 1, 1, 1] + 128*m[2, 1, 1, 1, 1] + 64*m[2, 2, 1, 1] + 32*m[2, 2, 2] + 48*m[3, 1, 1, 1] + 24*m[3, 2, 1] + 8*m[3, 3] + 16*m[4, 1, 1] + 8*m[4, 2] + 4*m[5, 1]
Symmetric functions¶
Classical basis:
sage: Sym = SymmetricFunctions(QQ)
sage: Sym
Symmetric Functions over Rational Field
sage: s = Sym.schur()
sage: h = Sym.complete()
sage: e = Sym.elementary()
sage: m = Sym.monomial()
sage: p = Sym.powersum()
sage: m(( ( h[2,1] * ( 1 + 3 * p[2,1]) ) + s[2](s[3])))
Jack polynomials:
sage: Sym = SymmetricFunctions(QQ['t'])
sage: Jack = Sym.jack_polynomials() # todo: not implemented
sage: P = Jack.P(); J = Jack.J(); Q = Jack.Q() # todo: not implemented
sage: J(P[2,1]) # todo: not implemented
Traceback (most recent call last):
...
AttributeError: 'SymmetricFunctions_with_category' object has no attribute 'jack_polynomials'
Macdonald polynomials:
sage: J = MacdonaldPolynomialsJ(QQ)
sage: P = MacdonaldPolynomialsP(QQ)
sage: Q = MacdonaldPolynomialsQ(QQ)
sage: J
Macdonald polynomials in the J basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field
sage: f = P(J[2,2] + 3 * Q[3,1])
sage: f
(q^2*t^6-q^2*t^5-q^2*t^4-q*t^5+q^2*t^3+2*q*t^3+t^3-q*t-t^2-t+1)*McdP[2, 2] + ((3*q^3*t^5-6*q^3*t^4+3*q^3*t^3-3*q^2*t^4+6*q^2*t^3-3*q^2*t^2-3*q*t^3+6*q*t^2-3*q*t+3*t^2-6*t+3)/(q^7*t-2*q^6*t+2*q^4*t-q^4-q^3*t+2*q^3-2*q+1))*McdP[3, 1]
sage: f
sage: Sym = SymmetricFunctions(J.base_ring())
sage: s = Sym.s()
sage: s(f)
Semigroups¶
sage: Cat = FiniteSemigroups()
sage: Cat
Category of finite semigroups
sage: Cat.category_graph().plot(layout = "acyclic")
sage: S = Cat.example(alphabet = ('a','b','c'))
sage: S
An example of finite semi-group: the left regular band generated by ('a', 'b', 'c')
sage: S.cayley_graph(side = "left", simple = True).plot()
sage: S.j_transversal_of_idempotents()
['cab', 'ca', 'ab', 'cb', 'a', 'c', 'b']
sage: S
Hopf algebras¶
sage: Cat = HopfAlgebrasWithBasis(QQ); Cat
Category of hopf algebras with basis over Rational Field
sage: g = Cat.category_graph()
sage: g.set_latex_options(format = "dot2tex")
sage: view(g, tightpage = True, viewer = "pdf")
sage: Cat
sage: H = Cat.example()
sage: H
The Hopf algebra of the Dihedral group of order 6 as a permutation group over Rational Field
sage: H
A real life example¶
sage: def path_to_line(path, grid=True):
....: vert = lambda x, y: circle((x, y), .05, rgbcolor=(0, 0, 1), fill=True)
....: pline = [(0,0)]
....: vertices = [vert(0,0)]
....: h = 0
....: maxh = h
....: ln = len(path)
....: for x, y in enumerate(path):
....: h += y
....: if h > maxh:
....: maxh = h
....: pline += [(x+1, h)]
....: vertices += [vert(x+1, h)]
....: plotted_path = line(pline) + sum(vertices)
....: if grid:
....: gridline = lambda a, b, c, d: line([(a, b), (c,d)], rgbcolor=(0,) * 3, linestyle='--', alpha=.25)
....: # vertical gridlines
....: grid = [gridline(x, 0, x, maxh) for x in [1..ln]]
....: # horizontal gridlines
....: for y in [1..maxh]:
....: grid += [gridline(0, y, ln, y)]
....: plotted_path += sum(grid)
....: plotted_path.set_aspect_ratio(1)
....: return plotted_path
sage: from sage.combinat.backtrack import GenericBacktracker
sage: class LukPaths(GenericBacktracker):
....: def __init__(self, n, k=1):
....: GenericBacktracker.__init__(self, [], (0, 0))
....: self._n = n
....: self._k = k
....: if n < 0 or k < 1 or n % (k+1) != 0:
....: def jane_stop_this_crazy_thing(*args):
....: raise StopIteration
....: self._rec = jane_stop_this_crazy_thing
....: def _rec(self, path, state):
....: ln, ht = state
....: if ln < self._n:
....: # if we're high enough, we can yield a path with a
....: # k-downstep at the end
....: if ht >= self._k:
....: yield path + [-self._k], (ln + 1, ht - self._k), False
....: # if the path isn't too high, it can also take an upstep
....: if ht / self._k < (self._n - ln):
....: yield path + [1], (ln + 1, ht + 1), False
....: else:
....: # if length is n, set state to None so we stop trying to
....: # make new paths, and yield what we've got
....: yield path, None, True
sage: plots = [path_to_line(p) for p in LukPaths(12, 2)]
sage: ga = graphics_array(plots, ceil(len(plots)/6), 6)
sage: ga.show(figsize=[9,7])
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Algebraic Combinatorics¶
Tableaux and the like¶
sage: s = Permutation([5,3,2,6,4,8,9,7,1])
sage: s
sage: (p,q) = s.robinson_schensted()
sage: p.pp()
1 4 7 9
2 6 8
3
5
sage: p.row_stabilizer()
Permutation Group with generators [(), (7,9), (6,8), (4,7), (2,6), (1,4)]
Symmetric functions¶
Usual bases:
sage: Sym = SymmetricFunctions(QQ); Sym
Symmetric Functions over Rational Field
sage: Sym.inject_shorthands()
sage: m(( ( h[2,1] * ( 1 + 3 * p[2,1]) ) + s[2](s[3])))
Macdonald polynomials:
sage: J = MacdonaldPolynomialsJ(QQ)
sage: P = MacdonaldPolynomialsP(QQ)
sage: Q = MacdonaldPolynomialsQ(QQ)
sage: J
Macdonald polynomials in the J basis over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field
sage: P(J[2,2] + 3 * Q[3,1])
Root systems¶
sage: L = RootSystem(['A',2,1]).weight_space()
sage: L.plot(size=[[-1..1],[-1..1]],alcovewalks=[[0,2,0,1,2,1,2,0,2,1]])
sage: W = WeylGroup(["B", 3])
sage: W.cayley_graph(side = "left").plot3d(color_by_label = True)
GAP at work¶
sage: print(W.character_table()) # Thanks GAP!
CT1
2 4 4 3 3 4 3 1 1 3 4
3 1 . . . . . 1 1 . 1
1a 2a 2b 4a 2c 2d 6a 3a 4b 2e
X.1 1 1 1 1 1 1 1 1 1 1
X.2 1 1 1 -1 -1 -1 -1 1 1 -1
X.3 1 1 -1 -1 1 -1 1 1 -1 1
X.4 1 1 -1 1 -1 1 -1 1 -1 -1
X.5 2 2 . . -2 . 1 -1 . -2
X.6 2 2 . . 2 . -1 -1 . 2
X.7 3 -1 1 1 1 -1 . . -1 -3
X.8 3 -1 -1 -1 1 1 . . 1 -3
X.9 3 -1 -1 1 -1 -1 . . 1 3
X.10 3 -1 1 -1 -1 1 . . -1 3
sage: type(W.character_table())
sage: G = gap(W); G
sage: G.Ch
sage: T = G.CharacterTable(); T
sage: T.Irr()[10,10]
sage: type(T.Irr()[10,10])
Coxeter3 at work¶
sage: W3 = CoxeterGroup(W, implementation="coxeter3")
sage: KL = matrix([ [ W3.kazhdan_lusztig_polynomial(u,v) if u.bruhat_le(v) else 0 for u in W3 ]
....: for v in W3])
sage: show(KL)
sage: W = WeylGroup(["C", 3, 1])
sage: W
Weyl Group of type ['C', 3, 1] (as a matrix group acting on the root space)
sage: W.from_reduced_word([1,2,3,0,3,0,3,2,1,3,3,2]).stanley_symmetric_function()
256*m[1, 1, 1, 1, 1, 1] + 128*m[2, 1, 1, 1, 1] + 64*m[2, 2, 1, 1] + 32*m[2, 2, 2] + 48*m[3, 1, 1, 1] + 24*m[3, 2, 1] + 8*m[3, 3] + 16*m[4, 1, 1] + 8*m[4, 2] + 4*m[5, 1]
Crystals¶
sage: latex.jsmath_avoid_list(['tikzpicture'])
sage: K = KirillovReshetikhinCrystal(['A',3,1], 2,2)
sage: G = K.digraph()
sage: G.set_latex_options(format = "dot2tex", edge_labels = True, color_by_label = {0:"black", 1:"blue", 2:"red", 3:"green"}, edge_options = lambda (u,v,label):({"backward":label ==0}))
sage: view(G, viewer="pdf", tightpage=True)
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Combinatorics (short)¶
Counting¶
sage: Partitions(100000).cardinality()
Species:
sage: from sage.combinat.species.library import *
sage: o = var("o")
sage: BT = CombinatorialSpecies()
sage: Leaf = SingletonSpecies()
sage: BT.define(Leaf+(BT*BT))
sage: BT.isotypes([o]*5).list()
[o*(o*(o*(o*o))), o*(o*((o*o)*o)), o*((o*o)*(o*o)), o*((o*(o*o))*o), o*(((o*o)*o)*o), (o*o)*(o*(o*o)), (o*o)*((o*o)*o), (o*(o*o))*(o*o), ((o*o)*o)*(o*o), (o*(o*(o*o)))*o, (o*((o*o)*o))*o, ((o*o)*(o*o))*o, ((o*(o*o))*o)*o, (((o*o)*o)*o)*o]
Words¶
sage: m = WordMorphism('a->acabb,b->bcacacbb,c->baba')
sage: m.fixed_point('a')
word: acabbbabaacabbbcacacbbbcacacbbbcacacbbac...
For more, see: sage.combinat.words.demo
.
Lattice points of polytopes¶
sage: A=random_matrix(ZZ,3,6,x=7)
sage: L=LatticePolytope(A)
sage: L.plot3d()
sage: L.npoints() # should be cardinality!
28
This example used PALP and J-mol
Graphs up to an isomorphism¶
sage: show(graphs(5, lambda G: G.size() <= 4))
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Tutorial: Enumerated sets¶
Exercice: Poker and probability¶
A poker card is characterized by a suit (“spades”, “hearts”, “diamonds”, “clubs”) and a rank (\(2, 3, \dots, 9\), “Jack”, “Queen”, “King” and “Ace”). Here is the way to define suits in sage:
sage: Suits = Set(["spades", "hearts", "diamonds", "clubs"])
Define similarly the set Ranks
:
sage: # edit here
The deck of card is the cartesian product of the set of suits by the set of
ranks. Define a set Cards
accordingly:
sage: # edit here
Use the method .cardinality()
to compute the number of suits, ranks and
cards:
sage: # edit here
sage: # edit here
sage: # edit here
Draw a card at random:
sage: # edit here
Cards are (currently) returned as lists. To be able to build a set of cards, we need them to be hashable. Let’s redefine the set of cards by transforming cards to tuples:
sage: Cards = CartesianProduct(Suits, Ranks).map(tuple)
Use Subsets
to draw a hand of five cards at random:
sage: # edit here
Use .cardinality()
to compute the number of hands, check the result with
binomial
:
sage: # edit here
To go further, see exercises 38, 39, 40 in Calcul Mathématique avec Sage (version 1.0) page 255.
Using existing Enumerated Sets¶
List all the strict partitions of \(5\) (hint: use
Partitions
withmax_slope
):sage: # edit here
List all the vectors of
0
and1
of length5
(hint: useIntegerVectors
withmax_part
):sage: # edit here
You can also use a cartesian product:
sage: # edit here
List all the Dyck words of length
6
:sage: # edit here
Here is the way to print the standard tableaux of size $4$:
sage: for t in StandardTableaux(3): t.pp(); print
1 2 3
1 3
2
1 2
3
1
2
3
Define the set of all the partitions of \(1\) to \(5\) (hint: use
DisjointUnionEnumeratedSets
):sage: # edit here
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Databases¶
sage: sloane_find([1,3,19,211])
sage: T = TransitiveGroups(30)
sage: T.cardinality()
sage: T[10]
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: graphics (short)¶
sage: plot(sin(x), -pi, pi, fill = 'axis')
Taylor approximation:
sage: var('x')
sage: @interact
sage: def g(f=sin(x), c=0, n=(1..30),
....: xinterval=range_slider(-10, 10, 1, default=(-8,8), label="x-interval"),
....: yinterval=range_slider(-50, 50, 1, default=(-3,3), label="y-interval")):
....: x0 = c
....: degree = n
....: xmin,xmax = xinterval
....: ymin,ymax = yinterval
....: p = plot(f, xmin, xmax, thickness=4)
....: dot = point((x0,f(x=x0)),pointsize=80,rgbcolor=(1,0,0))
....: ft = f.taylor(x,x0,degree)
....: pt = plot(ft, xmin, xmax, color='red', thickness=2, fill=f)
....: show(dot + p + pt, ymin=ymin, ymax=ymax, xmin=xmin, xmax=xmax)
....: html('$f(x)\;=\;%s$'%latex(f))
....: html('$P_{%s}(x)\;=\;%s+R_{%s}(x)$'%(degree,latex(ft),degree))
Curves of pursuits:
sage: npi = RDF(pi)
sage: def rot(t):
....: from math import cos,sin
....: return matrix([[cos(t),sin(t)],[-sin(t),cos(t)]])
....:
sage: def pursuit(n,x0,y0,lamb,steps = 100, threshold = .01):
....: paths = [[[x0,y0]]]
....: for i in range(1,n):
....: rx,ry = list(rot(2*npi*i/n)*vector([x0,y0]))
....: paths.append([[rx,ry]])
....: oldpath = [x[-1] for x in paths]
....: for q in range(steps):
....: diffs = [[oldpath[(j+1)%n][0]-oldpath[j][0],oldpath[(j+1)%n][1]-oldpath[j][1]] for j in range(n)]
....: npath = [[oldpath[j][0]+lamb*diffs[j][0],oldpath[j][1]+lamb*diffs[j][1]] for j in range(n)]
....: for j in range(n):
....: paths[j].append(npath[j])
....: oldpath = npath
....: return paths
....:
sage: @interact
sage: def curves_of_pursuit(n = slider([2..20],default = 5, label="# of points"),steps = slider([floor(1.4^i) for i in range(2,18)],default = 10, label="# of steps"), stepsize = slider(srange(.01,1,.01),default = .2, label="stepsize"), colorize = selector(['BW','Line color', 'Filled'],default = 'BW')):
....: outpaths = pursuit(n,0,1,stepsize, steps = steps)
....: mcolor = (0,0,0)
....: outer = line([q[0] for q in outpaths]+[outpaths[0][0]], rgbcolor = mcolor)
....: polys = Graphics()
....: if colorize=='Line color':
....: colors = [hue(j/steps,1,1) for j in range(len(outpaths[0]))]
....: elif colorize == 'BW':
....: colors = [(0,0,0) for j in range(len(outpaths[0]))]
....: else:
....: colors = [hue(j/steps,1,1) for j in range(len(outpaths[0]))]
....: polys = sum([polygon([outpaths[(i+1)%n][j+1],outpaths[(i+1)%n][j], outpaths[i][j+1]], rgbcolor = colors[j]) for i in range(n) for j in range(len(outpaths[0])-1)])
....: #polys = polys[0]
....: colors = [(0,0,0) for j in range(len(outpaths[0]))]
....: nested = sum([line([q[j] for q in outpaths]+[outpaths[0][j]], rgbcolor = colors[j]) for j in range(len(outpaths[0]))])
....: lpaths = [line(x, rgbcolor = mcolor) for x in outpaths]
....: show(sum(lpaths)+nested+polys, axes = False, figsize = [5,5], xmin = -1, xmax = 1, ymin = -1, ymax =1)
Yoda: use the source!:
sage: %hide
sage: import scipy
sage: from scipy import io
sage: x=io.loadmat('/home/nthiery/Sage-Combinat/yodapose_low.mat') # you can change it to yodapose/yodapose_low to use the high/low res version
sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
sage: V=x['V']
sage: F3=x['F3']-1
sage: F4=x['F4']-1
sage: yoda=IndexFaceSet(F3,V,color='green')+IndexFaceSet(F4,V,color='green')
sage: yoda.show(figsize=5, frame=False)
This document is one of More SageMath Tutorials. You may edit it on github. \(\def\NN{\mathbb{N}}\) \(\def\ZZ{\mathbb{Z}}\) \(\def\QQ{\mathbb{Q}}\) \(\def\RR{\mathbb{R}}\) \(\def\CC{\mathbb{C}}\)
Demonstration: Combinatorics on words¶
Words¶
Finite Words¶
One can create a finite word from anything.
From Python objects:
sage: Word('abfasdfas') word: abfasdfas sage: Word([2,3,4,5,6,6]) word: 234566 sage: Word((0,1,2,1,2,)) word: 01212
From an iterator:
sage: it = iter(range(10)) sage: Word(it) word: 0123456789
From a function:
sage: f = lambda n : (3 ^ n) % 5 sage: Word(f, length=20) word: 13421342134213421342
Infinite Words¶
One can create an infinite word.
From an iterator:
sage: from itertools import count, repeat sage: Word(count()) word: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,... sage: Word(repeat('a')) word: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...
From a function:
sage: f = lambda n : (3 ^ n) % 5 sage: Word(f) word: 1342134213421342134213421342134213421342...
Word methods and algorithms¶
There are more than one hundreds methods and algorithms implemented for finite words and infinite words. One can list them using the TAB command:
sage: w = Word(range(10))
sage: w.<TAB>
For instance, one can slice an infinite word to get a certain finite factor and compute its factor complexity:
sage: w = Word(p % 3 for p in primes(10000))
sage: w
word: 2021212122112122211211221212121221211122...
sage: factor = w[1000:2000]
sage: factor
word: 1122111211211222121222211211121212211212...
sage: map(factor.number_of_factors, range(20))
[1, 2, 4, 8, 16, 32, 62, 110, 156, 190, 206, 214, 218, 217, 216, 215, 214, 213, 212, 211]
Word Morphisms¶
Creation¶
Creation of a word morphism:
from a dictionary:
sage: m = WordMorphism({'a':'abcab','b':'cb','c':'ab'}) sage: print m WordMorphism: a->abcab, b->cb, c->ab
from a string:
sage: m = WordMorphism('a->abcab,b->cb,c->ab') sage: print m WordMorphism: a->abcab, b->cb, c->ab
Word Morphisms methods¶
Image of a word under a morphism:
sage: m('a')
word: abcab
sage: m('abcabc')
word: abcabcbababcabcbab
Power of a morphism:
sage: print m ^ 2
WordMorphism: a->abcabcbababcabcb, b->abcb, c->abcabcb
Incidence matrix:
sage: matrix(m)
[2 0 1]
[2 1 1]
[1 1 0]
Fixed point of a morphism¶
Iterated image under a morphism:
sage: print m
WordMorphism: a->abcab, b->cb, c->ab
sage: m('c')
word: ab
sage: m(m('c'))
word: abcabcb
sage: m(m(m('c')))
word: abcabcbababcabcbabcb
sage: m('c', 3)
word: abcabcbababcabcbabcb
Infinite fixed point of morphism:
sage: m('a', Infinity)
word: abcabcbababcabcbabcbabcabcbabcabcbababca...
or equivalently:
sage: m.fixed_point('a')
word: abcabcbababcabcbabcbabcabcbabcabcbababca...
S-adic sequences¶
Definition¶
Let \(w\) be a infinite word over an alphabet \(A=A_0\).
\(w\\in A_0^*\\xleftarrow{\\sigma_0}A_1^*\\xleftarrow{\\sigma_1}A_2^*\\xleftarrow{\\sigma_2} A_3^*\\xleftarrow{\\sigma_3}\\cdots\)
A standard representation of \(w\) is obtained from a sequence of substitutions \(\\sigma_k:A_{k+1}^*\\to A_k^*\) and a sequence of letters \(a_k\\in A_k\) such that:
\(w = \\lim_{k\\to\\infty}\\sigma_0\\circ\\sigma_1\\circ\\cdots\\sigma_k(a_k)\).
Given a set of substitutions \(S\), we say that the representation is \(S\) -adic standard if the subtitutions are chosen in \(S\).
One finite example¶
Let \(A_0=\\{g,h\\}\), \(A_1=\\{e,f\\}\), \(A_2=\\{c,d\\}\) and \(A_3=\\{a,b\\}\). Let \(\\sigma_0 : \\begin{array}{l}e\\mapsto gh\\\\f\\mapsto hg\\end{array}\), \(\\sigma_1 : \\begin{array}{l}c\\mapsto ef\\\\d\\mapsto e\\end{array}\) and \(\\sigma_2 : \\begin{array}{l}a\\mapsto cd\\\\b\\mapsto dc\\end{array}\).
\(\\begin{array}{lclclcl} g \\\\ gh \& \\xleftarrow{\\sigma_0} \& e \\\\ ghhg \& \\xleftarrow{\\sigma_0} \& ef \& \\xleftarrow{\\sigma_1} \& c \\\\ ghhggh \& \\xleftarrow{\\sigma_0} \& efe \& \\xleftarrow{\\sigma_1} \& cd \& \\xleftarrow{\\sigma_2} \& a\\end{array}\)
Let us define three morphisms and compute the first nested succesive prefixes of the s-adic word:
sage: sigma0 = WordMorphism('e->gh,f->hg')
sage: sigma1 = WordMorphism('c->ef,d->e')
sage: sigma2 = WordMorphism('a->cd,b->dc')
sage: words.s_adic([sigma2],'a')
word: cd
sage: words.s_adic([sigma1,sigma2],'ca')
word: efe
sage: words.s_adic([sigma0,sigma1,sigma2],'eca')
word: ghhggh
When the given sequence of morphism is finite, one may simply give
the last letter, i.e. 'a'
, instead of giving all of them,
i.e. 'eca'
:
sage: words.s_adic([sigma0,sigma1,sigma2],'a')
word: ghhggh
But if the letters don’t satisfy the hypothesis of the algorithm (nested prefixes), an error is raised:
sage: words.s_adic([sigma0,sigma1,sigma2],'ecb')
Traceback (most recent call last):
...
ValueError: The hypothesis of the algorithm used is not satisfied: the image of the 3-th letter (=b) under the 3-th morphism (=WordMorphism: a->cd, b->dc) should start with the 2-th letter (=c).
Infinite examples¶
Let \(A=A_i=\\{a,b\\}\) for all \(i\) and Let \(S = \\left\\{ tm : \\begin{array}{l}a\\mapsto ab\\\\b\\mapsto ba\\end{array}, fibo : \\begin{array}{l}a\\mapsto ab\\\\b\\mapsto a\\end{array} \\right\\}\).
\(\\begin{array}{lclclcl} a \\\\ ab \& \\xleftarrow{tm} \& a \\\\ abba \& \\xleftarrow{tm} \& ab \& \\xleftarrow{fibo} \& a \\\\ abbaab \& \\xleftarrow{tm} \& aba \& \\xleftarrow{fibo} \& ab \& \\xleftarrow{tm} \& a \\end{array}\)
Let us define the Thue-Morse and the Fibonacci morphism
and let’s import the repeat
tool from the itertools
:
sage: tm = WordMorphism('a->ab,b->ba')
sage: fib = WordMorphism('a->ab,b->a')
sage: from itertools import repeat
Fixed point are trivial examples of infinite s-adic words:
sage: words.s_adic(repeat(tm), repeat('a'))
word: abbabaabbaababbabaababbaabbabaabbaababba...
sage: tm.fixed_point('a')
word: abbabaabbaababbabaababbaabbabaabbaababba...
Let us alternate the application of the substitutions \(tm\) and \(fibo\) according to the Thue-Morse word:
sage: tmwordTF = words.ThueMorseWord('TF')
sage: words.s_adic(tmwordTF, repeat('a'), {'T':tm,'F':fib})
word: abbaababbaabbaabbaababbaababbaabbaababba...
Random infinite s-adic words:
sage: from sage.misc.prandom import randint
sage: def it():
....: while True: yield randint(0,1)
sage: words.s_adic(it(), repeat('a'), [tm,fib])
word: abbaabababbaababbaabbaababbaabababbaabba...
sage: words.s_adic(it(), repeat('a'), [tm,fib])
word: abbaababbaabbaababbaababbaabbaababbaabba...
sage: words.s_adic(it(), repeat('a'), [tm,fib])
word: abaaababaabaabaaababaabaaababaaababaabaa...
Language¶
Soon in Sage…