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

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

Documentation Status

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

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

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}}\)

Finding help

Here are several ways of getting help from within Sage.

Tab completion

Does Sage have a command for defining a permutation? (Hint: Start typing Perm and then hit the tab key.)

sage: Perm
?: documentation and examples

To see documentation and examples for the Permutation command, type Permutation? or Permutation( and hit tab (or enter).

sage: Permutation

Exercises:

  1. Create the permutation \(51324\) and assign it to the variable p .

    sage: # edit here
    
  2. Find the inverse and the length of p . (Hint: to see the methods available to p , you can type ‘ p. ‘ and hit tab.)

    sage: p.
    
  3. Does p have the pattern \(123\)? What about \(1234\)? And \(312\)?

    sage: p.
    
??: get source code

To see the how the inverse of p is computed, type p.inverse?? and hit tab (or enter).

sage: p.inverse()
Searching documentation

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:

  1. 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()
    
  2. 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()
    
Project Euler

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.

Project Euler Problem 3

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.)

Project Euler Problem 5

\(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}}\)

Working with Lists

To create a list of objects, use square brackets.

  1. Create the list [63, 12, -10, 'a', 12] , assign it to the variable L , and print the list. (Hint : Variable assignment in Sage/Python is done with = . For example, a = 3 defines the a to be 3.)

    sage: # edit here
    
  2. Use the len command to find the length of the list L.

    sage: # edit here
    
  3. To access an element of the list, use the syntax L[i] , where i is the index of the item. What is L[3]?

    sage: # edit here
    
  4. What is L[1]?

    sage: # edit here
    
  5. What is the index of the first item of L?

    sage: # edit here
    
  6. What is L[-1], L[-2]?

    sage: # edit here
    
  7. 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.

  1. Change L[3] to 17.

    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.

  1. By typing L.<tab key>, you get a list of methods for L. Use one of these methods to append 17 to the end of L.

    sage: # edit here
    
  2. Insert the letter ‘b’ at index position 2 (do not change the element in position 2, but add a new element).

    sage: # edit here
    
  3. Remove the second occurrence of \(12\) from L.

    sage: # edit here
    
  4. Redefine L to be the list [3, 1, 4, 1, 5, -1, 0].

    sage: # edit here
    
  5. Reverse the list L.

    sage: # edit here
    
  6. Sort the list L.

    sage: # edit here
    
  1. Guess the result of the following commands.

    sage: L = [3, 1, 2]
    sage: M = L.sort()
    sage: print L, M
    
  2. Now try the following.

    sage: L = [3, 1, 2]
    sage: M = sorted(L)
    sage: print L, M
    
The range command

The range command provides an easy way to construct a list of integers.

  1. Read the documentation (type: range? and hit enter or tab). Use it to create the list \([1,2,\ldots,50]\).

    sage: # edit here
    
  2. Create the list of even numbers between 1 and 100 (including 100).

    sage: # edit here
    
  3. The step argument in the range command can be negative. Use range to construct the list \([10, 7, 4, 1, -2]\).

    sage: # edit here
    
  4. 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
    
  5. 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
    
List Comprehensions

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:

  1. Create two lists:

    \[\begin{split}x = [1, 2, \ldots, 100] \\ y = [1^2, 2^2, \ldots, 100^2]\end{split}\]
    sage: # edit here
    
  2. Use a list comprehension to construct the list

    \[[x_0 + y_0, x_1 + y_1, \ldots, x_{99}+y_{99}]\]
    sage: # edit here
    
  3. Using a list comprehension and the command sum, compute

    \[\sum_{i=0}^{99} x_i y_i\]
    sage: # edit here
    
Project Euler Problem 6

The sum of the squares of the first ten natural numbers is:

\[1^2 + 2^2 + 3^2 + ... + 10^2 = 385\]

The square of the sum of the first ten natural numbers is:

\[(1 + 2 + ... + 10)^2 = 3025\]

Hence the difference between the sum of the squares of the first ten natural numbers and the square of the sum is

\[3025 - 385 = 2640\]

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
Filtering lists with a list comprehension

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
Project Euler Problem 1

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}}\)

First steps towards programming

In this worksheet, you will learn to define functions, write for loops, conditional statements and and continue to learn about lists.

Functions

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:

  1. Define a function called square that returns the square of a number:

    sage: # edit here
    
If statement

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:

  1. Define a function sign that returns the sign of a number:

    sage: # edit here
    
For loops

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:

  1. 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
    
Project Euler Problem 2

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, \ldots\]

Find the sum of all the even-valued terms in the sequence which do not exceed four million.

sage: # edit here
Slicing lists

You can slice a list to obtain only part of it. The syntax is L[start:stop:step].

  1. Let L = range(100), and try the following L[0:3], L[:3], L[1:], L[1:-1], L[::2].

    sage: # edit here
    
  2. Create a list L.

    sage: # edit here
    
  3. Use a slice to obtain the reversal of L .

    sage: # edit here
    
  4. Revese the list L using L.reverse() .

    sage: # edit here
    
  5. What is the difference between these methods of reversing a list?

    sage: # edit here
    
Project Euler Problem 4

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}}\)

Calculus, plotting & interact
Some differentiating and plotting

Exercises

  1. Let \(f(x) = x^4 + x^3 - 13 x^2 - x + 12\). Define \(f\) as a symbolic function.

    sage: # edit here
    
  2. Plot \(f\) on the domain \(-4.5 \leq x \leq 3.5\).

    sage: # edit here
    
  3. 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
    
  4. 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
    
  5. 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
    
  6. 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
    
  7. 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
    
  8. Convert the function you created above into an @interact object. Turn the argument \(x\) into a slider . (Hint: see the documentation for interact for examples on creating sliders.)

    sage: # edit here
    
Differential Equations

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:

\[y'(t) + y(t) = 1\]

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

  1. 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
    
  2. 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
    
Problem

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

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.

The 3n+1 operation

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

  1. Write a function that implements this operation, and compute the images of \(1, 2, \ldots 100\).

    sage: # edit here
    
Statement of the conjecture

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:

\[6, 3, 10, 5, 16, 8, 4, 2, 1, 4, 2, 1, 4, 2, 1, 4, 2, 1, \ldots\]

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

  1. 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 for 1, 3, 6, 9, 16, 27

    (Hint : You might find a while helpful here. Below is a very simple example that repeatedly adds 2 to the variable x until x is no longer less than 7.)

    x = 0
    while x < 7:
        x = x + 2
    print x
    
    sage: # edit here
    
  2. Use the line command to plot the sequence for 27.

    sage: # edit here
    
  3. Write an @interact function that takes an integer \(n\) and plots the sequence for \(n\).

    sage: # edit here
    
Stopping Time

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

  1. 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
    
  2. 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
    
Extension to Complex Numbers

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

  1. Implement the \(\frac{3n+1}{2}\)-operator.

    sage: # edit here
    
  2. 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
    
  3. 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
    
  4. Use the complex_plot command to plot g in the domain \(x=-5,...,5\) and \(y=-5,...,5\).

    sage: edit here
    
  5. 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 and graphics_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 with CDF.pi(). Type CDF? and CDF.pi? for more information.)

    sage: edit here
    
  6. 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 and interpolation options for the complex_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}}\)

Linear Algebra
Vectors

To create a vector in Sage, use the vector command.

Note

vectors in Sage are row vectors!

Exercises

  1. Create the vector \(x = (1, 2, \ldots, 100)\).

    sage: # edit here
    
  2. Create the vector \(y = (1^2, 2^2, \ldots, 100^2)\).

    sage: # edit here
    
  3. Type x. and hit tab to see the available methods for vectors. Find the norm (length) of the vectors x and y.

    sage: # edit here
    
  4. Find the dot product of x and y .

    sage: # edit here
    

[The above exercises are essentially the first problem on Exercise Set 1 of William Stein’s Math 480b]

Matrices
  1. 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}\]
    1. Find the echelon form of the above matrix.

      sage: # edit here
      
    2. Find the right kernel of the matrix.

      sage: # edit here
      
    3. Find the eigenvalues of the matrix.

      sage: # edit here
      
    4. Find the left eigenvectors of the matrix.

      sage: # edit here
      
    5. Find the right eigenspaces of the matrix.

      sage: # edit here
      
  2. 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]

  3. 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]

  4. 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
    
  5. 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]

  6. 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
    
Project Euler Problem 11

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 08
49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00
81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65
52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 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 03 45 02 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 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21
24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72
21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95
78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92
16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57
86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58
19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40
04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66
88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69
04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36
20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16
20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54
01 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}}\)

Strings and the Burrows-Wheeler Transform

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

  1. Create and print the following string

      \ | ( | ) / /
    _________________
    |               |
    |               |
    |  I <3 Coffee! /--\
    |               |  |
     \             /\--/
      \___________/
    
  2. Without using cut-and-paste(!) replace the substring I <3 Coffee! with the substring I <3 Tea!.

  3. Print a copy of your string with all the letters capitalized (upercase).

Operations on strings

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

  1. The factors of length 2 of ‘rhubarb’ are

    rh
    hu
    ub
    ba
    ar
    rb

    Write a function called factors that returns a list of the factors of length l of s , and list all the factors of length 3 of ‘rhubarb’.

    sage: # edit here
    
  2. 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
    
Run-length encoding

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

  1. 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
    
Rotations

The rotations of the string ‘bananas’ are:

bananas
ananasb
nanasba
anasban
nasbana
asbanan
sbanana

and if we sort these alphabetically, then we get:

ananasb
anasban
asbanan
bananas
nanasba
nasbana
sbanana

Exercises

  1. 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

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’:

ananasb
anasban
asbanan
bananas
nanasba
nasbana
sbanana

then the last column is bnnsaaa , so the BWT of bananas is bnnsaaa.

Exercises

  1. 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
    
  2. Combine the functions you defined above to create an @interact object that takes a string s 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
    
  3. Use your interact object to explore this transformation, and to answer the following questions.

    1. Compute the BWT of the following.
      • xxyxyxyxyxyxyxyxyxxyxyxyxyxyxyxyxyxy
      • 01101001100101101001011001101001100101100110100101
      • cdccdcdccdccdcdccdcdccdccdcdccdccdcdccdcdccdccdcdc
    2. Do you notice any patterns in the BWT of a string?
    3. Can you think of an application for this transformation?
    4. Find 3 other strings that have a ‘nice’ image under the BWT.
    5. Is the Burrows-Wheeler transformation invertible? (That is, can you find two strings that have the same BWT?)
    sage: # edit here
    
  4. 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 of s .

    sage: # edit here
    
  5. 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
    
  6. 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
    
  7. Write a function that reconstructs the entire array of sorted rotations of a string from the BWT of the string.

    sage: # edit here
    
  8. 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
    
  9. 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}}\)

Dictionaries and Graph Theory
Dictionaries

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

  1. In the directed graph below, the vertex 1 points to the vertices in the list [2, 3].
  1. Use the DiGraph command to contruct the above directed graph, and plot the directed graph (Hint : In the documentation for DiGraph , take a look at the dictionary of lists example.)

    sage: # edit here
    
  2. Find the adjacency matrix of the graph you constructed above.

    sage: # edit here
    
  3. 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
    
The Seven Bridges of Königsberg

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

  1. Enter the graph on the right into Sage (use the Graph command, not the DiGraph command).
  2. 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

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:

  1. \(v_{0,i}\) is connected to \(v_{1,i}, v_{2,i}, v_{3,i}\) for all \(0\leq i \leq 6\);
  2. \(v_{1,j}\) is connected to \(v_{1, j+1 (mod\, 7)}\) for all \(0\leq j \leq 6\);
  3. \(v_{2,j}\) is connected to \(v_{2, j+2 (mod\, 7)}\) for all \(0\leq j \leq 6\);
  4. \(v_{3,j}\) is connected to \(v_{3, j+3 (mod\, 7)}\) for all \(0\leq j \leq 6\).

Exercises

  1. Construct a dictionary V such that V[(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 writing V[i,j] is shorthand for writing V[(i,j)] . You should be able to generate the lists of vertices by using loops and list comprehensions.)

    sage: # edit here
    
Spectrum of a graph

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

  1. 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
    
  2. 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
    
Birthday Paradox

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

  1. 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
    
  2. Plot a graph g created by your function above using the g.plot(layout='circular').

    sage: # edit here
    
  3. 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 command g.num_edges() returns the number of edges in g.)

    sage: # edit here
    
  4. Repeat the above exercise with \(n=57\) vertices.

    sage: # edit here
    
  5. 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}}\)

