Rows

For ease of notation, we use the idea that a row is an individual permutation of bells (such as 13572468), and a change is a means of getting from one row to another, by swapping pairs of bells; the most convenient way to write a change is a place notation (for example X or 1258).

Row Operations

Rows have a set of operations which we can use on them; in mathematical terms, they form a group. The most important operation is that of row multiplication or transposition - in this operation, the bells in one row are rearranged according to the order given by another row.

For example:

21345678 × 13572468 = 23571468

and:

13572468 × 21345678 = 31572468

Note that the two above examples do not give the same result; that is, the order in which two things are multiplied does matter.

As an example of how row multiplication is used, suppose that we want to know the 4th row of the lead of Plain Bob Major with lead head 17856342. The 4th row of the first lead (which has a lead head of rounds) is 42618375. Multiplying these together gives us the answer we are looking for, namely 57312846.

The identity for this operation is rounds; in other words, any row multiplied by rounds gives itself, and rounds multiplied by any row gives that row.

It is possible to define the inverse of a row as the row which, when multiplied by that row, will give rounds. For example, the inverse of 13572468 is 15263748, as:

13572468 × 15263748 = 12345678

The opposite of row multiplication is row division. If a × b = c, we can define c / b = a. Using the same example as above, suppose we have a lead of Plain Bob Major and we know that the fourth row is 57312846, and we wish to find the lead head. Just divide by the fourth row of the plain course (42618375) to get the answer.

Row Properties

There are several properties of rows which arise from group theory and can be useful in looking at properties of methods.

The order of a row is the number of times which that row has to be multiplied by itself before it gets back to rounds. For example, the row 21436587 has order 2, because if it is multiplied by itself twice, you get back to rounds. Similarly the row 23145678 has order 3, and the row 23456781 has order 8. This can be useful, for example, in seeing how many leads of a method are needed in a plain course before it comes round.

Another useful concept is the sign or parity of a row. A row is considered even if it takes an even number of swaps of pairs of bells to get from rounds to that row, and odd if it takes an odd number of swaps. (It can be shown that whether the number is odd or even doesn’t depend on exactly what the sequence of swaps is).

Finally, every row can be expressed as a set of cycles. A cycle is a set of bells which move round in a sequential way as the row is repeated; for example, 21345678 has only one cycle, which is (12); and 12356478 has one cycle, which is (456). Combining these two cycles will give us the row 213564678.

The Row Class

class ringing.Row([spec])

Constructs a Row.

Rows may be constructed in a variety of ways:

>>> from ringing import Row
>>> Row(4)  # from an integer number of bells
Row('1234')
>>> Row('1234')  # from a string containing the row
Row('1234')
>>> Row(Row('1234'))  # from another row
Row('1234')

Rows are immutable once created.

This library makes extensive use of Row objects as function parameters. These may be passed in the same ways:

  • as a row

  • as a string containing the row

  • as an integer number of bells

If a row parameter can’t be read then a TypeError or ValueError exception will be raised as most appropriate.

Parameters:

spec (Row or int or string) –

Specification for constructing the row. This might be:

  • Nothing. Constructs a row on zero bells.

  • Another row. Constructs a copy.

  • An integer number of bells. Constructs rounds.

  • A string (unicode or bytes) representation of a row.

__lt__(row)
__le__(row)
__eq__(row)
__ne__(row)
__gt__(row)
__ge__(row)

Compare a row to another:

>>> from ringing import Row
>>> Row(4) == '1234'
True
Parameters:

row (Row or int or string) – value to compare

Returns:

result

Return type:

boolean

__getitem__(i)

This returns the ith bell in the row:

>>> from ringing import Row
>>> r = Row('512364')
>>> r[0]
Bell(4)
>>> r[1].to_char()
'1'
>>> print([b for b in r])
[Bell(4), Bell(0), Bell(1), Bell(2), Bell(5), Bell(3)]

Note

Note that this is not an lvalue, so you cannot assign a value to an individual bell in a row.

Parameters:

i (int) – bell position to return (0-indexed)

