Cycle enumeration

This module is meant for all functions related to cycle enumeration in graphs.

all_cycles_iterator()

Return an iterator over the cycles of self.

all_simple_cycles()

Return an iterator over the simple cycles of self.

Functions

sage.graphs.cycle_enumeration.all_cycles_iterator(self, starting_vertices=None, simple=False, rooted=False, max_length=None, trivial=False, weight_function=None, by_weight=False, check_weight=True, report_weight=False, algorithm='A')[source]

Return an iterator over all the cycles of self starting with one of the given vertices. Each edge must have a positive weight.

The cycles are enumerated in increasing length order. Here, a cycle means a closed walk \(v_0e_0v_1e_1 \ldots v_{k-1}e_{k-1}v_0\) where \(v_i\) are vertices and \(e_i\) are edges. \(v_i\) and \(e_i\) (\(0 \leq i < k\)) may not be distinct. If simple=True, then cycles with distinct \(v_i\) and \(e_i\) (\(0 \leq i < k\)) are enumerated.

INPUT:

  • starting_vertices – iterable (default: None); vertices from which the cycles must start. If None, then all vertices of the graph can be starting points. This argument is necessary if rooted is set to True.

  • simple – boolean (default: False); if set to True, then only simple cycles are considered. A cycle is simple if the only vertex occurring twice in it is the starting and ending one and no edge occurs twice.

  • rooted – boolean (default: False); if set to False, then cycles differing only by their starting vertex are considered the same (e.g. ['a', 'b', 'c', 'a'] and ['b', 'c', 'a', 'b']). Otherwise, all cycles are enumerated.

  • max_length – nonnegative integer (default: None); the maximum length of the enumerated paths. If set to None, then all lengths are allowed.

  • trivial – boolean (default: False); if set to True, then the empty paths are also enumerated

  • weight_function – function (default: None); a function that takes as input an edge (u, v, l) and outputs its weight. If not None, by_weight is automatically set to True. If None and by_weight is True, we use the edge label l as a weight.

  • by_weight – boolean (default: False); if True, the edges in the graph are weighted, otherwise all edges have weight 1

  • check_weight – boolean (default: True); whether to check that the weight_function outputs a number for each edge

  • report_weight – boolean (default: False); if False, just a cycle is returned. Otherwise a tuple of cycle length and cycle is returned.

  • algorithm – string (default: 'A'); the algorithm used to enumerate the cycles.

    • The algorithm 'A' holds cycle iterators starting with each vertex, and output them in increasing length order.

    • The algorithm 'B' holds cycle iterators starting with each edge, and output them in increasing length order. It depends on the k-shortest simple paths algorithm. Thus, it is not available if simple=False or rooted=True or starting_vertices is not None.

OUTPUT: iterator

AUTHOR:

Alexandre Blondin Masse

EXAMPLES:

sage: g = DiGraph({'a': ['a', 'b'], 'b': ['c'], 'c': ['d'], 'd': ['c']}, loops=True)
sage: it = g.all_cycles_iterator()
sage: for _ in range(7): print(next(it))
['a', 'a']
['a', 'a', 'a']
['c', 'd', 'c']
['a', 'a', 'a', 'a']
['a', 'a', 'a', 'a', 'a']
['c', 'd', 'c', 'd', 'c']
['a', 'a', 'a', 'a', 'a', 'a']
>>> from sage.all import *
>>> g = DiGraph({'a': ['a', 'b'], 'b': ['c'], 'c': ['d'], 'd': ['c']}, loops=True)
>>> it = g.all_cycles_iterator()
>>> for _ in range(Integer(7)): print(next(it))
['a', 'a']
['a', 'a', 'a']
['c', 'd', 'c']
['a', 'a', 'a', 'a']
['a', 'a', 'a', 'a', 'a']
['c', 'd', 'c', 'd', 'c']
['a', 'a', 'a', 'a', 'a', 'a']

There are no cycles in the empty graph and in acyclic graphs:

sage: g = DiGraph()
sage: it = g.all_cycles_iterator()
sage: list(it)
[]
sage: g = DiGraph({0:[1]})
sage: it = g.all_cycles_iterator()
sage: list(it)
[]
[Python]
>>> from sage.all import *
>>> g = DiGraph()
>>> it = g.all_cycles_iterator()
>>> list(it)
[]
>>> g = DiGraph({Integer(0):[Integer(1)]})
>>> it = g.all_cycles_iterator()
>>> list(it)
[]

It is possible to restrict the starting vertices of the cycles:

sage: g = DiGraph({'a': ['a', 'b'], 'b': ['c'], 'c': ['d'], 'd': ['c']}, loops=True)
sage: it = g.all_cycles_iterator(starting_vertices=['b', 'c'])
sage: for _ in range(3): print(next(it))
['c', 'd', 'c']
['c', 'd', 'c', 'd', 'c']
['c', 'd', 'c', 'd', 'c', 'd', 'c']
>>> from sage.all import *
>>> g = DiGraph({'a': ['a', 'b'], 'b': ['c'], 'c': ['d'], 'd': ['c']}, loops=True)
>>> it = g.all_cycles_iterator(starting_vertices=['b', 'c'])
>>> for _ in range(Integer(3)): print(next(it))
['c', 'd', 'c']
['c', 'd', 'c', 'd', 'c']
['c', 'd', 'c', 'd', 'c', 'd', 'c']

