Sandpiles - Discrete dynamics

Skip to content
Back to top
View this page
Edit this page

# Sandpiles

Functions and classes for mathematical sandpiles.

Version: 2.4

AUTHOR:

- David Perkinson (June 4, 2015) Upgraded from version 2.3 to 2.4.

MAJOR CHANGES

1. Eliminated dependence on 4ti2, substituting the use of Polyhedron methods. Thus, no optional packages are necessary.

2. Fixed bug in `Sandpile.__init__` so that now multigraphs are handled correctly.

3. Created `sandpiles` to handle examples of Sandpiles in analogy with `graphs`, `simplicial_complexes`, and `polytopes`. In the process, we implemented a much faster way of producing the sandpile grid graph.

4. Added support for open and closed sandpile Markov chains.

5. Added support for Weierstrass points.

6. Implemented the Cori-Le Borgne algorithm for computing ranks of divisors on complete graphs.

NEW METHODS

Sandpile: avalanche_polynomial, genus, group_gens, help, jacobian_representatives, markov_chain, picard_representatives, smith_form, stable_configs, stationary_density, tutte_polynomial.

SandpileConfig: burst_size, help.

SandpileDivisor: help, is_linearly_equivalent, is_q_reduced, is_weierstrass_pt, polytope, polytope_integer_pts, q_reduced, rank, simulate_threshold, stabilize, weierstrass_div, weierstrass_gap_seq, weierstrass_pts, weierstrass_rank_seq.

MINOR CHANGES

- The `sink` argument to `Sandpile.__init__` now defaults to the first vertex.

- A SandpileConfig or SandpileDivisor may now be multiplied by an integer.

- Sped up `__add__` method for SandpileConfig and SandpileDivisor.

- Enhanced string representation of a Sandpile (via `_repr_` and the `name` methods).

- Recurrents for complete graphs and cycle graphs are computed more quickly.

- The stabilization code for SandpileConfig has been made more efficient.

- Added optional probability distribution arguments to `add_random` methods.

- Marshall Hampton (2010-1-10) modified for inclusion as a module within Sage library.

- David Perkinson (2010-12-14) added show3d(), fixed bug in resolution(), replaced elementary_divisors() with invariant_factors(), added show() for SandpileConfig and SandpileDivisor.

- David Perkinson (2010-9-18): removed is_undirected, added show(), added verbose arguments to several functions to display SandpileConfigs and divisors as lists of integers

- David Perkinson (2010-12-19): created separate SandpileConfig, SandpileDivisor, and Sandpile classes

- David Perkinson (2009-07-15): switched to using config_to_list instead of .values(), thus fixing a few bugs when not using integer labels for vertices.

- David Perkinson (2009): many undocumented improvements

- David Perkinson (2008-12-27): initial version

EXAMPLES:

For general help, enter `Sandpile.help()`, `SandpileConfig.help()`, and `SandpileDivisor.help()`. Miscellaneous examples appear below.

A weighted directed graph given as a Python dictionary:

Sage

    sage: from sage.sandpiles import *
    sage: g = {0: {},
    ....:      1: {0: 1, 2: 1, 3: 1},
    ....:      2: {1: 1, 3: 1, 4: 1},
    ....:      3: {1: 1, 2: 1, 4: 1},
    ....:      4: {2: 1, 3: 1}}

Python

    >>> from sage.all import *
    >>> from sage.sandpiles import *
    >>> g = {Integer(0): {},
    ...      Integer(1): {Integer(0): Integer(1), Integer(2): Integer(1), Integer(3): Integer(1)},
    ...      Integer(2): {Integer(1): Integer(1), Integer(3): Integer(1), Integer(4): Integer(1)},
    ...      Integer(3): {Integer(1): Integer(1), Integer(2): Integer(1), Integer(4): Integer(1)},
    ...      Integer(4): {Integer(2): Integer(1), Integer(3): Integer(1)}}

The associated sandpile with 0 chosen as the sink:

Sage

    sage: S = Sandpile(g,0)

[Python]

    >>> from sage.all import *
    >>> S = Sandpile(g,Integer(0))

Or just:

Sage

    sage: S = Sandpile(g)

Python

    >>> from sage.all import *
    >>> S = Sandpile(g)

A picture of the graph:

Sage

    sage: S.show()                              # long time

[Python]

    >>> from sage.all import *
    >>> S.show()                              # long time

The relevant Laplacian matrices:

Sage

    sage: S.laplacian()
    [ 0  0  0  0  0]
    [-1  3 -1 -1  0]
    [ 0 -1  3 -1 -1]
    [ 0 -1 -1  3 -1]
    [ 0  0 -1 -1  2]
    sage: S.reduced_laplacian()
    [ 3 -1 -1  0]
    [-1  3 -1 -1]
    [-1 -1  3 -1]
    [ 0 -1 -1  2]

Python

    >>> from sage.all import *
    >>> S.laplacian()
    [ 0  0  0  0  0]
    [-1  3 -1 -1  0]
    [ 0 -1  3 -1 -1]
    [ 0 -1 -1  3 -1]
    [ 0  0 -1 -1  2]
    >>> S.reduced_laplacian()
    [ 3 -1 -1  0]
    [-1  3 -1 -1]
    [-1 -1  3 -1]
    [ 0 -1 -1  2]

The number of elements of the sandpile group for S:

Sage

    sage: S.group_order()
    8

[Python]

    >>> from sage.all import *
    >>> S.group_order()
    8

The structure of the sandpile group:

Sage

    sage: S.invariant_factors()
    [1, 1, 1, 8]

Python

    >>> from sage.all import *
    >>> S.invariant_factors()
    [1, 1, 1, 8]

The elements of the sandpile group for S:

Sage

    sage: S.recurrents()
    [{1: 2, 2: 2, 3: 2, 4: 1},
     {1: 2, 2: 2, 3: 2, 4: 0},
     {1: 2, 2: 1, 3: 2, 4: 0},
     {1: 2, 2: 2, 3: 0, 4: 1},
     {1: 2, 2: 0, 3: 2, 4: 1},
     {1: 2, 2: 2, 3: 1, 4: 0},
     {1: 2, 2: 1, 3: 2, 4: 1},
     {1: 2, 2: 2, 3: 1, 4: 1}]

[Python]

    >>> from sage.all import *
    >>> S.recurrents()
    [{1: 2, 2: 2, 3: 2, 4: 1},
     {1: 2, 2: 2, 3: 2, 4: 0},
     {1: 2, 2: 1, 3: 2, 4: 0},
     {1: 2, 2: 2, 3: 0, 4: 1},
     {1: 2, 2: 0, 3: 2, 4: 1},
     {1: 2, 2: 2, 3: 1, 4: 0},
     {1: 2, 2: 1, 3: 2, 4: 1},
     {1: 2, 2: 2, 3: 1, 4: 1}]