Returns:

bell number in that position (0-indexed; 0 is the treble)

Return type:

Bell

__mul__(row)

Multiplies two rows together as explained above:

>>> from ringing import Row
>>> r = Row('21345678')
>>> r * '13572468'
Row('23571468')
>>> '13572468' * r
Row('31572468')

If the rows are not of the same length, the shorter row is considered to be first padded out to the length of the longer row by adding the extra bells in order at the end.

Parameters:

row (Row or int or string) – value to multiply by

Returns:

result

Return type:

Row

__mul__(change)

Applies a change to a row:

>>> from ringing import Row, Change
>>> Row('214365') * Change(6, '1')
Row('241635')

If the number of bells c differs from the number of bells in r, then c is considered to be padded or truncated in the obvious way.

Parameters:

change (Change) – change to apply

Returns:

result

Return type:

Row

__div__(row)

Divides two rows, as explained above:

>>> from ringing import Row
>>> Row('23571468') / Row('13572468')
Row('21345678')

If the rows are not of the same length, the shorter row is considered to be first padded out to the length of the longer row by adding the extra bells in order at the end.

Parameters:

row (Row or int or string) – value to divide by

Returns:

result

Return type:

Row

__invert__()
inverse()

Returns the inverse of a row:

>>> from ringing import Row
>>> r = Row('13572468')
>>> ~r
Row('15263748')
>>> r.inverse()
Row('15263748')
>>> r * ~r
Row('12345678')
>>> ~r * r
Row('12345678')
Returns:

the row’s inverse

Return type:

Row

__pow__(n)

Returns the nth power of a row:

>>> from ringing import Row
>>> r = Row('13572468')
>>> r ** 0
Row('12345678')
>>> r ** 1
Row('13572468')
>>> r ** 2
Row('15263748')
>>> r ** 3
Row('12345678')
Parameters:

n (int) – power to which the row should be raised

Returns:

result

Return type:

Row

bells

Number of bells which the row contains:

>>> from ringing import Row
>>> Row('2143').bells
4
static rounds(n)
static queens(n)
static kings(n)
static tittums(n)
static reverse_rounds(n)

Return the row corresponding to rounds, queens, kings, tittums and reverse rounds respectively on n bells.

Parameters:

n (int) – number of bells

Returns:

the computed row

Return type:

Row

static pblh(n[, h=1])

Returns the first lead head of Plain Bob (h = 1), Grandsire (h = 2), or more generally the Plain Bob type method on n bells with h hunt bells:

>>> from ringing import Row
>>> Row.pblh(6)
Row('135264')
Parameters:
  • n (int) – number of bells

  • h (int) – number of hunt bells

Returns:

the computed row

Return type:

Row

static cyclic(n[, h=1][, c=1])

Returns a cyclic row on n bells with h initial fixed (hunt) bells. The variable c controls the number of bells moved from the front of the row to the end:

>>> from ringing import Row
>>> Row.cyclic(6, 1, 2)
Row('145623')
Parameters:
  • n (int) – number of bells

  • h (int) – number of hunt bells

  • c (int) – number of bells to move to the end of the row

Returns:

the computed row

Return type:

Row

is_rounds()

Determines whether the row is rounds:

>>> from ringing import Row
>>> Row('12345678').is_rounds()
True
>>> Row('72635184').is_rounds()
False
Returns:

True if the row is rounds, and False otherwise

Return type:

boolean

is_pblh([hunts=0])

If the row is a lead head of Plain Bob, Grandsire or, more generally, of the Plain Bob type method with any number of hunt bells, then this function returns an integer indicating which lead head it is. Otherwise, it returns False.

Parameters:

hunts (int) – number of hunt bells

Returns:

lead head number, or False if not a Plain Bob-type lead head

Return type:

int

sign()

Returns the sign or parity of a row:

>>> from ringing import Row
>>> Row('12345678').sign()
1
>>> Row('21345678').sign()
-1
Returns:

1 for even, -1 for odd

Rype:

int

cycles()

