this repo has no description
1/* Python Interface for MiniZinc constraint modelling
2 * Author:
3 * Tai Tran <tai.tran@student.adelaide.edu.au>
4 * Supervisor:
5 * Guido Tack <guido.tack@monash.edu>
6 */
7
8#include "pyinterface.h"
9
10using namespace MiniZinc;
11using namespace std;
12
13static PyObject* Mzn_Call(PyObject* self, PyObject* args) {
14 const char* name;
15 PyObject* variableTuple;
16 PyTypeObject* returnType;
17 if (!PyArg_ParseTuple(args, "sO|O", &name, &variableTuple, &returnType)) {
18 PyErr_SetString(PyExc_TypeError,
19 "MiniZinc: Mzn_Call: Accepts 2 values: a string, a list of minizinc variable");
20 PyErr_Print();
21 return NULL;
22 }
23
24 if (!PyList_Check(variableTuple)) {
25 PyErr_SetString(PyExc_TypeError, "MiniZinc: Mzn_Call: Second argument must be a list");
26 return NULL;
27 }
28
29 long len = PyList_GET_SIZE(variableTuple);
30 vector<Expression*> expressionList(len);
31 for (long i = 0; i != len; ++i) {
32 PyObject* pyval = PyList_GET_ITEM(variableTuple, i);
33 if (PyObject_TypeCheck(pyval, &MznObject_Type)) {
34 expressionList[i] = MznObject_get_e(reinterpret_cast<MznObject*>(pyval));
35 } else {
36 Type type;
37 vector<pair<int, int> > dimList;
38 expressionList[i] = python_to_minizinc(pyval, type, dimList);
39 if (expressionList[i] == NULL) {
40 MZN_PYERR_SET_STRING(PyExc_RuntimeError,
41 "MiniZinc: MznCall: Second argument, item at position %li must be a "
42 "MiniZinc Object or Python int/float/string/list/tuple",
43 i);
44 return NULL;
45 }
46 }
47 }
48
49 PyObject* ret = MznExpression_new(&MznExpression_Type, NULL, NULL);
50
51 reinterpret_cast<MznExpression*>(ret)->e = new Call(Location(), string(name), expressionList);
52
53 return ret;
54}
55
56static PyObject* Mzn_Id(PyObject* self, PyObject* args) {
57 const char* name;
58 if (!PyArg_ParseTuple(args, "s", &name)) {
59 PyErr_SetString(PyExc_TypeError, "MiniZinc: Mzn_Id: Argument must be a string");
60 return NULL;
61 }
62 PyObject* ret = MznExpression_new(&MznExpression_Type, NULL, NULL);
63 reinterpret_cast<MznExpression*>(ret)->e = new Id(Location(), name, NULL);
64 return ret;
65}
66
67static PyObject* Mzn_at(PyObject* self, PyObject* args) {
68 PyObject* Py_array;
69 PyObject* Py_idx;
70 if (!PyArg_ParseTuple(args, "OO", &Py_array, &Py_idx)) {
71 PyErr_SetString(PyExc_TypeError, "MiniZinc: at: Parsing error");
72 return NULL;
73 }
74
75 if (!PyObject_TypeCheck(Py_array, &MznExpression_Type)) {
76 PyErr_SetString(PyExc_TypeError, "MiniZinc: at: First argument must be a MiniZinc Expression");
77 return NULL;
78 }
79
80 if (!PyList_Check(Py_idx)) {
81 PyErr_SetString(PyExc_TypeError, "MiniZinc: at: Second argument must be a list of indices");
82 return NULL;
83 }
84
85 Py_ssize_t n = PyList_GET_SIZE(Py_idx);
86
87 vector<Expression*> idx(n);
88 for (Py_ssize_t i = 0; i != n; ++i) {
89 PyObject* obj = PyList_GetItem(Py_idx, i);
90#if PY_MAJOR_VERSION < 3
91 if (PyInt_Check(obj)) {
92 long index = PyInt_AS_LONG(obj);
93 idx[i] = IntLit::a(IntVal(index));
94 } else
95#endif
96 if (PyLong_Check(obj)) {
97 int overflow;
98 long long index = PyLong_AsLongLongAndOverflow(obj, &overflow);
99 if (overflow) {
100 MZN_PYERR_SET_STRING(PyExc_OverflowError, "MiniZinc: at: Index at pos %li overflowed", i);
101 return NULL;
102 }
103 idx[i] = IntLit::a(IntVal(index));
104 } else if (PyObject_TypeCheck(obj, &MznExpression_Type)) {
105 idx[i] = reinterpret_cast<MznExpression*>(obj)->e;
106 } else {
107 PyErr_SetString(PyExc_TypeError,
108 "MiniZinc: ArrayAccess: Indices must be integers or MiniZinc Expression");
109 return NULL;
110 }
111 }
112
113 MznExpression* ret =
114 reinterpret_cast<MznExpression*>(MznExpression_new(&MznExpression_Type, NULL, NULL));
115 ret->e = new ArrayAccess(Location(), reinterpret_cast<MznExpression*>(Py_array)->e, idx);
116 return reinterpret_cast<PyObject*>(ret);
117}
118
119static PyObject* Mzn_UnOp(PyObject* self, PyObject* args) {
120 /*
121 enum UnOpType {
122 UOT_NOT, // 0
123 UOT_PLUS, // 1
124 UOT_MINUS // 2
125 };*/
126 PyObject* r;
127 unsigned int op;
128 if (!PyArg_ParseTuple(args, "IO", &op, &r)) {
129 PyErr_SetString(PyExc_TypeError,
130 "MiniZinc: Mzn_UnOp: Requires a MiniZinc object and an integer");
131 return NULL;
132 }
133 Expression* rhs;
134
135 if (PyObject_TypeCheck(r, &MznExpression_Type)) {
136 rhs = reinterpret_cast<MznExpression*>(r)->e;
137 } else if (PyBool_Check(r)) {
138 rhs = new BoolLit(Location(), PyObject_IsTrue(r));
139 } else
140#if PY_MAJOR_VERSION < 3
141 if (PyInt_Check(r)) {
142 rhs = IntLit::a(IntVal(PyInt_AS_LONG(r)));
143 } else
144#endif
145 if (PyLong_Check(r)) {
146 int overflow;
147 long long c_val = PyLong_AsLongLongAndOverflow(r, &overflow);
148 if (overflow) {
149 PyErr_SetString(PyExc_OverflowError, "MiniZinc: Mzn_UnOp: Object is overflowed");
150 return NULL;
151 }
152 rhs = IntLit::a(IntVal(c_val));
153 } else if (PyFloat_Check(r)) {
154 rhs = new FloatLit(Location(), PyFloat_AS_DOUBLE(r));
155 } else if (PyUnicode_Check(r)) {
156 rhs = new StringLit(Location(), string(PyUnicode_AsUTF8(r)));
157 } else {
158 PyErr_SetString(PyExc_TypeError,
159 "MiniZinc: Mzn_UnOp: Object must be a Python value or a MiniZinc object");
160 return NULL;
161 }
162
163 GCLock Lock;
164
165 PyObject* ret = MznExpression_new(&MznExpression_Type, NULL, NULL);
166 reinterpret_cast<MznExpression*>(ret)->e = new UnOp(Location(), static_cast<UnOpType>(op), rhs);
167 return ret;
168}
169
170/*
171 * Description: Creates a minizinc BinOp expression
172 * Note: Need an outer GCLock for this to work
173 */
174static PyObject* Mzn_BinOp(PyObject* self, PyObject* args) {
175 /*
176 enum BinOpType {
177 BOT_PLUS, // 0
178 BOT_MINUS, // 1
179 BOT_MULT, // 2
180 BOT_DIV, // 3
181 BOT_IDIV, // 4
182 BOT_MOD, // 5
183 BOT_LE, // 6
184 BOT_LQ, // 7
185 BOT_GR, // 8
186 BOT_GQ, // 9
187 BOT_EQ, //10
188 BOT_NQ, //11
189 BOT_IN, //12
190 BOT_SUBSET, //13
191 BOT_SUPERSET, //14
192 BOT_UNION, //15
193 BOT_DIFF, //16
194 BOT_SYMDIFF, //17
195 BOT_INTERSECT, //18
196 BOT_PLUSPLUS, //19
197 BOT_EQUIV, //20
198 BOT_IMPL, //21
199 BOT_RIMPL, //22
200 BOT_OR, //23
201 BOT_AND, //24
202 BOT_XOR, //25
203 BOT_DOTDOT //26
204 };*/
205 PyObject* PyPre[2];
206 unsigned int op;
207 if (!PyArg_ParseTuple(args, "OIO", &PyPre[0], &op, &PyPre[1])) {
208 PyErr_SetString(PyExc_TypeError,
209 "MiniZinc: Mzn_BinOp: Requires two MiniZinc objects and an integer");
210 return NULL;
211 }
212 Expression* pre[2];
213 // pre[0]: lhs;
214 // pre[1]: rhs;
215 for (int i = 0; i != 2; ++i) {
216 if (PyObject_TypeCheck(PyPre[i], &MznExpression_Type)) {
217 pre[i] = reinterpret_cast<MznExpression*>(PyPre[i])->e;
218 } else if (PyBool_Check(PyPre[i])) {
219 pre[i] = new BoolLit(Location(), PyObject_IsTrue(PyPre[i]));
220 } else
221#if PY_MAJOR_VERSION < 3
222 if (PyInt_Check(PyPre[i])) {
223 pre[i] = IntLit::a(IntVal(PyInt_AS_LONG(PyPre[i])));
224 } else
225#endif
226
227 if (PyLong_Check(PyPre[i])) {
228 int overflow;
229 long long c_val = PyLong_AsLongLongAndOverflow(PyPre[i], &overflow);
230 if (overflow) {
231 PyErr_SetString(PyExc_OverflowError, "MiniZinc: Mzn_UnOp: Object is overflowed");
232 return NULL;
233 }
234 pre[i] = IntLit::a(IntVal(c_val));
235 } else if (PyFloat_Check(PyPre[i])) {
236 pre[i] = new FloatLit(Location(), PyFloat_AS_DOUBLE(PyPre[i]));
237 } else if (PyUnicode_Check(PyPre[i])) {
238 pre[i] = new StringLit(Location(), string(PyUnicode_AsUTF8(PyPre[i])));
239 } else {
240 if (i == 0)
241 PyErr_SetString(
242 PyExc_TypeError,
243 "MiniZinc: Mzn_BinOp: Left hand side object must be a Python value or MiniZinc object");
244 else
245 PyErr_SetString(PyExc_TypeError,
246 "MiniZinc: Mzn_BinOp: Right hand side object must be a Python value or "
247 "MiniZinc object");
248 return NULL;
249 }
250 }
251
252 GCLock Lock;
253
254 PyObject* ret = MznExpression_new(&MznExpression_Type, NULL, NULL);
255 reinterpret_cast<MznExpression*>(ret)->e =
256 (new BinOp(Location(), pre[0], static_cast<BinOpType>(op), pre[1]));
257 return ret;
258}
259
260static PyObject* Mzn_load(PyObject* self, PyObject* args, PyObject* keywds) {
261 PyObject* model = MznModel_new(&MznModel_Type, NULL, NULL);
262 if (MznModel_init(reinterpret_cast<MznModel*>(model), NULL) < 0) return NULL;
263 if (MznModel_load(reinterpret_cast<MznModel*>(model), args, keywds) == NULL) return NULL;
264 return model;
265}
266
267static PyObject* Mzn_load_from_string(PyObject* self, PyObject* args, PyObject* keywds) {
268 PyObject* model = MznModel_new(&MznModel_Type, NULL, NULL);
269 if (model == NULL) return NULL;
270 if (MznModel_init(reinterpret_cast<MznModel*>(model), NULL) < 0) return NULL;
271 if (MznModel_load_from_string(reinterpret_cast<MznModel*>(model), args, keywds) == NULL)
272 return NULL;
273 return model;
274}
275
276static PyObject* Mzn_retrieveNames(PyObject* self, PyObject* args) {
277 PyObject* boolfuncs = PyDict_New();
278 PyObject* annfuncs = PyDict_New();
279 PyObject* annvars = PyList_New(0);
280 PyObject* libName = NULL;
281
282 {
283 Py_ssize_t n = PyTuple_GET_SIZE(args);
284 if (n > 1) {
285 PyErr_SetString(PyExc_TypeError, "MiniZinc: Mzn_retrieveNames: accepts at most 1 argument");
286 return NULL;
287 } else if (n == 1) {
288 libName = PyTuple_GET_ITEM(args, 0);
289 if (PyObject_IsTrue(libName)) {
290 if (!PyUnicode_Check(libName)) {
291 PyErr_SetString(PyExc_TypeError,
292 "MiniZinc: Mzn_retrieveNames: first argument must be a string");
293 return NULL;
294 }
295 } else
296 libName = NULL;
297 }
298 }
299
300 // If a library name is specified here, it means that this function is called at least once
301 // already. If that's the case, functions in globals.mzn and stdlib.mzn will be already defined,
302 // so we dont want to reinclude it
303 bool include_global_mzn = (libName == NULL);
304
305 MznModel* tempModel = reinterpret_cast<MznModel*>(MznModel_new(&MznModel_Type, NULL, NULL));
306
307 if (MznModel_init(tempModel, libName) != 0) {
308 return NULL;
309 }
310 CollectBoolFuncNames bool_fv(boolfuncs, include_global_mzn);
311 CollectAnnNames ann_fv(annfuncs, annvars, include_global_mzn);
312 iterItems(bool_fv, tempModel->_m);
313 iterItems(ann_fv, tempModel->_m);
314 MznModel_dealloc(tempModel);
315
316 PyObject* dict = PyDict_New();
317 PyDict_SetItemString(dict, "boolfuncs", boolfuncs);
318 PyDict_SetItemString(dict, "annfuncs", annfuncs);
319 PyDict_SetItemString(dict, "annvars", annvars);
320
321 Py_DECREF(boolfuncs);
322 Py_DECREF(annfuncs);
323 Py_DECREF(annvars);
324 return dict;
325}
326
327#if PY_MAJOR_VERSION >= 3
328
329#define INITERROR return NULL
330
331// PyObject*
332PyMODINIT_FUNC PyInit_minizinc_internal(void)
333#else
334#define INITERROR return
335
336PyMODINIT_FUNC initminizinc_internal(void)
337
338#endif
339
340{
341#if PY_MAJOR_VERSION >= 3
342 PyObject* module = PyModule_Create(&moduledef);
343#else
344 PyObject* module = Py_InitModule3("minizinc_internal", Mzn_methods,
345 "A python interface for MiniZinc constraint modeling");
346#endif
347
348 if (module == NULL) INITERROR;
349
350 if (PyType_Ready(&MznObject_Type) < 0) INITERROR;
351 Py_INCREF(&MznObject_Type);
352 PyModule_AddObject(module, "Object", reinterpret_cast<PyObject*>(&MznObject_Type));
353
354 if (PyType_Ready(&MznSetIter_Type) < 0) INITERROR;
355 Py_INCREF(&MznSetIter_Type);
356 PyModule_AddObject(module, "Set_Iter", reinterpret_cast<PyObject*>(&MznSetIter_Type));
357
358 if (PyType_Ready(&MznExpression_Type) < 0) INITERROR;
359 Py_INCREF(&MznExpression_Type);
360 PyModule_AddObject(module, "Expression", reinterpret_cast<PyObject*>(&MznExpression_Type));
361
362 if (PyType_Ready(&MznAnnotation_Type) < 0) INITERROR;
363 Py_INCREF(&MznAnnotation_Type);
364 PyModule_AddObject(module, "Annotation", reinterpret_cast<PyObject*>(&MznAnnotation_Type));
365
366 if (PyType_Ready(&MznSet_Type) < 0) INITERROR;
367 Py_INCREF(&MznSet_Type);
368 PyModule_AddObject(module, "Set", reinterpret_cast<PyObject*>(&MznSet_Type));
369
370 if (PyType_Ready(&MznVarSet_Type) < 0) INITERROR;
371 Py_INCREF(&MznVarSet_Type);
372 PyModule_AddObject(module, "VarSet", reinterpret_cast<PyObject*>(&MznVarSet_Type));
373
374 if (PyType_Ready(&MznModel_Type) < 0) INITERROR;
375 Py_INCREF(&MznModel_Type);
376 PyModule_AddObject(module, "Model", reinterpret_cast<PyObject*>(&MznModel_Type));
377
378 if (PyType_Ready(&PyMznSolver_Type) < 0) INITERROR;
379 Py_INCREF(&PyMznSolver_Type);
380 PyModule_AddObject(module, "Solver", reinterpret_cast<PyObject*>(&PyMznSolver_Type));
381
382#if PY_MAJOR_VERSION >= 3
383 return module;
384#endif
385}