The maximal stable element (2 grains of sand on vertices 1, 2, and 3, and 1 grain of sand on vertex 4:

Sage

    sage: S.max_stable()
    {1: 2, 2: 2, 3: 2, 4: 1}
    sage: S.max_stable().values()
    [2, 2, 2, 1]

Python

    >>> from sage.all import *
    >>> S.max_stable()
    {1: 2, 2: 2, 3: 2, 4: 1}
    >>> S.max_stable().values()
    [2, 2, 2, 1]

The identity of the sandpile group for S:

Sage

    sage: S.identity()
    {1: 2, 2: 2, 3: 2, 4: 0}

[Python]

    >>> from sage.all import *
    >>> S.identity()
    {1: 2, 2: 2, 3: 2, 4: 0}

An arbitrary sandpile configuration:

Sage

    sage: c = SandpileConfig(S,[1,0,4,-3])
    sage: c.equivalent_recurrent()
    {1: 2, 2: 2, 3: 2, 4: 0}

Python

    >>> from sage.all import *
    >>> c = SandpileConfig(S,[Integer(1),Integer(0),Integer(4),-Integer(3)])
    >>> c.equivalent_recurrent()
    {1: 2, 2: 2, 3: 2, 4: 0}

Some group operations:

Sage

    sage: m = S.max_stable()
    sage: i = S.identity()
    sage: m.values()
    [2, 2, 2, 1]
    sage: i.values()
    [2, 2, 2, 0]
    sage: m + i    # coordinate-wise sum
    {1: 4, 2: 4, 3: 4, 4: 1}
    sage: m - i
    {1: 0, 2: 0, 3: 0, 4: 1}
    sage: m & i  # add, then stabilize
    {1: 2, 2: 2, 3: 2, 4: 1}
    sage: e = m + m
    sage: e
    {1: 4, 2: 4, 3: 4, 4: 2}
    sage: ~e   # stabilize
    {1: 2, 2: 2, 3: 2, 4: 0}
    sage: a = -m
    sage: a & m
    {1: 0, 2: 0, 3: 0, 4: 0}
    sage: a * m   # add, then find the equivalent recurrent
    {1: 2, 2: 2, 3: 2, 4: 0}
    sage: a^3  # a*a*a
    {1: 2, 2: 2, 3: 2, 4: 1}
    sage: a^(-1) == m
    True
    sage: a < m  # every coordinate of a is < that of m
    True

[Python]

    >>> from sage.all import *
    >>> m = S.max_stable()
    >>> i = S.identity()
    >>> m.values()
    [2, 2, 2, 1]
    >>> i.values()
    [2, 2, 2, 0]
    >>> m + i    # coordinate-wise sum
    {1: 4, 2: 4, 3: 4, 4: 1}
    >>> m - i
    {1: 0, 2: 0, 3: 0, 4: 1}
    >>> m & i  # add, then stabilize
    {1: 2, 2: 2, 3: 2, 4: 1}
    >>> e = m + m
    >>> e
    {1: 4, 2: 4, 3: 4, 4: 2}
    >>> ~e   # stabilize
    {1: 2, 2: 2, 3: 2, 4: 0}
    >>> a = -m
    >>> a & m
    {1: 0, 2: 0, 3: 0, 4: 0}
    >>> a * m   # add, then find the equivalent recurrent
    {1: 2, 2: 2, 3: 2, 4: 0}
    >>> a**Integer(3)  # a*a*a
    {1: 2, 2: 2, 3: 2, 4: 1}
    >>> a**(-Integer(1)) == m
    True
    >>> a < m  # every coordinate of a is < that of m
    True

Firing an unstable vertex returns resulting configuration:

Sage

    sage: c = S.max_stable() + S.identity()
    sage: c.fire_vertex(1)
    {1: 1, 2: 5, 3: 5, 4: 1}
    sage: c
    {1: 4, 2: 4, 3: 4, 4: 1}

Python

    >>> from sage.all import *
    >>> c = S.max_stable() + S.identity()
    >>> c.fire_vertex(Integer(1))
    {1: 1, 2: 5, 3: 5, 4: 1}
    >>> c
    {1: 4, 2: 4, 3: 4, 4: 1}

Fire all unstable vertices:

Sage

    sage: c.unstable()
    [1, 2, 3]
    sage: c.fire_unstable()
    {1: 3, 2: 3, 3: 3, 4: 3}

[Python]

    >>> from sage.all import *
    >>> c.unstable()
    [1, 2, 3]
    >>> c.fire_unstable()
    {1: 3, 2: 3, 3: 3, 4: 3}

Stabilize c, returning the resulting configuration and the firing vector:

Sage

    sage: c.stabilize(True)
    [{1: 2, 2: 2, 3: 2, 4: 1}, {1: 6, 2: 8, 3: 8, 4: 8}]
    sage: c
    {1: 4, 2: 4, 3: 4, 4: 1}
    sage: S.max_stable() & S.identity() == c.stabilize()
    True

Python

    >>> from sage.all import *
    >>> c.stabilize(True)
    [{1: 2, 2: 2, 3: 2, 4: 1}, {1: 6, 2: 8, 3: 8, 4: 8}]
    >>> c
    {1: 4, 2: 4, 3: 4, 4: 1}
    >>> S.max_stable() & S.identity() == c.stabilize()
    True

The number of superstable configurations of each degree:

Sage

    sage: S.h_vector()
    [1, 3, 4]
    sage: S.postulation()
    2

[Python]

    >>> from sage.all import *
    >>> S.h_vector()
    [1, 3, 4]
    >>> S.postulation()
    2

the saturated homogeneous toppling ideal:

Sage

    sage: S.ideal()
    Ideal (x1 - x0, x3*x2 - x0^2, x4^2 - x0^2, x2^3 - x4*x3*x0,
           x4*x2^2 - x3^2*x0, x3^3 - x4*x2*x0, x4*x3^2 - x2^2*x0) of
     Multivariate Polynomial Ring in x4, x3, x2, x1, x0 over Rational Field

Python

    >>> from sage.all import *
    >>> S.ideal()
    Ideal (x1 - x0, x3*x2 - x0^2, x4^2 - x0^2, x2^3 - x4*x3*x0,
           x4*x2^2 - x3^2*x0, x3^3 - x4*x2*x0, x4*x3^2 - x2^2*x0) of
     Multivariate Polynomial Ring in x4, x3, x2, x1, x0 over Rational Field

its minimal free resolution:

Sage

    sage: S.resolution()
    'R^1 <-- R^7 <-- R^15 <-- R^13 <-- R^4'

[Python]

    >>> from sage.all import *
    >>> S.resolution()
    'R^1 <-- R^7 <-- R^15 <-- R^13 <-- R^4'

and its Betti numbers:

Sage

    sage: S.betti()
               0     1     2     3     4
    ------------------------------------
        0:     1     1     -     -     -
        1:     -     2     2     -     -
        2:     -     4    13    13     4
    ------------------------------------
    total:     1     7    15    13     4

Python

    >>> from sage.all import *
    >>> S.betti()
               0     1     2     3     4
    ------------------------------------
        0:     1     1     -     -     -
        1:     -     2     2     -     -
        2:     -     4    13    13     4
    ------------------------------------
    total:     1     7    15    13     4

Some various ways of creating Sandpiles:

Sage

    sage: S = sandpiles.Complete(4) # for more options enter ``sandpile.TAB``
    sage: S = sandpiles.Wheel(6)

[Python]

    >>> from sage.all import *
    >>> S = sandpiles.Complete(Integer(4)) # for more options enter ``sandpile.TAB``
    >>> S = sandpiles.Wheel(Integer(6))

A multidigraph with loops (vertices 0, 1, 2; for example, there is a directed edge from vertex 2 to vertex 1 of weight 3, which can be thought of as three directed edges of the form (2,3). There is also a single loop at vertex 2 and an edge (2,0) of weight 2):

Sage

    sage: S = Sandpile({0:[1,2], 1:[0,0,2], 2:[0,0,1,1,1,2], 3:[2]})

Python

    >>> from sage.all import *
    >>> S = Sandpile({Integer(0):[Integer(1),Integer(2)], Integer(1):[Integer(0),Integer(0),Integer(2)], Integer(2):[Integer(0),Integer(0),Integer(1),Integer(1),Integer(1),Integer(2)], Integer(3):[Integer(2)]})

Using the graph library (vertex 1 is specified as the sink; omitting this would make the sink vertex 0 by default):

Sage

    sage: S = Sandpile(graphs.PetersenGraph(),1)

[Python]

    >>> from sage.all import *
    >>> S = Sandpile(graphs.PetersenGraph(),Integer(1))

Distribution of avalanche sizes:

Sage

    sage: S = sandpiles.Grid(10,10)
    sage: m = S.max_stable()
    sage: a = []
    sage: for i in range(1000):
    ....:     m = m.add_random()
    ....:     m, f = m.stabilize(True)
    ....:     a.append(sum(f.values()))

    sage: p = list_plot([[log(i + 1), log(a.count(i))]
    ....:                for i in [0..max(a)] if a.count(i)])
    sage: p.axes_labels(['log(N)', 'log(D(N))'])
    sage: t = text("Distribution of avalanche sizes", (2,2), rgbcolor=(1,0,0))
    sage: show(p + t, axes_labels=['log(N)', 'log(D(N))'])      # long time

Python

    >>> from sage.all import *
    >>> S = sandpiles.Grid(Integer(10),Integer(10))
    >>> m = S.max_stable()
    >>> a = []
    >>> for i in range(Integer(1000)):
    ...     m = m.add_random()
    ...     m, f = m.stabilize(True)
    ...     a.append(sum(f.values()))

    >>> p = list_plot([[log(i + Integer(1)), log(a.count(i))]
    ...                for i in (ellipsis_range(Integer(0),Ellipsis,max(a))) if a.count(i)])
    >>> p.axes_labels(['log(N)', 'log(D(N))'])
    >>> t = text("Distribution of avalanche sizes", (Integer(2),Integer(2)), rgbcolor=(Integer(1),Integer(0),Integer(0)))
    >>> show(p + t, axes_labels=['log(N)', 'log(D(N))'])      # long time

Working with sandpile divisors:

Sage

    sage: S = sandpiles.Complete(4)
    sage: D = SandpileDivisor(S, [0,0,0,5])
    sage: E = D.stabilize(); E
    {0: 1, 1: 1, 2: 1, 3: 2}
    sage: D.is_linearly_equivalent(E)
    True
    sage: D.q_reduced()
    {0: 4, 1: 0, 2: 0, 3: 1}
    sage: S = sandpiles.Complete(4)
    sage: D = SandpileDivisor(S, [0,0,0,5])
    sage: E = D.stabilize(); E
    {0: 1, 1: 1, 2: 1, 3: 2}
    sage: D.is_linearly_equivalent(E)
    True
    sage: D.q_reduced()
    {0: 4, 1: 0, 2: 0, 3: 1}
    sage: D.rank()
    2

    sage: sorted(D.effective_div(), key=str)
    [{0: 0, 1: 0, 2: 0, 3: 5},
     {0: 0, 1: 0, 2: 4, 3: 1},
     {0: 0, 1: 4, 2: 0, 3: 1},
     {0: 1, 1: 1, 2: 1, 3: 2},
     {0: 4, 1: 0, 2: 0, 3: 1}]
    sage: sorted(D.effective_div(False))
    [[0, 0, 0, 5], [0, 0, 4, 1], [0, 4, 0, 1], [1, 1, 1, 2], [4, 0, 0, 1]]
    sage: D.rank()
    2
    sage: D.rank(True)
    (2, {0: 2, 1: 1, 2: 0, 3: 0})
    sage: E = D.rank(True)[1]  # E proves the rank is not 3
    sage: E.values()
    [2, 1, 0, 0]
    sage: E.deg()
    3
    sage: rank(D - E)
    -1
    sage: (D - E).effective_div()
    []

    sage: D.weierstrass_pts()
    (0, 1, 2, 3)
    sage: D.weierstrass_rank_seq(0)
    (2, 1, 0, 0, 0, -1)
    sage: D.weierstrass_pts()
    (0, 1, 2, 3)
    sage: D.weierstrass_rank_seq(0)
    (2, 1, 0, 0, 0, -1)

[Python]

    >>> from sage.all import *
    >>> S = sandpiles.Complete(Integer(4))
    >>> D = SandpileDivisor(S, [Integer(0),Integer(0),Integer(0),Integer(5)])
    >>> E = D.stabilize(); E
    {0: 1, 1: 1, 2: 1, 3: 2}
    >>> D.is_linearly_equivalent(E)
    True
    >>> D.q_reduced()
    {0: 4, 1: 0, 2: 0, 3: 1}
    >>> S = sandpiles.Complete(Integer(4))
    >>> D = SandpileDivisor(S, [Integer(0),Integer(0),Integer(0),Integer(5)])
    >>> E = D.stabilize(); E
    {0: 1, 1: 1, 2: 1, 3: 2}
    >>> D.is_linearly_equivalent(E)
    True
    >>> D.q_reduced()
    {0: 4, 1: 0, 2: 0, 3: 1}
    >>> D.rank()
    2

    >>> sorted(D.effective_div(), key=str)
    [{0: 0, 1: 0, 2: 0, 3: 5},
     {0: 0, 1: 0, 2: 4, 3: 1},
     {0: 0, 1: 4, 2: 0, 3: 1},
     {0: 1, 1: 1, 2: 1, 3: 2},
     {0: 4, 1: 0, 2: 0, 3: 1}]
    >>> sorted(D.effective_div(False))
    [[0, 0, 0, 5], [0, 0, 4, 1], [0, 4, 0, 1], [1, 1, 1, 2], [4, 0, 0, 1]]
    >>> D.rank()
    2
    >>> D.rank(True)
    (2, {0: 2, 1: 1, 2: 0, 3: 0})
    >>> E = D.rank(True)[Integer(1)]  # E proves the rank is not 3
    >>> E.values()
    [2, 1, 0, 0]
    >>> E.deg()
    3
    >>> rank(D - E)
    -1
    >>> (D - E).effective_div()
    []

    >>> D.weierstrass_pts()
    (0, 1, 2, 3)
    >>> D.weierstrass_rank_seq(Integer(0))
    (2, 1, 0, 0, 0, -1)
    >>> D.weierstrass_pts()
    (0, 1, 2, 3)
    >>> D.weierstrass_rank_seq(Integer(0))
    (2, 1, 0, 0, 0, -1)

class sage.sandpiles.sandpile.Sandpile(g, sink=None)

  Bases: `DiGraph`

  Class for Dhar’s abelian sandpile model.

  all_k_config(k)

    The constant configuration with all values set to \(k\).

    INPUT:

    - `k` – integer

    OUTPUT: SandpileConfig

    EXAMPLES:

    Sage

        sage: s = sandpiles.Diamond()
        sage: s.all_k_config(7)
        {1: 7, 2: 7, 3: 7}

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Diamond()
        >>> s.all_k_config(Integer(7))
        {1: 7, 2: 7, 3: 7}

  all_k_div(k)

    The divisor with all values set to \(k\).

    INPUT:

    - `k` – integer

    OUTPUT: SandpileDivisor

    EXAMPLES:

    Sage

        sage: S = sandpiles.House()
        sage: S.all_k_div(7)
        {0: 7, 1: 7, 2: 7, 3: 7, 4: 7}

    Python

        >>> from sage.all import *
        >>> S = sandpiles.House()
        >>> S.all_k_div(Integer(7))
        {0: 7, 1: 7, 2: 7, 3: 7, 4: 7}

  avalanche_polynomial(multivariable=True)

    The avalanche polynomial. See NOTE for details.

    INPUT:

    - `multivariable` – boolean (default: `True`)

    OUTPUT: polynomial

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(4)
        sage: s.avalanche_polynomial()
        9*x0*x1*x2 + 2*x0*x1 + 2*x0*x2 + 2*x1*x2 + 3*x0 + 3*x1 + 3*x2 + 24
        sage: s.avalanche_polynomial(False)
        9*x0^3 + 6*x0^2 + 9*x0 + 24

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(4))
        >>> s.avalanche_polynomial()
        9*x0*x1*x2 + 2*x0*x1 + 2*x0*x2 + 2*x1*x2 + 3*x0 + 3*x1 + 3*x2 + 24
        >>> s.avalanche_polynomial(False)
        9*x0^3 + 6*x0^2 + 9*x0 + 24

    Note

    For each nonsink vertex \(v\), let \(x_v\) be an indeterminate. If \((r,v)\) is a pair consisting of a recurrent \(r\) and nonsink vertex \(v\), then for each nonsink vertex \(w\), let \(n_w\) be the number of times vertex \(w\) fires in the stabilization of \(r + v\). Let \(M(r,v)\) be the monomial \(\prod_w x_w^{n_w}\), i.e., the exponent records the vector of \(n_w\) as \(w\) ranges over the nonsink vertices. The avalanche polynomial is then the sum of \(M(r,v)\) as \(r\) ranges over the recurrents and \(v\) ranges over the nonsink vertices. If `multivariable` is `False`, then set all the indeterminates equal to each other (and, thus, only count the number of vertex firings in the stabilizations, forgetting which particular vertices fired).

  betti(verbose=True)

    Return the Betti table for the homogeneous toppling ideal.

    If `verbose` is `True`, it prints the standard Betti table, otherwise, it returns a less formatted table.

    INPUT:

    - `verbose` – boolean (default: `True`)

    OUTPUT: Betti numbers for the sandpile

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: S.betti()
                   0     1     2     3
        ------------------------------
            0:     1     -     -     -
            1:     -     2     -     -
            2:     -     4     9     4
        ------------------------------
        total:     1     6     9     4
        sage: S.betti(False)
        [1, 6, 9, 4]

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> S.betti()
                   0     1     2     3
        ------------------------------
            0:     1     -     -     -
            1:     -     2     -     -
            2:     -     4     9     4
        ------------------------------
        total:     1     6     9     4
        >>> S.betti(False)
        [1, 6, 9, 4]

  betti_complexes()

    The support-complexes with non-trivial homology. (See NOTE.)

    OUTPUT: list (of pairs [divisors, corresponding simplicial complex])

    EXAMPLES:

    Sage

        sage: S = Sandpile({0:{},1:{0: 1, 2: 1, 3: 4},2:{3: 5},3:{1: 1, 2: 1}},0)
        sage: p = S.betti_complexes()
        sage: p[0]
        [{0: -8, 1: 5, 2: 4, 3: 1},
         Simplicial complex with vertex set (1, 2, 3) and facets {(3,), (1, 2)}]
        sage: S.resolution()
        'R^1 <-- R^5 <-- R^5 <-- R^1'
        sage: S.betti()
                   0     1     2     3
        ------------------------------
            0:     1     -     -     -
            1:     -     5     5     -
            2:     -     -     -     1
        ------------------------------
        total:     1     5     5     1
        sage: len(p)
        11
        sage: p[0][1].homology()
        {0: Z, 1: 0}
        sage: p[-1][1].homology()
        {0: 0, 1: 0, 2: Z}

    Python

        >>> from sage.all import *
        >>> S = Sandpile({Integer(0):{},Integer(1):{Integer(0): Integer(1), Integer(2): Integer(1), Integer(3): Integer(4)},Integer(2):{Integer(3): Integer(5)},Integer(3):{Integer(1): Integer(1), Integer(2): Integer(1)}},Integer(0))
        >>> p = S.betti_complexes()
        >>> p[Integer(0)]
        [{0: -8, 1: 5, 2: 4, 3: 1},
         Simplicial complex with vertex set (1, 2, 3) and facets {(3,), (1, 2)}]
        >>> S.resolution()
        'R^1 <-- R^5 <-- R^5 <-- R^1'
        >>> S.betti()
                   0     1     2     3
        ------------------------------
            0:     1     -     -     -
            1:     -     5     5     -
            2:     -     -     -     1
        ------------------------------
        total:     1     5     5     1
        >>> len(p)
        11
        >>> p[Integer(0)][Integer(1)].homology()
        {0: Z, 1: 0}
        >>> p[-Integer(1)][Integer(1)].homology()
        {0: 0, 1: 0, 2: Z}

    Note

    A `support-complex` is the simplicial complex formed from the supports of the divisors in a linear system.

  burning_config()

    The minimal burning configuration.

    OUTPUT:

    dict (configuration)

    EXAMPLES:

    Sage

        sage: g = {0:{},1:{0:1,3:1,4:1},2:{0:1,3:1,5:1},
        ....:      3:{2:1,5:1},4:{1:1,3:1},5:{2:1,3:1}}
        sage: S = Sandpile(g,0)
        sage: S.burning_config()
        {1: 2, 2: 0, 3: 1, 4: 1, 5: 0}
        sage: S.burning_config().values()
        [2, 0, 1, 1, 0]
        sage: S.burning_script()
        {1: 1, 2: 3, 3: 5, 4: 1, 5: 4}
        sage: script = S.burning_script().values()
        sage: script
        [1, 3, 5, 1, 4]
        sage: matrix(script)*S.reduced_laplacian()
        [2 0 1 1 0]

    Python

        >>> from sage.all import *
        >>> g = {Integer(0):{},Integer(1):{Integer(0):Integer(1),Integer(3):Integer(1),Integer(4):Integer(1)},Integer(2):{Integer(0):Integer(1),Integer(3):Integer(1),Integer(5):Integer(1)},
        ...      Integer(3):{Integer(2):Integer(1),Integer(5):Integer(1)},Integer(4):{Integer(1):Integer(1),Integer(3):Integer(1)},Integer(5):{Integer(2):Integer(1),Integer(3):Integer(1)}}
        >>> S = Sandpile(g,Integer(0))
        >>> S.burning_config()
        {1: 2, 2: 0, 3: 1, 4: 1, 5: 0}
        >>> S.burning_config().values()
        [2, 0, 1, 1, 0]
        >>> S.burning_script()
        {1: 1, 2: 3, 3: 5, 4: 1, 5: 4}
        >>> script = S.burning_script().values()
        >>> script
        [1, 3, 5, 1, 4]
        >>> matrix(script)*S.reduced_laplacian()
        [2 0 1 1 0]

    Note

    The burning configuration and script are computed using a modified version of Speer’s script algorithm. This is a generalization to directed multigraphs of Dhar’s burning algorithm.

    A burning configuration is a nonnegative integer-linear combination of the rows of the reduced Laplacian matrix having nonnegative entries and such that every vertex has a path from some vertex in its support. The corresponding burning script gives the integer-linear combination needed to obtain the burning configuration. So if \(b\) is the burning configuration, \(\sigma\) is its script, and \(\tilde{L}\) is the reduced Laplacian, then \(\sigma\cdot \tilde{L} = b\). The minimal burning configuration is the one with the minimal script (its components are no larger than the components of any other script for a burning configuration).

    The following are equivalent for a configuration \(c\) with burning configuration \(b\) having script \(\sigma\):

    > - \(c\) is recurrent;

    > - \(c+b\) stabilizes to \(c\);

    > - the firing vector for the stabilization of \(c+b\) is \(\sigma\).

  burning_script()

    A script for the minimal burning configuration.

    OUTPUT: dictionary

    EXAMPLES:

    Sage

        sage: g = {0:{},1:{0:1,3:1,4:1},2:{0:1,3:1,5:1},
        ....:      3:{2:1,5:1},4:{1:1,3:1},5:{2:1,3:1}}
        sage: S = Sandpile(g,0)
        sage: S.burning_config()
        {1: 2, 2: 0, 3: 1, 4: 1, 5: 0}
        sage: S.burning_config().values()
        [2, 0, 1, 1, 0]
        sage: S.burning_script()
        {1: 1, 2: 3, 3: 5, 4: 1, 5: 4}
        sage: script = S.burning_script().values()
        sage: script
        [1, 3, 5, 1, 4]
        sage: matrix(script)*S.reduced_laplacian()
        [2 0 1 1 0]

    Python

        >>> from sage.all import *
        >>> g = {Integer(0):{},Integer(1):{Integer(0):Integer(1),Integer(3):Integer(1),Integer(4):Integer(1)},Integer(2):{Integer(0):Integer(1),Integer(3):Integer(1),Integer(5):Integer(1)},
        ...      Integer(3):{Integer(2):Integer(1),Integer(5):Integer(1)},Integer(4):{Integer(1):Integer(1),Integer(3):Integer(1)},Integer(5):{Integer(2):Integer(1),Integer(3):Integer(1)}}
        >>> S = Sandpile(g,Integer(0))
        >>> S.burning_config()
        {1: 2, 2: 0, 3: 1, 4: 1, 5: 0}
        >>> S.burning_config().values()
        [2, 0, 1, 1, 0]
        >>> S.burning_script()
        {1: 1, 2: 3, 3: 5, 4: 1, 5: 4}
        >>> script = S.burning_script().values()
        >>> script
        [1, 3, 5, 1, 4]
        >>> matrix(script)*S.reduced_laplacian()
        [2 0 1 1 0]

    Note

    The burning configuration and script are computed using a modified version of Speer’s script algorithm. This is a generalization to directed multigraphs of Dhar’s burning algorithm.

    A burning configuration is a nonnegative integer-linear combination of the rows of the reduced Laplacian matrix having nonnegative entries and such that every vertex has a path from some vertex in its support. The corresponding burning script gives the integer-linear combination needed to obtain the burning configuration. So if \(b\) is the burning configuration, \(s\) is its script, and \(L_{\mathrm{red}}\) is the reduced Laplacian, then \(s\cdot L_{\mathrm{red}}= b\). The minimal burning configuration is the one with the minimal script (its components are no larger than the components of any other script for a burning configuration).

    The following are equivalent for a configuration \(c\) with burning configuration \(b\) having script \(s\):

    > - \(c\) is recurrent;

    > - \(c+b\) stabilizes to \(c\);

    > - the firing vector for the stabilization of \(c+b\) is \(s\).

  canonical_divisor()

    The canonical divisor. This is the divisor with \(\deg(v)-2\) grains of sand on each vertex (not counting loops). Only for undirected graphs.

    OUTPUT: SandpileDivisor

    EXAMPLES:

    Sage

        sage: S = sandpiles.Complete(4)
        sage: S.canonical_divisor()
        {0: 1, 1: 1, 2: 1, 3: 1}
        sage: s = Sandpile({0:[1,1],1:[0,0,1,1,1]},0)
        sage: s.canonical_divisor()  # loops are disregarded
        {0: 0, 1: 0}

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Complete(Integer(4))
        >>> S.canonical_divisor()
        {0: 1, 1: 1, 2: 1, 3: 1}
        >>> s = Sandpile({Integer(0):[Integer(1),Integer(1)],Integer(1):[Integer(0),Integer(0),Integer(1),Integer(1),Integer(1)]},Integer(0))
        >>> s.canonical_divisor()  # loops are disregarded
        {0: 0, 1: 0}

    Warning

    The underlying graph must be undirected.

  dict()

    A dictionary of dictionaries representing a directed graph.

    OUTPUT: dictionary

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: S.dict()
        {0: {1: 1, 2: 1},
         1: {0: 1, 2: 1, 3: 1},
         2: {0: 1, 1: 1, 3: 1},
         3: {1: 1, 2: 1}}
        sage: S.sink()
        0

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> S.dict()
        {0: {1: 1, 2: 1},
         1: {0: 1, 2: 1, 3: 1},
         2: {0: 1, 1: 1, 3: 1},
         3: {1: 1, 2: 1}}
        >>> S.sink()
        0

  genus()

    The genus: (# non-loop edges) - (# vertices) + 1.

    This is only defined for undirected graphs.

    OUTPUT: integer

    EXAMPLES:

    Sage

        sage: sandpiles.Complete(4).genus()
        3
        sage: sandpiles.Cycle(5).genus()
        1

    Python

        >>> from sage.all import *
        >>> sandpiles.Complete(Integer(4)).genus()
        3
        >>> sandpiles.Cycle(Integer(5)).genus()
        1

  groebner()

    Return a Groebner basis for the homogeneous toppling ideal.

    It is computed with respect to the standard sandpile ordering (see `ring`).

    OUTPUT: Groebner basis

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: S.groebner()
        [x3*x2^2 - x1^2*x0, x2^3 - x3*x1*x0, x3*x1^2 - x2^2*x0,
         x1^3 - x3*x2*x0, x3^2 - x0^2, x2*x1 - x0^2]

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> S.groebner()
        [x3*x2^2 - x1^2*x0, x2^3 - x3*x1*x0, x3*x1^2 - x2^2*x0,
         x1^3 - x3*x2*x0, x3^2 - x0^2, x2*x1 - x0^2]

  group_gens(verbose=True)

    A minimal list of generators for the sandpile group.

    If `verbose` is `False` then the generators are represented as lists of integers.

    INPUT:

    - `verbose` – boolean (default: `True`)

    OUTPUT:

    list of SandpileConfig (or of lists of integers if `verbose` is `False`)

    EXAMPLES:

    Sage

        sage: s = sandpiles.Cycle(5)
        sage: s.group_gens()
        [{1: 0, 2: 1, 3: 1, 4: 1}]
        sage: s.group_gens()[0].order()
        5
        sage: s = sandpiles.Complete(5)
        sage: s.group_gens(False)
        [[2, 3, 2, 2], [2, 2, 3, 2], [2, 2, 2, 3]]
        sage: [i.order() for i in s.group_gens()]
        [5, 5, 5]
        sage: s.invariant_factors()
        [1, 5, 5, 5]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Cycle(Integer(5))
        >>> s.group_gens()
        [{1: 0, 2: 1, 3: 1, 4: 1}]
        >>> s.group_gens()[Integer(0)].order()
        5
        >>> s = sandpiles.Complete(Integer(5))
        >>> s.group_gens(False)
        [[2, 3, 2, 2], [2, 2, 3, 2], [2, 2, 2, 3]]
        >>> [i.order() for i in s.group_gens()]
        [5, 5, 5]
        >>> s.invariant_factors()
        [1, 5, 5, 5]

  group_order()

    The size of the sandpile group.

    OUTPUT: integer

    EXAMPLES:

    Sage

        sage: S = sandpiles.House()
        sage: S.group_order()
        11

    Python

        >>> from sage.all import *
        >>> S = sandpiles.House()
        >>> S.group_order()
        11

  h_vector()

    The number of superstable configurations in each degree. Equivalently, this is the list of first differences of the Hilbert function of the (homogeneous) toppling ideal.

    OUTPUT: list of nonnegative integers

    EXAMPLES:

    Sage

        sage: s = sandpiles.Grid(2,2)
        sage: s.hilbert_function()
        [1, 5, 15, 35, 66, 106, 146, 178, 192]
        sage: s.h_vector()
        [1, 4, 10, 20, 31, 40, 40, 32, 14]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Grid(Integer(2),Integer(2))
        >>> s.hilbert_function()
        [1, 5, 15, 35, 66, 106, 146, 178, 192]
        >>> s.h_vector()
        [1, 4, 10, 20, 31, 40, 40, 32, 14]

  static help(verbose=True)

    List of Sandpile-specific methods (not inherited from `Graph`). If `verbose`, include short descriptions.

    INPUT:

    - `verbose` – boolean (default: `True`)

    OUTPUT: printed string

    EXAMPLES:

    Sage

        sage: Sandpile.help() # long time
        For detailed help with any method FOO listed below,
        enter "Sandpile.FOO?" or enter "S.FOO?" for any Sandpile S.

        all_k_config             -- The constant configuration with all values set to k.
        all_k_div                -- The divisor with all values set to k.
        avalanche_polynomial     -- The avalanche polynomial.
        betti                    -- Return the Betti table for the homogeneous toppling ideal.
        betti_complexes          -- The support-complexes with non-trivial homology.
        burning_config           -- The minimal burning configuration.
        burning_script           -- A script for the minimal burning configuration.
        canonical_divisor        -- The canonical divisor.
        dict                     -- A dictionary of dictionaries representing a directed graph.
        genus                    -- The genus: (# non-loop edges) - (# vertices) + 1.
        groebner                 -- Return a Groebner basis for the homogeneous toppling ideal.
        group_gens               -- A minimal list of generators for the sandpile group.
        group_order              -- The size of the sandpile group.
        h_vector                 -- The number of superstable configurations in each degree.
        help                     -- List of Sandpile-specific methods (not inherited from ...Graph...).
        hilbert_function         -- The Hilbert function of the homogeneous toppling ideal.
        ideal                    -- The saturated homogeneous toppling ideal.
        identity                 -- The identity configuration.
        in_degree                -- The in-degree of a vertex or a list of all in-degrees.
        invariant_factors        -- The invariant factors of the sandpile group.
        is_undirected            -- Is the underlying graph undirected?
        jacobian_representatives -- Representatives for the elements of the Jacobian group.
        laplacian                -- Return the Laplacian matrix of the graph.
        markov_chain             -- The sandpile Markov chain for configurations or divisors.
        max_stable               -- The maximal stable configuration.
        max_stable_div           -- The maximal stable divisor.
        max_superstables         -- The maximal superstable configurations.
        min_recurrents           -- The minimal recurrent elements.
        nonsink_vertices         -- The nonsink vertices.
        nonspecial_divisors      -- The nonspecial divisors.
        out_degree               -- The out-degree of a vertex or a list of all out-degrees.
        picard_representatives   -- Representatives of the divisor classes of degree d in the Picard group.
        points                   -- Generators for the multiplicative group of zeros of the sandpile ideal.
        postulation              -- The postulation number of the toppling ideal.
        recurrents               -- The recurrent configurations.
        reduced_laplacian        -- Return the reduced Laplacian matrix of the graph.
        reorder_vertices         -- A copy of the sandpile with vertex names permuted.
        resolution               -- A minimal free resolution of the homogeneous toppling ideal.
        ring                     -- The ring containing the homogeneous toppling ideal.
        show                     -- Draw the underlying graph.
        show3d                   -- Draw the underlying graph.
        sink                     -- The sink vertex.
        smith_form               -- The Smith normal form for the Laplacian.
        solve                    -- Approximations of the complex affine zeros of the sandpile ideal.
        stable_configs           -- Generator for all stable configurations.
        stationary_density       -- The stationary density of the sandpile.
        superstables             -- The superstable configurations.
        symmetric_recurrents     -- The symmetric recurrent configurations.
        tutte_polynomial         -- The Tutte polynomial of the underlying graph.
        unsaturated_ideal        -- The unsaturated, homogeneous toppling ideal.
        version                  -- The version number of Sage Sandpiles.
        zero_config              -- The all-zero configuration.
        zero_div                 -- The all-zero divisor.

    Python

        >>> from sage.all import *
        >>> Sandpile.help() # long time
        For detailed help with any method FOO listed below,
        enter "Sandpile.FOO?" or enter "S.FOO?" for any Sandpile S.
        <BLANKLINE>
        all_k_config             -- The constant configuration with all values set to k.
        all_k_div                -- The divisor with all values set to k.
        avalanche_polynomial     -- The avalanche polynomial.
        betti                    -- Return the Betti table for the homogeneous toppling ideal.
        betti_complexes          -- The support-complexes with non-trivial homology.
        burning_config           -- The minimal burning configuration.
        burning_script           -- A script for the minimal burning configuration.
        canonical_divisor        -- The canonical divisor.
        dict                     -- A dictionary of dictionaries representing a directed graph.
        genus                    -- The genus: (# non-loop edges) - (# vertices) + 1.
        groebner                 -- Return a Groebner basis for the homogeneous toppling ideal.
        group_gens               -- A minimal list of generators for the sandpile group.
        group_order              -- The size of the sandpile group.
        h_vector                 -- The number of superstable configurations in each degree.
        help                     -- List of Sandpile-specific methods (not inherited from ...Graph...).
        hilbert_function         -- The Hilbert function of the homogeneous toppling ideal.
        ideal                    -- The saturated homogeneous toppling ideal.
        identity                 -- The identity configuration.
        in_degree                -- The in-degree of a vertex or a list of all in-degrees.
        invariant_factors        -- The invariant factors of the sandpile group.
        is_undirected            -- Is the underlying graph undirected?
        jacobian_representatives -- Representatives for the elements of the Jacobian group.
        laplacian                -- Return the Laplacian matrix of the graph.
        markov_chain             -- The sandpile Markov chain for configurations or divisors.
        max_stable               -- The maximal stable configuration.
        max_stable_div           -- The maximal stable divisor.
        max_superstables         -- The maximal superstable configurations.
        min_recurrents           -- The minimal recurrent elements.
        nonsink_vertices         -- The nonsink vertices.
        nonspecial_divisors      -- The nonspecial divisors.
        out_degree               -- The out-degree of a vertex or a list of all out-degrees.
        picard_representatives   -- Representatives of the divisor classes of degree d in the Picard group.
        points                   -- Generators for the multiplicative group of zeros of the sandpile ideal.
        postulation              -- The postulation number of the toppling ideal.
        recurrents               -- The recurrent configurations.
        reduced_laplacian        -- Return the reduced Laplacian matrix of the graph.
        reorder_vertices         -- A copy of the sandpile with vertex names permuted.
        resolution               -- A minimal free resolution of the homogeneous toppling ideal.
        ring                     -- The ring containing the homogeneous toppling ideal.
        show                     -- Draw the underlying graph.
        show3d                   -- Draw the underlying graph.
        sink                     -- The sink vertex.
        smith_form               -- The Smith normal form for the Laplacian.
        solve                    -- Approximations of the complex affine zeros of the sandpile ideal.
        stable_configs           -- Generator for all stable configurations.
        stationary_density       -- The stationary density of the sandpile.
        superstables             -- The superstable configurations.
        symmetric_recurrents     -- The symmetric recurrent configurations.
        tutte_polynomial         -- The Tutte polynomial of the underlying graph.
        unsaturated_ideal        -- The unsaturated, homogeneous toppling ideal.
        version                  -- The version number of Sage Sandpiles.
        zero_config              -- The all-zero configuration.
        zero_div                 -- The all-zero divisor.

  hilbert_function()

    The Hilbert function of the homogeneous toppling ideal.

    OUTPUT: list of nonnegative integers

    EXAMPLES:

    Sage

        sage: s = sandpiles.Wheel(5)
        sage: s.hilbert_function()
        [1, 5, 15, 31, 45]
        sage: s.h_vector()
        [1, 4, 10, 16, 14]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Wheel(Integer(5))
        >>> s.hilbert_function()
        [1, 5, 15, 31, 45]
        >>> s.h_vector()
        [1, 4, 10, 16, 14]

  ideal(gens=False)

    The saturated homogeneous toppling ideal. If `gens` is `True`, the generators for the ideal are returned instead.

    INPUT:

    - `gens` – boolean (default: `False`)

    OUTPUT: ideal or, optionally, the generators of an ideal

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: S.ideal()
        Ideal (x2*x1 - x0^2, x3^2 - x0^2, x1^3 - x3*x2*x0,
               x3*x1^2 - x2^2*x0, x2^3 - x3*x1*x0, x3*x2^2 - x1^2*x0)
         of Multivariate Polynomial Ring in x3, x2, x1, x0 over Rational Field
        sage: S.ideal(True)
        [x2*x1 - x0^2, x3^2 - x0^2, x1^3 - x3*x2*x0,
         x3*x1^2 - x2^2*x0, x2^3 - x3*x1*x0, x3*x2^2 - x1^2*x0]
        sage: S.ideal().gens()  # another way to get the generators
        [x2*x1 - x0^2, x3^2 - x0^2, x1^3 - x3*x2*x0,
         x3*x1^2 - x2^2*x0, x2^3 - x3*x1*x0, x3*x2^2 - x1^2*x0]

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> S.ideal()
        Ideal (x2*x1 - x0^2, x3^2 - x0^2, x1^3 - x3*x2*x0,
               x3*x1^2 - x2^2*x0, x2^3 - x3*x1*x0, x3*x2^2 - x1^2*x0)
         of Multivariate Polynomial Ring in x3, x2, x1, x0 over Rational Field
        >>> S.ideal(True)
        [x2*x1 - x0^2, x3^2 - x0^2, x1^3 - x3*x2*x0,
         x3*x1^2 - x2^2*x0, x2^3 - x3*x1*x0, x3*x2^2 - x1^2*x0]
        >>> S.ideal().gens()  # another way to get the generators
        [x2*x1 - x0^2, x3^2 - x0^2, x1^3 - x3*x2*x0,
         x3*x1^2 - x2^2*x0, x2^3 - x3*x1*x0, x3*x2^2 - x1^2*x0]

  identity(verbose=True)

    The identity configuration.

    If `verbose` is `False`, the configuration is converted to a list of integers.

    INPUT:

    - `verbose` – boolean (default: `True`)

    OUTPUT: SandpileConfig or a list of integers

    EXAMPLES:

    Sage

        sage: s = sandpiles.Diamond()
        sage: s.identity()
        {1: 2, 2: 2, 3: 0}
        sage: s.identity(False)
        [2, 2, 0]
        sage: s.identity() & s.max_stable() == s.max_stable()
        True

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Diamond()
        >>> s.identity()
        {1: 2, 2: 2, 3: 0}
        >>> s.identity(False)
        [2, 2, 0]
        >>> s.identity() & s.max_stable() == s.max_stable()
        True

  in_degree(v=None)

    The in-degree of a vertex or a list of all in-degrees.

    INPUT:

    - `v` – (optional) vertex name

    OUTPUT: integer or dict

    EXAMPLES:

    Sage

        sage: s = sandpiles.House()
        sage: s.in_degree()
        {0: 2, 1: 2, 2: 3, 3: 3, 4: 2}
        sage: s.in_degree(2)
        3

    Python

        >>> from sage.all import *
        >>> s = sandpiles.House()
        >>> s.in_degree()
        {0: 2, 1: 2, 2: 3, 3: 3, 4: 2}
        >>> s.in_degree(Integer(2))
        3

  invariant_factors()

    The invariant factors of the sandpile group.

    OUTPUT: list of integers

    EXAMPLES:

    Sage

        sage: s = sandpiles.Grid(2,2)
        sage: s.invariant_factors()
        [1, 1, 8, 24]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Grid(Integer(2),Integer(2))
        >>> s.invariant_factors()
        [1, 1, 8, 24]

  is_undirected()

    Is the underlying graph undirected? `True` if \((u,v)\) is and edge if and only if \((v,u)\) is an edge, each edge with the same weight.

    OUTPUT: boolean

    EXAMPLES:

    Sage

        sage: sandpiles.Complete(4).is_undirected()
        True
        sage: s = Sandpile({0:[1,2], 1:[0,2], 2:[0]}, 0)
        sage: s.is_undirected()
        False

    Python

        >>> from sage.all import *
        >>> sandpiles.Complete(Integer(4)).is_undirected()
        True
        >>> s = Sandpile({Integer(0):[Integer(1),Integer(2)], Integer(1):[Integer(0),Integer(2)], Integer(2):[Integer(0)]}, Integer(0))
        >>> s.is_undirected()
        False

  jacobian_representatives(verbose=True)

    Representatives for the elements of the Jacobian group. If `verbose` is `False`, then lists representing the divisors are returned.

    INPUT:

    - `verbose` – boolean (default: `True`)

    OUTPUT: list of SandpileDivisor (or of lists representing divisors)

    EXAMPLES:

    For an undirected graph, divisors of the form `s - deg(s)*sink` as `s` varies over the superstables forms a distinct set of representatives for the Jacobian group.:

    Sage

        sage: s = sandpiles.Complete(3)
        sage: s.superstables(False)
        [[0, 0], [0, 1], [1, 0]]
        sage: s.jacobian_representatives(False)
        [[0, 0, 0], [-1, 0, 1], [-1, 1, 0]]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(3))
        >>> s.superstables(False)
        [[0, 0], [0, 1], [1, 0]]
        >>> s.jacobian_representatives(False)
        [[0, 0, 0], [-1, 0, 1], [-1, 1, 0]]

    If the graph is directed, the representatives described above may by equivalent modulo the rowspan of the Laplacian matrix:

    Sage

        sage: s = Sandpile({0: {1: 1, 2: 2}, 1: {0: 2, 2: 4}, 2: {0: 4, 1: 2}},0)
        sage: s.group_order()
        28
        sage: s.jacobian_representatives()
        [{0: -5, 1: 3, 2: 2}, {0: -4, 1: 3, 2: 1}]

    [Python]

        >>> from sage.all import *
        >>> s = Sandpile({Integer(0): {Integer(1): Integer(1), Integer(2): Integer(2)}, Integer(1): {Integer(0): Integer(2), Integer(2): Integer(4)}, Integer(2): {Integer(0): Integer(4), Integer(1): Integer(2)}},Integer(0))
        >>> s.group_order()
        28
        >>> s.jacobian_representatives()
        [{0: -5, 1: 3, 2: 2}, {0: -4, 1: 3, 2: 1}]

    Let \(\tau\) be the nonnegative generator of the kernel of the transpose of the Laplacian, and let \(\tau_s\) be its sink component, then the sandpile group is isomorphic to the direct sum of the cyclic group of order \(\tau_s\) and the Jacobian group. In the example above, we have:

    Sage

        sage: s.laplacian().left_kernel()
        Free module of degree 3 and rank 1 over Integer Ring
        Echelon basis matrix:
        [14  5  8]

    Python

        >>> from sage.all import *
        >>> s.laplacian().left_kernel()
        Free module of degree 3 and rank 1 over Integer Ring
        Echelon basis matrix:
        [14  5  8]

    Note

    The Jacobian group is the set of all divisors of degree zero modulo the integer rowspan of the Laplacian matrix.

  laplacian()

    Return the Laplacian matrix of the graph.

    Its rows encode the vertex firing rules.

    OUTPUT: matrix

    EXAMPLES:

    Sage

        sage: G = sandpiles.Diamond()
        sage: G.laplacian()
        [ 2 -1 -1  0]
        [-1  3 -1 -1]
        [-1 -1  3 -1]
        [ 0 -1 -1  2]

    Python

        >>> from sage.all import *
        >>> G = sandpiles.Diamond()
        >>> G.laplacian()
        [ 2 -1 -1  0]
        [-1  3 -1 -1]
        [-1 -1  3 -1]
        [ 0 -1 -1  2]

    Warning

    The function `laplacian_matrix` should be avoided. It returns the indegree version of the Laplacian.

  markov_chain(state, distrib=None)

    The sandpile Markov chain for configurations or divisors. The chain starts at `state`. See NOTE for details.

    INPUT:

    - `state` – SandpileConfig, SandpileDivisor, or list representing one of these

    - `distrib` – (optional) list of nonnegative numbers summing to 1 (representing a prob. dist.)

    OUTPUT: generator for Markov chain (see NOTE)

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(4)
        sage: m = s.markov_chain([0,0,0])
        sage: next(m)          # random
        {1: 0, 2: 0, 3: 0}
        sage: next(m).values() # random
        [0, 0, 0]
        sage: next(m).values() # random
        [0, 0, 0]
        sage: next(m).values() # random
        [0, 0, 0]
        sage: next(m).values() # random
        [0, 1, 0]
        sage: next(m).values() # random
        [0, 2, 0]
        sage: next(m).values() # random
        [0, 2, 1]
        sage: next(m).values() # random
        [1, 2, 1]
        sage: next(m).values() # random
        [2, 2, 1]
        sage: m = s.markov_chain(s.zero_div(), [0.1,0.1,0.1,0.7])
        sage: next(m).values() # random
        [0, 0, 0, 1]
        sage: next(m).values() # random
        [0, 0, 1, 1]
        sage: next(m).values() # random
        [0, 0, 1, 2]
        sage: next(m).values() # random
        [1, 1, 2, 0]
        sage: next(m).values() # random
        [1, 1, 2, 1]
        sage: next(m).values() # random
        [1, 1, 2, 2]
        sage: next(m).values() # random
        [1, 1, 2, 3]
        sage: next(m).values() # random
        [1, 1, 2, 4]
        sage: next(m).values() # random
        [1, 1, 3, 4]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(4))
        >>> m = s.markov_chain([Integer(0),Integer(0),Integer(0)])
        >>> next(m)          # random
        {1: 0, 2: 0, 3: 0}
        >>> next(m).values() # random
        [0, 0, 0]
        >>> next(m).values() # random
        [0, 0, 0]
        >>> next(m).values() # random
        [0, 0, 0]
        >>> next(m).values() # random
        [0, 1, 0]
        >>> next(m).values() # random
        [0, 2, 0]
        >>> next(m).values() # random
        [0, 2, 1]
        >>> next(m).values() # random
        [1, 2, 1]
        >>> next(m).values() # random
        [2, 2, 1]
        >>> m = s.markov_chain(s.zero_div(), [RealNumber('0.1'),RealNumber('0.1'),RealNumber('0.1'),RealNumber('0.7')])
        >>> next(m).values() # random
        [0, 0, 0, 1]
        >>> next(m).values() # random
        [0, 0, 1, 1]
        >>> next(m).values() # random
        [0, 0, 1, 2]
        >>> next(m).values() # random
        [1, 1, 2, 0]
        >>> next(m).values() # random
        [1, 1, 2, 1]
        >>> next(m).values() # random
        [1, 1, 2, 2]
        >>> next(m).values() # random
        [1, 1, 2, 3]
        >>> next(m).values() # random
        [1, 1, 2, 4]
        >>> next(m).values() # random
        [1, 1, 3, 4]

    Note

    The `closed sandpile Markov chain` has state space consisting of the configurations on a sandpile. It transitions from a state by choosing a vertex at random (according to the probability distribution `distrib`), dropping a grain of sand at that vertex, and stabilizing. If the chosen vertex is the sink, the chain stays at the current state.

    The `open sandpile Markov chain` has state space consisting of the recurrent elements, i.e., the state space is the sandpile group. It transitions from the configuration \(c\) by choosing a vertex \(v\) at random according to `distrib`. The next state is the stabilization of \(c+v\). If \(v\) is the sink vertex, then the stabilization of \(c+v\) is defined to be \(c\).

    Note that in either case, if `distrib` is specified, its length is equal to the total number of vertices (including the sink).

    REFERENCES:

    - [Lev2014]

  max_stable()

    The maximal stable configuration.

    OUTPUT: SandpileConfig (the maximal stable configuration)

    EXAMPLES:

    Sage

        sage: S = sandpiles.House()
        sage: S.max_stable()
        {1: 1, 2: 2, 3: 2, 4: 1}

    Python

        >>> from sage.all import *
        >>> S = sandpiles.House()
        >>> S.max_stable()
        {1: 1, 2: 2, 3: 2, 4: 1}

  max_stable_div()

    The maximal stable divisor.

    OUTPUT:

    SandpileDivisor (the maximal stable divisor)

    EXAMPLES:

    Sage

        sage: s = sandpiles.Diamond()
        sage: s.max_stable_div()
        {0: 1, 1: 2, 2: 2, 3: 1}
        sage: s.out_degree()
        {0: 2, 1: 3, 2: 3, 3: 2}

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Diamond()
        >>> s.max_stable_div()
        {0: 1, 1: 2, 2: 2, 3: 1}
        >>> s.out_degree()
        {0: 2, 1: 3, 2: 3, 3: 2}

  max_superstables(verbose=True)

    The maximal superstable configurations. If the underlying graph is undirected, these are the superstables of highest degree. If `verbose` is `False`, the configurations are converted to lists of integers.

    INPUT:

    - `verbose` – boolean (default: `True`)

    OUTPUT: tuple of SandpileConfig

    EXAMPLES:

    Sage

        sage: s = sandpiles.Diamond()
        sage: s.superstables(False)
        [[0, 0, 0],
         [0, 0, 1],
         [1, 0, 1],
         [0, 2, 0],
         [2, 0, 0],
         [0, 1, 1],
         [1, 0, 0],
         [0, 1, 0]]
        sage: s.max_superstables(False)
        [[1, 0, 1], [0, 2, 0], [2, 0, 0], [0, 1, 1]]
        sage: s.h_vector()
        [1, 3, 4]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Diamond()
        >>> s.superstables(False)
        [[0, 0, 0],
         [0, 0, 1],
         [1, 0, 1],
         [0, 2, 0],
         [2, 0, 0],
         [0, 1, 1],
         [1, 0, 0],
         [0, 1, 0]]
        >>> s.max_superstables(False)
        [[1, 0, 1], [0, 2, 0], [2, 0, 0], [0, 1, 1]]
        >>> s.h_vector()
        [1, 3, 4]

  min_recurrents(verbose=True)

    The minimal recurrent elements. If the underlying graph is undirected, these are the recurrent elements of least degree. If `verbose` is `False`, the configurations are converted to lists of integers.

    INPUT:

    - `verbose` – boolean (default: `True`)

    OUTPUT: list of SandpileConfig

    EXAMPLES:

    Sage

        sage: s = sandpiles.Diamond()
        sage: s.recurrents(False)
        [[2, 2, 1],
         [2, 2, 0],
         [1, 2, 0],
         [2, 0, 1],
         [0, 2, 1],
         [2, 1, 0],
         [1, 2, 1],
         [2, 1, 1]]
        sage: s.min_recurrents(False)
        [[1, 2, 0], [2, 0, 1], [0, 2, 1], [2, 1, 0]]
        sage: [i.deg() for i in s.recurrents()]
        [5, 4, 3, 3, 3, 3, 4, 4]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Diamond()
        >>> s.recurrents(False)
        [[2, 2, 1],
         [2, 2, 0],
         [1, 2, 0],
         [2, 0, 1],
         [0, 2, 1],
         [2, 1, 0],
         [1, 2, 1],
         [2, 1, 1]]
        >>> s.min_recurrents(False)
        [[1, 2, 0], [2, 0, 1], [0, 2, 1], [2, 1, 0]]
        >>> [i.deg() for i in s.recurrents()]
        [5, 4, 3, 3, 3, 3, 4, 4]

  nonsink_vertices()

    The nonsink vertices.

    OUTPUT: list of vertices

    EXAMPLES:

    Sage

        sage: s = sandpiles.Grid(2,3)
        sage: s.nonsink_vertices()
        [(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3)]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Grid(Integer(2),Integer(3))
        >>> s.nonsink_vertices()
        [(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3)]

  nonspecial_divisors(verbose=True)

    The nonspecial divisors. Only for undirected graphs. (See NOTE.)

    INPUT:

    - `verbose` – boolean (default: `True`)

    OUTPUT: list (of divisors)

    EXAMPLES:

    Sage

        sage: S = sandpiles.Complete(4)
        sage: ns = S.nonspecial_divisors()
        sage: D = ns[0]
        sage: D.values()
        [-1, 0, 1, 2]
        sage: D.deg()
        2
        sage: [i.effective_div() for i in ns]
        [[], [], [], [], [], []]

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Complete(Integer(4))
        >>> ns = S.nonspecial_divisors()
        >>> D = ns[Integer(0)]
        >>> D.values()
        [-1, 0, 1, 2]
        >>> D.deg()
        2
        >>> [i.effective_div() for i in ns]
        [[], [], [], [], [], []]

    Note

    The “nonspecial divisors” are those divisors of degree \(g-1\) with empty linear system. The term is only defined for undirected graphs. Here, \(g = |E| - |V| + 1\) is the genus of the graph (not counting loops as part of \(|E|\)). If `verbose` is `False`, the divisors are converted to lists of integers.

    Warning

    The underlying graph must be undirected.

  out_degree(v=None)

    The out-degree of a vertex or a list of all out-degrees.

    INPUT:

    - `v` – (optional) vertex name

    OUTPUT: integer or dict

    EXAMPLES:

    Sage

        sage: s = sandpiles.House()
        sage: s.out_degree()
        {0: 2, 1: 2, 2: 3, 3: 3, 4: 2}
        sage: s.out_degree(2)
        3

    Python

        >>> from sage.all import *
        >>> s = sandpiles.House()
        >>> s.out_degree()
        {0: 2, 1: 2, 2: 3, 3: 3, 4: 2}
        >>> s.out_degree(Integer(2))
        3

  picard_representatives(d, verbose=True)

    Representatives of the divisor classes of degree \(d\) in the Picard group.

    (Also see the documentation for `jacobian_representatives`.)

    INPUT:

    - `d` – integer

    - `verbose` – boolean (default: `True`)

    OUTPUT: slist of SandpileDivisors (or lists representing divisors)

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(3)
        sage: s.superstables(False)
        [[0, 0], [0, 1], [1, 0]]
        sage: s.jacobian_representatives(False)
        [[0, 0, 0], [-1, 0, 1], [-1, 1, 0]]
        sage: s.picard_representatives(3,False)
        [[3, 0, 0], [2, 0, 1], [2, 1, 0]]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(3))
        >>> s.superstables(False)
        [[0, 0], [0, 1], [1, 0]]
        >>> s.jacobian_representatives(False)
        [[0, 0, 0], [-1, 0, 1], [-1, 1, 0]]
        >>> s.picard_representatives(Integer(3),False)
        [[3, 0, 0], [2, 0, 1], [2, 1, 0]]

  points()

    Generators for the multiplicative group of zeros of the sandpile ideal.

    OUTPUT: list of complex numbers

    EXAMPLES:

    The sandpile group in this example is cyclic, and hence there is a single generator for the group of solutions.

    Sage

        sage: S = sandpiles.Complete(4)
        sage: S.points()
        [[-I, I, 1], [-I, 1, I]]

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Complete(Integer(4))
        >>> S.points()
        [[-I, I, 1], [-I, 1, I]]

  postulation()

    The postulation number of the toppling ideal. This is the largest weight of a superstable configuration of the graph.

    OUTPUT: nonnegative integer

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(4)
        sage: s.postulation()
        3

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(4))
        >>> s.postulation()
        3

  recurrents(verbose=True)

    The recurrent configurations. If `verbose` is `False`, the configurations are converted to lists of integers.

    INPUT:

    - `verbose` – boolean (default: `True`)

    OUTPUT: list of recurrent configurations

    EXAMPLES:

    Sage

        sage: r = Sandpile(graphs.HouseXGraph(),0).recurrents()
        sage: r[:3]
        [{1: 2, 2: 3, 3: 3, 4: 1},
         {1: 1, 2: 3, 3: 3, 4: 0},
         {1: 1, 2: 3, 3: 3, 4: 1}]
        sage: sandpiles.Complete(4).recurrents(False)
        [[2, 2, 2],
         [2, 2, 1],
         [2, 1, 2],
         [1, 2, 2],
         [2, 2, 0],
         [2, 0, 2],
         [0, 2, 2],
         [2, 1, 1],
         [1, 2, 1],
         [1, 1, 2],
         [2, 1, 0],
         [2, 0, 1],
         [1, 2, 0],
         [1, 0, 2],
         [0, 2, 1],
         [0, 1, 2]]
        sage: sandpiles.Cycle(4).recurrents(False)
        [[1, 1, 1], [0, 1, 1], [1, 0, 1], [1, 1, 0]]

    Python

        >>> from sage.all import *
        >>> r = Sandpile(graphs.HouseXGraph(),Integer(0)).recurrents()
        >>> r[:Integer(3)]
        [{1: 2, 2: 3, 3: 3, 4: 1},
         {1: 1, 2: 3, 3: 3, 4: 0},
         {1: 1, 2: 3, 3: 3, 4: 1}]
        >>> sandpiles.Complete(Integer(4)).recurrents(False)
        [[2, 2, 2],
         [2, 2, 1],
         [2, 1, 2],
         [1, 2, 2],
         [2, 2, 0],
         [2, 0, 2],
         [0, 2, 2],
         [2, 1, 1],
         [1, 2, 1],
         [1, 1, 2],
         [2, 1, 0],
         [2, 0, 1],
         [1, 2, 0],
         [1, 0, 2],
         [0, 2, 1],
         [0, 1, 2]]
        >>> sandpiles.Cycle(Integer(4)).recurrents(False)
        [[1, 1, 1], [0, 1, 1], [1, 0, 1], [1, 1, 0]]

  reduced_laplacian()

    Return the reduced Laplacian matrix of the graph.

    OUTPUT: matrix

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: S.laplacian()
        [ 2 -1 -1  0]
        [-1  3 -1 -1]
        [-1 -1  3 -1]
        [ 0 -1 -1  2]
        sage: S.reduced_laplacian()
        [ 3 -1 -1]
        [-1  3 -1]
        [-1 -1  2]

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> S.laplacian()
        [ 2 -1 -1  0]
        [-1  3 -1 -1]
        [-1 -1  3 -1]
        [ 0 -1 -1  2]
        >>> S.reduced_laplacian()
        [ 3 -1 -1]
        [-1  3 -1]
        [-1 -1  2]

    Note

    This is the Laplacian matrix with the row and column indexed by the sink vertex removed.

  reorder_vertices()

    A copy of the sandpile with vertex names permuted.

    After reordering, vertex \(u\) comes before vertex \(v\) in the list of vertices if \(u\) is closer to the sink.

    OUTPUT: Sandpile

    EXAMPLES:

    Sage

        sage: S = Sandpile({0:[1], 2:[0,1], 1:[2]})
        sage: S.dict()
        {0: {1: 1}, 1: {2: 1}, 2: {0: 1, 1: 1}}
        sage: T = S.reorder_vertices()

    Python

        >>> from sage.all import *
        >>> S = Sandpile({Integer(0):[Integer(1)], Integer(2):[Integer(0),Integer(1)], Integer(1):[Integer(2)]})
        >>> S.dict()
        {0: {1: 1}, 1: {2: 1}, 2: {0: 1, 1: 1}}
        >>> T = S.reorder_vertices()

    The vertices 1 and 2 have been swapped:

    Sage

        sage: T.dict()
        {0: {1: 1}, 1: {0: 1, 2: 1}, 2: {0: 1}}

    [Python]

        >>> from sage.all import *
        >>> T.dict()
        {0: {1: 1}, 1: {0: 1, 2: 1}, 2: {0: 1}}

  resolution(verbose=False)

    A minimal free resolution of the homogeneous toppling ideal.

    If `verbose` is `True`, then all of the mappings are returned. Otherwise, the resolution is summarized.

    INPUT:

    - `verbose` – boolean (default: `False`)

    OUTPUT: free resolution of the toppling ideal

    EXAMPLES:

    Sage

        sage: S = Sandpile({0: {}, 1: {0: 1, 2: 1, 3: 4}, 2: {3: 5}, 3: {1: 1, 2: 1}},0)
        sage: S.resolution()  # a Gorenstein sandpile graph
        'R^1 <-- R^5 <-- R^5 <-- R^1'
        sage: S.resolution(True)
        [
        [ x1^2 - x3*x0 x3*x1 - x2*x0  x3^2 - x2*x1  x2*x3 - x0^2  x2^2 - x1*x0],

        [ x3  x2   0  x0   0]  [ x2^2 - x1*x0]
        [-x1 -x3  x2   0 -x0]  [-x2*x3 + x0^2]
        [ x0  x1   0  x2   0]  [-x3^2 + x2*x1]
        [  0   0 -x1 -x3  x2]  [x3*x1 - x2*x0]
        [  0   0  x0  x1 -x3], [ x1^2 - x3*x0]
        ]
        sage: r = S.resolution(True)
        sage: r[0]*r[1]
        [0 0 0 0 0]
        sage: r[1]*r[2]
        [0]
        [0]
        [0]
        [0]
        [0]

    Python

        >>> from sage.all import *
        >>> S = Sandpile({Integer(0): {}, Integer(1): {Integer(0): Integer(1), Integer(2): Integer(1), Integer(3): Integer(4)}, Integer(2): {Integer(3): Integer(5)}, Integer(3): {Integer(1): Integer(1), Integer(2): Integer(1)}},Integer(0))
        >>> S.resolution()  # a Gorenstein sandpile graph
        'R^1 <-- R^5 <-- R^5 <-- R^1'
        >>> S.resolution(True)
        [
        [ x1^2 - x3*x0 x3*x1 - x2*x0  x3^2 - x2*x1  x2*x3 - x0^2  x2^2 - x1*x0],
        <BLANKLINE>
        [ x3  x2   0  x0   0]  [ x2^2 - x1*x0]
        [-x1 -x3  x2   0 -x0]  [-x2*x3 + x0^2]
        [ x0  x1   0  x2   0]  [-x3^2 + x2*x1]
        [  0   0 -x1 -x3  x2]  [x3*x1 - x2*x0]
        [  0   0  x0  x1 -x3], [ x1^2 - x3*x0]
        ]
        >>> r = S.resolution(True)
        >>> r[Integer(0)]*r[Integer(1)]
        [0 0 0 0 0]
        >>> r[Integer(1)]*r[Integer(2)]
        [0]
        [0]
        [0]
        [0]
        [0]

  ring()

    The ring containing the homogeneous toppling ideal.

    OUTPUT: ring

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: S.ring()
        Multivariate Polynomial Ring in x3, x2, x1, x0 over Rational Field
        sage: S.ring().gens()
        (x3, x2, x1, x0)

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> S.ring()
        Multivariate Polynomial Ring in x3, x2, x1, x0 over Rational Field
        >>> S.ring().gens()
        (x3, x2, x1, x0)

    Note

    The indeterminate `xi` corresponds to the \(i\)-th vertex as listed my the method `vertices`. The term-ordering is degrevlex with indeterminates ordered according to their distance from the sink (larger indeterminates are further from the sink).

  show(**kwds)

    Draw the underlying graph.

    INPUT:

    - `kwds` – (optional) arguments passed to the show method for Graph or DiGraph

    EXAMPLES:

    Sage

        sage: S = Sandpile({0:[], 1:[0,3,4], 2:[0,3,5], 3:[2,5], 4:[1,1], 5:[2,4]})
        sage: S.show()
        sage: S.show(graph_border=True, edge_labels=True)

    Python

        >>> from sage.all import *
        >>> S = Sandpile({Integer(0):[], Integer(1):[Integer(0),Integer(3),Integer(4)], Integer(2):[Integer(0),Integer(3),Integer(5)], Integer(3):[Integer(2),Integer(5)], Integer(4):[Integer(1),Integer(1)], Integer(5):[Integer(2),Integer(4)]})
        >>> S.show()
        >>> S.show(graph_border=True, edge_labels=True)

  show3d(**kwds)

    Draw the underlying graph.

    INPUT:

    - `kwds` – (optional) arguments passed to the show method for Graph or DiGraph

    EXAMPLES:

    Sage

        sage: S = sandpiles.House()
        sage: S.show3d()                    # long time

    Python

        >>> from sage.all import *
        >>> S = sandpiles.House()
        >>> S.show3d()                    # long time

  sink()

    The sink vertex.

    OUTPUT: sink vertex

    EXAMPLES:

    Sage

        sage: G = sandpiles.House()
        sage: G.sink()
        0
        sage: H = sandpiles.Grid(2,2)
        sage: H.sink()
        (0, 0)
        sage: type(H.sink())
        <... 'tuple'>

    Python

        >>> from sage.all import *
        >>> G = sandpiles.House()
        >>> G.sink()
        0
        >>> H = sandpiles.Grid(Integer(2),Integer(2))
        >>> H.sink()
        (0, 0)
        >>> type(H.sink())
        <... 'tuple'>

  smith_form()

    The Smith normal form for the Laplacian. In detail: a list of integer matrices \(D, U, V\) such that \(ULV = D\) where \(L\) is the transpose of the Laplacian, \(D\) is diagonal, and \(U\) and \(V\) are invertible over the integers.

    OUTPUT: list of integer matrices

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(4)
        sage: D,U,V = s.smith_form()
        sage: D
        [1 0 0 0]
        [0 4 0 0]
        [0 0 4 0]
        [0 0 0 0]
        sage: U*s.laplacian()*V == D  # Laplacian symmetric => transpose not necessary
        True

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(4))
        >>> D,U,V = s.smith_form()
        >>> D
        [1 0 0 0]
        [0 4 0 0]
        [0 0 4 0]
        [0 0 0 0]
        >>> U*s.laplacian()*V == D  # Laplacian symmetric => transpose not necessary
        True

  solve()

    Approximations of the complex affine zeros of the sandpile ideal.

    OUTPUT: list of complex numbers

    EXAMPLES:

    Sage

        sage: S = Sandpile({0: {}, 1: {2: 2}, 2: {0: 4, 1: 1}}, 0)
        sage: Z = S.solve(); Z
        [[-0.707107000000000 + 0.707107000000000*I,
          0.707107000000000 - 0.707107000000000*I],
         [-0.707107000000000 - 0.707107000000000*I,
          0.707107000000000 + 0.707107000000000*I],
         [-I, -I],
         [I, I],
         [0.707107000000000 + 0.707107000000000*I,
          -0.707107000000000 - 0.707107000000000*I],
         [0.707107000000000 - 0.707107000000000*I,
          -0.707107000000000 + 0.707107000000000*I],
         [1, 1],
         [-1, -1]]
        sage: len(Z)
        8
        sage: S.group_order()
        8

    Python

        >>> from sage.all import *
        >>> S = Sandpile({Integer(0): {}, Integer(1): {Integer(2): Integer(2)}, Integer(2): {Integer(0): Integer(4), Integer(1): Integer(1)}}, Integer(0))
        >>> Z = S.solve(); Z
        [[-0.707107000000000 + 0.707107000000000*I,
          0.707107000000000 - 0.707107000000000*I],
         [-0.707107000000000 - 0.707107000000000*I,
          0.707107000000000 + 0.707107000000000*I],
         [-I, -I],
         [I, I],
         [0.707107000000000 + 0.707107000000000*I,
          -0.707107000000000 - 0.707107000000000*I],
         [0.707107000000000 - 0.707107000000000*I,
          -0.707107000000000 + 0.707107000000000*I],
         [1, 1],
         [-1, -1]]
        >>> len(Z)
        8
        >>> S.group_order()
        8

    Note

    The solutions form a multiplicative group isomorphic to the sandpile group. Generators for this group are given exactly by `points()`.

  stable_configs(smax=None)

    Generator for all stable configurations.

    If `smax` is provided, then the generator gives all stable configurations less than or equal to `smax`. If `smax` does not represent a stable configuration, then each component of `smax` is replaced by the corresponding component of the maximal stable configuration.

    INPUT:

    - `smax` – (optional) SandpileConfig or list representing a SandpileConfig

    OUTPUT: generator for all stable configurations

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(3)
        sage: a = s.stable_configs()
        sage: next(a)
        {1: 0, 2: 0}
        sage: [i.values() for i in a]
        [[0, 1], [1, 0], [1, 1]]
        sage: b = s.stable_configs([1,0])
        sage: list(b)
        [{1: 0, 2: 0}, {1: 1, 2: 0}]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(3))
        >>> a = s.stable_configs()
        >>> next(a)
        {1: 0, 2: 0}
        >>> [i.values() for i in a]
        [[0, 1], [1, 0], [1, 1]]
        >>> b = s.stable_configs([Integer(1),Integer(0)])
        >>> list(b)
        [{1: 0, 2: 0}, {1: 1, 2: 0}]

  stationary_density()

    The stationary density of the sandpile.

    OUTPUT: rational number

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(3)
        sage: s.stationary_density()
        10/9

        sage: s = Sandpile(digraphs.DeBruijn(2,2),'00')
        sage: s.stationary_density()
        9/8

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(3))
        >>> s.stationary_density()
        10/9

        >>> s = Sandpile(digraphs.DeBruijn(Integer(2),Integer(2)),'00')
        >>> s.stationary_density()
        9/8

    Note

    The stationary density of a sandpile is the sum \(\sum_c (\deg(c) + \deg(s))\) where \(\deg(s)\) is the degree of the sink and the sum is over all recurrent configurations.

    REFERENCES:

    - [Lev2014]

  superstables(verbose=True)

    The superstable configurations. If `verbose` is `False`, the configurations are converted to lists of integers. Superstables for undirected graphs are also known as `G-parking functions`.

    INPUT:

    - `verbose` – boolean (default: `True`)

    OUTPUT: list of SandpileConfig

    EXAMPLES:

    Sage

        sage: sp = Sandpile(graphs.HouseXGraph(),0).superstables()
        sage: sp[:3]
        [{1: 0, 2: 0, 3: 0, 4: 0},
         {1: 1, 2: 0, 3: 0, 4: 1},
         {1: 1, 2: 0, 3: 0, 4: 0}]
        sage: sandpiles.Complete(4).superstables(False)
        [[0, 0, 0],
         [0, 0, 1],
         [0, 1, 0],
         [1, 0, 0],
         [0, 0, 2],
         [0, 2, 0],
         [2, 0, 0],
         [0, 1, 1],
         [1, 0, 1],
         [1, 1, 0],
         [0, 1, 2],
         [0, 2, 1],
         [1, 0, 2],
         [1, 2, 0],
         [2, 0, 1],
         [2, 1, 0]]
        sage: sandpiles.Cycle(4).superstables(False)
        [[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]]

    Python

        >>> from sage.all import *
        >>> sp = Sandpile(graphs.HouseXGraph(),Integer(0)).superstables()
        >>> sp[:Integer(3)]
        [{1: 0, 2: 0, 3: 0, 4: 0},
         {1: 1, 2: 0, 3: 0, 4: 1},
         {1: 1, 2: 0, 3: 0, 4: 0}]
        >>> sandpiles.Complete(Integer(4)).superstables(False)
        [[0, 0, 0],
         [0, 0, 1],
         [0, 1, 0],
         [1, 0, 0],
         [0, 0, 2],
         [0, 2, 0],
         [2, 0, 0],
         [0, 1, 1],
         [1, 0, 1],
         [1, 1, 0],
         [0, 1, 2],
         [0, 2, 1],
         [1, 0, 2],
         [1, 2, 0],
         [2, 0, 1],
         [2, 1, 0]]
        >>> sandpiles.Cycle(Integer(4)).superstables(False)
        [[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]]

  symmetric_recurrents(orbits)

    The symmetric recurrent configurations.

    INPUT:

    - `orbits` – list of lists partitioning the vertices

    OUTPUT: list of recurrent configurations

    EXAMPLES:

    Sage

        sage: S = Sandpile({0: {},
        ....:              1: {0: 1, 2: 1, 3: 1},
        ....:              2: {1: 1, 3: 1, 4: 1},
        ....:              3: {1: 1, 2: 1, 4: 1},
        ....:              4: {2: 1, 3: 1}})
        sage: S.symmetric_recurrents([[1],[2,3],[4]])
        [{1: 2, 2: 2, 3: 2, 4: 1}, {1: 2, 2: 2, 3: 2, 4: 0}]
        sage: S.recurrents()
        [{1: 2, 2: 2, 3: 2, 4: 1},
         {1: 2, 2: 2, 3: 2, 4: 0},
         {1: 2, 2: 1, 3: 2, 4: 0},
         {1: 2, 2: 2, 3: 0, 4: 1},
         {1: 2, 2: 0, 3: 2, 4: 1},
         {1: 2, 2: 2, 3: 1, 4: 0},
         {1: 2, 2: 1, 3: 2, 4: 1},
         {1: 2, 2: 2, 3: 1, 4: 1}]

    Python

        >>> from sage.all import *
        >>> S = Sandpile({Integer(0): {},
        ...              Integer(1): {Integer(0): Integer(1), Integer(2): Integer(1), Integer(3): Integer(1)},
        ...              Integer(2): {Integer(1): Integer(1), Integer(3): Integer(1), Integer(4): Integer(1)},
        ...              Integer(3): {Integer(1): Integer(1), Integer(2): Integer(1), Integer(4): Integer(1)},
        ...              Integer(4): {Integer(2): Integer(1), Integer(3): Integer(1)}})
        >>> S.symmetric_recurrents([[Integer(1)],[Integer(2),Integer(3)],[Integer(4)]])
        [{1: 2, 2: 2, 3: 2, 4: 1}, {1: 2, 2: 2, 3: 2, 4: 0}]
        >>> S.recurrents()
        [{1: 2, 2: 2, 3: 2, 4: 1},
         {1: 2, 2: 2, 3: 2, 4: 0},
         {1: 2, 2: 1, 3: 2, 4: 0},
         {1: 2, 2: 2, 3: 0, 4: 1},
         {1: 2, 2: 0, 3: 2, 4: 1},
         {1: 2, 2: 2, 3: 1, 4: 0},
         {1: 2, 2: 1, 3: 2, 4: 1},
         {1: 2, 2: 2, 3: 1, 4: 1}]

    Note

    The user is responsible for ensuring that the list of orbits comes from a group of symmetries of the underlying graph.

  tutte_polynomial()

    The Tutte polynomial of the underlying graph. Only defined for undirected sandpile graphs.

    OUTPUT: polynomial

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(4)
        sage: s.tutte_polynomial()
        x^3 + y^3 + 3*x^2 + 4*x*y + 3*y^2 + 2*x + 2*y
        sage: s.tutte_polynomial().subs(x=1)
        y^3 + 3*y^2 + 6*y + 6
        sage: s.tutte_polynomial().subs(x=1).coefficients() == s.h_vector()
        True

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(4))
        >>> s.tutte_polynomial()
        x^3 + y^3 + 3*x^2 + 4*x*y + 3*y^2 + 2*x + 2*y
        >>> s.tutte_polynomial().subs(x=Integer(1))
        y^3 + 3*y^2 + 6*y + 6
        >>> s.tutte_polynomial().subs(x=Integer(1)).coefficients() == s.h_vector()
        True

  unsaturated_ideal()

    The unsaturated, homogeneous toppling ideal.

    OUTPUT: ideal

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: S.unsaturated_ideal().gens()
        [x1^3 - x3*x2*x0, x2^3 - x3*x1*x0, x3^2 - x2*x1]
        sage: S.ideal().gens()
        [x2*x1 - x0^2, x3^2 - x0^2, x1^3 - x3*x2*x0,
         x3*x1^2 - x2^2*x0, x2^3 - x3*x1*x0, x3*x2^2 - x1^2*x0]

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> S.unsaturated_ideal().gens()
        [x1^3 - x3*x2*x0, x2^3 - x3*x1*x0, x3^2 - x2*x1]
        >>> S.ideal().gens()
        [x2*x1 - x0^2, x3^2 - x0^2, x1^3 - x3*x2*x0,
         x3*x1^2 - x2^2*x0, x2^3 - x3*x1*x0, x3*x2^2 - x1^2*x0]

  static version()

    The version number of Sage Sandpiles.

    OUTPUT: string

    EXAMPLES:

    Sage

        sage: Sandpile.version()
        Sage Sandpiles Version 2.4
        sage: S = sandpiles.Complete(3)
        sage: S.version()
        Sage Sandpiles Version 2.4

    Python

        >>> from sage.all import *
        >>> Sandpile.version()
        Sage Sandpiles Version 2.4
        >>> S = sandpiles.Complete(Integer(3))
        >>> S.version()
        Sage Sandpiles Version 2.4

  zero_config()

    The all-zero configuration.

    OUTPUT: SandpileConfig

    EXAMPLES:

    Sage

        sage: s = sandpiles.Diamond()
        sage: s.zero_config()
        {1: 0, 2: 0, 3: 0}

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Diamond()
        >>> s.zero_config()
        {1: 0, 2: 0, 3: 0}

  zero_div()

    The all-zero divisor.

    OUTPUT: SandpileDivisor

    EXAMPLES:

    Sage

        sage: S = sandpiles.House()
        sage: S.zero_div()
        {0: 0, 1: 0, 2: 0, 3: 0, 4: 0}

    Python

        >>> from sage.all import *
        >>> S = sandpiles.House()
        >>> S.zero_div()
        {0: 0, 1: 0, 2: 0, 3: 0, 4: 0}

