Skip to content

Commit

Permalink
Merge pull request #11 from Aaron-Dwyer/coveringarray
Browse files Browse the repository at this point in the history
Coveringarray
  • Loading branch information
aadwyer authored Jun 26, 2023
2 parents 2054d99 + 54b0172 commit 0a1a2f3
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 136 deletions.
1 change: 1 addition & 0 deletions src/doc/en/reference/combinat/module_list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ Comprehensive Module List
sage/combinat/designs/resolvable_bibd
sage/combinat/designs/group_divisible_designs
sage/combinat/designs/block_design
sage/combinat/designs/covering_array
sage/combinat/designs/covering_design
sage/combinat/designs/database
sage/combinat/designs/design_catalog
Expand Down
46 changes: 46 additions & 0 deletions src/sage/combinat/designs/covering_array.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
r"""
Covering Arrays (CA)
A Covering Array, denoted CA`(N,k,v,t)`, is an `n` by `k` array with
entries from a set of `v` elements with the property that in every
selection of `t` columns, eachrow contains every sequence of
`t`-elements at least once.
An Orthogonal Array, denoted OA`(N,k,v,t)` is a covering array with the
property that each row contains every sequence of `t`-elements exactly
once
REFERENCES:
- [Colb2004]_
- [Sher2006]_
- [Wal2007]_
AUTHORS:
- Aaron Dwyer and brett stevens (2022): initial version
.. TODO::
Implement various construction methods for CAs
Functions
---------
"""

# **********************************************************************
# Copyright (C) 2022 Aaron Dwyer <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# https://www.gnu.org/licenses/
# **********************************************************************

from .orthogonal_arrays import OA_relabel, OA_standard_label
CA_relabel = OA_relabel
CA_standard_label = OA_standard_label
2 changes: 2 additions & 0 deletions src/sage/combinat/designs/design_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,5 @@

lazy_import('sage.combinat.designs.gen_quadrangles_with_spread',
('generalised_quadrangle_with_spread', 'generalised_quadrangle_hermitian_with_ovoid'))

lazy_import('sage.combinat.designs.covering_array', 'CoveringArray')
277 changes: 142 additions & 135 deletions src/sage/combinat/designs/designs_pyx.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,22 @@ from cysignals.memory cimport sig_malloc, sig_calloc, sig_realloc, sig_free

from sage.misc.unknown import Unknown

def is_covering_array(array,strength=None,symbol_set=None,verbose=False,parameters=False):
#Current Version
#***********************************************************#