Combinatorics in Sage
Documentation/Resources
  • 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.

Iterators/Generators

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
Project Euler Problem 2

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
Combinatorial Classes

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
Operations producing new combinatorial classes

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
Example: Vexillary involutions

A vexillary involution is a permutation that:

  1. avoids the pattern 2143;
  2. 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]]
Defining your own Combinatorial Classes

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 to iter ate through the elements of the set;
  • __repr__ : (optional) a string repr 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
Exercise: Sums of subsets

(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\):

\[\{1, 2\}, \{1, 3\}, \{1, 4\}, \{2, 3\}, \{2, 4\}, \{3, 4\}\]

And the sums of the numbers in these subsets are

\[\begin{split}\sigma(\{1, 2\}) = 3 \\ \sigma(\{1, 3\}) = 4 \\ \sigma(\{1, 4\}) = 5 \\ \sigma(\{2, 3\}) = 5 \\ \sigma(\{2, 4\}) = 6 \\ \sigma(\{3, 4\}) = 7 \\\end{split}\]

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

  1. 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
    
  2. 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
    
  3. List all the subsets of \(\{1,3,6,8,10,11\}\) of size three.

    sage: # edit here
    
  4. 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
    
  5. How many 12-element subsets of \(\{1^2, 2^2, \dots, 24^2\}\) are there?

    sage: # edit here
    
  6. 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!
Exercise: Derangements

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.

  1. Define a function called is_derangement that returns True if \(p\) is a derangement and returns False otherwise.

    sage: # edit here
    
  2. Use the filter method of Permutations to create a combinatorial class of all the derangements of [1,2,3,4], and list them.

    sage: # edit here
    
  3. Create a list of the number of derangements of an \(n\)-element set, for \(n = 1, 2, \dots, 7\).

    sage: # edit here
    
  4. 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
    
Exercise: Vexillary involutions

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.

  1. __init__(self, n): this method will take as argument a positive integer n , and it will store the value in a data attribute for later access.
  2. __repr__(self): a string representation of the object. The command Derangements(5) should print ‘Derangements of a 5-element set’.
  3. __iter__(self): implement a generator that iterates through all the derangements. ( Hint: In the exercise above you used the filter method to construct derangements; it is okay to use that here.)
  4. __contains__(self, p): implement a method that tests whether p belongs to this combinatorial class (tests whether p is a derangement).
  5. 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}}\)

Introduction to Cython

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.

A Python function

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]
First steps with Cython

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
More Cython

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):

Jeudi 8 novembre:

Sage et LaTeX

Plus tard:

Cours:

  • Catégories
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}}\)

Travaux Pratiques: Combinatoire des Mots

Note

Indications de difficulté des exercices

(*) Requiers un peu de programmation (fonctions, …)

(**) Plus difficile

(projet)

Historique

Début 1906

Morse (1920-1930): systèmes dynamiques

Gros développements: 1960

  • École Française (Schützenberger)
  • École Russe
Mots finis

Exercice: Mots finis

  1. Construire le mot “adacbdafea”:

    sage: # edit here
    

    Indication: Consulter la documentation de la fonction Word(), ainsi que Demonstration: Combinatorics on words

  2. Explorer ses propriétés (est-il un palindrome?):

    sage: # edit here
    

    Indication: utiliser la complétion automatique

Exercice: Décimales de \(\pi\)

  1. Construire le mot constitué des \(100\) premières décimales de \(pi\):

    sage: # edit here
    

    Indications: numerical_approx() et la méthode str.

  2. Compter ses facteurs de longueur \(2\):

    sage: # edit here
    
  3. Combien de décimales de \(\pi\) faut-il pour trouver tous les mots de longueur \(1\), \(2\), \(3\), … comme facteurs?

    sage: # edit here
    
  4. Comparer avec un mot aléatoire:

    sage: # edit here
    

Exercice: Ensemble des mots

  1. Lister tous les mots de longueur 6 sur l’alphabet “abc”:

    sage: # edit here
    

    Indication: Words()

  2. Lister tous les mots primitif de longueur \(6\):

    sage: # edit here
    

    Indication: Tutorial: Comprehensions, Iterators, and Iterables.

  3. Trouver le plus petit mot tel que (.. TODO:: trouver une bonne propriété)

Langages, codes et Morphismes

Définition

Un langage \(X\) est un sous ensemble de \(A^*\)

Codes

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

  1. Factoriser le mot \(aaaababaaba\) sur le code \(X=\{a,ba\}\):

    sage: # edit here
    
  2. (*) Implanter une fonction factor_a_ba(w) qui renvoie la factorisation d’un mot sur le code \(X=\{a,ba\}\):

    sage: # edit here
    
  3. (*) Implanter une fonction factor_prefixe(w,X) qui renvoie la factorisation d’un mot sur un code préfixe:

    sage: # edit here
    
  4. (**) Implanter une fonction factor(w, X) qui renvoie la factorisation d’un mot \(w\) sur un code \(X\) quelconque:

    sage: # edit here
    
  5. Déterminer la complexité des algorithmes sous-jacents.

Exercice

  1. (*) Implanter une fonction est_code_prefixe(X) qui teste si un langage fini \(X\) est un code préfixe:

    sage: # edit here
    
  2. (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?

Morphismes

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:

  1. \(f: a\mapsto aba, b\mapsto cb, c\mapsto aba\):

    sage: # edit here
    
  2. \(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

  1. Construire un mot et un morphisme de votre choix:

    sage: # edit here
    
  2. Quelle est la longueur de \(f^{10}(w)\)?

    sage: # edit here
    
  3. Tracer la courbe de la fonction \(n\mapsto l(n)\)\(l(n)\) est la longueur de \(f^n(w)\):

    sage: # edit here
    
  4. 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.

  5. Utiliser ce fait pour retracer la courbe en échelle logarithmique pour \(n\) aussi grand que possible:

    sage: # edit here
    
  6. 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:

  1. \(f\) restreint à l’alphabet \(A\) est injectif
  2. \(f(A)\) est un code

Exercice

  1. (*) Implanter une fonction est_injective(f) qui calcule si le morphisme \(f\) est injectif:

    sage: # edit here
    
  2. (*) Implanter une fonction preimage(f,w) qui calcule la préimage d’un mot \(f\) par une fonction \(f\) injective:

    sage: # edit here
    
  3. (projet) Intégrer ces méthodes dans Sage

Théorème du défaut

Deux formulations:

  1. 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.

  2. 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:

  1. \(x\) et \(y\) commutent
  2. Il existe \(n,m>0\) tels que \(x^m=y^n\)
  3. Il existe \(z\in A^+\) tels que \(x,y\in z^*\)
  4. \(\{x,y\}\) n’est pas un code ou \(x=y\)

Il existe toute une littérature sur les équations sur les mots.

Conjugaison, périodicité, répétitions
Mots primitifs

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^*\)\(z\) est la racine primitive de \(w\).

Périodes, répétitions

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:

  1. \(x\) est une période de \(w\);
  2. \(w\) est un préfixe de \(xw\);
  3. \(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)

Mots infinis

Définition: topologie sur les mots infinis

Distance entre \(u\) et \(v\): \(2^{-k}\)\(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\).

Complexité en facteurs

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: Isométries par morceaux
Introduction

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\)?
Isométries par morceaux

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

Codage de l’application

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

  1. Implanter la fonction \(T\) de l’exemple précédent dans Sage.

  2. 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).

  3. Implanter une fonction code(T,m,n) qui renvoie le préfixe de longueur \(n\) du code de \(m\) par \(T\).

  4. 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?).
  5. 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\).

  6. 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}\):

\[\sigma_v = \bigcap_{i=0}^{n-1} T^{-i} P_{v_i}\]

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)\).

  1. Dessiner la partition par les cellules des mots de longueur \(2\) puis \(3\) puis \(4\).
  2. 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\):

\[\begin{split}Tz = \begin{cases} e^{i\pi/4}(z+1)\quad Im(z)>0\\ e^{i\pi/4}(z-1)\quad Im(z)<0 \end{cases}\end{split}\]
  1. Calculer l’orbite d’un point sous \(T\).
  2. Dessiner la partition associée à l’application \(T^n\).
  3. 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

\[\begin{split}T(x,y)= \begin{cases} (1+a-y,x) &amp; (x,y)\in A\\ (x-1,1-y) &amp; (x,y)\in B \end{cases}\end{split}\]
  1. Pour \(a\) rationnel, décrire la partition à l’étape \(n\).

  2. Comprendre la dynamique dans ce cas.

  3. É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\).

  1. Écrire une fonction qui dessine l’orbite d’un point.
  2. Écrire une fonction qui donne le codage des \(n\) premiers termes de l’orbite d’un point.
  3. 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\).
Échange d’intervalles

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\).

  1. Écrire une fonction qui donne l’application de premier retour sur un sous intervalle.
  2. 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 .

Échanges d’intervalles IDOC

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})\).

  1. Implanter un interact qui permet de tracer les \(n\) premiers points de l’orbite d’un point sous cet échange d’intervalles.
  2. Cet échange est il minimal ?
Premier retour pour un échange d’intervales

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})\).

  1. Implanter l’application de premier retour sur l’intervalle \([0,1-\frac{3\sqrt{2}}{10}\).
  2. Recommencer avec l’intervalle \([0,\frac{3\sqrt{2}}{10}\).
  3. Quelle est la meilleure des deux inductions?
Graphe de Rauzy

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}\).

  1. Implanter le codage d’un point sous l’action de la rotation.
  2. Vérifier que cette rotation est minimale.
  3. Tracer les graphes de Rauzy correspondant aux mots de longueur \(1,2,3,4\).
Billard polygonal

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, …

Billard dual / Isométries par morceaux

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

  1. Implanter le billard dual autour du carré.
  2. Dessiner la partition associée aux cellules des mots de longueur \(n\).
  3. Recommencer pour un polygone régulier à \(5,6,7\) sommets.
Translation d’intervalles

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.

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.

References
[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]:
_images/2017-05-29-CRM_demo-symmetric-functions_6_0.png
[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
Demo : bases of multipolynomials

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 monomial basis

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\)

[ ]:

Schubert basis

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}}\)

Material and activities linked to the CRM-2017 school and workshop on “Equivariant Combinatorics”
Documents and references
Exercises and computational sessions (week: June 12-16)
Monday 4-5:30pm
  • 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

Tuesday 2pm-5:30pm
  • Informal help desk
Wednesday 4-5:30pm
Friday 4-5:30pm

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}}\)

Exercise sheet

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

\[D_{q,k} = (1+qx_1\partial_1)\partial_1^k+\cdots+ (1+qx_n\partial_n)\partial_n^k\]

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}}\)

Computational sessions at the CRM «Algebraic and Geometric Combinatorics of Reflection Groups» school and workshop

This is part of a series of Computational sessions at the CRM thematic semester: Algebra and Words in Combinatorics.

Session 1 (Monday 4-6pm)
Session 2 (Tuesday 4-6pm)
Session 3 (Wednesday 4-6pm)
  • Help desk, work in small groups.
Session 4 (Thursday 4-6pm)
Session 5 (Friday 4-6pm)
  • 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}}\)

Exercise sheet

This sheet contains some computational exercises related to the lectures.