class sage.sandpiles.sandpile.SandpileConfig(S, c)

  Bases: `dict`

  Class for configurations on a sandpile.

  add_random(distrib=None)

    Add one grain of sand to a random vertex.

    Optionally, a probability distribution, `distrib`, may be placed on the vertices or the nonsink vertices.

    See NOTE for details.

    INPUT:

    - `distrib` – (optional) list of nonnegative numbers summing to 1 (representing a prob. dist.)

    OUTPUT: SandpileConfig

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(4)
        sage: c = s.zero_config()
        sage: c.add_random() # random
        {1: 0, 2: 1, 3: 0}
        sage: c
        {1: 0, 2: 0, 3: 0}
        sage: c.add_random([0.1,0.1,0.8]) # random
        {1: 0, 2: 0, 3: 1}
        sage: c.add_random([0.7,0.1,0.1,0.1]) # random
        {1: 0, 2: 0, 3: 0}

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(4))
        >>> c = s.zero_config()
        >>> c.add_random() # random
        {1: 0, 2: 1, 3: 0}
        >>> c
        {1: 0, 2: 0, 3: 0}
        >>> c.add_random([RealNumber('0.1'),RealNumber('0.1'),RealNumber('0.8')]) # random
        {1: 0, 2: 0, 3: 1}
        >>> c.add_random([RealNumber('0.7'),RealNumber('0.1'),RealNumber('0.1'),RealNumber('0.1')]) # random
        {1: 0, 2: 0, 3: 0}

    We compute the “sizes” of the avalanches caused by adding random grains of sand to the maximal stable configuration on a grid graph. The function `stabilize()` returns the firing vector of the stabilization, a dictionary whose values say how many times each vertex fires in the stabilization.:

    Sage

        sage: S = sandpiles.Grid(10,10)
        sage: m = S.max_stable()
        sage: a = []
        sage: for i in range(1000):
        ....:     m = m.add_random()
        ....:     m, f = m.stabilize(True)
        ....:     a.append(sum(f.values()))

        sage: p = list_plot([[log(i + 1), log(a.count(i))]
        ....:                for i in [0..max(a)] if a.count(i)])
        sage: p.axes_labels(['log(N)', 'log(D(N))'])
        sage: t = text("Distribution of avalanche sizes", (2,2), rgbcolor=(1,0,0))
        sage: show(p + t, axes_labels=['log(N)', 'log(D(N))'])      # long time

    [Python]

        >>> from sage.all import *
        >>> S = sandpiles.Grid(Integer(10),Integer(10))
        >>> m = S.max_stable()
        >>> a = []
        >>> for i in range(Integer(1000)):
        ...     m = m.add_random()
        ...     m, f = m.stabilize(True)
        ...     a.append(sum(f.values()))

        >>> p = list_plot([[log(i + Integer(1)), log(a.count(i))]
        ...                for i in (ellipsis_range(Integer(0),Ellipsis,max(a))) if a.count(i)])
        >>> p.axes_labels(['log(N)', 'log(D(N))'])
        >>> t = text("Distribution of avalanche sizes", (Integer(2),Integer(2)), rgbcolor=(Integer(1),Integer(0),Integer(0)))
        >>> show(p + t, axes_labels=['log(N)', 'log(D(N))'])      # long time

    Note

    If `distrib` is `None`, then the probability is the uniform probability on the nonsink vertices. Otherwise, there are two possibilities:

    (i) the length of `distrib` is equal to the number of vertices, and `distrib` represents a probability distribution on all of the vertices. In that case, the sink may be chosen at random, in which case, the configuration is unchanged.

    (ii) Otherwise, the length of `distrib` must be equal to the number of nonsink vertices, and `distrib` represents a probability distribution on the nonsink vertices.

    Warning

    If `distrib != None`, the user is responsible for assuring the sum of its entries is 1 and that its length is equal to the number of sink vertices or the number of nonsink vertices.

  burst_size(v)

    Return the burst size of the configuration with respect to the given vertex.

    INPUT:

    - `v` – vertex

    OUTPUT: integer

    EXAMPLES:

    Sage

        sage: s = sandpiles.Diamond()
        sage: [i.burst_size(0) for i in s.recurrents()]
        [1, 1, 1, 1, 1, 1, 1, 1]
        sage: [i.burst_size(1) for i in s.recurrents()]
        [0, 0, 1, 2, 1, 2, 0, 2]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Diamond()
        >>> [i.burst_size(Integer(0)) for i in s.recurrents()]
        [1, 1, 1, 1, 1, 1, 1, 1]
        >>> [i.burst_size(Integer(1)) for i in s.recurrents()]
        [0, 0, 1, 2, 1, 2, 0, 2]

    Note

    To define `c.burst(v)`, if \(v\) is not the sink, let \(c'\) be the unique recurrent for which the stabilization of \(c' + v\) is \(c\). The burst size is then the amount of sand that goes into the sink during this stabilization. If \(v\) is the sink, the burst size is defined to be 1.

    REFERENCES:

    - [Lev2014]

  deg()

    The degree of the configuration.

    OUTPUT: integer

    EXAMPLES:

    Sage

        sage: S = sandpiles.Complete(3)
        sage: c = SandpileConfig(S, [1,2])
        sage: c.deg()
        3

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Complete(Integer(3))
        >>> c = SandpileConfig(S, [Integer(1),Integer(2)])
        >>> c.deg()
        3

  dualize()

    The difference with the maximal stable configuration.

    OUTPUT: SandpileConfig

    EXAMPLES:

    Sage

        sage: S = sandpiles.Cycle(3)
        sage: c = SandpileConfig(S, [1,2])
        sage: S.max_stable()
        {1: 1, 2: 1}
        sage: c.dualize()
        {1: 0, 2: -1}
        sage: S.max_stable() - c == c.dualize()
        True

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Cycle(Integer(3))
        >>> c = SandpileConfig(S, [Integer(1),Integer(2)])
        >>> S.max_stable()
        {1: 1, 2: 1}
        >>> c.dualize()
        {1: 0, 2: -1}
        >>> S.max_stable() - c == c.dualize()
        True

  equivalent_recurrent(with_firing_vector=False)

    Return the recurrent configuration equivalent to the given configuration.

    Optionally, this returns the corresponding firing vector.

    INPUT:

    - `with_firing_vector` – boolean (default: `False`)

    OUTPUT: SandpileConfig or [SandpileConfig, firing_vector]

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: c = SandpileConfig(S, [0,0,0])
        sage: c.equivalent_recurrent() == S.identity()
        True
        sage: x = c.equivalent_recurrent(True)
        sage: r = vector([x[0][v] for v in S.nonsink_vertices()])
        sage: f = vector([x[1][v] for v in S.nonsink_vertices()])
        sage: cv = vector(c.values())
        sage: r == cv - f*S.reduced_laplacian()
        True

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> c = SandpileConfig(S, [Integer(0),Integer(0),Integer(0)])
        >>> c.equivalent_recurrent() == S.identity()
        True
        >>> x = c.equivalent_recurrent(True)
        >>> r = vector([x[Integer(0)][v] for v in S.nonsink_vertices()])
        >>> f = vector([x[Integer(1)][v] for v in S.nonsink_vertices()])
        >>> cv = vector(c.values())
        >>> r == cv - f*S.reduced_laplacian()
        True

    Note

    Let \(L\) be the reduced Laplacian, \(c\) the initial configuration, \(r\) the returned configuration, and \(f\) the firing vector. Then \(r = c - f\cdot L\).

  equivalent_superstable(with_firing_vector=False)

    Return the equivalent superstable configuration.

    Optionally, this returns the corresponding firing vector.

    INPUT:

    - `with_firing_vector` – boolean (default: `False`)

    OUTPUT: SandpileConfig or [SandpileConfig, firing_vector]

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: m = S.max_stable()
        sage: m.equivalent_superstable().is_superstable()
        True
        sage: x = m.equivalent_superstable(True)
        sage: s = vector(x[0].values())
        sage: f = vector(x[1].values())
        sage: mv = vector(m.values())
        sage: s == mv - f*S.reduced_laplacian()
        True

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> m = S.max_stable()
        >>> m.equivalent_superstable().is_superstable()
        True
        >>> x = m.equivalent_superstable(True)
        >>> s = vector(x[Integer(0)].values())
        >>> f = vector(x[Integer(1)].values())
        >>> mv = vector(m.values())
        >>> s == mv - f*S.reduced_laplacian()
        True

    Note

    Let \(L\) be the reduced Laplacian, \(c\) the initial configuration, \(s\) the returned configuration, and \(f\) the firing vector. Then \(s = c - f\cdot L\).

  fire_script(sigma)

    Fire the given script.

    In other words, fire each vertex the number of times indicated by `sigma`.

    INPUT:

    - `sigma` – SandpileConfig or (list or dict representing a SandpileConfig)

    OUTPUT: SandpileConfig

    EXAMPLES:

    Sage

        sage: S = sandpiles.Cycle(4)
        sage: c = SandpileConfig(S, [1,2,3])
        sage: c.unstable()
        [2, 3]
        sage: c.fire_script(SandpileConfig(S,[0,1,1]))
        {1: 2, 2: 1, 3: 2}
        sage: c.fire_script(SandpileConfig(S,[2,0,0])) == c.fire_vertex(1).fire_vertex(1)
        True

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Cycle(Integer(4))
        >>> c = SandpileConfig(S, [Integer(1),Integer(2),Integer(3)])
        >>> c.unstable()
        [2, 3]
        >>> c.fire_script(SandpileConfig(S,[Integer(0),Integer(1),Integer(1)]))
        {1: 2, 2: 1, 3: 2}
        >>> c.fire_script(SandpileConfig(S,[Integer(2),Integer(0),Integer(0)])) == c.fire_vertex(Integer(1)).fire_vertex(Integer(1))
        True

  fire_unstable()

    Fire all unstable vertices.

    OUTPUT: SandpileConfig

    EXAMPLES:

    Sage

        sage: S = sandpiles.Cycle(4)
        sage: c = SandpileConfig(S, [1,2,3])
        sage: c.fire_unstable()
        {1: 2, 2: 1, 3: 2}

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Cycle(Integer(4))
        >>> c = SandpileConfig(S, [Integer(1),Integer(2),Integer(3)])
        >>> c.fire_unstable()
        {1: 2, 2: 1, 3: 2}

  fire_vertex(v)

    Fire the given vertex.

    INPUT:

    - `v` – vertex

    OUTPUT: SandpileConfig

    EXAMPLES:

    Sage

        sage: S = sandpiles.Cycle(3)
        sage: c = SandpileConfig(S, [1,2])
        sage: c.fire_vertex(2)
        {1: 2, 2: 0}

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Cycle(Integer(3))
        >>> c = SandpileConfig(S, [Integer(1),Integer(2)])
        >>> c.fire_vertex(Integer(2))
        {1: 2, 2: 0}

  static help(verbose=True)

    List of SandpileConfig methods.

    If `verbose`, include short descriptions.

    INPUT:

    - `verbose` – boolean (default: `True`)

    OUTPUT: printed string

    EXAMPLES:

    Sage

        sage: SandpileConfig.help()
        Shortcuts for SandpileConfig operations:
        ~c    -- stabilize
        c & d -- add and stabilize
        c * c -- add and find equivalent recurrent
        c^k   -- add k times and find equivalent recurrent
                 (taking inverse if k is negative)

        For detailed help with any method FOO listed below,
        enter "SandpileConfig.FOO?" or enter "c.FOO?" for any SandpileConfig c.

        add_random             -- Add one grain of sand to a random vertex.
        burst_size             -- Return the burst size of the configuration with respect to the given
        vertex.
        deg                    -- The degree of the configuration.
        dualize                -- The difference with the maximal stable configuration.
        equivalent_recurrent   -- Return the recurrent configuration equivalent to the given
        configuration.
        equivalent_superstable -- Return the equivalent superstable configuration.
        fire_script            -- Fire the given script.
        fire_unstable          -- Fire all unstable vertices.
        fire_vertex            -- Fire the given vertex.
        help                   -- List of SandpileConfig methods.
        is_recurrent           -- Return whether the configuration is recurrent.
        is_stable              -- Return whether the configuration is stable.
        is_superstable         -- Return whether the configuration is superstable.
        is_symmetric           -- Return whether the configuration is symmetric.
        order                  -- The order of the equivalent recurrent element.
        sandpile               -- The configuration's underlying sandpile.
        show                   -- Show the configuration.
        stabilize              -- Return the stabilized configuration.
        support                -- Return the vertices containing sand.
        unstable               -- The unstable vertices.
        values                 -- The values of the configuration as a list.

    Python

        >>> from sage.all import *
        >>> SandpileConfig.help()
        Shortcuts for SandpileConfig operations:
        ~c    -- stabilize
        c & d -- add and stabilize
        c * c -- add and find equivalent recurrent
        c^k   -- add k times and find equivalent recurrent
                 (taking inverse if k is negative)
        <BLANKLINE>
        For detailed help with any method FOO listed below,
        enter "SandpileConfig.FOO?" or enter "c.FOO?" for any SandpileConfig c.
        <BLANKLINE>
        add_random             -- Add one grain of sand to a random vertex.
        burst_size             -- Return the burst size of the configuration with respect to the given
        vertex.
        deg                    -- The degree of the configuration.
        dualize                -- The difference with the maximal stable configuration.
        equivalent_recurrent   -- Return the recurrent configuration equivalent to the given
        configuration.
        equivalent_superstable -- Return the equivalent superstable configuration.
        fire_script            -- Fire the given script.
        fire_unstable          -- Fire all unstable vertices.
        fire_vertex            -- Fire the given vertex.
        help                   -- List of SandpileConfig methods.
        is_recurrent           -- Return whether the configuration is recurrent.
        is_stable              -- Return whether the configuration is stable.
        is_superstable         -- Return whether the configuration is superstable.
        is_symmetric           -- Return whether the configuration is symmetric.
        order                  -- The order of the equivalent recurrent element.
        sandpile               -- The configuration's underlying sandpile.
        show                   -- Show the configuration.
        stabilize              -- Return the stabilized configuration.
        support                -- Return the vertices containing sand.
        unstable               -- The unstable vertices.
        values                 -- The values of the configuration as a list.

  is_recurrent()

    Return whether the configuration is recurrent.

    OUTPUT: boolean

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: S.identity().is_recurrent()
        True
        sage: S.zero_config().is_recurrent()
        False

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> S.identity().is_recurrent()
        True
        >>> S.zero_config().is_recurrent()
        False

  is_stable()

    Return whether the configuration is stable.

    OUTPUT: boolean

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: S.max_stable().is_stable()
        True
        sage: (2*S.max_stable()).is_stable()
        False
        sage: (S.max_stable() & S.max_stable()).is_stable()
        True

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> S.max_stable().is_stable()
        True
        >>> (Integer(2)*S.max_stable()).is_stable()
        False
        >>> (S.max_stable() & S.max_stable()).is_stable()
        True

  is_superstable()

    Return whether the configuration is superstable.

    OUTPUT: boolean

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: S.zero_config().is_superstable()
        True

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> S.zero_config().is_superstable()
        True

  is_symmetric(orbits)

    Return whether the configuration is symmetric.

    This returns `True` if the values of the configuration are constant over the vertices in each sublist of `orbits`.

    INPUT:

    - `orbits` – list of lists of vertices

    OUTPUT: boolean

    EXAMPLES:

    Sage

        sage: S = Sandpile({0: {},
        ....:              1: {0: 1, 2: 1, 3: 1},
        ....:              2: {1: 1, 3: 1, 4: 1},
        ....:              3: {1: 1, 2: 1, 4: 1},
        ....:              4: {2: 1, 3: 1}})
        sage: c = SandpileConfig(S, [1, 2, 2, 3])
        sage: c.is_symmetric([[2,3]])
        True

    Python

        >>> from sage.all import *
        >>> S = Sandpile({Integer(0): {},
        ...              Integer(1): {Integer(0): Integer(1), Integer(2): Integer(1), Integer(3): Integer(1)},
        ...              Integer(2): {Integer(1): Integer(1), Integer(3): Integer(1), Integer(4): Integer(1)},
        ...              Integer(3): {Integer(1): Integer(1), Integer(2): Integer(1), Integer(4): Integer(1)},
        ...              Integer(4): {Integer(2): Integer(1), Integer(3): Integer(1)}})
        >>> c = SandpileConfig(S, [Integer(1), Integer(2), Integer(2), Integer(3)])
        >>> c.is_symmetric([[Integer(2),Integer(3)]])
        True

  order()

    The order of the equivalent recurrent element.

    OUTPUT: integer

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: c = SandpileConfig(S,[2,0,1])
        sage: c.order()
        4
        sage: ~(c + c + c + c) == S.identity()
        True
        sage: c = SandpileConfig(S,[1,1,0])
        sage: c.order()
        1
        sage: c.is_recurrent()
        False
        sage: c.equivalent_recurrent() == S.identity()
        True

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> c = SandpileConfig(S,[Integer(2),Integer(0),Integer(1)])
        >>> c.order()
        4
        >>> ~(c + c + c + c) == S.identity()
        True
        >>> c = SandpileConfig(S,[Integer(1),Integer(1),Integer(0)])
        >>> c.order()
        1
        >>> c.is_recurrent()
        False
        >>> c.equivalent_recurrent() == S.identity()
        True

  sandpile()

    The configuration’s underlying sandpile.

    OUTPUT: Sandpile

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: c = S.identity()
        sage: c.sandpile()
        Diamond sandpile graph: 4 vertices, sink = 0
        sage: c.sandpile() == S
        True

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> c = S.identity()
        >>> c.sandpile()
        Diamond sandpile graph: 4 vertices, sink = 0
        >>> c.sandpile() == S
        True

  show(sink=True, colors=True, heights=False, directed=None, **kwds)

    Show the configuration.

    INPUT:

    - `sink` – boolean (default: `True`); whether to show the sink

    - `colors` – boolean (default: `True`); whether to color-code the amount of sand on each vertex

    - `heights` – boolean (default: `False`); whether to label each vertex with the amount of sand

    - `directed` – (optional) whether to draw directed edges

    - `kwds` – (optional) arguments passed to the show method for Graph

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: c = S.identity()
        sage: c.show()
        sage: c.show(directed=False)
        sage: c.show(sink=False, colors=False, heights=True)

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> c = S.identity()
        >>> c.show()
        >>> c.show(directed=False)
        >>> c.show(sink=False, colors=False, heights=True)

  stabilize(with_firing_vector=False)

    Return the stabilized configuration.

    Optionally this returns the corresponding firing vector.

    INPUT:

    - `with_firing_vector` – boolean (default: `False`)

    OUTPUT: `SandpileConfig` or `[SandpileConfig, firing_vector]`

    EXAMPLES:

    Sage

        sage: S = sandpiles.House()
        sage: c = 2*S.max_stable()
        sage: c._set_stabilize()
        sage: '_stabilize' in c.__dict__
        True
        sage: S = sandpiles.House()
        sage: c = S.max_stable() + S.identity()
        sage: c.stabilize(True)
        [{1: 1, 2: 2, 3: 2, 4: 1}, {1: 2, 2: 2, 3: 3, 4: 3}]
        sage: S.max_stable() & S.identity() == c.stabilize()
        True
        sage: ~c == c.stabilize()
        True

    Python

        >>> from sage.all import *
        >>> S = sandpiles.House()
        >>> c = Integer(2)*S.max_stable()
        >>> c._set_stabilize()
        >>> '_stabilize' in c.__dict__
        True
        >>> S = sandpiles.House()
        >>> c = S.max_stable() + S.identity()
        >>> c.stabilize(True)
        [{1: 1, 2: 2, 3: 2, 4: 1}, {1: 2, 2: 2, 3: 3, 4: 3}]
        >>> S.max_stable() & S.identity() == c.stabilize()
        True
        >>> ~c == c.stabilize()
        True

  support()

    Return the vertices containing sand.

    OUTPUT: list - support of the configuration

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: c = S.identity()
        sage: c
        {1: 2, 2: 2, 3: 0}
        sage: c.support()
        [1, 2]

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> c = S.identity()
        >>> c
        {1: 2, 2: 2, 3: 0}
        >>> c.support()
        [1, 2]

  unstable()

    The unstable vertices.

    OUTPUT: list of vertices

    EXAMPLES:

    Sage

        sage: S = sandpiles.Cycle(4)
        sage: c = SandpileConfig(S, [1,2,3])
        sage: c.unstable()
        [2, 3]

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Cycle(Integer(4))
        >>> c = SandpileConfig(S, [Integer(1),Integer(2),Integer(3)])
        >>> c.unstable()
        [2, 3]

  values()

    The values of the configuration as a list.

    The list is sorted in the order of the vertices.

    OUTPUT: list of integers

    EXAMPLES:

    Sage

        sage: S = Sandpile({'a':['c','b'], 'b':['c','a'], 'c':['a']},'a')
        sage: c = SandpileConfig(S, {'b':1, 'c':2})
        sage: c
        {'b': 1, 'c': 2}
        sage: c.values()
        [1, 2]
        sage: S.nonsink_vertices()
        ['b', 'c']

    Python

        >>> from sage.all import *
        >>> S = Sandpile({'a':['c','b'], 'b':['c','a'], 'c':['a']},'a')
        >>> c = SandpileConfig(S, {'b':Integer(1), 'c':Integer(2)})
        >>> c
        {'b': 1, 'c': 2}
        >>> c.values()
        [1, 2]
        >>> S.nonsink_vertices()
        ['b', 'c']