Also, one can bound the length of the cycles:

sage: it = g.all_cycles_iterator(max_length=3)
sage: list(it)
[['a', 'a'], ['a', 'a', 'a'], ['c', 'd', 'c'],
 ['a', 'a', 'a', 'a']]
[Python]
>>> from sage.all import *
>>> it = g.all_cycles_iterator(max_length=Integer(3))
>>> list(it)
[['a', 'a'], ['a', 'a', 'a'], ['c', 'd', 'c'],
 ['a', 'a', 'a', 'a']]

By default, cycles differing only by their starting point are not all enumerated, but this may be parametrized:

sage: it = g.all_cycles_iterator(max_length=3, rooted=False)
sage: list(it)
[['a', 'a'], ['a', 'a', 'a'], ['c', 'd', 'c'],
 ['a', 'a', 'a', 'a']]
sage: it = g.all_cycles_iterator(max_length=3, rooted=True)
sage: list(it)
[['a', 'a'], ['a', 'a', 'a'], ['c', 'd', 'c'], ['d', 'c', 'd'],
 ['a', 'a', 'a', 'a']]
>>> from sage.all import *
>>> it = g.all_cycles_iterator(max_length=Integer(3), rooted=False)
>>> list(it)
[['a', 'a'], ['a', 'a', 'a'], ['c', 'd', 'c'],
 ['a', 'a', 'a', 'a']]
>>> it = g.all_cycles_iterator(max_length=Integer(3), rooted=True)
>>> list(it)
[['a', 'a'], ['a', 'a', 'a'], ['c', 'd', 'c'], ['d', 'c', 'd'],
 ['a', 'a', 'a', 'a']]

One may prefer to enumerate simple cycles, i.e. cycles such that the only vertex occurring twice in it is the starting and ending one (see also all_simple_cycles()):

sage: it = g.all_cycles_iterator(simple=True)
sage: list(it)
[['a', 'a'], ['c', 'd', 'c']]
sage: g = digraphs.Circuit(4)
sage: list(g.all_cycles_iterator(simple=True))
[[0, 1, 2, 3, 0]]
[Python]
>>> from sage.all import *
>>> it = g.all_cycles_iterator(simple=True)
>>> list(it)
[['a', 'a'], ['c', 'd', 'c']]
>>> g = digraphs.Circuit(Integer(4))
>>> list(g.all_cycles_iterator(simple=True))
[[0, 1, 2, 3, 0]]

A cycle is enumerated in increasing length order for a weighted graph:

sage: g = DiGraph()
sage: g.add_edges([('a', 'b', 2), ('a', 'e', 2), ('b', 'a', 1), ('b', 'c', 1),
....:              ('c', 'd', 2), ('d', 'b', 1), ('e', 'a', 2)])
sage: it = g.all_cycles_iterator(simple=False, max_length=None,
....:                            by_weight=True, report_weight=True)
sage: for i in range(9): print(next(it))
(3, ['a', 'b', 'a'])
(4, ['a', 'e', 'a'])
(4, ['b', 'c', 'd', 'b'])
(6, ['a', 'b', 'a', 'b', 'a'])
(7, ['a', 'b', 'a', 'e', 'a'])
(7, ['a', 'b', 'c', 'd', 'b', 'a'])
(7, ['a', 'e', 'a', 'b', 'a'])
(8, ['a', 'e', 'a', 'e', 'a'])
(8, ['b', 'c', 'd', 'b', 'c', 'd', 'b'])
>>> from sage.all import *
>>> g = DiGraph()
>>> g.add_edges([('a', 'b', Integer(2)), ('a', 'e', Integer(2)), ('b', 'a', Integer(1)), ('b', 'c', Integer(1)),
...              ('c', 'd', Integer(2)), ('d', 'b', Integer(1)), ('e', 'a', Integer(2))])
>>> it = g.all_cycles_iterator(simple=False, max_length=None,
...                            by_weight=True, report_weight=True)
>>> for i in range(Integer(9)): print(next(it))
(3, ['a', 'b', 'a'])
(4, ['a', 'e', 'a'])
(4, ['b', 'c', 'd', 'b'])
(6, ['a', 'b', 'a', 'b', 'a'])
(7, ['a', 'b', 'a', 'e', 'a'])
(7, ['a', 'b', 'c', 'd', 'b', 'a'])
(7, ['a', 'e', 'a', 'b', 'a'])
(8, ['a', 'e', 'a', 'e', 'a'])
(8, ['b', 'c', 'd', 'b', 'c', 'd', 'b'])

Each edge must have a positive weight:

sage: g = DiGraph()
sage: g.add_edges([('a', 'b', -2), ('b', 'a', 1)])
sage: next(g.all_cycles_iterator(simple=False, max_length=None,
....:                            by_weight=True, report_weight=True))
Traceback (most recent call last):
...
ValueError: negative weight is not allowed
[Python]
>>> from sage.all import *
>>> g = DiGraph()
>>> g.add_edges([('a', 'b', -Integer(2)), ('b', 'a', Integer(1))])
>>> next(g.all_cycles_iterator(simple=False, max_length=None,
...                            by_weight=True, report_weight=True))
Traceback (most recent call last):
...
ValueError: negative weight is not allowed

