Greedy Algorithm Module

class chipfiring.CFGreedyAlgorithm.GreedyAlgorithm(graph: CFGraph, divisor: CFDivisor)[source]

Bases: object

__init__(graph: CFGraph, divisor: CFDivisor)[source]

Initialize the greedy algorithm for the dollar game.

Parameters:
  • graph – A CFGraph object representing the graph.

  • divisor – A CFDivisor object representing the initial chip configuration.

Example

>>> G = CFGraph({"A", "B", "C", "D"}, [])
>>> G.add_edge("A", "B", 1)
>>> G.add_edge("B", "C", 1)
>>> G.add_edge("C", "D", 1)
>>> G.add_edge("D", "A", 1)
>>> G.add_edge("A", "C", 1)
>>> divisor = CFDivisor(G, [("A", 2), ("B", 1), ("C", 0), ("D", 1)])
>>> algorithm = GreedyAlgorithm(G, divisor)
>>> algorithm.graph == G
True
>>> algorithm.divisor == divisor
True
>>> # The firing script is initialized with all zeros
>>> all(algorithm.firing_script.get_firings(v) == 0 for v in "ABCD")
True
is_effective() bool[source]

Check if all vertices have non-negative wealth.

Returns:

True if effective (all vertices have non-negative chips), otherwise False.

Example

>>> G = CFGraph({"A", "B", "C", "D"}, [])
>>> G.add_edge("A", "B", 1)
>>> G.add_edge("B", "C", 1)
>>> G.add_edge("C", "D", 1)
>>> G.add_edge("D", "A", 1)
>>> G.add_edge("A", "C", 1)
>>> # With all non-negative chips
>>> divisor = CFDivisor(G, [("A", 2), ("B", 1), ("C", 0), ("D", 1)])
>>> algorithm = GreedyAlgorithm(G, divisor)
>>> algorithm.is_effective()
True
>>> # With one vertex having negative chips
>>> divisor2 = CFDivisor(G, [("A", 2), ("B", -1), ("C", 0), ("D", 1)])
>>> algorithm2 = GreedyAlgorithm(G, divisor2)
>>> algorithm2.is_effective()
False
borrowing_move(vertex_name: str) None[source]

Perform a borrowing move at the specified vertex.

Parameters:

vertex_name – The name of the vertex at which to perform the borrowing move.

Example

>>> G = CFGraph({"A", "B", "C", "D"}, [])
>>> G.add_edge("A", "B", 1)
>>> G.add_edge("B", "C", 1)
>>> G.add_edge("C", "D", 1)
>>> G.add_edge("D", "A", 1)
>>> G.add_edge("A", "C", 1)
>>> divisor = CFDivisor(G, [("A", 2), ("B", -1), ("C", 0), ("D", 1)])
>>> algorithm = GreedyAlgorithm(G, divisor)
>>> # Initial state
>>> algorithm.divisor.get_degree("B")
-1
>>> algorithm.firing_script.get_firings("B")
0
>>> # Perform a borrowing move at vertex B
>>> algorithm.borrowing_move("B")
>>> # After borrowing, B's wealth increases by its valence (2 in this graph)
>>> algorithm.divisor.get_degree("B")  # -1 + 2 = 1
1
>>> # Firing script decrements for borrowing vertex
>>> algorithm.firing_script.get_firings("B")
-1
>>> # Neighbors lose chips equal to edge weights
>>> algorithm.divisor.get_degree("A")  # 2 - 1 = 1
1
>>> algorithm.divisor.get_degree("C")  # 0 - 1 = -1
-1
play() Tuple[bool, CFiringScript | None][source]

Execute the greedy algorithm to determine winnability.

Returns:

Tuple (True, firing_script) if the game is winnable; otherwise (False, None). The firing script is a dictionary mapping vertex names to their net number of firings.

Example

>>> G = CFGraph({"A", "B", "C", "D"}, [])
>>> G.add_edge("A", "B", 1)
>>> G.add_edge("B", "C", 1)
>>> G.add_edge("C", "D", 1)
>>> G.add_edge("D", "A", 1)
>>> G.add_edge("A", "C", 1)
>>> # Create a divisor with some debt
>>> divisor = CFDivisor(G, [("A", 2), ("B", -1), ("C", 0), ("D", 1)])
>>> algorithm = GreedyAlgorithm(G, divisor)
>>> # Play the game
>>> winnable, firing_script = algorithm.play()
>>> winnable  # The game should be winnable
True
>>> isinstance(firing_script, CFiringScript)
True
>>> # Check that the resulting divisor is effective
>>> all(algorithm.divisor.get_degree(v) >= 0 for v in "ABCD")
True
>>>
>>> # Example with an unwinnable configuration (too much debt)
>>> divisor2 = CFDivisor(G, [("A", -100), ("B", -100), ("C", -100), ("D", -100)])
>>> algorithm2 = GreedyAlgorithm(G, divisor2)
>>> winnable2, firing_script2 = algorithm2.play()
>>> winnable2  # Should exceed move limit and be unwinnable
False
>>> firing_script2 is None
True