Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

Environment to perform calculations of equivariant vector bundles on homogeneous varieties

1842 views
License: GPL3
ubuntu2204
1
from typing import Iterator
2
3
4
5
class Young_Structure ( object ) :
6
7
def __eq__ ( self , other ) -> bool :
8
if isinstance( other , self.__class__ ) : return self.Remove_Vanishing_Values().__repr__() == other.Remove_Vanishing_Values().__repr__()
9
else : return False
10
11
12
def __getitem__ ( self , Coordinate :tuple[ int ] ) -> int :
13
"""Returns the value of a given coordinate."""
14
assert isinstance( Coordinate , tuple ) , \
15
'The input for `Coordinate` need to be a tuple.'
16
assert len(Coordinate) == self.Rank() , \
17
'The length of `Coordinate` need to coincide with the rank of `self`.'
18
for CoordinateCounter , CoordinateEntry in enumerate( Coordinate , start=1 ) :
19
assert isinstance( CoordinateEntry , ( int , Integer ) ) , \
20
'The '+str(CoordinateCounter)+'-th entry of the coordinate need to be an integer.'
21
if Coordinate in self.Data().keys() : return self.Data()[Coordinate]
22
else : return 0
23
24
25
def __ge__ ( self , other ) -> bool :
26
"""Compares Young structures with respect to the >=-operator."""
27
return other <= self
28
29
30
def __gt__ ( self , other ) -> bool :
31
"""Compares Young structures with respect to the >-operator."""
32
return other < self
33
34
35
def __init__( self , Data :dict , Ranges :list ) -> None :
36
"""
37
Initialize a Young structure.
38
39
INPUT:
40
- ``Data`` -- Dictionary ;
41
- ``Ranges`` -- List of 2-tuples ;
42
43
OUTPUT: None.
44
"""
45
assert isinstance( Ranges , list ) , \
46
'The input for `Ranges` need to be a list.'
47
for RangeCounter , Entry in enumerate( Ranges , start=1 ) :
48
assert isinstance( Entry , tuple ) , \
49
'The '+str(RangeCounter)+'-th range need to be a tuple.'
50
assert len(Entry) == 2 , \
51
'The '+str(RangeCounter)+'-th range need to be of length 2.'
52
Minimum , Maximum = Entry
53
assert isinstance( Minimum , ( int , Integer ) ) , \
54
'The minimum entry of the '+str(RangeCounter)+'-th range need to be an integer.'
55
assert isinstance( Maximum , ( int , Integer ) ) , \
56
'The maximum entry of the '+str(RangeCounter)+'-th range need to be an integer.'
57
assert Minimum <= Maximum , \
58
'For the '+str(RangeCounter)+'-th range, the minimum entry need to smaller or equal to the maximum entry.'
59
self._Ranges = Ranges
60
61
self._Rank = len(Ranges)
62
63
assert isinstance( Data , dict ) , \
64
'The input for `Data` need to be dictionary.'
65
Adjusted_Data = dict({})
66
for ValueCounter , ( Coordinate , Value ) in enumerate( Data.items() , start=1 ) :
67
assert isinstance( Coordinate , tuple ) , \
68
'The '+str(ValueCounter)+'-th coordinates need to be a tuple.'
69
assert len(Coordinate) <= self._Rank , \
70
'The '+str(ValueCounter)+'-th coordinates need to be of length <= '+str(self._Rank)+'.'
71
Coordinate += tuple( ( self._Rank-len(Coordinate) )*[0] )
72
if Coordinate in Adjusted_Data.keys() : raise ValueError('CONFLICT! There are two values associated the same coordinate (after adjusting).')
73
else : Adjusted_Data.update({ Coordinate : Value })
74
for CoordinateCounter , CoordinateValue in enumerate( Coordinate ) :
75
assert isinstance( CoordinateValue , ( Integer , int ) ) , \
76
'The '+str(CoordinateCounter+1)+'-th entry of the '+str(ValueCounter)+'-th coordinate need to be an integer.'
77
Minimum = self._Ranges[CoordinateCounter][0]
78
assert Minimum <= CoordinateValue , \
79
'The '+str(CoordinateCounter+1)+'-th entry of the '+str(ValueCounter)+'-th coordinate need to >= '+str(Minimum)+'.'
80
Maximum = self._Ranges[CoordinateCounter][1]
81
assert CoordinateValue <= Maximum , \
82
'The '+str(CoordinateCounter+1)+'-th entry of the '+str(ValueCounter)+'-th coordinate need to <= '+str(Maximum)+'.'
83
self._Data = Adjusted_Data
84
85
86
def __iter__ ( self ) -> Iterator[ tuple[ tuple[ int ] , int ] ] :
87
"""Returns an iterator of ``self``."""
88
return ( ( Coordinate , Value ) for Coordinate , Value in self.Data().items() )
89
90
91
def __le__ ( self , other ) -> bool :
92
"""Compares Young structures with respect to the <=-operator."""
93
if self == other : return True
94
elif self < other : return True
95
else : return False
96
97
98
# Synonym for the method `Rank`
99
def __len__( self ) :
100
"""Returns the rank ``self``."""
101
return self.Rank()
102
103
104
def __ne__ ( self , other ) -> bool :
105
return not self == other
106
107
108
def __repr__ ( self ) -> tuple[ dict[ tuple[int] : int ] , list[ tuple[ int , int ] ] ] :
109
"""Returns all attributes which are necessary to initialize the object."""
110
return self.Data() , self.Ranges()
111
112
113
def __str__ ( self ) -> str :
114
"""Returns a description in terms of diagrams (sliced if necessary)."""
115
if self.Rank() < 3 :
116
# Low-dimensional Young structures can be printed.
117
( xMinimum , xMaximum ) , ( yMinimum , yMaximum ) = self.Ranges() + (2-self.Rank())*[ ( 0 , 0 ) ]
118
Width = xMaximum-xMinimum+1
119
Height = yMaximum-yMinimum+1
120
M = matrix( nrows=Height , ncols=Width )
121
if 0 in [ yMinimum .. yMaximum ] : ySubdivision = [ abs(yMinimum) ]
122
else : ySubdivision = None
123
if 0 in [ xMinimum .. xMaximum ] : xSubdivision = [ abs(xMinimum) ]
124
else : xSubdivision = None
125
M.subdivide( ySubdivision , xSubdivision )
126
for Coordinate , Value in self :
127
xCoordinate , yCoordinate = Coordinate + tuple( (2-self.Rank())*[ 0 ] )
128
M[ yCoordinate-yMinimum , xCoordinate-xMinimum ] = Value
129
return str(M)
130
131
else :
132
# Slice down to the first two coordinates x and y.
133
Output = ''
134
x = self.Coordinates()
135
Condition_Factors = []
136
for CoordinateCounter in range( self.Rank()-1 , 1 , -1 ) :
137
Minimum , Maximum = self.Ranges()[CoordinateCounter]
138
Condition_Factors += [ [ x[CoordinateCounter] == Entry for Entry in [ Minimum .. Maximum ] ] ]
139
for Conditions in cartesian_product(Condition_Factors) :
140
YS = self.Locus( *Conditions )
141
for CoordinateCounter in range( YS.Rank()-1 , 1 , -1 ) :
142
YS.Remove_Coordinate(CoordinateCounter)
143
Output += str( Conditions ) + '\n'
144
Output += str( YS ) + '\n'
145
Output += '\n'
146
147
return Output
148
149
150
#ToDo: Test if `Permutation` is the correct instance.
151
def Conjugate ( self , Permutation: "Permutation" ) -> "Young_Structure" :
152
"""Returns a conjugated Young structure."""
153
assert len(Permutation) == self.Rank() , \
154
'The length of the permutation need to coincide with the rank of `self`.'
155
New_Data = { tuple(Permutation.action(list(Coordinate))) : Value for Coordinate , Value in self }
156
New_Ranges = Permutation.action(self.Ranges())
157
return self.__class__( New_Data , New_Ranges )
158
159
160
@staticmethod
161
def Constructor ( *Input:tuple ) -> "Young_Structure" :
162
163
def Get_Empty_Young_Structure ( Ranges :list[ tuple[ int , int ] ] ) -> "Young_Structure" :
164
Data = { tuple(Coordinate) : 0 for Coordinate in cartesian_product([ [ Minimum .. Maximum ]
165
for Minimum , Maximum in Ranges
166
])
167
}
168
return Young_Structure( Data , Ranges )
169
170
def Get_Young_Structure_From_Binary_Description ( String :str ) -> "Young_Structure" :
171
Keep_Running = True
172
while Keep_Running :
173
Keep_Running = False
174
for SubString in [ 'RL' , 'LR' , 'DU' , 'UD' ] :
175
if String.find(SubString) != -1 :
176
String = String.replace( SubString , '' )
177
Keep_Running = True
178
179
xAmplitude = [0]+[ String[:Counter].count('R')-String[:Counter].count('L') for Counter in range(len(String)) ]
180
xRange = ( min(xAmplitude) , max(xAmplitude)-1 )
181
if xRange == (0,-1) : xRange = (0,0)
182
xMinimum , xMaximum = xRange
183
yAmplitude = [0]+[ String[:Counter].count('D')-String[:Counter].count('U') for Counter in range(len(String)) ]
184
yRange = ( min(yAmplitude) , max(yAmplitude)-1 )
185
if yRange == (0,-1) : yRange = (0,0)
186
yMinimum , yMaximum = yRange
187
188
YS = Get_Empty_Young_Structure( [ xRange , yRange ] )
189
Data = YS.Data()
190
Ranges = YS.Ranges()
191
192
xPointer , yPointer = (0,0)
193
for Letter in String :
194
if Letter == 'R' :
195
for yPos in [ yPointer .. yMaximum ] :
196
Data[ (xPointer,yPos) ] += 1
197
xPointer += 1
198
elif Letter == 'L' :
199
xPointer -= 1
200
for yPos in [ yPointer .. yMaximum ] :
201
Data[ (xPointer,yPos) ] -= 1
202
elif Letter == 'D' :
203
yPointer += 1
204
elif Letter == 'U' :
205
yPointer -= 1
206
return Young_Structure( Data , Ranges )
207
208
def Get_Young_Structure_From_Integer ( IntegerValue :int ) -> "Young_Structure" :
209
if IntegerValue == 0 :
210
Data = dict({})
211
Ranges = [ ( 0 , 0 ) ]
212
else :
213
Minimum = min([ 0 , IntegerValue ])
214
Maximum = max([ 0 , IntegerValue ])
215
Data = { (Index,) : 1 for Index in range(Minimum,Maximum) }
216
Ranges = [ ( Minimum , Maximum-1 ) ]
217
return Young_Structure( Data , Ranges )
218
219
def Stacking_Young_Structures ( Stack :dict[ int : "Young_Structure" ] ) -> "Young_Structure" :
220
if 0 < len( Stack.keys() ) :
221
New_Rank = max([0] + [ New_Slice.Rank() for New_Slice in Stack.values() ]) + 1
222
New_Data = dict({})
223
New_Ranges = New_Rank * [ ( +infinity , -infinity ) ]
224
for New_Coordinate , New_Slice in Stack.items() :
225
Filling = tuple( (New_Rank-New_Slice.Rank()-1)*[ 0 ])
226
New_Data.update({ Coordinate + Filling + (New_Coordinate,) : Value for Coordinate , Value in New_Slice })
227
Adjusted_Ranges = [ ( min([ New_Ranges[RangeCounter][0] , Range[0] ]) , max([ New_Ranges[RangeCounter][1] , Range[1] ]) ) for RangeCounter , Range in enumerate( New_Slice.Ranges() ) ]
228
New_Ranges = Adjusted_Ranges
229
New_Ranges += [ ( min(Stack.keys()) , max(Stack.keys()) ) ]
230
else :
231
New_Data = dict({})
232
New_Ranges = []
233
return Young_Structure( New_Data , New_Ranges )
234
235
if len(Input) == 1 :
236
Input = Input[0]
237
if isinstance( Input , Integer ) :
238
return Get_Young_Structure_From_Integer( Input )
239
240
elif isinstance( Input , ( list , tuple ) ) :
241
Stack = dict({})
242
for EntryCounter , Entry in enumerate( Input ) :
243
assert isinstance( Entry , ( int , Integer ) ) , \
244
'If the input is given as list or tuple, then the '+str(EntryCounter+1)+'-th entry need to be an integer.'
245
Stack.update({ EntryCounter : Get_Young_Structure_From_Integer(Entry) })
246
return Stacking_Young_Structures( Stack )
247
248
elif isinstance( Input , str ) :
249
Input = Input.upper()
250
RightCounts = Input.count('R')
251
LeftCounts = Input.count('L')
252
DownCounts = Input.count('D')
253
UpCounts = Input.count('U')
254
assert len(Input) == sum([ RightCounts , LeftCounts , DownCounts , UpCounts ]) , \
255
'The only admissible letters in `Input` are R (right), L (left), D (down), and U (up).'
256
assert RightCounts == LeftCounts , \
257
'The number of R-letters ('+str(RightCounts)+'x) and L-letters ('+str(LeftCounts)+'x) need to coincide.'
258
assert DownCounts == UpCounts , \
259
'The number of D-letters ('+str(DownCounts)+'x) and U-letters ('+str(UpCounts)+'x) need to coincide.'
260
return Get_Young_Structure_From_Binary_Description( Input )
261
262
elif isinstance( Input , dict ) :
263
for New_Coordinate , New_Slice in Input.items() :
264
assert isinstance( New_Coordinate , ( int , Integer ) ) , \
265
'If the input is a dictionary, then the keys need to be integers.'
266
assert isinstance( New_Slice , ( Young_Structure ) ) , \
267
'If the input is a dictionary, then the values need to be Young structures.'
268
return Stacking_Young_Structures( Input )
269
270
elif isinstance( Input , Young_Structure ) :
271
return Input
272
273
else :
274
raise ValueError('The single input is inappropriate.')
275
276
elif len(Input) == 2 :
277
if Input[0] == 'Empty' :
278
Ranges = Input[1]
279
return Get_Empty_Young_Structure( Ranges )
280
281
else :
282
raise ValueError('The two inputs are inappropriate.')
283
284
else :
285
raise ValueError('The input is inappropriate.')
286
287
288
def Coordinates ( self ) -> "Variables" :
289
"""Returns the variable coordinates."""
290
return var([ 'x'+str(Counter) for Counter in range(self.Rank()) ])
291
292
293
def Data ( self ) -> dict[ tuple : int ] :
294
"""Returns the attribute ``_Data``."""
295
return self._Data
296
297
298
def Expand_Ranges ( self , New_Ranges ) -> "Young_Structure" :
299
"""Returns a Young structure with expanded ranges."""
300
assert isinstance( New_Ranges , list ) , \
301
'The input for `New_Ranges` need to be a list.'
302
assert len(New_Ranges) == self.Rank() , \
303
'The length of `New_Ranges` need to coincide with the rank of `self`.'
304
YS = self
305
for RangeCounter , New_Range in enumerate( New_Ranges ) :
306
if New_Range == None :
307
pass
308
309
else :
310
Old_Minimum , Old_Maximum = YS.Ranges()[RangeCounter]
311
assert isinstance( New_Range , tuple ) , \
312
'The '+str(RangeCounter)+'-th entry need to be a tuple.'
313
assert len(New_Range) == 2 , \
314
'The '+str(RangeCounter)+'-th range tuple need to be of length 2.'
315
New_Minimum , New_Maximum = New_Range
316
assert New_Minimum <= New_Maximum , \
317
'The new minimum need to be smaller than or equal to the new maximum.'
318
assert New_Minimum <= Old_Minimum , \
319
'The new minimum need to be smaller than or equal to the old minimum.'
320
assert Old_Maximum <= New_Maximum , \
321
'The old maximum need to be smaller than or equal to the new maximum.'
322
YS._Ranges[RangeCounter] = New_Range
323
324
return YS
325
326
327
def Locus ( self , *Conditions ) -> "Young_Structure" :
328
"""Returns locus of Young structure with respect to given conditions."""
329
for ConditionCounter , Condition in enumerate( Conditions , start=1 ) :
330
assert isinstance( Condition , sage.symbolic.expression.Expression ) , \
331
'The input for the '+str(ConditionCounter)+'-th condition need to be an symbolic expression.'
332
x = self.Coordinates()
333
New_Data = dict({})
334
for Coordinate , Value in self :
335
Value_Satisfies_All_Conditions = True
336
for ConditionCounter , Condition in enumerate( Conditions , start=1 ) :
337
if bool( Condition({x[CoordinateCounter] : CoordinateEntry for CoordinateCounter , CoordinateEntry in enumerate( Coordinate ) }) ) :
338
pass
339
else :
340
Value_Satisfies_All_Conditions = False
341
break
342
if Value_Satisfies_All_Conditions : New_Data.update({ Coordinate : Value })
343
New_Ranges = self.Ranges()
344
return self.__class__( New_Data , New_Ranges )
345
346
347
def Move_Origin ( self , Steps:list[ int ] ) -> "Young_Structure" :
348
"""Returns a Young structure with moved origin."""
349
assert isinstance( Steps , list ) , \
350
'The input for `Steps` need to be a list.'
351
assert len(Steps) == self.Rank() , \
352
'The length of the tuple `Steps` need to coincide with the rank of `self`.'
353
for StepCounter , Step in enumerate( Steps ) :
354
assert isinstance( Step , ( int , Integer ) ) , \
355
'The '+str(StepCounter)+'-th step need to be an integer.'
356
New_Data = { tuple( vector( ZZ , Coordinate ) + vector( ZZ , Steps ) ) : Value for Coordinate , Value in self }
357
New_Ranges = [ ( Minimum+Steps[RageCounter] , Maximum+Steps[RageCounter] ) for RageCounter , ( Minimum , Maximum ) in enumerate( self.Ranges() ) ]
358
return self.__class__( New_Data , New_Ranges )
359
360
361
def Ranges ( self ) -> list[ tuple[ int , int ] ] :
362
"""Returns the attribute ``_Ranges``."""
363
return self._Ranges
364
365
366
def Rank ( self ) -> int :
367
"""Returns the attribute ``_Rank``."""
368
return self._Rank
369
370
371
def Remove_Coordinate ( self , Index ) -> None :
372
"""Remove non-varying coordinates."""
373
assert Index in range(self.Rank()) , \
374
'The input for `Index` need to be in the range '+str(range(self.Rank()))+'-.'
375
New_Data = dict({})
376
for Coordinate , Value in self :
377
Coordinate = tuple([ CoordinateEntry for CoordinateCounter , CoordinateEntry in enumerate(Coordinate) if CoordinateCounter != Index ])
378
New_Data.update({ Coordinate : Value })
379
New_Ranges = [ Range for RangeCounter , Range in enumerate( self.Ranges() ) if RangeCounter != Index ]
380
self.__init__( New_Data , New_Ranges )
381
382
383
def Remove_Vanishing_Values ( self ) -> "Young_Structure" :
384
"""Returns a Young structure without zero-values."""
385
return self.__class__( { Coordinate : Value for Coordinate , Value in self if Value != 0 } , self.Ranges() )
386
387
388
def Sizes ( self ) -> list[ int ] :
389
"""Returns the sizes per direction."""
390
return [ Maximum-Minimum+1 for Minimum , Maximum in self.Ranges() ]
391
392
393
def Support ( self , Output_Type :str ='Absolute' ) -> "Young_Structure" :
394
"Returns the support."
395
if Output_Type in [ 'Absolute' , 'abs' ] :
396
Data = { tuple(self.Rank()*[0]) : 0 }
397
Data.update({ Coordinate : 1 for Coordinate , Value in self if Value != 0 })
398
return self.__class__( Data , self.Ranges() )
399
elif Output_Type in [ 'Relative' , 'rel' , 'Sign' , 'sgn' ] :
400
return self.__class__( { Coordinate : sgn(Value) for Coordinate , Value in self } , self.Ranges() )
401
else :
402
raise ValueError('The input for `Output_Type` is inappropriate.')
403
404
405
def Volumne ( self ) -> int :
406
"""Returns the sum of all entries."""
407
return sum([ Value for Coordinate , Value in self ])
408
409
410
# ToDo: NEED TO BE DONE.
411
def __lt__ ( self , other ) -> bool :
412
"""Compares Young structures with respect to the <-operator."""
413
raise ValueError('The method `less-than` is not yet implemented.')
414
415
416
#def Compress ( self , Direction :int ) -> "Young_Structure" :
417
# assert isinstance( Direction , ( int , Integer ) ) , \
418
# 'The input for `Direction` need to be an integer.'
419
# assert Direction in range(self.Rank()) , \
420
# 'The value of `Direction` need to be in the range from 0 to '+str(self.Rank())+'.'
421
422
#def Inflate ( self , Direction :int ) -> "Young_Structure" :
423
# pass
424
425
426
427
class Young_Diagram ( Young_Structure ) :
428
429
# Synonym for the method `Twist`
430
def __call__ ( self , Step :int =0 ) -> "Young_Diagram" :
431
"""Return a twisted Young diagram."""
432
return self.Twist( Step )
433
434
435
def __init__( self , Data :dict , Ranges :list or None =None ) -> None :
436
super( Young_Diagram , self ).__init__( Data , Ranges )
437
438
assert self.Rank() == 2 , \
439
'`self` need to be of rank 2.'
440
441
xRange , yRange = self.Ranges()
442
xMinimum , xMaximum = xRange
443
yMinimum , yMaximum = yRange
444
assert xMinimum == 0 , \
445
'The x-minimum need to be zero.'
446
assert yMinimum == 0 , \
447
'The y-minimum need to be zero.'
448
449
for Coordinate , Value in self :
450
xCoordinate , yCoordinate = Coordinate
451
assert Value in [ 0 , 1 ] , \
452
'The value at coordinate '+str(Coordinate)+' need to be zero or one.'
453
if 0 < xCoordinate :
454
assert Value <= self[(xCoordinate-1,yCoordinate)] , \
455
'The value at '+str((xCoordinate-1,yCoordinate))+' need to be smaller than or equal to the value at '+str((xCoordinate,yCoordinate))+'.'
456
if 0 < yCoordinate :
457
assert Value <= self[(xCoordinate,yCoordinate-1)] , \
458
'The value at '+str((xCoordinate,yCoordinate-1))+' need to be smaller than or equal to the value at '+str((xCoordinate,yCoordinate))+'.'
459
460
461
def __lshift__ ( self , Step :int =1 ) -> "Young_Diagram" :
462
"""Returns a shifted Young diagram (cyclic action)."""
463
return self >> -1*Step
464
465
466
# Synonym for the method `Shift`
467
def __rshift__ ( self , Step :int =1 ) -> "Young_Diagram" :
468
"""Returns a shifted Young diagram (cyclic action)."""
469
return self.Shift( Step )
470
471
472
def Binary_Description ( self ) -> str :
473
Width = self.Width()
474
xPointer = Width
475
Height = self.Height()
476
yPointer = 0
477
Description = ''
478
while 0 < xPointer and yPointer < Height :
479
if self[xPointer,yPointer] < self[xPointer-1,yPointer] :
480
yPointer += 1
481
Description += 'D'
482
else :
483
xPointer -= 1
484
Description += 'L'
485
if 0 < xPointer : Description += xPointer*'L'
486
if yPointer < Height : Description += (Height-yPointer)*'D'
487
return Description
488
489
490
def Complement ( self ) -> "Young_Diagram" :
491
"""Returns the complement of `self`."""
492
Width = self.Width()
493
Height = self.Height()
494
return self.__class__.Constructor( [ Width-Entry for Entry in self.Usual_Description()[::-1] ] , (Width,Height) )
495
496
497
def Column_Differences ( self ) -> list[int] :
498
"""Returns the differences s_{i}-s_{i+1} for i running through xRange"""
499
return self.Conjugate().Row_Differences()
500
501
502
def Column_Heights ( self ) -> list[ int ] :
503
"""Returns the height of each column."""
504
return self.Conjugate().Row_Widths()
505
506
507
def Column_Volumnes ( self ) -> list[ int ] :
508
"""Returns the volumne of each column."""
509
return self.Conjugate().Row_Volumnes()
510
511
512
def Columns ( self ) -> Iterator[ list[ int ] ] :
513
"""Returns an iterator running over the columns."""
514
return self.Conjugate().Rows()
515
516
517
def Conjugate ( self ) :
518
"""Returns the conjugated Young diagram."""
519
return super( Young_Diagram , self ).Conjugate( Permutation( '(1,2)' ) )
520
521
522
@staticmethod
523
def Constructor ( Description :tuple[ int ] or str , Sizes :tuple[ int , int ] or None =None ) -> "Young_Diagram" :
524
if Description in [ None , 'Empty' , [] , tuple([]) , dict({}) ] :
525
MinimumWidth = 0
526
MinimumHeight = 0
527
Description = [ 0 ]
528
529
elif isinstance( Description , ( list , tuple ) ) :
530
MinimumWidth = max([1] + list(Description))
531
MinimumHeight = len(Description)
532
Previous_Entry = +infinity
533
for EntryCounter , Current_Entry in enumerate( Description ) :
534
assert isinstance( Current_Entry , ( int , Integer ) ) , \
535
'The '+str(EntryCounter)+'-th entry need to be an integer.'
536
assert Previous_Entry >= Current_Entry , \
537
'The '+str(EntryCounter)+'-th entry need to be smaller than or equal to the previous one.'
538
Previous_Entry = Current_Entry
539
540
elif isinstance( Description , str ) :
541
if set(Description).issubset({ 'D' , 'L' }) :
542
MinimumWidth = Description.count('L')
543
MinimumHeight = Description.count('D')
544
Description = MinimumWidth*'R' + Description + MinimumHeight*'U'
545
else :
546
raise ValueError( 'The input for `Description` is given as string, then its letters must be from the set { D (down) , L (letft) }.' )
547
548
else :
549
raise ValueError( 'The input for `Description is inappropriate.`' )
550
551
if Sizes == None : Sizes = ( MinimumWidth , MinimumHeight )
552
Width , Height = Sizes
553
assert MinimumWidth <= Width , \
554
'The width need to be => '+str(MinimumWidth)+'.'
555
assert MinimumHeight <= Height , \
556
'The height need to be => '+str(MinimumHeight)+'.'
557
558
YS = Young_Structure.Constructor( Description )
559
YD = Young_Diagram( YS.Data() , YS.Ranges() )
560
return YD.Expand_Sizes( Width , Height )
561
562
563
def Cyclic_Orbit ( self ) -> Iterator[ "Young_Diagram" ] :
564
"""Runs through the cyclic orbit of `self`."""
565
First_Object = self
566
Current_Object = First_Object
567
while True :
568
yield Current_Object
569
Current_Object = Current_Object >> 1
570
if Current_Object == First_Object : break
571
572
573
def Expand_Sizes ( self , New_Width :int , New_Height :int ) -> "Young_Diagram" :
574
"""Returns a Young diagram with expanded sizes."""
575
assert isinstance( New_Width , ( int , Integer ) ) , \
576
'The input for `New_Width` need to be an integer.'
577
assert self.Width() <= New_Width , \
578
'The new width need to be greater than or equal to the (old) width.'
579
assert isinstance( New_Height , ( int , Integer ) ) , \
580
'The input for `New_Height` need to be an integer.'
581
assert self.Height() <= New_Height , \
582
'The new height need to be greater than or equal to the (old) height.'
583
return self.Expand_Ranges( [ ( 0 , New_Width-1 ) , ( 0 , New_Height-1 ) ] )
584
585
586
def Height ( self ) -> int :
587
"""Returns the size in y-direction."""
588
return self.Sizes()[1]
589
590
591
def Is_Upper_Triangular ( self ) -> bool :
592
"""Tests if `self` is upper triangular"""
593
Width = self.Width()
594
Height = self.Height()
595
for xPos , Column_Height in enumerate( self.Column_Heights() ) :
596
if not Column_Height <= floor( Height*(Width-xPos-1) / Width ) : return False
597
return True
598
599
600
def N ( self ) -> int :
601
"""Returns the sum of width and height."""
602
return sum(self.Sizes())
603
604
605
def Row_Differences ( self ) -> list[int] :
606
"""Returns the differences s_{i}-s_{i+1} for j running through yRange"""
607
s = self.Row_Volumnes() + [ 0 ]
608
return [ s[j]-s[j+1] for j in self.yRange() ]
609
610
611
def Row_Widths ( self ) -> list[ int ] :
612
"""Returns the volumne of each row."""
613
return [ sum(Row) for Row in self.Support().Rows() ]
614
615
616
def Row_Volumnes ( self ) -> list[ int ] :
617
"""Returns the volumne of each row."""
618
return [ sum(Row) for Row in self.Rows() ]
619
620
621
def Rows ( self ) -> Iterator[ list[ int ] ] :
622
"""Returns an iterator running over the rows."""
623
xRange = self.xRange()
624
yRange = self.yRange()
625
for yPos in yRange :
626
yield [ self[xPos,yPos] for xPos in xRange ]
627
628
629
def Shift ( self , Step :int =1 ) -> "Young_Diagram" :
630
"""Returns a shifted Young diagram (cyclic action)."""
631
assert isinstance( Step , ( int , Integer ) ) , \
632
'The input for `Step` need to be an integer.'
633
Step = Step % self.N()
634
if Step == 0 :
635
return self
636
else :
637
Old_Binary_Description = self.Binary_Description()
638
New_Binary_Description = Old_Binary_Description[-1] + Old_Binary_Description[ : -1 ]
639
New_Step = Step-1
640
return Young_Diagram.Constructor( New_Binary_Description ).Shift(New_Step)
641
642
643
def Twist ( self , Step :int =1 ) -> "Young_Diagram" :
644
"""Returns the Young diagram after a twist-action."""
645
assert isinstance( Step , ( int , Integer ) ) , \
646
'The input for `Step` need to be an integer.'
647
New_YD = Young_Diagram.Constructor( [ Entry+Step for Entry in self.Usual_Description() ] )
648
New_Width = max([ New_YD.Width() , self.Width() ])
649
New_Height = max([ New_YD.Height() , self.Height() ])
650
return New_YD.Expand_Sizes( New_Width , New_Height )
651
652
653
# Synonym for the method `Row_Volumnes`.
654
def Usual_Description ( self ) -> list[ int ] :
655
"""Returns the usual description of `self`."""
656
return self.Row_Volumnes()
657
658
659
def Width ( self ) -> int :
660
"""Returns the size in x-direction."""
661
return self.Sizes()[0]
662
663
664
def xRange ( self ) -> list[ int ] :
665
"""Returns the range in x-direction."""
666
return [ 0 .. self.Width()-1 ]
667
668
669
def yRange ( self ) -> list[ int ] :
670
"""Returns the range in y-direction."""
671
return [ 0 .. self.Height()-1 ]
672
673
674
@staticmethod
675
def Lexicographically_Ordered ( Width :int , Height :int ) -> Iterator[ "Young_Diagrams" ] :
676
assert isinstance( Height , ( int , Integer ) ) , \
677
'The input for `Height` need to be an integer.'
678
assert 1 <= Height , \
679
'The integer Height need to be greater than or equal to 1.'
680
assert isinstance( Width , ( int , Integer ) ) , \
681
'The input for `Width` need to be an integer.'
682
assert 1 <= Width , \
683
'The integer Width need to be greater than or equal to 1.'
684
Usual_Descriptions = [ list(Usual_Description) for Usual_Description in IntegerListsLex( max_part=Width , length=Height , max_slope=0 ) ]
685
Usual_Descriptions.reverse()
686
for Usual_Description in Usual_Descriptions :
687
yield Young_Diagram.Constructor( Usual_Description , ( Width , Height ) )
688
689
690
@staticmethod
691
def Upper_Triangulars ( Width :int , Height :int ) -> Iterator[ "Young_Diagrams" ] :
692
assert isinstance( Height , ( int , Integer ) ) , \
693
'The input for `Height` need to be an integer.'
694
assert 1 <= Height , \
695
'The integer Height need to be greater than or equal to 1.'
696
assert isinstance( Width , ( int , Integer ) ) , \
697
'The input for `Width` need to be an integer.'
698
assert 1 <= Width , \
699
'The integer Width need to be greater than or equal to 1.'
700
Ceiling = [ floor( Width*(Height-Counter)/Height ) for Counter in [ 1 .. Height ] ]
701
Usual_Descriptions = [ list(Usual_Description) for Usual_Description in IntegerListsLex( max_part=Width , length=Height , ceiling=Ceiling , max_slope=0 ) ]
702
Usual_Descriptions.reverse()
703
for Usual_Description in Usual_Descriptions :
704
yield Young_Diagram.Constructor( Usual_Description , ( Width , Height ) )
705
706
707
@staticmethod
708
def Minimal_Upper_Triangulars ( Width :int , Height :int ) -> Iterator[ "Young_Diagrams" ] :
709
Orbits = []
710
for YD in Young_Diagram.Upper_Triangulars( Width , Height ) :
711
YD_Did_Already_Appear = False
712
for Partial_Orbit in Orbits :
713
if YD in Partial_Orbit :
714
YD_Did_Already_Appear = True
715
break
716
if not YD_Did_Already_Appear :
717
Orbit_Length = 0
718
Partial_Orbit = []
719
for Fellow in YD.Cyclic_Orbit() :
720
Orbit_Length += 1
721
if Fellow.Is_Upper_Triangular() : Partial_Orbit += [ Fellow ]
722
Orbits += [ Partial_Orbit ]
723
yield YD , Orbit_Length
724
725