class sage.sandpiles.sandpile.SandpileDivisor(S, D)

  Bases: `dict`

  Class for divisors on a sandpile.

  Dcomplex()

    The support-complex. (See NOTE.)

    OUTPUT: simplicial complex

    EXAMPLES:

    Sage

        sage: S = sandpiles.House()
        sage: p = SandpileDivisor(S, [1,2,1,0,0]).Dcomplex()
        sage: p.homology()
        {0: 0, 1: Z x Z, 2: 0}
        sage: p.f_vector()
        [1, 5, 10, 4]
        sage: p.betti()
        {0: 1, 1: 2, 2: 0}

    Python

        >>> from sage.all import *
        >>> S = sandpiles.House()
        >>> p = SandpileDivisor(S, [Integer(1),Integer(2),Integer(1),Integer(0),Integer(0)]).Dcomplex()
        >>> p.homology()
        {0: 0, 1: Z x Z, 2: 0}
        >>> p.f_vector()
        [1, 5, 10, 4]
        >>> p.betti()
        {0: 1, 1: 2, 2: 0}

    Note

    The “support-complex” is the simplicial complex determined by the supports of the linearly equivalent effective divisors.

  add_random(distrib=None)

    Add one grain of sand to a random vertex.

    INPUT:

    - `distrib` – (optional) list of nonnegative numbers representing a probability distribution on the vertices

    OUTPUT: SandpileDivisor

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(4)
        sage: D = s.zero_div()
        sage: D.add_random() # random
        {0: 0, 1: 0, 2: 1, 3: 0}
        sage: D.add_random([0.1,0.1,0.1,0.7]) # random
        {0: 0, 1: 0, 2: 0, 3: 1}

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(4))
        >>> D = s.zero_div()
        >>> D.add_random() # random
        {0: 0, 1: 0, 2: 1, 3: 0}
        >>> D.add_random([RealNumber('0.1'),RealNumber('0.1'),RealNumber('0.1'),RealNumber('0.7')]) # random
        {0: 0, 1: 0, 2: 0, 3: 1}

    Warning

    If `distrib` is not `None`, the user is responsible for assuring the sum of its entries is 1.

  betti()

    The Betti numbers for the support-complex. (See NOTE.)

    OUTPUT: dictionary of integers

    EXAMPLES:

    Sage

        sage: S = sandpiles.Cycle(3)
        sage: D = SandpileDivisor(S, [2,0,1])
        sage: D.betti()
        {0: 1, 1: 1}

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Cycle(Integer(3))
        >>> D = SandpileDivisor(S, [Integer(2),Integer(0),Integer(1)])
        >>> D.betti()
        {0: 1, 1: 1}

    Note

    The “support-complex” is the simplicial complex determined by the supports of the linearly equivalent effective divisors.

  deg()

    The degree of the divisor.

    OUTPUT: integer

    EXAMPLES:

    Sage

        sage: S = sandpiles.Cycle(3)
        sage: D = SandpileDivisor(S, [1,2,3])
        sage: D.deg()
        6

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Cycle(Integer(3))
        >>> D = SandpileDivisor(S, [Integer(1),Integer(2),Integer(3)])
        >>> D.deg()
        6

  dualize()

    The difference with the maximal stable divisor.

    OUTPUT: SandpileDivisor

    EXAMPLES:

    Sage

        sage: S = sandpiles.Cycle(3)
        sage: D = SandpileDivisor(S, [1,2,3])
        sage: D.dualize()
        {0: 0, 1: -1, 2: -2}
        sage: S.max_stable_div() - D == D.dualize()
        True

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Cycle(Integer(3))
        >>> D = SandpileDivisor(S, [Integer(1),Integer(2),Integer(3)])
        >>> D.dualize()
        {0: 0, 1: -1, 2: -2}
        >>> S.max_stable_div() - D == D.dualize()
        True

  effective_div(verbose=True, with_firing_vectors=False)

    Return all linearly equivalent effective divisors.

    If `verbose` is `False`, the divisors are converted to lists of integers. If `with_firing_vectors` is `True` then a list of firing vectors is also given, each of which prescribes the vertices to be fired in order to obtain an effective divisor.

    INPUT:

    - `verbose` – boolean (default: `True`)

    - `with_firing_vectors` – boolean (default: `False`)

    OUTPUT: list (of divisors)

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(4)
        sage: D = SandpileDivisor(s,[4,2,0,0])
        sage: sorted(D.effective_div(), key=str)
        [{0: 0, 1: 2, 2: 0, 3: 4},
         {0: 0, 1: 2, 2: 4, 3: 0},
         {0: 0, 1: 6, 2: 0, 3: 0},
         {0: 1, 1: 3, 2: 1, 3: 1},
         {0: 2, 1: 0, 2: 2, 3: 2},
         {0: 4, 1: 2, 2: 0, 3: 0}]
        sage: sorted(D.effective_div(False))
        [[0, 2, 0, 4],
         [0, 2, 4, 0],
         [0, 6, 0, 0],
         [1, 3, 1, 1],
         [2, 0, 2, 2],
         [4, 2, 0, 0]]
        sage: sorted(D.effective_div(with_firing_vectors=True), key=str)
        [({0: 0, 1: 2, 2: 0, 3: 4}, (0, -1, -1, -2)),
         ({0: 0, 1: 2, 2: 4, 3: 0}, (0, -1, -2, -1)),
         ({0: 0, 1: 6, 2: 0, 3: 0}, (0, -2, -1, -1)),
         ({0: 1, 1: 3, 2: 1, 3: 1}, (0, -1, -1, -1)),
         ({0: 2, 1: 0, 2: 2, 3: 2}, (0, 0, -1, -1)),
         ({0: 4, 1: 2, 2: 0, 3: 0}, (0, 0, 0, 0))]
        sage: a = _[2]
        sage: a[0].values()
        [0, 6, 0, 0]
        sage: vector(D.values()) - s.laplacian()*a[1]
        (0, 6, 0, 0)
        sage: sorted(D.effective_div(False, True))
        [([0, 2, 0, 4], (0, -1, -1, -2)),
         ([0, 2, 4, 0], (0, -1, -2, -1)),
         ([0, 6, 0, 0], (0, -2, -1, -1)),
         ([1, 3, 1, 1], (0, -1, -1, -1)),
         ([2, 0, 2, 2], (0, 0, -1, -1)),
         ([4, 2, 0, 0], (0, 0, 0, 0))]
        sage: D = SandpileDivisor(s,[-1,0,0,0])
        sage: D.effective_div(False,True)
        []

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(4))
        >>> D = SandpileDivisor(s,[Integer(4),Integer(2),Integer(0),Integer(0)])
        >>> sorted(D.effective_div(), key=str)
        [{0: 0, 1: 2, 2: 0, 3: 4},
         {0: 0, 1: 2, 2: 4, 3: 0},
         {0: 0, 1: 6, 2: 0, 3: 0},
         {0: 1, 1: 3, 2: 1, 3: 1},
         {0: 2, 1: 0, 2: 2, 3: 2},
         {0: 4, 1: 2, 2: 0, 3: 0}]
        >>> sorted(D.effective_div(False))
        [[0, 2, 0, 4],
         [0, 2, 4, 0],
         [0, 6, 0, 0],
         [1, 3, 1, 1],
         [2, 0, 2, 2],
         [4, 2, 0, 0]]
        >>> sorted(D.effective_div(with_firing_vectors=True), key=str)
        [({0: 0, 1: 2, 2: 0, 3: 4}, (0, -1, -1, -2)),
         ({0: 0, 1: 2, 2: 4, 3: 0}, (0, -1, -2, -1)),
         ({0: 0, 1: 6, 2: 0, 3: 0}, (0, -2, -1, -1)),
         ({0: 1, 1: 3, 2: 1, 3: 1}, (0, -1, -1, -1)),
         ({0: 2, 1: 0, 2: 2, 3: 2}, (0, 0, -1, -1)),
         ({0: 4, 1: 2, 2: 0, 3: 0}, (0, 0, 0, 0))]
        >>> a = _[Integer(2)]
        >>> a[Integer(0)].values()
        [0, 6, 0, 0]
        >>> vector(D.values()) - s.laplacian()*a[Integer(1)]
        (0, 6, 0, 0)
        >>> sorted(D.effective_div(False, True))
        [([0, 2, 0, 4], (0, -1, -1, -2)),
         ([0, 2, 4, 0], (0, -1, -2, -1)),
         ([0, 6, 0, 0], (0, -2, -1, -1)),
         ([1, 3, 1, 1], (0, -1, -1, -1)),
         ([2, 0, 2, 2], (0, 0, -1, -1)),
         ([4, 2, 0, 0], (0, 0, 0, 0))]
        >>> D = SandpileDivisor(s,[-Integer(1),Integer(0),Integer(0),Integer(0)])
        >>> D.effective_div(False,True)
        []

  fire_script(sigma)

    Fire the given script.

    In other words, fire each vertex the number of times indicated by `sigma`.

    INPUT:

    - `sigma` – SandpileDivisor or (list or dict representing a SandpileDivisor)

    OUTPUT: SandpileDivisor

    EXAMPLES:

    Sage

        sage: S = sandpiles.Cycle(3)
        sage: D = SandpileDivisor(S, [1,2,3])
        sage: D.unstable()
        [1, 2]
        sage: D.fire_script([0,1,1])
        {0: 3, 1: 1, 2: 2}
        sage: D.fire_script(SandpileDivisor(S,[2,0,0])) == D.fire_vertex(0).fire_vertex(0)
        True

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Cycle(Integer(3))
        >>> D = SandpileDivisor(S, [Integer(1),Integer(2),Integer(3)])
        >>> D.unstable()
        [1, 2]
        >>> D.fire_script([Integer(0),Integer(1),Integer(1)])
        {0: 3, 1: 1, 2: 2}
        >>> D.fire_script(SandpileDivisor(S,[Integer(2),Integer(0),Integer(0)])) == D.fire_vertex(Integer(0)).fire_vertex(Integer(0))
        True

  fire_unstable()

    Fire all unstable vertices.

    OUTPUT: SandpileDivisor

    EXAMPLES:

    Sage

        sage: S = sandpiles.Cycle(3)
        sage: D = SandpileDivisor(S, [1,2,3])
        sage: D.fire_unstable()
        {0: 3, 1: 1, 2: 2}

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Cycle(Integer(3))
        >>> D = SandpileDivisor(S, [Integer(1),Integer(2),Integer(3)])
        >>> D.fire_unstable()
        {0: 3, 1: 1, 2: 2}

  fire_vertex(v)

    Fire the given vertex.

    INPUT:

    - `v` – vertex

    OUTPUT: SandpileDivisor

    EXAMPLES:

    Sage

        sage: S = sandpiles.Cycle(3)
        sage: D = SandpileDivisor(S, [1,2,3])
        sage: D.fire_vertex(1)
        {0: 2, 1: 0, 2: 4}

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Cycle(Integer(3))
        >>> D = SandpileDivisor(S, [Integer(1),Integer(2),Integer(3)])
        >>> D.fire_vertex(Integer(1))
        {0: 2, 1: 0, 2: 4}

  static help(verbose=True)

    List of SandpileDivisor methods. If `verbose`, include short descriptions.

    INPUT:

    - `verbose` – boolean (default: `True`)

    OUTPUT: printed string

    EXAMPLES:

    Sage

        sage: SandpileDivisor.help()
        For detailed help with any method FOO listed below,
        enter "SandpileDivisor.FOO?" or enter "D.FOO?" for any SandpileDivisor D.

        Dcomplex               -- The support-complex.
        add_random             -- Add one grain of sand to a random vertex.
        betti                  -- The Betti numbers for the support-complex.
        deg                    -- The degree of the divisor.
        dualize                -- The difference with the maximal stable divisor.
        effective_div          -- Return all linearly equivalent effective divisors.
        fire_script            -- Fire the given script.
        fire_unstable          -- Fire all unstable vertices.
        fire_vertex            -- Fire the given vertex.
        help                   -- List of SandpileDivisor methods.
        is_alive               -- Return whether the divisor is stabilizable.
        is_linearly_equivalent -- Return whether the given divisor is linearly equivalent.
        is_q_reduced           -- Return whether the divisor is q-reduced.
        is_symmetric           -- Return whether the divisor is symmetric.
        is_weierstrass_pt      -- Return whether the given vertex is a Weierstrass point.
        polytope               -- Return the polytope determining the complete linear system.
        polytope_integer_pts   -- Return the integer points inside divisor's polytope.
        q_reduced              -- Return the linearly equivalent q-reduced divisor.
        rank                   -- Return the rank of the divisor.
        sandpile               -- The divisor's underlying sandpile.
        show                   -- Show the divisor.
        simulate_threshold     -- Return the first unstabilizable divisor in the closed Markov chain.
        stabilize              -- The stabilization of the divisor.
        support                -- List of vertices at which the divisor is nonzero.
        unstable               -- Return the unstable vertices.
        values                 -- The values of the divisor as a list.
        weierstrass_div        -- The Weierstrass divisor.
        weierstrass_gap_seq    -- The Weierstrass gap sequence at the given vertex.
        weierstrass_pts        -- The Weierstrass points (vertices).
        weierstrass_rank_seq   -- The Weierstrass rank sequence at the given vertex.

    Python

        >>> from sage.all import *
        >>> SandpileDivisor.help()
        For detailed help with any method FOO listed below,
        enter "SandpileDivisor.FOO?" or enter "D.FOO?" for any SandpileDivisor D.
        <BLANKLINE>
        Dcomplex               -- The support-complex.
        add_random             -- Add one grain of sand to a random vertex.
        betti                  -- The Betti numbers for the support-complex.
        deg                    -- The degree of the divisor.
        dualize                -- The difference with the maximal stable divisor.
        effective_div          -- Return all linearly equivalent effective divisors.
        fire_script            -- Fire the given script.
        fire_unstable          -- Fire all unstable vertices.
        fire_vertex            -- Fire the given vertex.
        help                   -- List of SandpileDivisor methods.
        is_alive               -- Return whether the divisor is stabilizable.
        is_linearly_equivalent -- Return whether the given divisor is linearly equivalent.
        is_q_reduced           -- Return whether the divisor is q-reduced.
        is_symmetric           -- Return whether the divisor is symmetric.
        is_weierstrass_pt      -- Return whether the given vertex is a Weierstrass point.
        polytope               -- Return the polytope determining the complete linear system.
        polytope_integer_pts   -- Return the integer points inside divisor's polytope.
        q_reduced              -- Return the linearly equivalent q-reduced divisor.
        rank                   -- Return the rank of the divisor.
        sandpile               -- The divisor's underlying sandpile.
        show                   -- Show the divisor.
        simulate_threshold     -- Return the first unstabilizable divisor in the closed Markov chain.
        stabilize              -- The stabilization of the divisor.
        support                -- List of vertices at which the divisor is nonzero.
        unstable               -- Return the unstable vertices.
        values                 -- The values of the divisor as a list.
        weierstrass_div        -- The Weierstrass divisor.
        weierstrass_gap_seq    -- The Weierstrass gap sequence at the given vertex.
        weierstrass_pts        -- The Weierstrass points (vertices).
        weierstrass_rank_seq   -- The Weierstrass rank sequence at the given vertex.

  is_alive(cycle=False)

    Return whether the divisor is stabilizable.

    In other words, will the divisor stabilize under repeated firings of all unstable vertices? Optionally this returns the resulting cycle.

    INPUT:

    - `cycle` – boolean (default: `False`)

    OUTPUT: boolean or optionally, a list of SandpileDivisors

    EXAMPLES:

    Sage

        sage: S = sandpiles.Complete(4)
        sage: D = SandpileDivisor(S, {0: 4, 1: 3, 2: 3, 3: 2})
        sage: D.is_alive()
        True
        sage: D.is_alive(True)
        [{0: 4, 1: 3, 2: 3, 3: 2}, {0: 3, 1: 2, 2: 2, 3: 5}, {0: 1, 1: 4, 2: 4, 3: 3}]

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Complete(Integer(4))
        >>> D = SandpileDivisor(S, {Integer(0): Integer(4), Integer(1): Integer(3), Integer(2): Integer(3), Integer(3): Integer(2)})
        >>> D.is_alive()
        True
        >>> D.is_alive(True)
        [{0: 4, 1: 3, 2: 3, 3: 2}, {0: 3, 1: 2, 2: 2, 3: 5}, {0: 1, 1: 4, 2: 4, 3: 3}]

  is_linearly_equivalent(D, with_firing_vector=False)

    Return whether the given divisor is linearly equivalent.

    Optionally, this returns the firing vector. (See NOTE.)

    INPUT:

    - `D` – SandpileDivisor or list, tuple, etc. representing a divisor

    - `with_firing_vector` – boolean (default: `False`)

    OUTPUT: boolean or integer vector

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(3)
        sage: D = SandpileDivisor(s,[2,0,0])
        sage: D.is_linearly_equivalent([0,1,1])
        True
        sage: D.is_linearly_equivalent([0,1,1],True)
        (0, -1, -1)
        sage: v = vector(D.is_linearly_equivalent([0,1,1],True))
        sage: vector(D.values()) - s.laplacian()*v
        (0, 1, 1)
        sage: D.is_linearly_equivalent([0,0,0])
        False
        sage: D.is_linearly_equivalent([0,0,0],True)
        ()

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(3))
        >>> D = SandpileDivisor(s,[Integer(2),Integer(0),Integer(0)])
        >>> D.is_linearly_equivalent([Integer(0),Integer(1),Integer(1)])
        True
        >>> D.is_linearly_equivalent([Integer(0),Integer(1),Integer(1)],True)
        (0, -1, -1)
        >>> v = vector(D.is_linearly_equivalent([Integer(0),Integer(1),Integer(1)],True))
        >>> vector(D.values()) - s.laplacian()*v
        (0, 1, 1)
        >>> D.is_linearly_equivalent([Integer(0),Integer(0),Integer(0)])
        False
        >>> D.is_linearly_equivalent([Integer(0),Integer(0),Integer(0)],True)
        ()

    Note

    - If `with_firing_vector` is `False`, returns either `True` or `False`.

    - If `with_firing_vector` is `True` then: (i) if `self` is linearly equivalent to \(D\), returns a vector \(v\) such that `self - v*self.laplacian().transpose() = D`. Otherwise, (ii) if `self` is not linearly equivalent to \(D\), the output is the empty vector, `()`.

  is_q_reduced()

    Return whether the divisor is \(q\)-reduced.

    This would mean that \(self = c + kq\) where \(c\) is superstable, \(k\) is an integer, and \(q\) is the sink vertex.

    OUTPUT: boolean

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(4)
        sage: D = SandpileDivisor(s,[2,-3,2,0])
        sage: D.is_q_reduced()
        False
        sage: SandpileDivisor(s,[10,0,1,2]).is_q_reduced()
        True

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(4))
        >>> D = SandpileDivisor(s,[Integer(2),-Integer(3),Integer(2),Integer(0)])
        >>> D.is_q_reduced()
        False
        >>> SandpileDivisor(s,[Integer(10),Integer(0),Integer(1),Integer(2)]).is_q_reduced()
        True

    For undirected or, more generally, Eulerian graphs, \(q\)-reduced divisors are linearly equivalent if and only if they are equal. The same does not hold for general directed graphs:

    Sage

        sage: s = Sandpile({0:[1],1:[1,1]})
        sage: D = SandpileDivisor(s,[-1,1])
        sage: Z = s.zero_div()
        sage: D.is_q_reduced()
        True
        sage: Z.is_q_reduced()
        True
        sage: D == Z
        False
        sage: D.is_linearly_equivalent(Z)
        True

    [Python]

        >>> from sage.all import *
        >>> s = Sandpile({Integer(0):[Integer(1)],Integer(1):[Integer(1),Integer(1)]})
        >>> D = SandpileDivisor(s,[-Integer(1),Integer(1)])
        >>> Z = s.zero_div()
        >>> D.is_q_reduced()
        True
        >>> Z.is_q_reduced()
        True
        >>> D == Z
        False
        >>> D.is_linearly_equivalent(Z)
        True

  is_symmetric(orbits)

    Return whether the divisor is symmetric.

    This returns `True` if the values of the configuration are constant over the vertices in each sublist of `orbits`.

    INPUT:

    - `orbits` – list of lists of vertices

    OUTPUT: boolean

    EXAMPLES:

    Sage

        sage: S = sandpiles.House()
        sage: S.dict()
        {0: {1: 1, 2: 1},
         1: {0: 1, 3: 1},
         2: {0: 1, 3: 1, 4: 1},
         3: {1: 1, 2: 1, 4: 1},
         4: {2: 1, 3: 1}}
        sage: D = SandpileDivisor(S, [0,0,1,1,3])
        sage: D.is_symmetric([[2,3], [4]])
        True

    Python

        >>> from sage.all import *
        >>> S = sandpiles.House()
        >>> S.dict()
        {0: {1: 1, 2: 1},
         1: {0: 1, 3: 1},
         2: {0: 1, 3: 1, 4: 1},
         3: {1: 1, 2: 1, 4: 1},
         4: {2: 1, 3: 1}}
        >>> D = SandpileDivisor(S, [Integer(0),Integer(0),Integer(1),Integer(1),Integer(3)])
        >>> D.is_symmetric([[Integer(2),Integer(3)], [Integer(4)]])
        True

  is_weierstrass_pt(v='sink')

    Return whether the given vertex is a Weierstrass point.

    INPUT:

    - `v` – (default: `sink`) vertex

    OUTPUT: boolean

    EXAMPLES:

    Sage

        sage: s = sandpiles.House()
        sage: K = s.canonical_divisor()
        sage: K.weierstrass_rank_seq()  # sequence at the sink vertex, 0
        (1, 0, -1)
        sage: K.is_weierstrass_pt()
        False
        sage: K.weierstrass_rank_seq(4)
        (1, 0, 0, -1)
        sage: K.is_weierstrass_pt(4)
        True

    Python

        >>> from sage.all import *
        >>> s = sandpiles.House()
        >>> K = s.canonical_divisor()
        >>> K.weierstrass_rank_seq()  # sequence at the sink vertex, 0
        (1, 0, -1)
        >>> K.is_weierstrass_pt()
        False
        >>> K.weierstrass_rank_seq(Integer(4))
        (1, 0, 0, -1)
        >>> K.is_weierstrass_pt(Integer(4))
        True

    Note

    The vertex \(v\) is a (generalized) Weierstrass point for divisor \(D\) if the sequence of ranks \(r(D - nv)\) for \(n = 0, 1, 2, \dots\) is not \(r(D), r(D)-1, \dots, 0, -1, -1, \dots\)

  polytope()

    Return the polytope determining the complete linear system.

    OUTPUT: polytope

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(4)
        sage: D = SandpileDivisor(s,[4,2,0,0])
        sage: p = D.polytope()
        sage: p.inequalities()
        (An inequality (-3, 1, 1) x + 2 >= 0,
         An inequality (1, 1, 1) x + 4 >= 0,
         An inequality (1, -3, 1) x + 0 >= 0,
         An inequality (1, 1, -3) x + 0 >= 0)
        sage: D = SandpileDivisor(s,[-1,0,0,0])
        sage: D.polytope()
        The empty polyhedron in QQ^3

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(4))
        >>> D = SandpileDivisor(s,[Integer(4),Integer(2),Integer(0),Integer(0)])
        >>> p = D.polytope()
        >>> p.inequalities()
        (An inequality (-3, 1, 1) x + 2 >= 0,
         An inequality (1, 1, 1) x + 4 >= 0,
         An inequality (1, -3, 1) x + 0 >= 0,
         An inequality (1, 1, -3) x + 0 >= 0)
        >>> D = SandpileDivisor(s,[-Integer(1),Integer(0),Integer(0),Integer(0)])
        >>> D.polytope()
        The empty polyhedron in QQ^3

    Note

    For a divisor \(D\), this is the intersection of (i) the polyhedron determined by the system of inequalities \(L^t x \leq D\) where \(L^t\) is the transpose of the Laplacian with (ii) the hyperplane \(x_{\mathrm{sink\_vertex}} = 0\). The polytope is thought of as sitting in \((n-1)\)-dimensional Euclidean space where \(n\) is the number of vertices.

  polytope_integer_pts()

    Return the integer points inside divisor’s polytope.

    The polytope referred to here is the one determining the divisor’s complete linear system (see the documentation for `polytope`).

    OUTPUT: tuple of integer vectors

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(4)
        sage: D = SandpileDivisor(s,[4,2,0,0])
        sage: sorted(D.polytope_integer_pts())
        [(-2, -1, -1),
         (-1, -2, -1),
         (-1, -1, -2),
         (-1, -1, -1),
         (0, -1, -1),
         (0, 0, 0)]
        sage: D = SandpileDivisor(s,[-1,0,0,0])
        sage: D.polytope_integer_pts()
        ()

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(4))
        >>> D = SandpileDivisor(s,[Integer(4),Integer(2),Integer(0),Integer(0)])
        >>> sorted(D.polytope_integer_pts())
        [(-2, -1, -1),
         (-1, -2, -1),
         (-1, -1, -2),
         (-1, -1, -1),
         (0, -1, -1),
         (0, 0, 0)]
        >>> D = SandpileDivisor(s,[-Integer(1),Integer(0),Integer(0),Integer(0)])
        >>> D.polytope_integer_pts()
        ()

  q_reduced(verbose=True)

    Return the linearly equivalent \(q\)-reduced divisor.

    INPUT:

    - `verbose` – boolean (default: `True`)

    OUTPUT: SandpileDivisor or list representing SandpileDivisor

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(4)
        sage: D = SandpileDivisor(s,[2,-3,2,0])
        sage: D.q_reduced()
        {0: -2, 1: 1, 2: 2, 3: 0}
        sage: D.q_reduced(False)
        [-2, 1, 2, 0]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(4))
        >>> D = SandpileDivisor(s,[Integer(2),-Integer(3),Integer(2),Integer(0)])
        >>> D.q_reduced()
        {0: -2, 1: 1, 2: 2, 3: 0}
        >>> D.q_reduced(False)
        [-2, 1, 2, 0]

    Note

    The divisor \(D\) is \(qreduced if `D = c + kq\) where \(c\) is superstable, \(k\) is an integer, and \(q\) is the sink.

  rank(with_witness=False)

    Return the rank of the divisor.

    Optionally this returns an effective divisor \(E\) such that \(D - E\) is not winnable (has an empty complete linear system).

    INPUT:

    - `with_witness` – boolean (default: `False`)

    OUTPUT: integer or (integer, SandpileDivisor)

    EXAMPLES:

           sage: S = sandpiles.Complete(4)
           sage: D = SandpileDivisor(S,[4,2,0,0])
           sage: D.rank()
           3
           sage: D.rank(True)
           (3, {0: 3, 1: 0, 2: 1, 3: 0})
           sage: E = _[1]
           sage: (D - E).rank()
           -1

        Riemann-Roch theorem::

           sage: D.rank() - (S.canonical_divisor()-D).rank() == D.deg() + 1 - S.genus()
           True

        Riemann-Roch theorem::

           sage: D.rank() - (S.canonical_divisor()-D).rank() == D.deg() + 1 - S.genus()
           True
           sage: S = Sandpile({0:[1,1,1,2],1:[0,0,0,1,1,1,2,2],2:[2,2,1,1,0]},0)  # multigraph with loops
           sage: D = SandpileDivisor(S,[4,2,0])
           sage: D.rank(True)
           (2, {0: 1, 1: 1, 2: 1})
           sage: S = Sandpile({0:[1,2], 1:[0,2,2], 2: [0,1]},0) # directed graph
           sage: S.is_undirected()
           False
           sage: D = SandpileDivisor(S,[0,2,0])
           sage: D.effective_div()
           [{0: 0, 1: 2, 2: 0}, {0: 2, 1: 0, 2: 0}]
           sage: D.rank(True)
           (0, {0: 0, 1: 0, 2: 1})
           sage: E = D.rank(True)[1]
           sage: (D - E).effective_div()
           []

    Note

    The rank of a divisor \(D\) is -1 if \(D\) is not linearly equivalent to an effective divisor (i.e., the dollar game represented by \(D\) is unwinnable). Otherwise, the rank of \(D\) is the largest integer \(r\) such that \(D - E\) is linearly equivalent to an effective divisor for all effective divisors \(E\) with \(\deg(E) = r\).

  sandpile()

    The divisor’s underlying sandpile.

    OUTPUT: Sandpile

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: D = SandpileDivisor(S,[1,-2,0,3])
        sage: D.sandpile()
        Diamond sandpile graph: 4 vertices, sink = 0
        sage: D.sandpile() == S
        True

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> D = SandpileDivisor(S,[Integer(1),-Integer(2),Integer(0),Integer(3)])
        >>> D.sandpile()
        Diamond sandpile graph: 4 vertices, sink = 0
        >>> D.sandpile() == S
        True

  show(heights=True, directed=None, **kwds)

    Show the divisor.

    INPUT:

    - `heights` – boolean (default: `True`); whether to label each vertex with the amount of sand

    - `directed` – (optional) whether to draw directed edges

    - `kwds` – (optional) arguments passed to the show method for Graph

    EXAMPLES:

    Sage

        sage: S = sandpiles.Diamond()
        sage: D = SandpileDivisor(S, [1,-2,0,2])
        sage: D.show(graph_border=True, vertex_size=700, directed=False)

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Diamond()
        >>> D = SandpileDivisor(S, [Integer(1),-Integer(2),Integer(0),Integer(2)])
        >>> D.show(graph_border=True, vertex_size=Integer(700), directed=False)

  simulate_threshold(distrib=None)

    Return the first unstabilizable divisor in the closed Markov chain.

    (See NOTE.)

    INPUT:

    - `distrib` – (optional) list of nonnegative numbers representing a probability distribution on the vertices

    OUTPUT: SandpileDivisor

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(4)
        sage: D = s.zero_div()
        sage: D.simulate_threshold()  # random
        {0: 2, 1: 3, 2: 1, 3: 2}
        sage: n(mean([D.simulate_threshold().deg() for _ in range(10)]))  # random
        7.10000000000000
        sage: n(s.stationary_density()*s.n_vertices())
        6.93750000000000

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(4))
        >>> D = s.zero_div()
        >>> D.simulate_threshold()  # random
        {0: 2, 1: 3, 2: 1, 3: 2}
        >>> n(mean([D.simulate_threshold().deg() for _ in range(Integer(10))]))  # random
        7.10000000000000
        >>> n(s.stationary_density()*s.n_vertices())
        6.93750000000000

    Note

    Starting at `self`, repeatedly choose a vertex and add a grain of sand to it. Return the first unstabilizable divisor that is reached. Also see the `markov_chain` method for the underlying sandpile.

  stabilize(with_firing_vector=False)

    The stabilization of the divisor. If not stabilizable, return an error.

    INPUT:

    - `with_firing_vector` – boolean (default: `False`)

    EXAMPLES:

    Sage

        sage: s = sandpiles.Complete(4)
        sage: D = SandpileDivisor(s,[0,3,0,0])
        sage: D.stabilize()
        {0: 1, 1: 0, 2: 1, 3: 1}
        sage: D.stabilize(with_firing_vector=True)
        [{0: 1, 1: 0, 2: 1, 3: 1}, {0: 0, 1: 1, 2: 0, 3: 0}]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Complete(Integer(4))
        >>> D = SandpileDivisor(s,[Integer(0),Integer(3),Integer(0),Integer(0)])
        >>> D.stabilize()
        {0: 1, 1: 0, 2: 1, 3: 1}
        >>> D.stabilize(with_firing_vector=True)
        [{0: 1, 1: 0, 2: 1, 3: 1}, {0: 0, 1: 1, 2: 0, 3: 0}]

  support()

    List of vertices at which the divisor is nonzero.

    OUTPUT: list representing the support of the divisor

    EXAMPLES:

    Sage

        sage: S = sandpiles.Cycle(4)
        sage: D = SandpileDivisor(S, [0,0,1,1])
        sage: D.support()
        [2, 3]
        sage: S.vertices(sort=True)
        [0, 1, 2, 3]

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Cycle(Integer(4))
        >>> D = SandpileDivisor(S, [Integer(0),Integer(0),Integer(1),Integer(1)])
        >>> D.support()
        [2, 3]
        >>> S.vertices(sort=True)
        [0, 1, 2, 3]

  unstable()

    Return the unstable vertices.

    OUTPUT: list of vertices

    EXAMPLES:

    Sage

        sage: S = sandpiles.Cycle(3)
        sage: D = SandpileDivisor(S, [1,2,3])
        sage: D.unstable()
        [1, 2]

    Python

        >>> from sage.all import *
        >>> S = sandpiles.Cycle(Integer(3))
        >>> D = SandpileDivisor(S, [Integer(1),Integer(2),Integer(3)])
        >>> D.unstable()
        [1, 2]

  values()

    The values of the divisor as a list.

    The list is sorted in the order of the vertices.

    OUTPUT: list of integers

    boolean

    EXAMPLES:

    Sage

        sage: S = Sandpile({'a':['c','b'], 'b':['c','a'], 'c':['a']},'a')
        sage: D = SandpileDivisor(S, {'a':0, 'b':1, 'c':2})
        sage: D
        {'a': 0, 'b': 1, 'c': 2}
        sage: D.values()
        [0, 1, 2]
        sage: S.vertices(sort=True)
        ['a', 'b', 'c']

    Python

        >>> from sage.all import *
        >>> S = Sandpile({'a':['c','b'], 'b':['c','a'], 'c':['a']},'a')
        >>> D = SandpileDivisor(S, {'a':Integer(0), 'b':Integer(1), 'c':Integer(2)})
        >>> D
        {'a': 0, 'b': 1, 'c': 2}
        >>> D.values()
        [0, 1, 2]
        >>> S.vertices(sort=True)
        ['a', 'b', 'c']

  weierstrass_div(verbose=True)

    The Weierstrass divisor. Its value at a vertex is the weight of that vertex as a Weierstrass point. (See `SandpileDivisor.weierstrass_gap_seq`.)

    INPUT:

    - `verbose` – boolean (default: `True`)

    OUTPUT: SandpileDivisor

    EXAMPLES:

    Sage

        sage: s = sandpiles.Diamond()
        sage: D = SandpileDivisor(s,[4,2,1,0])
        sage: [D.weierstrass_rank_seq(v) for v in s]
        [(5, 4, 3, 2, 1, 0, 0, -1),
         (5, 4, 3, 2, 1, 0, -1),
         (5, 4, 3, 2, 1, 0, 0, 0, -1),
         (5, 4, 3, 2, 1, 0, 0, -1)]
        sage: D.weierstrass_div()
        {0: 1, 1: 0, 2: 2, 3: 1}
        sage: k5 = sandpiles.Complete(5)
        sage: K = k5.canonical_divisor()
        sage: K.weierstrass_div()
        {0: 9, 1: 9, 2: 9, 3: 9, 4: 9}

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Diamond()
        >>> D = SandpileDivisor(s,[Integer(4),Integer(2),Integer(1),Integer(0)])
        >>> [D.weierstrass_rank_seq(v) for v in s]
        [(5, 4, 3, 2, 1, 0, 0, -1),
         (5, 4, 3, 2, 1, 0, -1),
         (5, 4, 3, 2, 1, 0, 0, 0, -1),
         (5, 4, 3, 2, 1, 0, 0, -1)]
        >>> D.weierstrass_div()
        {0: 1, 1: 0, 2: 2, 3: 1}
        >>> k5 = sandpiles.Complete(Integer(5))
        >>> K = k5.canonical_divisor()
        >>> K.weierstrass_div()
        {0: 9, 1: 9, 2: 9, 3: 9, 4: 9}

  weierstrass_gap_seq(v='sink', weight=True)

    The Weierstrass gap sequence at the given vertex. If `weight` is `True`, then also compute the weight of each gap value.

    INPUT:

    - `v` – (default: `sink`) vertex

    - `weight` – boolean (default: `True`)

    OUTPUT: list or (list of list) of integers

    EXAMPLES:

    Sage

        sage: s = sandpiles.Cycle(4)
        sage: D = SandpileDivisor(s,[2,0,0,0])
        sage: [D.weierstrass_gap_seq(v,False) for v in s.vertices(sort=True)]
        [(1, 3), (1, 2), (1, 3), (1, 2)]
        sage: [D.weierstrass_gap_seq(v) for v in s.vertices(sort=True)]
        [((1, 3), 1), ((1, 2), 0), ((1, 3), 1), ((1, 2), 0)]
        sage: D.weierstrass_gap_seq()   # gap sequence at sink vertex, 0
        ((1, 3), 1)
        sage: D.weierstrass_rank_seq()  # rank sequence at the sink vertex
        (1, 0, 0, -1)

    Python

        >>> from sage.all import *
        >>> s = sandpiles.Cycle(Integer(4))
        >>> D = SandpileDivisor(s,[Integer(2),Integer(0),Integer(0),Integer(0)])
        >>> [D.weierstrass_gap_seq(v,False) for v in s.vertices(sort=True)]
        [(1, 3), (1, 2), (1, 3), (1, 2)]
        >>> [D.weierstrass_gap_seq(v) for v in s.vertices(sort=True)]
        [((1, 3), 1), ((1, 2), 0), ((1, 3), 1), ((1, 2), 0)]
        >>> D.weierstrass_gap_seq()   # gap sequence at sink vertex, 0
        ((1, 3), 1)
        >>> D.weierstrass_rank_seq()  # rank sequence at the sink vertex
        (1, 0, 0, -1)

    Note

    The integer \(k\) is a Weierstrass gap for the divisor \(D\) at vertex \(v\) if the rank of \(D - (k-1)v\) does not equal the rank of \(D - kv\). Let \(r\) be the rank of \(D\) and let \(k_i\) be the \(i\)-th gap at \(v\). The Weierstrass weight of \(v\) for \(D\) is the sum of \((k_i - i)\) as \(i\) ranges from \(1\) to \(r + 1\). It measure the difference between the sequence \(r, r - 1, ..., 0, -1, -1, ...\) and the rank sequence \(\mathrm{rank}(D), \mathrm{rank}(D - v), \mathrm{rank}(D - 2v), \dots\)

  weierstrass_pts(with_rank_seq=False)

    The Weierstrass points (vertices). Optionally, return the corresponding rank sequences.

    INPUT:

    - `with_rank_seq` – boolean (default: `False`)

    OUTPUT: tuple of vertices or list of (vertex, rank sequence)

    EXAMPLES:

    Sage

        sage: s = sandpiles.House()
        sage: K = s.canonical_divisor()
        sage: K.weierstrass_pts()
        (4,)
        sage: K.weierstrass_pts(True)
        [(4, (1, 0, 0, -1))]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.House()
        >>> K = s.canonical_divisor()
        >>> K.weierstrass_pts()
        (4,)
        >>> K.weierstrass_pts(True)
        [(4, (1, 0, 0, -1))]

    Note

    The vertex \(v\) is a (generalized) Weierstrass point for divisor \(D\) if the sequence of ranks \(r(D - nv)\) for \(n = 0, 1, 2, \dots`\) is not \(r(D), r(D)-1, \dots, 0, -1, -1, \dots\)

  weierstrass_rank_seq(v='sink')

    The Weierstrass rank sequence at the given vertex. Computes the rank of the divisor \(D - nv\) starting with \(n=0\) and ending when the rank is \(-1\).

    INPUT:

    - `v` – (default: `sink`) vertex

    OUTPUT: tuple of int

    EXAMPLES:

    Sage

        sage: s = sandpiles.House()
        sage: K = s.canonical_divisor()
        sage: [K.weierstrass_rank_seq(v) for v in s.vertices(sort=True)]
        [(1, 0, -1), (1, 0, -1), (1, 0, -1), (1, 0, -1), (1, 0, 0, -1)]

    Python

        >>> from sage.all import *
        >>> s = sandpiles.House()
        >>> K = s.canonical_divisor()
        >>> [K.weierstrass_rank_seq(v) for v in s.vertices(sort=True)]
        [(1, 0, -1), (1, 0, -1), (1, 0, -1), (1, 0, -1), (1, 0, 0, -1)]

