Blogue

Releasing slabbe, my own Sage package

27 août 2014 | Catégories: sage, slabbe spkg | View Comments

Since two years I wrote thousands of line of private code for my own research. Each module having between 500 and 2000 lines of code. The code which is the more clean corresponds to code written in conjunction with research articles. People who know me know that I systematically put docstrings and doctests in my code to facilitate reuse of the code by myself, but also in the idea of sharing it and eventually making it public.

I did not made that code into Sage because it was not mature enough. Also, when I tried to make a complete module go into Sage (see #13069 and #13346), then the monstrous never evolving #12224 became a dependency of the first and the second was unofficially reviewed asking me to split it into smaller chunks to make the review process easier. I never did it because I spent already too much time on it (making a module 100% doctested takes time). Also, the module was corresponding to a published article and I wanted to leave it just like that.

Getting new modules into Sage is hard

In general, the introduction of a complete new module into Sage is hard especially for beginners. Here are two examples I feel responsible for: #10519 is 4 years old and counting, the author has a new work and responsabilities; in #12996, the author was decouraged by the amount of work given by the reviewers. There is a lot of things a beginner has to consider to obtain a positive review. And even for a more advanced developper, other difficulties arise. Indeed, a module introduces a lot of new functions and it may also introduce a lot of new bugs... and Sage developpers are sometimes reluctant to give it a positive review. And if it finally gets a positive review, it is not available easily to normal users of Sage until the next release of Sage.

Releasing my own Sage package

Still I felt the need around me to make my code public. But how? There are people (a few of course but I know there are) who are interested in reproducing computations and images done in my articles. This is why I came to the idea of releasing my own Sage package containing my public research code. This way both developpers and colleagues that are user of Sage but not developpers will be able to install and use my code. This will make people more aware if there is something useful in a module for them. And if one day, somebody tells me: "this should be in Sage", then I will be able to say : "I agree! Do you want to review it?".

Old style Sage package vs New sytle git Sage package

Then I had to chose between the old and the new style for Sage packages. I did not like the new style, because

  • I wanted the history of my package to be independant of the history of Sage,
  • I wanted it to be as easy to install as sage -i slabbe,
  • I wanted it to work on any recent enough version of Sage,
  • I wanted to be able to release a new version, give it to a colleague who could install it right away without changing its own Sage (i.e., updating the checksums).

Therefore, I choose the old style. I based my work on other optional Sage packages, namely the SageManifolds spkg and the ore_algebra spkg.

Content of the initial version

The initial version of the slabbe Sage package has modules concerning four topics: Digital geometry, Combinatorics on words, Combinatorics and Python class inheritance.

/Files/2014/slabbe_content.png

For installation or for release notes of the initial version of the spkg, consult the slabbe spkg section of the Sage page of this website.

Read and Post Comments

Mes commentaires transmis à reinventerlaville.ca (Lac-Mégantic)

27 avril 2014 | Catégories: Uncategorized | View Comments

1. Quelles villes ou villages du Québec, ou d’ailleurs dans le monde, pourraient servir d’inspiration pour Lac-Mégantic ? Pourquoi ?

Montpellier (France).

Un quartier piéton de un kilomètre carré dans le centre de la ville. C'est lorsque j'ai habité dans cette ville en 2009-2010 que j'ai réalisé à quel point les voitures sont bruyantes et désagréables. Au Québec, comme les voitures sont partout, on les tolère et on ne réalise pas qu'elles nous stressent. Un endroit où les voitures ne circulent pas permet aux citoyens de se réapproprier l'espace public (air, sol, son). Les terrasses des resto prennent la rue. On s'entend parler. On peut discuter. Et moins d'exhaust de char dans notre assiette.

Autour du quartier piéton, il y a des embouteillages de voitures causés par les accès rendus difficiles. Je suis retourné en 2013 et vous ne devinerez pas ce que la ville de Montpellier a fait comme changement dans l'une de ces rues embouteillées : le nombre de voies pour les voitures est passé de 2 à 1 au profit d'une piste cyclable. Les embouteillages sont rendus si pire que la vitesse moyenne des voitures est de 16 km/h. Il vaut mieux se rendre au centre-ville à pied, prendre le tram ou prendre le vélo. Voilà une ville qui a compris comment régler les embouteillages de voitures : les rendre pire! Comme ça les gens trouvent de meilleures solutions de transport souvent plus vertes et plus écolos.

Pour s'inspirer justement et pour en savoir plus sur une ville qui se voit en 2040 en train de relever les défis de demain et non en 1960 en train de subir les erreurs du passé, je vous invite à consulter les pages web de la ville de Montpellier à propos de l'urbanisme, du déplacement actif, de la circulation et d'aménagement du territoire.

/Files/2014/montpellier.jpg

2. Quels types de projets ou d'activités pourraient améliorer la qualité de vie à Lac-Mégantic ?

Il faut surtout éviter que l'urbanisme fasse de Lac-Mégantic une ville pour le char. Si l'accès aux services principaux de Lac-Mégantic est facilité pour les voitures et compliqué ou dangereux pour les piétons, je comprends que le monde choisissent d'habiter à Frontenac ou dans une autre banlieue parce qu'il n'y a pas d'avantage à habiter proche et de toute façon, il faut s'y rendre en char.

/Files/2014/cycliste-pieton.jpg

Or, je sais que Lac-Mégantic est capable de concevoir son urbanisme en pensant très bien aux voitures et très peu aux piétons. D'où l'importance d'en parler. En effet, avant les événements du 6 juillet, il y a eu la construction d'un nouveau centre sportif. Y aller en voiture, c'était génial : tu t'y rends, tu te stationnes, tu rentres. Par contre à vélo ou à pied, c'était compliqué et dangereux. D'abord, mélanger vélo et piéton sur la même voie, ça coûte peut-être moins cher, mais c'est dangereux : les deux n'ont vraiment pas la même vitesse. Ensuite, l'accès pour les piétons et les vélos nécessitait de traverser la rue à la toute fin pour se rendre au bâtiment. À mon avis, c'était dangereux (un piéton qui regarde le bâtiment traverse en diagonal et ne voit pas la voiture qui lui rentre dedans), inefficace (une traversée piéton dans le goulot d'entrée pour les voitures).

Il faut que les concepteurs se mettent à la place des piétons dans les plans qu'ils conçoivent. C'est important, c'est pour les résidents de notre ville!

3. Quelle thématique principale, quelle ambiance, devrait ressortir du futur centre-ville?

Un centre-ville où on passe d'une activité à l'autre en marchant. Pour ce faire, il faut des rues piétonnes, des trottoirs des deux côtés pour les rues qui ne peuvent pas être que piétonnes, des axes cyclables utiles. Par utile, je veux dire qui partent des quartiers résidentiels et qui se rendent devant la porte des magasins. Les stationnements à vélos doivent être nombreux et mieux placés que les stationnement pour voitures. Par mieux placés, je veux dire plus proche des entrées des magasins. Bien sûr, ça prend aussi des arbres.

Par définition, les axes cyclables utiles seront utilisés par les résidents. C'est bien une piste cyclable qui fait le tour du lac et qui se rendent dans le parc des vétérans ou pourquoi pas jusqu'au bout de la rue d'Orsennens. Les cul-de-sac, c'est bien pour les touristes et les citoyens le dimanche après-midi. Mais, une piste cyclable on le sait, c'est pas juste pour les touristes, ça peut aussi être aussi un moyen de transport pour aller d'un point A (maison) à un point B (service, épicerie, centre-ville).

Je pense qu'à Lac-Mégantic, il manque des pistes cyclables qui se rendent dans les quartiers résidentiels. Il faudrait une piste cyclable qui fasse toute la rue Papineau pour aller chercher les résidents de ce quartier. C'est pas si cher dans une telle rue : il suffit de réasphalter un peu et mettre de la peinture (voir la piste cyclable de la rue Boyer ou Rachel à Montréal). Il faudra aussi une piste cyclable qui relie le cégep avec le centre-ville. Il faut réfléchir un peu pour choisir les bonnes rues. Peut-être que la rue dollard serait la bonne, je ne sais pas.

/Files/2014/boyer.jpg

Ensuite, ça prend des pistes cyclables qui se rendent aux services, aux restaurants (le centre-ville par exemple) mais aussi qui se rendent dans les lieux de travail importants tel que le parc industriel. Est-ce que les pistes cyclables qui longent la rivière déboucheront sur le parc industriel? Il faut! Il faut que les employés des usines puissent se rendre à vélo à leur travail en évitant si possible la rue villeneuve.

4. Vous avez d’autres idées ou commentaires ? N’hésitez pas, cet espace vous appartient.

Un cercle de pierre à Lac-Mégantic! Quand je suis allé en Irlande il y a qq années, j'ai visité des alignements de roche vieux de 6000 ans (à la StoneEdge) et des cercles de pierres. Les cercles de pierre sont toujours placé à un endroit où on voit bien l'horizon. Les alignement dans le cercle sont faits en fonction du lieu à l'horizon où se couche le soleil. Les alignements de l'entrée du cercle avec les autres pierres indiquent le solstice d'été (le coucher de soleil le plus à droite sur l'horizon), le solstice d'hiver (le coucher de soleil le plus à gauche à l'horizon) et les équinoxes de printemps et d'automne (au milieu). Ils étaient utilisés peut-être comme calendrier pour prédire les saisons, le temps de la récolte, etc.