Exploring the available features for reflection groups in Sage

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`
  1. Compute the length of the longest element of \(W\)

See CoxeterGroup(), samples()

Exercise (pictures)

  1. Construct the root lattice for type \(G_2\) and plot it (see Root Systems, Tutorial: visualizing root systems).
  2. Draw more pictures, for finite and affine Weyl groups!

Exercise (computing with roots)

  1. 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)

  1. 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.
  2. Check on computer that this matches with OEIS’s suggestion about standard Young tableaux).
  3. The bijection is known as Edelman-Green’s insertion. Search for its implementation is Sage (see search_src()).
  4. Try with other types.
Around Piotr’s lectures

Exercises

  1. Draw the (truncated) Cayley graph for Gamma = 3,3,3
  2. Implement the twist operation
  3. Implement the twist-rigidity test
  4. Implement listing all applicable twists
  5. Compute all Coxeter systems that can be obtained from a given Coxeter system by applying twists (see RecursivelyEnumeratedSet)
  6. Implement the (truncated) Davis complex
Around Vic’s lectures

Exercise (product formula for inversions)

  1. Check the product formula for the inversions statistic in the
    symmetric group;
  2. Retrieve the analogue product formula for some other reflection groups.

Exercise (other product formula)

  1. 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);
  2. 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).
Exploring symmetric function features
[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')
Interactive plots
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)
Introspection
sage: f.i

sage: f.is_idempotent

sage: f.is_idempotent
Elementary combinatorics
Combinatorial objects
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.
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 "-----------------------------"
....:     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: 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, …)
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&gt;=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&gt;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)
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

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
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: 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}
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])))

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}}\)

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}}\)

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}}\)

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}}\)

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}}\)

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

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 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.

Twenty Two Easy Steps
class borderless
  1. Find a bug
  1. Build the documentation
  1. Sage trac server
  1. Update the current patch
  1. Create a ticket
  1. Export a patch
  1. Clone your Sage
  1. Verify the patch
  1. Mercurial
  1. Upload the patch
  1. Enable Mercurial queues
  1. More on Mercurial queues
  1. Create an empty patch
  1. Dowload a patch
  1. Fix the bug
  1. Edit the series file
  1. View your changes
  1. Reviewing a patch
  1. Test the changes
  1. Positive review or Needs work
  1. Run tests
  1. Do some cleaning
Are you ready?
1. Find a bug

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.

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:

  • 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.

4. 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 ~/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
5. Mercurial

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.
5. Mercurial
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.
6. 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 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.

7. Create an empty patch

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
8. Fix the bug

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

9. View your changes

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
10. Test the changes
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.

11. Run tests
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…

12. Build 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 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
13. Update the current patch

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
14. Export a patch

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`'
15. Verify the content the patch

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
16. Upload the patch on Sage trac

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.
17. More on Mercurial queues

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
18. Dowload a patch

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.

19. Edit the series file

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

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.
20. 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 and hg 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>
20. Reviewing a patch
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.

21. Positive review or Needs work

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.
21. Positive review or Needs work

Note

In Sage, a negative review does not exist!
There is always place for work and improvement!
22. 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:

rm -rf sage-slabbe
References

Sage

Sage trac

Sage Developer’s Guide

Reviewing a Sage trac ticket, William Stein’s blog post, October 31, 2010.

This talk was generated

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}}\)

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 ...
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é.
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?

  1. 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
    
  2. 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'>
    
  3. 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}}\)

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}}\)

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

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.

Formes normales et applications
L’algorithme de Gauß revisité

On se place dans un corps \(K\) quelconque

Forme échelon (réduite)

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:

\[\begin{split}\begin{pmatrix} \underline{*} & * & * & * & * & * & * & * & * \\ 0 & 0 & \underline{*} & * & * & * & * & * & * \\ 0 & 0 & 0 & \underline{*} & * & * & * & * & * \\ 0 & 0 & 0 & 0 & 0 & 0 & \underline{*} & * & * \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & \underline{*} \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \end{pmatrix}\end{split}\]

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:

\[\begin{split}\begin{pmatrix} 1 & * & 0 & 0 & * & * & 0 & * & 0 \\ 0 & 0 & 1 & 0 & * & * & 0 & * & 0 \\ 0 & 0 & 0 & 1 & * & * & 0 & * & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & * & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \end{pmatrix}\end{split}\]

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).

Forme échelon et matrices équivalentes

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\)\(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.

Interprétation géométrique

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:

\[\{id\} = S_0\subsetneq S_1 \subsetneq \cdots \subsetneq S_n\]

et la suite induite des groupes emboîtés \(G_i:=G \cap S_i\):

\[\{id\} = G_0\subset G_1 \subset \cdots \subset G_n=G\]

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:

\[\{0\} = V_0 \subsetneq V_1 \subsetneq \cdots \subsetneq V_n=V\]

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\):

\[V_i:=\langle e_{m-i+1} \cdots e_m \rangle\]

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:

\[\overline V_i:=\langle e_i \cdots e_m \rangle=V_{n-i+1}\]

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.

Applications des formes échelon

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:

  1. Déterminer une base de \(E\).

  2. Tester si un vecteur appartient à \(E\).

  3. Tester si \(E=F\).

  4. Tester si deux vecteurs \(x\) et \(y\) de \(V\) sont égaux modulo \(E\)

  5. Calculer l’orthogonal d’un sous-espace vectoriel

  6. Calculer la somme \(E+F\) et l’intersection \(E\cap F\) de deux espaces vectoriels

  7. 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

  8. Calculer dans l’espace quotient \(E/F\)

  9. 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:

  1. Calculer le noyau de \(\phi\)
  2. Calculer l’image de \(\phi\)
  3. Calculer l’image réciproque par \(\phi\) d’un vecteur \(f\) de \(F\)
  4. Arithmétique: composition, combinaison linéaires, inverse
  5. Calculer le polynôme caractéristique
  6. Calculer les valeurs propres de \(\phi\)
  7. Calculer les espaces propres de \(\phi\)
Forme de Hermite

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]
  1. Quel candidat pour une forme échelon?

  2. Interprétation en terme de multiplication par une matrice?

  3. Interprétation en terme de sous-espace engendré?

  4. Cette forme échelon est elle réduite?

    sage: M.echelon_form()
    [  2   3  -4]
    [  0   7 -11]
    
  5. 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:

\[\begin{split}M := \begin{pmatrix} u & v \\ \frac br & \frac ar \\ \end{pmatrix}\end{split}\]

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

  1. Donner un exemple de quotient d’un module libre \(\ZZ^n\) qui n’est pas isomorphe à un module libre.

  2. Donner un exemple de drapeau infini

  3. 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.

Application: classification des groupes abéliens de type fini

Exercice

Soit \(G\) un groupe additif abélien engendré par un nombre fini \(n\) d’éléments, mettons \(a\), \(b\), \(c\), avec \(n=3\).

  1. Que peut-on dire sur l’ensemble des relations entre ces éléments?
  2. En déduire la structure de \(G\).
Gauß sans fractions et Gauß-Bareiss

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?

Exemple: dimension 1

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!

Algorithme de Gauß sans fraction

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
Algorithme de Gauß-Bareiss

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.

Conclusion

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.

TP

Exercice: Du calcul matriciel au calcul sur les sous espace vectoriels

  1. 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.

  2. 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.

  3. Implanter votre propre fonction SEV_egaux(U, V) qui teste si deux listes deux vecteurs engendrent le même sous espace vectoriel.

  4. 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?

  5. 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\).

  6. De même implanter SEV_intersection(U,V) et SEV_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.

  1. Implanter l’opération cycle.
  2. Implanter une fonction code_cyclique(v) qui renvoie le plus petit code cyclique \(C\) contenant \(v\).
  3. 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\).
  4. 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).

  1. (É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
    
  2. É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?

  3. En déduire une fonction qui calcule le déterminant d’une matrice à coefficients entiers.

  4. Faire la même chose pour des matrices à coefficients polynomiaux univariés.

  5. 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.

Quelques références
[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}\)

Algèbre linéaire sur \(\ZZ\)?

Todo

Exercices de motivation

  1. Décrire \(<u,v>\) pour \(u,v=...\in\ZZ^3\)
  2. Résoudre le système d’équations …
  3. 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

  1. 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)
  2. 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)
Forme de Hermite
Matrices à deux lignes

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]
  1. 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\).
  2. Interprétation en terme de multiplication par une matrice?
  3. La forme échelon est elle réduite?
  4. 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:

\[\begin{split}M := \begin{pmatrix} u & v \\ -\frac br & \frac ar \\ \end{pmatrix}\end{split}\]

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\).

Application: sous-modules, images de morphismes

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

  1. 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.
  2. Existe-t’il des drapeaux croissants infinis dans \(\ZZ^n\)

Solution

Un drapeau infini décroissant:

\[\ZZ \supsetneq 2\ZZ \supsetneq 4\ZZ \supsetneq 8\ZZ \supsetneq\cdots\]

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:

\[\ZZ \subsetneq 2\ZZ \subsetneq 4\ZZ \subsetneq 8\ZZ \subsetneq\cdots\]

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.

Application: résolution de systèmes d’équations, noyaux de morphismes

Exercice: Une équation affine

Déterminer l’ensemble des solutions entières de chacune des équations suivantes:

  1. \(6x+4y+10z=15\)
  2. \(6x+4y+10z=18\)

Todo

Solution

Exercice: noyau

  1. Soit \(M\) une matrice sous forme échelon. Décrire son noyau à gauche \(K=\{v, vM =0\}\).
  2. 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.

Application: Torsion et quotients

Exercice: Torsion

  1. Donner un exemple de quotient d’un module libre \(\ZZ^n\) qui n’est pas isomorphe à un \(\ZZ\)-module libre.

  2. 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

Classification des groupes abéliens de type fini et forme normale de Smith
Groupes abéliens et classes d’équivalences doubles de matrices

On s’intéresse aux groupes (additifs) abéliens engendrés par un nombre fini d’éléments.

Exercice

  1. Donner des exemples de tels groupes
  2. 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?

\[H=\langle a,b,c \suchthat 14a+19b-10c=0, 10a+14b-7\rangle ?\]

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.

  1. Montrer que le sous-module correspondant à \(S M T\) est isomorphe à \(F\).

  2. 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]
    
  3. 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 à

\[H=\langle a',b',c' \suchthat a'=0, b'=0 \rangle \approx \ZZ\,.\]

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
Forme normale de Smith

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

  1. La procédure utilisée dans l’exemple termine
  2. 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

\[\ZZ/d_1\ZZ \times \cdots \times \ZZ/d_k\ZZ \times \ZZ \times\cdots\times\ZZ\]

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.

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.

Gauß sans fractions et Gauß-Bareiss

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?

Exemple: dimension 1

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

Algorithme de Gauß sans fraction

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]
Algorithme de Gauß-Bareiss

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.

Conclusion

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.

TP

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».

  1. (É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
    
  2. É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.

  3. É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?

  4. En déduire une fonction qui calcule le déterminant d’une matrice à coefficients entiers.

  5. Faire la même chose pour des matrices à coefficients polynomiaux univariés.

  6. 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:

  1. Réaliser les relations comme sous-module \(R\) de \(F=\ZZ^n\) donné par sa base \(B\) sous forme échelon réduite.
  2. 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\)
  3. Calculer la forme de Smith pour obtenir les diviseurs du groupe; en déduire sa structure.
  4. Tenter de construire explicitement le générateur correspondant à chaque facteur dans \(F/R\).
  5. 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

  1. Implanter l’algorithme de calcul de la forme de Hermite d’une matrice
  2. Implanter l’algorithme de calcul de la forme de Smith d’une matrice
  3. 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.

Formes normales

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?

Échauffement

Exercice

Résoudre le système d’équations suivant sur \(GF(5)\):

\[\begin{split}\begin{align*} 3x_3 + x_4 + 4x_5 &= 0\\ 3x_1 + x_2 + 4x_3 + 2x_4 + x_5 &= 0\\ 4x_1 + 3x_2 + 2x_3 + x_4 + 3x_5 &= 0\\ \end{align*}\end{split}\]

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]
L’algorithme de Gauß revisité

On se place dans un corps \(K\) quelconque

Forme échelon (réduite)

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:

\[\begin{split}\begin{pmatrix} \underline{*} & * & * & * & * & * & * & * & * \\ 0 & 0 & \underline{*} & * & * & * & * & * & * \\ 0 & 0 & 0 & \underline{*} & * & * & * & * & * \\ 0 & 0 & 0 & 0 & 0 & 0 & \underline{*} & * & * \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & \underline{*} \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \end{pmatrix}\end{split}\]

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:

\[\begin{split}\begin{pmatrix} 1 & * & 0 & 0 & * & * & 0 & * & 0 \\ 0 & 0 & 1 & 0 & * & * & 0 & * & 0 \\ 0 & 0 & 0 & 1 & * & * & 0 & * & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & * & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \end{pmatrix}\end{split}\]

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).

Forme échelon, réduction, et division euclidienne

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.

Forme échelon et matrices équivalentes

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\)\(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.

Interprétation géométrique

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?

Sous espaces vectoriels et formes échelon

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

Drapeaux

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

\[E_i = E \cap \langle e_i,\ldots,e_5\rangle\]

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:

\[\{id\} = S_0\subsetneq S_1 \subsetneq \cdots \subsetneq S_n\]

et la suite induite des groupes emboîtés \(G_i:=G \cap S_i\):

\[\{id\} = G_0\subset G_1 \subset \cdots \subset G_n=G\]

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:

\[\{0\} = V_0 \subsetneq V_1 \subsetneq \cdots \subsetneq V_n=V\]

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\):

