# HG changeset patch
# User hthomas@unb.ca
# Date 1369014625 10800
# Node ID 756a267710a5463b85054d8c1cf3f5f964b6de2d
# Parent f398269b85eab62ff1c22230a6def24e5ca76dbb
implement pseudo-line arrangements (review patch)
diff --git a/sage/geometry/pseudolines.py b/sage/geometry/pseudolines.py
a
|
b
|
|
2 | 2 | Pseudolines |
3 | 3 | |
4 | 4 | This module gathers everything that has to do with pseudolines, and for a start |
5 | | a :class:`PseudolinesArrangement` class that can be used to describe an |
| 5 | a :class:`PseudolineArrangement` class that can be used to describe an |
6 | 6 | arrangement of pseudolines in several different ways, and to translate one |
7 | 7 | description into another, as well as to display *Wiring diagrams* via the |
8 | | :meth:`show <sage.geometry.pseudolines.PseudolinesArrangement.show>` method. |
| 8 | :meth:`show <sage.geometry.pseudolines.PseudolineArrangement.show>` method. |
9 | 9 | |
10 | 10 | In the following, we try to stick to the terminology given in [Felsner]_, which |
11 | 11 | can be checked in case of doubt. And please fix this module's documentation |
… |
… |
|
17 | 17 | `x`-monotone curve in the plane. A *set* of pseudolines, however, represents a |
18 | 18 | set of such curves that pairwise intersect exactly once (and hence mimic the |
19 | 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 on the |
| 20 | pseudolines are in general position, that is that no three of them cross at the |
21 | 21 | same point. |
22 | 22 | |
23 | 23 | The present class is made to deal with a combinatorial encoding of a pseudolines |
… |
… |
|
25 | 25 | `l_0, ..., l_{n-1}` crosses the `n-1` other lines. |
26 | 26 | |
27 | 27 | .. WARNING:: |
28 | | |
| 28 | |
29 | 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)`` |
| 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)`` |
31 | 32 | (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`. |
| 33 | because one of them would have to cross `l_1` first. |
35 | 34 | |
36 | 35 | Encodings |
37 | 36 | ---------- |
… |
… |
|
39 | 38 | **Permutations** |
40 | 39 | |
41 | 40 | An arrangement of pseudolines can be described by a sequence of `n` lists of |
42 | | length `n-1`, where the `i` list is a permutation of `\{1, ..., n\} \backslash |
| 41 | length `n-1`, where the `i` list is a permutation of `\{0, ..., n-1\} \backslash |
43 | 42 | i` representing the ordering in which the `i` th pseudoline meets the other |
44 | 43 | ones. |
45 | 44 | |
46 | 45 | :: |
47 | 46 | |
48 | | sage: from sage.geometry.pseudolines import PseudolinesArrangement |
| 47 | sage: from sage.geometry.pseudolines import PseudolineArrangement |
49 | 48 | sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]] |
50 | | sage: p = PseudolinesArrangement(permutations) |
51 | | sage: p |
| 49 | sage: p = PseudolineArrangement(permutations) |
| 50 | sage: p |
52 | 51 | Arrangement of pseudolines of size 4 |
53 | 52 | sage: p.show() |
54 | 53 | |
… |
… |
|
59 | 58 | transposition `(2,3)` appears before `(8, 2)` iif `l_2` crosses `l_3` before it |
60 | 59 | crosses `l_8`. This encoding is easy to obtain by reading the wiring diagram |
61 | 60 | from left to right (see the :meth:`show |
62 | | <sage.geometry.pseudolines.PseudolinesArrangement.show>` method). |
| 61 | <sage.geometry.pseudolines.PseudolineArrangement.show>` method). |
63 | 62 | |
64 | 63 | :: |
65 | 64 | |
66 | | sage: from sage.geometry.pseudolines import PseudolinesArrangement |
| 65 | sage: from sage.geometry.pseudolines import PseudolineArrangement |
67 | 66 | sage: transpositions = [(3, 2), (3, 1), (0, 3), (2, 1), (0, 2), (0, 1)] |
68 | | sage: p = PseudolinesArrangement(transpositions) |
69 | | sage: p |
| 67 | sage: p = PseudolineArrangement(transpositions) |
| 68 | sage: p |
70 | 69 | Arrangement of pseudolines of size 4 |
71 | 70 | sage: p.show() |
72 | 71 | |
… |
… |
|
107 | 106 | transpositions, it is sufficient to look for occurrences of |
108 | 107 | `\begin{array}{c}0\\1\end{array}` in the first column of the matrix, as it |
109 | 108 | corresponds in the wiring diagram to a line going up while the line immediately |
110 | | above it does down -- those two lines cross. Each time such a pattern is found |
| 109 | above it goes down -- those two lines cross. Each time such a pattern is found |
111 | 110 | it yields a new transposition, and the matrix can be updated so that this |
112 | 111 | pattern disappears. A more detailed description of this algorithm is given in |
113 | 112 | [Felsner]_. |
114 | 113 | |
115 | 114 | :: |
116 | 115 | |
117 | | sage: from sage.geometry.pseudolines import PseudolinesArrangement |
| 116 | sage: from sage.geometry.pseudolines import PseudolineArrangement |
118 | 117 | sage: felsner_matrix = [[0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 1, 1]] |
119 | | sage: p = PseudolinesArrangement(felsner_matrix) |
| 118 | sage: p = PseudolineArrangement(felsner_matrix) |
120 | 119 | sage: p |
121 | 120 | Arrangement of pseudolines of size 4 |
122 | 121 | |
… |
… |
|
133 | 132 | sage: print l[:5] # not tested |
134 | 133 | [(96, 278.0130613051349), (74, 332.92512282478714), (13, 155.65820951249867), (209, 34.753946221755307), (147, 193.51376457741441)] |
135 | 134 | sage: l.sort() |
136 | | sage: n = len(l) |
137 | 135 | |
138 | | We can now compute for each `i` the order in which line `i` meet the other lines:: |
| 136 | We can now compute for each `i` the order in which line `i` meets the other lines:: |
139 | 137 | |
140 | 138 | sage: permutations = [[0..i-1]+[i+1..n-1] for i in range(n)] |
141 | 139 | sage: a = lambda x : l[x][0] |
142 | 140 | sage: b = lambda x : l[x][1] |
143 | 141 | sage: for i, perm in enumerate(permutations): |
144 | | ... perm.sort(key = lambda j : (b(j)-b(i))/(a(i)-a(j))) |
| 142 | ....: perm.sort(key = lambda j : (b(j)-b(i))/(a(i)-a(j))) |
145 | 143 | |
146 | 144 | And finally build the line arrangement:: |
147 | 145 | |
148 | | sage: from sage.geometry.pseudolines import PseudolinesArrangement |
149 | | sage: p = PseudolinesArrangement(permutations) |
| 146 | sage: from sage.geometry.pseudolines import PseudolineArrangement |
| 147 | sage: p = PseudolineArrangement(permutations) |
150 | 148 | sage: print p |
151 | 149 | Arrangement of pseudolines of size 20 |
152 | 150 | sage: p.show(figsize=[20,8]) |
… |
… |
|
175 | 173 | |
176 | 174 | from copy import deepcopy |
177 | 175 | |
178 | | class PseudolinesArrangement: |
| 176 | class PseudolineArrangement: |
179 | 177 | |
180 | 178 | def __init__(self, seq, encoding = "auto"): |
181 | 179 | r""" |
… |
… |
|
200 | 198 | * The pseudolines are assumed to be integers `0..(n-1)`. |
201 | 199 | |
202 | 200 | * For more information on the different encodings, see the |
203 | | :mod:`pseudolimes module <sage.geometry.pseudolines>`'s |
| 201 | :mod:`pseudolines module <sage.geometry.pseudolines>`'s |
204 | 202 | documentation. |
205 | | |
| 203 | |
206 | 204 | TESTS: |
207 | 205 | |
208 | 206 | From permutations:: |
209 | | |
210 | | sage: from sage.geometry.pseudolines import PseudolinesArrangement |
| 207 | |
| 208 | sage: from sage.geometry.pseudolines import PseudolineArrangement |
211 | 209 | sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]] |
212 | | sage: PseudolinesArrangement(permutations) |
| 210 | sage: PseudolineArrangement(permutations) |
213 | 211 | Arrangement of pseudolines of size 4 |
214 | 212 | |
215 | 213 | From transpositions :: |
216 | 214 | |
217 | | sage: from sage.geometry.pseudolines import PseudolinesArrangement |
| 215 | sage: from sage.geometry.pseudolines import PseudolineArrangement |
218 | 216 | sage: transpositions = [(3, 2), (3, 1), (0, 3), (2, 1), (0, 2), (0, 1)] |
219 | | sage: PseudolinesArrangement(transpositions) |
| 217 | sage: PseudolineArrangement(transpositions) |
220 | 218 | Arrangement of pseudolines of size 4 |
221 | 219 | |
222 | 220 | From a Felsner matrix:: |
223 | 221 | |
224 | | sage: from sage.geometry.pseudolines import PseudolinesArrangement |
| 222 | sage: from sage.geometry.pseudolines import PseudolineArrangement |
225 | 223 | sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]] |
226 | | sage: p = PseudolinesArrangement(permutations) |
| 224 | sage: p = PseudolineArrangement(permutations) |
227 | 225 | sage: matrix = p.felsner_matrix() |
228 | | sage: PseudolinesArrangement(matrix) == p |
| 226 | sage: PseudolineArrangement(matrix) == p |
229 | 227 | True |
230 | 228 | |
231 | 229 | TESTS: |
232 | 230 | |
233 | 231 | Wrong input:: |
234 | 232 | |
235 | | sage: PseudolinesArrangement([[5, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]]) |
| 233 | sage: PseudolineArrangement([[5, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]]) |
236 | 234 | Traceback (most recent call last): |
237 | 235 | ... |
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)]) |
| 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)]) |
240 | 238 | Traceback (most recent call last): |
241 | 239 | ... |
242 | | ValueError: A line is numbered with 3but the number of transpositions ... |
| 240 | ValueError: A line is numbered 3 but the number of transpositions ... |
243 | 241 | """ |
244 | | |
| 242 | |
245 | 243 | # Sequence of transpositions |
246 | | if (encoding == "transpositions" or |
| 244 | if (encoding == "transpositions" or |
247 | 245 | (encoding == "auto" and len(seq[0]) == 2 and len(seq) > 3)): |
248 | | |
249 | | self._n = max(map(max, seq)) + 1 |
| 246 | |
| 247 | self._n = max(map(max, seq)) + 1 |
250 | 248 | if (self._n * (self._n-1))/2 != len(seq): |
251 | 249 | 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.") |
| 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.") |
256 | 254 | |
257 | 255 | self._permutations = [[] for i in range(self._n)] |
258 | 256 | |
… |
… |
|
261 | 259 | self._permutations[j].append(i) |
262 | 260 | |
263 | 261 | # Sequence of permutations |
264 | | elif (encoding == "permutations" or |
| 262 | elif (encoding == "permutations" or |
265 | 263 | (encoding == "auto" and (len(seq[0]) == len(seq)-1) and max(seq[0]) > 1)): |
266 | | |
| 264 | |
267 | 265 | self._n = len(seq) |
268 | 266 | self._permutations = deepcopy(seq) |
269 | 267 | |
270 | 268 | if max(map(max, seq)) != self._n -1 : |
271 | | raise ValueError("Are the lines really numbered from 0 to n-1 ?") |
| 269 | raise ValueError("Are the lines really numbered from 0 to n-1?") |
272 | 270 | |
273 | 271 | # Felsner encoding |
274 | | elif (encoding == "Felsner" or |
| 272 | elif (encoding == "Felsner" or |
275 | 273 | (encoding == "auto" and len(seq[0]) == len(seq) -1)): |
276 | 274 | |
277 | 275 | seq = deepcopy(seq) |
… |
… |
|
289 | 287 | seq[i+1][0] == 1)): |
290 | 288 | |
291 | 289 | crossings -= 1 |
292 | | |
| 290 | |
293 | 291 | self._permutations[ordering[i]].append(ordering[i+1]) |
294 | 292 | self._permutations[ordering[i+1]].append(ordering[i]) |
295 | 293 | |
296 | 294 | ordering[i], ordering[i+1] = ordering[i+1], ordering[i] |
297 | 295 | seq[i], seq[i+1] = seq[i+1], seq[i] |
298 | | |
| 296 | |
299 | 297 | seq[i].pop(0) |
300 | 298 | seq[i+1].pop(0) |
301 | 299 | |
302 | 300 | if i > 0 and seq[i-1] is not []: |
303 | 301 | i -= 1 |
304 | | elif seq[i] is not []: |
305 | | i += 1 |
| 302 | else: |
| 303 | i += 1 |
306 | 304 | else: |
307 | 305 | i += 1 |
308 | 306 | else: |
… |
… |
|
310 | 308 | if encoding != "auto": |
311 | 309 | raise ValueError("The value of encoding must be one of 'transpositions', 'permutations', 'Felsner' or 'auto'.") |
312 | 310 | |
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.") |
| 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.") |
314 | 312 | |
315 | 313 | def transpositions(self): |
316 | 314 | r""" |
317 | 315 | Returns the arrangement as `\binom n 2` transpositions. |
318 | 316 | |
319 | | See the :mod:`pseudolimes module <sage.geometry.pseudolines>`'s |
| 317 | See the :mod:`pseudolines module <sage.geometry.pseudolines>`'s |
320 | 318 | documentation for more information on this encoding. |
321 | 319 | |
322 | 320 | EXAMPLE:: |
323 | 321 | |
324 | | sage: from sage.geometry.pseudolines import PseudolinesArrangement |
| 322 | sage: from sage.geometry.pseudolines import PseudolineArrangement |
325 | 323 | sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]] |
326 | | sage: p1 = PseudolinesArrangement(permutations) |
| 324 | sage: p1 = PseudolineArrangement(permutations) |
327 | 325 | sage: transpositions = [(3, 2), (3, 1), (0, 3), (2, 1), (0, 2), (0, 1)] |
328 | | sage: p2 = PseudolinesArrangement(transpositions) |
| 326 | sage: p2 = PseudolineArrangement(transpositions) |
329 | 327 | sage: p1 == p2 |
330 | 328 | True |
331 | 329 | sage: p1.transpositions() |
… |
… |
|
366 | 364 | |
367 | 365 | if max(map(len,perm)) != 0: |
368 | 366 | raise ValueError("There has been an error while computing the transpositions.") |
369 | | |
| 367 | |
370 | 368 | return t |
371 | 369 | |
372 | 370 | def permutations(self): |
373 | 371 | r""" |
374 | 372 | Returns the arrangements as `n` permutations of size `n-1`. |
375 | 373 | |
376 | | See the :mod:`pseudolimes module <sage.geometry.pseudolines>`'s |
| 374 | See the :mod:`pseudolines module <sage.geometry.pseudolines>`'s |
377 | 375 | documentation for more information on this encoding. |
378 | 376 | |
379 | 377 | EXAMPLE:: |
380 | 378 | |
381 | | sage: from sage.geometry.pseudolines import PseudolinesArrangement |
| 379 | sage: from sage.geometry.pseudolines import PseudolineArrangement |
382 | 380 | sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]] |
383 | | sage: p = PseudolinesArrangement(permutations) |
| 381 | sage: p = PseudolineArrangement(permutations) |
384 | 382 | sage: p.permutations() |
385 | 383 | [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]] |
386 | 384 | """ |
… |
… |
|
390 | 388 | r""" |
391 | 389 | Returns a Felsner matrix describing the arrangement. |
392 | 390 | |
393 | | See the :mod:`pseudolimes module <sage.geometry.pseudolines>`'s |
| 391 | See the :mod:`pseudolines module <sage.geometry.pseudolines>`'s |
394 | 392 | documentation for more information on this encoding. |
395 | 393 | |
396 | 394 | EXAMPLE:: |
397 | 395 | |
398 | | sage: from sage.geometry.pseudolines import PseudolinesArrangement |
| 396 | sage: from sage.geometry.pseudolines import PseudolineArrangement |
399 | 397 | sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]] |
400 | | sage: p = PseudolinesArrangement(permutations) |
| 398 | sage: p = PseudolineArrangement(permutations) |
401 | 399 | sage: p.felsner_matrix() |
402 | 400 | [[0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 1, 1]] |
403 | 401 | """ |
… |
… |
|
417 | 415 | r""" |
418 | 416 | Displays the pseudoline arrangement as a wiring diagram. |
419 | 417 | |
420 | | INPUT: |
| 418 | INPUT: |
421 | 419 | |
422 | 420 | - ``**args`` -- any arguments to be forwarded to the ``show`` method. In |
423 | 421 | particular, to tune the dimensions, use the ``figsize`` argument |
… |
… |
|
425 | 423 | |
426 | 424 | EXAMPLE:: |
427 | 425 | |
428 | | sage: from sage.geometry.pseudolines import PseudolinesArrangement |
| 426 | sage: from sage.geometry.pseudolines import PseudolineArrangement |
429 | 427 | sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]] |
430 | | sage: p = PseudolinesArrangement(permutations) |
| 428 | sage: p = PseudolineArrangement(permutations) |
431 | 429 | sage: p.show(figsize=[7,5]) |
432 | 430 | |
433 | 431 | TESTS:: |
434 | 432 | |
435 | | sage: from sage.geometry.pseudolines import PseudolinesArrangement |
| 433 | sage: from sage.geometry.pseudolines import PseudolineArrangement |
436 | 434 | sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 0, 1], [2, 0, 1]] |
437 | | sage: p = PseudolinesArrangement(permutations) |
| 435 | sage: p = PseudolineArrangement(permutations) |
438 | 436 | sage: p.show() |
439 | 437 | Traceback (most recent call last): |
440 | 438 | ... |
… |
… |
|
464 | 462 | lines[j].append((x+2,iy)) |
465 | 463 | |
466 | 464 | x += 2 |
467 | | |
| 465 | |
468 | 466 | L = line([(1,1)]) |
469 | | |
| 467 | |
470 | 468 | for i, l in enumerate(lines): |
471 | 469 | l.append((x+2, l[-1][1])) |
472 | 470 | 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)) |
| 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") |
476 | 474 | |
477 | 475 | return L.show(axes = False, **args) |
478 | | |
| 476 | |
479 | 477 | |
480 | 478 | def __repr__(self): |
481 | 479 | r""" |
482 | | A short txt description of the pseudolines arrangement. |
| 480 | A short txt description of the pseudoline arrangement. |
483 | 481 | |
484 | 482 | EXAMPLE:: |
485 | 483 | |
486 | | sage: from sage.geometry.pseudolines import PseudolinesArrangement |
| 484 | sage: from sage.geometry.pseudolines import PseudolineArrangement |
487 | 485 | sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]] |
488 | | sage: p = PseudolinesArrangement(permutations) |
| 486 | sage: p = PseudolineArrangement(permutations) |
489 | 487 | sage: p |
490 | 488 | Arrangement of pseudolines of size 4 |
491 | 489 | """ |
… |
… |
|
497 | 495 | |
498 | 496 | TEST:: |
499 | 497 | |
500 | | sage: from sage.geometry.pseudolines import PseudolinesArrangement |
| 498 | sage: from sage.geometry.pseudolines import PseudolineArrangement |
501 | 499 | sage: permutations = [[3, 2, 1], [3, 2, 0], [3, 1, 0], [2, 1, 0]] |
502 | | sage: p1 = PseudolinesArrangement(permutations) |
| 500 | sage: p1 = PseudolineArrangement(permutations) |
503 | 501 | sage: transpositions = [(3, 2), (3, 1), (0, 3), (2, 1), (0, 2), (0, 1)] |
504 | | sage: p2 = PseudolinesArrangement(transpositions) |
| 502 | sage: p2 = PseudolineArrangement(transpositions) |
505 | 503 | sage: p1 == p2 |
506 | | True |
| 504 | True |
507 | 505 | """ |
508 | 506 | return (self._n == other._n) and (self._permutations == other._permutations) |