The algorithm 'B' works for undirected graphs as well:

sage: g = Graph({0: [1, 2], 1: [0, 2], 2: [0, 1, 3, 5], 3: [2, 4], 4: [3, 5], 5: [4, 2]})
sage: for cycle in g.all_cycles_iterator(algorithm='B', simple=True, by_weight=True):
....:     print(cycle)
[0, 1, 2, 0]
[2, 3, 4, 5, 2]
>>> from sage.all import *
>>> g = Graph({Integer(0): [Integer(1), Integer(2)], Integer(1): [Integer(0), Integer(2)], Integer(2): [Integer(0), Integer(1), Integer(3), Integer(5)], Integer(3): [Integer(2), Integer(4)], Integer(4): [Integer(3), Integer(5)], Integer(5): [Integer(4), Integer(2)]})
>>> for cycle in g.all_cycles_iterator(algorithm='B', simple=True, by_weight=True):
...     print(cycle)
[0, 1, 2, 0]
[2, 3, 4, 5, 2]

The algorithm 'B' is unavailable when simple=False or rooted=True or starting_vertices is not None:

sage: g = DiGraph()
sage: g.add_edges([('a', 'b', 1), ('b', 'a', 1)])
sage: next(g.all_cycles_iterator(algorithm='B', simple=False))
....:
Traceback (most recent call last):
...
ValueError: The algorithm 'B' is unavailable when simple=False.
sage: next(g.all_cycles_iterator(algorithm='B', simple=True, rooted=True))
Traceback (most recent call last):
...
ValueError: The algorithm 'B' is unavailable when rooted=True.
sage: next(g.all_cycles_iterator(algorithm='B', simple=True, starting_vertices=['a']))
Traceback (most recent call last):
...
ValueError: The algorithm 'B' is unavailable when starting_vertices is not None.
[Python]
>>> from sage.all import *
>>> g = DiGraph()
>>> g.add_edges([('a', 'b', Integer(1)), ('b', 'a', Integer(1))])
>>> next(g.all_cycles_iterator(algorithm='B', simple=False))
....:
Traceback (most recent call last):
...
ValueError: The algorithm 'B' is unavailable when simple=False.
>>> next(g.all_cycles_iterator(algorithm='B', simple=True, rooted=True))
Traceback (most recent call last):
...
ValueError: The algorithm 'B' is unavailable when rooted=True.
>>> next(g.all_cycles_iterator(algorithm='B', simple=True, starting_vertices=['a']))
Traceback (most recent call last):
...
ValueError: The algorithm 'B' is unavailable when starting_vertices is not None.

The algorithm 'A' works for undirected graphs as well. Specifically, each cycle is enumerated exactly once, meaning a cycle and its reverse are not listed separately:

sage: g = Graph({0: [1, 2], 1: [0, 2], 2: [0, 1]})
sage: for cycle in g.all_cycles_iterator(algorithm='A', simple=True):
....:     print(cycle)
[0, 1, 2, 0]
>>> from sage.all import *
>>> g = Graph({Integer(0): [Integer(1), Integer(2)], Integer(1): [Integer(0), Integer(2)], Integer(2): [Integer(0), Integer(1)]})
>>> for cycle in g.all_cycles_iterator(algorithm='A', simple=True):
...     print(cycle)
[0, 1, 2, 0]
sage.graphs.cycle_enumeration.all_simple_cycles(self, starting_vertices=None, rooted=False, max_length=None, trivial=False, weight_function=None, by_weight=False, check_weight=True, report_weight=False, algorithm='B')[source]

Return a list of all simple cycles of self. The cycles are enumerated in increasing length order. Each edge must have a positive weight.

INPUT:

  • starting_vertices – iterable (default: None); vertices from which the cycles must start. If None, then all vertices of the graph can be starting points. This argument is necessary if rooted is set to True.

  • rooted – boolean (default: False); if set to False, then cycles differing only by their starting vertex are considered the same (e.g. ['a', 'b', 'c', 'a'] and ['b', 'c', 'a', 'b']). Otherwise, all cycles are enumerated.

  • max_length – nonnegative integer (default: None); the maximum length of the enumerated paths. If set to None, then all lengths are allowed.

  • trivial – boolean (default: False); if set to True, then the empty paths are also enumerated

  • weight_function – function (default: None); a function that takes as input an edge (u, v, l) and outputs its weight. If not None, by_weight is automatically set to True. If None and by_weight is True, we use the edge label l as a weight.

  • by_weight – boolean (default: False); if True, the edges in the graph are weighted, otherwise all edges have weight 1

  • check_weight – boolean (default: True); whether to check that the weight_function outputs a number for each edge

  • report_weight – boolean (default: False); if False, just a cycle is returned. Otherwise a tuple of cycle length and cycle is returned.

  • algorithm – string (default: 'B'); the algorithm used to enumerate the cycles.

    • The algorithm 'A' holds cycle iterators starting with each vertex, and output them in increasing length order.

    • The algorithm 'B' holds cycle iterators starting with each edge, and output them in increasing length order.

