Module: sage.combinat.matrices.latin
Latin squares
A latin square of order
is an
array such that
each symbol
appears precisely once in each
row, and precisely once in each column. A partial latin square of
order
is an
array such that
each symbol
appears at most once in each
row, and at most once in each column. A latin square
is a
completion of a partial latin square
if
. If
completes to just
then
has unique completion.
A latin bitrade
is a pair of partial
latin squares such that:
Intuitively speaking, a bitrade gives the difference between two latin
squares, so if
is a bitrade
for the pair of latin squares
, then
and
.
This file contains
sage: from sage.combinat.matrices.latin import * sage: B = back_circulant(5) sage: print B [0 1 2 3 4] [1 2 3 4 0] [2 3 4 0 1] [3 4 0 1 2] [4 0 1 2 3] sage: B.is_latin_square() True sage: B[0, 1] = 0 sage: B.is_latin_square() False
sage: (a, b, c, G) = alternating_group_bitrade_generators(1) sage: (T1, T2) = bitrade_from_group(a, b, c, G) sage: print T1 [ 0 -1 3 1] [-1 1 0 2] [ 1 3 2 -1] [ 2 0 -1 3] sage: print T2 [ 1 -1 0 3] [-1 0 2 1] [ 2 1 3 -1] [ 0 3 -1 2] sage: print T1.nr_filled_cells() 12 sage: print genus(T1, T2) 1
To do:
Author: - Carlo Hamalainen (2008-03-23): initial version
TESTS:
sage: L = elementary_abelian_2group(3) sage: L == loads(dumps(L)) True
Module-level Functions
| L_start, [check_assertions=False]) |
Generator for a sequence of uniformly distributed latin squares, given L_start as the initial latin square. This code implements the Markov chain algorithm of Jacobson and Matthews (1996), see below for the BibTex entry. This generator will never throw the StopIteration exception, so it provides an infinite sequence of latin squares.
Use the back circulant latin square of order 4 as the initial square and print the next two latin squares given by the Markov chain:
sage: from sage.combinat.matrices.latin import * sage: g = LatinSquare_generator(back_circulant(4)) sage: print g.next().is_latin_square() True
REFERENCE:
@articleMR1410617, Author: = Jacobson, Mark T. and Matthews, Peter, TITLE = Generating uniformly distributed random Latin squares, JOURNAL = J. Combin. Des., FJOURNAL = Journal of Combinatorial Designs, VOLUME = 4, YEAR = 1996, NUMBER = 6, PAGES = 405-437, ISSN = 1063-8539, MRCLASS = 05B15 (60J10), MRNUMBER = MR1410617 (98b:05021), MRREVIEWER = Lars Døvling Andersen,
| m) |
Construct generators a, b, c for the alternating group on 3m+1 points, such that a*b*c = 1.
sage: from sage.combinat.matrices.latin import * sage: a, b, c, G = alternating_group_bitrade_generators(1)
((1,2,3), (1,4,2), (2,4,3), Permutation Group with generators [(1,2,3), (1,4,2)])
sage: a*b*c ()
sage: (T1, T2) = bitrade_from_group(a, b, c, G) sage: print T1 [ 0 -1 3 1] [-1 1 0 2] [ 1 3 2 -1] [ 2 0 -1 3] sage: print T2 [ 1 -1 0 3] [-1 0 2 1] [ 2 1 3 -1] [ 0 3 -1 2]
| n) |
The back-circulant latin square of order n is the Cayley table for (Z_n, +), the integers under addition modulo n.
Input:
sage: from sage.combinat.matrices.latin import * sage: print back_circulant(5) [0 1 2 3 4] [1 2 3 4 0] [2 3 4 0 1] [3 4 0 1 2] [4 0 1 2 3]
| rce, T1, T2) |
Find the unique (x, c, e) in T2 such that (r, c, e) is in T1.
Input:
sage: from sage.combinat.matrices.latin import * sage: T1 = back_circulant(5) sage: x = isotopism( (0,1,2,3,4) ) sage: y = isotopism(5) # identity sage: z = isotopism(5) # identity sage: T2 = T1.apply_isotopism(x, y, z) sage: print is_bitrade(T1, T2) True sage: print beta1([0, 0, 0], T1, T2) (1, 0, 0)
| rce, T1, T2) |
Find the unique (r, x, e) in T2 such that (r, c, e) is in T1.
Input:
sage: from sage.combinat.matrices.latin import * sage: T1 = back_circulant(5) sage: x = isotopism( (0,1,2,3,4) ) sage: y = isotopism(5) # identity sage: z = isotopism(5) # identity sage: T2 = T1.apply_isotopism(x, y, z) sage: print is_bitrade(T1, T2) True sage: print beta2([0, 0, 0], T1, T2) (0, 1, 0)
| rce, T1, T2) |
Find the unique (r, c, x) in T2 such that (r, c, e) is in T1.
Input:
sage: from sage.combinat.matrices.latin import * sage: T1 = back_circulant(5) sage: x = isotopism( (0,1,2,3,4) ) sage: y = isotopism(5) # identity sage: z = isotopism(5) # identity sage: T2 = T1.apply_isotopism(x, y, z) sage: print is_bitrade(T1, T2) True sage: print beta3([0, 0, 0], T1, T2) (0, 0, 4)
| T1, T2) |
Form the bitrade (Q1, Q2) from (T1, T2) by setting empty the cells (r, c) such that T1[r, c] == T2[r, c].
sage: from sage.combinat.matrices.latin import * sage: B1 = back_circulant(5) sage: alpha = isotopism((0,1,2,3,4)) sage: beta = isotopism((1,0,2,3,4)) sage: gamma = isotopism((2,1,0,3,4)) sage: B2 = B1.apply_isotopism(alpha, beta, gamma) sage: T1, T2 = bitrade(B1, B2) sage: print T1 [ 0 1 -1 3 4] [ 1 -1 -1 4 0] [ 2 -1 4 0 1] [ 3 4 0 1 2] [ 4 0 1 2 3] sage: print T2 [ 3 4 -1 0 1] [ 0 -1 -1 1 4] [ 1 -1 0 4 2] [ 4 0 1 2 3] [ 2 1 4 3 0]
| a, b, c, G) |
Given group elements a, b, c in G such that abc = 1 and the subgroups <a>, <b>, <c> intersect (pairwise) only in the identity, construct a bitrade (T1, T2) where rows, columns, and symbols correspond to cosets of <a>, <b>, and <c>, respectively.
sage: from sage.combinat.matrices.latin import * sage: a, b, c, G = alternating_group_bitrade_generators(1) sage: (T1, T2) = bitrade_from_group(a, b, c, G) sage: print T1 [ 0 -1 3 1] [-1 1 0 2] [ 1 3 2 -1] [ 2 0 -1 3] sage: print T2 [ 1 -1 0 3] [-1 0 2 1] [ 2 1 3 -1] [ 0 3 -1 2]
| cells_map, n) |
Returns a LatinSquare with cells numbered from 1, 2, ... to given the dictionary cells_map.
NOTE: The value n should be the maximum of the number of rows and columns of the original partial latin square
sage: from sage.combinat.matrices.latin import * sage: (a, b, c, G) = alternating_group_bitrade_generators(1) sage: (T1, T2) = bitrade_from_group(a, b, c, G) sage: print T1 [ 0 -1 3 1] [-1 1 0 2] [ 1 3 2 -1] [ 2 0 -1 3]
There are 12 filled cells in T:
sage: print cells_map_as_square(T1.filled_cells_map(), max(T1.nrows(), T1.ncols())) [ 1 -1 2 3] [-1 4 5 6] [ 7 8 9 -1] [10 11 -1 12]
| a, b, c) |
Three group elements a, b, c will generate a bitrade if a*b*c = 1 and the subgroups <a>, <b>, <c> intersect (pairwise) in just the identity.
sage: from sage.combinat.matrices.latin import * sage: a, b, c, G = p3_group_bitrade_generators(3) sage: print check_bitrade_generators(a, b, c) True sage: print check_bitrade_generators(a, b, G.identity()) False
| ) |
Simulates a fair coin (returns True or False) using ZZ.random_element(2).
sage: from sage.combinat.matrices.latin import * sage: x = coin() sage: x == 0 or x == 1 True
| L, r, x) |
Given an improper latin square L with L[r, c1] = L[r, c2] = x, return c1 or c2 with equal probability. This is an internal function and should only be used in LatinSquare_generator().
sage: from sage.combinat.matrices.latin import * sage: L = matrix([(1, 0, 2, 3), (0, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)]) sage: print L [1 0 2 3] [0 2 3 0] [2 3 0 1] [3 0 1 2] sage: c = column_containing_sym(L, 1, 0) sage: print c == 0 or c == 3 True
| x, G) |
For some group element x in G, return a list of the canonical coset representatives for the subgroup <x> in G.
sage: from sage.combinat.matrices.latin import * sage: a, b, c, G = alternating_group_bitrade_generators(1) sage: print coset_representatives(a, G) [(), (1,2)(3,4), (1,4)(2,3), (1,3)(2,4)]
| L1, L2, L3, L4) |
The 'direct product' of four latin squares L1, L2, L3, L4 of order n is the latin square of order 2n consisting of:
------ | L1 | L2 | ------ | L3 | L4 | ------
where the subsquares L2 and L3 have entries offset by n.
sage: from sage.combinat.matrices.latin import * sage: direct_product(back_circulant(4), back_circulant(4), elementary_abelian_2group(2), elementary_abelian_2group(2)) [0 1 2 3 4 5 6 7] [1 2 3 0 5 6 7 4] [2 3 0 1 6 7 4 5] [3 0 1 2 7 4 5 6] [4 5 6 7 0 1 2 3] [5 4 7 6 1 0 3 2] [6 7 4 5 2 3 0 1] [7 6 5 4 3 2 1 0]
| P, [nr_to_find=None]) |
Returns a list of all latin squares L of the same order as P such that P is contained in L. The optional parameter nr_to_find limits the number of latin squares that are found.
sage: from sage.combinat.matrices.latin import * sage: dlxcpp_find_completions(LatinSquare(2)) [[0 1] [1 0], [1 0] [0 1]]
sage: dlxcpp_find_completions(LatinSquare(2), 1) [[0 1] [1 0]]
| P) |
Internal function for dlxcpp_find_completions. Given a partial latin square P we construct a list of rows of a 0-1 matrix M such that an exact cover of M corresponds to a completion of P to a latin square.
sage: from sage.combinat.matrices.latin import *
sage: dlxcpp_rows_and_map(LatinSquare(2))
([[0, 4, 8], [1, 5, 8], [2, 4, 9], [3, 5, 9], [0, 6, 10], [1, 7, 10], [2,
6, 11], [3, 7, 11]], {(2, 4, 9): (0, 1, 0), (1, 5, 8): (0, 0, 1), (1, 7,
10): (1, 0, 1), (0, 6, 10): (1, 0, 0), (3, 7, 11): (1, 1, 1), (2, 6, 11):
(1, 1, 0), (0, 4, 8): (0, 0, 0), (3, 5, 9): (0, 1, 1)})
| s) |
Returns the latin square based on the Cayley table for the elementary abelian 2-group of order 2s.
Input:
sage: from sage.combinat.matrices.latin import * sage: print elementary_abelian_2group(3) [0 1 2 3 4 5 6 7] [1 0 3 2 5 4 7 6] [2 3 0 1 6 7 4 5] [3 2 1 0 7 6 5 4] [4 5 6 7 0 1 2 3] [5 4 7 6 1 0 3 2] [6 7 4 5 2 3 0 1] [7 6 5 4 3 2 1 0]
| eLabels, C, x) |
ARGUMENTS: eLabels -- list of coset representatives of C in G C -- subgroup G of G x -- element of G
RETURNS: integer i such that C*eLabels[i] == C*x
sage: from sage.combinat.matrices.latin import * sage: a, b, c, G = alternating_group_bitrade_generators(1) sage: print entry_label(coset_representatives(c, G), gap.Group([c]), a) 1
| n) |
The forward-circulant latin square of order n is the Cayley table for the operation r + c = (n-c+r) mod n.
Input:
sage: from sage.combinat.matrices.latin import * sage: print forward_circulant(5) [0 4 3 2 1] [1 0 4 3 2] [2 1 0 4 3] [3 2 1 0 4] [4 3 2 1 0]
| T1, T2) |
Returns the genus of hypermap embedding associated with the bitrade (T1, T2). Informally, we compute the [tau_1, tau_2, tau_3] permutation representation of the bitrade. Each cycle of tau_1, tau_2, and tau_3 gives a rotation scheme for a black, white, and star vertex (respectively). The genus then comes from Euler's formula. For more details see Carlo Hamalainen: Partitioning 3-homogeneous latin bitrades. To appear in Geometriae Dedicata, available at http://arxiv.org/abs/0710.0938
sage: from sage.combinat.matrices.latin import * sage: (a, b, c, G) = alternating_group_bitrade_generators(1) sage: (T1, T2) = bitrade_from_group(a, b, c, G) sage: print genus(T1, T2) 1 sage: (a, b, c, G) = pq_group_bitrade_generators(3, 7) sage: (T1, T2) = bitrade_from_group(a, b, c, G) sage: print genus(T1, T2) 3
| G) |
Construct a latin square on the symbols [0, 1, ..., n-1] for a group with an n by n Cayley table.
sage: from sage.combinat.matrices.latin import * sage: print group_to_LatinSquare(DihedralGroup(2)) [0 1 2 3] [1 0 3 2] [2 3 0 1] [3 2 1 0]
sage: G = gap.Group(PermutationGroupElement((1,2,3))) sage: print group_to_LatinSquare(G) [0 1 2] [1 2 0] [2 0 1]
| T1, T2) |
Combinatorially, a pair (T1, T2) of partial latin squares is a bitrade if they are disjoint, have the same shape, and have row and column balance. For definitions of each of these terms see the relevant function in this file.
sage: from sage.combinat.matrices.latin import * sage: T1 = back_circulant(5) sage: x = isotopism( (0,1,2,3,4) ) sage: y = isotopism(5) # identity sage: z = isotopism(5) # identity sage: T2 = T1.apply_isotopism(x, y, z) sage: print is_bitrade(T1, T2) True
| T1, T2) |
The partial latin squares T1 and T2 are disjoint if T1[r, c] != T2[r, c] or T1[r, c] == T2[r, c] == -1 for each cell [r, c].
sage: from sage.combinat.matrices.latin import is_disjoint, back_circulant, isotopism sage: is_disjoint(back_circulant(2), back_circulant(2)) False
sage: T1 = back_circulant(5) sage: x = isotopism( (0,1,2,3,4) ) sage: y = isotopism(5) # identity sage: z = isotopism(5) # identity sage: T2 = T1.apply_isotopism(x, y, z) sage: print is_disjoint(T1, T2) True
| a, b, c, G) |
A bitrade generated from elements a, b, c is primary if <a, b, c> = G.
sage: from sage.combinat.matrices.latin import * sage: (a, b, c, G) = p3_group_bitrade_generators(5) sage: print is_primary_bitrade(a, b, c, G) True
| T1, T2) |
Partial latin squares T1 and T2 are balanced if the symbols appearing in row r of T1 are the same as the symbols appearing in row r of T2, for each r, and if the same condition holds on columns.
sage: from sage.combinat.matrices.latin import * sage: T1 = matrix([[0,1,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1]]) sage: T2 = matrix([[0,1,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1]]) sage: is_row_and_col_balanced(T1, T2) True sage: T2 = matrix([[0,3,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1]]) sage: is_row_and_col_balanced(T1, T2) False
| T1, T2) |
Two partial latin squares T1, T2 have the same shape if T1[r, c] >= 0 if and only if T2[r, c] >= 0.
sage: from sage.combinat.matrices.latin import * sage: is_same_shape(elementary_abelian_2group(2), back_circulant(4)) True sage: is_same_shape(LatinSquare(5), LatinSquare(5)) True sage: is_same_shape(forward_circulant(5), LatinSquare(5)) False
| p) |
Returns a Permutation object that represents an isotopism (for rows, columns or symbols of a partial latin square). Since matrices in Sage are indexed from 0, this function translates +1 to agree with the Permutation class. We also handle PermutationGroupElements:
sage: from sage.combinat.matrices.latin import * sage: isotopism(5) # identity on 5 points [1, 2, 3, 4, 5]
sage: G = PermutationGroup(['(1,2,3)(4,5)']) sage: g = G.gen(0) sage: isotopism(g) [2, 3, 1, 5, 4]
sage: isotopism([0,3,2,1]) # 0 goes to 0, 1 goes to 3, etc. [1, 4, 3, 2]
sage: isotopism( (0,1,2) ) # single cycle, presented as a tuple [2, 3, 1]
sage: x = isotopism( ((0,1,2), (3,4)) ) # tuple of cycles sage: print x [2, 3, 1, 5, 4] sage: x.to_cycles() [(1, 2, 3), (4, 5)]
| L) |
Permute L[r, c] = e to the conjugate L[c, e] = r
We assume that L is an n by n matrix and has values in the range 0, 1, ..., n-1.
sage: from sage.combinat.matrices.latin import * sage: L = back_circulant(6) sage: print L [0 1 2 3 4 5] [1 2 3 4 5 0] [2 3 4 5 0 1] [3 4 5 0 1 2] [4 5 0 1 2 3] [5 0 1 2 3 4] sage: print next_conjugate(L) [0 1 2 3 4 5] [5 0 1 2 3 4] [4 5 0 1 2 3] [3 4 5 0 1 2] [2 3 4 5 0 1] [1 2 3 4 5 0] sage: L == next_conjugate(next_conjugate(next_conjugate(L))) True
| p) |
Generators for a group of order p3 where p is a prime.
sage: from sage.combinat.matrices.latin import * sage: p3_group_bitrade_generators(3) ((1,2,3)(4,16,17)(5,20,21)(6,10,14)(7,11,15)(8,24,18)(9,26,23)(12,19,25)(13 ,22,27), (1,4,5)(2,8,9)(3,12,13)(6,18,22)(7,19,23)(10,25,20)(11,16,27)(14,17,26)(15 ,24,21), (1,21,8)(2,23,12)(3,27,4)(5,17,10)(6,13,25)(7,26,16)(9,18,14)(11,22,24)(15 ,20,19), Permutation Group with generators [(1,2,3)(4,16,17)(5,20,21)(6,10,14)(7,11,15)(8,24,18)(9,26,23)(12,19,25)(13 ,22,27), (1,4,5)(2,8,9)(3,12,13)(6,18,22)(7,19,23)(10,25,20)(11,16,27)(14,1 7,26)(15,24,21), (1,6,7)(2,10,11)(3,14,15)(4,18,19)(5,22,23)(8,25,16)(9,20, 27)(12,17,24)(13,26,21)])
| p, q) |
Generators for a group of order pq where p and q are primes such that (q
sage: from sage.combinat.matrices.latin import * sage: pq_group_bitrade_generators(3,7) ((2,3,5)(4,7,6), (1,2,3,4,5,6,7), (1,4,2)(3,5,6), Permutation Group with generators [(2,3,5)(4,7,6), (1,2,3,4,5,6,7)])
| L, c, x) |
Given an improper latin square L with L[r1, c] = L[r2, c] = x, return r1 or r2 with equal probability. This is an internal function and should only be used in LatinSquare_generator().
sage: from sage.combinat.matrices.latin import * sage: L = matrix([(0, 1, 0, 3), (3, 0, 2, 1), (1, 0, 3, 2), (2, 3, 1, 0)]) sage: print L [0 1 0 3] [3 0 2 1] [1 0 3 2] [2 3 1 0] sage: c = row_containing_sym(L, 1, 0) sage: print c == 1 or c == 2 True
| T1, T2, cells_map) |
The definition of tau1 is:
tau1 : T1 -> T1 tau1 = beta(-1)_2 beta_3 (composing left to right 000)
where
beta_i : T2 -> T1
changes just the i-th coordinate of a triple.
sage: from sage.combinat.matrices.latin import * sage: T1 = back_circulant(5) sage: x = isotopism( (0,1,2,3,4) ) sage: y = isotopism(5) # identity sage: z = isotopism(5) # identity sage: T2 = T1.apply_isotopism(x, y, z) sage: print is_bitrade(T1, T2) True sage: (cells_map, t1, t2, t3) = tau123(T1, T2) sage: t1 = tau1(T1, T2, cells_map) sage: print t1 [2, 3, 4, 5, 1, 7, 8, 9, 10, 6, 12, 13, 14, 15, 11, 17, 18, 19, 20, 16, 22, 23, 24, 25, 21] sage: print t1.to_cycles() [(1, 2, 3, 4, 5), (6, 7, 8, 9, 10), (11, 12, 13, 14, 15), (16, 17, 18, 19, 20), (21, 22, 23, 24, 25)]
| T1, T2) |
Compute the tau_i representation for a bitrade (T1, T2). See the functions tau1, tau2, and tau3 for the mathematical definitions.
RETURNS: (cells_map, t1, t2, t3)
where cells_map is a map to/from the filled cells of T1, and t1, t2, t3 are the tau1, tau2, tau3 permutations.
sage: from sage.combinat.matrices.latin import *
sage: (a, b, c, G) = pq_group_bitrade_generators(3, 7)
sage: (T1, T2) = bitrade_from_group(a, b, c, G)
sage: print T1
[0 6 4]
[1 0 5]
[2 1 6]
[3 2 0]
[4 3 1]
[5 4 2]
[6 5 3]
sage: print T2
[6 4 0]
[0 5 1]
[1 6 2]
[2 0 3]
[3 1 4]
[4 2 5]
[5 3 6]
sage: (cells_map, t1, t2, t3) = tau123(T1, T2)
sage: print cells_map
{1: (0, 0), 2: (0, 1), 3: (0, 2), 4: (1, 0), 5: (1, 1), 6: (1, 2), 7: (2,
0), 8: (2, 1), 9: (2, 2), 10: (3, 0), 11: (3, 1), 12: (3, 2), 13: (4, 0),
(2, 1): 8, 15: (4, 2), 16: (5, 0), 17: (5, 1), 18: (5, 2), 19: (6, 0), 20:
(6, 1), 21: (6, 2), (5, 1): 17, (4, 0): 13, (1, 2): 6, (3, 0): 10, (5, 0):
16, (2, 2): 9, (4, 1): 14, (1, 1): 5, (3, 2): 12, (0, 0): 1, (6, 0): 19,
14: (4, 1), (4, 2): 15, (1, 0): 4, (0, 1): 2, (6, 1): 20, (3, 1): 11, (2,
0): 7, (6, 2): 21, (5, 2): 18, (0, 2): 3}
sage: print cells_map_as_square(cells_map, max(T1.nrows(), T1.ncols()))
[ 1 2 3 -1 -1 -1 -1]
[ 4 5 6 -1 -1 -1 -1]
[ 7 8 9 -1 -1 -1 -1]
[10 11 12 -1 -1 -1 -1]
[13 14 15 -1 -1 -1 -1]
[16 17 18 -1 -1 -1 -1]
[19 20 21 -1 -1 -1 -1]
sage: t1
[3, 1, 2, 6, 4, 5, 9, 7, 8, 12, 10, 11, 15, 13, 14, 18, 16, 17, 21, 19, 20]
sage: t2
[19, 17, 12, 1, 20, 15, 4, 2, 18, 7, 5, 21, 10, 8, 3, 13, 11, 6, 16, 14, 9]
sage: print t3
[5, 9, 13, 8, 12, 16, 11, 15, 19, 14, 18, 1, 17, 21, 4, 20, 3, 7, 2, 6, 10]
sage: t1.to_cycles() [(1, 3, 2), (4, 6, 5), (7, 9, 8), (10, 12, 11), (13, 15, 14), (16, 18, 17), (19, 21, 20)] sage: t2.to_cycles() [(1, 19, 16, 13, 10, 7, 4), (2, 17, 11, 5, 20, 14, 8), (3, 12, 21, 9, 18, 6, 15)] sage: t3.to_cycles() [(1, 5, 12), (2, 9, 19), (3, 13, 17), (4, 8, 15), (6, 16, 20), (7, 11, 18), (10, 14, 21)]
The product t1*t2*t3 is the identity, i.e. it fixes every point:
sage: len((t1*t2*t3).fixed_points()) == T1.nr_filled_cells() True
| T1, T2, cells_map) |
The definition of tau2 is:
tau2 : T1 -> T1 tau2 = beta(-1)_3 beta_1 (composing left to right)
where
beta_i : T2 -> T1
changes just the i-th coordinate of a triple.
sage: from sage.combinat.matrices.latin import * sage: T1 = back_circulant(5) sage: x = isotopism( (0,1,2,3,4) ) sage: y = isotopism(5) # identity sage: z = isotopism(5) # identity sage: T2 = T1.apply_isotopism(x, y, z) sage: print is_bitrade(T1, T2) True sage: (cells_map, t1, t2, t3) = tau123(T1, T2) sage: t2 = tau2(T1, T2, cells_map) sage: print t2 [21, 22, 23, 24, 25, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] sage: print t2.to_cycles() [(1, 21, 16, 11, 6), (2, 22, 17, 12, 7), (3, 23, 18, 13, 8), (4, 24, 19, 14, 9), (5, 25, 20, 15, 10)]
| T1, T2, cells_map) |
The definition of tau1 is:
tau1 : T1 -> T1 tau1 = beta(-1)_3 beta_1 (composing left to right)
where
beta_i : T2 -> T1
changes just the i-th coordinate of a triple.
sage: from sage.combinat.matrices.latin import * sage: T1 = back_circulant(5) sage: x = isotopism( (0,1,2,3,4) ) sage: y = isotopism(5) # identity sage: z = isotopism(5) # identity sage: T2 = T1.apply_isotopism(x, y, z) sage: print is_bitrade(T1, T2) True sage: (cells_map, t1, t2, t3) = tau123(T1, T2) sage: t3 = tau3(T1, T2, cells_map) sage: print t3 [10, 6, 7, 8, 9, 15, 11, 12, 13, 14, 20, 16, 17, 18, 19, 25, 21, 22, 23, 24, 5, 1, 2, 3, 4] sage: print t3.to_cycles() [(1, 10, 14, 18, 22), (2, 6, 15, 19, 23), (3, 7, 11, 20, 24), (4, 8, 12, 16, 25), (5, 9, 13, 17, 21)]
Class: LatinSquare
| self) |
Latin squares.
This class implements a latin square of order n with rows and columns indexed by the set 0, 1, ..., n-1 and symbols from the same set. The underlying latin square is a matrix(ZZ, n, n). If L is a latin square, then the cell at row r, column c is empty if and only if L[r, c] < 0. In this way we allow partial latin squares and can speak of completions to latin squares, etc.
There are two ways to declare a latin square:
Empty latin square of order n:
sage: n = 3 sage: L = LatinSquare(n) sage: L [-1 -1 -1] [-1 -1 -1] [-1 -1 -1]
Latin square from a matrix:
sage: M = matrix(ZZ, [[0, 1], [2, 3]]) sage: print LatinSquare(M) [0 1] [2 3]
Functions: actual_row_col_sym_sizes,
apply_isotopism,
clear_cells,
column,
contained_in,
copy,
disjoint_mate_dlxcpp_rows_and_map,
dlxcpp_has_unique_completion,
dumps,
filled_cells_map,
find_disjoint_mates,
gcs,
is_completable,
is_empty_column,
is_empty_row,
is_latin_square,
is_partial_latin_square,
is_uniquely_completable,
latex,
list,
ncols,
nr_distinct_symbols,
nr_filled_cells,
nrows,
permissable_values,
random_empty_cell,
row,
set_immutable,
top_left_empty_cell,
vals_in_col,
vals_in_row
| self) |
Bitrades sometimes end up in partial latin squares with unused rows, columns, or symbols. This function works out the actual number of used rows, columns, and symbols.
WARNING: we assume that the unused rows/columns occur in the lower right of self, and that the used symbols are in the range 0, 1, ..., m (no holes in that list).
sage: from sage.combinat.matrices.latin import * sage: B = back_circulant(3) sage: B[0,2] = B[1,2] = B[2,2] = -1 sage: B[0,0] = B[2,1] = -1 sage: print B [-1 1 -1] [ 1 2 -1] [ 2 -1 -1] sage: print B.actual_row_col_sym_sizes() (3, 2, 2)
| self, row_perm, col_perm, sym_perm) |
An isotopism is a permutation of the rows, columns, and symbols of a partial latin square self. Use isotopism() to convert a tuple (indexed from 0) to a Permutation object.
sage: from sage.combinat.matrices.latin import * sage: B = back_circulant(5) sage: print B [0 1 2 3 4] [1 2 3 4 0] [2 3 4 0 1] [3 4 0 1 2] [4 0 1 2 3] sage: alpha = isotopism((0,1,2,3,4)) sage: beta = isotopism((1,0,2,3,4)) sage: gamma = isotopism((2,1,0,3,4)) sage: print B.apply_isotopism(alpha, beta, gamma) [3 4 2 0 1] [0 2 3 1 4] [1 3 0 4 2] [4 0 1 2 3] [2 1 4 3 0]
| self) |
Mark every cell in self as being empty.
sage: A = LatinSquare(matrix(ZZ, [[0, 1], [2, 3]])) sage: A.clear_cells() sage: print A [-1 -1] [-1 -1]
| self, x) |
Returns column x of the latin square.
sage: from sage.combinat.matrices.latin import * sage: back_circulant(3).column(0) (0, 1, 2)
| self, Q) |
Is self
subseteq Q?
sage: from sage.combinat.matrices.latin import * sage: P = elementary_abelian_2group(2) sage: P[0, 0] = -1 sage: print P.contained_in(elementary_abelian_2group(2)) True sage: print back_circulant(4).contained_in(elementary_abelian_2group(2)) False
| self) |
To copy a latin square we must copy the underlying matrix.
sage: A = LatinSquare(matrix(ZZ, [[0, 1], [2, 3]])) sage: B = A.copy() sage: print A [0 1] [2 3]
| self, allow_subtrade) |
Internal function for find_disjoint_mates.
sage: from sage.combinat.matrices.latin import *
sage: B = back_circulant(4)
sage: print B.disjoint_mate_dlxcpp_rows_and_map(allow_subtrade = True)
([[0, 16, 32], [1, 17, 32], [2, 18, 32], [3, 19, 32], [4, 16, 33], [5, 17,
33], [6, 18, 33], [7, 19, 33], [8, 16, 34], [9, 17, 34], [10, 18, 34], [11,
19, 34], [12, 16, 35], [13, 17, 35], [14, 18, 35], [15, 19, 35], [0, 20,
36], [1, 21, 36], [2, 22, 36], [3, 23, 36], [4, 20, 37], [5, 21, 37], [6,
22, 37], [7, 23, 37], [8, 20, 38], [9, 21, 38], [10, 22, 38], [11, 23, 38],
[12, 20, 39], [13, 21, 39], [14, 22, 39], [15, 23, 39], [0, 24, 40], [1,
25, 40], [2, 26, 40], [3, 27, 40], [4, 24, 41], [5, 25, 41], [6, 26, 41],
[7, 27, 41], [8, 24, 42], [9, 25, 42], [10, 26, 42], [11, 27, 42], [12, 24,
43], [13, 25, 43], [14, 26, 43], [15, 27, 43], [0, 28, 44], [1, 29, 44],
[2, 30, 44], [3, 31, 44], [4, 28, 45], [5, 29, 45], [6, 30, 45], [7, 31,
45], [8, 28, 46], [9, 29, 46], [10, 30, 46], [11, 31, 46], [12, 28, 47],
[13, 29, 47], [14, 30, 47], [15, 31, 47]], {(9, 29, 46): (3, 2, 1), (13,
17, 35): (0, 3, 1), (7, 19, 33): (0, 1, 3), (14, 26, 43): (2, 3, 2), (0,
28, 44): (3, 0, 0), (5, 25, 41): (2, 1, 1), (11, 31, 46): (3, 2, 3), (14,
18, 35): (0, 3, 2), (11, 23, 38): (1, 2, 3), (5, 29, 45): (3, 1, 1), (13,
21, 39): (1, 3, 1), (1, 29, 44): (3, 0, 1), (0, 20, 36): (1, 0, 0), (12,
24, 43): (2, 3, 0), (8, 28, 46): (3, 2, 0), (12, 20, 39): (1, 3, 0), (11,
27, 42): (2, 2, 3), (6, 22, 37): (1, 1, 2), (1, 17, 32): (0, 0, 1), (10,
18, 34): (0, 2, 2), (12, 28, 47): (3, 3, 0), (1, 25, 40): (2, 0, 1), (10,
22, 38): (1, 2, 2), (5, 17, 33): (0, 1, 1), (3, 23, 36): (1, 0, 3), (6, 26,
41): (2, 1, 2), (9, 25, 42): (2, 2, 1), (7, 31, 45): (3, 1, 3), (15, 27,
43): (2, 3, 3), (3, 31, 44): (3, 0, 3), (8, 20, 38): (1, 2, 0), (2, 22,
36): (1, 0, 2), (3, 19, 32): (0, 0, 3), (9, 17, 34): (0, 2, 1), (15, 31,
47): (3, 3, 3), (8, 16, 34): (0, 2, 0), (14, 22, 39): (1, 3, 2), (4, 16,
33): (0, 1, 0), (14, 30, 47): (3, 3, 2), (2, 30, 44): (3, 0, 2), (4, 20,
37): (1, 1, 0), (6, 30, 45): (3, 1, 2), (12, 16, 35): (0, 3, 0), (15, 19,
35): (0, 3, 3), (5, 21, 37): (1, 1, 1), (4, 24, 41): (2, 1, 0), (13, 25,
43): (2, 3, 1), (0, 16, 32): (0, 0, 0), (15, 23, 39): (1, 3, 3), (7, 23,
37): (1, 1, 3), (6, 18, 33): (0, 1, 2), (10, 30, 46): (3, 2, 2), (13, 29,
47): (3, 3, 1), (11, 19, 34): (0, 2, 3), (1, 21, 36): (1, 0, 1), (7, 27,
41): (2, 1, 3), (0, 24, 40): (2, 0, 0), (10, 26, 42): (2, 2, 2), (3, 27,
40): (2, 0, 3), (2, 26, 40): (2, 0, 2), (9, 21, 38): (1, 2, 1), (8, 24,
42): (2, 2, 0), (4, 28, 45): (3, 1, 0), (2, 18, 32): (0, 0, 2)})
| self) |
Check if the partial latin square self of order n can be embedded in precisely one latin square of order n.
sage: from sage.combinat.matrices.latin import * sage: back_circulant(2).dlxcpp_has_unique_completion() True sage: P = LatinSquare(2) sage: P.dlxcpp_has_unique_completion() False sage: P[0, 0] = 0 sage: P.dlxcpp_has_unique_completion() True
| self) |
Since the latin square class doesn't hold any other private variables we just call dumps on self.square:
sage: from sage.combinat.matrices.latin import * sage: back_circulant(2) == loads(dumps(back_circulant(2))) True
| self) |
Number the filled cells of self with integers from 1, 2, 3, ....
Input:
sage: from sage.combinat.matrices.latin import *
sage: (a, b, c, G) = alternating_group_bitrade_generators(1)
sage: (T1, T2) = bitrade_from_group(a, b, c, G)
sage: print T1.filled_cells_map()
{1: (0, 0), 2: (0, 2), 3: (0, 3), 4: (1, 1), 5: (1, 2), 6: (1, 3), 7: (2,
0), 8: (2, 1), 9: (2, 2), 10: (3, 0), 11: (3, 1), 12: (3, 3), (2, 1): 8,
(1, 3): 6, (0, 3): 3, (1, 2): 5, (3, 3): 12, (3, 0): 10, (2, 2): 9, (1, 1):
4, (0, 0): 1, (3, 1): 11, (2, 0): 7, (0, 2): 2}
| self, [nr_to_find=None], [allow_subtrade=False]) |
WARNING: if allow_subtrade is True then we may return a partial latin square that is *not* disjoint to self. In that case, use bitrade(P, Q) to get an actual bitrade.
sage: from sage.combinat.matrices.latin import *
sage: B = back_circulant(4)
sage: g = B.find_disjoint_mates(allow_subtrade = True)
sage: B1 = g.next()
sage: B0, B1 = bitrade(B, B1)
sage: assert is_bitrade(B0, B1)
sage: print B0, "
,
", B1
[-1 1 2 -1]
[-1 2 -1 0]
[-1 -1 -1 -1]
[-1 0 1 2]
,
[-1 2 1 -1]
[-1 0 -1 2]
[-1 -1 -1 -1]
[-1 1 2 0]
| self) |
A greedy critical set of a latin square self is found by successively removing elements in a row-wise (bottom-up) manner, checking for unique completion at each step.
sage: from sage.combinat.matrices.latin import * sage: A = elementary_abelian_2group(3) sage: G = A.gcs() sage: print A [0 1 2 3 4 5 6 7] [1 0 3 2 5 4 7 6] [2 3 0 1 6 7 4 5] [3 2 1 0 7 6 5 4] [4 5 6 7 0 1 2 3] [5 4 7 6 1 0 3 2] [6 7 4 5 2 3 0 1] [7 6 5 4 3 2 1 0] sage: print G [ 0 1 2 3 4 5 6 -1] [ 1 0 3 2 5 4 -1 -1] [ 2 3 0 1 6 -1 4 -1] [ 3 2 1 0 -1 -1 -1 -1] [ 4 5 6 -1 0 1 2 -1] [ 5 4 -1 -1 1 0 -1 -1] [ 6 -1 4 -1 2 -1 0 -1] [-1 -1 -1 -1 -1 -1 -1 -1]
| self) |
Returns True if the partial latin square can be completed to a latin square.
The following partial latin square has no completion because there is nowhere that we can place the symbol 0 in the third row:
sage: B = LatinSquare(3)
sage: B[0, 0] = 0 sage: B[1, 1] = 0 sage: B[2, 2] = 1
sage: print B [ 0 -1 -1] [-1 0 -1] [-1 -1 1]
sage: print B.is_completable() False
sage: B[2, 2] = 0 sage: print B.is_completable() True
| self, c) |
Checks if column c of the partial latin square self is empty.
sage: from sage.combinat.matrices.latin import * sage: L = back_circulant(4) sage: print L.is_empty_column(0) False sage: L[0,0] = L[1,0] = L[2,0] = L[3,0] = -1 sage: print L.is_empty_column(0) True
| self, r) |
Checks if row r of the partial latin square self is empty.
sage: from sage.combinat.matrices.latin import * sage: L = back_circulant(4) sage: print L.is_empty_row(0) False sage: L[0,0] = L[0,1] = L[0,2] = L[0,3] = -1 sage: print L.is_empty_row(0) True
| self) |
self is a latin square if it is an n by n matrix, and each symbol in [0, 1, ..., n-1] appears exactly once in each row, and exactly once in each column.
sage: from sage.combinat.matrices.latin import * sage: elementary_abelian_2group(4).is_latin_square() True
sage: forward_circulant(7).is_latin_square() True
| self) |
self is a partial latin square if it is an n by n matrix, and each symbol in [0, 1, ..., n-1] appears at most once in each row, and at most once in each column.
sage: from sage.combinat.matrices.latin import * sage: LatinSquare(4).is_partial_latin_square() True sage: back_circulant(3).gcs().is_partial_latin_square() True sage: back_circulant(6).is_partial_latin_square() True
| self) |
Returns True if the partial latin square self has exactly one completion to a latin square. This is just a wrapper for the current best-known algorithm, Dancing Links by Knuth. See dancing_links.spyx
sage: from sage.combinat.matrices.latin import * sage: back_circulant(4).gcs().is_uniquely_completable() True
sage: G = elementary_abelian_2group(3).gcs() sage: G.is_uniquely_completable() True
sage: G[0, 0] = -1 sage: G.is_uniquely_completable() False
| self) |
Returns LaTeX code for the latin square.
sage: from sage.combinat.matrices.latin import *
sage: print back_circulant(3).latex()
\begin{array}{|c|c|c|}\hline 0 \& 1 \& 2\\\hline 1 \& 2 \& 0\\\hline 2 \& 0
\& 1\\\hline\end{array}
| self) |
Convert the latin square into a list, in a row-wise manner.
sage: from sage.combinat.matrices.latin import * sage: back_circulant(3).list() [0, 1, 2, 1, 2, 0, 2, 0, 1]
| self) |
Number of columns in the latin square.
sage: print LatinSquare(3).ncols() 3
| self) |
Returns the number of distinct symbols in the partial latin square self.
sage: from sage.combinat.matrices.latin import * sage: back_circulant(5).nr_distinct_symbols() 5 sage: L = LatinSquare(10) sage: print L.nr_distinct_symbols() 0 sage: L[0, 0] = 0 sage: L[0, 1] = 1 sage: print L.nr_distinct_symbols() 2
| self) |
Returns the number of filled cells (i.e. cells with a positive value) in the partial latin square self.
sage: from sage.combinat.matrices.latin import * sage: LatinSquare(matrix([[0, -1], [-1, 0]])).nr_filled_cells() 2
| self) |
Number of rows in the latin square.
sage: print LatinSquare(3).nrows() 3
| self, r, c) |
Find all values that do not appear in row r and column c of the latin square self. If self[r, c] is filled then we return the empty list.
Input:
sage: from sage.combinat.matrices.latin import * sage: L = back_circulant(5) sage: L[0, 0] = -1 sage: print L.permissable_values(0, 0) [0]
| self) |
Find an empty cell of self, uniformly at random.
Input:
RETURNS: [r, c] - cell such that self[r, c] is empty, or returns None if self is a (full) latin square.
sage: from sage.combinat.matrices.latin import * sage: P = back_circulant(2) sage: P[1,1] = -1 sage: P.random_empty_cell() [1, 1]
| self, x) |
Returns row x of the latin square.
sage: from sage.combinat.matrices.latin import * sage: back_circulant(3).row(0) (0, 1, 2)
| self) |
A latin square is immutable if the underlying matrix is immutable.
sage: L = LatinSquare(matrix(ZZ, [[0, 1], [2, 3]]))
sage: L.set_immutable()
sage: print {L : 0} # this would fail without set_immutable()
{[0 1]
[2 3]: 0}
| self) |
Returns the least [r, c] such that self[r, c] is an empty cell. If all cells are filled then we return None.
Input:
sage: from sage.combinat.matrices.latin import * sage: B = back_circulant(5) sage: B[3, 4] = -1 sage: print B.top_left_empty_cell() [3, 4]
| self, c) |
Returns a dictionary with key e if and only if column c of self has the symbol e.
sage: from sage.combinat.matrices.latin import *
sage: B = back_circulant(3)
sage: B[0, 0] = -1
sage: print back_circulant(3).vals_in_col(0)
{0: True, 1: True, 2: True}
| self, r) |
Returns a dictionary with key e if and only if row r of self has the symbol e.
sage: from sage.combinat.matrices.latin import *
sage: B = back_circulant(3)
sage: B[0, 0] = -1
sage: print back_circulant(3).vals_in_row(0)
{0: True, 1: True, 2: True}
Special Functions: __eq__,
__getitem__,
__init__,
__repr__,
__setitem__,
__str__
| self, Q) |
Two latin squares are equal if the underlying matrices are equal.
sage: A = LatinSquare(matrix(ZZ, [[0, 1], [2, 3]])) sage: B = LatinSquare(matrix(ZZ, [[0, 4], [2, 3]])) sage: print A == B False sage: B[0, 1] = 1 sage: print A == B True
| self, rc) |
If L is a LatinSquare then this method allows us to evaluate L[r, c].
sage: from sage.combinat.matrices.latin import * sage: B = back_circulant(3) sage: print B[1, 1] 2
| self) |
The representation of a latin square is the same as the underlying matrix.
sage: print LatinSquare(matrix(ZZ, [[0, 1], [2, 3]])).__repr__() [0 1] [2 3]
| self, rc, val) |
If L is a LatinSquare then this method allows us to set L[r, c].
sage: from sage.combinat.matrices.latin import * sage: B = back_circulant(3) sage: B[1, 1] = 10 sage: print B[1, 1] 10
| self) |
The string representation of a latin square is the same as the underlying matrix.
sage: print LatinSquare(matrix(ZZ, [[0, 1], [2, 3]])).__str__() [0 1] [2 3]