\[V_i:=\langle e_{m-i+1}, \ldots, e_m \rangle\]

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:

\[\overline V_i:=\langle e_i, \ldots, e_m \rangle=V_{n-i+1}\]

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\):

\[\langle B \cap E_i\rangle = E_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

Applications des formes échelon

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:

  1. Déterminer une base de \(E\).
  2. Tester si un vecteur \(x\) appartient à \(E\).
  3. Tester si \(E=F\).
  4. Tester si deux vecteurs \(x\) et \(y\) de \(V\) sont égaux modulo \(E\).
  5. Calculer l’orthogonal d’un sous-espace vectoriel.
  6. Calculer la somme \(E+F\) et l’intersection \(E\cap F\) de deux espaces vectoriels.
  7. Calculer la sous-algèbre de \(V\) engendrée par \(E\) (en supposant \(V\) muni d’une structure d’algèbre \((V,+,.,*)\)).
  8. Plus généralement: clôture de \(E\) sous des opérations linéaires.
  9. Calculer dans l’espace quotient \(E/F\).
  10. 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:

  1. Calculer le noyau de \(\phi\).
  2. Calculer l’image de \(\phi\).
  3. Calculer l’image réciproque par \(\phi\) d’un vecteur \(f\) de \(F\).
  4. Arithmétique: composition, combinaison linéaires, inverse.
  5. Calculer le polynôme caractéristique.
  6. Calculer les valeurs propres de \(\phi\).
  7. 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
Résumé

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.

TP
Exercice 1: Du calcul matriciel au calcul sur les sous espaces vectoriels
Calcul d’une base d’un sous espace vectoriel donné par des générateurs

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() ]
    
Test d’appartenance d’un vecteur à un sous-espace vectoriel

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\).

Test d’égalité de deux espaces vectoriels

Implanter votre propre fonction SEV_egaux(U, V) qui teste si deux listes deux vecteurs engendrent le même sous espace vectoriel.

Calcul de l’orthogonal d’un 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?

Calcul de la somme et l’intersection de deux sous espace vectoriels

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.

Exercice 2: 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.

Quelques références
[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

Méthodes modulaires et généralisations
Introduction

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]
Analyse: Complexité arithmétique versus complexité en bits

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\).

Méthodes modulaires et multimodulaires

Todo

Donner les complexités explicites, quitte à ne pas les démontrer

Exemple: le calcul du déterminant

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

  1. Déterminer une borne \(b\) sur le déterminant (par ex: borne de Hadamard)
  2. Choisir un grand nombre premier \(p>2b\)
  3. Calculer \(\det(M)\) modulo \(p\):
\[\begin{split}\require{AMScd} \begin{CD} M @>{\ \det\ }>> \det(M)\\ @VV{\mod p}V @VV{\mod p}V \\ M\!\mod p @>{\ \det\ }>> \det(M\!\mod p) \end{CD}\end{split}\]
  1. En déduire \(\det(M)\).

Algorithme multimodulaire

  1. Comme ci-dessus
  2. Choisir plusieurs (combien?) petits nombres premiers tels que \(p_1\cdots p_k>2b\)
  3. Calculer \(\det(M)\) modulo \(p_i\) pour chaque \(i\)
  4. Utiliser le lemme chinois pour reconstruire \(\det(M)\).

Intérêt du multimodulaire?

  1. 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.
  2. Voire dans un flottant machine (seul intérêt: les processeurs actuels sont plus optimisés pour manipuler des flottants …).
  3. Parallélisation
Exemple: bornes sur le rang

Remarque

\[\operatorname{rang} (M\mod p) \leq \operatorname{rang}(M)\]
Généralisations

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

  1. Déterminer une borne \(k\) sur le degré du déterminant.
  2. Choisir \(k\) éléments du corps de base.
  3. Prendre le morphisme d’évaluation en ces points:
\[\begin{split}\phi: \begin{cases} K[x] & \rightarrow K^k\\ P & \mapsto (P(a_1), \dots, P(a_k)) \end{cases}\end{split}\]
  1. Calculer \(\phi(\det(M))\) en se ramenant au calcul de \(k\) déterminants de matrices à coefficients dans le corps de base.
  2. 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)\).

Variante: méthodes \(p\)-adiques

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\)

Expansion \(p\)-adique

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

  1. Prendre un nombre premier \(p\) qui ne divise pas le déterminant de \(M\).
  2. Une bonne stratégie est de choisir \(p\) au hasard! Statistiquement il sera bon, et sinon on s’en rendra compte et on recommencera.
  3. Calculer l’inverse \(N\) de \(M\) modulo \(p\).
  4. 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\)
  5. \(k\) double à chaque itération!!!
  6. 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:

\[M = M_0 + M_1 p + M_2 p^2 + \cdots\]

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\).

Algorithmes de type «Boîte noire»
Problème

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???

Algorithmes de type «boîtes noire»

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.

Algorithme de Wiedemann

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

  1. Prendre des vecteurs \(U\) et \(V\) aléatoires
  2. Déterminer les premiers termes de la suite \(u_k\) en calculant itérativement \(V, MV, M^2V, \ldots\)
  3. En déduire par Berlekamp-Massey la relation de récurence minimale qu’elle satisfait
  4. Cette relation divise le polynôme minimal \(P\) de \(M\)
  5. Réitérer «suffisamment de fois»

Remarques

  1. On n’a eu besoin de calculer que des produits \(MV\)
  2. On voit \(M\) comme un endomorphisme
  3. Complexité mémoire bornée par \(n\)
Application: calcul d’inverses

Exercice

Supposer que le polynôme minimal de \(M\) soit \(X^3-2X+1\).

Déterminer l’inverse de \(M\).

Application: calcul du rang

Voir TP.

Algorithme de Faddeev-Leverrier

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

Travaux Pratiques

Todo

rajouter un exo sur Faddeev-Leverrier

Berlekamp Massé

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.

Proposition de correction.

Wiedemann

Exercice: Polynome minimal et Wiedemann sur un exemple

  1. 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’algorithme diagonalizable; on pourra tirer les multiplicités au hasard avec IntegerVectors.
  2. Calculer son polynôme minimal avec la méthode minimal_polynomial.
  3. 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}\).
  4. 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.
  5. Réciproquement, utiliser l’algorithme de Berlekamp-Massey pour retrouver le polynôme minimal de \(M\).

Exercice: Implantation de l’algorithme de Wiedemann

  1. É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\).
  2. 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.
  3. Vérifier le résultat sur la matrice précédente.
  4. É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.
  5. É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.

  1. 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).
  2. 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.
Une conjecture sur les matrices d’incidence arbres / forêts

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\).
La matrice d'incidence `T_6` des graphes acycliques à `6` sommets et `5` arêtes versus ceux à `4` arêtes

Exercice

Écrire une fonction qui construit la matrice \(T_n\).

Indications:

  1. Construire les deux listes de graphes

  2. 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
    
  3. 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}
    
  4. Numéroter les graphes dans les deux listes en utilisant sage.combinat.ranker.from_list().

  5. Utiliser les méthodes copy et delete_edge pour obtenir \(f\) par suppression d’une arête de \(t\). Puis utiliser la méthode canonical_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
Introduction

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.

La bibliothèque de Tsetlin

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\).
Graphe et matrice de transition

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\):

Le graphe de transition de la bibliothèque de Tsetlin avec trois livres {1,2,3}
Exercice: étude du graphe/automate de transition

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.

Linéarisation de la chaîne de Markov

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\).

Exercice: étude de la matrice de transition

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?

R-trivialité et conséquences

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\).

Exercice: le monoïde de transition est \(R\)-trivial

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é.

Application

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:

\[\{0\}=V_0 \subsetneq V_1\subsetneq\cdots\subsetneq V_k=K\Omega_n\]

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!

Exercice: caractérisation des valeurs propres possibles de \(M\)

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.

Exercice: une conjecture pour les valeurs propres et leur multiplicité

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.

Exercice: Détermination des valeurs propres et leur multiplicité par la théorie des caractères

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.

  1. Vérifier que \(\tau_S\) est idempotent.
  2. Compter le nombre \(c_S\) de points fixes de chaque \(\tau_S\).
  3. 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.
Conclusion

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

Introduction
Objectif du codage

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.

Exemples d’applications
  1. NASA/CNES/…: communication avec des sondes et satellites
  2. CD / DVD
  3. Transfert de données par Internet (TCP, CRC, MD5 checksum)
  4. Téléphones portables

Quelles sont les contraintes spécifiques à chacune de ces applications?

Premiers exemples de codes
Langages humains!

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, …

Codage de parité sur 7 bits
Premiers concepts

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:

  1. Détection d’erreur: est-ce que \(c'\) est dans \(C\)?
  2. 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:

\[B(x,k) = \{y\in M,\quad d(x,y) \leq k\}\]

Alors:

\[D(C) := \max_{k\in \NN} \quad \forall c\in C, \quad B(c,k) \cap C = \{c\}\]
\[e(C) := \max_{k\in \NN} \quad \forall c_1,c_2\in C, \quad B(c_1,k) \cap B(c_2,k) \ne \emptyset \Longrightarrow c_1=c_2\]
\[d(C) := \min_{x\ne y\in C} d(x,y)\]

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:

\[d(C) := \max_{k\in \NN}, \forall x\ne y \in C, \quad k\leq d(x,y)\]

Exercice: En petite dimension:

  1. Trouver tous les codes de \((\mathbb{Z}/2\mathbb{Z})^{n}\) pour \(n=1\), \(n=2\), \(n=0\).
  2. 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)\).
  3. Permettent-t’ils de corriger une erreur?
  4. Donner un code de \((\mathbb{Z}/2\mathbb{Z})^{3}\) permettant de corriger une erreur.
  5. 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\).

Borne de Hamming, codes parfaits

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\).

  1. 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\).
  2. Taille de \(A^n\)?
  3. Conclusion?

Solution

\[|C| \sum_{k=0}^{e(C)} \binom n k (q-1)^k \quad \leq \quad q^n\]

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.

\[|C| \sum_{k=0}^{e(C)} \binom n k (q-1)^k \quad = \quad q^n\]

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?

Codes linéaires

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
  1. Calculer le noyau de \(H\).
  2. 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\)?
  3. 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:

\[\begin{split}a_{5} = a_{2}+a_{3}+a_{4}\\ a_{6} = a_{1}+a_{3}+a_{4}\\ a_{7} = a_{1}+a_{2}+a_{4}\end{split}\]

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)
Décodage par syndrome

Exercice

  1. 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.
  2. 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.
  3. Que se passe-t’il s’il y a deux erreurs?
Codes cycliques

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:

\[1010010\in C \Longleftrightarrow 0101001\in C \Longleftrightarrow 1010100\in C \Longleftrightarrow \cdots\]

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)\):

\[\begin{split}X(1+X^2+X^5) = X + X^3 + X^6\\ X(X + X^3 + X^6) = X^2+X^4+X^7 = 1+X^2+X^4\end{split}\]

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.

Codage par interpolation (Reed-Solomon)

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.

\[\underbrace{P_1(x_1),P_2(x_1),\ldots,P_k(x_1)}, \underbrace{P_1(x_2),P_2(x_2),\ldots,P_k(x_2)},\ldots \underbrace{P_1(x_l),P_2(x_l),\ldots,P_k(x_l)}\]

Deuxième étage: codage de chacun des \(l\) blocs avec un code permettant de détecter les erreurs.

TP: Codage et décodage

Exercice préliminaire

  1. 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.

  2. 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:

  1. Illustrer visuellement les liens entre distance, capacité de correction et de détection, ainsi que les notions de distance de Hamming, boules, …

  2. 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\).
  3. 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

  4. 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.

  5. Implanter toute la chaîne: codage, transmission, détection, correction, décodage.

  6. Implanter des fonctions de calcul de distance et test de perfection.

Pour ces derniers points, on pourra considérer des codes:

  1. décrits par une liste exhaustive de mots
  2. linéaires
  3. cycliques (voir ci-dessous)
  4. par interpolation
  5. 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.

  1. Implanter l’opération cycle.
  2. Implanter une fonction code_cyclique(v) qui renvoie une base du plus petit code cyclique \(C\) contenant \(v\).
  3. 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\).
  4. 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