def is_covering_array(array, strength=None, levels=None, verbose=False, parameters=False):
r"""
Checks if the input is a covering array with given strength.
Check if the input is a covering array with given strength.
- ``array`` -- The Covering Array to be tested.
- ``array`` -- the Covering Array to be tested.
- ``strength`` (integer) -- The parameter `t` of the covering array,
such thatin any selection of `t` columns of the array, every `t`
-tuple appearsat least once. If set to None then all t>0 are
tested to and themaximal strength is used.
- ``strength`` (integer) -- the parameter `t` of the covering array,
such that in any selection of `t` columns of the array, every `t`
-tuple appears at least once. If set to None then all t > 0 are
tested to and the maximal strength is used.
- ``symbol_set`` -- The collection of symbols that is used in
``array``. If set to None, then a symbol set will be assumed by
checking for each unique entry in the given ``array``.
- ``levels`` -- the number of symbols that appear in ``array``.
If set to None, then each unique entry in ``array`` is counted.
- ``verbose`` (boolean) -- whether to display some information about
the covering array.
Expand All @@ -38,171 +40,176 @@ def is_covering_array(array,strength=None,symbol_set=None,verbose=False,paramete
pair ``(boolean_answer,(N,t,k,v))``.
EXAMPLES::
sage: from sage.combinat.designs.designs_pyx import is_covering_array
sage: C = ((1, 1, 1, 0),
....: (1, 1, 0, 0),
....: (0, 0, 0))
sage: C = [[1, 1, 1, 0],
....: [1, 1, 0, 0],
....: [0, 0, 0]]
sage: is_covering_array(C)
Traceback (most recent call last):
...
ValueError: Not all rows are the same length, row 2 is not the same length as row 0
sage: C = (('a', 'a', 'a', 'b'),
....: ('a', 'a', 'b', 'a'),
....: ('a', 'b', 'a', 'a'),
....: ('b', 'a', 'a', 'a'),
....: ('b', 'b', 'b', 'b'))
sage: is_covering_array(C,verbose=True)
A 5 by 4 Covering Array with strength 2 with entries from ['a', 'b']
True
sage: is_covering_array(C,strength=3,verbose=True)
A 5 by 4 Covering Array with strength 0 with entries from ['a', 'b']
False
sage: C = [[0, 1, 1],
....: [1, 1, 0],
....: [1, 0, 1],
....: [0, 0, 0,]]
sage: is_covering_array(C,strength=4)
Traceback (most recent call last):
...
ValueError: Strength must be equal or less than number of columns
sage: from sage.combinat.designs.designs_pyx import is_covering_array
sage: C = ((0, 1, 0),
....: (1, 1, 0),
....: (1, 0, 0))
sage: C = [[0, 1, 1],
....: [1, 1, 1],
....: [1, 0, 1]]
sage: is_covering_array(C,verbose=True)
A 3 by 3 Covering Array with strength 0 with entries from [0, 1]
A 3 by 3 Covering Array with strength 0 with entries from a symbol set of size 2
True
sage: C = ((0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
....: (0, 0, 0, 0, 1, 1, 1, 1, 1, 1),
....: (0, 1, 1, 1, 0, 0, 0, 1, 1, 1),
....: (1, 0, 1, 1, 0, 1, 1, 0, 0, 1),
....: (1, 1, 0, 1, 1, 0, 1, 0, 1, 0),
....: (1, 1, 1, 0, 1, 1, 0, 1, 2, 0))
sage: is_covering_array(C,symbol_set=(0,1))
sage: C = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
....: [0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
....: [0, 1, 1, 1, 0, 0, 0, 1, 1, 1],
....: [1, 0, 1, 1, 0, 1, 1, 0, 0, 1],
....: [1, 1, 0, 1, 1, 0, 1, 0, 1, 0],
....: [1, 1, 1, 0, 1, 1, 0, 1, 2, 0]]
sage: is_covering_array(C,levels=2)
Traceback (most recent call last):
...
ValueError: 2 appears in the array but not in the given symbol set
sage: C = ((1, 0, 0, 2, 0, 2, 1, 2, 2, 1, 0, 2, 2),
....: (1, 1, 0, 0, 2, 0, 2, 1, 2, 2, 1, 0, 2),
....: (1, 1, 1, 0, 0, 2, 0, 2, 1, 2, 2, 1, 0),
....: (0, 1, 1, 1, 0, 0, 2, 0, 2, 1, 2, 2, 1),
....: (2, 0, 1, 1, 1, 0, 0, 2, 0, 2, 1, 2, 2),
....: (1, 2, 0, 1, 1, 1, 0, 0, 2, 0, 2, 1, 2),
....: (1, 1, 2, 0, 1, 1, 1, 0, 0, 2, 0, 2, 1),
....: (2, 1, 1, 2, 0, 1, 1, 1, 0, 0, 2, 0, 2),
....: (1, 2, 1, 1, 2, 0, 1, 1, 1, 0, 0, 2, 0),
....: (0, 1, 2, 1, 1, 2, 0, 1, 1, 1, 0, 0, 2),
....: (1, 0, 1, 2, 1, 1, 2, 0, 1, 1, 1, 0, 0),
....: (0, 1, 0, 1, 2, 1, 1, 2, 0, 1, 1, 1, 0),
....: (0, 0, 1, 0, 1, 2, 1, 1, 2, 0, 1, 1, 1),
....: (2, 0, 0, 1, 0, 1, 2, 1, 1, 2, 0, 1, 1),
....: (2, 2, 0, 0, 1, 0, 1, 2, 1, 1, 2, 0, 1),
....: (2, 2, 2, 0, 0, 1, 0, 1, 2, 1, 1, 2, 0),
....: (0, 2, 2, 2, 0, 0, 1, 0, 1, 2, 1, 1, 2),
....: (1, 0, 2, 2, 2, 0, 0, 1, 0, 1, 2, 1, 1),
....: (2, 1, 0, 2, 2, 2, 0, 0, 1, 0, 1, 2, 1),
....: (2, 2, 1, 0, 2, 2, 2, 0, 0, 1, 0, 1, 2),
....: (1, 2, 2, 1, 0, 2, 2, 2, 0, 0, 1, 0, 1),
....: (2, 1, 2, 2, 1, 0, 2, 2, 2, 0, 0, 1, 0),
....: (0, 2, 1, 2, 2, 1, 0, 2, 2, 2, 0, 0, 1),
....: (2, 0, 2, 1, 2, 2, 1, 0, 2, 2, 2, 0, 0),
....: (0, 2, 0, 2, 1, 2, 2, 1, 0, 2, 2, 2, 0),
....: (0, 0, 2, 0, 2, 1, 2, 2, 1, 0, 2, 2, 2),
....: (1, 1, 0, 2, 1, 1, 2, 1, 0, 1, 0, 0, 2),
....: (1, 1, 1, 0, 2, 1, 1, 2, 1, 0, 1, 0, 0),
....: (0, 1, 1, 1, 0, 2, 1, 1, 2, 1, 0, 1, 0),
....: (0, 0, 1, 1, 1, 0, 2, 1, 1, 2, 1, 0, 1),
....: (2, 0, 0, 1, 1, 1, 0, 2, 1, 1, 2, 1, 0),
....: (0, 2, 0, 0, 1, 1, 1, 0, 2, 1, 1, 2, 1),
....: (2, 0, 2, 0, 0, 1, 1, 1, 0, 2, 1, 1, 2),
....: (1, 2, 0, 2, 0, 0, 1, 1, 1, 0, 2, 1, 1),
....: (2, 1, 2, 0, 2, 0, 0, 1, 1, 1, 0, 2, 1),
....: (2, 2, 1, 2, 0, 2, 0, 0, 1, 1, 1, 0, 2),
....: (1, 2, 2, 1, 2, 0, 2, 0, 0, 1, 1, 1, 0),
....: (0, 1, 2, 2, 1, 2, 0, 2, 0, 0, 1, 1, 1),
....: (2, 0, 1, 2, 2, 1, 2, 0, 2, 0, 0, 1, 1),
....: (2, 2, 0, 1, 2, 2, 1, 2, 0, 2, 0, 0, 1),
....: (2, 2, 2, 0, 1, 2, 2, 1, 2, 0, 2, 0, 0),
....: (0, 2, 2, 2, 0, 1, 2, 2, 1, 2, 0, 2, 0),
....: (0, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2, 0, 2),
....: (1, 0, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2, 0),
....: (0, 1, 0, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2),
....: (1, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2, 2, 1),
....: (2, 1, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2, 2),
....: (1, 2, 1, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2),
....: (1, 1, 2, 1, 0, 1, 0, 0, 2, 2, 2, 0, 1),
....: (2, 1, 1, 2, 1, 0, 1, 0, 0, 2, 2, 2, 0),
....: (0, 2, 1, 1, 2, 1, 0, 1, 0, 0, 2, 2, 2),
....: (1, 0, 2, 1, 1, 2, 1, 0, 1, 0, 0, 2, 2),
....: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
ValueError: Array should contain integer symbols from 0 to 1
sage: C = [[1, 0, 0, 2, 0, 2, 1, 2, 2, 1, 0, 2, 2],
....: [1, 1, 0, 0, 2, 0, 2, 1, 2, 2, 1, 0, 2],
....: [1, 1, 1, 0, 0, 2, 0, 2, 1, 2, 2, 1, 0],
....: [0, 1, 1, 1, 0, 0, 2, 0, 2, 1, 2, 2, 1],
....: [2, 0, 1, 1, 1, 0, 0, 2, 0, 2, 1, 2, 2],
....: [1, 2, 0, 1, 1, 1, 0, 0, 2, 0, 2, 1, 2],
....: [1, 1, 2, 0, 1, 1, 1, 0, 0, 2, 0, 2, 1],
....: [2, 1, 1, 2, 0, 1, 1, 1, 0, 0, 2, 0, 2],
....: [1, 2, 1, 1, 2, 0, 1, 1, 1, 0, 0, 2, 0],
....: [0, 1, 2, 1, 1, 2, 0, 1, 1, 1, 0, 0, 2],
....: [1, 0, 1, 2, 1, 1, 2, 0, 1, 1, 1, 0, 0],
....: [0, 1, 0, 1, 2, 1, 1, 2, 0, 1, 1, 1, 0],
....: [0, 0, 1, 0, 1, 2, 1, 1, 2, 0, 1, 1, 1],
....: [2, 0, 0, 1, 0, 1, 2, 1, 1, 2, 0, 1, 1],
....: [2, 2, 0, 0, 1, 0, 1, 2, 1, 1, 2, 0, 1],
....: [2, 2, 2, 0, 0, 1, 0, 1, 2, 1, 1, 2, 0],
....: [0, 2, 2, 2, 0, 0, 1, 0, 1, 2, 1, 1, 2],
....: [1, 0, 2, 2, 2, 0, 0, 1, 0, 1, 2, 1, 1],
....: [2, 1, 0, 2, 2, 2, 0, 0, 1, 0, 1, 2, 1],
....: [2, 2, 1, 0, 2, 2, 2, 0, 0, 1, 0, 1, 2],
....: [1, 2, 2, 1, 0, 2, 2, 2, 0, 0, 1, 0, 1],
....: [2, 1, 2, 2, 1, 0, 2, 2, 2, 0, 0, 1, 0],
....: [0, 2, 1, 2, 2, 1, 0, 2, 2, 2, 0, 0, 1],
....: [2, 0, 2, 1, 2, 2, 1, 0, 2, 2, 2, 0, 0],
....: [0, 2, 0, 2, 1, 2, 2, 1, 0, 2, 2, 2, 0],
....: [0, 0, 2, 0, 2, 1, 2, 2, 1, 0, 2, 2, 2],
....: [1, 1, 0, 2, 1, 1, 2, 1, 0, 1, 0, 0, 2],
....: [1, 1, 1, 0, 2, 1, 1, 2, 1, 0, 1, 0, 0],
....: [0, 1, 1, 1, 0, 2, 1, 1, 2, 1, 0, 1, 0],
....: [0, 0, 1, 1, 1, 0, 2, 1, 1, 2, 1, 0, 1],
....: [2, 0, 0, 1, 1, 1, 0, 2, 1, 1, 2, 1, 0],
....: [0, 2, 0, 0, 1, 1, 1, 0, 2, 1, 1, 2, 1],
....: [2, 0, 2, 0, 0, 1, 1, 1, 0, 2, 1, 1, 2],
....: [1, 2, 0, 2, 0, 0, 1, 1, 1, 0, 2, 1, 1],
....: [2, 1, 2, 0, 2, 0, 0, 1, 1, 1, 0, 2, 1],
....: [2, 2, 1, 2, 0, 2, 0, 0, 1, 1, 1, 0, 2],
....: [1, 2, 2, 1, 2, 0, 2, 0, 0, 1, 1, 1, 0],
....: [0, 1, 2, 2, 1, 2, 0, 2, 0, 0, 1, 1, 1],
....: [2, 0, 1, 2, 2, 1, 2, 0, 2, 0, 0, 1, 1],
....: [2, 2, 0, 1, 2, 2, 1, 2, 0, 2, 0, 0, 1],
....: [2, 2, 2, 0, 1, 2, 2, 1, 2, 0, 2, 0, 0],
....: [0, 2, 2, 2, 0, 1, 2, 2, 1, 2, 0, 2, 0],
....: [0, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2, 0, 2],
....: [1, 0, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2, 0],
....: [0, 1, 0, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2],
....: [1, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2, 2, 1],
....: [2, 1, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2, 2],
....: [1, 2, 1, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2],
....: [1, 1, 2, 1, 0, 1, 0, 0, 2, 2, 2, 0, 1],
....: [2, 1, 1, 2, 1, 0, 1, 0, 0, 2, 2, 2, 0],
....: [0, 2, 1, 1, 2, 1, 0, 1, 0, 0, 2, 2, 2],
....: [1, 0, 2, 1, 1, 2, 1, 0, 1, 0, 0, 2, 2],
....: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
sage: is_covering_array(C,parameters=True)
(True, (53, 3, 13, 3))
sage: C = [[1, 0, 1, 1, 2, 0, 2, 2],
....: [2, 1, 0, 1, 1, 2, 0, 2],
....: [2, 2, 1, 0, 1, 1, 2, 0],
....: [0, 2, 2, 1, 0, 1, 1, 2],
....: [2, 0, 2, 2, 1, 0, 1, 1],
....: [1, 2, 0, 2, 2, 1, 0, 1],
....: [1, 1, 2, 0, 2, 2, 1, 0],
....: [0, 1, 1, 2, 0, 2, 2, 1]]
sage: is_covering_array(C,strength=2,parameters=True)
(False, (8, 0, 8, 3))
"""
from itertools import product, combinations

