|
| 1 | +// -*- C++ -*- |
| 2 | + |
| 3 | +////////////////////////////////////////////////////////////////////////////// |
| 4 | +// Converting NLopt/C++ exceptions to Python exceptions |
| 5 | + |
| 6 | +%{ |
| 7 | +#ifndef Py_LIMITED_API |
| 8 | + |
| 9 | +#define ExceptionSubclass(EXCNAME, EXCDOC) \ |
| 10 | + static PyTypeObject MyExc_ ## EXCNAME = { \ |
| 11 | + PyVarObject_HEAD_INIT(NULL, 0) \ |
| 12 | + "nlopt." # EXCNAME, \ |
| 13 | + sizeof(PyBaseExceptionObject), \ |
| 14 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ |
| 15 | + Py_TPFLAGS_DEFAULT, \ |
| 16 | + PyDoc_STR(EXCDOC) \ |
| 17 | + }; \ |
| 18 | + static void init_ ## EXCNAME(PyObject *m) { \ |
| 19 | + MyExc_ ## EXCNAME .tp_base = (PyTypeObject *) PyExc_Exception; \ |
| 20 | + PyType_Ready(&MyExc_ ## EXCNAME); \ |
| 21 | + Py_INCREF(&MyExc_ ## EXCNAME); \ |
| 22 | + PyModule_AddObject(m, # EXCNAME, (PyObject *) &MyExc_ ## EXCNAME); \ |
| 23 | + } |
| 24 | + |
| 25 | + |
| 26 | +ExceptionSubclass(ForcedStop, |
| 27 | + "Python version of nlopt::forced_stop exception.") |
| 28 | + |
| 29 | +ExceptionSubclass(RoundoffLimited, |
| 30 | + "Python version of nlopt::roundoff_limited exception.") |
| 31 | + |
| 32 | +#endif |
| 33 | +%} |
| 34 | + |
| 35 | +%init %{ |
| 36 | +#ifndef Py_LIMITED_API |
| 37 | + init_ForcedStop(m); |
| 38 | + init_RoundoffLimited(m); |
| 39 | +#endif |
| 40 | +%} |
| 41 | +%pythoncode %{ |
| 42 | +try: |
| 43 | + ForcedStop = _nlopt.ForcedStop |
| 44 | + RoundoffLimited = _nlopt.RoundoffLimited |
| 45 | +except AttributeError: |
| 46 | + ForcedStop = RuntimeError |
| 47 | + RoundoffLimited = RuntimeError |
| 48 | +__version__ = str(_nlopt.version_major())+'.'+str(_nlopt.version_minor())+'.'+str(_nlopt.version_bugfix()) |
| 49 | +%} |
| 50 | + |
| 51 | +%typemap(throws) std::bad_alloc %{ |
| 52 | + PyErr_SetString(PyExc_MemoryError, ($1).what()); |
| 53 | + SWIG_fail; |
| 54 | +%} |
| 55 | + |
| 56 | +%typemap(throws) nlopt::forced_stop %{ |
| 57 | + if (!PyErr_Occurred()) |
| 58 | +#ifndef Py_LIMITED_API |
| 59 | + PyErr_SetString((PyObject*)&MyExc_ForcedStop, "NLopt forced stop"); |
| 60 | +#else |
| 61 | + PyErr_SetString(PyExc_RuntimeError, "NLopt forced stop"); |
| 62 | +#endif |
| 63 | + SWIG_fail; |
| 64 | +%} |
| 65 | + |
| 66 | +%typemap(throws) nlopt::roundoff_limited %{ |
| 67 | + if (!PyErr_Occurred()) |
| 68 | +#ifndef Py_LIMITED_API |
| 69 | + PyErr_SetString((PyObject*)&MyExc_RoundoffLimited, "NLopt roundoff-limited"); |
| 70 | +#else |
| 71 | + PyErr_SetString(PyExc_RuntimeError, "NLopt roundoff-limited"); |
| 72 | +#endif |
| 73 | + SWIG_fail; |
| 74 | +%} |
| 75 | + |
| 76 | +////////////////////////////////////////////////////////////////////////////// |
| 77 | + |
| 78 | +%{ |
| 79 | +#define SWIG_FILE_WITH_INIT |
| 80 | +%} |
| 81 | +%include "numpy.i" |
| 82 | +%init %{ |
| 83 | + import_array(); |
| 84 | +%} |
| 85 | +%numpy_typemaps(double, NPY_DOUBLE, unsigned) |
| 86 | + |
| 87 | +////////////////////////////////////////////////////////////////////////////// |
| 88 | +// numpy.i does not include maps for std::vector<double>, so I add them here, |
| 89 | +// taking advantage of the conversion functions provided by numpy.i |
| 90 | + |
| 91 | +// Typemap for input arguments of type const std::vector<double> & |
| 92 | +%typecheck(SWIG_TYPECHECK_POINTER, fragment="NumPy_Macros") |
| 93 | + const std::vector<double> & |
| 94 | +{ |
| 95 | + $1 = is_array($input) || PySequence_Check($input); |
| 96 | +} |
| 97 | +%typemap(in, fragment="NumPy_Fragments") |
| 98 | + const std::vector<double> & |
| 99 | +(PyArrayObject* array=NULL, int is_new_object=0, std::vector<double> arrayv) |
| 100 | +{ |
| 101 | + npy_intp size[1] = { -1 }; |
| 102 | + array = obj_to_array_allow_conversion($input, NPY_DOUBLE, &is_new_object); |
| 103 | + if (!array || !require_dimensions(array, 1) || |
| 104 | + !require_size(array, size, 1)) SWIG_fail; |
| 105 | + arrayv = std::vector<double>(array_size(array,0)); |
| 106 | + $1 = &arrayv; |
| 107 | + { |
| 108 | + double *arr_data = (double *) array_data(array); |
| 109 | + int arr_i, arr_s = array_stride(array,0) / sizeof(double); |
| 110 | + int arr_sz = array_size(array,0); |
| 111 | + for (arr_i = 0; arr_i < arr_sz; ++arr_i) |
| 112 | + arrayv[arr_i] = arr_data[arr_i * arr_s]; |
| 113 | + } |
| 114 | +} |
| 115 | +%typemap(freearg) |
| 116 | + const std::vector<double> & |
| 117 | +{ |
| 118 | + if (is_new_object$argnum && array$argnum) |
| 119 | + { Py_DECREF(array$argnum); } |
| 120 | +} |
| 121 | + |
| 122 | +// Typemap for return values of type std::vector<double> |
| 123 | +%typemap(out, fragment="NumPy_Fragments") std::vector<double> |
| 124 | +{ |
| 125 | + npy_intp sz = $1.size(); |
| 126 | + $result = PyArray_SimpleNew(1, &sz, NPY_DOUBLE); |
| 127 | + std::memcpy(array_data($result), $1.empty() ? NULL : &$1[0], |
| 128 | + sizeof(double) * sz); |
| 129 | +} |
| 130 | + |
| 131 | +////////////////////////////////////////////////////////////////////////////// |
| 132 | +// Wrapper for objective function callbacks |
| 133 | + |
| 134 | +%{ |
| 135 | +static void *free_pyfunc(void *p) { Py_DECREF((PyObject*) p); return p; } |
| 136 | +static void *dup_pyfunc(void *p) { Py_INCREF((PyObject*) p); return p; } |
| 137 | + |
| 138 | +#if NPY_API_VERSION < 0x00000007 |
| 139 | +# define NPY_ARRAY_C_CONTIGUOUS NPY_C_CONTIGUOUS |
| 140 | +# define NPY_ARRAY_ALIGNED NPY_ALIGNED |
| 141 | +#endif |
| 142 | + |
| 143 | +static double func_python(unsigned n, const double *x, double *grad, void *f) |
| 144 | +{ |
| 145 | + npy_intp sz = npy_intp(n), sz0 = 0, stride1 = sizeof(double); |
| 146 | + PyObject *xpy = PyArray_New(&PyArray_Type, 1, &sz, NPY_DOUBLE, &stride1, |
| 147 | + const_cast<double*>(x), // not NPY_WRITEABLE |
| 148 | + 0, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_ALIGNED, NULL); |
| 149 | + PyObject *gradpy = grad |
| 150 | + ? PyArray_SimpleNewFromData(1, &sz, NPY_DOUBLE, grad) |
| 151 | + : PyArray_SimpleNew(1, &sz0, NPY_DOUBLE); |
| 152 | + |
| 153 | + PyObject *arglist = Py_BuildValue("OO", xpy, gradpy); |
| 154 | + PyObject *result = PyEval_CallObject((PyObject *) f, arglist); |
| 155 | + Py_DECREF(arglist); |
| 156 | + |
| 157 | + Py_DECREF(gradpy); |
| 158 | + Py_DECREF(xpy); |
| 159 | + |
| 160 | + double val = HUGE_VAL; |
| 161 | + if (PyErr_Occurred()) { |
| 162 | + Py_XDECREF(result); |
| 163 | + throw nlopt::forced_stop(); // just stop, don't call PyErr_Clear() |
| 164 | + } |
| 165 | + else if (result && PyFloat_Check(result)) { |
| 166 | + val = PyFloat_AsDouble(result); |
| 167 | + Py_DECREF(result); |
| 168 | + } |
| 169 | + else if (result && PyLong_Check(result)) { |
| 170 | + val = PyLong_AsUnsignedLong(result); |
| 171 | + Py_DECREF(result); |
| 172 | + } |
| 173 | + else { |
| 174 | + Py_XDECREF(result); |
| 175 | + throw std::invalid_argument("invalid result passed to nlopt"); |
| 176 | + } |
| 177 | + return val; |
| 178 | +} |
| 179 | + |
| 180 | +static void mfunc_python(unsigned m, double *result, |
| 181 | + unsigned n, const double *x, double *grad, void *f) |
| 182 | +{ |
| 183 | + npy_intp nsz = npy_intp(n), msz = npy_intp(m); |
| 184 | + npy_intp mnsz[2] = {msz, nsz}; |
| 185 | + npy_intp sz0 = 0, stride1 = sizeof(double); |
| 186 | + PyObject *xpy = PyArray_New(&PyArray_Type, 1, &nsz, NPY_DOUBLE, &stride1, |
| 187 | + const_cast<double*>(x), // not NPY_WRITEABLE |
| 188 | + 0, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_ALIGNED, NULL); |
| 189 | + PyObject *rpy = PyArray_SimpleNewFromData(1, &msz, NPY_DOUBLE, result); |
| 190 | + PyObject *gradpy = grad |
| 191 | + ? PyArray_SimpleNewFromData(2, mnsz, NPY_DOUBLE, grad) |
| 192 | + : PyArray_SimpleNew(1, &sz0, NPY_DOUBLE); |
| 193 | + |
| 194 | + PyObject *arglist = Py_BuildValue("OOO", rpy, xpy, gradpy); |
| 195 | + PyObject *res = PyEval_CallObject((PyObject *) f, arglist); |
| 196 | + Py_XDECREF(res); |
| 197 | + Py_DECREF(arglist); |
| 198 | + |
| 199 | + Py_DECREF(gradpy); |
| 200 | + Py_DECREF(rpy); |
| 201 | + Py_DECREF(xpy); |
| 202 | + |
| 203 | + if (PyErr_Occurred()) { |
| 204 | + throw nlopt::forced_stop(); // just stop, don't call PyErr_Clear() |
| 205 | + } |
| 206 | +} |
| 207 | +%} |
| 208 | + |
| 209 | +%typemap(in)(nlopt::func f, void *f_data, nlopt_munge md, nlopt_munge mc) { |
| 210 | + $1 = func_python; |
| 211 | + $2 = dup_pyfunc((void*) $input); |
| 212 | + $3 = free_pyfunc; |
| 213 | + $4 = dup_pyfunc; |
| 214 | +} |
| 215 | +%typecheck(SWIG_TYPECHECK_POINTER)(nlopt::func f, void *f_data, nlopt_munge md, nlopt_munge mc) { |
| 216 | + $1 = PyCallable_Check($input); |
| 217 | +} |
| 218 | + |
| 219 | +%typemap(in)(nlopt::mfunc mf, void *f_data, nlopt_munge md, nlopt_munge mc) { |
| 220 | + $1 = mfunc_python; |
| 221 | + $2 = dup_pyfunc((void*) $input); |
| 222 | + $3 = free_pyfunc; |
| 223 | + $4 = dup_pyfunc; |
| 224 | +} |
| 225 | +%typecheck(SWIG_TYPECHECK_POINTER)(nlopt::mfunc mf, void *f_data, nlopt_munge md, nlopt_munge mc) { |
| 226 | + $1 = PyCallable_Check($input); |
| 227 | +} |
0 commit comments