Groupe symétrique

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
Permutations
Quelques permutations particulières
  • 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}\).
Représentation des permutations
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)
    
Produit de deux permutations

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

  1. 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]
    
  2. Deux cycles disjoints commutent.

  3. Toute permutation se décompose de manière unique comme un produit de cycles (à l’ordre près).

Exercice

  1. Comment calculer l’inverse d’une permutation? Complexité?
  2. Calcul de la décomposition en cycles? Complexité?
Type cyclique

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

  1. 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
    
  2. Quelles sont les classes de conjugaisons du groupe symétrique?

Solution

  1. Chaque cycle \((i_1,\dots,i_k)\) de \(\tau\) contribue un cycle \((\sigma(i_1),\dots,\sigma(i_k))\) dans \(\sigma\tau\sigma^{-1}\).
  2. 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.

Générateurs du groupe symétrique

Proposition

  1. \(S_n\) est engendré par les cycles.
  2. \(S_n\) est engendré par les transpositions.
  3. \(S_n\) est engendré par les transpositions élémentaires.
  4. \(S_n\) est engendré par la transposition \((1,2)\) et le cycle \((1,\dots,n)\).
Présentation par générateurs et relations

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 n=3

Le permutoèdre pour \(S_3\)

Le permutoèdre pour n=4

Le permutoèdre pour \(S_4\)

Exemple de lien combinatoire/algèbre: comptage des permutations par niveau et \(q\)-factorielle
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\).

Groupes de permutations

Un groupe de permutations est un groupe donné comme sous-groupe d’un groupe symétrique.

Exemples
  • 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([...])
Applications:
  • 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.
Systèmes générateurs forts

Problème: Soit \(G\subset S_n\) un groupe de permutation; \(G\) est typiquement très gros.

  1. Comment le représenter? Le manipuler?
  2. Calculer son nombre d’éléments?
  3. Tester si un élément est dedans?
  4. Exprimer un élément en fonction des générateurs?
  5. Déterminer ses sous-groupes?
  6. 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\).

  1. Supposons \(|H|\) connu. Comment en déduire \(|G|\)?
  2. Comment obtenir des représentants des classes de \(G/H\)?
  3. 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:

\[\begin{split}\phi: \begin{cases} G &\longmapsto G.n\\ g &\longrightarrow g(n) \end{cases}\end{split}\]

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\).

  1. \(|G| = |H|\ |G.x|\)
  2. Il suffit de choisir pour chaque \(y\) dans \(G.n\) une permutation \(\sigma_{n,y}\) telle que \(\sigma_{n,y}(n)=y\).
  3. 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

\[\{ id \} = G_0 \subset G_1 \subset \cdots \subset G_n = G\,,\]

\(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\):

\[\langle S\cap S_i\rangle = G \cap S_i = G_i\]

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:

  1. Calcul du nombre d’éléments
  2. Tester si un élément est dedans

Exercices

  1. 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.
  2. 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

  1. Vérifier que \(\left\{5,4,3\right\}\) est une base pour \(A_{5}\).
Algorithme de Schreier-Sims

Comment calculer un système générateur fort?

  1. Calculer l’orbite \(G.n\) de \(n\) (comment on fait?)
  2. Les permutations \(\sigma_{n,y}\) qui envoient \(n\) sur \(y\), \(y\) dans
    \(G.n\) donnent des représentants des classes de \(G/G_n\)
  3. Calculer les générateurs de \(G_n\) avec le Lemme de Schreier (voir ci-dessous).
  4. Réitérer récursivement

Todo

Donner la complexité

Lemme de Schreier

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:

\[G = \langle A \rangle \qquad \text { et } G=\bigcup_{u\in U} uH,\]

Alors:

\[H = \langle v^{-1} a u \ \mid\ a\in A,\ u\in U \ \text{ et }\ v\in U,\ au\in vH \rangle\]

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:

\[g = a_1\cdots a_k = (a_1 u_2) (u_2^{-1}a_2u_3)(u_3^{-1}a_3u_4) \cdots (u_na_n)\,.\]

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)]]
Synthèse: méthodes d’éliminations

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):

  1. Test d’appartenance d’un élément à \(I\),
  2. Test d’égalité de \(I\) et de \(J\),
  3. Calcul de «taille» de \(I\),

Pour cela, on se donne:

  1. Un ordre
  2. Un drapeau de sous-structures vis à vis de cet ordre
  3. Un procédé de division: Euclide, …
  4. Une notion de système générateur fort: PGCD, base de Gröbner, forme échelon, système fort de générateurs,
  5. Un algorithme de calcul d’un tel système: algorithme d’Euclide, de Buchberger, de Gauss, de Schreier-Sims, …
TP: Énumération de Pólya

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).

image

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:

\[\sum_{\overline{f}}w\left(\overline{f}\right) = 1+q+2q^{2}+2q^{3}+q^{4}+q^{5},\]

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:

\[p_{k} := \sum_{c\in F} w(c)^{k}\]

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:

\[\sum_{\overline{f}}w\left(\overline{f}\right) = \frac{1}{\left|G\right|}\sum_{\sigma\in G}\; \prod_{k\in l(\sigma)}p_{k}\]

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.

Exercice: comptage de colliers
  1. Écrire une fonction p(k,poids) qui calcule \(p_{k}\) à partir de la liste des poids des éléments de \(F\).

  2. 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 permutation sigma à partir de la méthode cycle_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.

  3. Lister les permutations de \(C_{5}\).

  4. Écrire la formule ci-dessus pour \(poids=[1,1]\).

  5. Écrire une fonction Polya(G, poids) implantant la formule ci-dessus pour un groupe \(G\) et des poids quelconques.

  6. Compter le nombre de colliers bicolores à dix perles selon leur nombre de perles rouges.

  7. Compter le nombre de colliers à dix perles de trois couleurs.

Exercice: comptage de colliers (suite)

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.

Exercice: colorations du cube

Compter le nombre de cubes que l’on peut obtenir en peignant leurs faces en au plus trois couleurs.

Indications:

  1. Numéroter les faces, considérer le groupe des isométries positives du cube, comme groupe de permutations de ses faces.
  2. Déterminer les générateurs de ce groupe (par exemple sous forme de produit de cycles).
  3. Construire le groupe dans Sage en utilisant PermutationGroup().
  4. Poursuivre comme ci-dessus.
Exercice: énumération des graphes (plus avancé)

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:

  1. 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).
  2. 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.
  3. 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\).
Exercice: énumération des multigraphes (plus avancé)

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}}\).

Exercice (plus avancé)
  1. 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!
  2. Utilisez-la pour recalculer les exemples précédents.
  3. Est-elle plus ou moins performante que votre implantation?
  4. Comment fonctionne-t-elle?
TP: Systèmes générateurs forts

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:

  1. Calculer la taille du groupe,
  2. Calculer la liste des éléments du groupe,
    • Indication: récursion
    • Variante (avancé): implanter un itérateur
  3. 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\).

Quelques références
[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
Objectifs de l’option
  • 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
Calcul exact (par opposition à calcul numérique):
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 algébrique (Computer Algebra):
Résidus modulo, corps finis

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
Matrices
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]
Polynômes, fractions rationnelles
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
Nombres algébriques
sage: k.<a> = NumberField(x^3 + x + 1)

sage: a^3
-a - 1

sage: a^4+3*a
-a^2 + 2*a
Calcul symbolique
Digression: variables de programmation vs variables symboliques

Démontration: Calcul symbolique

Résumé

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
Grands thèmes
  • Arithmétique
  • Algèbre linéaire
  • Factorisation
  • Polynômes et systèmes polynomiaux
  • Groupes, combinatoire, …
  • En filigrane: algorithmique et complexité
Applications
  • Cryptographie
  • Codage
  • Solveurs exacts (linéaire, …) pour les sciences de l’ingénieur
  • Robotique
Idées centrales
  • 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:

  1. 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.
  2. 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
Applications
Polymorphisme

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]
Introspection

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
Éléments, parents, catégories
Éléments et parents

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)
Exemple: 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   = 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...
Complément: Constructions

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()
Exemple: 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: 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 ...
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é.
Complément: Catégories

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.

Expressions versus domaines de calcul explicites

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.

Exemple: simplification d’expressions

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.

Exemples: polynômes et formes normales

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.

Exemple: factorisation des polynômes

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)
Synthèse

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
Les systèmes de calcul (formel)
Composants d’un Système de Calcul Formel (Computer Algebra System)
  • 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
Quelques systèmes de calcul

Systèmes généralistes:

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)
Quelques Caractéristiques communes
Représentation arborescentes des objets, notion d’opérandes
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]
Gestion automatique de la mémoire

Que se passe-t’il lorsque l’on fait:

sage: F = 0

(comptage de références ou glaneur de cellule)

Structures de données

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
Langage de programmation

Exécution conditionnelle, boucles, fonctions, …

Les origines de SageMath
Années 50:

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, …
Années 80-90:
  • 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
Années 2000:
  • 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:

Voici quelques pistes pour la suite:

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».

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
Motivation: «Tout se ramène aux produits»
Inversion de matrices

Exercice: matrices \(2\times 2\) génériques

Soit \(M=\begin{pmatrix}a&b\\c&d\end{pmatrix}\).

  1. Calculer \(M^{-1}\) en utilisant les cofacteurs.
  2. 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,

\[\begin{split}M^{-1} = \begin{pmatrix} A^{-1}+A^{-1}B\Delta^{-1}CA^{-1} & - A^{-1}B \Delta^{-1}\\ -\Delta^{-1}CA^{-1} & \Delta^{-1}\\ \end{pmatrix}\,.\end{split}\]

Voir: https://en.wikipedia.org/wiki/Block_matrix#Block_matrix_inversion

Exercice

  1. Vérifier que l’on retrouve bien la formule d’inversion des matrices \(2\times 2\) à partir de la formule d’inversion par blocs.
  2. 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\)),

\[d_{2n} \,\leq\, 2d_n + 14c_n \,\leq\, 2ban^\omega + 14an^\omega \,\leq\, \frac{1+\frac 7b}{2^{\omega-1}} \,ba(2n)^\omega\]

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\).

Méthode de Newton
Approximation numérique de solutions de \(f(a) = 0\)

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:

\[a_1 = a_0 - \frac{f(a_0)}{f'(a_0)}\]
  1. Calculer \(f(a)\) par développement de Taylor de \(f\) en \(a_0\).
  2. Qu’en déduire sur \(a_1-a\) par rapport à \(a_0-a\)?
  3. Quelle conclusion peut-on en tirer? Sous quelles hypothèses?

Pour les détails, voir l’article de la Wikipedia.

Inversion de séries

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:

\[C(z) = A(z) + O(z^k) \Longleftrightarrow C(z)B(z) = 1 + O(z^k)\]

On suppose que l’on dispose d’une approximation \(A_0(z)\) de l’inverse \(A(z)\) de \(B(z)\):

\[A_0(z)B(z)=1+O(z^k)\]

et on pose:

\[A_1(z)=A_0(z)(2-A_0(z)B(z))\]

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\):

\[C(z)B(z) - 1 = (C(z)-A(z)) B(z) = O(z^k) B(z) = O(z^k)\]

Pour \(\Longleftarrow\):

\[C(z)- A(z) = (C(z)B(z)-1) A(z) = O(z^k) A(z) = O(z^k)\]

Posons maintenant \(C(z)\) tel que \(A_0(z)B(z) = 1 + C(z)\), et calculons \(A_1(z)B(z)\):

\[A_1(z)B(z) = A_0(z)(2-A_0(z)B(z))B(z) = (1+C(z)) (1-C(z)) = 1-C(z)^2 = 1 + O(z^{2k})\]

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\).

\[d_{2n} \,\leq\, d_n + 2c_{2n} \,\leq\, banf(n) + 2a(2n)f(2n) \,\leq\, (\frac12 + \frac 2b) \,ba(2n)f(2n)\]

On conclue comme précédemment.

Approximation en série d’une équation implicite \(F(A(z)) = 0\)

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)\).

  1. En vous inspirant de la méthode de Newton usuelle, proposer une meilleure approximation \(A_1(z)\) de \(A(z)\).
  2. Quelle est la vitesse de convergence?
  3. Quelles opérations sont nécessaires lors d’une itération?
  4. Proposer un analogue pour la résolution des équations implicites du théorème sur l’inversion de matrices ci-dessus.