number_rows=len(array)
number_columns=len(array[0])

for row in range(number_rows):
if len(array[row]) != number_columns:
raise ValueError("Not all rows are the same length, row {} is not the same length as row 0".format(row))

if symbol_set is None:
symbol_set = list({x for l in array for x in l})
symbol_set.sort()
if levels is None:
symbol_list = list({x for l in array for x in l})
levels = len(symbol_list)
else:
for l in array:
for x in l:
if x not in symbol_set:
raise ValueError("{} appears in the array but not in the given symbol set".format(x))
symbol_list = [num for num in range(levels)]

number_rows = len(array)
number_columns = len(array[0])

# Set wstrength to be the current t value to be checked
if strength is None:
wstrength = 1
else:
if strength > number_columns:
raise ValueError("Strength must be equal or less than number of columns")
wstrength = strength
finished = False

# if no strength inputted, tries increasing values for t until one
# does not work. If strength is inputted ends after one check
for row in array:
if len(row) != number_columns:
raise ValueError("Not all rows are the same length, row {} is not the same length as row 0".format(array.index(row)))
else:
for entry in row:
if int(entry) != entry or entry < -1 or entry >= levels:
raise ValueError("Array should contain integer symbols from 0 to {}".format(levels-1))

finished = False
result = True
# If no strength inputted, try increasing values for t until one
# does not work. If strength is inputted end after one check
while finished is False:
# Iterates over every possible selections of t columns, and
# Iterate over every possible selections of t columns, and
# count the t-tuples appearing in each selection
for comb in combinations(range(number_columns),wstrength):
tuple_dictionary = {item: 0 for item in product(symbol_set, repeat=wstrength)}
for comb in combinations(range(number_columns), wstrength):
tuple_dictionary = {item: 0 for item in product(symbol_list, repeat=wstrength)}
for row in array:
tuple_dictionary[tuple([row[ti] for ti in comb])]+=1

