-
Notifications
You must be signed in to change notification settings - Fork 0
/
tam2.py
4726 lines (3759 loc) · 216 KB
/
tam2.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
from __future__ import print_function, division
import copy,collections
VERBOSE_SIGNALS = True
"""Contains classes and functions for creating and
manipulating 2D tiles and tile assembly systems in the Tile
Assembly Model. The fundamental classes are
- Multisignal: a mapping type, mapping signal names to values
- MultisignalType: a mapping type, mapping signal names to lists of valid
values
- Tile: a type of tile, composed of one glue on each of the four sides, as well
as a name, label, and possible other properties such as tilecolor,
textcolor, concentration, etc.
- TileSystem: a list of unique tiles together with a seed assembly, which is
a dict mapping 2D coordinates ((int,int) pairs) to Tiles
- TileTemplate: a class for specifying a family of Tiles that share a common
set of input sides, each with the same GlueTemplate, and a
common set of output sides, each with the same GlueTemplate.
- TileSetTemplate: a group of TileTemplates and Tiles. This object is
responsible for managing the input-output relationships
between the TileTemplates, and can be used to generate a
list of all the tiles that are represented by all the
TileTemplates (and Tiles) it contains.
"""
##############################################################################
## Exceptions
##############################################################################
class TAMError(Exception):
"""Base class for exceptions produced by the TAM library."""
pass
class TAMImmediateError(TAMError):
'''Immediate errors are those that ought to result in an excepton immediately
being raised, since there is no valid reason to want the error to persist
even temporarily.'''
pass
class TAMLazyError(TAMError):
'''Lazy errors are those that we allow the user to make temporarily, which will
prevent the tiles from being generated if TileSetTemplate.createTiles is
called, but which otherwise will be allowed to persist as they construct the
TileSetTemplate (for instance, in a GUI), so that the errors may be visually
reported to the user without stopping them from continuing to work.'''
pass
class ErrorListError(TAMError):
"""Raised to indicate that one or more errors occurred when executing
TileSetTemplate.createTiles. It can be queried to retrieve that list."""
def __init__(self, errorList):
self.errorList = errorList
def errors(self):
return self.errorList
class DuplicateSignalNameConflictingValuesError(TAMError):
'''JP - Raised to indicated that two different multisignals that wish to be combined must have signals with the same value to be joined'''
def __init__(self, signal_name, multisignal1, multisignal1value, multisignal2, multisignal2value):
self.signal_name = signal_name
self.multisignal1 = multisignal1
self.multisignal1value = multisignal1value
self.multisignal2 = multisignal2
self.multisignal2value = multisignal2value
def __str__(self):
return 'signal {} contained in multisignal {} with value {} and multisignal {} with value {}; must be exactly equal to {} to combine these Multisignals'.format(self.signal_name,
self.multisignal1,
self.multisignal1value,
self.multisignal2,
self.multisignal2value)
# Immediate errors are those that ought to result in an excepton immediately
# being raised, since there is no valid reason to want the error to persist
# even temporarily.
# Lazy errors are those that we allow the user to make temporarily, which will
# prevent the tiles from being generated if TileSetTemplate.createTiles is
# called, but which otherwise will be allowed to persist as they construct the
# TileSetTemplate (for instance, in a GUI), so that the errors may be visually
# reported to the user without stopping them from continuing to work.
# immediate
class NonexistentTransitionError(TAMError):
"""Raised to indicate that a transition attempted to be removed does not exist."""
"""JP- TODO: add the Tile Template to which the user is trying to remove the transition for?"""
def __init__(self, outputNamesGiven, transitions):
self.outputNamesGiven = outputNamesGiven
self.transitions = transitions
def __str__(self):
return '{0} is not a set of outputs defining any transition in the list {1}'.format(self.outputNamesGiven, self.transitions)
# immediate when calling create or restrict on a MultisignalType
# lazy when input signal names or output signal names given with transition are invalid
class ExisitngParrentError(TAMError):
def __init__(self,child,type,name,parent):
self.child = child
self.parent = parent
def __str__(self):
return "the child: {}, which is of type: {}, has sough to be added to: {} already has the parent: {}".format(self.child, self.type,self.name, self.parent)
class SignalInvalidNameError(TAMError):
"""Raised to indicate an invalid signal name."""
def __init__(self, invalidName, validNames):
self.invalidName = invalidName
self.validNames = validNames
def __str__(self):
return '"{0}" is an invalid signal name; valid signal names are {1}'.format(
self.invalidName, self.validNames)
class tooManyInitatiorPortsError(TAMError):
''' raised when there are the strength of initiator ports attached to a module is greater than 2 '''
def __init__(self, moduleName):
self.moduleName = moduleName
def __str__(self):
return "the strength of initiator ports in the module {} exceeds 2".format(self.moduleName)
class portParentConfigurationNotMet(TAMError):
def __init__(self,tileTemplate):
self.tileTemplate = tileTemplate
def __str__(self):
return "The list of ports contained in the portNeihborhod of the tileTemplate: {} do not meet the required parent configuration".format(self.tileTemplate)
# immediate when joining or rejoining and MultisignalType objects with overlapping signal names are used
class SignalDuplicateNameError(TAMError):
"""Raised to indicate a duplicate signal name."""
def __init__(self, name):
self.name = name
def __str__(self):
return '"{0}" appears more than once as a signal name'.format(self.name)
# lazy when transition applied for specific TileTemplate
class DuplicateOutputSignalValues(TAMError):
def __init__(self, signal_name, olddirection, newdirection,tileTemplate):
self.signal_name = signal_name
self.olddirection = olddirection
self.newdirection = newdirection
self.tileTemplate = tileTemplate
def __str__(self):
return "same output multisignal {} for tile template: {} is used in two different directions: {} and {}".format(self.signal_name,self.tileTemplate, self.olddirection, self.newdirection)
# immediate when restricting multisignaltype;
# lazy when transition outputs invalid value
class SignalInvalidValueError(TAMError):
"""Raised to indicate an invalid signal value."""
def __init__(self, signalName, invalidVal, validVals):
self.signalName = signalName
self.invalidVal = invalidVal
self.validVals = validVals
'''JP - added __str__ function as it was not listed before'''
def __str__(self):
return 'The signal: {0} contains the invalid value: {1}. Valid values are {2}'.format(self.signalname,self.invalidVal,self.validVals)
# immediate when creating or restricting multisignal type
class SignalDuplicateValueError(TAMError):
"""Raised to indicate a duplicate signal name."""
'''JP - TODO: List which duplicate signal valid values are contained?'''
def __init__(self, vals):
self.vals = vals
def __str__(self):
return '"{0}" contains duplicate signal valid values'.format(self.vals)
# immediate; should result in prompting to see if user wants to update other joins
class SignalMissingNameError(TAMError):
"""Raised to indicate a join is missing a signal name for its neighborhood."""
def __init__(self, neighborhood, join, signalType):
self.signalType = signalType
self.neighborhood = neighborhood
self.join = join
def __str__(self):
return 'join {0} is missing signal type {1} required for neighborhood {2}'.format(self.join, self.signalType, self.join)
# immediate
class InputOutputSideConflictError(TAMError):
"""Raised to indicate that a single tile template side has been specified as both an input and an output."""
'''JP - TODO: add the join which is acting as an input and the join acting as the output to make debugging easier'''
def __init__(self, tileTemplate, direction):
self.tileTemplate = tileTemplate
self.direction = direction
def __str__(self):
return 'tile template {0} cannot have side {1} as both an input and output'.format(self.tileTemplate, self.direction)
# lazy
class StrengthError(TAMError):
"""Raised to indicate that a join has the incorrect strength, as calculated by the number of input sides on the
TileTemplate on the output direction of the join."""
def __init__(self, tileTemplate, join):
self.tileTemplate = tileTemplate
self.join = join
def __str__(self):
properStrength = 1 if self.tileTemplate.numInputSides() == 2 else 2
return 'tile template {0} has {1} input sides and must therefore have strength {2} on join {3}'.format(self.tileTemplate, self.tileTemplate.numInputSides(), properStrength, self.join)
# lazy
class TooManyInputSidesError(TAMError):
"""Raised to indicate that a tile template has too many input sides."""
'''JP - TODO: add which sides act as input sides '''
def __init__(self, tileTemplate):
self.tileTemplate = tileTemplate
def __str__(self):
return 'tile template {0} has {1} input sides but must have at most 2'.format(self.tileTemplate, self.tileTemplate.numInputSides())
# lazy
class OutputNotComputedError(TAMError):
"""Raised to indicate that one of the output signals of a TileTemplate was not computed by any transition."""
def __init__(self, tileTemplate,outputSignal):
self.tileTemplate = tileTemplate
self.outputSignal = outputSignal
def __str__(self):
return 'the output signal {0} for the tile template: {1} was not computed by any transition'.format(self.outputSignal,self.tileTemplate)
# lazy
class OutputMultiplyComputedError(TAMError):
"""Raised to indicate that one of the output signals of a TileTemplate has more than one transition computing it."""
def __init__(self,outputSignal,tileTemplate,transitions):
self.outputSignal = outputSignal
self.tileTemplate = tileTemplate
self.transitions = transitions
def __str__(self):
return 'the output signal: {0} for the tile template {1} has the following transition computing it {2}, must only be computed by one'.format(self.outputSignal,self.tileTemplate,self.transitions)
# lazy because it is only dynamically detectable by calling function with all possible inputs (not all of
# which may have been specified yet if there are remaining TileTemplates left to wire as inputs to this one)
class OutputArityError(TAMError):
"""Raised to indicate that an output tuple from a transition does not have the same length as the list
of output signal names."""
def __init__(self,outputTuple,transition,TileTemplate,outputSigNames):
self.outputTuple = outputTuple
self.transition = transition
self.TileTemplate = TileTemplate
self.outputSigNames = outputSigNames
def __str__(self):
return 'the output tuple: {0} computed by the transition {1} for the tile template {2} must have the same length as the list of output signal names: {3}'.format(self.outputTuple,self.transition,self.TileTemplate,self.outputSigNames)
class TileTemplateFromObjNotConfomingtoModuleConfigurationError(TAMError):
def __init__(self,fromObj,toObj):
self.fromObj = fromObj.parent
self.toObj = toObj.parent
def __str__(self):
return 'the parents of the fromObj,tileTemplate:{0} and the toObj,{1} do not fit an allowed configuration for modules'.format(self.fromObj,self.toObj)
class PortFromObjNotConformingtoModuleConfigurationError():
def __init__(self,fromObj,toObj):
self.fromObj = fromObj.parent
self.toObj = toObj.parent
def __str__(self):
return 'the parents of the fromObj,port:{0} and the toObj,{1} do not fit an allowed configuration for modules'.format(self.fromObj,self.toObj)
# immediate because immediately detectable from signature of function
class InputArityError(TAMError):
"""Raised to indicate that the arity of a transition does not have the same length as the list
of input signal names. If the function takes varargs, then it ensures that the number of input signal
names specified is at least the number of non-varargs given."""
def __init__(self,transition,tileTemplate,inputSignalNames):
self.transition = transition
self.tileTemplate = tileTemplate
self.inputSignalNames = inputSignalNames
def __str__(self):
return 'The arity of the transition: {0} for tile template {1} must have the same length as the input signal names: {2}'.format(self.transition,self.tileTemplate,self.inputSignalNames)
# lazy
class InvalidChooserTileTemplateError(TAMError):
"""Raised to indicate that a tile template or list of tile templates is not an element/subset of the TileTemplates
that could actually be the output."""
def __init__(self,tileTemplates,outputTileTemplates,chooserFunction):
self.tileTemplates = tileTemplates
self.outputTileTemplates = outputTileTemplates
self.chooserFunction = chooserFunction
def __str__(self):
return 'The tile template(s): {0} are not an element of the tile templates {1} that can be used as an output for the chooser function {2}'.format(self.tileTemplates,self.outputTileTemplates,self.chooserFunction)
# lazy
class MissingChooserError(TAMError):
"""Raised to indicate that the user needs to specify which of multiple TileTemplates to choose for a
given input multisignal, when more than one could be the output as computed by the library."""
def __init__(self,tileTemplates,multisignal):
self.tileTemplates = tileTemplates
self.multisignal = multisignal
def __str__(self):
return 'The tile templates: {0} need a choser function for the input multisignal {1}'.format(self.tileTemplates,self.multisignal)
# lazy
class ConflictingChooserError(TAMError):
"""Raised to indicate that there are multiple chooser functions defined for a chooser set on
a given input multisignal."""
def __init__(self,tileTemplates,chooserFunctions,inputMultisignal):
self.tileTemplates = tileTemplates
self.chooserFunction = chooserFunctions
self.inputMultisignal = inputMultisignal
def __str__(self):
return 'There a multiple conflicting chooser functions: {0} for the input multisignal: {1} contained within the tile templates {2}'.format(self.chooserFunction,self.inputMultisignal,self.tileTemplates)
class portsNotConnectedtoTileTemplatesOutput(TAMError):
def __init__(self, unvistedPortsList):
self.unvisistedPortsList = unvistedPortsList
def __str__(self):
return "The ports within the following list: {0} are not connected to a tile templates output side".format(self.unvisistedPortsList)
class portsNotConnectedToTileTemplatesInput(TAMError):
def __init__(self, unvistedPortsList):
self.unvisistedPortsList = unvistedPortsList
def __str__(self):
return "The ports within the following list: {0} are not connected to a tile templates input side".format(self.unvisistedPortsList)
#when there are multiple from objects in a neighborhood and a port is contained in this list
class fromObjectsPortError(TAMError):
def __init__(self, neighborhood):
self.neighborhood = neighborhood
def __str__(self):
return "The from objects contained within the joins in the neighborhood: {0} have a port when their are multiple objects".format(self.neighborhood)
#when there are multiple to objects in a neighborhood and a port is contained in this list
class toObjectsPortError(TAMError):
def __init__(self, neighborhood):
self.neighborhood = neighborhood
def __str__(self):
return "The to objects contained within the joins in the neighborhood: {0} have a port when their are multiple objects".format(self.neighborhood)
# immediate
class NameDifferenceError(TAMError):
"""Raised to indicate that two multisignals cannot be valueUnion'ed because their signal names are not identical."""
'''JP - TODO: List for which multisingals these signal names belong to'''
def __init__(self, names1, names2):
self.names1 = names1
self.names2 = names2
def __str__(self):
return 'list of names {0} must be identical to {1}'.format(self.names1, self.names2)
# immediate
class NameOverlapError(TAMError):
"""Raised to indicate that two multisignals cannot be nameUnion'ed because their signal names are not disjoint."""
'''JP - TODO: List for which multisingals these signal names belong to'''
def __init__(self, names1, names2):
self.names1 = names1
self.names2 = names2
def __str__(self):
return 'list of names {0} must be disjoint from to {1}'.format(self.names1, self.names2)
class NonexistentChooserSetError(TAMError):
"""Raised to indicate that a chooserSet consisting of the set of tiles does not exist."""
def __init__(self, tileSet):
self.tileSet = tileSet
def __str__(self):
nameList = list([tile.name for tile in self.tileSet])
return 'There is no chooser set consisting of this list of tile templates: {0}'.format(nameList)
class InputSideMismatchError(TAMError):
"""Raised to indicate that a tileTemplate does not have input sides matching the chooserSet it's being added to"""
def __init__(self, tileTemplate, tileTemplateInDirs, chooserSetInDirs):
self.tileTemplate = tileTemplate
self.tileTemplateInDirs = tileTemplateInDirs
self.chooserSetInDirs = chooserSetInDirs
def __str__(self):
return 'TileTemplate {0} has input side list {1} which does not match the list for the chooserSet {2}'.format(self.tileTemplate.name, self.tileTemplateInDirs, self.chooserSetInDirs)
##############################################################################
## These aren't really tile related, but they are useful.
##############################################################################
# allow multi-arity identity; e.g. identity(3,4,5) = (3,4,5)
def identity(*x):
"""Return x, returning a tuple for 0-arity or multi-arity calls.
e.g. identity(3,'a',5) = (3, 'a', 5)"""
if len(x) == 1:
return x[0]
else:
return x
def reprByProps(obj):
"""Create a string representing obj using the properties.
Assumes each property can be specified in the constructor.
For instance, an object of type moduleName.A with properties self.a = 123
and self.b = 'abc' would have the string
"moduleName.A(a=123,b='abc')" returned.
Except for the top-level call, which always performs this pairing, it uses
the condition of whether obj defines __repr__ to decide whether to use
repr(val) to represent an attribute value, or to recurse into the property
and use reprByProps to represent that value also. The idea is that we
want to use the object's way of representing itself literally if it has
defined one, but since we use this function to make defining __repr__
easier (and not to replace it), we don't want to perform this check on obj
itself, which will have __repr__ defined but will rely on reprByProps
to define it.
WARNING: this should only be used to help define __repr__ in the following
way:
def __repr__(self):
return tam.reprByProps(self)
Other than that one use case, it should not be called or used directly.
This is not a serialization mechanism; it will not handle loops in the
object graph. It is just a way to make creating "standard" __repr__
methods easier.
"""
className = obj.__class__.__name__
nameValList = ', '.join(
"{0}={1}".format(name, repr(val) if hasattr(val, '__repr__') else reprByProps(val))
for name, val in sorted(obj.__dict__.items())
)
# this code was an attempt to use only those properties that the constructor
# specifies through arguments, but it has some issues that I forget
# nameValList = ', '.join(
# "{0}={1}".format(name, repr(obj.__dict__[name]) if hasattr(obj.__dict__[name],'__repr__') else reprByProps(obj.__dict__[name]))
# for name in sorted(inspect.getargspec(obj.__init__).args)
# if name != 'self'
# )
moduleName = obj.__class__.__module__
return '{moduleName}.{className}({nameValList})'.format(
moduleName=moduleName, className=className, nameValList=nameValList)
# taken from http://code.activestate.com/recipes/413486/
def Enum(*names):
assert names, "Empty enums are not supported" # <- Don't like empty enums? Uncomment!
class EnumClass(object):
__slots__ = tuple(names) + ('__dict__',)
def __iter__(self): return iter(constants)
def __len__(self): return len(constants)
def __getitem__(self, i): return constants[i]
def __repr__(self): return 'Enum' + str(names)
def __str__(self): return 'enum ' + str(constants)
class EnumValue(object):
__slots__ = ('__value')
def __init__(self, value): self.__value = value
Value = property(lambda self: self.__value)
EnumType = property(lambda self: EnumType)
def __hash__(self): return hash(self.__value)
def __cmp__(self, other):
# C fans might want to remove the following assertion
# to make all enums comparable by ordinal value {;))
assert self.EnumType is other.EnumType, "Only values from the same enum are comparable"
return cmp(self.__value, other.__value)
def __invert__(self): return constants[maximum - self.__value]
def __nonzero__(self): return bool(self.__value)
def __repr__(self): return str(names[self.__value])
maximum = len(names) - 1
constants = [None] * len(names)
for i, each in enumerate(names):
val = EnumValue(i)
setattr(EnumClass, each, val)
constants[i] = val
constants = tuple(constants)
EnumType = EnumClass()
return EnumType
Direction = Enum('East', 'North', 'South', 'West', 'Nondet')
def oppositeDirection(self):
"""Gets opposite direction of self; raises ValueError if self is Nondet."""
if self == Direction.East: return Direction.West
if self == Direction.West: return Direction.East
if self == Direction.North: return Direction.South
if self == Direction.South: return Direction.North
if self == Direction.Nondet: raise ValueError('there is no opposite of Direction.Nondet')
def oppositeDirectionHorizontal(self):
"""Gets opposite direction of West and East Directions; raises ValueError if self is Nondet."""
if self == Direction.East: return Direction.West
if self == Direction.West: return Direction.East
if self == Direction.North: return Direction.North
if self == Direction.South: return Direction.South
if self == Direction.Nondet: raise ValueError('there is no opposite of Direction.Nondet')
def oppositeDirectionVertical(self):
"""Gets opposite direction of North and South Directions; raises ValueError if self is Nondet."""
if self == Direction.East: return Direction.East
if self == Direction.West: return Direction.West
if self == Direction.North: return Direction.South
if self == Direction.South: return Direction.North
if self == Direction.Nondet: raise ValueError('there is no opposite of Direction.Nondet')
def directionShortName(self):
if self == Direction.East: return 'E'
if self == Direction.West: return 'W'
if self == Direction.North: return 'N'
if self == Direction.South: return 'S'
if self == Direction.Nondet: return 'Nondet'
def rotateDirectionClockwise90(self):
if self == Direction.East: return Direction.South
if self == Direction.West: return Direction.North
if self == Direction.North: return Direction.East
if self == Direction.South: return Direction.West
if self == Direction.Nondet: raise ValueError('there is no opposite of Direction.Nondet')
def rotateDirectionCounterclockwise90(self):
if self == Direction.East: return Direction.North
if self == Direction.West: return Direction.South
if self == Direction.North: return Direction.West
if self == Direction.South: return Direction.East
if self == Direction.Nondet: raise ValueError('there is no opposite of Direction.Nondet')
N = Direction.North
S = Direction.South
E = Direction.East
W = Direction.West
NONDET = Direction.Nondet
# Usage of Enum:
#
#if __name__ == '__main__':
# print('\n*** Enum Demo ***')
# print('--- Days of week ---')
# Days = Enum('Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su')
# print(Days)
# print(Days.Mo)
# print(Days.Fr)
# print(Days.Mo < Days.Fr)
# print(list(Days))
# for each in Days:
# print('Day:', each)
# print('--- Yes/No ---')
# Confirmation = Enum('No', 'Yes')
# answer = Confirmation.No
# print('Your answer is not {0}'.format(not answer))
##############################################################################
## Tile stuff below here.
##############################################################################
class Tile(object):
"""
Represents a tile type, with the properties that are recognized by
Matt Patitz's TAS application.
"""
def __init__(self, name, label='',
tilecolor='white', textcolor='black', concentration=1,
northglue=('', 0), southglue=('', 0),
westglue=('', 0), eastglue=('', 0)):
"""
Each glue is a tuple (label, strength).
"""
self.name = name
self.label = label
self.tilecolor = tilecolor
self.textcolor = textcolor
self.concentration = concentration
self.northglue = northglue
self.southglue = southglue
self.westglue = westglue
self.eastglue = eastglue
self.parent = None
def clone(self):
newName = self.name
newLabel = self.label
newTileColor = self.tilecolor
newTextColor = self.textcolor
newConcentration = self.concentration
newNorthGlue = self.northglue
newSouthGlue = self.southglue
newWestGlue = self.westglue
newEastGlue = self.eastglue
newTile = Tile(newName, newLabel, newTextColor, newTileColor, newConcentration, newNorthGlue, newSouthGlue, newWestGlue, newEastGlue)
return newTile
def __eq__(self, other):
return all(self.__dict__[propname] == other.__dict__[propname]
for propname in self.__dict__.keys())
def __ne__(self, other):
return not (self == other)
def tdsFormat(self):
"""
Returns a string in the format recognized by the TAS application,
suitable for writing to a .TDS file.
"""
return """TILENAME %s
LABEL %s
TILECOLOR %s
TEXTCOLOR %s
CONCENTRATION %s
NORTHBIND %s
SOUTHBIND %s
WESTBIND %s
EASTBIND %s
NORTHLABEL %s
SOUTHLABEL %s
WESTLABEL %s
EASTLABEL %s
CREATE""" % (self.name, self.label, self.tilecolor, self.textcolor,
self.concentration,
self.northglue[1], self.southglue[1],
self.westglue[1], self.eastglue[1],
self.northglue[0], self.southglue[0],
self.westglue[0], self.eastglue[0])
def __str__(self):
return self.tdsFormat()
def __repr__(self):
return reprByProps(self)
def reflectNS(self):
"""Reflect tile north/south."""
return Tile(
name=self.name,
label=self.label,
tilecolor=self.tilecolor,
textcolor=self.textcolor,
concentration=self.concentration,
northglue=self.southglue,
southglue=self.northglue,
westglue=self.westglue,
eastglue=self.eastglue
)
def reflectEW(self):
"""Reflect tile east/west."""
return Tile(
name=self.name,
label=self.label,
tilecolor=self.tilecolor,
textcolor=self.textcolor,
concentration=self.concentration,
northglue=self.northglue,
southglue=self.southglue,
westglue=self.eastglue,
eastglue=self.westglue
)
def rotateLeft(self):
"""Rotate tile left."""
return Tile(
name=self.name,
label=self.label,
tilecolor=self.tilecolor,
textcolor=self.textcolor,
concentration=self.concentration,
northglue=self.eastglue,
southglue=self.westglue,
westglue=self.northglue,
eastglue=self.southglue
)
def rotateRight(self):
"""Rotate tile left."""
return Tile(
name=self.name,
label=self.label,
tilecolor=self.tilecolor,
textcolor=self.textcolor,
concentration=self.concentration,
northglue=self.westglue,
southglue=self.eastglue,
westglue=self.southglue,
eastglue=self.northglue
)
def rotate180(self):
"""Rotate tile 180 degrees.
Note that this is different from reflecting since sides along both axes
are swapped."""
return Tile(
name=self.name,
label=self.label,
tilecolor=self.tilecolor,
textcolor=self.textcolor,
concentration=self.concentration,
northglue=self.southglue,
southglue=self.northglue,
westglue=self.eastglue,
eastglue=self.westglue
)
class TileSystem:
"""
Represents a tile assembly system. This is a set of tile types, together with
all other contextual information (such as the seed assembly and temperature)
needed to simulate the self assembly of the tiles.
"""
def __init__(self, name, tileTypes=[], seedAssembly={}):
"""Create a tile assembly system with the given name, no tile types,
and an empty seed assembly."""
self.name = name # name of the tile assembly system
self.tileTypes = tileTypes # set of tile types
self.seedAssembly = seedAssembly # maps position in Z^3 to tile type
def tdpFormat(self):
"""TDP format of this tile assembly system.
Return a string representing the tile assembly system's .TDP file, suitable
for creating a file that can be read by the TAS application.
"""
seedTileNamesAndPositions = (
"{0} {1}".format(self.seedAssembly[pos].name, ' '.join(map(str, pos)))
for pos in self.seedAssembly.keys())
return self.name + ".tds\n" + '\n'.join(seedTileNamesAndPositions)
def tdsFormat(self):
"""TDS format of tile set of this tile assembly system.
Return a string representing all the tiles in the format recognized
by the TAS application, suitable for writing to a .TDS file.
"""
return '\n\n'.join(map(Tile.tdsFormat, self.tileTypes))
def __str__(self):
return self.tdpFormat()
def __repr__(self):
return reprByProps(self)
def addTileType(self, tile):
"""Add tile to this tile assembly system's set of tiles."""
#self.tileTypes.add(tile)
#self.tileTypes.append(tile)
self.addTileTypes([tile])
def addTileTypes(self, tiles):
"""Add tiles to this tile assembly system's set of tiles."""
#self.tileTypes |= set(tiles)
#existingNames = {tile.name for tile in self.tileTypes} #PYTHON3
existingNames = set([tile.name for tile in self.tileTypes])
for name in (tile.name for tile in tiles):
if name in existingNames:
raise ValueError('tile with name {0} already exists'.format(name))
existingNames.add(name)
self.tileTypes += tiles
def addToSeedAssembly(self, pos, tile):
"""Add tile to 3D position represented by pos."""
self.seedAssembly[pos] = tile
def writeToFiles(self, outFilename):
"""Write out tile assembly system to files for use by TAS program.
Write the given tileSystem to the files named <outFilename>.tdp
(for tile system) and <outFilename>.tds (for tile set), in the format
recognized by the TAS application.
"""
tds = open(outFilename + '.tds', 'w')
tds.write(self.tdsFormat())
tds.close()
tdp = open(outFilename + '.tdp', 'w')
tdp.write(self.tdpFormat())
tdp.close()
def isSequence(seq):
# return type(seq) in (tuple, list)
return isinstance(seq, tuple) or isinstance(seq, list)
class MultisignalType(object):
"""Tuple of signal types.
Each signal type is a pair (name, validValues), where name is a string
representing the name of the signal and valid values is a list of strings
representing the values the signal can take."""
def __init__(self, verbose=VERBOSE_SIGNALS, **signalTypeDict):
self.signalTypeDict = dict(signalTypeDict)
for name, values in self.signalTypeDict.items():
if not isSequence(values):
self.signalTypeDict[name] = (values,)
else:
self.signalTypeDict[name] = tuple(values)
self.updateSignalTypeList()
self.verbose = verbose
for validVals in self.signalTypeDict.values():
if len(set(validVals)) != len(validVals):
raise SignalDuplicateValueError(validVals)
def noChoiceMultisignal(self):
"""Returns the multisignal consisting of those signals which have only one valid value"""
ret = Multisignal([(signalName, signalValues[0]) for (signalName,signalValues) in self.signalTypeDict.items()
if len(signalValues) == 1])
return ret
def __len__(self):
return len(self.signalTypeDict)
def names(self):
"""Return list of signal names."""
return [name for (name, values) in self.signalTypeList]
@classmethod
def empty(verbose=VERBOSE_SIGNALS):
return MultisignalType(**dict())
def clone(self):
newDict = dict(self.signalTypeDict)
newMultisignalType = MultisignalType(self.verbose, **newDict)
return newMultisignalType
# return MultisignalType(self.verbose, **dict(self.signalTypeDict))
def updateSignalTypeList(self):
self.signalTypeList = list(self.signalTypeDict.items())
self.signalTypeList.sort(key=lambda (name, vals): name)
def addSignalTypes(self, **newSignalTypeDict):
"""Add new signal types."""
for validVals in newSignalTypeDict.values():
if len(set(validVals)) != len(validVals):
raise SignalDuplicateValueError(validVals)
for name, validVals in newSignalTypeDict.items():
if name in self.signalTypeDict:
raise SignalDuplicateNameError(name)
self.signalTypeDict[name] = tuple(validVals)
self.signalTypeList.append((name, validVals))
self.signalTypeList.sort(key=lambda (name, vals): name)
def removeSignalTypes(self, *names):
"""Remove signal types with the given names."""
for name in names:
if name not in self.signalTypeDict:
raise SignalInvalidNameError(name, self.signalTypeDict.keys())
del self.signalTypeDict[name]
self.signalTypeList = list(self.signalTypeDict.items())
self.signalTypeList.sort(key=lambda (name, vals): name)
def create(self, **nameValDict):
"""Create a multisignal with the specified mapping of names to values."""
for name, val in nameValDict.items():
if name not in self.signalTypeDict:
raise SignalInvalidNameError(name, self.signalTypeDict.keys())
validVals = self.signalTypeDict[name]
if val not in validVals:
raise SignalInvalidValueError(name, val, validVals)
nameValList = [(name, nameValDict[name]) for (name, validVals) in self.signalTypeList]
return Multisignal(nameValList, self.verbose)
def createNoNameCheck(self, **nameValDict):
"""Create a multisignal with the specified mapping of names to values.
The names are not checked, so the given nameValDict could contain
more or fewer signal names than this MultisignalType represents."""
for name, val in nameValDict.items():
if name in self.signalTypeDict:
validVals = self.signalTypeDict[name]
if val not in validVals:
raise SignalInvalidValueError(name, val, validVals)
nameValList = [(name, nameValDict[name]) for (name, validVals) in self.signalTypeList
if name in nameValDict]
return Multisignal(nameValList, self.verbose)
def restrict(self, **nameValuesDict):
"""Create a MultisignalType with the given restrictions of values.
Assume for any unspecified signal name that all original values are kept."""
for name, values in nameValuesDict.items():
if not isSequence(values):
nameValuesDict[name] = values = (values,)
if name not in self.signalTypeDict:
raise SignalInvalidNameError(name, self.signalTypeDict.keys())
validVals = self.signalTypeDict[name]
for value in values:
if value not in validVals:
raise SignalInvalidValueError(name, value, validVals)
for name, values in self.signalTypeDict.items():
if name not in nameValuesDict:
nameValuesDict[name] = values
return MultisignalType(verbose=self.verbose, **nameValuesDict)
def __getitem__(self, name):
"""Allows multisignals to index values by name; i.e. ms['bit'] == ['0','1']"""
return self.signalTypeDict[name]
def __setitem__(self, name, validVals):
"""Allows multisignals to alter valid values by name; i.e. ms['bit'] = ['0','1']"""
self.signalTypeDict[name] = tuple(validVals)
self.updateSignalTypeList()
def __delitem__(self, name):
"""Allows multisignals to delete values by name; i.e. del(ms['bit'])"""
del(self.signalTypeDict[name])
self.signalTypeList = [(sigName, validVals)
for (sigName, validVals) in self.signalTypeList
if name != sigName]
def __str__(self):
return ';'.join('{0}:{1}'.format(name, validVals) for name, validVals in self.signalTypeList)
def __repr__(self):
return (("tam.MultisignalType(verbose={v}, "
+ "**{signalTypeDict})").format(
signalTypeDict=self.signalTypeDict, v=self.verbose))
def __iter__(self):
return self.multisignals()
def multisignals(self):
"""Enumerate all possible multisignals represented by this type."""
lengths = [len(values) for name, values in self.signalTypeList]
curSignalIndices = [0] * len(self.signalTypeList)
numGlues = 1
for length in lengths:
numGlues *= length
for glueIdx in range(numGlues):
multisignalDict = dict((signalName, signalsList[i]) for i, (signalName, signalsList)
in zip(curSignalIndices, self.signalTypeList))
yield self.create(**multisignalDict)
# increment to next glue using carry addition
for i in range(len(self.signalTypeList)):
curSignalIndices[i] += 1
# update next index if necessary, otherwise leave rest alone
if curSignalIndices[i] == lengths[i]:
curSignalIndices[i] = 0
else:
break
def __eq__(self, other):
if set(self.signalTypeDict.keys()) != set(other.signalTypeDict.keys()):
return False
for ((name, values), (otherName, otherValues)) in zip(self.signalTypeList, other.signalTypeList):
if values != otherValues:
return False
return True
def __ne__(self, other):
return not (self == other)
def __hash__(self):
return hash(tuple(self.signalTypeList))
def nameUnion(self, other):
"""Combine signal types with disjoint names to be the union of the signal types.
For instance,
carryMst = tam.MultisignalType(carry=['n','c'])
borrowMst = tam.MultisignalType(borrow=['nb','b'])
carryBorrowMst = carryMst.nameUnion(borrowMst)
creates a MultisignalType that can represent a carry and a borrow
simultaneously. They must also have disjoint signal names.
"""
for name, validVals in self.signalTypeDict.items():
if name in other.signalTypeDict:
raise NameOverlapError(list(self.signalTypeDict.keys()), list(other.signalTypeDict.keys()))
signalTypeDict = dict(self.signalTypeDict)
signalTypeDict.update(other.signalTypeDict)
newMst = MultisignalType(verbose=self.verbose, **signalTypeDict)
return newMst
def valueUnion(self, other):
"""Combine signal types with the same names by taking the union of their
values for each shared name.
For instance,
carryMst = tam.MultisignalType(carry=['n','c'])
borrowMst = tam.MultisignalType(carry=['n','b'])
carryBorrowMst = carryMst.valueUnion(borrowMst)
creates a MultisignalType that can represent a carry with values
['n','b','c']. They must also have exactly the same signal names.
"""
if set(self.signalTypeDict.keys()) != set(other.signalTypeDict.keys()):
raise NameDifferenceError(list(self.signalTypeDict.keys()), list(other.signalTypeDict.keys()))
signalTypeDict = dict(self.signalTypeDict)
for name in signalTypeDict:
signalTypeDict[name] = list(set(signalTypeDict[name] + other.signalTypeDict[name]))
newMst = MultisignalType(verbose=self.verbose, **signalTypeDict)
return newMst
def isValidMultisignal(self, ms):
"""Indicates whether ms is a Multisignal that this MultisignalType produces."""
if set(ms.nameValDict.keys()) != set(self.signalTypeDict.keys()):