sage.sandpiles.sandpile.admissible_partitions(S, k)

  The partitions of the vertices of \(S\) into \(k\) parts, each of which is connected.

  INPUT:

  - `S` – Sandpile

  - `k` – integer

  OUTPUT: partitions

  EXAMPLES:

  Sage

      sage: from sage.sandpiles.sandpile import admissible_partitions
      sage: from sage.sandpiles.sandpile import partition_sandpile
      sage: S = sandpiles.Cycle(4)
      sage: P = [list(admissible_partitions(S, i)) for i in [2,3,4]]
      sage: P
      [[{{0, 2, 3}, {1}},
        {{0, 3}, {1, 2}},
        {{0, 1, 3}, {2}},
        {{0}, {1, 2, 3}},
        {{0, 1}, {2, 3}},
        {{0, 1, 2}, {3}}],
       [{{0, 3}, {1}, {2}},
        {{0}, {1}, {2, 3}},
        {{0}, {1, 2}, {3}},
        {{0, 1}, {2}, {3}}],
       [{{0}, {1}, {2}, {3}}]]
      sage: for p in P:
      ....:  sum([partition_sandpile(S, i).betti(verbose=False)[-1] for i in p])
      6
      8
      3
      sage: S.betti()
                 0     1     2     3
      ------------------------------
          0:     1     -     -     -
          1:     -     6     8     3
      ------------------------------
      total:     1     6     8     3

  Python

      >>> from sage.all import *
      >>> from sage.sandpiles.sandpile import admissible_partitions
      >>> from sage.sandpiles.sandpile import partition_sandpile
      >>> S = sandpiles.Cycle(Integer(4))
      >>> P = [list(admissible_partitions(S, i)) for i in [Integer(2),Integer(3),Integer(4)]]
      >>> P
      [[{{0, 2, 3}, {1}},
        {{0, 3}, {1, 2}},
        {{0, 1, 3}, {2}},
        {{0}, {1, 2, 3}},
        {{0, 1}, {2, 3}},
        {{0, 1, 2}, {3}}],
       [{{0, 3}, {1}, {2}},
        {{0}, {1}, {2, 3}},
        {{0}, {1, 2}, {3}},
        {{0, 1}, {2}, {3}}],
       [{{0}, {1}, {2}, {3}}]]
      >>> for p in P:
      ...  sum([partition_sandpile(S, i).betti(verbose=False)[-Integer(1)] for i in p])
      6
      8
      3
      >>> S.betti()
                 0     1     2     3
      ------------------------------
          0:     1     -     -     -
          1:     -     6     8     3
      ------------------------------
      total:     1     6     8     3

