Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/designs/ext_rep.py
4057 views
1
"""
2
External Representations of Block Designs
3
4
The "ext_rep" module is an API to the abstract tree represented by
5
an XML document containing the External Representation of a list of
6
block designs. The module also provides the related I/O operations for
7
reading/writing ext-rep files or data. The parsing is based on expat.
8
9
This is a modified form of the module ext_rep.py (version 0.8)
10
written by Peter Dobcsanyi [D2009]_ [email protected].
11
12
REFERENCES:
13
14
.. [D2009] P. Dobcsanyi et al. DesignTheory.org
15
http://designtheory.org/database/
16
17
TODO: The XML data from the designtheory.org database contains a wealth
18
of information about things like automorphism groups, transitivity,
19
cycle type representatives, etc, but none of this data is made
20
available through the current implementation.
21
"""
22
23
###########################################################################
24
# This software is released under the terms of the GNU General Public
25
# License, version 2 or above (your choice). For details on licensing,
26
# see the accompanying documentation.
27
#
28
# This is a modified form of the module ext_rep.py (version 0.8)
29
# written by Peter Dobcsanyi [email protected].
30
#
31
# Copyright 2004 by Peter Dobcsanyi [email protected], and copyright
32
# 2009 Carlo Hamalainen [email protected]
33
###########################################################################
34
35
import sys
36
import xml.parsers.expat
37
from types import *
38
import re
39
import os.path
40
import gzip
41
import bz2
42
from sage.misc.misc import tmp_filename
43
import urllib2
44
import sys
45
46
XML_NAMESPACE = 'http://designtheory.org/xml-namespace'
47
DTRS_PROTOCOL = '2.0'
48
49
# The following string is the file
50
# http://designtheory.org/database/v-b-k/v2-b2-k2.icgsa.txt.bz2
51
# We use this for doctests to make sure that the parsing works.
52
53
v2_b2_k2_icgsa = \
54
"""<?xml version="1.0"?>
55
<list_of_designs
56
design_type="block_design"
57
dtrs_protocol="2.0"
58
no_designs="1"
59
pairwise_nonisomorphic="true"
60
xmlns="http://designtheory.org/xml-namespace">
61
<info>
62
<software>
63
[ DESIGN-1.1, GRAPE-4.2, GAPDoc-0.9999, GAP-4.4.3 ]
64
</software>
65
<software>
66
[ bdstat-0.8/280, numarray-1.1.1, pydesign-0.5/274, python-2.4.0.final.0 ]
67
</software>
68
</info>
69
<designs>
70
<block_design
71
b="2"
72
id="v2-b2-k2-0"
73
v="2">
74
<blocks ordered="true">
75
<block>
76
<z>0</z>
77
<z>1</z>
78
</block>
79
<block>
80
<z>0</z>
81
<z>1</z>
82
</block>
83
</blocks>
84
<indicators>
85
<repeated_blocks flag="true"/>
86
<resolvable flag="true"/>
87
<affine_resolvable
88
flag="true"
89
mu="2"/>
90
<equireplicate
91
flag="true"
92
r="2"/>
93
<constant_blocksize
94
flag="true"
95
k="2"/>
96
<t_design
97
flag="true"
98
maximum_t="2"/>
99
<connected
100
flag="true"
101
no_components="1"/>
102
<pairwise_balanced
103
flag="true"
104
lambda="2"/>
105
<variance_balanced flag="true"/>
106
<efficiency_balanced flag="true"/>
107
<cyclic flag="true"/>
108
<one_rotational flag="true"/>
109
</indicators>
110
<combinatorial_properties>
111
<point_concurrences>
112
<function_on_ksubsets_of_indices
113
domain_base="points"
114
k="1"
115
n="2"
116
ordered="true"
117
title="replication_numbers">
118
<map>
119
<preimage>
120
<entire_domain/>
121
</preimage>
122
<image>
123
<z>2</z>
124
</image>
125
</map>
126
</function_on_ksubsets_of_indices>
127
<function_on_ksubsets_of_indices
128
domain_base="points"
129
k="2"
130
n="2"
131
ordered="true"
132
title="pairwise_point_concurrences">
133
<map>
134
<preimage>
135
<entire_domain/>
136
</preimage>
137
<image>
138
<z>2</z>
139
</image>
140
</map>
141
</function_on_ksubsets_of_indices>
142
</point_concurrences>
143
<block_concurrences>
144
<function_on_ksubsets_of_indices
145
domain_base="blocks"
146
k="1"
147
n="2"
148
ordered="unknown"
149
title="block_sizes">
150
<map>
151
<preimage_cardinality>
152
<z>2</z>
153
</preimage_cardinality>
154
<image>
155
<z>2</z>
156
</image>
157
</map>
158
</function_on_ksubsets_of_indices>
159
<function_on_ksubsets_of_indices
160
domain_base="blocks"
161
k="2"
162
n="2"
163
ordered="unknown"
164
title="pairwise_block_intersection_sizes">
165
<map>
166
<preimage_cardinality>
167
<z>1</z>
168
</preimage_cardinality>
169
<image>
170
<z>2</z>
171
</image>
172
</map>
173
</function_on_ksubsets_of_indices>
174
</block_concurrences>
175
<t_design_properties>
176
<parameters
177
b="2"
178
k="2"
179
lambda="2"
180
r="2"
181
t="2"
182
v="2"/>
183
<square flag="true"/>
184
<projective_plane flag="false"/>
185
<affine_plane flag="false"/>
186
<steiner_system flag="false"/>
187
<steiner_triple_system flag="false"/>
188
</t_design_properties>
189
<alpha_resolvable>
190
<index_flag
191
flag="true"
192
index="2"/>
193
</alpha_resolvable>
194
<t_wise_balanced>
195
<index_flag
196
flag="true"
197
index="1"/>
198
<index_flag
199
flag="true"
200
index="2"/>
201
</t_wise_balanced>
202
</combinatorial_properties>
203
<automorphism_group>
204
<permutation_group
205
degree="2"
206
domain="points"
207
order="2">
208
<generators>
209
<permutation>
210
<z>1</z>
211
<z>0</z>
212
</permutation>
213
</generators>
214
<permutation_group_properties>
215
<primitive flag="true"/>
216
<generously_transitive flag="true"/>
217
<multiplicity_free flag="true"/>
218
<stratifiable flag="true"/>
219
<no_orbits value="1"/>
220
<degree_transitivity value="2"/>
221
<rank value="2"/>
222
<cycle_type_representatives>
223
<cycle_type_representative>
224
<permutation>
225
<z>1</z>
226
<z>0</z>
227
</permutation>
228
<cycle_type ordered="true">
229
<z>2</z>
230
</cycle_type>
231
<no_having_cycle_type>
232
<z>1</z>
233
</no_having_cycle_type>
234
</cycle_type_representative>
235
<cycle_type_representative>
236
<permutation>
237
<z>0</z>
238
<z>1</z>
239
</permutation>
240
<cycle_type ordered="true">
241
<z>1</z>
242
<z>1</z>
243
</cycle_type>
244
<no_having_cycle_type>
245
<z>1</z>
246
</no_having_cycle_type>
247
</cycle_type_representative>
248
</cycle_type_representatives>
249
</permutation_group_properties>
250
</permutation_group>
251
<automorphism_group_properties>
252
<block_primitive flag="not_applicable"/>
253
<no_block_orbits value="not_applicable"/>
254
<degree_block_transitivity value="not_applicable"/>
255
</automorphism_group_properties>
256
</automorphism_group>
257
<resolutions
258
all_classes_represented="true"
259
pairwise_nonisomorphic="true">
260
<resolution>
261
<function_on_indices
262
domain="blocks"
263
n="2"
264
ordered="true"
265
title="resolution">
266
<map>
267
<preimage>
268
<z>0</z>
269
</preimage>
270
<image>
271
<z>0</z>
272
</image>
273
</map>
274
<map>
275
<preimage>
276
<z>0</z>
277
</preimage>
278
<image>
279
<z>1</z>
280
</image>
281
</map>
282
</function_on_indices>
283
<automorphism_group>
284
<permutation_group
285
degree="2"
286
domain="points"
287
order="2">
288
<generators>
289
<permutation>
290
<z>1</z>
291
<z>0</z>
292
</permutation>
293
</generators>
294
</permutation_group>
295
</automorphism_group>
296
</resolution>
297
</resolutions>
298
<statistical_properties precision="9">
299
<canonical_variances
300
no_distinct="1"
301
ordered="true">
302
<value multiplicity="1">
303
<d>0.5</d>
304
</value>
305
</canonical_variances>
306
<pairwise_variances>
307
<function_on_ksubsets_of_indices
308
domain_base="points"
309
k="2"
310
n="2"
311
ordered="true">
312
<map>
313
<preimage>
314
<entire_domain/>
315
</preimage>
316
<image>
317
<d>1.0</d>
318
</image>
319
</map>
320
</function_on_ksubsets_of_indices>
321
</pairwise_variances>
322
<optimality_criteria>
323
<phi_0>
324
<value>
325
<d>-0.693147181</d>
326
</value>
327
<absolute_efficiency>
328
<z>1</z>
329
</absolute_efficiency>
330
<calculated_efficiency>
331
<z>1</z>
332
</calculated_efficiency>
333
</phi_0>
334
<phi_1>
335
<value>
336
<d>0.5</d>
337
</value>
338
<absolute_efficiency>
339
<z>1</z>
340
</absolute_efficiency>
341
<calculated_efficiency>
342
<z>1</z>
343
</calculated_efficiency>
344
</phi_1>
345
<phi_2>
346
<value>
347
<d>0.25</d>
348
</value>
349
<absolute_efficiency>
350
<z>1</z>
351
</absolute_efficiency>
352
<calculated_efficiency>
353
<z>1</z>
354
</calculated_efficiency>
355
</phi_2>
356
<maximum_pairwise_variances>
357
<value>
358
<d>1.0</d>
359
</value>
360
<absolute_efficiency>
361
<z>1</z>
362
</absolute_efficiency>
363
<calculated_efficiency>
364
<z>1</z>
365
</calculated_efficiency>
366
</maximum_pairwise_variances>
367
<E_criteria>
368
<E_value index="1">
369
<value>
370
<d>0.5</d>
371
</value>
372
<absolute_efficiency>
373
<z>1</z>
374
</absolute_efficiency>
375
<calculated_efficiency>
376
<z>1</z>
377
</calculated_efficiency>
378
</E_value>
379
</E_criteria>
380
</optimality_criteria>
381
<other_ordering_criteria>
382
<trace_of_square_of_C>
383
<value>
384
<d>4.0</d>
385
</value>
386
<absolute_comparison>
387
<z>1</z>
388
</absolute_comparison>
389
<calculated_comparison>
390
<z>1</z>
391
</calculated_comparison>
392
</trace_of_square_of_C>
393
<max_min_ratio_canonical_variances>
394
<value>
395
<d>1.0</d>
396
</value>
397
<absolute_comparison>
398
<z>1</z>
399
</absolute_comparison>
400
<calculated_comparison>
401
<z>1</z>
402
</calculated_comparison>
403
</max_min_ratio_canonical_variances>
404
<max_min_ratio_pairwise_variances>
405
<value>
406
<d>1.0</d>
407
</value>
408
<absolute_comparison>
409
<z>1</z>
410
</absolute_comparison>
411
<calculated_comparison>
412
<z>1</z>
413
</calculated_comparison>
414
</max_min_ratio_pairwise_variances>
415
<no_distinct_canonical_variances>
416
<value>
417
<z>1</z>
418
</value>
419
<absolute_comparison>
420
<z>1</z>
421
</absolute_comparison>
422
<calculated_comparison>
423
<z>1</z>
424
</calculated_comparison>
425
</no_distinct_canonical_variances>
426
<no_distinct_pairwise_variances>
427
<value>
428
<z>1</z>
429
</value>
430
<absolute_comparison>
431
<z>1</z>
432
</absolute_comparison>
433
<calculated_comparison>
434
<z>1</z>
435
</calculated_comparison>
436
</no_distinct_pairwise_variances>
437
</other_ordering_criteria>
438
<canonical_efficiency_factors
439
no_distinct="1"
440
ordered="true">
441
<value multiplicity="1">
442
<d>1.0</d>
443
</value>
444
</canonical_efficiency_factors>
445
<functions_of_efficiency_factors>
446
<harmonic_mean alias="A">
447
<value>
448
<d>1.0</d>
449
</value>
450
</harmonic_mean>
451
<geometric_mean alias="D">
452
<value>
453
<d>1.0</d>
454
</value>
455
</geometric_mean>
456
<minimum alias="E">
457
<value>
458
<d>1.0</d>
459
</value>
460
</minimum>
461
</functions_of_efficiency_factors>
462
</statistical_properties>
463
</block_design>
464
</designs>
465
</list_of_designs>
466
"""
467
468
def dump_to_tmpfile(s):
469
"""
470
Utility function to dump a string to a temporary file.
471
472
EXAMPLE::
473
474
sage: from sage.combinat.designs import ext_rep
475
sage: file_loc = ext_rep.dump_to_tmpfile("boo")
476
sage: os.remove(file_loc)
477
"""
478
479
file_loc = tmp_filename()
480
f = open(file_loc,"w")
481
f.write(v2_b2_k2_icgsa)
482
f.close()
483
return file_loc
484
485
def check_dtrs_protocols(input_name, input_pv):
486
"""
487
Check that the XML data is in a valid format. We can currently
488
handle version 2.0. For more information see
489
http://designtheory.org/library/extrep/
490
491
EXAMPLES::
492
493
sage: from sage.combinat.designs import ext_rep
494
sage: ext_rep.check_dtrs_protocols('source', '2.0')
495
sage: ext_rep.check_dtrs_protocols('source', '3.0')
496
Traceback (most recent call last):
497
...
498
RuntimeError: Incompatible dtrs_protocols: program: 2.0 source: 3.0
499
"""
500
501
program_pv = DTRS_PROTOCOL
502
ppv_major, ppv_minor = program_pv.split('.')
503
ipv_major, ipv_minor = input_pv.split('.')
504
if ppv_major != ipv_major or int(ppv_minor) < int(ipv_minor):
505
msg = ('''Incompatible dtrs_protocols: program: %s %s: %s''' % (program_pv, input_name, input_pv))
506
raise RuntimeError, msg
507
508
def open_extrep_file(fname):
509
"""
510
Try to guess the compression type from extension
511
and open the extrep file.
512
513
EXAMPLES::
514
515
sage: from sage.combinat.designs import ext_rep
516
sage: file_loc = ext_rep.dump_to_tmpfile(ext_rep.v2_b2_k2_icgsa)
517
sage: proc = ext_rep.XTreeProcessor()
518
sage: f = ext_rep.open_extrep_file(file_loc)
519
sage: proc.parse(f)
520
sage: f.close()
521
sage: os.remove(file_loc)
522
"""
523
524
if fname == '-':
525
f = sys.stdin
526
else:
527
root, ext = os.path.splitext(fname)
528
if ext == '.gz':
529
f = gzip.GzipFile(fname)
530
elif ext == '.bz2':
531
f = bz2.BZ2File(fname)
532
else:
533
f = open(fname)
534
return f
535
536
def open_extrep_url(url):
537
"""
538
Try to guess the compression type from extension
539
and open the extrep file pointed to by the url. This function
540
(unlike open_extrep_file) returns the uncompressed text contained in
541
the file.
542
543
EXAMPLES::
544
545
sage: from sage.combinat.designs import ext_rep
546
sage: file_loc = ext_rep.dump_to_tmpfile(ext_rep.v2_b2_k2_icgsa)
547
sage: proc = ext_rep.XTreeProcessor()
548
sage: s = ext_rep.open_extrep_url("file://" + file_loc)
549
sage: proc.parse(s)
550
sage: os.remove(file_loc)
551
552
sage: from sage.combinat.designs import ext_rep
553
sage: s = ext_rep.designs_from_XML_url("http://designtheory.org/database/v-b-k/v3-b6-k2.icgsa.txt.bz2") # optional - requires internet
554
"""
555
556
f = urllib2.urlopen(url)
557
558
root, ext = os.path.splitext(url)
559
if ext == '.gz':
560
# f = gzip.GzipFile(f_url)
561
raise NotImplemented
562
elif ext == '.bz2':
563
return bz2.decompress(f.read())
564
else:
565
return f.read()
566
567
pattern_integer = re.compile(r'\d+$')
568
pattern_decimal = re.compile(r'-?\d+\.\d+$')
569
pattern_rational = re.compile(r'-?\d+/\d+$')
570
571
def _encode_attribute(string):
572
"""
573
Convert numbers in attributes into binary format.
574
Currently integer and floating point conversions are implemented.
575
576
EXAMPLES::
577
578
sage: from sage.combinat.designs.ext_rep import _encode_attribute
579
sage: _encode_attribute('1')
580
1
581
sage: _encode_attribute('2')
582
2
583
sage: _encode_attribute('12')
584
12
585
sage: _encode_attribute('true')
586
'true'
587
sage: _encode_attribute('A')
588
'A'
589
sage: _encode_attribute('D')
590
'D'
591
sage: _encode_attribute('E')
592
'E'
593
"""
594
595
if pattern_integer.match(string):
596
return int(string)
597
elif pattern_decimal.match(string):
598
return float(string)
599
else:
600
return string
601
602
class XTree(object):
603
'''
604
A lazy class to wrap a rooted tree representing an XML document.
605
The tree's nodes are tuples of the structure:
606
607
(name, {dictionary of attributes}, [list of children])
608
609
Methods and services of an XTree object ``t``:
610
611
- ``t.attribute`` -- attribute named
612
- ``t.child`` -- first child named
613
- ``t[i]`` -- i-th child
614
- ``for child in t:`` -- iterate over ``t``'s children
615
- ``len(t)`` -- number of ``t``'s children
616
617
If child is not an empty subtree, return the subtree as an ``XTree``
618
object. If child is an empty subtree, return ``_name`` of the subtree.
619
Otherwise return the child itself.
620
621
The lazy tree idea originated from a utility class of the
622
pyRXP 0.9 package by Robin Becker at ReportLab.
623
'''
624
625
def __init__(self, node):
626
"""
627
Initialisation method given a node in an XML document.
628
629
EXAMPLES::
630
631
sage: from sage.combinat.designs.ext_rep import *
632
sage: xt = XTree(('blocks', {'ordered': 'true'}, [('block', {}, [[0, 1, 2]]), ('block', {}, [[0, 3, 4]]), ('block', {}, [[0, 5, 6]]), ('block', {}, [[0, 7, 8]]), ('block', {}, [[0, 9, 10]]), ('block', {}, [[0, 11, 12]]), ('block', {}, [[1, 3, 5]]), ('block', {}, [[1, 4, 6]]), ('block', {}, [[1, 7, 9]]), ('block', {}, [[1, 8, 11]]), ('block', {}, [[1, 10, 12]]), ('block', {}, [[2, 3, 7]]), ('block', {}, [[2, 4, 8]]), ('block', {}, [[2, 5, 10]]), ('block', {}, [[2, 6, 12]]), ('block', {}, [[2, 9, 11]]), ('block', {}, [[3, 6, 9]]), ('block', {}, [[3, 8, 12]]), ('block', {}, [[3, 10, 11]]), ('block', {}, [[4, 5, 11]]), ('block', {}, [[4, 7, 10]]), ('block', {}, [[4, 9, 12]]), ('block', {}, [[5, 7, 12]]), ('block', {}, [[5, 8, 9]]), ('block', {}, [[6, 7, 11]]), ('block', {}, [[6, 8, 10]])]))
633
sage: xt.xt_children
634
[('block', {}, [[0, 1, 2]]),
635
('block', {}, [[0, 3, 4]]),
636
('block', {}, [[0, 5, 6]]),
637
('block', {}, [[0, 7, 8]]),
638
('block', {}, [[0, 9, 10]]),
639
('block', {}, [[0, 11, 12]]),
640
('block', {}, [[1, 3, 5]]),
641
('block', {}, [[1, 4, 6]]),
642
('block', {}, [[1, 7, 9]]),
643
('block', {}, [[1, 8, 11]]),
644
('block', {}, [[1, 10, 12]]),
645
('block', {}, [[2, 3, 7]]),
646
('block', {}, [[2, 4, 8]]),
647
('block', {}, [[2, 5, 10]]),
648
('block', {}, [[2, 6, 12]]),
649
('block', {}, [[2, 9, 11]]),
650
('block', {}, [[3, 6, 9]]),
651
('block', {}, [[3, 8, 12]]),
652
('block', {}, [[3, 10, 11]]),
653
('block', {}, [[4, 5, 11]]),
654
('block', {}, [[4, 7, 10]]),
655
('block', {}, [[4, 9, 12]]),
656
('block', {}, [[5, 7, 12]]),
657
('block', {}, [[5, 8, 9]]),
658
('block', {}, [[6, 7, 11]]),
659
('block', {}, [[6, 8, 10]])]
660
"""
661
662
663
if type(node) == StringType:
664
node = (node, {}, [])
665
name, attributes, children = node
666
self.xt_node = node
667
self.xt_name = name
668
self.xt_attributes = attributes
669
self.xt_children = children
670
671
def __repr__(self):
672
"""
673
String representation of an XTree object.
674
675
EXAMPLES::
676
677
sage: from sage.combinat.designs.ext_rep import *
678
sage: xt = XTree(('blocks', {'ordered': 'true'}, [('block', {}, [[0, 1, 2]]), ('block', {}, [[0, 3, 4]]), ('block', {}, [[0, 5, 6]]), ('block', {}, [[0, 7, 8]]), ('block', {}, [[0, 9, 10]]), ('block', {}, [[0, 11, 12]]), ('block', {}, [[1, 3, 5]]), ('block', {}, [[1, 4, 6]]), ('block', {}, [[1, 7, 9]]), ('block', {}, [[1, 8, 11]]), ('block', {}, [[1, 10, 12]]), ('block', {}, [[2, 3, 7]]), ('block', {}, [[2, 4, 8]]), ('block', {}, [[2, 5, 10]]), ('block', {}, [[2, 6, 12]]), ('block', {}, [[2, 9, 11]]), ('block', {}, [[3, 6, 9]]), ('block', {}, [[3, 8, 12]]), ('block', {}, [[3, 10, 11]]), ('block', {}, [[4, 5, 11]]), ('block', {}, [[4, 7, 10]]), ('block', {}, [[4, 9, 12]]), ('block', {}, [[5, 7, 12]]), ('block', {}, [[5, 8, 9]]), ('block', {}, [[6, 7, 11]]), ('block', {}, [[6, 8, 10]])]))
679
sage: xt.__repr__()
680
'XTree<blocks>'
681
"""
682
683
return 'XTree<%s>' % self.xt_name
684
685
def __getattr__(self, attr):
686
"""
687
Returns the data for the first attribute with name attr.
688
689
EXAMPLES::
690
691
sage: from sage.combinat.designs.ext_rep import *
692
sage: xt = XTree(('blocks', {'ordered': 'true'}, [('block', {}, [[0, 1, 2]]), ('block', {}, [[0, 3, 4]]), ('block', {}, [[0, 5, 6]]), ('block', {}, [[0, 7, 8]]), ('block', {}, [[0, 9, 10]]), ('block', {}, [[0, 11, 12]]), ('block', {}, [[1, 3, 5]]), ('block', {}, [[1, 4, 6]]), ('block', {}, [[1, 7, 9]]), ('block', {}, [[1, 8, 11]]), ('block', {}, [[1, 10, 12]]), ('block', {}, [[2, 3, 7]]), ('block', {}, [[2, 4, 8]]), ('block', {}, [[2, 5, 10]]), ('block', {}, [[2, 6, 12]]), ('block', {}, [[2, 9, 11]]), ('block', {}, [[3, 6, 9]]), ('block', {}, [[3, 8, 12]]), ('block', {}, [[3, 10, 11]]), ('block', {}, [[4, 5, 11]]), ('block', {}, [[4, 7, 10]]), ('block', {}, [[4, 9, 12]]), ('block', {}, [[5, 7, 12]]), ('block', {}, [[5, 8, 9]]), ('block', {}, [[6, 7, 11]]), ('block', {}, [[6, 8, 10]])]))
693
sage: xt.__getattr__('block')
694
[0, 1, 2]
695
"""
696
697
if self.xt_attributes.has_key(attr):
698
return self.xt_attributes[attr]
699
else:
700
for child in self.xt_children:
701
name, attributes, children = child
702
if name == attr:
703
if len(attributes) > 0:
704
return XTree(child)
705
else:
706
if len(children) == 0:
707
# need this to get an empty Xtree, for append
708
return XTree(child)
709
grandchild = children[0]
710
if type(grandchild) == TupleType:
711
if len(grandchild[1]) == 0 and \
712
len(grandchild[2]) == 0:
713
return grandchild[0]
714
else:
715
return XTree(child)
716
else:
717
return grandchild
718
msg = '"%s" is not found in attributes of %s or its children.' % \
719
(attr, self)
720
raise AttributeError, msg
721
722
def __getitem__(self, i):
723
"""
724
Get the ``i``-th item in the current node.
725
726
EXAMPLES::
727
728
sage: from sage.combinat.designs.ext_rep import *
729
sage: xt = XTree(('blocks', {'ordered': 'true'}, [('block', {}, [[0, 1, 2]]), ('block', {}, [[0, 3, 4]]), ('block', {}, [[0, 5, 6]]), ('block', {}, [[0, 7, 8]]), ('block', {}, [[0, 9, 10]]), ('block', {}, [[0, 11, 12]]), ('block', {}, [[1, 3, 5]]), ('block', {}, [[1, 4, 6]]), ('block', {}, [[1, 7, 9]]), ('block', {}, [[1, 8, 11]]), ('block', {}, [[1, 10, 12]]), ('block', {}, [[2, 3, 7]]), ('block', {}, [[2, 4, 8]]), ('block', {}, [[2, 5, 10]]), ('block', {}, [[2, 6, 12]]), ('block', {}, [[2, 9, 11]]), ('block', {}, [[3, 6, 9]]), ('block', {}, [[3, 8, 12]]), ('block', {}, [[3, 10, 11]]), ('block', {}, [[4, 5, 11]]), ('block', {}, [[4, 7, 10]]), ('block', {}, [[4, 9, 12]]), ('block', {}, [[5, 7, 12]]), ('block', {}, [[5, 8, 9]]), ('block', {}, [[6, 7, 11]]), ('block', {}, [[6, 8, 10]])]))
730
sage: xt.__getitem__(0)
731
[0, 1, 2]
732
sage: xt.__getitem__(1)
733
[0, 3, 4]
734
"""
735
736
try:
737
child = self.xt_children[i]
738
except IndexError:
739
raise IndexError, '%s no index %s' % (self.__repr__(), `i`)
740
if type(child) == TupleType:
741
name, attributes, children = child
742
if len(attributes) > 0:
743
return XTree(child)
744
else:
745
grandchild = children[0]
746
if type(grandchild) == TupleType:
747
if len(grandchild[1]) == 0 and len(grandchild[2]) == 0:
748
return grandchild[0]
749
else:
750
return XTree(child)
751
else:
752
return grandchild
753
else:
754
return child
755
756
def __len__(self):
757
"""
758
Returns the length of the current node.
759
760
EXAMPLES::
761
762
sage: from sage.combinat.designs.ext_rep import *
763
sage: xt = XTree(('blocks', {'ordered': 'true'}, [('block', {}, [[0, 1, 2]]), ('block', {}, [[0, 3, 4]]), ('block', {}, [[0, 5, 6]]), ('block', {}, [[0, 7, 8]]), ('block', {}, [[0, 9, 10]]), ('block', {}, [[0, 11, 12]]), ('block', {}, [[1, 3, 5]]), ('block', {}, [[1, 4, 6]]), ('block', {}, [[1, 7, 9]]), ('block', {}, [[1, 8, 11]]), ('block', {}, [[1, 10, 12]]), ('block', {}, [[2, 3, 7]]), ('block', {}, [[2, 4, 8]]), ('block', {}, [[2, 5, 10]]), ('block', {}, [[2, 6, 12]]), ('block', {}, [[2, 9, 11]]), ('block', {}, [[3, 6, 9]]), ('block', {}, [[3, 8, 12]]), ('block', {}, [[3, 10, 11]]), ('block', {}, [[4, 5, 11]]), ('block', {}, [[4, 7, 10]]), ('block', {}, [[4, 9, 12]]), ('block', {}, [[5, 7, 12]]), ('block', {}, [[5, 8, 9]]), ('block', {}, [[6, 7, 11]]), ('block', {}, [[6, 8, 10]])]))
764
sage: xt.__len__()
765
26
766
"""
767
768
return len(self.xt_children)
769
770
class XTreeProcessor(object):
771
'''
772
An incremental event-driven parser for ext-rep documents.
773
The processing stages:
774
775
- ``<list_of_designs ...>`` opening element.
776
call-back: ``list_of_designs_proc``
777
778
- ``<list_definition>`` subtree.
779
call-back: ``list_definition_proc``
780
781
- ``<info>`` subtree.
782
call-back: ``info_proc``
783
784
- iterating over ``<designs>`` processing each ``<block_design>``
785
separately.
786
call-back: ``block_design_proc``
787
788
- finishing with closing ``</designs>`` and ``</list_of_designs>``.
789
'''
790
def _init(self):
791
"""
792
Internal initialisation for the processor of XTrees.
793
794
EXAMPLES::
795
796
sage: from sage.combinat.designs import ext_rep
797
sage: proc = ext_rep.XTreeProcessor()
798
sage: proc._init()
799
sage: proc.current_node
800
('root0', {}, [])
801
"""
802
803
self.current_node = ('root0', {}, [])
804
self.node_stack = [self.current_node]
805
self.in_item = False
806
807
def __init__(self):
808
"""
809
Internal initialisation for the processor of XTrees.
810
811
EXAMPLES::
812
813
sage: from sage.combinat.designs.ext_rep import *
814
sage: proc = XTreeProcessor()
815
sage: proc.current_node
816
('root0', {}, [])
817
sage: proc.node_stack
818
[('root0', {}, [])]
819
sage: proc.in_item
820
False
821
"""
822
823
self._init()
824
self.outf = sys.stdout
825
# call-back handlers
826
self.list_of_designs_start_proc = None
827
self.list_definition_proc = None
828
self.info_proc = None
829
self.designs_start_proc = None
830
self.block_design_proc = None
831
self.designs_end_proc = None
832
self.list_of_designs_end_proc = None
833
834
self.save_designs = False
835
self.list_of_designs = []
836
837
def _start_element(self, name, attrs):
838
"""
839
Process the start of an element with certain name and
840
attributes.
841
842
EXAMPLES::
843
844
sage: from sage.combinat.designs.ext_rep import *
845
sage: name = "block_design"
846
sage: attrs = {'b': '26', 'id': 't2-v13-b26-r6-k3-L1-0', 'v': '13'}
847
sage: proc = XTreeProcessor()
848
sage: proc._start_element(name, attrs)
849
sage: proc.current_node
850
('block_design', {'b': 26, 'id': 't2-v13-b26-r6-k3-L1-0', 'v': 13}, [])
851
"""
852
853
if name == 'block_design' or name == 'info' or name == 'list_definition':
854
self.in_item = True
855
elif name == 'list_of_designs':
856
check_dtrs_protocols('source', attrs['dtrs_protocol'])
857
if self.list_of_designs_start_proc:
858
self.list_of_designs_start_proc(attrs)
859
#self.outf.write('<%s' % name)
860
#pp_attributes(self.outf, attrs, indent='', precision_stack=[])
861
#self.outf.write('>\n')
862
elif name == 'designs':
863
pass # self.outf.write(' <%s>\n' % name)
864
if self.in_item:
865
for k, v in attrs.iteritems():
866
attrs[k] = _encode_attribute(v)
867
new_node = (name, attrs, [])
868
self.node_stack.append(new_node)
869
self.current_node[2].append(new_node)
870
self.current_node = new_node
871
872
def _end_element(self, name):
873
"""
874
Finish processing the element name.
875
876
EXAMPLES::
877
878
sage: from sage.combinat.designs.ext_rep import *
879
sage: name = "block_design"
880
sage: attrs = {'b': '26', 'id': 't2-v13-b26-r6-k3-L1-0', 'v': '13'}
881
sage: proc = XTreeProcessor()
882
sage: proc._start_element(name, attrs)
883
sage: proc._end_element(name)
884
sage: proc.current_node
885
('root0', {}, [])
886
"""
887
888
if self.in_item:
889
children = self.current_node[2]
890
if len(children) > 0 and type(children[0]) == TupleType:
891
if children[0][0] == 'z' or children[0][0] == 'd' \
892
or children[0][0] == 'q':
893
if children[0][0] == 'z':
894
convert = int
895
elif children[0][0] == 'd':
896
convert = float
897
else:
898
raise NotImplementedError, 'rational numbers'
899
ps = []
900
for x in children:
901
ps.append(convert(''.join(x[2])))
902
del children[:]
903
if name == 'block' or name == 'permutation' \
904
or name == 'preimage' or name == 'ksubset' \
905
or name == 'cycle_type' or name == 'row':
906
# these enclose lists of numbers
907
children.append(ps)
908
else:
909
# the rest is a single number
910
children.append(ps[0])
911
self.node_stack.pop()
912
self.current_node = self.node_stack[-1]
913
if name == 'block_design':
914
if self.block_design_proc:
915
self.block_design_proc(self.current_node[2][0])
916
if self.save_designs:
917
init_bd = XTree(self.current_node[2][0])
918
self.list_of_designs.append((init_bd.v, [b for b in init_bd.blocks]))
919
#print_subxt(self.current_node[2][0], level=2, outf=self.outf)
920
self._init()
921
elif name == 'info':
922
if self.info_proc:
923
self.info_proc(self.current_node[2][0])
924
#print_subxt(self.current_node[2][0], level=1, outf=self.outf)
925
self._init()
926
else:
927
if name == 'designs':
928
if self.designs_end_proc:
929
self.designs_end_proc()
930
#self.outf.write(' ')
931
#self.outf.write('</%s>\n' % name)
932
933
def _char_data(self, data):
934
"""
935
Internal function to tidy up character data.
936
937
EXAMPLES::
938
939
sage: from sage.combinat.designs.ext_rep import *
940
sage: name = "block_design"
941
sage: attrs = {'b': '26', 'id': 't2-v13-b26-r6-k3-L1-0', 'v': '13'}
942
943
sage: proc = XTreeProcessor()
944
sage: proc._start_element(name, attrs)
945
946
sage: proc._char_data(r" [ DESIGN-1.1, GRAPE-4.2, GAPDoc-0.9999, GAP-4.4.3]")
947
sage: proc.current_node
948
('block_design',
949
{'b': 26, 'id': 't2-v13-b26-r6-k3-L1-0', 'v': 13},
950
['[ DESIGN-1.1, GRAPE-4.2, GAPDoc-0.9999, GAP-4.4.3]'])
951
"""
952
953
if self.in_item:
954
#@ this stripping may distort char data in the <info> subtree
955
# if they are not bracketed in some way.
956
data = data.strip()
957
if data != '':
958
# we use the xtree's childrens list here to collect char data
959
# since only leaves have char data.
960
self.current_node[2].append(data)
961
962
def parse(self, xml_source):
963
"""
964
The main parsing function. Given an XML source (either a file
965
handle or a string), parse the entire XML source.
966
967
EXAMPLES::
968
969
sage: from sage.combinat.designs import ext_rep
970
sage: file_loc = ext_rep.dump_to_tmpfile(ext_rep.v2_b2_k2_icgsa)
971
sage: proc = ext_rep.XTreeProcessor()
972
sage: proc.save_designs = True
973
sage: f = ext_rep.open_extrep_file(file_loc)
974
sage: proc.parse(f)
975
sage: f.close()
976
sage: os.remove(file_loc)
977
sage: proc.list_of_designs[0]
978
(2, [[0, 1], [0, 1]])
979
"""
980
981
p = xml.parsers.expat.ParserCreate()
982
p.StartElementHandler = self._start_element
983
p.EndElementHandler = self._end_element
984
p.CharacterDataHandler = self._char_data
985
p.returns_unicode = 0
986
987
if isinstance(xml_source, StringType):
988
p.Parse(xml_source)
989
else:
990
p.ParseFile(xml_source)
991
992
def designs_from_XML(fname):
993
"""
994
Returns a list of designs contained in an XML file fname. The list
995
contains tuples of the form (v, bs) where v is the number of points of
996
the design and bs is the list of blocks.
997
998
EXAMPLES::
999
1000
sage: from sage.combinat.designs import ext_rep
1001
sage: file_loc = ext_rep.dump_to_tmpfile(ext_rep.v2_b2_k2_icgsa)
1002
sage: ext_rep.designs_from_XML(file_loc)[0]
1003
(2, [[0, 1], [0, 1]])
1004
sage: os.remove(file_loc)
1005
1006
sage: from sage.combinat.designs import ext_rep
1007
sage: from sage.combinat.designs.block_design import BlockDesign
1008
sage: file_loc = ext_rep.dump_to_tmpfile(ext_rep.v2_b2_k2_icgsa)
1009
sage: v, blocks = ext_rep.designs_from_XML(file_loc)[0]
1010
sage: d = BlockDesign(v, blocks)
1011
sage: d.blocks()
1012
[[0, 1], [0, 1]]
1013
sage: d.parameters()
1014
(2, 2, 2, 2)
1015
"""
1016
1017
proc = XTreeProcessor()
1018
proc.save_designs = True
1019
f = open_extrep_file(fname)
1020
proc.parse(f)
1021
f.close()
1022
1023
return proc.list_of_designs
1024
1025
def designs_from_XML_url(url):
1026
"""
1027
Returns a list of designs contained in an XML file named by a URL.
1028
The list contains tuples of the form (v, bs) where v is the number
1029
of points of the design and bs is the list of blocks.
1030
1031
EXAMPLES::
1032
1033
sage: from sage.combinat.designs import ext_rep
1034
sage: file_loc = ext_rep.dump_to_tmpfile(ext_rep.v2_b2_k2_icgsa)
1035
sage: ext_rep.designs_from_XML_url("file://" + file_loc)[0]
1036
(2, [[0, 1], [0, 1]])
1037
sage: os.remove(file_loc)
1038
1039
sage: from sage.combinat.designs import ext_rep
1040
sage: ext_rep.designs_from_XML_url("http://designtheory.org/database/v-b-k/v3-b6-k2.icgsa.txt.bz2") # optional - requires internet
1041
[(3, [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 2]]),
1042
(3, [[0, 1], [0, 1], [0, 1], [0, 1], [0, 2], [0, 2]]),
1043
(3, [[0, 1], [0, 1], [0, 1], [0, 1], [0, 2], [1, 2]]),
1044
(3, [[0, 1], [0, 1], [0, 1], [0, 2], [0, 2], [0, 2]]),
1045
(3, [[0, 1], [0, 1], [0, 1], [0, 2], [0, 2], [1, 2]]),
1046
(3, [[0, 1], [0, 1], [0, 2], [0, 2], [1, 2], [1, 2]])]
1047
"""
1048
proc = XTreeProcessor()
1049
proc.save_designs = True
1050
s = open_extrep_url(url)
1051
proc.parse(s)
1052
1053
return proc.list_of_designs
1054
1055
1056