OUTPUT: list

Note

Although the number of simple cycles of a finite graph is always finite, computing all its cycles may take a very long time.

EXAMPLES:

sage: g = DiGraph({'a': ['a', 'b'], 'b': ['c'], 'c': ['d'], 'd': ['c']}, loops=True)
sage: g.all_simple_cycles()
[['a', 'a'], ['c', 'd', 'c']]
>>> from sage.all import *
>>> g = DiGraph({'a': ['a', 'b'], 'b': ['c'], 'c': ['d'], 'd': ['c']}, loops=True)
>>> g.all_simple_cycles()
[['a', 'a'], ['c', 'd', 'c']]

The directed version of the Petersen graph:

sage: g = graphs.PetersenGraph().to_directed()
sage: g.all_simple_cycles(max_length=4)
[[0, 1, 0], [0, 4, 0], [0, 5, 0], [1, 2, 1], [1, 6, 1], [2, 3, 2],
 [2, 7, 2], [3, 4, 3], [3, 8, 3], [4, 9, 4], [5, 7, 5], [5, 8, 5],
 [6, 8, 6], [6, 9, 6], [7, 9, 7]]
sage: g.all_simple_cycles(max_length=6)
[[0, 1, 0], [0, 4, 0], [0, 5, 0], [1, 2, 1], [1, 6, 1], [2, 3, 2],
 [2, 7, 2], [3, 4, 3], [3, 8, 3], [4, 9, 4], [5, 7, 5], [5, 8, 5],
 [6, 8, 6], [6, 9, 6], [7, 9, 7], [0, 1, 6, 8, 5, 0],
 [0, 1, 6, 9, 4, 0], [0, 1, 2, 7, 5, 0], [0, 1, 2, 3, 4, 0],
 [0, 4, 9, 7, 5, 0], [0, 4, 9, 6, 1, 0], [0, 4, 3, 8, 5, 0],
 [0, 4, 3, 2, 1, 0], [0, 5, 8, 6, 1, 0], [0, 5, 8, 3, 4, 0],
 [0, 5, 7, 2, 1, 0], [0, 5, 7, 9, 4, 0], [1, 2, 7, 9, 6, 1],
 [1, 2, 3, 8, 6, 1], [1, 6, 9, 7, 2, 1], [1, 6, 8, 3, 2, 1],
 [2, 3, 8, 5, 7, 2], [2, 3, 4, 9, 7, 2], [2, 7, 9, 4, 3, 2],
 [2, 7, 5, 8, 3, 2], [3, 4, 9, 6, 8, 3], [3, 8, 6, 9, 4, 3],
 [5, 7, 9, 6, 8, 5], [5, 8, 6, 9, 7, 5], [0, 1, 2, 3, 8, 5, 0],
 [0, 1, 2, 7, 9, 4, 0], [0, 1, 6, 9, 7, 5, 0],
 [0, 1, 6, 8, 3, 4, 0], [0, 4, 3, 2, 7, 5, 0],
 [0, 4, 3, 8, 6, 1, 0], [0, 4, 9, 6, 8, 5, 0],
 [0, 4, 9, 7, 2, 1, 0], [0, 5, 7, 9, 6, 1, 0],
 [0, 5, 7, 2, 3, 4, 0], [0, 5, 8, 3, 2, 1, 0],
 [0, 5, 8, 6, 9, 4, 0], [1, 2, 3, 4, 9, 6, 1],
 [1, 2, 7, 5, 8, 6, 1], [1, 6, 8, 5, 7, 2, 1],
 [1, 6, 9, 4, 3, 2, 1], [2, 3, 8, 6, 9, 7, 2],
 [2, 7, 9, 6, 8, 3, 2], [3, 4, 9, 7, 5, 8, 3],
 [3, 8, 5, 7, 9, 4, 3]]
[Python]
>>> from sage.all import *
>>> g = graphs.PetersenGraph().to_directed()
>>> g.all_simple_cycles(max_length=Integer(4))
[[0, 1, 0], [0, 4, 0], [0, 5, 0], [1, 2, 1], [1, 6, 1], [2, 3, 2],
 [2, 7, 2], [3, 4, 3], [3, 8, 3], [4, 9, 4], [5, 7, 5], [5, 8, 5],
 [6, 8, 6], [6, 9, 6], [7, 9, 7]]