Expresses the row as separate cycles. The returned string will afterwards contain a list of all the cycles in the row, separated by commas:

>>> from ringing import Row
>>> Row('21453678').cycles()
'12,345,6,7,8'
Returns:

representation of the row as disjoint cycles

Return type:

string

order()

Returns the order of the row:

>>> from ringing import Row
>>> r = Row('21453678')
>>> r.order()
6
>>> (r ** 6).is_rounds()
True
Returns:

the row’s order

Return type:

int

find(b)

Locates the bell, b, in this row and returns its place:

>>> from ringing import Row
>>> r = Row('512364')
>>> r.find(0)
1
>>> r.find('2')
2

See also __getitem__().

Parameters:

b (Bell or string or int) – bell to find

Returns:

bell position (0-indexed)

Return type:

int

static conjugator(x, y)

Two rows, x and y, are conjugate if there exists some row, r, such that:

y = (r ^ -1) . x . r

or, in Python:

y = ~r * x * r

If x and y are conjugate, this method computes the row r that relates them.

Parameters:
  • x (Row or int or string) – row x

  • y (Row or int or string) – row y

Returns:

row r, or None if the rows are not conjugate

Return type:

Row or None

static are_conjugate(x, y)

Determines whether the rows x and y are conjugate as described above.

Parameters:
  • x (Row or int or string) – row x

  • y (Row or int or string) – row y

Returns:

True if the rows are conjugate, or False otherwise

Return type:

boolean

The Group Class

class ringing.Group(generator[, generator[, ...]])

Generates a group of rows.

Once created, groups can be tested for membership or iterated over using the in operator (and hence converted to lists via list()). Groups are immutable.

Parameters:

generator (Row or int or string) – generators for the group (rows)

__lt__(group)
__le__(group)
__eq__(group)
__ne__(group)
__gt__(group)
__ge__(group)

Compare a group to another.

Parameters:

group (Group) – value to compare

Returns:

result

Return type:

boolean

bells

Number of bells which the group’s rows contain:

>>> from ringing import Group
>>> Group('2143', '1324').bells
4
size

Number of rows which the group contains (the order of the group):

>>> from ringing import group
>>> Group('2143', '1324').size
8
static symmetric_group(working_bells[, hunt_bells[, total_bells]])
static alternating_group(working_bells[, hunt_bells[, total_bells]])

Returns the symmetric or alternating group respectively.

The symmetric group Sn contains all permutations on n bells, i.e. the extent. The alternating group An contains all even permutations on n bells (see Row.sign()).

By specifying hunt_bells and total_bells it’s possible to produce groups with more fixed bells, e.g.:

>>> from ringing import Group
>>> list(Group.alternating_group(3, 1, 8))
[Row('12345678'), Row('13425678'), Row('14235678')]
Parameters:
  • working_bells (int) – n, number of bells involved in permutations

  • hunt_bells (int) – number of fixed bells at the start of the change

  • total_bells (int) – number of bells in each row

Returns:

the computed group

Return type:

Group

Raises:

ValueError if total_bells is less than the sum of working_bells and hunt_bells

conjugate(r)

Conjugates the group by a row r, in pseudocode:

H = (r ^ -1) . g . r for g in G
Parameters:

r (Row or int or string) – row r

Returns:

the computed group

Return type:

Group

rcoset_label(r)
lcoset_label(r)

A subgroup H of Sn partitions the group into n! / |G| cosets.

  • gH is the left coset of H in G with respect to g

  • Hg is the right coset of H in G with respect to g

Each coset may be conveniently labelled by choosing the lexicographically least element within it.

These methods return the label of the right or left coset (respectively) of G in Sn with respect to the row r.

Parameters:

r (Row or int or string) – row r

Returns:

the computed label

Return type:

Row

invariants()

Returns a list of invariant bells as 0-indexed integers, e.g.:

>>> from ringing import Group
>>> Group.alternating_group(3, 1, 8).invariants()
[Bell(0), Bell(4), Bell(5), Bell(6), Bell(7)]
Returns:

invariant bells

Return type:

[Bell]