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