Dancing Links internal pyx code#
EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver
sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]]
sage: x = dlx_solver(rows)
sage: x
Dancing links solver for 6 columns and 6 rows
>>> from sage.all import *
>>> from sage.combinat.matrices.dancing_links import dlx_solver
>>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(3),Integer(4),Integer(5)], [Integer(0),Integer(1)], [Integer(2),Integer(3),Integer(4),Integer(5)], [Integer(0)], [Integer(1),Integer(2),Integer(3),Integer(4),Integer(5)]]
>>> x = dlx_solver(rows)
>>> x
Dancing links solver for 6 columns and 6 rows
The number of solutions:
sage: x.number_of_solutions()
3
>>> from sage.all import *
>>> x.number_of_solutions()
3
Iterate over the solutions:
sage: sorted(map(sorted, x.solutions_iterator()))
[[0, 1], [2, 3], [4, 5]]
>>> from sage.all import *
>>> sorted(map(sorted, x.solutions_iterator()))
[[0, 1], [2, 3], [4, 5]]
All solutions (computed in parallel):
sage: sorted(map(sorted, x.all_solutions()))
[[0, 1], [2, 3], [4, 5]]
>>> from sage.all import *
>>> sorted(map(sorted, x.all_solutions()))
[[0, 1], [2, 3], [4, 5]]
Return the first solution found when the computation is done in parallel:
sage: sorted(x.one_solution(ncpus=2)) # random
[0, 1]
>>> from sage.all import *
>>> sorted(x.one_solution(ncpus=Integer(2))) # random
[0, 1]
Find all solutions using some specific rows:
sage: x_using_row_2 = x.restrict([2])
sage: x_using_row_2
Dancing links solver for 7 columns and 6 rows
sage: sorted(map(sorted, x_using_row_2.solutions_iterator()))
[[2, 3]]
>>> from sage.all import *
>>> x_using_row_2 = x.restrict([Integer(2)])
>>> x_using_row_2
Dancing links solver for 7 columns and 6 rows
>>> sorted(map(sorted, x_using_row_2.solutions_iterator()))
[[2, 3]]
The two basic methods that are wrapped in this class are search
which
returns 1
if a solution is found or 0
otherwise and get_solution
which return the current solution:
sage: x = dlx_solver(rows)
sage: x.search()
1
sage: x.get_solution()
[0, 1]
sage: x.search()
1
sage: x.get_solution()
[2, 3]
sage: x.search()
1
sage: x.get_solution()
[4, 5]
sage: x.search()
0
>>> from sage.all import *
>>> x = dlx_solver(rows)
>>> x.search()
1
>>> x.get_solution()
[0, 1]
>>> x.search()
1
>>> x.get_solution()
[2, 3]
>>> x.search()
1
>>> x.get_solution()
[4, 5]
>>> x.search()
0
There is also a method reinitialize
to reinitialize the algorithm:
sage: x.reinitialize()
sage: x.search()
1
sage: x.get_solution()
[0, 1]
>>> from sage.all import *
>>> x.reinitialize()
>>> x.search()
1
>>> x.get_solution()
[0, 1]
- class sage.combinat.matrices.dancing_links.dancing_linksWrapper[source]#
Bases:
object
A simple class that implements dancing links.
The main methods to list the solutions are
search()
andget_solution()
. You can also usenumber_of_solutions()
to count them.This class simply wraps a C++ implementation of Carlo Hamalainen.
- all_solutions(ncpus=None, column=None)[source]#
Return all solutions found after splitting the problem to allow parallel computation.
INPUT:
ncpus
– integer (default:None
), maximal number of subprocesses to use at the same time. IfNone
, it detects the number of effective CPUs in the system usingsage.parallel.ncpus.ncpus()
.column
– integer (default:None
), the column used to split the problem, ifNone
a random column is chosen
OUTPUT:
list of solutions
EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]] sage: d = dlx_solver(rows) sage: S = d.all_solutions() sage: sorted(sorted(s) for s in S) [[0, 1], [2, 3], [4, 5]]
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(3),Integer(4),Integer(5)], [Integer(0),Integer(1)], [Integer(2),Integer(3),Integer(4),Integer(5)], [Integer(0)], [Integer(1),Integer(2),Integer(3),Integer(4),Integer(5)]] >>> d = dlx_solver(rows) >>> S = d.all_solutions() >>> sorted(sorted(s) for s in S) [[0, 1], [2, 3], [4, 5]]
The number of CPUs can be specified as input:
sage: S = Subsets(range(4)) sage: rows = map(list, S) sage: dlx = dlx_solver(rows) sage: dlx Dancing links solver for 4 columns and 16 rows sage: dlx.number_of_solutions() 15 sage: sorted(sorted(s) for s in dlx.all_solutions(ncpus=2)) [[1, 2, 3, 4], [1, 2, 10], [1, 3, 9], [1, 4, 8], [1, 14], [2, 3, 7], [2, 4, 6], [2, 13], [3, 4, 5], [3, 12], [4, 11], [5, 10], [6, 9], [7, 8], [15]]
>>> from sage.all import * >>> S = Subsets(range(Integer(4))) >>> rows = map(list, S) >>> dlx = dlx_solver(rows) >>> dlx Dancing links solver for 4 columns and 16 rows >>> dlx.number_of_solutions() 15 >>> sorted(sorted(s) for s in dlx.all_solutions(ncpus=Integer(2))) [[1, 2, 3, 4], [1, 2, 10], [1, 3, 9], [1, 4, 8], [1, 14], [2, 3, 7], [2, 4, 6], [2, 13], [3, 4, 5], [3, 12], [4, 11], [5, 10], [6, 9], [7, 8], [15]]
If
ncpus=1
, the computation is not done in parallel:sage: sorted(sorted(s) for s in dlx.all_solutions(ncpus=1)) [[1, 2, 3, 4], [1, 2, 10], [1, 3, 9], [1, 4, 8], [1, 14], [2, 3, 7], [2, 4, 6], [2, 13], [3, 4, 5], [3, 12], [4, 11], [5, 10], [6, 9], [7, 8], [15]]
>>> from sage.all import * >>> sorted(sorted(s) for s in dlx.all_solutions(ncpus=Integer(1))) [[1, 2, 3, 4], [1, 2, 10], [1, 3, 9], [1, 4, 8], [1, 14], [2, 3, 7], [2, 4, 6], [2, 13], [3, 4, 5], [3, 12], [4, 11], [5, 10], [6, 9], [7, 8], [15]]
- get_solution()[source]#
Return the current solution.
After a new solution is found using the method
search()
this method return the rows that make up the current solution.
- ncols()[source]#
Return the number of columns.
EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2], [1,2], [0], [3,4,5]] sage: dlx = dlx_solver(rows) sage: dlx.ncols() 6
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(1),Integer(2)], [Integer(0)], [Integer(3),Integer(4),Integer(5)]] >>> dlx = dlx_solver(rows) >>> dlx.ncols() 6
- nrows()[source]#
Return the number of rows.
EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2], [1,2], [0], [3,4,5]] sage: dlx = dlx_solver(rows) sage: dlx.nrows() 4
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(1),Integer(2)], [Integer(0)], [Integer(3),Integer(4),Integer(5)]] >>> dlx = dlx_solver(rows) >>> dlx.nrows() 4
- number_of_solutions(ncpus=None, column=None)[source]#
Return the number of distinct solutions.
INPUT:
ncpus
– integer (default:None
), maximal number of subprocesses to use at the same time. Ifncpus>1
the dancing links problem is split into independent subproblems to allow parallel computation. IfNone
, it detects the number of effective CPUs in the system usingsage.parallel.ncpus.ncpus()
.column
– integer (default:None
), the column used to split the problem, ifNone
a random column is chosen (this argument is ignored ifncpus
is1
)
OUTPUT:
integer
EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2]] sage: rows += [[0,2]] sage: rows += [[1]] sage: rows += [[3]] sage: x = dlx_solver(rows) sage: x.number_of_solutions() 2
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)]] >>> rows += [[Integer(0),Integer(2)]] >>> rows += [[Integer(1)]] >>> rows += [[Integer(3)]] >>> x = dlx_solver(rows) >>> x.number_of_solutions() 2
The number of CPUs can be specified as input:
sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]] sage: x = dlx_solver(rows) sage: x.number_of_solutions(ncpus=2, column=3) 3
>>> from sage.all import * >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(3),Integer(4),Integer(5)], [Integer(0),Integer(1)], [Integer(2),Integer(3),Integer(4),Integer(5)], [Integer(0)], [Integer(1),Integer(2),Integer(3),Integer(4),Integer(5)]] >>> x = dlx_solver(rows) >>> x.number_of_solutions(ncpus=Integer(2), column=Integer(3)) 3
sage: S = Subsets(range(5)) sage: rows = map(list, S) sage: d = dlx_solver(rows) sage: d.number_of_solutions() 52
>>> from sage.all import * >>> S = Subsets(range(Integer(5))) >>> rows = map(list, S) >>> d = dlx_solver(rows) >>> d.number_of_solutions() 52
- one_solution(ncpus=None, column=None)[source]#
Return the first solution found.
This method allows parallel computations which might be useful for some kind of problems when it is very hard just to find one solution.
INPUT:
ncpus
– integer (default:None
), maximal number of subprocesses to use at the same time. IfNone
, it detects the number of effective CPUs in the system usingsage.parallel.ncpus.ncpus()
. Ifncpus=1
, the first solution is searched serially.column
– integer (default:None
), the column used to split the problem (seerestrict()
). IfNone
, a random column is chosen. This argument is ignored ifncpus=1
.
OUTPUT:
list of rows or
None
if no solution is foundNote
For some case, increasing the number of cpus makes it faster. For other instances,
ncpus=1
is faster. It all depends on problem which is considered.EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]] sage: d = dlx_solver(rows) sage: solutions = [[0,1], [2,3], [4,5]] sage: sorted(d.one_solution()) in solutions True
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(3),Integer(4),Integer(5)], [Integer(0),Integer(1)], [Integer(2),Integer(3),Integer(4),Integer(5)], [Integer(0)], [Integer(1),Integer(2),Integer(3),Integer(4),Integer(5)]] >>> d = dlx_solver(rows) >>> solutions = [[Integer(0),Integer(1)], [Integer(2),Integer(3)], [Integer(4),Integer(5)]] >>> sorted(d.one_solution()) in solutions True
The number of CPUs can be specified as input:
sage: sorted(d.one_solution(ncpus=2)) in solutions True
>>> from sage.all import * >>> sorted(d.one_solution(ncpus=Integer(2))) in solutions True
The column used to split the problem for parallel computations can be given:
sage: sorted(d.one_solution(ncpus=2, column=4)) in solutions True
>>> from sage.all import * >>> sorted(d.one_solution(ncpus=Integer(2), column=Integer(4))) in solutions True
When no solution is found:
sage: rows = [[0,1,2], [2,3,4,5], [0,1,2,3]] sage: d = dlx_solver(rows) sage: d.one_solution() is None True
>>> from sage.all import * >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(2),Integer(3),Integer(4),Integer(5)], [Integer(0),Integer(1),Integer(2),Integer(3)]] >>> d = dlx_solver(rows) >>> d.one_solution() is None True
- one_solution_using_milp_solver(solver=None, integrality_tolerance=0.001)[source]#
Return a solution found using a MILP solver.
INPUT:
solver
– string orNone
(default:None
), possible values include'GLPK'
,'GLPK/exact'
,'Coin'
,'CPLEX'
,'Gurobi'
,'CVXOPT'
,'PPL'
,'InteractiveLP'
.
OUTPUT:
list of rows or
None
if no solution is foundNote
When comparing the time taken by method
one_solution
, have in mind thatone_solution_using_milp_solver
first creates (and caches) the MILP solver instance from the dancing links solver. This copy of data may take many seconds depending on the size of the problem.EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]] sage: d = dlx_solver(rows) sage: solutions = [[0,1], [2,3], [4,5]] sage: d.one_solution_using_milp_solver() in solutions # needs sage.numerical.mip True
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(3),Integer(4),Integer(5)], [Integer(0),Integer(1)], [Integer(2),Integer(3),Integer(4),Integer(5)], [Integer(0)], [Integer(1),Integer(2),Integer(3),Integer(4),Integer(5)]] >>> d = dlx_solver(rows) >>> solutions = [[Integer(0),Integer(1)], [Integer(2),Integer(3)], [Integer(4),Integer(5)]] >>> d.one_solution_using_milp_solver() in solutions # needs sage.numerical.mip True
Using optional solvers:
sage: # optional - gurobi sage_numerical_backends_gurobi, needs sage.numerical.mip sage: s = d.one_solution_using_milp_solver('gurobi') sage: s in solutions True
>>> from sage.all import * >>> # optional - gurobi sage_numerical_backends_gurobi, needs sage.numerical.mip >>> s = d.one_solution_using_milp_solver('gurobi') >>> s in solutions True
When no solution is found:
sage: rows = [[0,1,2], [2,3,4,5], [0,1,2,3]] sage: d = dlx_solver(rows) sage: d.one_solution_using_milp_solver() is None # needs sage.numerical.mip True
>>> from sage.all import * >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(2),Integer(3),Integer(4),Integer(5)], [Integer(0),Integer(1),Integer(2),Integer(3)]] >>> d = dlx_solver(rows) >>> d.one_solution_using_milp_solver() is None # needs sage.numerical.mip True
- one_solution_using_sat_solver(solver=None)[source]#
Return a solution found using a SAT solver.
INPUT:
solver
– string orNone
(default:None
), possible values include'picosat'
,'cryptominisat'
,'LP'
,'glucose'
,'glucose-syrup'
.
OUTPUT:
list of rows or
None
if no solution is foundNote
When comparing the time taken by method
one_solution
, have in mind thatone_solution_using_sat_solver
first creates the SAT solver instance from the dancing links solver. This copy of data may take many seconds depending on the size of the problem.EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]] sage: d = dlx_solver(rows) sage: solutions = [[0,1], [2,3], [4,5]] sage: d.one_solution_using_sat_solver() in solutions # needs sage.sat True
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(3),Integer(4),Integer(5)], [Integer(0),Integer(1)], [Integer(2),Integer(3),Integer(4),Integer(5)], [Integer(0)], [Integer(1),Integer(2),Integer(3),Integer(4),Integer(5)]] >>> d = dlx_solver(rows) >>> solutions = [[Integer(0),Integer(1)], [Integer(2),Integer(3)], [Integer(4),Integer(5)]] >>> d.one_solution_using_sat_solver() in solutions # needs sage.sat True
Using optional solvers:
sage: s = d.one_solution_using_sat_solver('glucose') # optional - glucose, needs sage.sat sage: s in solutions # optional - glucose, needs sage.sat True
>>> from sage.all import * >>> s = d.one_solution_using_sat_solver('glucose') # optional - glucose, needs sage.sat >>> s in solutions # optional - glucose, needs sage.sat True
When no solution is found:
sage: rows = [[0,1,2], [2,3,4,5], [0,1,2,3]] sage: d = dlx_solver(rows) sage: d.one_solution_using_sat_solver() is None # needs sage.sat True
>>> from sage.all import * >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(2),Integer(3),Integer(4),Integer(5)], [Integer(0),Integer(1),Integer(2),Integer(3)]] >>> d = dlx_solver(rows) >>> d.one_solution_using_sat_solver() is None # needs sage.sat True
- reinitialize()[source]#
Reinitialization of the search algorithm
This recreates an empty
dancing_links
object and adds the rows to the instance ofdancing_links.
EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]] sage: x = dlx_solver(rows) sage: x.get_solution() if x.search() else None [0, 1] sage: x.get_solution() if x.search() else None [2, 3]
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(3),Integer(4),Integer(5)], [Integer(0),Integer(1)], [Integer(2),Integer(3),Integer(4),Integer(5)], [Integer(0)], [Integer(1),Integer(2),Integer(3),Integer(4),Integer(5)]] >>> x = dlx_solver(rows) >>> x.get_solution() if x.search() else None [0, 1] >>> x.get_solution() if x.search() else None [2, 3]
Reinitialization of the algorithm:
sage: x.reinitialize() sage: x.get_solution() if x.search() else None [0, 1] sage: x.get_solution() if x.search() else None [2, 3] sage: x.get_solution() if x.search() else None [4, 5] sage: x.get_solution() if x.search() else None
>>> from sage.all import * >>> x.reinitialize() >>> x.get_solution() if x.search() else None [0, 1] >>> x.get_solution() if x.search() else None [2, 3] >>> x.get_solution() if x.search() else None [4, 5] >>> x.get_solution() if x.search() else None
Reinitialization works after solutions are exhausted:
sage: x.reinitialize() sage: x.get_solution() if x.search() else None [0, 1] sage: x.get_solution() if x.search() else None [2, 3] sage: x.get_solution() if x.search() else None [4, 5] sage: x.get_solution() if x.search() else None
>>> from sage.all import * >>> x.reinitialize() >>> x.get_solution() if x.search() else None [0, 1] >>> x.get_solution() if x.search() else None [2, 3] >>> x.get_solution() if x.search() else None [4, 5] >>> x.get_solution() if x.search() else None
- restrict(indices)[source]#
Return a dancing links solver solving the subcase which uses some given rows.
For every row that is wanted in the solution, we add a new column to the row to make sure it is in the solution.
INPUT:
indices
– list, row indices to be found in the solution
OUTPUT:
dancing links solver
EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]] sage: d = dlx_solver(rows) sage: d Dancing links solver for 6 columns and 6 rows sage: sorted(map(sorted, d.solutions_iterator())) [[0, 1], [2, 3], [4, 5]]
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(3),Integer(4),Integer(5)], [Integer(0),Integer(1)], [Integer(2),Integer(3),Integer(4),Integer(5)], [Integer(0)], [Integer(1),Integer(2),Integer(3),Integer(4),Integer(5)]] >>> d = dlx_solver(rows) >>> d Dancing links solver for 6 columns and 6 rows >>> sorted(map(sorted, d.solutions_iterator())) [[0, 1], [2, 3], [4, 5]]
To impose that the 0th row is part of the solution, the rows of the new problem are:
sage: d_using_0 = d.restrict([0]) sage: d_using_0 Dancing links solver for 7 columns and 6 rows sage: d_using_0.rows() [[0, 1, 2, 6], [3, 4, 5], [0, 1], [2, 3, 4, 5], [0], [1, 2, 3, 4, 5]]
>>> from sage.all import * >>> d_using_0 = d.restrict([Integer(0)]) >>> d_using_0 Dancing links solver for 7 columns and 6 rows >>> d_using_0.rows() [[0, 1, 2, 6], [3, 4, 5], [0, 1], [2, 3, 4, 5], [0], [1, 2, 3, 4, 5]]
After restriction the subproblem has one more columns and the same number of rows as the original one:
sage: d.restrict([1]).rows() [[0, 1, 2], [3, 4, 5, 6], [0, 1], [2, 3, 4, 5], [0], [1, 2, 3, 4, 5]] sage: d.restrict([2]).rows() [[0, 1, 2], [3, 4, 5], [0, 1, 6], [2, 3, 4, 5], [0], [1, 2, 3, 4, 5]]
>>> from sage.all import * >>> d.restrict([Integer(1)]).rows() [[0, 1, 2], [3, 4, 5, 6], [0, 1], [2, 3, 4, 5], [0], [1, 2, 3, 4, 5]] >>> d.restrict([Integer(2)]).rows() [[0, 1, 2], [3, 4, 5], [0, 1, 6], [2, 3, 4, 5], [0], [1, 2, 3, 4, 5]]
This method allows to find solutions where the 0th row is part of a solution:
sage: sorted(map(sorted, d.restrict([0]).solutions_iterator())) [[0, 1]]
>>> from sage.all import * >>> sorted(map(sorted, d.restrict([Integer(0)]).solutions_iterator())) [[0, 1]]
Some other examples:
sage: sorted(map(sorted, d.restrict([1]).solutions_iterator())) [[0, 1]] sage: sorted(map(sorted, d.restrict([2]).solutions_iterator())) [[2, 3]] sage: sorted(map(sorted, d.restrict([3]).solutions_iterator())) [[2, 3]]
>>> from sage.all import * >>> sorted(map(sorted, d.restrict([Integer(1)]).solutions_iterator())) [[0, 1]] >>> sorted(map(sorted, d.restrict([Integer(2)]).solutions_iterator())) [[2, 3]] >>> sorted(map(sorted, d.restrict([Integer(3)]).solutions_iterator())) [[2, 3]]
Here there are no solution using both 0th and 3rd row:
sage: list(d.restrict([0,3]).solutions_iterator()) []
>>> from sage.all import * >>> list(d.restrict([Integer(0),Integer(3)]).solutions_iterator()) []
- rows()[source]#
Return the list of rows.
EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2], [1,2], [0]] sage: x = dlx_solver(rows) sage: x.rows() [[0, 1, 2], [1, 2], [0]]
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(1),Integer(2)], [Integer(0)]] >>> x = dlx_solver(rows) >>> x.rows() [[0, 1, 2], [1, 2], [0]]
- search()[source]#
Search for a new solution.
Return
1
if a new solution is found and0
otherwise. To recover the solution, use the methodget_solution()
.EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2]] sage: rows+= [[0,2]] sage: rows+= [[1]] sage: rows+= [[3]] sage: x = dlx_solver(rows) sage: print(x.search()) 1 sage: print(x.get_solution()) [3, 0]
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)]] >>> rows+= [[Integer(0),Integer(2)]] >>> rows+= [[Integer(1)]] >>> rows+= [[Integer(3)]] >>> x = dlx_solver(rows) >>> print(x.search()) 1 >>> print(x.get_solution()) [3, 0]
- solutions_iterator()[source]#
Return an iterator of the solutions.
EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]] sage: d = dlx_solver(rows) sage: sorted(map(sorted, d.solutions_iterator())) [[0, 1], [2, 3], [4, 5]]
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(3),Integer(4),Integer(5)], [Integer(0),Integer(1)], [Integer(2),Integer(3),Integer(4),Integer(5)], [Integer(0)], [Integer(1),Integer(2),Integer(3),Integer(4),Integer(5)]] >>> d = dlx_solver(rows) >>> sorted(map(sorted, d.solutions_iterator())) [[0, 1], [2, 3], [4, 5]]
- split(column)[source]#
Return a dict of independent solvers.
For each
i
-th row containing a1
in thecolumn
, the dict associates the solver giving all solution using thei
-th row.This is used for parallel computations.
INPUT:
column
– integer, the column used to split the problem into independent subproblems
OUTPUT:
dict where keys are row numbers and values are dlx solvers
EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]] sage: d = dlx_solver(rows) sage: d Dancing links solver for 6 columns and 6 rows sage: sorted(map(sorted, d.solutions_iterator())) [[0, 1], [2, 3], [4, 5]]
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(3),Integer(4),Integer(5)], [Integer(0),Integer(1)], [Integer(2),Integer(3),Integer(4),Integer(5)], [Integer(0)], [Integer(1),Integer(2),Integer(3),Integer(4),Integer(5)]] >>> d = dlx_solver(rows) >>> d Dancing links solver for 6 columns and 6 rows >>> sorted(map(sorted, d.solutions_iterator())) [[0, 1], [2, 3], [4, 5]]
After the split each subproblem has one more column and the same number of rows as the original problem:
sage: D = d.split(0) sage: D {0: Dancing links solver for 7 columns and 6 rows, 2: Dancing links solver for 7 columns and 6 rows, 4: Dancing links solver for 7 columns and 6 rows}
>>> from sage.all import * >>> D = d.split(Integer(0)) >>> D {0: Dancing links solver for 7 columns and 6 rows, 2: Dancing links solver for 7 columns and 6 rows, 4: Dancing links solver for 7 columns and 6 rows}
The (disjoint) union of the solutions of the subproblems is equal to the set of solutions shown above:
sage: for x in D.values(): sorted(map(sorted, x.solutions_iterator())) [[0, 1]] [[2, 3]] [[4, 5]]
>>> from sage.all import * >>> for x in D.values(): sorted(map(sorted, x.solutions_iterator())) [[0, 1]] [[2, 3]] [[4, 5]]
- to_milp(solver=None)[source]#
Return the mixed integer linear program (MILP) representing an equivalent problem.
See also
sage.numerical.mip.MixedIntegerLinearProgram
.INPUT:
solver
– string orNone
(default:None
), possible values include'GLPK'
,'GLPK/exact'
,'Coin'
,'CPLEX'
,'Gurobi'
,'CVXOPT'
,'PPL'
,'InteractiveLP'
.
OUTPUT:
MixedIntegerLinearProgram instance
MIPVariable with binary components
EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2], [0,2], [1], [3]] sage: d = dlx_solver(rows) sage: p,x = d.to_milp() # needs sage.numerical.mip sage: p # needs sage.numerical.mip Boolean Program (no objective, 4 variables, ... constraints) sage: x # needs sage.numerical.mip MIPVariable with 4 binary components
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(0),Integer(2)], [Integer(1)], [Integer(3)]] >>> d = dlx_solver(rows) >>> p,x = d.to_milp() # needs sage.numerical.mip >>> p # needs sage.numerical.mip Boolean Program (no objective, 4 variables, ... constraints) >>> x # needs sage.numerical.mip MIPVariable with 4 binary components
In the reduction, the boolean variable x_i is True if and only if the i-th row is in the solution:
sage: p.show() # needs sage.numerical.mip Maximization: Constraints:... one 1 in 0-th column: 1.0 <= x_0 + x_1 <= 1.0 one 1 in 1-th column: 1.0 <= x_0 + x_2 <= 1.0 one 1 in 2-th column: 1.0 <= x_0 + x_1 <= 1.0 one 1 in 3-th column: 1.0 <= x_3 <= 1.0 Variables: x_0 is a boolean variable (min=0.0, max=1.0) x_1 is a boolean variable (min=0.0, max=1.0) x_2 is a boolean variable (min=0.0, max=1.0) x_3 is a boolean variable (min=0.0, max=1.0)
>>> from sage.all import * >>> p.show() # needs sage.numerical.mip Maximization: <BLANKLINE> <BLANKLINE> Constraints:... one 1 in 0-th column: 1.0 <= x_0 + x_1 <= 1.0 one 1 in 1-th column: 1.0 <= x_0 + x_2 <= 1.0 one 1 in 2-th column: 1.0 <= x_0 + x_1 <= 1.0 one 1 in 3-th column: 1.0 <= x_3 <= 1.0 Variables: x_0 is a boolean variable (min=0.0, max=1.0) x_1 is a boolean variable (min=0.0, max=1.0) x_2 is a boolean variable (min=0.0, max=1.0) x_3 is a boolean variable (min=0.0, max=1.0)
Using some optional MILP solvers:
sage: d.to_milp('gurobi') # optional - gurobi sage_numerical_backends_gurobi, needs sage.numerical.mip (Boolean Program (no objective, 4 variables, 4 constraints), MIPVariable with 4 binary components)
>>> from sage.all import * >>> d.to_milp('gurobi') # optional - gurobi sage_numerical_backends_gurobi, needs sage.numerical.mip (Boolean Program (no objective, 4 variables, 4 constraints), MIPVariable with 4 binary components)
- to_sat_solver(solver=None)[source]#
Return the SAT solver solving an equivalent problem.
Note that row index \(i\) in the dancing links solver corresponds to the boolean variable index \(ì+1\) for the SAT solver to avoid the variable index \(0\).
See also
sage.sat.solvers.satsolver
.INPUT:
solver
– string orNone
(default:None
), possible values include'picosat'
,'cryptominisat'
,'LP'
,'glucose'
,'glucose-syrup'
.
OUTPUT:
SAT solver instance
EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2], [0,2], [1], [3]] sage: x = dlx_solver(rows) sage: s = x.to_sat_solver() # needs sage.sat
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)], [Integer(0),Integer(2)], [Integer(1)], [Integer(3)]] >>> x = dlx_solver(rows) >>> s = x.to_sat_solver() # needs sage.sat
Using some optional SAT solvers:
sage: x.to_sat_solver('cryptominisat') # optional - pycryptosat # needs sage.sat CryptoMiniSat solver: 4 variables, 7 clauses.
>>> from sage.all import * >>> x.to_sat_solver('cryptominisat') # optional - pycryptosat # needs sage.sat CryptoMiniSat solver: 4 variables, 7 clauses.
- sage.combinat.matrices.dancing_links.dlx_solver(rows)[source]#
Internal-use wrapper for the dancing links C++ code.
EXAMPLES:
sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2]] sage: rows+= [[0,2]] sage: rows+= [[1]] sage: rows+= [[3]] sage: x = dlx_solver(rows) sage: print(x.search()) 1 sage: print(x.get_solution()) [3, 0] sage: print(x.search()) 1 sage: print(x.get_solution()) [3, 1, 2] sage: print(x.search()) 0
>>> from sage.all import * >>> from sage.combinat.matrices.dancing_links import dlx_solver >>> rows = [[Integer(0),Integer(1),Integer(2)]] >>> rows+= [[Integer(0),Integer(2)]] >>> rows+= [[Integer(1)]] >>> rows+= [[Integer(3)]] >>> x = dlx_solver(rows) >>> print(x.search()) 1 >>> print(x.get_solution()) [3, 0] >>> print(x.search()) 1 >>> print(x.get_solution()) [3, 1, 2] >>> print(x.search()) 0