Abelian sandpile model/Identity: Difference between revisions
Content added Content deleted
(→Python :: Functional: Added a display function to sugar test outputs. Added a full cascade sequence.) |
|||
Line 934: | Line 934: | ||
<lang python>'''Abelian Sandpile''' |
<lang python>'''Abelian Sandpile''' |
||
from itertools import takewhile |
|||
from functools import reduce |
|||
from operator import add |
from operator import add |
||
Line 942: | Line 944: | ||
'''Tests of cascades and additions''' |
'''Tests of cascades and additions''' |
||
s0 = [[4, 3, 3], [3, 1, 2], [0, 2, 3]] |
s0 = [[4, 3, 3], [3, 1, 2], [0, 2, 3]] |
||
series = list(cascadeSeries(s0)) |
|||
s1 = [[1, 2, 0], [2, 1, 1], [0, 1, 3]] |
s1 = [[1, 2, 0], [2, 1, 1], [0, 1, 3]] |
||
s2 = [[2, 1, 3], [1, 0, 1], [0, 1, 0]] |
s2 = [[2, 1, 3], [1, 0, 1], [0, 1, 0]] |
||
Line 949: | Line 952: | ||
for expr in [ |
for expr in [ |
||
'Cascaded outcome:', |
'Cascaded outcome:', |
||
showSandPiles( |
|||
[(' ', series[0])] + [ |
|||
(':', xs) for xs in series[1:] |
|||
] |
|||
), |
|||
'', |
'', |
||
f's1 + s2 == s2 + s1 -> {addSand(s1)(s2) == addSand(s2)(s1)}', |
f's1 + s2 == s2 + s1 -> {addSand(s1)(s2) == addSand(s2)(s1)}', |
||
showSandPiles([ |
|||
(' ', s1), |
|||
('+', cascaded(s2)), |
|||
('=', addSand(s1)(s2)) |
|||
]), |
|||
'', |
|||
showSandPiles([ |
|||
(' ', s2), |
|||
('+', cascaded(s1)), |
|||
('=', addSand(s2)(s1)) |
|||
]), |
|||
'', |
'', |
||
f's3 + s3_id == s3 -> {addSand(s3)(s3_id) == s3}', |
f's3 + s3_id == s3 -> {addSand(s3)(s3_id) == s3}', |
||
showSandPiles([ |
|||
(' ', s3), |
|||
('+', s3_id), |
|||
('=', s3) |
|||
]), |
|||
'', |
'', |
||
f's3_id + s3_id == s3_id -> {addSand(s3_id)(s3_id) == s3_id}', |
f's3_id + s3_id == s3_id -> {addSand(s3_id)(s3_id) == s3_id}', |
||
showSandPiles([ |
|||
(' ', s3_id), |
|||
('+', s3_id), |
|||
('=', addSand(s3_id)(s3_id)) |
|||
]), |
|||
]: |
]: |
||
print(expr) |
print(expr) |
||
Line 992: | Line 1,014: | ||
xs = list(rows) |
xs = list(rows) |
||
w = len(xs) |
w = len(xs) |
||
⚫ | |||
⚫ | |||
return list(chunksOf(w)( |
return list(chunksOf(w)( |
||
until( |
until(lambda ys: all(y <= w for y in ys))( |
||
nextState(w) |
nextState(w) |
||
)( |
)( |
||
concat(xs) |
|||
) |
|||
)) |
)) |
||
# cascadeSeries :: [[Int]] -> [[[Int]]] |
|||
def cascadeSeries(rows): |
|||
'''The sequence of states from a given |
|||
sand pile to a stable condition. |
|||
''' |
|||
xs = list(rows) |
|||
w = len(xs) |
|||
series = iterate(nextState(w))( |
|||
concat(rows) |
|||
) |
|||
return list(map( |
|||
compose(list, chunksOf(w)), |
|||
takewhile( |
|||
⚫ | |||
series |
|||
) |
|||
)) + [list(chunksOf(w)(next(series)))] |
|||
Line 1,038: | Line 1,077: | ||
) |
) |
||
return go |
return go |
||
# ------------------------ DISPLAY ------------------------- |
|||
# showSandPiles :: [(String, [[Int]])] -> String |
|||
def showSandPiles(pairs): |
|||
'''Indented multi-line representation |
|||
of a sequence of matrixes, delimited |
|||
by preceding operators or indents. |
|||
''' |
|||
return '\n'.join([ |
|||
' '.join([' '.join(map(str, seq)) for seq in tpl]) |
|||
for tpl in zip(*[ |
|||
zip( |
|||
*[list(str(pfx).center(len(rows)))] |
|||
+ list(zip(*rows)) |
|||
) |
|||
for (pfx, rows) in pairs |
|||
]) |
|||
]) |
|||
Line 1,054: | Line 1,113: | ||
) if 0 < n else None |
) if 0 < n else None |
||
return go |
return go |
||
# compose :: ((a -> a), ...) -> (a -> a) |
|||
def compose(*fs): |
|||
'''Composition, from right to left, |
|||
of a series of functions. |
|||
''' |
|||
def go(f, g): |
|||
def fg(x): |
|||
return f(g(x)) |
|||
return fg |
|||
return reduce(go, fs, lambda x: x) |
|||
Line 1,075: | Line 1,147: | ||
None |
None |
||
) |
) |
||
return go |
|||
# iterate :: (a -> a) -> a -> Gen [a] |
|||
def iterate(f): |
|||
'''An infinite list of repeated |
|||
applications of f to x. |
|||
''' |
|||
⚫ | |||
v = x |
|||
while True: |
|||
yield v |
|||
v = f(v) |
|||
return go |
return go |
||
Line 1,098: | Line 1,183: | ||
{{Out}} |
{{Out}} |
||
<pre>Cascaded outcome: |
<pre>Cascaded outcome: |
||
4 3 3 0 4 3 1 0 4 1 1 0 2 1 0 |
|||
3 1 2 : 4 1 2 : 4 2 2 : 4 2 3 : 0 3 3 |
|||
0 2 3 0 2 3 0 2 3 0 2 3 1 2 3 |
|||
s1 + s2 == s2 + s1 -> True |
s1 + s2 == s2 + s1 -> True |
||
1 2 0 2 1 3 3 3 3 |
|||
2 1 1 + 1 0 1 = 3 1 2 |
|||
0 1 3 0 1 0 0 2 3 |
|||
2 1 3 1 2 0 3 3 3 |
|||
1 0 1 + 2 1 1 = 3 1 2 |
|||
0 1 0 0 1 3 0 2 3 |
|||
s3 + s3_id == s3 -> True |
s3 + s3_id == s3 -> True |
||
3 3 3 2 1 2 3 3 3 |
|||
3 3 3 + 1 0 1 = 3 3 3 |
|||
3 3 3 2 1 2 3 3 3 |
|||
s3_id + s3_id == s3_id -> True |
s3_id + s3_id == s3_id -> True |
||
2 1 2 2 1 2 2 1 2 |
|||
1 0 1 + 1 0 1 = 1 0 1 |
|||
2 1 2 2 1 2 2 1 2</pre> |
|||
=={{header|Raku}}== |
=={{header|Raku}}== |