sage.sandpiles.sandpile.aztec_sandpile(n)

  The aztec diamond graph.

  INPUT:

  - `n` – integer

  OUTPUT: dictionary for the aztec diamond graph

  EXAMPLES:

  Sage

      sage: from sage.sandpiles.sandpile import aztec_sandpile
      sage: T = aztec_sandpile(2)
      sage: sorted(len(v) for u, v in T.items())
      [3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 8]
      sage: Sandpile(T,(0, 0)).group_order()
      4542720

  Python

      >>> from sage.all import *
      >>> from sage.sandpiles.sandpile import aztec_sandpile
      >>> T = aztec_sandpile(Integer(2))
      >>> sorted(len(v) for u, v in T.items())
      [3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 8]
      >>> Sandpile(T,(Integer(0), Integer(0))).group_order()
      4542720

  Note

  This is the aztec diamond graph with a sink vertex added. Boundary vertices have edges to the sink so that each vertex has degree 4.

sage.sandpiles.sandpile.firing_graph(S, eff)

  Create a digraph with divisors as vertices and edges between two divisors \(D\) and \(E\) if firing a single vertex in \(D\) gives \(E\).

  INPUT:

  - `S` – Sandpile

  - `eff` – list of divisors

  OUTPUT: DiGraph

  EXAMPLES:

  Sage

      sage: S = sandpiles.Cycle(6)
      sage: D = SandpileDivisor(S, [1,1,1,1,2,0])
      sage: eff = D.effective_div()
      sage: firing_graph(S, eff).show3d(edge_size=.005,               # long time
      ....:                             vertex_size=0.01)

  Python

      >>> from sage.all import *
      >>> S = sandpiles.Cycle(Integer(6))
      >>> D = SandpileDivisor(S, [Integer(1),Integer(1),Integer(1),Integer(1),Integer(2),Integer(0)])
      >>> eff = D.effective_div()
      >>> firing_graph(S, eff).show3d(edge_size=RealNumber('.005'),               # long time
      ...                             vertex_size=RealNumber('0.01'))