Exercice

  1. 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)\)?
  2. 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)\)

Division Euclidienne de polynômes

Soit \(F=F(z)\) et \(G(z)\) deux polynômes. On veut déterminer \(Q\) et \(R\) avec \(\deg R < \deg G\) tels que

\[F = GQ + R\]

Récrivons ceci sous la forme:

\[\frac FG = Q + \frac RG\]

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]

Produits rapides
Produits rapides de polynômes
Algorithme naïf

Dans la suite, on considère un anneau \(K\) et deux polynômes dans \(K[z]\):

\[A = A(z) = a_0 + a_1 z + \cdots + a_n z^n\]
\[B = B(z) = b_0 + b_1 z + \cdots + b_n z^m\]

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?

Karatsuba

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

\[A = A_0 + A_1 z^l\]
\[B = B_0 + B_1 z^l\]

\(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\):

\[AB = A_0B_0 + ( A_0B_1 + A_1B_0 ) z^l + (A_1B_1)z^{2l}\]

Ou seulement avec trois:

\[AB = A_0B_0 + ( (A_0+A_1)(B_0+B_1) - A_0B_0 - A_1B_1 ) z^l + (A_1B_1)z^{2l}\]

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:

\[f(r)=3f(r-1)=3^rf(0)=3^r\]

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:

\[3^{\lceil \log_2 n \rceil} \leq 3. 3^{\log_2 n} = 3.2^{\log_2 3 . \log_2 n} = 3 n^{\log_2 3}\]

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, …)
Produit par évaluation

Remarque stupide

Si \(x_0\) est un élément de \(K\), et \(C(z) = A(z)B(z)\) alors:

\[C(x_0) = A(x_0) B(x_0)\]

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:

\[\begin{split}\Phi: \begin{cases} K[z] &\mapsto (K^n,+,.)\\ P(z) &\mapsto ( P(x_1), \ldots, P(x_n) ) \end{cases}\end{split}\]

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:

\[A(z)B(z) = \Phi^{-1} ( \Phi(A) \Phi(B) )\]

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?

Produit par transformée de Fourier Discrète
Transformée de Fourier Discrète

Proposition

Supposons que l’anneau \(K\) contienne une racine primitive \(\omega\) de l’unité. Alors le morphisme d’algèbre:

\[\begin{split}DFT_\omega: \begin{cases} K[z] &\mapsto (K^n,+,.)\\ P(z) &\mapsto ( P(1), P(w), \ldots, P(w^{n-1}) ) \end{cases}\end{split}\]

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

  1. \(DFT_\omega\) est une application linéaire. Donner sa matrice.
  2. 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:

\[DFT_\omega^{-1} = \frac 1n DFT_{\omega^{-1}}\]

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.

Transformée de Fourier rapide (FFT: Fast Fourier Transform)

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

\[P_+(z) = P(z) [ z^k - 1 ]\]

(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

\[P_-(z) = P(z) [ z^k + 1 ]\]

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\)!

Produits rapides d’entiers

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.

Produits rapides de matrices

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 …

Résumé
  • «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
Travaux Pratiques

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

  1. Implanter l’algorithme naïf pour multiplier deux polynômes
  2. Implanter l’algorithme de Karatsuba pour multiplier deux polynômes
  3. 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.
  4. Avec votre implantation, à partir de quel seuil est-il préférable d’utiliser l’algorithme de Karatsuba?

Prolongements possibles:

  1. Implanter un algorithme mixte Karatsuba/naïf qui tienne compte du seuil obtenu. Comparer.
  2. Comparer la complexité pratique de votre implantation du produit avec celle de la bibliothèque de Sage.
  3. Deviner, d’après sa complexité pratique, le ou les algorithmes utilisés par Sage.
  4. 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

  1. 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.
  2. 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\).

  1. Choisir \(A_0(z)\) tel que \(A_0(z)\equiv A(z) [z]\)
  2. 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.

  1. Écrire l’équation ensembliste satisfaite par \(C\).
  2. La traduire en équation algébrique satisfaite par \(C(z)\).
  3. Choisir \(C_0(z)\) tel que \(C_0(z)\equiv C(z) [z]\).
  4. 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.

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.

Programmation linéaire
Qu’est-ce que la programmation linéaire?
Exemple: le problème du régime de Pauline

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
  1. Pauline peut-elle trouver une solution ?
  2. Comment formaliser le problème ? (modélisation)
  3. Qu’est-ce qui fait la spécificité du problème ?
  4. 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: #
Forme standard d’un programme linéaire

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:

\[z:=\sum_{j=1}^nc_jx_j\]

Sous les contraintes:

\[\sum_{j=1}^na_{ij}x_j\leq b_i\text{, pour $i=1,\ldots,m$ }\]
\[x_j\geq0 \text{, pour $j=1,\ldots,n$}\]

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 ?

Existence de solutions optimales ?

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.
Algorithme du simplexe
Mini rappel d’algèbre affine

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: #
Première résolution d’un programe linéaire

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 ?

Introduction de variables d’écart

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
Premier pivot à la main

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.

Premier pivot sous forme matricielle

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:

\[z -5x_1-4x_2-3x_3 = 0\]

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: #
Séquence complète de pivots matriciels

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?

Variables d’écart

Est-ce que l’introduction de ces variables change le problème ?

Tableaux

Définition: tableau initial

Tableau initial:

\[z-\sum_{j=1}^nc_jx_j=0\]
\[s_i+\sum_{j=1}^na_{ij}x_j = b_i \text{, pour $i=1,\ldots,m$}\]

Ou sous forme matricielle:

\[\begin{split}\begin{aligned} z- & CX & = 0\\ S+ & AX & = B\\ X >= 0 \end{aligned}\end{split}\]

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.

Point de vue géométrique

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: #
Pièges et comment les éviter
Bilan des épisodes précédents

On a un algorithme qui marche sur quelques exemples.

Il faut vérifier trois points pour savoir s’il marche en général:

  1. Initialisation
  2. Itération
  3. Terminaison
Itération

Proposition

Étant donné un tableau faisable, on peut toujours effectuer l’une des opérations suivantes:

  1. Conclure que le système a une solution optimale unique, la calculer et la certifier;
  2. Conclure que le système a une infinité de solutions optimales, les calculer et les certifier;
  3. 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.
  4. 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\).

  1. 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.
  2. 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.
  3. 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\).
  4. 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.

Terminaison

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 ?

La méthode des perturbations

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

La méthode du plus petit index

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.

Méthodes mixtes

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.

Initialisation

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:

  1. Si \((0,\ldots,0)\) est solution faisable de \(P\), on passe directement à la phase II.

  2. Définir un problème auxiliaire \(P_0\).

  3. Le premier tableau pour \(P_0\) est infaisable.

  4. Le rendre faisable par un pivot approprié de \(x_0\).

  5. Appliquer le simplexe habituel:

    1. 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.

    2. Si à une étape donnée on atteint une solution optimale:

      1. Si \(x_0\) n’est pas basique:

        Il y a une solution faisable avec \(x_0=0\). On peut donc passer en phase II.

      2. Si \(x_0\) est basique et \(z_0<0\):

        \(P\) est infaisable, et on s’arrête.

      3. Sinon \(x_0\) est basique et \(z_0=0\):

        Situation impossible si on fait toujours sortir \(x_0\) en priorité de la base.

  6. Tirer de \(P_0\) un tableau faisable pour \(P\).

Phase II:

  1. 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: #
Le théorème fondamental de la programmation linéaire

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:

  1. Si \(P\) n’a pas de solutions optimales, alors \(P\) est infaisable ou non borné;
  2. Si \(P\) a une solutions faisable, alors \(P\) a une solution basique faisable;
  3. 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.

Efficacité de l’algorithme du simplexe

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)
Le théorème de dualité
Motivation: estimer la valeur optimale de la fonction objective

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:

\[z^*\leq4x_1+x_2+5x_3+3x_4\leq\frac{25}3x_1+\frac53x_2+5x_3+\frac{40}3x_4\leq\frac{275}3\]

En utilisant la somme de la deuxième et troisième contrainte:

\[z^*\leq4x_1+3x_2+6x_3+3x_4\leq58\]

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:

\[(y_1+5y_2-y_3)x_1+(-y_1+y_2+2y_3)x_2+(-y_1+3y_2+3y_3)x_3+(3y_1+8y_2-5y_3)x_4\]
\[\leq y_1+55y_2+3y_3\]

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\).

Le problème dual

Définition

Soit \(P\) un programme linéaire sous forme standard:

Maximiser:

\[z=\sum_{j=1}^nc_j\ x_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\]

Le dual de \(P\) est le problème:

Minimiser:

\[w=\sum_{i=1}^mb_i\ y_i\]

Sous les contraintes:

\[\sum_{i=1}^ma_{ij}\ y_i\geq c_j,\textrm{ pour }j=1,\ldots,n\]
\[y_i\geq0,\textrm{ pour }i=1,\ldots,m\]

\(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.

\[\sum_{j=1}^nc_j\ x_j\leq\sum_{i=1}^mb_i\ y_i\]

Démonstration

Il suffit d’appliquer les inégalités qui définissent les solutions faisables:

\[z=\sum_{j=1}^nc_j\ x_j\leq\sum_{j=1}^n\left(\sum_{i=1}^ma_{ij}\ y_i\right)x_j=\sum_{i=1}^m\left(\sum_{j=1}^na_{ij}\ x_j\right)y_i\leq\sum_{i=1}^mb_i\ y_i=w\]

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.

Le théorème de dualité

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.

\[\sum_{j=1}^nc_j\ x_j^*=\sum_{i=1}^mb_i\ y_i^*.\]

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^*:=11,\ y_2^*:=0,\ y_3^*:=6.\]

\((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

\[z=z^*+\sum_{j=1}^n\overline{c_j}\ x_j+\sum_{i=1}^md_i\ s_i,\]

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:

\[y_i^*:=-d_i\text{, pour $i=1,\ldots,m$ }.\]

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:

\[z=\sum_{j=1}^nc_j\ x_j\]
\[s_i=b_i-\sum_{j=1}^na_{ij}\ x_j\]

En remplaçant dans l’expression ci-dessus, on obtient

\[\sum_{j=1}^nc_j\ x_j=z^*+\sum_{j=1}^n\overline{c_j}\ x_j-\sum_{i=1}^my_i^*(b_i-\sum_{j=1}^na_{ij}x_j)\]
\[\sum_{j=1}^nc_j\ x_j=z^*-\sum_{i=1}^mb_i\ y_i^*+\sum_{j=1}^n(\overline{c_j}+\sum_{i=1}^ma_{ij}\ y_i^*)\ x_j\]

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

\[z^*=\sum_{j=1}^nb_i\ y_i^*=w^*,\]

comme voulu, et d’autre part que

\[\sum_{i=1}^ma_{ij}\ y_i^*=c_j-\overline{c_j}\geq c_j,\]

c’est-à-dire que \((y_1^*,\ldots,y_m^*)\) est une solution faisable du problème dual.

Relations entre un problème et son 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\):

  1. \(P\) admet une solution optimale si et seulement si \(Q\) en admet une.
  2. 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
Notations matricielles

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.

Conditions de complémentarité des variables d’écart

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

\[s_i=b_i-\sum_{j=1}^na_{ij}x_j,\]

et donc

\[b_i=s_i+\sum_{j=1}^na_{ij}x_j.\]

De même, par définition des variables d’écart \(t_j\) pour le problème dual, on a

\[t_j=\sum_{i=1}^ma_{ij}y_i-c_j,\]

que l’on utilise pour exprimer \(c_j\)

\[c_j=\sum_{i=1}^ma_{ij}y_i-t_j.\]

En remplaçant dans l’expression de \(w-z\), on obtient

\[w-z=\sum_{i=1}^mb_iy_i-\sum_{j=1}^nc_jx_j=\sum_{i=1}^ms_iy_i+\sum_{i=1}^m\left(\sum_{j=1}^na_{ij}x_j\right)y_i-\sum_{j=1}^n\left(\sum_{i=1}^ma_{ij}y_i\right)x_j+\sum_{j=1}^nt_jx_j\]

Qui se simplifie en:

\[w-z=\sum_{i=1}^ms_iy_i+\sum_{j=1}^nt_jx_j.\]

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:

\[y_i^*=0\textrm{ ou }s_i^*=0,\textrm{ pour tout }i=1,\ldots,m;\]
\[x_j^*=0\textrm{ ou }t_j^*=0,\textrm{ pour tout }j=1,\ldots,n.\]

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.

Interprétation géométrique de la dualité

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.\)

  1. Faire une figure dans le plan de la région des solutions faisables.
  2. Donner le problème dual.
  3. 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.
  4. Donner quelques solutions faisables du problème dual.
  5. Tracer sur la figure les régions délimitées par les inégalités correspondantes.
  6. Calculer la solution optimale du primal et du dual.
  7. Les tracer sur la figure.
  8. 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

