Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement partial latin square to list colouring problem #9

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.10"]
python-version: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3
Expand Down
10 changes: 0 additions & 10 deletions .gitpod.yml

This file was deleted.

5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
test:
poetry run pytest

install:
poetry install
23 changes: 21 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 2 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
[tool.poetry]
name = "ryser"
version = "0.1.0"
version = "0.1.0.9001"
description = "Latin squares and related designs."
authors = ["Matthew Henderson <[email protected]>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.10"
networkx = "^3.4.2"


[tool.poetry.group.test.dependencies]
Expand All @@ -16,7 +17,3 @@ pytest = "^8.1.1"
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.pytest.ini_options]
pythonpath = [
"."
]
73 changes: 19 additions & 54 deletions ryser/__init__.py → src/ryser/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import networkx as nx

def row_string(fixed, size, row, col_sep='|', padding=0):
"""
Converts a dict of fixed cells to a string.
Expand Down Expand Up @@ -87,12 +89,12 @@ def list_assignment(partial_latin_square, size):
if i in P.keys():
L[row(i,size),col(i,size)] = [P[i]]
else:
L[row(i,size),col(i,size)] = range(1, size + 1)
L[row(i,size),col(i,size)] = list(range(1, size + 1))
# update lists (remove used symbols from lists of same row/col)
for i in range(1, size**2 + 1):
if i in P.keys():
# then remove P[i] from any list of a cell not in P from the same row/col
for j in row_r(i, size) + col_r(i, size):
for j in list(row_r(i, size)) + list(col_r(i, size)):
if j not in P.keys():
if P[i] in L[row(j, size), col(j, size)]:
L[row(j, size), col(j, size)].remove(P[i])
Expand Down Expand Up @@ -130,57 +132,20 @@ def com_2_csm(P, size):
L[cell(row, col, size)] = int(P[i][j])
return L

class Latin:

def __init__(self, P, size, symbols = None, format = ''):
self._size = size
if symbols == None:
self._symbols = range(1, size + 1)
else:
self._symbols = symbols
if format == 'sim':
self._P = sim_2_csm(P, size)
elif format == 'com':
self._P = com_2_csm(P, size)
else:
self._P = P

def __repr__(self):
return dict_to_string_simple(self._P, self._size)

def __str__(self):
return self.__repr__()

def __getitem__(self, key):
return self._P[cell(key[0], key[1], self._size)]

def size(self):
return self._size

def symbols(self):
return self._symbols

def row_presences(self, row_index):
result = []
first_cell = cell(row_index, 1, self._size)
row = row_r(first_cell, self._size)
for cell_ in row:
symbol = self._P.get(cell_)
if symbol!=None:
result.append(symbol)
return result

def row_absences(self, row_index):
result = self.symbols()[:]
presences = self.row_presences(row_index)
for x in presences:
if x in result:
result.remove(x)
return result

def fixed_cells(self):
return self._P

def extend(self, disjoint_fixed):
self._P.update(disjoint_fixed)
def pls_list_colouring_problem(fixed, size):
"""
Return a complete digraph (including self-loops on every node) with a list-assignment
to edges such that the list on edge (i, j) is the set of all symbols not used in row i
or column j in the partial latin square represented by the input dictionary.

:param fixed: A dictionary of filled cells of a partial latin square.
:param size: Number of rows and columns in the completed latin square.
:return A complete digraph with edge list-assignment representing a partial latin square completion problem.
"""
G = nx.complete_graph(size, create_using = nx.DiGraph)
for node in G.nodes():
G.add_edge(node, node)
nx.set_edge_attributes(G, list_assignment(fixed, size), "permissible")
return(G)

5 changes: 0 additions & 5 deletions tests/test_latin.py

This file was deleted.

7 changes: 7 additions & 0 deletions tests/test_pls-list-colouring-problem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import ryser as ry

def test_pls_list_colouring_problem():
F = {1:1, 2:2, 6:1}
G = ry.pls_list_colouring_problem(F, 3)
assert list(G.nodes()) == [0, 1, 2]
assert list(G.edges(data = True)) == [(0, 1, {'permissible': [2]}), (0, 2, {'permissible': [3]}), (0, 0, {'permissible': [1]}), (1, 0, {'permissible': [2, 3]}), (1, 2, {'permissible': [1]}), (1, 1, {'permissible': [3]}), (2, 0, {'permissible': [2, 3]}), (2, 1, {'permissible': [1, 3]}), (2, 2, {'permissible': [2, 3]})]
Loading