/Files/2014/cercle_de_pierre.jpg

Quand, je suis revenu, je m'étais dit que ce serait bon d'en faire un à Lac-Mégantic. J'avais pensé à deux lieux : près de la piste cyclable en bas de la polyvalente. C'est un endroit où on voit bien l'horizon. Aussi, j'avais pensé à la pelouse entre le centre sportif et le centre-ville. De cet endroit, on voyait aussi bien l'horizon. C'est une idée qui ne coût pas grand chose (il y avait déjà un empilement de roches près du centre sportif) et qui peut devenir un lieu de recueillement, d'apprentissage (un écriteau explique les alignements choisi pour le cercle de pierre de Lac-Mégantic, explique les premiers alignements de pierre faits il y a 6000 ans, etc.) et de réflexion (on regarde le coucher du soleil).

Read and Post Comments

My status report at Sage Days 57 (RecursivelyEnumeratedSet)

11 avril 2014 | Catégories: sage | View Comments

At Sage Days 57, I worked on the trac ticket #6637: standardize the interface to TransitiveIdeal and friends. My patch proposes to replace TransitiveIdeal and SearchForest by a new class called RecursivelyEnumeratedSet that would handle every case.

A set S is called recursively enumerable if there is an algorithm that enumerates the members of S. We consider here the recursively enumerated set that are described by some seeds and a successor function succ. The successor function may have some structure (symmetric, graded, forest) or not. Many kinds of iterators are provided: depth first search, breadth first search or elements of given depth.

TransitiveIdeal and TransitiveIdealGraded

Consider the permutations of \(\{1,2,3\}\) and the poset generated by the method permutohedron_succ:

sage: P = Permutations(3)
sage: d = {p:p.permutohedron_succ() for p in P}
sage: S = Poset(d)
sage: S.plot()
/Files/2014/poset_123.png

The TransitiveIdeal allows to generates all permutations from the identity permutation using the method permutohedron_succ as successor function:

sage: succ = attrcall("permutohedron_succ")
sage: seed = [Permutation([1,2,3])]
sage: T = TransitiveIdeal(succ, seed)
sage: list(T)
[[1, 2, 3], [2, 1, 3], [1, 3, 2], [2, 3, 1], [3, 2, 1], [3, 1, 2]]

Remark that the previous ordering is neither breadth first neither depth first. It is a naive search because it stores the element to process in a set instead of a queue or a stack.

Note that the method permutohedron_succ produces a graded poset. Therefore, one may use the TransitiveIdealGraded class instead:

sage: T = TransitiveIdealGraded(succ, seed)
sage: list(T)
[[1, 2, 3], [2, 1, 3], [1, 3, 2], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

For TransitiveIdealGraded, the enumeration is breadth first search. Althougth, if you look at the code (version Sage 6.1.1 or earlier), we see that this iterator do not make use of the graded hypothesis at all because the known set remembers every generated elements:

current_level = self._generators
known = set(current_level)
depth = 0
while len(current_level) > 0 and depth <= self._max_depth:
    next_level = set()
    for x in current_level:
        yield x
        for y in self._succ(x):
            if y == None or y in known:
                continue
            next_level.add(y)
            known.add(y)
    current_level = next_level
    depth += 1
return

Timings for TransitiveIdeal

sage: succ = attrcall("permutohedron_succ")
sage: seed = [Permutation([1..5])]
sage: T = TransitiveIdeal(succ, seed)
sage: %time L = list(T)
CPU times: user 26.6 ms, sys: 1.57 ms, total: 28.2 ms
Wall time: 28.5 ms
sage: seed = [Permutation([1..8])]
sage: T = TransitiveIdeal(succ, seed)
sage: %time L = list(T)
CPU times: user 14.4 s, sys: 141 ms, total: 14.5 s
Wall time: 14.8 s

Timings for TransitiveIdealGraded

sage: seed = [Permutation([1..5])]
sage: T = TransitiveIdealGraded(succ, seed)
sage: %time L = list(T)
CPU times: user 25.3 ms, sys: 1.04 ms, total: 26.4 ms
Wall time: 27.4 ms
sage: seed = [Permutation([1..8])]
sage: T = TransitiveIdealGraded(succ, seed)
sage: %time L = list(T)
CPU times: user 14.5 s, sys: 85.8 ms, total: 14.5 s
Wall time: 14.7 s

In conlusion, use TransitiveIdeal for naive search algorithm and use TransitiveIdealGraded for breadth search algorithm. Both class do not use the graded hypothesis.

Recursively enumerated set with a graded structure

The new class RecursivelyEnumeratedSet provides all iterators for each case. The example below are for the graded case.

Depth first search iterator:

sage: succ = attrcall("permutohedron_succ")
sage: seed = [Permutation([1..5])]
sage: R = RecursivelyEnumeratedSet(seed, succ, structure='graded')
sage: it_depth = R.depth_first_search_iterator()
sage: [next(it_depth) for _ in range(5)]
[[1, 2, 3, 4, 5],
 [1, 2, 3, 5, 4],
 [1, 2, 5, 3, 4],
 [1, 2, 5, 4, 3],
 [1, 5, 2, 4, 3]]

Breadth first search iterator:

sage: it_breadth = R.breadth_first_search_iterator()
sage: [next(it_breadth) for _ in range(5)]
[[1, 2, 3, 4, 5],
 [1, 3, 2, 4, 5],
 [1, 2, 4, 3, 5],
 [2, 1, 3, 4, 5],
 [1, 2, 3, 5, 4]]

Elements of given depth iterator:

sage: list(R.elements_of_depth_iterator(9))
[[5, 4, 2, 3, 1], [4, 5, 3, 2, 1], [5, 3, 4, 2, 1], [5, 4, 3, 1, 2]]
sage: list(R.elements_of_depth_iterator(10))
[[5, 4, 3, 2, 1]]

Levels (a level is a set of elements of the same depth):

sage: R.level(0)
[[1, 2, 3, 4, 5]]
sage: R.level(1)
{[1, 2, 3, 5, 4], [1, 2, 4, 3, 5], [1, 3, 2, 4, 5], [2, 1, 3, 4, 5]}
sage: R.level(2)
{[1, 2, 4, 5, 3],
 [1, 2, 5, 3, 4],
 [1, 3, 2, 5, 4],
 [1, 3, 4, 2, 5],
 [1, 4, 2, 3, 5],
 [2, 1, 3, 5, 4],
 [2, 1, 4, 3, 5],
 [2, 3, 1, 4, 5],
 [3, 1, 2, 4, 5]}
sage: R.level(3)
{[1, 2, 5, 4, 3],
 [1, 3, 4, 5, 2],
 [1, 3, 5, 2, 4],
 [1, 4, 2, 5, 3],
 [1, 4, 3, 2, 5],
 [1, 5, 2, 3, 4],
 [2, 1, 4, 5, 3],
 [2, 1, 5, 3, 4],
 [2, 3, 1, 5, 4],
 [2, 3, 4, 1, 5],
 [2, 4, 1, 3, 5],
 [3, 1, 2, 5, 4],
 [3, 1, 4, 2, 5],
 [3, 2, 1, 4, 5],
 [4, 1, 2, 3, 5]}
sage: R.level(9)
{[4, 5, 3, 2, 1], [5, 3, 4, 2, 1], [5, 4, 2, 3, 1], [5, 4, 3, 1, 2]}
sage: R.level(10)
{[5, 4, 3, 2, 1]}

Recursively enumerated set with a symmetric structure

We construct a recursively enumerated set with symmetric structure and depth first search for default enumeration algorithm:

sage: succ = lambda a: [(a[0]-1,a[1]), (a[0],a[1]-1), (a[0]+1,a[1]), (a[0],a[1]+1)]
sage: seeds = [(0,0)]
sage: C = RecursivelyEnumeratedSet(seeds, succ, structure='symmetric', algorithm='depth')
sage: C
A recursively enumerated set with a symmetric structure (depth first search)

In this case, depth first search is the default algorithm for iteration:

sage: it_depth = iter(C)
sage: [next(it_depth) for _ in range(10)]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8), (0, 9)]