>>> g.all_simple_cycles(max_length=Integer(6))
[[0, 1, 0], [0, 4, 0], [0, 5, 0], [1, 2, 1], [1, 6, 1], [2, 3, 2],
 [2, 7, 2], [3, 4, 3], [3, 8, 3], [4, 9, 4], [5, 7, 5], [5, 8, 5],
 [6, 8, 6], [6, 9, 6], [7, 9, 7], [0, 1, 6, 8, 5, 0],
 [0, 1, 6, 9, 4, 0], [0, 1, 2, 7, 5, 0], [0, 1, 2, 3, 4, 0],
 [0, 4, 9, 7, 5, 0], [0, 4, 9, 6, 1, 0], [0, 4, 3, 8, 5, 0],
 [0, 4, 3, 2, 1, 0], [0, 5, 8, 6, 1, 0], [0, 5, 8, 3, 4, 0],
 [0, 5, 7, 2, 1, 0], [0, 5, 7, 9, 4, 0], [1, 2, 7, 9, 6, 1],
 [1, 2, 3, 8, 6, 1], [1, 6, 9, 7, 2, 1], [1, 6, 8, 3, 2, 1],
 [2, 3, 8, 5, 7, 2], [2, 3, 4, 9, 7, 2], [2, 7, 9, 4, 3, 2],
 [2, 7, 5, 8, 3, 2], [3, 4, 9, 6, 8, 3], [3, 8, 6, 9, 4, 3],
 [5, 7, 9, 6, 8, 5], [5, 8, 6, 9, 7, 5], [0, 1, 2, 3, 8, 5, 0],
 [0, 1, 2, 7, 9, 4, 0], [0, 1, 6, 9, 7, 5, 0],
 [0, 1, 6, 8, 3, 4, 0], [0, 4, 3, 2, 7, 5, 0],
 [0, 4, 3, 8, 6, 1, 0], [0, 4, 9, 6, 8, 5, 0],
 [0, 4, 9, 7, 2, 1, 0], [0, 5, 7, 9, 6, 1, 0],
 [0, 5, 7, 2, 3, 4, 0], [0, 5, 8, 3, 2, 1, 0],
 [0, 5, 8, 6, 9, 4, 0], [1, 2, 3, 4, 9, 6, 1],
 [1, 2, 7, 5, 8, 6, 1], [1, 6, 8, 5, 7, 2, 1],
 [1, 6, 9, 4, 3, 2, 1], [2, 3, 8, 6, 9, 7, 2],
 [2, 7, 9, 6, 8, 3, 2], [3, 4, 9, 7, 5, 8, 3],
 [3, 8, 5, 7, 9, 4, 3]]

The complete graph (without loops) on \(4\) vertices:

sage: g = graphs.CompleteGraph(4).to_directed()
sage: g.all_simple_cycles()
[[0, 1, 0], [0, 2, 0], [0, 3, 0], [1, 2, 1], [1, 3, 1], [2, 3, 2],
 [0, 1, 3, 0], [0, 1, 2, 0], [0, 2, 3, 0], [0, 2, 1, 0],
 [0, 3, 2, 0], [0, 3, 1, 0], [1, 2, 3, 1], [1, 3, 2, 1],
 [0, 1, 2, 3, 0], [0, 1, 3, 2, 0], [0, 2, 1, 3, 0],
 [0, 2, 3, 1, 0], [0, 3, 1, 2, 0], [0, 3, 2, 1, 0]]
>>> from sage.all import *
>>> g = graphs.CompleteGraph(Integer(4)).to_directed()
>>> g.all_simple_cycles()
[[0, 1, 0], [0, 2, 0], [0, 3, 0], [1, 2, 1], [1, 3, 1], [2, 3, 2],
 [0, 1, 3, 0], [0, 1, 2, 0], [0, 2, 3, 0], [0, 2, 1, 0],
 [0, 3, 2, 0], [0, 3, 1, 0], [1, 2, 3, 1], [1, 3, 2, 1],
 [0, 1, 2, 3, 0], [0, 1, 3, 2, 0], [0, 2, 1, 3, 0],
 [0, 2, 3, 1, 0], [0, 3, 1, 2, 0], [0, 3, 2, 1, 0]]

If the graph contains a large number of cycles, one can bound the length of the cycles, or simply restrict the possible starting vertices of the cycles:

sage: g = graphs.CompleteGraph(20).to_directed()
sage: g.all_simple_cycles(max_length=2)
[[0, 1, 0], [0, 2, 0], [0, 3, 0], [0, 4, 0], [0, 5, 0], [0, 6, 0], [0, 7, 0],
 [0, 8, 0], [0, 9, 0], [0, 10, 0], [0, 11, 0], [0, 12, 0], [0, 13, 0], [0, 14, 0],
 [0, 15, 0], [0, 16, 0], [0, 17, 0], [0, 18, 0], [0, 19, 0], [1, 2, 1], [1, 3, 1],
 [1, 4, 1], [1, 5, 1], [1, 6, 1], [1, 7, 1], [1, 8, 1], [1, 9, 1], [2, 3, 2],
 [2, 4, 2], [2, 5, 2], [2, 6, 2], [2, 7, 2], [2, 8, 2], [2, 9, 2], [3, 4, 3],
 [3, 5, 3], [3, 6, 3], [3, 7, 3], [3, 8, 3], [3, 9, 3], [4, 5, 4], [4, 6, 4],
 [4, 7, 4], [4, 8, 4], [4, 9, 4], [5, 6, 5], [5, 7, 5], [5, 8, 5], [5, 9, 5],
 [6, 7, 6], [6, 8, 6], [6, 9, 6], [7, 8, 7], [7, 9, 7], [8, 9, 8], [10, 1, 10],
 [10, 2, 10], [10, 3, 10], [10, 4, 10], [10, 5, 10], [10, 6, 10], [10, 7, 10],
 [10, 8, 10], [10, 9, 10], [11, 1, 11], [11, 2, 11], [11, 3, 11], [11, 4, 11],
 [11, 5, 11], [11, 6, 11], [11, 7, 11], [11, 8, 11], [11, 9, 11], [11, 10, 11],
 [12, 1, 12], [12, 2, 12], [12, 3, 12], [12, 4, 12], [12, 5, 12], [12, 6, 12],
 [12, 7, 12], [12, 8, 12], [12, 9, 12], [12, 10, 12], [12, 11, 12], [13, 1, 13],
 [13, 2, 13], [13, 3, 13], [13, 4, 13], [13, 5, 13], [13, 6, 13], [13, 7, 13],
 [13, 8, 13], [13, 9, 13], [13, 10, 13], [13, 11, 13], [13, 12, 13], [14, 1, 14],
 [14, 2, 14], [14, 3, 14], [14, 4, 14], [14, 5, 14], [14, 6, 14], [14, 7, 14],
 [14, 8, 14], [14, 9, 14], [14, 10, 14], [14, 11, 14], [14, 12, 14], [14, 13, 14],
 [15, 1, 15], [15, 2, 15], [15, 3, 15], [15, 4, 15], [15, 5, 15], [15, 6, 15],
 [15, 7, 15], [15, 8, 15], [15, 9, 15], [15, 10, 15], [15, 11, 15], [15, 12, 15],
 [15, 13, 15], [15, 14, 15], [16, 1, 16], [16, 2, 16], [16, 3, 16], [16, 4, 16],
 [16, 5, 16], [16, 6, 16], [16, 7, 16], [16, 8, 16], [16, 9, 16], [16, 10, 16],
 [16, 11, 16], [16, 12, 16], [16, 13, 16], [16, 14, 16], [16, 15, 16], [17, 1, 17],
 [17, 2, 17], [17, 3, 17], [17, 4, 17], [17, 5, 17], [17, 6, 17], [17, 7, 17],
 [17, 8, 17], [17, 9, 17], [17, 10, 17], [17, 11, 17], [17, 12, 17], [17, 13, 17],
 [17, 14, 17], [17, 15, 17], [17, 16, 17], [18, 1, 18], [18, 2, 18], [18, 3, 18],
 [18, 4, 18], [18, 5, 18], [18, 6, 18], [18, 7, 18], [18, 8, 18], [18, 9, 18],
 [18, 10, 18], [18, 11, 18], [18, 12, 18], [18, 13, 18], [18, 14, 18], [18, 15, 18],
 [18, 16, 18], [18, 17, 18], [19, 1, 19], [19, 2, 19], [19, 3, 19], [19, 4, 19],
 [19, 5, 19], [19, 6, 19], [19, 7, 19], [19, 8, 19], [19, 9, 19], [19, 10, 19],
 [19, 11, 19], [19, 12, 19], [19, 13, 19], [19, 14, 19], [19, 15, 19], [19, 16, 19],
 [19, 17, 19], [19, 18, 19]]

sage: g = graphs.CompleteGraph(20).to_directed()
sage: g.all_simple_cycles(max_length=2, starting_vertices=[0], algorithm='A')
[[0, 1, 0], [0, 2, 0], [0, 3, 0], [0, 4, 0], [0, 5, 0],
 [0, 6, 0], [0, 7, 0], [0, 8, 0], [0, 9, 0], [0, 10, 0],
 [0, 11, 0], [0, 12, 0], [0, 13, 0], [0, 14, 0], [0, 15, 0],
 [0, 16, 0], [0, 17, 0], [0, 18, 0], [0, 19, 0]]