# Checks if any t-tuple is not covered in current columns
tuple_dictionary[tuple([row[ti] for ti in comb])] += 1
# Check if any t-tuple is not covered in current columns
if 0 in tuple_dictionary.values():
if strength != None:
wstrength = 0
result = False
if strength is None:
wstrength -= 1
finished = True
break
else:
wstrength -=1
wstrength = 0
result = False
finished = True
break

else:
if strength != None:
result = True
finished = True
break
if finished is False:
wstrength +=1
if strength is None and wstrength < number_columns:
wstrength += 1
else:
finished = True
break

if verbose:
print('A {} by {} Covering Array with strength {} with entries from {}'.format(number_rows,number_columns,wstrength,symbol_set))

if strength is None:
if parameters:
return (True,(number_rows,wstrength,number_columns,len(symbol_set)))
else:
return True
print('A {} by {} Covering Array with strength {} with entries from a symbol set of size {}'.format(number_rows,number_columns,wstrength,levels))

if parameters:
return (result, (number_rows, wstrength, number_columns, levels))
else:
if parameters:
return (result,(number_rows,wstrength,number_columns,len(symbol_set)))
else:
return result
return result


def is_orthogonal_array(OA, int k, int n, int t=2, verbose=False, terminology="OA"):
r"""
Expand Down
Loading

0 comments on commit 0a1a2f3

Please sign in to comment.