Interprétation économique des variables duales

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.

  1. 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.\]
  1. Quelle dimension (au sens physique) ont les variables \(x_j\) , \(b_i\) , \(c_j\) , \(a_{ij}\)?
  2. 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:

\[z=\sum_{j=1}^nc_jx_j\]

Sous les contraintes:

\[\sum_{j=1}^na_{ij}x_j\leq b_i+t_i,\textrm{ pour }i=1,\ldots,m;\]
\[x_j\geq0,\textrm{ pour }j=1,\ldots,n.\]

a une solution optimale, et la valeur optimale est

\[z^*+\sum_{i=1}^my_i^*t_i\]

\(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.

Problèmes

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

\[\sum_{j=1}^nc_jx_j\leq z^*+\sum_{i=1}^my_i^*t_i\]

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.

\[\sum_{i=1}^my_i^*\left(\sum_{j=1}^na_{ij}x_j\right)=\sum_{j=1}^n\left(\sum_{i=1}^ma_{ij}y_i^*\right)x_j\geq\sum_{j=1}^nc_jx_j\]

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.

Applications de la programmation linéaire

Todo

Applications de la programmation linéaire et autres points abordés (méthodes alternatives) dans ProgrammationLinéaire.tex.

Combinatoire Polyhédrale

Todo

Finir de traduire en ReST la section sur Ford-Fulkerson & co dans RechercheOpérationnelle.tex, et mettre un lien depuis ici.

Synthèse

Todo

Note sur le corps/anneau de base

On a vu plusieurs modèles généraux pour faire de l’optimisation:

  1. Programmation linéaire

    1. Algorithme du simplexe
      Efficace en pratique (quasiment linéaire), non polynomial en théorie
    2. Algorithme de l’ellipsoïde
      Polynomial, mais non efficace en pratique
    3. Méthode des points intérieurs
      Plus ou moins efficace que le simplexe selon les cas
    4. Théorème de dualité \(\Longrightarrow\) Certification, optimisation, coûts marginaux, …

    5. Mais: solutions dans \(\mathbb{Q}\)

  2. Problèmes de flots

    1. Algorithme de Ford-Fulkerson
      Polynomial \(O(n^3)\). Plus efficace que le simplexe.
    2. Théorème de dualité (flots/coupes)

    3. Théorème d’intégralité
      \(\Longrightarrow\) Algorithmes et théorèmes min-max sur des problèmes discrets.
  3. Réseaux de transports

    1. Algorithme du simplexe pour les réseaux
    2. Théorème de dualité (coûts marginaux)
    3. Théorème d’intégralité
TP
Programmation linéaire

Exercice: Algorithme du simplexe

  1. Télécharger le fichier annexe contenant les utilitaires et exemples du cours.
  2. 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:

\[F = \left(A\vee B\right)\wedge\left(\neg\left(C\wedge D\right)\vee A\vee\neg D\right)\]

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).

  1. Peut-on se ramener à la résolution d’un programme linéaire? D’un programme linéaire mixte?
  2. Quelle est la complexité?
  3. 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.

Combinatoire polyhédrale
Application aux graphes

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\)?

image

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:

image
  1. 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?
  2. 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:

image

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].

Matrices bistochastiques et théorème de Birkhoff-Von Neumann

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\):

\[\begin{split}\left[\begin{array}{ccc} 0,5 & 0,2 & 0,3\\ 0,01 & 0,7 & 0,29\\ 0,49 & 0,1 & 0,41 \end{array}\right].\end{split}\]

\(X\) est une matrice de permutation si sur chaque ligne et chaque colonne il y a exactement un \(1\) et \(n-1\) zéros:

\[\begin{split}\left[\begin{array}{ccc} 0 & 1 & 0\\ 0 & 0 & 1\\ 1 & 0 & 0 \end{array}\right].\end{split}\]

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

  1. Écrire la matrice bistochastique ci-dessus comme combinaison linéaire convexe de matrices de permutations.

  2. 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\).

  3. En déduire une démonstration constructive du théorème de Birkhoff-Von Neumann, que vous écrirez sous la forme d’un programme.

  4. Tester votre programme sur des matrices bistochastiques aléatoires de grande taille (comment en fabriquer?)

Dualités chaînes/antichaînes dans les ordres partiels; théorème de Dilworth

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:

\[\forall x,y\in C, \ x<y \text{ ou } y<x.\]

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:

  1. Une chaîne de taille maximale
  2. Une antichaîne de taille maximale
  3. Une couverture en chaînes de \(P\) de taille minimale
  4. 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.

  1. 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\).

  2. 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

  1. Suivez le déroulement de la preuve sur l’ordre partiel précédent.
  2. 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.
  3. Quelle est la complexité de cet algorithme? Comparer avec la recherche de clique maximale et de colorations minimales dans un graphe.
Quelques références
[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é
Introduction à la complexité
Quelques problèmes
  • 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?
Complexité d’un algorithme
Exemple: recherche naïve dans une liste

Je recherche le nom «Zorro» dans un annuaire en utilisant la méthode suivante:

  1. Je pars du début de l’annuaire;
  2. Je compare le nom avec «Zorro»;
  3. Si oui, j’ai terminé;
  4. Si non, je recommence en 2 avec le nom suivant.

Problème

Combien est-ce que cela va me prendre de temps?

Synthèse

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:

  1. Choix de la mesure de la taille d’une instance du problème (le nombre de mots d’un dictionnaire donné)
  2. 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:

  1. Complexité au pire (\(n\) opérations)
  2. 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:

  1. Complexité en mémoire
  2. Complexité vis-à-vis de toute autre ressource (bande passante, …)
Exercices

Donner des algorithmes et leur complexité au pire et en moyenne pour les problèmes suivants:

  1. Calculer la somme de deux matrices carrées

  2. Calculer le produit de deux matrices carrées

  3. Calculer la somme de deux entiers

  4. Calculer le produit de deux entiers

  5. Calculer l’inverse d’une matrice

  6. Rechercher un élément dans une liste

  7. 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

  8. Rechercher un élément dans une liste triée

  9. Insérer un élément dans une liste triée

Ordres de grandeur
Exemple: recherche dichotomique
Quelques courbes de complexité
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?

Synthèse

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()\):

  1. \(O(4n+3)=O(n)\)
  2. \(O(\log n)+O(\log n)=O(\log n)\)
  3. \(O(n^{2})+O(n)=O(n^{2})\)
  4. \(O(n^{3})O(n^{2}\log n)=O(n^{5}\log n)\)
Exercices

Exercice (Règles mixtes)

Simplifier les expressions suivantes:

  1. \(O(n^3\log n) o(\log n)\)
  2. \(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

Complexité d’un problème

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

  1. Les algorithmes vus précédemment sont-ils optimaux?
  2. 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)\).
Comparaison de la complexité de quelques algorithmes de tri

On a une liste que l’on veut trier, mettons \([7,8,4,2,5,9,3,5]\).

Quelques algorithmes de tri
Tri sélection
  1. On échange le premier élément avec le plus petit des éléments: \(2,8,4,7,5,9,3,5\)
  2. On échange le deuxième élément avec le plus petit des éléments restants: \(2,3,4,7,5,9,8,5\)
  3. Etc.
  4. 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.
  5. À la fin, la liste est triée: \(2,3,4,5,5,7,8,9\).
Tri fusion
  1. On groupe les éléments par paquets de deux, et on trie chacun de ces paquets: \((7,8),(2,4),(5,9),(3,5)\).
  2. On groupe les éléments par paquets de quatre, et on trie chacun de ces paquets: \((2,4,7,8),(3,5,5,9)\).
  3. 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.
  4. À la fin, tous les éléments sont dans le même paquet et sont triés: \((2,3,4,5,5,7,8,9)\).
Tri rapide
  1. On choisit une valeur \(p\) dans la liste que l’on appelle pivot.
  2. 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.
  3. On applique récursivement l’algorithme sur les éléments avant et après \(p\).
Tri insertion, tri par arbre binaire de recherche
Analyse de complexité

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:

  1. Calcul du \(n\)-ième nombre de Fibonacci;
  2. Calcul du déterminant d’une matrice;
  3. Calcul du rang d’une matrice;
  4. Calcul de l’inverse d’une matrice;
  5. Calcul d’un vecteur \(x\) solution de \(Ax=b\), où \(A\) est une matrice et \(b\) un vecteur;
  6. Calcul du pgcd de deux nombres;
  7. Test de primalité de \(n\);
  8. Recherche du plus court chemin entre deux stations de métro à Paris;
  9. Calcul de la \(n\)-ième décimale de \(\sqrt{2}\);
  10. Calcul de l’inverse d’un nombre modulo \(3\);
  11. Recherche d’un échec et mat en \(4\) coups à partir d’une position donnée aux échecs.
  12. 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?
Quelques références

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
Consignes

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).

Objectifs

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.

Organisation du code
  1. Écrire des fonctions dans un fichier séparé et les charger dans Jupyter;
  2. Écrire des fonctions avec documentations et tests;
  3. Écrire du code modulaire.
Correction des programmes
  1. Cahier des charges d’une fonction: Préconditions, postconditions et invariants de boucles;
  2. Exécuter les tests de manière automatique,
Complexité pratique des programmes
  1. Mesurer un nombre d’opérations, un temps d’exécution;
  2. Représenter la complexité dans un graphique.
Exercice 1: Test et correction des algorithmes de recherche
  1. Implanter une fonction recherche(liste, valeur) renvoyant la première position de valeur dans la liste, ou None si valeur n’est pas dans la liste.

  2. 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)
    
  3. 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>
      
  4. 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.

  5. Vérifier que vous pouvez maintenant utiliser les fonctions présentes dans recherche.py.

  6. 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.

  7. 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 et sup, vérifiant à chaque étape l’invariant inf <= i < sup, où i est la première position de la valeur dans la liste, si elle y est présente.

Exercice 2: Complexité pratique des algorithmes de recherche
  1. 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.

  2. 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).

  3. 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:

    1. Voir randint() pour créer une liste aléatoire.

    2. Définir une fonction complexite_recherche(n) qui lance recherche sur un échantillon de listes de longueur \(n\), et renvoie le nombre de comparaisons en moyenne et au pire.

    3. Voir point() pour afficher un nuage de points. Que fait l’exemple suivant?

      sage: point( [ [i, i^2] for i in range(10) ] )
      
    4. Pour trier une liste:

      sage: sorted(['c', 'b', 'a'])
      ['a', 'b', 'c']
      
  4. Évaluer la taille maximale d’une liste dans laquelle on peut faire une recherche en moins d’une heure et d’une semaine.

Exercice 3: Implantation de quelques algorithmes de tri

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.

Un premier algorithme de tri

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».

Tri à bulle en place

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.

Tri fusion

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!\)

Tri rapide

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.

Autres tris

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:
    1. consulter la documentation de LabelledBinaryTree pour trouver comment construire des arbres binaires étiquetés.
    2. Définir une fonction récursive insere(arbre, i) qui insère un nombre i dans un arbre binaire de recherche.
Exercice 4: Complexité de l’algorithme de tri de Python

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

Exercice 5: bancs d’essais au chronomètre

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

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...

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.