sage.sandpiles.sandpile.glue_graphs(g, h, glue_g, glue_h)

  Glue two graphs together.

  INPUT:

  - `g`, `h` – dictionaries for directed multigraphs

  - `glue_h`, `glue_g` – dictionaries for a vertex

  OUTPUT: dictionary for a directed multigraph

  EXAMPLES:

  Sage

      sage: from sage.sandpiles.sandpile import glue_graphs
      sage: x = {0: {}, 1: {0: 1}, 2: {0: 1, 1: 1}, 3: {0: 1, 1: 1, 2: 1}}
      sage: y = {0: {}, 1: {0: 2}, 2: {1: 2}, 3: {0: 1, 2: 1}}
      sage: glue_x = {1: 1, 3: 2}
      sage: glue_y = {0: 1, 1: 2, 3: 1}
      sage: z = glue_graphs(x,y,glue_x,glue_y); z
      {'sink': {},
       'x0': {'sink': 1, 'x1': 1, 'x3': 2, 'y1': 2, 'y3': 1},
       'x1': {'x0': 1},
       'x2': {'x0': 1, 'x1': 1},
       'x3': {'x0': 1, 'x1': 1, 'x2': 1},
       'y1': {'sink': 2},
       'y2': {'y1': 2},
       'y3': {'sink': 1, 'y2': 1}}
      sage: S = Sandpile(z,'sink')
      sage: S.h_vector()
      [1, 6, 17, 31, 41, 41, 31, 17, 6, 1]
      sage: S.resolution()
      'R^1 <-- R^7 <-- R^21 <-- R^35 <-- R^35 <-- R^21 <-- R^7 <-- R^1'

  Python

      >>> from sage.all import *
      >>> from sage.sandpiles.sandpile import glue_graphs
      >>> x = {Integer(0): {}, Integer(1): {Integer(0): Integer(1)}, Integer(2): {Integer(0): Integer(1), Integer(1): Integer(1)}, Integer(3): {Integer(0): Integer(1), Integer(1): Integer(1), Integer(2): Integer(1)}}
      >>> y = {Integer(0): {}, Integer(1): {Integer(0): Integer(2)}, Integer(2): {Integer(1): Integer(2)}, Integer(3): {Integer(0): Integer(1), Integer(2): Integer(1)}}
      >>> glue_x = {Integer(1): Integer(1), Integer(3): Integer(2)}
      >>> glue_y = {Integer(0): Integer(1), Integer(1): Integer(2), Integer(3): Integer(1)}
      >>> z = glue_graphs(x,y,glue_x,glue_y); z
      {'sink': {},
       'x0': {'sink': 1, 'x1': 1, 'x3': 2, 'y1': 2, 'y3': 1},
       'x1': {'x0': 1},
       'x2': {'x0': 1, 'x1': 1},
       'x3': {'x0': 1, 'x1': 1, 'x2': 1},
       'y1': {'sink': 2},
       'y2': {'y1': 2},
       'y3': {'sink': 1, 'y2': 1}}
      >>> S = Sandpile(z,'sink')
      >>> S.h_vector()
      [1, 6, 17, 31, 41, 41, 31, 17, 6, 1]
      >>> S.resolution()
      'R^1 <-- R^7 <-- R^21 <-- R^35 <-- R^35 <-- R^21 <-- R^7 <-- R^1'

  Note

  This method makes a dictionary for a graph by combining those for \(g\) and \(h\). The sink of \(g\) is replaced by a vertex that is connected to the vertices of \(g\) as specified by `glue_g` the vertices of \(h\) as specified in `glue_h`. The sink of the glued graph is `'sink'`.

  Both `glue_g` and `glue_h` are dictionaries with entries of the form `v:w` where `v` is the vertex to be connected to and `w` is the weight of the connecting edge.