Breadth first search. This algorithm makes use of the symmetric structure and remembers only the last two levels:

sage: it_breadth = C.breadth_first_search_iterator()
sage: [next(it_breadth) for _ in range(10)]
[(0, 0), (0, 1), (0, -1), (1, 0), (-1, 0), (-1, 1), (-2, 0), (0, 2), (2, 0), (-1, -1)]

Levels (elements of given depth):

sage: sorted(C.level(0))
[(0, 0)]
sage: sorted(C.level(1))
[(-1, 0), (0, -1), (0, 1), (1, 0)]
sage: sorted(C.level(2))
[(-2, 0), (-1, -1), (-1, 1), (0, -2), (0, 2), (1, -1), (1, 1), (2, 0)]

Timings for RecursivelyEnumeratedSet

We get same timings as for TransitiveIdeal but it uses less memory so it might be able to enumerate bigger sets:

sage: succ = attrcall("permutohedron_succ")
sage: seed = [Permutation([1..5])]
sage: R = RecursivelyEnumeratedSet(seed, succ, structure='graded')
sage: %time L = list(R)
CPU times: user 24.7 ms, sys: 1.33 ms, total: 26.1 ms
Wall time: 26.4 ms
sage: seed = [Permutation([1..8])]
sage: R = RecursivelyEnumeratedSet(seed, succ, structure='graded')
sage: %time L = list(R)
CPU times: user 14.5 s, sys: 70.2 ms, total: 14.5 s
Wall time: 14.6 s
Read and Post Comments

Demo of the IPython notebook at Sage Paris group meeting

06 mars 2014 | Catégories: ipython, sage | View Comments

Today I am presenting the IPython notebook at the meeting of the Sage Paris group. This post gathers what I prepared.

Installation

First you can install the ipython notebook in Sage as explained in this previous blog post. If everything works, then you run:

sage -ipython notebook

and this will open a browser.

Turn on Sage preparsing

Create a new notebook and type:

In [1]: 3 + 3
6
In [2]: 2 / 3
0
In [3]: matrix
Traceback (most recent call last):
...
NameError: name 'matrix' is not defined

By default, Sage preparsing is turn off and Sage commands are not known. To turn on the Sage preparsing (thanks to a post of Jason on sage-devel):

%load_ext sage.misc.sage_extension

Since sage-6.2, according to sage-devel, the command is:

%load_ext sage

You now get Sage commands working in ipython:

In [4]: 3 + 4
Out[4]: 7
In [5]: 2 / 3
Out[5]: 2/3
In [6]: type(_)
Out[6]: <type 'sage.rings.rational.Rational'>
In [7]: matrix(3, range(9))
Out[7]:
[0 1 2]
[3 4 5]
[6 7 8]

Scroll and hide output

If the output is too big, click on Out to scroll or hide the output:

In [8]: range(1000)

Sage 3d Graphics

3D graphics works but open in a new Jmol window:

In [9]: sphere()

Sage 2d Graphics

Similarly, 2D graphics works but open in a new window:

In [10]: plot(sin(x), (x,0,10))

Inline Matplotlib graphics

To create inline matplotlib graphics, the notebook must be started with this command:

sage -ipython notebook --pylab=inline

Then, a matplotlib plot can be drawn inline (example taken from this notebook):

import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 3*np.pi, 500)
plt.plot(x, np.sin(x**2))
plt.title('A simple chirp');

Or with:

%load http://matplotlib.org/mpl_examples/showcase/integral_demo.py

According to the previous cited notebook, it seems, that the inline mode can also be decided from the notebook using a magic command, but with my version of ipython (0.13.2), I get an error:

In [11]: %matplotlib inline
ERROR: Line magic function `%matplotlib` not found.

Use latex in a markdown cell

Change an input cell into a markdown cell and then you may use latex:

Test $\alpha+\beta+\gamma$

Output in latex

The output can be shown with latex and mathjax using the ipython display function:

from IPython.display import display, Math
def my_show(obj): return display(Math(latex(obj)))
y = 1 / (x^2+1)
my_show(y)

ipynb format

Create a new notebook with only one cell. Name it range_10 and save:

In [1]: range(10)
Out[1]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

The file range_10.ipynb is saved in the directory. You can also download it from File > Download as > IPython (.ipynb). Here is the content of the file range_10.ipynb:

{
 "metadata": {
  "name": "range_10"
 },
 "nbformat": 3,
 "nbformat_minor": 0,
 "worksheets": [
  {
   "cells": [
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "range(10)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "pyout",
       "prompt_number": 1,
       "text": [
        "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]"
       ]
      }
     ],
     "prompt_number": 1
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [],
     "language": "python",
     "metadata": {},
     "outputs": []
    }
   ],
   "metadata": {}
  }
 ]
}

