Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/matrices/dancing_links.pyx
4076 views
1
"""
2
Dancing Links internal pyx code
3
"""
4
#*****************************************************************************
5
# Copyright (C) 2008 Carlo Hamalainen <[email protected]>,
6
#
7
# Distributed under the terms of the GNU General Public License (GPL)
8
#
9
# This code is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
# General Public License for more details.
13
#
14
# The full text of the GPL is available at:
15
#
16
# http://www.gnu.org/licenses/
17
#*****************************************************************************
18
19
#clang c++
20
21
import sys
22
23
include "../../ext/python_list.pxi"
24
include "../../ext/stdsage.pxi"
25
include "../../ext/python_int.pxi"
26
include "../../ext/python_ref.pxi"
27
28
cdef extern from "dancing_links_c.h":
29
ctypedef struct vector_int "std::vector<int>":
30
void (* push_back)(int elem)
31
void clear()
32
int at(size_t loc)
33
int size()
34
35
ctypedef struct vector_vector_int "std::vector<vector<int> >":
36
void (* push_back)(vector_int elem)
37
38
ctypedef struct dancing_links:
39
vector_int solution
40
void add_rows(vector_vector_int rows)
41
int search()
42
void freemem()
43
44
dancing_links* dancing_links_construct "Construct<dancing_links>"(void *mem)
45
void dancing_links_destruct "Destruct<dancing_links>"(dancing_links *mem)
46
47
from sage.rings.integer cimport Integer
48
49
cdef class dancing_linksWrapper:
50
cdef dancing_links x
51
cdef object rows
52
53
def __init__(self, rows):
54
"""
55
Initialize our wrapper (self.x) as an actual C++ object.
56
57
We must pass a list of rows at start up. There are no methods
58
for resetting the list of rows, so this class acts as a one-time
59
executor of the C++ code.
60
61
TESTS:
62
sage: rows = [[0,1,2], [1, 2]]
63
sage: x = make_dlxwrapper(dumps(rows))
64
sage: loads(x.__reduce__()[1][0])
65
[[0, 1, 2], [1, 2]]
66
"""
67
pass
68
69
# Note that the parameters to __cinit__, if any, must be identical to __init__
70
# This is due to the way Python constructs class instance.
71
def __cinit__(self, rows):
72
self.rows = PyList_New(len(rows))
73
dancing_links_construct(&self.x)
74
self.add_rows(rows)
75
76
def __dealloc__(self):
77
self.x.freemem()
78
dancing_links_destruct(&self.x)
79
80
def __str__(self):
81
"""
82
The string representation of this wrapper is just the list of
83
rows as supplied at startup.
84
85
TESTS:
86
sage: rows = [[0,1,2]]
87
sage: print make_dlxwrapper(dumps(rows)).__str__()
88
[[0, 1, 2]]
89
"""
90
91
return self.rows.__str__()
92
93
def __reduce__(self):
94
"""
95
This is used when pickling.
96
97
TESTS:
98
sage: rows = [[0,1,2]]
99
sage: x = make_dlxwrapper(dumps(rows))
100
sage: loads(x.__reduce__()[1][0])
101
[[0, 1, 2]]
102
"""
103
# A comment from sage/rings/integer.pyx:
104
105
# This single line below took me HOURS to figure out.
106
# It is the *trick* needed to pickle Cython extension types.
107
# The trick is that you must put a pure Python function
108
# as the first argument, and that function must return
109
# the result of unpickling with the argument in the second
110
# tuple as input. All kinds of problems happen
111
# if we don't do this.
112
#return sage.rings.integer.make_integer, (self.str(32),)
113
114
import sage.combinat.matrices.dancing_links
115
from sage.all import dumps
116
return sage.combinat.matrices.dancing_links.make_dlxwrapper, (dumps(self.rows),)
117
118
def __richcmp__(dancing_linksWrapper left, dancing_linksWrapper right, int op):
119
"""
120
Two dancing_linksWrapper objects are equal if they were
121
initialised using the same row list.
122
123
TESTS:
124
sage: from sage.combinat.matrices.dancing_links import dlx_solver
125
sage: rows = [[0,1,2]]
126
sage: X = dlx_solver(rows)
127
sage: Z = dlx_solver(rows)
128
sage: rows += [[2]]
129
sage: Y = dlx_solver(rows)
130
sage: X == Z
131
1
132
sage: X == Y
133
0
134
"""
135
136
cdef int equal
137
equal = left.rows == right.rows
138
139
if op == 2: # ==
140
return equal
141
elif op == 3: # !=
142
return not equal
143
else:
144
return NotImplemented
145
146
def dumps(self):
147
"""
148
TESTS:
149
sage: from sage.combinat.matrices.dancing_links import dlx_solver
150
sage: rows = [[0,1,2]]
151
sage: X = dlx_solver(rows)
152
sage: X == loads(dumps(X))
153
1
154
sage: rows += [[2]]
155
sage: Y = dlx_solver(rows)
156
sage: Y == loads(dumps(X))
157
0
158
"""
159
return self.rows.dumps()
160
161
def add_rows(self, rows):
162
"""
163
Initialize our instance of dancing_links with the given rows.
164
This is for internal use by dlx_solver.
165
166
This doctest tests add_rows vicariously!
167
168
TESTS:
169
sage: from sage.combinat.matrices.dancing_links import dlx_solver
170
sage: rows = [[0,1,2]]
171
sage: rows+= [[0,2]]
172
sage: rows+= [[1]]
173
sage: rows+= [[3]]
174
sage: x = dlx_solver(rows)
175
sage: print x.search()
176
1
177
"""
178
179
cdef vector_int v
180
cdef vector_vector_int vv
181
182
cdef int i = 0
183
184
for row in rows:
185
v.clear()
186
187
Py_INCREF(row);
188
PyList_SET_ITEM(self.rows, i, row)
189
i += 1
190
191
for x in row:
192
v.push_back(x)
193
194
vv.push_back(v)
195
196
self.x.add_rows(vv)
197
198
def get_solution(self):
199
"""
200
After calling search(), we can extract a solution
201
from the instance variable self.x.solution, a C++ vector<int>
202
listing the rows that make up the current solution.
203
204
TESTS:
205
sage: from sage.combinat.matrices.dancing_links import dlx_solver
206
sage: rows = [[0,1,2]]
207
sage: rows+= [[0,2]]
208
sage: rows+= [[1]]
209
sage: rows+= [[3]]
210
sage: x = dlx_solver(rows)
211
sage: print x.search()
212
1
213
sage: print x.get_solution()
214
[3, 0]
215
"""
216
217
s = []
218
for i in range(self.x.solution.size()):
219
s.append(self.x.solution.at(i))
220
221
return s
222
223
def search(self):
224
"""
225
TESTS:
226
sage: from sage.combinat.matrices.dancing_links import dlx_solver
227
sage: rows = [[0,1,2]]
228
sage: rows+= [[0,2]]
229
sage: rows+= [[1]]
230
sage: rows+= [[3]]
231
sage: x = dlx_solver(rows)
232
sage: print x.search()
233
1
234
sage: print x.get_solution()
235
[3, 0]
236
"""
237
238
x = self.x.search()
239
240
return x
241
242
def dlx_solver(rows):
243
"""
244
Internal-use wrapper for the dancing links C++ code.
245
246
EXAMPLES:
247
sage: from sage.combinat.matrices.dancing_links import dlx_solver
248
sage: rows = [[0,1,2]]
249
sage: rows+= [[0,2]]
250
sage: rows+= [[1]]
251
sage: rows+= [[3]]
252
sage: x = dlx_solver(rows)
253
sage: print x.search()
254
1
255
sage: print x.get_solution()
256
[3, 0]
257
sage: print x.search()
258
1
259
sage: print x.get_solution()
260
[3, 1, 2]
261
sage: print x.search()
262
0
263
"""
264
265
cdef dancing_linksWrapper dlw
266
267
dlw = dancing_linksWrapper(rows)
268
#dlw.add_rows(rows)
269
270
return dlw
271
272
273
def make_dlxwrapper(s):
274
"""
275
Create a dlx wrapper from a Python *string* s.
276
This is used in unpickling. We expect s to be dumps(rows) where
277
rows is the list of rows used to instantiate the object.
278
279
TESTS:
280
sage: rows = [[0,1,2]]
281
sage: x = make_dlxwrapper(dumps(rows))
282
sage: print x.__str__()
283
[[0, 1, 2]]
284
"""
285
286
from sage.all import loads
287
288
cdef dancing_linksWrapper dlw
289
dlw = dancing_linksWrapper(loads(s))
290
return dlw
291
292
293