\[\int e^x dx = e^x + c\]
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)
\[\left[X = -\frac{b + \sqrt{-4 \, a c + b^{2}}}{2 \, a}, X = -\frac{b - \sqrt{-4 \, a c + b^{2}}}{2 \, a}\right]\]
sage: s = solve(a*X^3 + b*X^2 + c*X + d == 0, X)
sage: show(s[0])
\[X = -\frac{1}{2} \, {\left(i \, \sqrt{3} + 1\right)} {\left(\frac{\sqrt{27 \, a^{2} d^{2} + 4 \, a c^{3} - b^{2} c^{2} - 2 \, {\left(9 \, a b c - 2 \, b^{3}\right)} d} \sqrt{3}}{18 \, a^{2}} - \frac{27 \, a^{2} d - 9 \, a b c + 2 \, b^{3}}{54 \, a^{3}}\right)}^{\left(\frac{1}{3}\right)} - \frac{b}{3 \, a} + \frac{{\left(-i \, \sqrt{3} + 1\right)} {\left(3 \, a c - b^{2}\right)}}{18 \, {\left(\frac{\sqrt{27 \, a^{2} d^{2} + 4 \, a c^{3} - b^{2} c^{2} - 2 \, {\left(9 \, a b c - 2 \, b^{3}\right)} d} \sqrt{3}}{18 \, a^{2}} - \frac{27 \, a^{2} d - 9 \, a b c + 2 \, b^{3}}{54 \, a^{3}}\right)}^{\left(\frac{1}{3}\right)} a^{2}}\]

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)
\[\begin{split}\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)\end{split}\]
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

\[r' = \sigma\ r\ \sigma^{-1} \quad \text{and} \quad u' = \sigma\ u\ \sigma^{-1}\]

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.

\[\begin{split}S = \left(\begin{array}{cc}0&-1\\1&0\end{array}\right) \qquad L = \left(\begin{array}{cc}1&1\\0&1\end{array}\right) \qquad R = \left(\begin{array}{cc}1&0\\1&1\end{array}\right) \qquad V = \left(\begin{array}{cc}-1&0\\0&1\end{array}\right)\end{split}\]

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

\[e_n = \frac{3 n (n-2)}{8} \prod_{p|n} \left(1 - \frac{1}{p^2} \right) \quad a_n = \frac{3 n (n-1)}{16} \prod_{p|n} \left(1 - \frac{1}{p^2} \right) \quad b_n = \frac{3 n (n-3)}{16} \prod_{p|n} \left(1 - \frac{1}{p^2} \right)\]

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^*\):

\[\frac{1}{|W|} \sum_{w\in W} \frac{\chi_V(w)}{\det(1-qw)} = \Hilb(\CC[V]^W,q) \quad ( q^{e_1} + \cdots + q^{e_n})\]
\[\frac{1}{|W|} \sum_{w\in W} \frac{\chi_V^*(w)}{\det(1-qw)} = \Hilb(\CC[V]^W,q) \quad ( q^{e_1^*} + \cdots + q^{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:

\[\Hilb((\CC[V]\otimes \bigwedge^\cdot V^*)^W,q,t) = \frac{1}{|W|} \sum_{w\in W} \frac{\det(1+tw)}{\det(1-qw)}\]

and then compare it to the prediction of Solomon’s formula, namely:

\[\Hilb((\CC[V]\otimes \bigwedge^\cdot V^*)^W,q,t) = \frac{\prod_{i=1}^n ( 1 + q^{e_i}t )}{\prod_{i=1}^n (1 - q^{d_i} )}\]

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
  1. Find a bug
  1. Verify the content the patch
  1. Sage trac server
  1. Upload the patch on Sage trac
  1. Create a ticket
  1. More on Mercurial queues
  1. Edit the sage sources
  1. Download a patch
  1. Enable Mercurial queues
  1. Edit the series file
  1. Create a patch
  1. Reviewing a patch
  1. Update the current patch
  1. Positive review or Needs work
  1. Export a patch
  1. Advanced tricks
Are you ready?

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

See Tutorial: Editing 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

Sage

Sage trac

Sage Developer’s Guide

Reviewing a Sage trac ticket, William Stein’s blog post, October 31, 2010.

This talk was generated

\[ \begin{align}\begin{aligned} \def\CC{\bf C} \def\QQ{\bf Q} \def\RR{\bf R} \def\ZZ{\bf Z} \def\NN{\bf N}\\# Partitions and Young tableaux tutorial\end{aligned}\end{align} \]

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}\):

\[\{(a,b)\ |\ 0\leq a<\mu_i\quad{\rm and}\quad b<\ell(\mu)\}.\]

This set is also denoted \(\mu\), and its elements are the cells of \(\mu\). The conjuguate of \(\mu\), is the partition \(\mu'\) such that

\[\mu'=\{(b,a)\ |\ (a,b)\in\mu\}.\]

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:

*Let \(\lambda\) be the partition \([15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]\). Compute *
\[\sum\limits_{i=0}^{20} \sum\limits_{\mu \vdash i,\ \mu \subseteq \lambda} q^i.\]
[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

\[ \begin{align}\begin{aligned}f^\mu:=\frac{n!}{\prod_{c\in\mu} h(c,\mu)},\\with :math:`h((i,j),\mu):= \mu_i+\mu'_j-i-j-1`, may be encoded as\end{aligned}\end{align} \]
[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.)

\begin{eqnarray} && n(\mu):=\sum_{(i,j)\in\mu} i, \qquad T_\mu:=\prod_{(i,j)\in\mu} t^iq^j,\\ && B_\mu:=\sum_{(i,j)\in\mu} t^iq^j, \qquad \Pi_\mu:=\prod_{(i,j)\in\mu,\ (i,j)\not=(0,0)} (1-t^iq^j),\\ &&\varepsilon_\mu:=\prod_{c\in\mu}(q^{a(c)}-t^{l(c)+1})(t^{l(c)}-q^{a(c)+1}), \end{eqnarray}

where \(a(c)\) et \(l(c)\) respectively denote the arm and the leg of a cell \(c\) in \(\mu\). We have

\[(n(\mu),n(\mu'))=\sum_{c \in \mu} c.\]
[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}}\)

Tutorial: Using the notebook and navigating the help system (outdated)

This worksheet is based on William Stein’s JPL09__intro_to_sage.sws worksheet.

Warning

Some of this tutorial needs to be updated w.r.t. the new Jupyter based notebook.

Making this help page into a worksheet

Go into the File menu, and click on Copy worksheet

Entering, Editing, and Evaluating Input

To evaluate code in the Sage Notebook, type the code into an input cell and press shift-enter or click the evaluate link. Try it now with a simple expression (e.g., \(2 + 2\)). The first time you evaluate a cell takes longer than subsequent times since a new Sage process is started:

sage: # edit here

Create new input cells by clicking on the blue line that appears between cells when you move your mouse around. Try it now:

sage: # edit here

You can go back and edit any cell by clicking in it (or using the keyboard to move up or down). Go back and change your 2+2 above to 3 + 3 and re-evaluate it.

You can also edit this text right here by double clicking on it, which will bring up the TinyMCE Javascript text editor. You can even put embedded mathematics like this $sin(x) - y^3$ just like with LaTeX.

You can also easily make interactive widgets as illustrated below. 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
....: def f(n=(1..15), m=(1..15)):
....:     print "n * m =", 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)

If you mess everything up, click on Action -&gt; Restart Worksheet at the top of the screen to reset all the variable names and restart everything. You can also click “Undo” in the upper right to revert the worksheet to a previously saved state.

Click the Log link at the top of this page to view a log of recent computations!

How to Get Context-Sensitive Help and Search the Documentation

You find out what functions you can call on an object X by typing X.<tab key>:

sage: X = 2009

Now type X. then press the tab key:

sage: X.

Once you have selected a function, say factor, type X.factor(<tab key> or X.factor? to get help and examples of how to use that function. Try this now:

sage: # edit here

To get full-text searchable help and a more extensive tutorial, click the Help link in the upper right of this page. The help pages are dynamic, and you can play with their examples. You can also access the Fast Static Versions of the Documentation.

If you are ready, you can now go to ``

If you need live help from a person, just click on Help above, then click on Help via Internet Chat (IRC). This brings you to the Sage chat room where you can often get help.

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}\) :

\[\left\{(a,b)|0 \leq a \leq \mu_i \text{ and } b < \ell(\mu)\right\}.\]

This set is also denoted \(\mu\), and its elements are the cells of \(\mu\). The conjugate of \(\mu\), is the partition \(\mu'\) such that

\[\mu' = \{(b,a) \vert (a,b) \in \mu\}.\]

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:
\[\sum\limits_{i=0}^{20} \sum\limits_{\mu \vdash i \subseteq \lambda} q^i.\]
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

\[\begin{eqnarray}f^{\mu}: = \frac{n!}{\prod_{c \in \mu} h(c,\mu)},\end{eqnarray}\]

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:

run button and 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:

\[\zeta(s)=\sum_{n=1}^{\infty}\frac{1}{n^s}=\prod_p \left(\frac{1}{1-p^{-s}}\right)\]

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.

\[\begin{split}M = \left(\begin{array}{rrrr} 10 & 4 & 1 & 1 \\ 4 & 6 & 5 & 1 \\ 1 & 5 & 6 & 4 \\ 1 & 1 & 4 & 10 \end{array}\right)\end{split}\]
sage: matrix?
sage:

Then, using methods of the matrix,

  1. Compute the determinant of the matrix.
  2. Compute the echelon form of the matrix.
  3. Compute the eigenvalues of the matrix.
  4. Compute the kernel of the matrix.
  5. 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,

  1. Create the vector \(v = (1, -1, -1, 1)\).
  2. 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\):
\[y = 1 + x y^2\]

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.

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 :
\[h_d = \sum \limits_{\mu \vdash d} \dfrac{1}{z_{\mu}} p_{\mu}\]

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

\[p_k(x_1,x_2,\ldots, x_n)= x_1^k+x_2^k+\ldots +x_n^k,\ (\hbox{for any } k\in\mathbb{N}),\ {\rm or}\]
\[e_n(x_1,x_2,\ldots, x_n) = x_1x_2\cdots x_n.\]

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\) :

\[\begin{split}e_k(n) = e_k(n-1) + x_ne_{k-1}(n-1), \\ h_k(n) = h_k(n-1) + x_nh_{k-1}(n), \\ e_k(0)=h_k(0) = \delta_{k,0},\end{split}\]

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,

\[\langle p_\mu,p_\lambda\rangle = z_\mu\,\delta_{\mu,\lambda}\]

Or, yet again, we have

\[\left(\langle p_\mu,p_\lambda/z_\lambda\rangle\right)_{\mu,\lambda}= {\rm Id}_{n\times n}\]

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:

\[\langle p_{\mu},p_{\mu}\rangle_{q,t} = z_\mu\,\prod_i\frac{1-q^{\mu_i}}{1-t^{\mu_i}}.\]

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

returns True if the given symmetric function is Schur positive and False 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

\[\prod_{\mu\vdash n}\frac{1}{k_\mu},\qquad {\rm where}\qquad k_\mu:=\sum_{\nu\vdash n} K_{\mu,\nu},\]

with \(K_{\mu,\nu}\) the Kostka numbers. Recall that these occur in the expansion of the Schur functions in terms of the monomial functions:

\[s_\mu=\sum_\nu K_{\mu,\nu}\, m_\nu.\]

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:

\[\nabla H_{\mu} = t^{n(\mu)} q^{n(\mu')} H_{\mu},\]

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 :

\[C_n(q,t) = \langle \nabla e_n , e_n \rangle.\]

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:
\[\Delta(g) = \sum_{k+j=n}\sum_{\mu\vdash k,\ \nu\vdash j} a_{\mu,\nu}\, s_\mu\otimes s_\nu\]
\[g(\mathbf{x}+\mathbf{y})= \sum_{k+j=n}\sum_{\mu\vdash k,\ \nu\vdash j} a_{\mu,\nu}\, s_\mu(\mathbf{x}) s_\nu(\mathbf{y})\]

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

\[\Delta(s_\lambda) = \sum_{\mu\subseteq \lambda} s_{\lambda/\mu}\otimes s_\mu.\]

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

\[\Delta(h_n) = \sum_{k+j=n} h_k\otimes h_j.\]
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

\[\sum_{n\geq 0} h_n(\mathbf{x}\mathbf{y})=\prod_{i,j}\frac{1}{1-x_iy_j}\]

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

\[h_n(\mathbf{x}\mathbf{y})=\sum_{\mu\vdash n} F_\mu\otimes G_\mu \qquad {\rm iff}\qquad \langle F_\mu,G_\lambda\rangle=\delta_{\mu\lambda}, \qquad (\delta_{\mu \lambda}:\ \hbox{Kronecker "delta"})`\]
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&gt;=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&gt;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

  1. List all the strict partitions of \(5\) (hint: use Partitions with max_slope):

    sage: # edit here
    
  2. List all the vectors of 0 and 1 of length 5 (hint: use IntegerVectors with max_part):

    sage: # edit here
    

    You can also use a cartesian product:

    sage: # edit here
    
  3. 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
  1. 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…