ipynb is just json

A ipynb file is written in json format. Below, we use json to open the file `range_10.ipynb as a Python dictionnary.

sage: s = open('range_10.ipynb','r').read()
sage: import json
sage: D = json.loads(s)
sage: type(D)
dict
sage: D.keys()
[u'nbformat', u'nbformat_minor', u'worksheets', u'metadata']
sage: D
{u'metadata': {u'name': u'range_10'},
 u'nbformat': 3,
 u'nbformat_minor': 0,
 u'worksheets': [{u'cells': [{u'cell_type': u'code',
     u'collapsed': False,
     u'input': [u'range(10)'],
     u'language': u'python',
     u'metadata': {},
     u'outputs': [{u'output_type': u'pyout',
       u'prompt_number': 1,
       u'text': [u'[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]']}],
     u'prompt_number': 1},
    {u'cell_type': u'code',
     u'collapsed': False,
     u'input': [],
     u'language': u'python',
     u'metadata': {},
     u'outputs': []}],
   u'metadata': {}}]}

Load vaucanson.ipynb

Download the file vaucanson.ipynb from the last meeting of Paris Sage Users. You can view the complete demo including pictures of automaton even if you are not able to install vaucanson on your machine.

IPython notebook from a Python file

In a Python file, separate your code with the following line to create cells:

# <codecell>

For example, create the following Python file. Then, import it in the notebook. It will get translated to ipynb format automatically.

# -*- coding: utf-8 -*-
# <nbformat>3.0</nbformat>

# <codecell>

%load_ext sage.misc.sage_extension

# <codecell>

matrix(4, range(16))

# <codecell>

factor(2^40-1)

More conversion

Since release 1.0 of IPython, many conversion from ipynb to other format are possible (html, latex, slides, markdown, rst, python). Unfortunately, the version of IPython in Sage is still 0.13.2 as of today but the version 1.2.1 will be in sage-6.2.

Read and Post Comments

Ranking algorithms for competitive Ultimate

02 mars 2014 | Mise à jour: 26 mars 2014 | Catégories: ultimate | View Comments

Ranking teams is a great question. There can be a lot of mathematics and complicated computations involved. The ranking algorithm used by USAUltimate is not an exception. Taking as huge input the results of thousands of games played during a season, it outputs a ranking of teams. Recently, USA Ultimate annonced modifications to their ranking algorithm for the 2014 Season which I see as local changes keeping the same global approach. I quickly shared my thoughts on ultiworld recently. Then, I was asked to explain my ideas more deeply which I do in this text.

I believe the actual USA ranking algorithm is not too bad at fullfilling its objective: decide bids per region. Once the number of bids are set, the season results and the USA ranking has no impact anymore on deciding which team is the best. The best team will be the team who wons the nationals after qualifying through sectionals and regionals. But depending on the objective to achieve, the choice of ranking algorithm can be more important as its role is more determinant:

Role A. Rank the teams at all time of the year (like tennis ATP and WTA rankings)
Role B. Choose the Season Champion (like in Formula 1)
Role C. Select teams for a Championship (Quebec Ultimate 4 on 4 Circuit)
Role D. Decide bids per region (USAU)

Depending on the role of the ranking, the chosen ranking algorithm might be more or less suited. In this text, I explain my point of view on ranking algorithms for competitive Ultimate. I consider the case where the season is made of many tournaments and where the ranking has a role in deciding the national champion, that is like role B or C above. It may also apply for deciding bids per region (role D). But whatever its role, to me, a good ranking algorithm for competitive Ultimate must:

  1. Produce a ranking
  2. Consider the structure of tournaments (not every game worth the same)
  3. Be predictable
  4. Reward what is the most valuable (winning when it counts)

Below, I explain each of the above condition. I also propose a ranking algorithm for competitive ultimate based on results of tournaments. This text ends with an example on 2013 Club Open season to see how my proposed ranking would work for bid allocation in USA Ultimate. In general, I hope my text can stimulate a reflexion about what algorithm is best suited for competitive ultimate.

1. Produce a ranking

Of course, the ranking algorithm should produce a overall ranking of teams. Everybody agrees with this.

2. Consider the structure of tournaments (not every game worth the same)

To me the principal weekness of the actual USAU Ranking algorithm is that it doesn't see the structure of tournaments. The algorithm can be great for a league or whatever sport where games are worth the same but not for competitive Ultimate where games are played during tournaments. For the algorithm used by USAU, a tournament is just a bunch of random games. The algorithm does not see the difference between the saturday morning game and the sunday afternoon final (apart the one day difference which won't affect much).

The following paradox illustrates how the results of games in a tournament is completely independent of the outcome of the tournament itself. Four teams (A, B, C and D) play in a tournament. The format is a round robin followed by semi-finals, final and 3rd place game: 10 games all in all. The results are (in no particular order):

  • game #0: (15) A - C (7)
  • game #1: (15) B - D (6)
  • game #2: (15) A - D (8)
  • game #3: (15) B - C (10)
  • game #4: (15) A - B (12)
  • game #5: (15) C - D (9)
  • game #6: (12) A - D (15)
  • game #7: (7) B - C (15)
  • game #8: (8) A - B (15)
  • game #9: (9) C - D (15)

From this information only, can you decide which team won the tournament? The answer is NO. If games #0, #1, #2, #3, #4, #5 are pool play games, then the seeding out of pool play is A(3-0), B(2-1), C(1-2), D(0-3). Games #6 and #7 are the semis, game #8 is the 3rd place game and game #9 is the final. Team D win the tournament. Final standing is D-C-B-A.

If games #0,#1,#6,#7,#8,#9 are played in the pool, then the pool outcome becomes B(2-1), D(2-1), A(1-2), C(1-2). Team B and C play in semi final game #3 while Team A and D play their semi in game #2. The final game is game #4. The 3rd place is game #5. Team A win the tournament. Final standing is A-B-C-D.

After the tournament, we can discuss about what should be the proper ranking of those four teams based on the results of the 10 games they played. But, there are good chances that we are just wrong as the above paradox shows. In a tournament all games are not equal. It is all about the context. A good ranking algorithm should consider the format of tournaments and accept that some games worth more than others. How to do this? The actual algorithm is already very complicated. How to put more value to particular games? Maybe possible, but may be complicated as well.

I believe there is an easy solution to this. The algorithm must take a step back. Looking at games is like looking at the microscope: we don't see the global picture. Which team get the trophy at the end of the tournament in front of everyone without contestation from anybody? The trophy is given at the end of the tournament to the team who reached the final and won it. This is the best team. The best team is not the one who got the best overall game results. This is why I believe ranking algorithm in competitive Ultimate should be based on the outcome of tournaments and not on the outcome of games.

3. Be predictable

In a text by Lou Burruss about ranking published during 2012, the author speak about the recursive aspect of the actual ranking algorithm making it unpredictable:

"In conversation with Sholom, he made clear there is still some mystery to this very complex process. Even after 20 years of official rankings and simulations, it is impossible to predict exactly how things will behave."

On the contrary, if the ranking system is predictable, then teams know in advance that winning a certain game will give them exactly 200 pts, let's say, and this is the amount they need to become #1 in the country, then it will be one more motivation to win the game.

This is when ranking is not anymore only about ranking. Ranking can also be a source of motivation for team to improve. But ranking will do this at its best when it is predictable. This is why I believe USA Ultimate should look into this if they want to enhance the level of competition as they claim:

"In the ongoing effort to enhance the level of competition and achieve organizational goals, USA Ultimate continues to look to improve the ranking algorithm [...]."

4. Reward what is the most valuable (winning when it counts)

In my opinion, ranking should reward what is the most valuable by the community. This should correspond to what is the most difficult to achieve. Again, this point is a necessary condition for the ranking to be used as a source of motivation for team to improve.

If a ranking values something else, then people won't value the ranking and team won't try to improve themselve to get a better ranking. For example, think about a ranking that would only consider the best differential. We all know that the best differential in a tournament can be reached by the team G who finishes 7th out of 16 teams. The team G will think they are the best in the country and they won't try to win a tournament : too much effort and useless. The team A who put all the effort to get to the final and win it will wonder if they are optimizing the good thing. They will be less motivated to win another tournament.

Naturally, the community values teams getting in the final and winning it. This is what I call the natural attractor. The ranking algorithm chosen also defines a ranking attractor. As we have seen in the last paragraph, the two attractors can be different. But, I strongly believe the best choice of ranking algorithm is when syzygy (picture here) happens, that is when the ranking attractor equals the natural attractor.

"The syzygy produces the more powerful spring tide due to the enhanced gravitational effect of the Sun added to the Moon's gravitational pull."

In this situation, the natural attractors and the ranking attractor double their forces and team then really wants to win tournaments because they get the natural recognition from the community plus they get an high rank in the official ranking. This is the best way to optimize the improvement of teams.

It is known that the actual ranking algorithm encourages teams to play a bit differently than they would if it was just about winning tournaments. In Maximizing Your Team’s Ranking: Strength of Schedule or Margin of Victory? (Dec. 2013), Scott Schriner confirms this. He wrote:

"To increase your game performance score, you will want to win more games — but also win each game by as large of a margin as possible. This might include playing your starters longer than necessary, or loading up your D Line with an O Line handler even if you don’t need that break. Of course that decision carries with it other tradeoffs: you may increase injury risk or stall the development of younger players on your team."

I believe it is a mark of respect for tournament structure that the ranking attractor correspond to the the natural attractor. Moreover, this system is trusting teams and let them all the liberty they need to win a tournament. Good team will peak at the good moment and will try new strategies or give more playing time to rookies at other moment. Teams that try to optimize every point at every game do not improve and do not push the game further.

A ranking system for competitive Ultimate

There is a important observation to be made when creating overall ranking in competitive ultimate. The output of a tournament is a ranking of teams. Why not build on this to create overall ranking? A natural system is to give points to teams according to the final ranking. In such a system, teams needs to win tournaments to get the maximal amount of points.

I think a system like tennis ATP and WTA ranking is a good inspiration for competitive ultimate. Tennis players go to tournaments they choose. Some tournaments worth more (Grand Slam) some less (ATP World Tour Masters 1000, ATP 500, ATP 250). The number of points of a tennis player is the sum of its best 18 results.

The problem with tennis compared to Ultimate is that there are no placement games. The outcome of a tennis tournament is that you either win the final, made the final, made the semis, made the quarters, made the round of 16, and so on. In Ultimate, there are placement games and finishing 9th is better than finishing 16th. Also, 18 tournaments is a lot. We need to consider less tournaments.

Since 2007, in Quebec we adapted the ATP ranking to Ultimate tournaments with placement games. In 2011, the series became so popular. We needed to adjust the system (88 teams took part in the Mars Attaque 2011 but only 32 got points). Since 2011, 1000 pts is given to the winner of a Grand Slam event, 938 pts is given to the finalist, 884 pts is given to the 3rd place, 835 pts to the 4th place and this goes until the team finishing in 50th place who obtain 1pt. The number of points is obtained from the integral of a logarithmic function. I am going to write another text on my blog where I explain the details and the math behind the system.

Example : ranking of USA Open Club team during 2013 regular season

As an example I am considering the 2013 USA Club Open seaon. I selected the following list of 14 tournaments. Those tournaments are divided into 2 groups (7 tournaments of 500 points, 7 elite tournaments of 1000 points).

Tournaments considered Points for champion
Cal State 500
Cazenovia 500
No Surf 500
Old Dominion Q. 500
Mot. Throwdown 500
San Diego Slammer 500
Col. Cup noTCT 500
Chesapeake Invite 1000
Club Terminus 1000
Colorado Cup 1000
Heavyweights 1000
Pro Flight Finale 1000
US Open 1000
West Coast Cup 1000

Below is the resulting ranking made from the 2013 USA Club Open season with the parameters chosen above. Comments are welcome.

  Pos   Pts    Team name                     Rg   Best                         2nd best                     3rd best
+-----+------+-----------------------------+----+----------------------------+----------------------------+----------------------------+
  1     2746    Doublewide                   SC   1 (1000) Colorado Cup        2 (911) Pro Flight Finale    3 (835) US Open
  2     2670    PoNY                         NE   1 (1000) Club Terminus       3 (835) Colorado Cup         3 (835) Chesapeake Invite
  3     2619    Revolver                     SW   1 (1000) US Open             2 (911) West Coast Cup       5 (708) Pro Flight Finale
  4     2603    Sockeye                      NW   1 (1000) West Coast Cup      3 (835) Club Terminus        4 (768) Pro Flight Finale
  5     2603    Machine                      GL   1 (1000) Heavyweights        3 (835) Pro Flight Finale    4 (768) Club Terminus
  6     2387    Johnny Bravo                 SC   2 (911) Colorado Cup         4 (768) West Coast Cup       5 (708) Club Terminus
  7     2377    Ironside                     NE   2 (911) US Open              2 (911) Chesapeake Invite    8 (555) Pro Flight Finale
  8     2361    GOAT                         NE   1 (1000) Pro Flight Finale   5 (708) Chesapeake Invite    6 (653) Club Terminus
  9     2166    Chain Lightning              SE   2 (911) Club Terminus        6 (653) Chesapeake Invite    7 (602) Pro Flight Finale
  10    2140    Sub Zero                     NC   1 (1000) Chesapeake Invite   5 (708) Heavyweights         11 (432) Club Terminus
  11    2043    Ring of Fire                 SE   3 (835) US Open              6 (653) Pro Flight Finale    8 (555) Chesapeake Invite
  12    1937    High Five                    GL   3 (835) Heavyweights         7 (602) Chesapeake Invite    1 (500) No Surf
  13    1927    Rhino                        NW   5 (708) Colorado Cup         5 (708) West Coast Cup       9 (511) Club Terminus
  14    1910    Madison Club                 NC   2 (911) Heavyweights         7 (602) Colorado Cup         12 (397) Club Terminus
  15    1881    Truck Stop                   MA   4 (768) Colorado Cup         7 (602) Club Terminus        9 (511) Chesapeake Invite
  16    1685    Inception                    SC   4 (768) Heavyweights         1 (500) Col. Cup noTCT       3 (417) San Diego Slammer
  17    1492    Streetgang                   SW   6 (653) Heavyweights         2 (455) San Diego Slammer    4 (384) Cal State
  18    1455    Chicago Club                 GL   5 (708) US Open              4 (384) Mot. Throwdown       13 (363) Heavyweights
  19    1340    LA Renegade                  SW   7 (602) Heavyweights         4 (384) Cal State            5 (354) San Diego Slammer
  20    1302    Condors                      SW   1 (500) Cal State            1 (500) San Diego Slammer    15 (302) Colorado Cup
  21    1284    Madcow                       GL   2 (455) Mot. Throwdown       11 (432) Colorado Cup        12 (397) Chesapeake Invite
  22    1252    Cash Crop                    SE   10 (471) Chesapeake Invite   12 (397) Colorado Cup        4 (384) Old Dominion Q.
  23    1087    Dire Wolf                    MA   2 (455) No Surf              4 (384) Cazenovia            17 (248) Heavyweights
  24    1085    Prairie Fire                 NC   6 (653) Colorado Cup         11 (432) Heavyweights        
  25    1011    Florida United               SE   9 (511) Colorado Cup         1 (500) Old Dominion Q.      
  26    1010    Boost Mobile                 SW   8 (555) Club Terminus        2 (455) Cal State            
  27    1007    Medicine Men                 MA   12 (397) Heavyweights        5 (354) Old Dominion Q.      9 (256) Cazenovia
  28    986     Mephisto                     NE   7 (602) US Open              4 (384) Cazenovia            
  29    888     Voodoo                       NW   10 (471) Colorado Cup        3 (417) San Diego Slammer    
  30    865     Sprawl                       SW   9 (511) Heavyweights         5 (354) San Diego Slammer    
  31    835     Buzz Bullets                      3 (835) West Coast Cup
  32    833     Garuda                       NE   8 (555) Heavyweights         8 (278) Cazenovia            
  33    768     Clapham                           4 (768) Chesapeake Invite
  34    686     Oaks                         SW   5 (354) Cal State            14 (332) Heavyweights        
  35    656     Castle                       NC   5 (354) Mot. Throwdown       15 (302) Heavyweights        
  36    653     Euforia                           6 (653) US Open
  37    632     Sheet Metal                       5 (354) No Surf              8 (278) Cazenovia
  38    594     Beachfront Property          GL   3 (417) Mot. Throwdown       20 (177) Heavyweights        
  39    575     Space City Ignite            SC   7 (301) San Diego Slammer    16 (274) Heavyweights        
  40    562     Chico                             6 (326) Cal State            10 (236) San Diego Slammer
  41    555     Plex                         SC   8 (555) Colorado Cup         
  42    555     Ragnarok                          8 (555) US Open
  43    534     Gridlock                     SW   8 (278) Cal State            9 (256) San Diego Slammer    
  44    500     Ulysse                            1 (500) Cazenovia
  45    471     Brickyard                    GL   10 (471) Heavyweights        
  46    471     Furious George               NW   10 (471) Club Terminus       
  47    467     Midnight Meat Train          GL   7 (301) No Surf              14 (166) Mot. Throwdown
  48    458     Lake Effect                  GL   7 (301) Mot. Throwdown       21 (157) Heavyweights        
  49    455     Tanasi                       SE   2 (455) Old Dominion Q.      
  50    455     Powderhogs                   NW   2 (455) Col. Cup noTCT       
  51    453     Madador                           10 (236) No Surf             11 (217) Mot. Throwdown
  52    435     BD Air Show                  SW   10 (236) Cal State           12 (199) San Diego Slammer
  53    432     Oakland                      MA   11 (432) Chesapeake Invite   
  54    417     Boneyard                          3 (417) Old Dominion Q.
  55    417     Choice City Hops                  3 (417) Col. Cup noTCT
  56    417     Madcow Y                          3 (417) No Surf
  57    405     Inception-Red                     4 (384) Col. Cup noTCT       30 (21) Heavyweights
  58    399     CAKti                             11 (217) No Surf             13 (182) Mot. Throwdown
  59    384     Madcow X                          4 (384) No Surf
  60    363     Phoenix                      NE   13 (363) Colorado Cup        
  61    354     Sweet Roll                   SC   5 (354) Col. Cup noTCT       
  62    354     The Nights Watch             NE   5 (354) Cazenovia            
  63    326     Grand Trunk                       6 (326) No Surf
  64    326     Vanier Wildcats                   6 (326) Cazenovia
  65    326     Floodwall                         6 (326) Old Dominion Q.
  66    326     Enigma                       GL   6 (326) Mot. Throwdown       
  67    301     The Ghosts                        7 (301) San Diego Slammer
  68    301     Swell                             7 (301) Old Dominion Q.
  69    278     Old Growth                        8 (278) Cal State
  70    278     Burnside                          8 (278) Old Dominion Q.
  71    278     Jurassic Shark               GL   8 (278) Mot. Throwdown       
  72    278     Maverick                          8 (278) No Surf
  73    274     Lancaster                    MA   16 (274) Colorado Cup        
  74    256     Journeymen                        9 (256) Cal State
  75    256     Grantham U.                       9 (256) No Surf
  76    256     VAlhalla                          9 (256) Old Dominion Q.
  77    256     Impulse                           9 (256) Mot. Throwdown
  78    237     Hustle                            10 (236) Mot. Throwdown      32 (1) Heavyweights
  79    236     Triforce                          10 (236) Old Dominion Q.
  80    236     Jester                            10 (236) Cazenovia
  81    231     Spoiler                           12 (199) Mot. Throwdown      29 (32) Heavyweights
  82    223     Haymaker                     GL   18 (223) Heavyweights        
  83    217     Brawl                        SW   11 (217) San Diego Slammer   
  84    199     ROY                               12 (199) No Surf
  85    199     Centretown Gunners                12 (199) Cazenovia
  86    199     Warriors of Rad                   12 (199) Cazenovia
  87    199     Gnarwhal                     NC   19 (199) Heavyweights        
  88    194     INfamous                          16 (137) Mot. Throwdown      27 (57) Heavyweights
  89    182     Stonefish                    NE   13 (182) Cazenovia           
  90    166     Youngbloods                  NE   14 (166) Cazenovia           
  91    151     Flying Pig                        15 (151) Mot. Throwdown
  92    137     Freaks Uv Nature             SE   22 (137) Heavyweights        
  93    137     Firebird                          16 (137) Cazenovia
  94    137     Throw'n Together                  16 (137) Cazenovia
  95    134     Illusion                     NC   17 (124) Mot. Throwdown      31 (10) Heavyweights         
  96    119     Mufasa                            23 (119) Heavyweights
  97    112     MicroMachines                     18 (112) Mot. Throwdown
  98    102     Mad Men                           24 (102) Heavyweights
  99    100     Rust Belt War Bonds               19 (100) Mot. Throwdown
  100   89      Salvage 3                         20 (89) Mot. Throwdown
  101   86      H1N1                         NC   25 (86) Heavyweights         
  102   71      yogosbo                           26 (71) Heavyweights
  103   44      City Park Ultimate           NC   28 (44) Heavyweights         

Suppose such a ranking was used for bid allocation in USA. The next table compares the number of bid that were allocated per region for the USAU Championshipwith with the number of teams per region in the top 16 of the above ranking.

Region Total bid allocated Teams in top 16
GL 2 2
MA 1 1
NC 1 2 (+1)
NE 3 3
NW 2 2
SC 2 3 (+1)
SE 3 2 (-1)
SW 2 1 (-1)

Of course, the relative number of points for tournaments is important. It must be well studied and tested to reach the good equilibrium. For example, maybe 400 or 600 points is best suited for second level tournaments instead of 500.

Read and Post Comments

« Previous Page -- Next Page »