sage.sandpiles.sandpile.min_cycles(G, v)

  Minimal length cycles in the digraph \(G\) starting at vertex \(v\).

  INPUT:

  - `G` – DiGraph

  - `v` – vertex of `G`

  OUTPUT: list of lists of vertices

  EXAMPLES:

  Sage

      sage: from sage.sandpiles.sandpile import min_cycles, sandlib
      sage: T = sandlib('gor')
      sage: [min_cycles(T, i) for i in T.vertices(sort=True)]
      [[], [[1, 3]], [[2, 3, 1], [2, 3]], [[3, 1], [3, 2]]]

  Python

      >>> from sage.all import *
      >>> from sage.sandpiles.sandpile import min_cycles, sandlib
      >>> T = sandlib('gor')
      >>> [min_cycles(T, i) for i in T.vertices(sort=True)]
      [[], [[1, 3]], [[2, 3, 1], [2, 3]], [[3, 1], [3, 2]]]

sage.sandpiles.sandpile.parallel_firing_graph(S, eff)

  Create a digraph with divisors as vertices and edges between two divisors \(D\) and \(E\) if firing all unstable vertices in \(D\) gives \(E\).

  INPUT:

  - `S` – Sandpile

  - `eff` – list of divisors

  OUTPUT: DiGraph

  EXAMPLES:

  Sage

      sage: S = sandpiles.Cycle(6)
      sage: D = SandpileDivisor(S, [1,1,1,1,2,0])
      sage: eff = D.effective_div()
      sage: parallel_firing_graph(S, eff).show3d(edge_size=.005,      # long time
      ....:                                      vertex_size=0.01)

  Python

      >>> from sage.all import *
      >>> S = sandpiles.Cycle(Integer(6))
      >>> D = SandpileDivisor(S, [Integer(1),Integer(1),Integer(1),Integer(1),Integer(2),Integer(0)])
      >>> eff = D.effective_div()
      >>> parallel_firing_graph(S, eff).show3d(edge_size=RealNumber('.005'),      # long time
      ...                                      vertex_size=RealNumber('0.01'))

sage.sandpiles.sandpile.partition_sandpile(S, p)

  Each set of vertices in \(p\) is regarded as a single vertex, with and edge between \(A\) and \(B\) if some element of \(A\) is connected by an edge to some element of \(B\) in \(S\).

  INPUT:

  - `S` – Sandpile

  - `p` – partition of the vertices of `S`

  OUTPUT: Sandpile

  EXAMPLES:

  Sage

      sage: from sage.sandpiles.sandpile import admissible_partitions, partition_sandpile
      sage: S = sandpiles.Cycle(4)
      sage: P = [list(admissible_partitions(S, i)) for i in [2,3,4]]
      sage: for p in P:
      ....:  sum([partition_sandpile(S, i).betti(verbose=False)[-1] for i in p])
      6
      8
      3
      sage: S.betti()
                 0     1     2     3
      ------------------------------
          0:     1     -     -     -
          1:     -     6     8     3
      ------------------------------
      total:     1     6     8     3

  Python

      >>> from sage.all import *
      >>> from sage.sandpiles.sandpile import admissible_partitions, partition_sandpile
      >>> S = sandpiles.Cycle(Integer(4))
      >>> P = [list(admissible_partitions(S, i)) for i in [Integer(2),Integer(3),Integer(4)]]
      >>> for p in P:
      ...  sum([partition_sandpile(S, i).betti(verbose=False)[-Integer(1)] for i in p])
      6
      8
      3
      >>> S.betti()
                 0     1     2     3
      ------------------------------
          0:     1     -     -     -
          1:     -     6     8     3
      ------------------------------
      total:     1     6     8     3

sage.sandpiles.sandpile.sandlib(selector=None)

  Return the sandpile identified by `selector`. If no argument is given, a description of the sandpiles in the sandlib is printed.

  INPUT:

  - `selector` – (optional) identifier or None

  OUTPUT: Sandpile or description

  EXAMPLES:

  Sage

      sage: from sage.sandpiles.sandpile import sandlib
      sage: sandlib()
        Sandpiles in the sandlib:
           ci1 : complete intersection, non-DAG but equivalent to a DAG
           generic : generic digraph with 6 vertices
           genus2 : Undirected graph of genus 2
           gor : Gorenstein but not a complete intersection
           kite : generic undirected graphs with 5 vertices
           riemann-roch1 : directed graph with postulation 9 and 3 maximal weight superstables
           riemann-roch2 : directed graph with a superstable not majorized by a maximal superstable
      sage: S = sandlib('gor')
      sage: S.resolution()
      'R^1 <-- R^5 <-- R^5 <-- R^1'

  Python

      >>> from sage.all import *
      >>> from sage.sandpiles.sandpile import sandlib
      >>> sandlib()
        Sandpiles in the sandlib:
           ci1 : complete intersection, non-DAG but equivalent to a DAG
           generic : generic digraph with 6 vertices
           genus2 : Undirected graph of genus 2
           gor : Gorenstein but not a complete intersection
           kite : generic undirected graphs with 5 vertices
           riemann-roch1 : directed graph with postulation 9 and 3 maximal weight superstables
           riemann-roch2 : directed graph with a superstable not majorized by a maximal superstable
      >>> S = sandlib('gor')
      >>> S.resolution()
      'R^1 <-- R^5 <-- R^5 <-- R^1'

sage.sandpiles.sandpile.triangle_sandpile(n)

  A triangular sandpile. Each nonsink vertex has out-degree six. The vertices on the boundary of the triangle are connected to the sink.

  INPUT:

  - `n` – integer

  OUTPUT: Sandpile

  EXAMPLES:

  Sage

      sage: from sage.sandpiles.sandpile import triangle_sandpile
      sage: T = triangle_sandpile(5)
      sage: T.group_order()
      135418115000

  Python

      >>> from sage.all import *
      >>> from sage.sandpiles.sandpile import triangle_sandpile
      >>> T = triangle_sandpile(Integer(5))
      >>> T.group_order()
      135418115000

sage.sandpiles.sandpile.wilmes_algorithm(M)

  Compute an integer matrix \(L\) with the same integer row span as \(M\) and such that \(L\) is the reduced Laplacian of a directed multigraph.

  INPUT:

  - `M` – square integer matrix of full rank

  OUTPUT: integer matrix (`L`)

  EXAMPLES:

  Sage

      sage: from sage.sandpiles.sandpile import wilmes_algorithm
      sage: P = matrix([[2,3,-7,-3],[5,2,-5,5],[8,2,5,4],[-5,-9,6,6]])
      sage: wilmes_algorithm(P)
      [ 3279   -79 -1599 -1600]
      [   -1  1539  -136 -1402]
      [    0    -1  1650 -1649]
      [    0     0 -1658  1658]

  Python

      >>> from sage.all import *
      >>> from sage.sandpiles.sandpile import wilmes_algorithm
      >>> P = matrix([[Integer(2),Integer(3),-Integer(7),-Integer(3)],[Integer(5),Integer(2),-Integer(5),Integer(5)],[Integer(8),Integer(2),Integer(5),Integer(4)],[-Integer(5),-Integer(9),Integer(6),Integer(6)]])
      >>> wilmes_algorithm(P)
      [ 3279   -79 -1599 -1600]
      [   -1  1539  -136 -1402]
      [    0    -1  1650 -1649]
      [    0     0 -1658  1658]

  REFERENCES:

  - [PPW2013]