[Python]
>>> from sage.all import *
>>> g = graphs.CompleteGraph(Integer(20)).to_directed()
>>> g.all_simple_cycles(max_length=Integer(2))
[[0, 1, 0], [0, 2, 0], [0, 3, 0], [0, 4, 0], [0, 5, 0], [0, 6, 0], [0, 7, 0],
 [0, 8, 0], [0, 9, 0], [0, 10, 0], [0, 11, 0], [0, 12, 0], [0, 13, 0], [0, 14, 0],
 [0, 15, 0], [0, 16, 0], [0, 17, 0], [0, 18, 0], [0, 19, 0], [1, 2, 1], [1, 3, 1],
 [1, 4, 1], [1, 5, 1], [1, 6, 1], [1, 7, 1], [1, 8, 1], [1, 9, 1], [2, 3, 2],
 [2, 4, 2], [2, 5, 2], [2, 6, 2], [2, 7, 2], [2, 8, 2], [2, 9, 2], [3, 4, 3],
 [3, 5, 3], [3, 6, 3], [3, 7, 3], [3, 8, 3], [3, 9, 3], [4, 5, 4], [4, 6, 4],
 [4, 7, 4], [4, 8, 4], [4, 9, 4], [5, 6, 5], [5, 7, 5], [5, 8, 5], [5, 9, 5],
 [6, 7, 6], [6, 8, 6], [6, 9, 6], [7, 8, 7], [7, 9, 7], [8, 9, 8], [10, 1, 10],
 [10, 2, 10], [10, 3, 10], [10, 4, 10], [10, 5, 10], [10, 6, 10], [10, 7, 10],
 [10, 8, 10], [10, 9, 10], [11, 1, 11], [11, 2, 11], [11, 3, 11], [11, 4, 11],
 [11, 5, 11], [11, 6, 11], [11, 7, 11], [11, 8, 11], [11, 9, 11], [11, 10, 11],
 [12, 1, 12], [12, 2, 12], [12, 3, 12], [12, 4, 12], [12, 5, 12], [12, 6, 12],
 [12, 7, 12], [12, 8, 12], [12, 9, 12], [12, 10, 12], [12, 11, 12], [13, 1, 13],
 [13, 2, 13], [13, 3, 13], [13, 4, 13], [13, 5, 13], [13, 6, 13], [13, 7, 13],
 [13, 8, 13], [13, 9, 13], [13, 10, 13], [13, 11, 13], [13, 12, 13], [14, 1, 14],
 [14, 2, 14], [14, 3, 14], [14, 4, 14], [14, 5, 14], [14, 6, 14], [14, 7, 14],
 [14, 8, 14], [14, 9, 14], [14, 10, 14], [14, 11, 14], [14, 12, 14], [14, 13, 14],
 [15, 1, 15], [15, 2, 15], [15, 3, 15], [15, 4, 15], [15, 5, 15], [15, 6, 15],
 [15, 7, 15], [15, 8, 15], [15, 9, 15], [15, 10, 15], [15, 11, 15], [15, 12, 15],
 [15, 13, 15], [15, 14, 15], [16, 1, 16], [16, 2, 16], [16, 3, 16], [16, 4, 16],
 [16, 5, 16], [16, 6, 16], [16, 7, 16], [16, 8, 16], [16, 9, 16], [16, 10, 16],
 [16, 11, 16], [16, 12, 16], [16, 13, 16], [16, 14, 16], [16, 15, 16], [17, 1, 17],
 [17, 2, 17], [17, 3, 17], [17, 4, 17], [17, 5, 17], [17, 6, 17], [17, 7, 17],
 [17, 8, 17], [17, 9, 17], [17, 10, 17], [17, 11, 17], [17, 12, 17], [17, 13, 17],
 [17, 14, 17], [17, 15, 17], [17, 16, 17], [18, 1, 18], [18, 2, 18], [18, 3, 18],
 [18, 4, 18], [18, 5, 18], [18, 6, 18], [18, 7, 18], [18, 8, 18], [18, 9, 18],
 [18, 10, 18], [18, 11, 18], [18, 12, 18], [18, 13, 18], [18, 14, 18], [18, 15, 18],
 [18, 16, 18], [18, 17, 18], [19, 1, 19], [19, 2, 19], [19, 3, 19], [19, 4, 19],
 [19, 5, 19], [19, 6, 19], [19, 7, 19], [19, 8, 19], [19, 9, 19], [19, 10, 19],
 [19, 11, 19], [19, 12, 19], [19, 13, 19], [19, 14, 19], [19, 15, 19], [19, 16, 19],
 [19, 17, 19], [19, 18, 19]]

>>> g = graphs.CompleteGraph(Integer(20)).to_directed()
>>> g.all_simple_cycles(max_length=Integer(2), starting_vertices=[Integer(0)], algorithm='A')
[[0, 1, 0], [0, 2, 0], [0, 3, 0], [0, 4, 0], [0, 5, 0],
 [0, 6, 0], [0, 7, 0], [0, 8, 0], [0, 9, 0], [0, 10, 0],
 [0, 11, 0], [0, 12, 0], [0, 13, 0], [0, 14, 0], [0, 15, 0],
 [0, 16, 0], [0, 17, 0], [0, 18, 0], [0, 19, 0]]

One may prefer to distinguish equivalent cycles having distinct starting vertices (compare the following examples):

sage: g = graphs.CompleteGraph(4).to_directed()
sage: g.all_simple_cycles(max_length=2, rooted=False, algorithm='A')
[[0, 1, 0], [0, 2, 0], [0, 3, 0], [1, 2, 1], [1, 3, 1], [2, 3, 2]]
sage: g.all_simple_cycles(max_length=2, rooted=True, algorithm='A')
[[0, 1, 0], [0, 2, 0], [0, 3, 0], [1, 0, 1], [1, 2, 1], [1, 3, 1],
 [2, 0, 2], [2, 1, 2], [2, 3, 2], [3, 0, 3], [3, 1, 3], [3, 2, 3]]
>>> from sage.all import *
>>> g = graphs.CompleteGraph(Integer(4)).to_directed()
>>> g.all_simple_cycles(max_length=Integer(2), rooted=False, algorithm='A')
[[0, 1, 0], [0, 2, 0], [0, 3, 0], [1, 2, 1], [1, 3, 1], [2, 3, 2]]
>>> g.all_simple_cycles(max_length=Integer(2), rooted=True, algorithm='A')
[[0, 1, 0], [0, 2, 0], [0, 3, 0], [1, 0, 1], [1, 2, 1], [1, 3, 1],
 [2, 0, 2], [2, 1, 2], [2, 3, 2], [3, 0, 3], [3, 1, 3], [3, 2, 3]]

A cycle is enumerated in increasing length order for a weighted graph:

sage: cycles = g.all_simple_cycles(weight_function=lambda e:e[0]+e[1],
....:                              by_weight=True, report_weight=True)
sage: cycles
[(2.0, [0, 1, 0]), (4.0, [0, 2, 0]), (6.0, [0, 1, 2, 0]), (6.0, [0, 2, 1, 0]),
 (6.0, [0, 3, 0]), (6.0, [1, 2, 1]), (8.0, [0, 1, 3, 0]), (8.0, [0, 3, 1, 0]),
 (8.0, [1, 3, 1]), (10.0, [0, 2, 3, 0]), (10.0, [0, 3, 2, 0]), (10.0, [2, 3, 2]),
 (12.0, [0, 1, 3, 2, 0]), (12.0, [0, 1, 2, 3, 0]), (12.0, [0, 2, 3, 1, 0]),
 (12.0, [0, 2, 1, 3, 0]), (12.0, [0, 3, 2, 1, 0]), (12.0, [0, 3, 1, 2, 0]),
 (12.0, [1, 2, 3, 1]), (12.0, [1, 3, 2, 1])]
[Python]
>>> from sage.all import *
>>> cycles = g.all_simple_cycles(weight_function=lambda e:e[Integer(0)]+e[Integer(1)],
...                              by_weight=True, report_weight=True)
>>> cycles
[(2.0, [0, 1, 0]), (4.0, [0, 2, 0]), (6.0, [0, 1, 2, 0]), (6.0, [0, 2, 1, 0]),
 (6.0, [0, 3, 0]), (6.0, [1, 2, 1]), (8.0, [0, 1, 3, 0]), (8.0, [0, 3, 1, 0]),
 (8.0, [1, 3, 1]), (10.0, [0, 2, 3, 0]), (10.0, [0, 3, 2, 0]), (10.0, [2, 3, 2]),
 (12.0, [0, 1, 3, 2, 0]), (12.0, [0, 1, 2, 3, 0]), (12.0, [0, 2, 3, 1, 0]),
 (12.0, [0, 2, 1, 3, 0]), (12.0, [0, 3, 2, 1, 0]), (12.0, [0, 3, 1, 2, 0]),
 (12.0, [1, 2, 3, 1]), (12.0, [1, 3, 2, 1])]

The algorithm 'B' can be used:

sage: cycles_B = g.all_simple_cycles(weight_function=lambda e:e[0]+e[1], by_weight=True,
....:                                report_weight=True, algorithm='B')
sage: cycles_B
[(2.0, [0, 1, 0]), (4.0, [0, 2, 0]), (6.0, [0, 1, 2, 0]), (6.0, [0, 2, 1, 0]),
 (6.0, [0, 3, 0]), (6.0, [1, 2, 1]), (8.0, [0, 1, 3, 0]), (8.0, [0, 3, 1, 0]),
 (8.0, [1, 3, 1]), (10.0, [0, 2, 3, 0]), (10.0, [0, 3, 2, 0]), (10.0, [2, 3, 2]),
 (12.0, [0, 1, 3, 2, 0]), (12.0, [0, 1, 2, 3, 0]), (12.0, [0, 2, 3, 1, 0]),
 (12.0, [0, 2, 1, 3, 0]), (12.0, [0, 3, 2, 1, 0]), (12.0, [0, 3, 1, 2, 0]),
 (12.0, [1, 2, 3, 1]), (12.0, [1, 3, 2, 1])]
sage: cycles.sort() == cycles_B.sort()
True
>>> from sage.all import *
>>> cycles_B = g.all_simple_cycles(weight_function=lambda e:e[Integer(0)]+e[Integer(1)], by_weight=True,
...                                report_weight=True, algorithm='B')
>>> cycles_B
[(2.0, [0, 1, 0]), (4.0, [0, 2, 0]), (6.0, [0, 1, 2, 0]), (6.0, [0, 2, 1, 0]),
 (6.0, [0, 3, 0]), (6.0, [1, 2, 1]), (8.0, [0, 1, 3, 0]), (8.0, [0, 3, 1, 0]),
 (8.0, [1, 3, 1]), (10.0, [0, 2, 3, 0]), (10.0, [0, 3, 2, 0]), (10.0, [2, 3, 2]),
 (12.0, [0, 1, 3, 2, 0]), (12.0, [0, 1, 2, 3, 0]), (12.0, [0, 2, 3, 1, 0]),
 (12.0, [0, 2, 1, 3, 0]), (12.0, [0, 3, 2, 1, 0]), (12.0, [0, 3, 1, 2, 0]),
 (12.0, [1, 2, 3, 1]), (12.0, [1, 3, 2, 1])]
>>> cycles.sort() == cycles_B.sort()
True

The algorithm 'A' is available for undirected graphs. Specifically, each cycle is enumerated exactly once, meaning a cycle and its reverse are not listed separately:

sage: g = Graph({0: [1, 2], 1: [0, 2], 2: [0, 1]})
sage: g.all_simple_cycles(algorithm='A')
[[0, 1, 2, 0]]
[Python]
>>> from sage.all import *
>>> g = Graph({Integer(0): [Integer(1), Integer(2)], Integer(1): [Integer(0), Integer(2)], Integer(2): [Integer(0), Integer(1)]})
>>> g.all_simple_cycles(algorithm='A')
[[0, 1, 2, 0]]