Ticket #12090: trac_12090-rebase.patch

File trac_12090-rebase.patch, 20.1 KB (added by hthomas, 8 years ago)
  • doc/en/reference/geometry/index.rst

    # HG changeset patch
    # User Nathann Cohen <nathann.cohen@gmail.com>
    # Date 1322923750 -3600
    # Node ID f398269b85eab62ff1c22230a6def24e5ca76dbb
    # Parent  2f9f180a6beed1bc12ac1dc9a4eb2217a5d01448
    trac #12090 - Arrangements of pseudolines
    
    diff --git a/doc/en/reference/geometry/index.rst b/doc/en/reference/geometry/index.rst
    a b  
    66polytopes and polyhedra (with rational or numerical coordinates).
    77
    88.. toctree::
    9    :maxdepth: 2
     9   :maxdepth: 1
    1010
    11    sage/geometry/toric_lattice   
     11   sage/geometry/toric_lattice
    1212   sage/geometry/cone
    1313   sage/geometry/fan
    1414   sage/geometry/fan_morphism
    1515   sage/geometry/point_collection
    1616   sage/geometry/toric_plotter
    17    
     17
    1818   sage/rings/polynomial/groebner_fan
    19    
     19
    2020   sage/geometry/lattice_polytope
    2121
    2222   sage/geometry/polyhedron/constructor
     
    3131   sage/geometry/polyhedron/backend_cdd
    3232   sage/geometry/polyhedron/backend_ppl
    3333   sage/geometry/polyhedron/cdd_file_format
     34   sage/geometry/pseudolines
    3435
    3536   sage/geometry/triangulation/point_configuration
    3637   sage/geometry/triangulation/base
  • sage/geometry/all.py

    diff --git a/sage/geometry/all.py b/sage/geometry/all.py
    a b  
    1515
    1616from toric_lattice import ToricLattice
    1717
     18import sage.geometry.pseudolines
     19
    1820
    1921import toric_plotter
  • new file sage/geometry/pseudolines.py

    diff --git a/sage/geometry/pseudolines.py b/sage/geometry/pseudolines.py
    new file mode 100644
    - +  
     1r"""
     2Pseudolines
     3
     4This module gathers everything that has to do with pseudolines, and for a start
     5a :class:`PseudolinesArrangement` class that can be used to describe an
     6arrangement of pseudolines in several different ways, and to translate one
     7description into another, as well as to display *Wiring diagrams* via the
     8:meth:`show <sage.geometry.pseudolines.PseudolinesArrangement.show>` method.
     9
     10In the following, we try to stick to the terminology given in [Felsner]_, which
     11can be checked in case of doubt. And please fix this module's documentation
     12afterwards :-)
     13
     14**Definition**
     15
     16A *pseudoline* can not be defined by itself, though it can be thought of as a
     17`x`-monotone curve in the plane. A *set* of pseudolines, however, represents a
     18set of such curves that pairwise intersect exactly once (and hence mimic the
     19behaviour of straight lines in general position). We also assume that those
     20pseudolines are in general position, that is that no three of them cross on the
     21same point.
     22
     23The present class is made to deal with a combinatorial encoding of a pseudolines
     24arrangement, that is the ordering in which a pseudoline `l_i` of an arrangement
     25`l_0, ..., l_{n-1}` crosses the `n-1` other lines.
     26
     27.. WARNING::
     28 
     29    It is assumed through all the methods that the given lines are numbered
     30    consistently : it is not possible that the first transposition be ``(0,2)``
     31    (or equivalently that the first line `l_0` crosses is `l_2` and conversely),
     32    for it would mean that one of them cuts `l_1`. It is actually assumed that
     33    the pseudolines are numbered according to their `y`-coordinate on the
     34    vertical line `x=-\infty`.
     35
     36Encodings
     37----------
     38
     39**Permutations**
     40
     41An arrangement of pseudolines can be described by a sequence of `n` lists of
     42length `n-1`, where the `i` list is a permutation of `\{1, ..., n\} \backslash
     43i` representing the ordering in which the `i` th pseudoline meets the other
     44ones.
     45
     46::
     47
     48    sage: from sage.geometry.pseudolines import PseudolinesArrangement
     49    sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]]
     50    sage: p = PseudolinesArrangement(permutations)
     51    sage: p
     52    Arrangement of pseudolines of size 4
     53    sage: p.show()
     54
     55**Sequence of transpositions**
     56
     57An arrangement of pseudolines can also be described as a sequence of `\binom n
     582` transpositions (permutations of two elements). In this sequence, the
     59transposition `(2,3)` appears before `(8, 2)` iif `l_2` crosses `l_3` before it
     60crosses `l_8`. This encoding is easy to obtain by reading the wiring diagram
     61from left to right (see the :meth:`show
     62<sage.geometry.pseudolines.PseudolinesArrangement.show>` method).
     63
     64::
     65
     66    sage: from sage.geometry.pseudolines import PseudolinesArrangement
     67    sage: transpositions = [(3, 2), (3, 1), (0, 3), (2, 1), (0, 2), (0, 1)]
     68    sage: p = PseudolinesArrangement(transpositions)
     69    sage: p
     70    Arrangement of pseudolines of size 4
     71    sage: p.show()
     72
     73
     74Note that this ordering is not necessarily unique.
     75
     76**Felsner's Matrix**
     77
     78Felser gave an encoding of an arrangement of pseudolines that takes `n^2` bits
     79instead of the `n^2log(n)` bits required by the two previous encodings.
     80
     81Instead of storing the permutation ``[3, 2, 1]`` to remember that line `l_0`
     82crosses `l_3` then `l_2` then `l_1`, it is sufficient to remember the positions
     83for which each line `l_i` meets a line `l_j` with `j < i`. As `l_0` -- the first
     84of the lines -- can only meet pseudolines with higher index, we can store ``[0,
     850, 0]`` instead of ``[3, 2, 1]`` stored previously. For `l_1`'s permutation
     86``[3, 2, 0]`` we only need to remember that `l_1` first crosses 2 pseudolines of
     87higher index, and then a pseudoline with smaller index, which yields the bit
     88vector ``[0, 0, 1]``. Hence we can transform the list of permutations above into
     89a list of `n` bit vectors of length `n-1`, that is
     90
     91.. MATH::
     92    \begin{array}{ccc}
     93      3 & 2 & 1\\
     94      3 & 2 & 0\\
     95      3 & 1 & 0\\
     96      2 & 1 & 0\\
     97    \end{array}
     98    \Rightarrow
     99    \begin{array}{ccc}
     100      0 & 0 & 0\\
     101      0 & 0 & 1\\
     102      0 & 1 & 1\\
     103      1 & 1 & 1\\
     104    \end{array}
     105
     106In order to go back from Felsner's matrix to an encoding by a sequence of
     107transpositions, it is sufficient to look for occurrences of
     108`\begin{array}{c}0\\1\end{array}` in the first column of the matrix, as it
     109corresponds in the wiring diagram to a line going up while the line immediately
     110above it does down -- those two lines cross. Each time such a pattern is found
     111it yields a new transposition, and the matrix can be updated so that this
     112pattern disappears. A more detailed description of this algorithm is given in
     113[Felsner]_.
     114
     115::
     116
     117    sage: from sage.geometry.pseudolines import PseudolinesArrangement
     118    sage: felsner_matrix = [[0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 1, 1]]
     119    sage: p = PseudolinesArrangement(felsner_matrix)
     120    sage: p
     121    Arrangement of pseudolines of size 4
     122
     123Example
     124-------
     125
     126Let us define in the plane several lines `l_i` of equation `y = a x+b` by
     127picking a coefficient `a` and `b` for each of them. We make sure that no two of
     128them are parallel by making sure all of the `a` chosen are different, and we
     129avoid a common crossing of three lines by adding a random noise to `b`::
     130
     131    sage: n = 20
     132    sage: l = zip(Subsets(20*n,n).random_element(), [randint(0,20*n)+random() for i in range(n)])
     133    sage: print l[:5]                            # not tested
     134    [(96, 278.0130613051349), (74, 332.92512282478714), (13, 155.65820951249867), (209, 34.753946221755307), (147, 193.51376457741441)]
     135    sage: l.sort()
     136    sage: n = len(l)
     137
     138We can now compute for each `i` the order in which line `i` meet the other lines::
     139
     140    sage: permutations = [[0..i-1]+[i+1..n-1] for i in range(n)]
     141    sage: a = lambda x : l[x][0]
     142    sage: b = lambda x : l[x][1]
     143    sage: for i, perm in enumerate(permutations):
     144    ...       perm.sort(key = lambda j : (b(j)-b(i))/(a(i)-a(j)))
     145
     146And finally build the line arrangement::
     147
     148    sage: from sage.geometry.pseudolines import PseudolinesArrangement
     149    sage: p = PseudolinesArrangement(permutations)
     150    sage: print p
     151    Arrangement of pseudolines of size 20
     152    sage: p.show(figsize=[20,8])
     153
     154
     155References
     156^^^^^^^^^^
     157
     158.. [Felsner] On the Number of Arrangements of Pseudolines,
     159  Stefan Felsner,
     160  http://page.math.tu-berlin.de/~felsner/Paper/numarr.pdf
     161
     162Author
     163^^^^^^
     164Nathann Cohen
     165
     166Methods
     167-------
     168"""
     169##############################################################################
     170#       Copyright (C) 2011 Nathann Cohen <nathann.cohen@gmail.com>
     171#  Distributed under the terms of the GNU General Public License (GPL)
     172#  The full text of the GPL is available at:
     173#                  http://www.gnu.org/licenses/
     174##############################################################################
     175
     176from copy import deepcopy
     177
     178class PseudolinesArrangement:
     179
     180    def __init__(self, seq, encoding = "auto"):
     181        r"""
     182        Creates an arrangement of pseudolines.
     183
     184        INPUT:
     185
     186        - ``seq`` (a sequence describing the line arrangement). It can be :
     187
     188            - A list of `n` permutations of size `n-1`.
     189            - A list of `\binom n 2` transpositions
     190            - A Felsner matrix, given as a sequence of `n` binary vectors of
     191              length `n-1`.
     192
     193        - ``encoding`` (information on how the data should be interpreted), and
     194          can assume any value among 'transpositions', 'permutations', 'Felsner'
     195          or 'auto'. In the latter case, the type will be guessed (default
     196          behaviour).
     197
     198        .. NOTE::
     199
     200           * The pseudolines are assumed to be integers `0..(n-1)`.
     201
     202           * For more information on the different encodings, see the
     203             :mod:`pseudolimes module <sage.geometry.pseudolines>`'s
     204             documentation.
     205   
     206        TESTS:
     207
     208        From permutations::
     209   
     210            sage: from sage.geometry.pseudolines import PseudolinesArrangement
     211            sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]]
     212            sage: PseudolinesArrangement(permutations)
     213            Arrangement of pseudolines of size 4
     214
     215        From transpositions ::
     216
     217            sage: from sage.geometry.pseudolines import PseudolinesArrangement
     218            sage: transpositions = [(3, 2), (3, 1), (0, 3), (2, 1), (0, 2), (0, 1)]
     219            sage: PseudolinesArrangement(transpositions)
     220            Arrangement of pseudolines of size 4
     221
     222        From a Felsner matrix::
     223
     224            sage: from sage.geometry.pseudolines import PseudolinesArrangement
     225            sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]]
     226            sage: p = PseudolinesArrangement(permutations)
     227            sage: matrix = p.felsner_matrix()
     228            sage: PseudolinesArrangement(matrix) == p
     229            True
     230
     231        TESTS:
     232
     233        Wrong input::
     234
     235            sage: PseudolinesArrangement([[5, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]])
     236            Traceback (most recent call last):
     237            ...
     238            ValueError: Are the lines really numbered from 0 to n-1 ?
     239            sage: PseudolinesArrangement([(3, 2), (3, 1), (0, 3), (2, 1), (0, 2)])
     240            Traceback (most recent call last):
     241            ...
     242            ValueError: A line is numbered with 3but the number of transpositions ...
     243        """
     244       
     245        # Sequence of transpositions
     246        if (encoding == "transpositions" or
     247            (encoding == "auto" and len(seq[0]) == 2 and len(seq) > 3)):
     248           
     249            self._n = max(map(max, seq)) + 1
     250            if (self._n * (self._n-1))/2 != len(seq):
     251                raise ValueError(
     252                    "A line is numbered with "+str(self._n-1)+"but the number "+
     253                    "of transpositions is different from binomial("+
     254                    str(self._n-1)+",2). Are the lines numbered from 0 to n-1 ?"+
     255                    "Are they really non-parallel ? Please check the documentation.")
     256
     257            self._permutations = [[] for i in range(self._n)]
     258
     259            for i,j in seq:
     260                self._permutations[i].append(j)
     261                self._permutations[j].append(i)
     262
     263        # Sequence of permutations
     264        elif (encoding == "permutations" or
     265            (encoding == "auto" and (len(seq[0]) == len(seq)-1) and max(seq[0]) > 1)):
     266           
     267            self._n = len(seq)
     268            self._permutations = deepcopy(seq)
     269
     270            if max(map(max, seq)) != self._n -1 :
     271                raise ValueError("Are the lines really numbered from 0 to n-1 ?")
     272
     273        # Felsner encoding
     274        elif (encoding == "Felsner" or
     275            (encoding == "auto" and len(seq[0]) == len(seq) -1)):
     276
     277            seq = deepcopy(seq)
     278            self._n = len(seq)
     279            ordering = range(self._n)
     280
     281            self._permutations = [[] for i in range(self._n)]
     282
     283            crossings = (self._n * (self._n-1))/2
     284
     285            i = 0
     286            while crossings > 0:
     287                if (seq[i] != [] and
     288                    (seq[i][0] == 0 and
     289                     seq[i+1][0] == 1)):
     290
     291                    crossings -= 1
     292                   
     293                    self._permutations[ordering[i]].append(ordering[i+1])
     294                    self._permutations[ordering[i+1]].append(ordering[i])
     295
     296                    ordering[i], ordering[i+1] = ordering[i+1], ordering[i]
     297                    seq[i], seq[i+1] = seq[i+1], seq[i]
     298                   
     299                    seq[i].pop(0)
     300                    seq[i+1].pop(0)
     301
     302                    if i > 0 and seq[i-1] is not []:
     303                        i -= 1
     304                    elif seq[i] is not []:
     305                        i += 1
     306                else:
     307                    i += 1
     308        else:
     309
     310            if encoding != "auto":
     311                raise ValueError("The value of encoding must be one of 'transpositions', 'permutations', 'Felsner' or 'auto'.")
     312
     313            raise ValueError("The encoding you use could not be guessed. Your input string is probably badly formatted, or you have less than 3 lines and we can make no diffence between the encoding. Please specify the encoding you used.")
     314
     315    def transpositions(self):
     316        r"""
     317        Returns the arrangement as `\binom n 2` transpositions.
     318
     319        See the :mod:`pseudolimes module <sage.geometry.pseudolines>`'s
     320        documentation for more information on this encoding.
     321
     322        EXAMPLE::
     323
     324            sage: from sage.geometry.pseudolines import PseudolinesArrangement
     325            sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]]
     326            sage: p1 = PseudolinesArrangement(permutations)
     327            sage: transpositions = [(3, 2), (3, 1), (0, 3), (2, 1), (0, 2), (0, 1)]
     328            sage: p2 = PseudolinesArrangement(transpositions)
     329            sage: p1 == p2
     330            True
     331            sage: p1.transpositions()
     332            [(3, 2), (3, 1), (0, 3), (2, 1), (0, 2), (0, 1)]
     333            sage: p2.transpositions()
     334            [(3, 2), (3, 1), (0, 3), (2, 1), (0, 2), (0, 1)]
     335        """
     336        t = []
     337        perm = deepcopy(self._permutations)
     338
     339        crossings = (self._n * (self._n-1))/2
     340
     341        while crossings > 0:
     342
     343            i = 0
     344
     345            while perm[i] == []:
     346                i += 1
     347
     348            k = 0
     349            while i != perm[perm[i][0]][0]:
     350                i = perm[i][0]
     351                k+= 1
     352
     353                if k > self._n:
     354                    raise ValueError(
     355                        "It looks like the data does not correspond to a"+
     356                        "pseudoline arrangement. We have found k>2 lines"+
     357                        "such that the ith line meets the (i+1)th before"+
     358                        " the (i-1)th (this creates a cyclic dependency)"+
     359                        " which is totally impossible.")
     360
     361            t.append((i, perm[i][0]))
     362            perm[perm[i][0]].pop(0)
     363            perm[i].pop(0)
     364
     365            crossings -= 1
     366
     367        if max(map(len,perm)) != 0:
     368            raise ValueError("There has been an error while computing the transpositions.")
     369           
     370        return t
     371
     372    def permutations(self):
     373        r"""
     374        Returns the arrangements as `n` permutations of size `n-1`.
     375
     376        See the :mod:`pseudolimes module <sage.geometry.pseudolines>`'s
     377        documentation for more information on this encoding.
     378
     379        EXAMPLE::
     380
     381            sage: from sage.geometry.pseudolines import PseudolinesArrangement
     382            sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]]
     383            sage: p = PseudolinesArrangement(permutations)
     384            sage: p.permutations()
     385            [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]]
     386        """
     387        return deepcopy(self._permutations)
     388
     389    def felsner_matrix(self):
     390        r"""
     391        Returns a Felsner matrix describing the arrangement.
     392
     393        See the :mod:`pseudolimes module <sage.geometry.pseudolines>`'s
     394        documentation for more information on this encoding.
     395
     396        EXAMPLE::
     397
     398            sage: from sage.geometry.pseudolines import PseudolinesArrangement
     399            sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]]
     400            sage: p = PseudolinesArrangement(permutations)
     401            sage: p.felsner_matrix()
     402            [[0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 1, 1]]
     403        """
     404
     405        m = [[] for i in range(self._n)]
     406
     407        for i,j in self.transpositions():
     408            if i < j:
     409                i, j = j, i
     410
     411            m[j].append(0)
     412            m[i].append(1)
     413
     414        return m
     415
     416    def show(self, **args):
     417        r"""
     418        Displays the pseudoline arrangement as a wiring diagram.
     419
     420        INPUT:
     421
     422        - ``**args`` -- any arguments to be forwarded to the ``show`` method. In
     423          particular, to tune the dimensions, use the ``figsize`` argument
     424          (example below).
     425
     426        EXAMPLE::
     427
     428            sage: from sage.geometry.pseudolines import PseudolinesArrangement
     429            sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]]
     430            sage: p = PseudolinesArrangement(permutations)
     431            sage: p.show(figsize=[7,5])
     432
     433        TESTS::
     434
     435            sage: from sage.geometry.pseudolines import PseudolinesArrangement
     436            sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 0, 1], [2, 0, 1]]
     437            sage: p = PseudolinesArrangement(permutations)
     438            sage: p.show()
     439            Traceback (most recent call last):
     440            ...
     441            ValueError: There has been a problem while plotting the figure...
     442        """
     443        x = 1
     444        from sage.plot.line import line
     445        from sage.plot.text import text
     446
     447        lines = [[(0,self._n-1-i)] for i in range(self._n)]
     448
     449        for i,j in self.transpositions():
     450            iy = lines[i][-1][1]
     451            jy = lines[j][-1][1]
     452
     453            lines[i].append((x, iy))
     454            lines[j].append((x, jy))
     455
     456            if abs(iy-jy) != 1:
     457                raise ValueError(
     458                    "There has been a problem while plotting the figure. It "+
     459                    "seems that the lines are not correctly ordered. Please "+
     460                    "check the pseudolines modules documentation, there is a "
     461                    +"warning about that. ")
     462
     463            lines[i].append((x+2,jy))
     464            lines[j].append((x+2,iy))
     465
     466            x += 2
     467                           
     468        L = line([(1,1)])
     469       
     470        for i, l in enumerate(lines):
     471            l.append((x+2, l[-1][1]))
     472            L += line(l)
     473           
     474            L += text(str(i), (0, l[0][1]+.3))
     475            L += text(str(i), (x+2, l[-1][1]+.3))
     476
     477        return L.show(axes = False, **args)
     478           
     479
     480    def __repr__(self):
     481        r"""
     482        A short txt description of the pseudolines arrangement.
     483
     484        EXAMPLE::
     485
     486            sage: from sage.geometry.pseudolines import PseudolinesArrangement
     487            sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]]
     488            sage: p = PseudolinesArrangement(permutations)
     489            sage: p
     490            Arrangement of pseudolines of size 4
     491        """
     492        return "Arrangement of pseudolines of size "+str(self._n)
     493
     494    def __eq__(self, other):
     495        r"""
     496        Test of equality.
     497
     498        TEST::
     499
     500            sage: from sage.geometry.pseudolines import PseudolinesArrangement
     501            sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]]
     502            sage: p1 = PseudolinesArrangement(permutations)
     503            sage: transpositions = [(3, 2), (3, 1), (0, 3), (2, 1), (0, 2), (0, 1)]
     504            sage: p2 = PseudolinesArrangement(transpositions)
     505            sage: p1 == p2
     506            True       
     507        """
     508        return (self._n == other._n) and (self._permutations == other._permutations)