1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
Jip J. Dekker f2a1c4e389 Squashed 'software/mza/' content from commit f970a59b17
git-subtree-dir: software/mza
git-subtree-split: f970a59b177c13ca3dd8aaef8cc6681d83b7e813
2021-07-11 16:34:30 +10:00

490 lines
16 KiB
C++

#include "global.h"
using namespace MiniZinc;
using namespace std;
inline PyObject* c_to_py_number(long long c_val) {
#if PY_MAJOR_VERSION < 3
if (c_val > LONG_MIN || c_val < LONG_MAX)
return PyInt_FromLong(static_cast<long>(c_val));
else
#endif
return PyLong_FromLongLong(c_val);
}
inline long long py_to_c_number(PyObject* py_val) {
#if PY_MAJOR_VERSION < 3
if (PyInt_Check(py_val)) {
return PyInt_AS_LONG(py_val);
} else
#endif
if (PyLong_Check(py_val)) {
int overflow;
long long c_val = PyLong_AsLongLongAndOverflow(py_val, &overflow);
if (overflow) {
PyErr_SetString(PyExc_OverflowError, "Python value is overflown");
return -1;
}
return c_val;
} else {
PyErr_SetString(PyExc_TypeError, "Python value must be an integer");
return -1;
}
}
inline long long py_to_c_number(PyObject* py_val, int* overflow) {
#if PY_MAJOR_VERSION < 3
if (PyInt_Check(py_val)) {
*overflow = 0;
return PyInt_AS_LONG(py_val);
} else
#endif
if (PyLong_Check(py_val)) {
long long c_val = PyLong_AsLongLongAndOverflow(py_val, overflow);
if (*overflow) {
PyErr_SetString(PyExc_OverflowError, "Python value is overflown");
return -1;
}
*overflow = 0;
return c_val;
} else {
*overflow = 0;
PyErr_SetString(PyExc_TypeError, "Python value must be an integer");
return -1;
}
}
bool compareType(const Type& type1, const Type& type2) {
return (type1.bt() == type2.bt() && type1.st() == type2.st() && type1.dim() == type2.dim());
}
string typePresentation(const Type& type) {
string baseTypeString;
switch (type.bt()) {
case Type::BT_BOOL:
baseTypeString = "bool";
break;
case Type::BT_INT:
baseTypeString = "int";
break;
case Type::BT_FLOAT:
baseTypeString = "float";
break;
case Type::BT_STRING:
baseTypeString = "string";
break;
default:
baseTypeString = "UNSET";
}
if (type.st() == Type::ST_SET)
return "a set of " + baseTypeString;
else if (type.dim() == 0) {
if (type.bt() == Type::BT_INT)
return "an " + baseTypeString;
else
return "a " + baseTypeString;
} else {
string ret = "an array of [";
for (int i = type.dim(); i--;) {
ret += baseTypeString;
if (i - 1 > 0) ret += ", ";
}
ret += ']';
return ret;
}
}
inline PyObject* one_dim_minizinc_to_python(Expression* e, const Type& type) {
Env env(NULL);
if (type.st() == Type::ST_SET) {
IntSetVal* isv = eval_intset(env.envi(), e);
MznSet* newSet = reinterpret_cast<MznSet*>(MznSet_new(&MznSet_Type, NULL, NULL));
for (IntSetRanges isr(isv); isr(); ++isr) {
newSet->push(isr.min().toInt(), isr.max().toInt());
}
return reinterpret_cast<PyObject*>(newSet);
} else
switch (type.bt()) {
case Type::BT_BOOL:
return PyBool_FromLong(eval_bool(env.envi(), e));
case Type::BT_INT:
return c_to_py_number(eval_int(env.envi(), e).toInt());
case Type::BT_FLOAT:
return PyFloat_FromDouble(eval_float(env.envi(), e));
case Type::BT_STRING: {
string temp(eval_string(env.envi(), e));
return PyUnicode_FromString(temp.c_str());
}
default:
throw logic_error("MiniZinc: one_dim_minizinc_to_python: Unexpected type code");
}
}
/*
* Convert minizinc expression to python value
*/
PyObject* minizinc_to_python(VarDecl* vd) {
GCLock Lock;
if (vd == NULL) {
PyErr_SetString(PyExc_ValueError, "MiniZinc_to_Python: Value is not set");
return NULL;
}
Type type = vd->type();
if (type.dim() == 0) {
return one_dim_minizinc_to_python(vd->e(), type);
} else {
Env env(NULL);
ArrayLit* al = eval_par(env.envi(), vd->e())->cast<ArrayLit>();
int dim = vd->type().dim();
// Maximum size of each dimension
vector<Py_ssize_t> dmax;
// Current size of each dimension
vector<Py_ssize_t> d;
// p[0] holds the final array, p[1+] builds up p[0]
vector<PyObject*> p(dim);
for (int i = 0; i < dim; ++i) {
d.push_back(0);
Py_ssize_t dtemp = al->max(i) - al->min(i) + 1;
dmax.push_back(dtemp);
p[i] = PyList_New(dtemp);
}
int i = dim - 1;
// next item to be put onto the final array
unsigned int currentPos = 0;
do {
PyList_SetItem(p[i], d[i], one_dim_minizinc_to_python(al->v()[currentPos], type));
currentPos++;
d[i]++;
while (d[i] >= dmax[i] && i > 0) {
PyList_SetItem(p[i - 1], d[i - 1], p[i]);
Py_DECREF(p[i]);
d[i] = 0;
p[i] = PyList_New(dmax[i]);
i--;
d[i]++;
}
i = dim - 1;
} while (d[0] < dmax[0]);
for (int i = 1; i < dim; i++) Py_DECREF(p[i]);
return p[0];
}
}
inline Expression* one_dim_python_to_minizinc(PyObject* pvalue, Type::BaseType& code) {
/*enum BaseType { BT_TOP, BT_BOOL, BT_INT, BT_FLOAT, BT_STRING, BT_ANN,
BT_BOT, BT_UNKNOWN };*/
switch (code) {
case Type::BT_UNKNOWN:
if (PyObject_TypeCheck(pvalue, &MznObject_Type)) {
return MznObject_get_e(reinterpret_cast<MznObject*>(pvalue));
// return reinterpret_cast<MznObject*>(pvalue)->e();
} else if (PyBool_Check(pvalue)) {
BT_BOOLEAN_PROCESS:
Expression* rhs = new BoolLit(Location(), PyObject_IsTrue(pvalue));
code = Type::BT_BOOL;
return rhs;
} else
#if PY_MAJOR_VERSION < 3
if (PyInt_Check(pvalue)) {
BT_INTEGER_PROCESS_2X_VERSION:
Expression* rhs = IntLit::a(IntVal(PyInt_AS_LONG(pvalue)));
code = Type::BT_INT;
return rhs;
} else
#endif
if (PyLong_Check(pvalue)) {
BT_INTEGER_PROCESS:
int overflow;
Expression* rhs = IntLit::a(IntVal(PyLong_AsLongLongAndOverflow(pvalue, &overflow)));
if (overflow) {
if (overflow > 0)
PyErr_SetString(PyExc_OverflowError,
"MiniZinc: Python integer value is larger than 2^63-1");
else
PyErr_SetString(PyExc_OverflowError,
"MiniZinc: Python integer value is smaller than -2^63");
return NULL;
}
code = Type::BT_INT;
return rhs;
} else if (PyFloat_Check(pvalue)) {
BT_FLOAT_PROCESS:
Expression* rhs = new FloatLit(Location(), PyFloat_AS_DOUBLE(pvalue));
code = Type::BT_FLOAT;
return rhs;
} else if (PyUnicode_Check(pvalue)) {
BT_STRING_PROCESS:
Expression* rhs = new StringLit(Location(), PyUnicode_AsUTF8(pvalue));
code = Type::BT_STRING;
return rhs;
} else {
PyErr_SetString(PyExc_TypeError, "MiniZinc: Unexpected python type");
return NULL;
}
break;
case Type::BT_INT:
#if PY_MAJOR_VERSION < 3
if (PyInt_Check(pvalue))
goto BT_INTEGER_PROCESS_2X_VERSION;
else
#endif
if (!PyLong_Check(pvalue)) {
PyErr_SetString(
PyExc_TypeError,
"MiniZinc: Object in an array must be of the same type: Expected an integer");
return NULL;
}
goto BT_INTEGER_PROCESS;
case Type::BT_FLOAT:
if (!PyFloat_Check(pvalue)) {
PyErr_SetString(PyExc_TypeError,
"MiniZinc: Object in an array must be of the same type: Expected an float");
return NULL;
}
goto BT_FLOAT_PROCESS;
case Type::BT_STRING:
if (!PyUnicode_Check(pvalue)) {
PyErr_SetString(
PyExc_TypeError,
"MiniZinc: Object in an array must be of the same type: Expected an string");
return NULL;
}
goto BT_STRING_PROCESS;
case Type::BT_BOOL:
if (!PyBool_Check(pvalue)) {
PyErr_SetString(
PyExc_TypeError,
"MiniZinc: Object in an array must be of the same type: Expected an boolean");
return NULL;
}
goto BT_BOOLEAN_PROCESS;
default:
throw std::invalid_argument("MiniZinc: Internal Error: Received unexpected base type code");
}
}
Expression* python_to_minizinc(PyObject* pvalue, const ASTExprVec<TypeInst>& ranges) {
if (PyObject_TypeCheck(pvalue, &MznObject_Type)) {
return MznObject_get_e(reinterpret_cast<MznObject*>(pvalue));
} else if (PyList_Check(pvalue)) {
vector<Py_ssize_t> dimensions;
vector<PyObject*> simpleArray;
if (getList(pvalue, dimensions, simpleArray, 0) == -1)
// getList should already set the error string
return NULL;
if (ranges.size() != dimensions.size()) {
PyErr_SetString(
PyExc_ValueError,
"MiniZinc: python_to_minizinc: size of declared array and actual array not matched");
return NULL;
}
vector<Expression*> callArgument(dimensions.size() + 1);
vector<Expression*> onedArray(simpleArray.size());
stringstream buffer;
buffer << "array" << dimensions.size() << "d";
string callName(buffer.str());
for (int i = 0; i != dimensions.size(); ++i) {
Expression* domain = ranges[i]->domain();
if (domain == NULL) {
Expression* e0 = IntLit::a(IntVal(1));
Expression* e1 = IntLit::a(IntVal(dimensions[i]));
callArgument[i] = new BinOp(Location(), e0, BOT_DOTDOT, e1);
} else {
callArgument[i] = domain;
}
}
Type::BaseType code = Type::BT_UNKNOWN;
for (int i = 0; i != simpleArray.size(); ++i) {
PyObject* temp = simpleArray[i];
Expression* rhs = one_dim_python_to_minizinc(temp, code);
if (rhs == NULL) return NULL;
onedArray[i] = rhs;
}
callArgument[dimensions.size()] = new ArrayLit(Location(), onedArray);
Expression* rhs = new Call(Location(), callName, callArgument);
return rhs;
} else {
Type::BaseType code = Type::BT_UNKNOWN;
Expression* rhs = one_dim_python_to_minizinc(pvalue, code);
return rhs;
}
}
Expression* python_to_minizinc(PyObject* pvalue, Type& returnType,
vector<pair<int, int> >& dimList) {
Type::BaseType code = Type::BT_UNKNOWN;
if (PyObject_TypeCheck(pvalue, &MznObject_Type)) {
returnType = Type::parsetint();
return MznObject_get_e(reinterpret_cast<MznObject*>(pvalue));
} else if (PyList_Check(pvalue)) {
vector<Py_ssize_t> dimensions;
vector<PyObject*> simpleArray;
if (getList(pvalue, dimensions, simpleArray, 0) == -1) {
// getList should set error string already
return NULL;
}
if (dimList.empty())
for (int i = 0; i != dimensions.size(); i++)
dimList.push_back(pair<Py_ssize_t, Py_ssize_t>(0, dimensions[i] - 1));
else {
if (dimList.size() != dimensions.size()) {
PyErr_SetString(
PyExc_ValueError,
"MiniZinc: python_to_minizinc: size of declared and actual array not matched");
return NULL;
}
for (int i = 0; i != dimensions.size(); i++) {
if ((dimList[i].second - (dimList[i].first) + 1) != dimensions[i]) {
PyErr_SetString(
PyExc_ValueError,
"MiniZinc: python_to_minizinc: size of each dimension of python array not matched");
return NULL;
}
}
}
vector<Expression*> v;
for (int i = 0; i != simpleArray.size(); ++i) {
PyObject* temp = simpleArray[i];
Expression* rhs = one_dim_python_to_minizinc(temp, code);
if (rhs == NULL) return NULL;
v.push_back(rhs);
}
returnType = Type();
returnType.bt(code);
returnType.dim(dimList.size());
Expression* rhs = new ArrayLit(Location(), v, dimList);
return rhs;
} else {
Expression* rhs = one_dim_python_to_minizinc(pvalue, code);
returnType = Type();
returnType.bt(code);
return rhs;
}
}
vector<TypeInst*> pydim_to_minizinc_ranges(PyObject* pydim, int& errorOccurred) {
if (!PyList_Check(pydim)) {
errorOccurred = 1;
PyErr_SetString(PyExc_TypeError, "MiniZinc: python_to_dimList: argument must be a python list");
return vector<TypeInst*>();
}
Py_ssize_t dim = PyList_GET_SIZE(pydim);
vector<TypeInst*> ranges(dim);
for (Py_ssize_t i = 0; i != dim; ++i) {
PyObject* temp = PyList_GET_ITEM(pydim, i);
if (!PyList_Check(temp)) {
for (int j = 0; j != i; ++j) delete ranges[j];
ranges.clear();
errorOccurred = 1;
PyErr_SetString(PyExc_TypeError,
"MiniZinc: python_to_dimList: objects in the list must be range lists");
return ranges;
}
PyObject* Py_bound[2];
Py_bound[0] = PyList_GetItem(temp, 0);
Py_bound[1] = PyList_GetItem(temp, 1);
if (PyErr_Occurred()) {
for (int j = 0; j != i; ++j) delete ranges[j];
ranges.clear();
errorOccurred = 1;
PyErr_SetString(PyExc_TypeError,
"MiniZinc: python_to_dimList: a range must consist of 2 values");
return ranges;
}
long long c_bound[2];
for (int k = 0; k != 2; ++k) {
#if PY_MAJOR_VERSION < 3
if (PyInt_Check(Py_bound[k]))
c_bound[k] = PyInt_AS_LONG(Py_bound[k]);
else
#endif
if (PyLong_Check(Py_bound[k])) {
int overflow;
c_bound[k] = PyLong_AsLongLongAndOverflow(Py_bound[k], &overflow);
if (overflow) {
switch (k) {
case 0:
MZN_PYERR_SET_STRING(
PyExc_OverflowError,
"MiniZinc: python_to_dimList: Range at pos %i: First argument is overflown", i);
break;
case 1:
MZN_PYERR_SET_STRING(
PyExc_OverflowError,
"MiniZinc: python_to_dimList: Range at pos %i: Second argument is overflown", i);
break;
default:
throw logic_error("pydim_to_dimList: Unexpected iterator value");
}
for (int j = 0; j != i; ++j) delete ranges[j];
ranges.clear();
errorOccurred = 1;
return ranges;
}
} else {
switch (k) {
case 0:
MZN_PYERR_SET_STRING(PyExc_OverflowError,
"MiniZinc: python_to_dimList: Range at pos %i: First argument "
"type is mismatched: expected a number",
i);
break;
case 1:
MZN_PYERR_SET_STRING(PyExc_OverflowError,
"MiniZinc: python_to_dimList: Range at pos %i: Second argument "
"type is mismatched: expected a number",
i);
break;
default:
throw logic_error("pydim_to_dimList: Unexpected iterator value");
}
for (int j = 0; j != i; ++j) delete ranges[j];
ranges.clear();
errorOccurred = 1;
return ranges;
}
}
if (c_bound[0] > c_bound[1]) swap(c_bound[0], c_bound[1]);
Expression* e0 = IntLit::a(IntVal(c_bound[0]));
Expression* e1 = IntLit::a(IntVal(c_bound[1]));
Expression* domain = new BinOp(Location(), e0, BOT_DOTDOT, e1);
ranges[i] = new TypeInst(Location(), Type(), domain);
}
errorOccurred = 0;
return ranges;
}
int getList(PyObject* value, vector<Py_ssize_t>& dimensions, vector<PyObject*>& simpleArray,
const int layer) {
for (Py_ssize_t i = 0; i < PyList_Size(value); i++) {
if (dimensions.size() <= layer) {
dimensions.push_back(PyList_Size(value));
} else if (dimensions[layer] != PyList_Size(value)) {
PyErr_SetString(PyExc_ValueError,
"MiniZinc: Inconsistency in size of multidimensional array");
return -1;
}
PyObject* li = PyList_GetItem(value, i);
if (PyList_Check(li)) {
if (getList(li, dimensions, simpleArray, layer + 1) == -1) {
return -1;
}
} else {
simpleArray.push_back(li);
}
}
return 0;
}