diff --git a/.github/build_real.sh b/.github/build_real.sh
index 0482dcc30..abb1b3c51 100755
--- a/.github/build_real.sh
+++ b/.github/build_real.sh
@@ -11,6 +11,10 @@ fi
mv ~/.config/pip/constraints.txt ~/.config/pip/constraints.txt.bkup
touch ~/.config/pip/constraints.txt
+# set $PKG_CONFIG_PATH so pkg-config can find ipopt
+# only necessary if IPOPT is installed outside of conda
+export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$IPOPT_DIR/lib/pkgconfig
+
pip install .[optview,testing] -v
# move pip constraints file back
diff --git a/.github/environment.yml b/.github/environment.yml
index b23fb74a7..bd57cd368 100644
--- a/.github/environment.yml
+++ b/.github/environment.yml
@@ -2,7 +2,6 @@ dependencies:
# build
- python >=3.9
- numpy >=2.0
- - ipopt
- swig
- meson >=1.3.2
- compilers
@@ -17,6 +16,7 @@ dependencies:
- mdolab-baseclasses >=1.3.1
- scipy >=1.7
- sqlitedict >=1.6
+ - cyipopt
# testing
- parameterized
- testflo
diff --git a/.github/windows.yaml b/.github/windows.yaml
index 4f600ccf3..5255487ec 100644
--- a/.github/windows.yaml
+++ b/.github/windows.yaml
@@ -27,7 +27,6 @@ jobs:
displayName: Install mamba and update environment
- script: |
- set IPOPT_DIR=%CONDA_PREFIX%\Library
set CC=cl
set FC=flang
set CC_LD=link
diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml
index 3d4ef09c9..440fee202 100644
--- a/.github/workflows/windows-build.yml
+++ b/.github/workflows/windows-build.yml
@@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- numpy_version: ["1.21.6", "1.25.2"]
+ numpy_version: ["1.21.6", "1.25.2", "2.1.3"]
steps:
- uses: actions/checkout@v4
- uses: conda-incubator/setup-miniconda@v3
@@ -39,8 +39,6 @@ jobs:
:: set fortran compiler, flang 5 activation doesn't seem to do it
set FC=flang.exe
- set MESON_ARGS=-Dipopt_dir=%CONDA_PREFIX%\Library\
-
python -m build -n -x .
pip install --no-deps --no-index --find-links dist pyoptsparse
diff --git a/doc/install.rst b/doc/install.rst
index 4b1ea597a..fe1b5424c 100644
--- a/doc/install.rst
+++ b/doc/install.rst
@@ -184,19 +184,6 @@ An ``environment.yml`` file is provided in the ``pyoptsparse`` repo:
conda config --env --set channel_priority strict
conda env update -f .github\environment.yml
- conda install libpgmath
-
-Next, we need to tell the compiler where to find IPOPT:
-
-.. tabs::
-
- .. code-tab:: bash Linux/OSX
-
- export IPOPT_DIR="$CONDA_PREFIX"
-
- .. code-tab:: powershell Windows
-
- set IPOPT_DIR=%CONDA_PREFIX%\Library
Finally, build the wheel and install it using pip:
diff --git a/doc/optimizers/IPOPT.rst b/doc/optimizers/IPOPT.rst
index dd2ff586c..e68aec044 100644
--- a/doc/optimizers/IPOPT.rst
+++ b/doc/optimizers/IPOPT.rst
@@ -4,71 +4,14 @@ IPOPT
=====
IPOPT (Interior Point OPTimizer) is an open source interior point optimizer, designed for large-scale nonlinear optimization.
The source code can be found `here `_.
-The latest version we support is 3.13.2.
+The latest version we support is 3.14.17.
Installation
------------
-IPOPT must be installed separately, then linked to pyOptSparse when building.
-For the full installation instructions, please see `their documentation `_.
-OpenMDAO also has a very helpful `script `_ which can be used to install IPOPT with other linear solvers.
-Here we explain a basic setup using MUMPS as the linear solver, together with METIS adapted from the OpenMDAO script.
-
-#. Download the tarball and extract it to ``$IPOPT_DIR`` which could be set to for example ``$HOME/packages/Ipopt``.
-
-#. Install METIS, which can be used to improve the performance of the MUMPS linear solver.
-
- .. code-block:: bash
-
- # build METIS
- cd $IPOPT_DIR
- git clone https://github.com/coin-or-tools/ThirdParty-Metis.git
- cd ThirdParty-Metis
- ./get.Metis
- ./configure --prefix=$IPOPT_DIR
- make
- make install
-
-#. Install MUMPS
-
- .. code-block:: bash
-
- # build MUMPS
- cd $IPOPT_DIR
- git clone https://github.com/coin-or-tools/ThirdParty-Mumps.git
- cd ThirdParty-Mumps
- ./get.Mumps
- ./configure --with-metis --with-metis-lflags="-L${IPOPT_DIR}/lib -lcoinmetis" \
- --with-metis-cflags="-I${IPOPT_DIR}/include -I${IPOPT_DIR}/include/coin-or -I${IPOPT_DIR}/include/coin-or/metis" \
- --prefix=$IPOPT_DIR CFLAGS="-I${IPOPT_DIR}/include -I${IPOPT_DIR}/include/coin-or -I${IPOPT_DIR}/include/coin-or/metis" \
- FCFLAGS="-I${IPOPT_DIR}/include -I${IPOPT_DIR}/include/coin-or -I${IPOPT_DIR}/include/coin-or/metis"
- make
- make install
-
-#. Build IPOPT
-
- .. code-block:: bash
-
- # build IPOPT
- cd $IPOPT_DIR
- mkdir build
- cd build
- ../configure --prefix=${IPOPT_DIR} --disable-java --with-mumps --with-mumps-lflags="-L${IPOPT_DIR}/lib -lcoinmumps" \
- --with-mumps-cflags="-I${IPOPT_DIR}/include/coin-or/mumps"
- make
- make install
-
-#. You must add the IPOPT library path to the ``LD_LIBRARY_PATH`` variable for things to work right.
- This could be done for example by adding the following to your ``.bashrc``:
-
- .. code-block:: bash
-
- export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$IPOPT_DIR/lib
-
- Furthermore, the environment variable ``$IPOPT_DIR`` must be set correctly in order to link to pyOptSparse.
- Alternatively, you can manually define the variables ``$IPOPT_LIB`` and ``$IPOPT_INC`` for the lib and include paths separately.
-
-
-#. Now clean build pyOptSparse. Verify that IPOPT works by running the relevant tests.
+IPOPT and its Python interface `cyipopt ` must be installed separately.
+Follow the instructions `here `_.
+OpenMDAO also has a very helpful `script `_ which can be used to install IPOPT with other linear solvers,
+but it does not install ``cyipopt`` for you.
Options
-------
diff --git a/meson_options.txt b/meson_options.txt
index 356ab893c..e41d6cc33 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,6 +1,3 @@
-option('ipopt_dir', type: 'string', value: '',
- description: 'Top-level dir for ipopt')
-
option('incdir_numpy', type: 'string', value: '',
description: 'Include directory for numpy. If left empty Meson will try to find it on its own.')
diff --git a/pyoptsparse/meson.build b/pyoptsparse/meson.build
index 0467ed190..7f1d811bc 100644
--- a/pyoptsparse/meson.build
+++ b/pyoptsparse/meson.build
@@ -66,7 +66,6 @@ inc_f2py = include_directories(incdir_f2py)
#)
subdir('pySNOPT')
-subdir('pyIPOPT')
subdir('pySLSQP')
subdir('pyCONMIN')
subdir('pyNLPQLP')
diff --git a/pyoptsparse/pyIPOPT/meson.build b/pyoptsparse/pyIPOPT/meson.build
index d26259d68..7ea72e616 100644
--- a/pyoptsparse/pyIPOPT/meson.build
+++ b/pyoptsparse/pyIPOPT/meson.build
@@ -1,60 +1,10 @@
-fs = import('fs')
-
-if get_option('ipopt_dir') != '' or fs.is_dir('Ipopt')
-
- ipopt_dir = ''
-
- if get_option('ipopt_dir') != ''
- ipopt_dir = get_option('ipopt_dir')
- elif fs.is_dir('Ipopt')
- ipopt_dir = fs.is_dir('Ipopt')
- endif
-
- ipopt_lib = []
- ipopt_idir = ''
-
- # Ipopt installs differently on some systems (i.e. Fedora)
- if fs.is_dir(ipopt_dir / 'lib')
- ipopt_lib = [ipopt_dir / 'lib']
- elif fs.is_dir(ipopt_dir / 'lib64')
- ipopt_lib = [ipopt_dir / 'lib64']
- endif
-
-
- if fs.is_dir(ipopt_dir / 'include' / 'coin-or')
- ipopt_idir = ipopt_dir / 'include' / 'coin-or'
- elif fs.is_dir(ipopt_dir / 'include' / 'coin')
- ipopt_idir = ipopt_dir / 'include' / 'coin'
- endif
-
- ipopt_dep = cc.find_library('ipopt-3', required: false, dirs: ipopt_lib) # only relevant on windows
- if not ipopt_dep.found()
- ipopt_dep = cc.find_library('ipopt', required: true, dirs: ipopt_lib)
- endif
-
- if fs.is_dir(ipopt_idir)
- ipopt_inc = include_directories(ipopt_idir)
- else
- error('IPOPT include directory not found: ', ipopt_dir)
- endif
-
- py3_target.extension_module('pyipoptcore',
- 'src/callback.c',
- 'src/pyipoptcoremodule.c',
- include_directories: [inc_np, 'src', ipopt_inc],
- dependencies : [py3_dep, ipopt_dep],
- subdir: 'pyoptsparse/pyIPOPT',
- link_language: 'c',
- install : false)
-endif
-
-#python_sources = [
+# python_sources = [
# '__init__.py',
# 'pyIPOPT.py',
-#]
-#
-#py3_target.install_sources(
+# ]
+
+# py3_target.install_sources(
# python_sources,
# pure: false,
# subdir: 'pyoptsparse/pyIPOPT'
-#)
+# )
diff --git a/pyoptsparse/pyIPOPT/pyIPOPT.py b/pyoptsparse/pyIPOPT/pyIPOPT.py
index 89f69fb5f..4f0de8bea 100644
--- a/pyoptsparse/pyIPOPT/pyIPOPT.py
+++ b/pyoptsparse/pyIPOPT/pyIPOPT.py
@@ -5,27 +5,20 @@
# Standard Python modules
import copy
import datetime
-import os
import time
# External modules
import numpy as np
+try:
+ # External modules
+ import cyipopt
+except ImportError:
+ cyipopt = None
+
# Local modules
from ..pyOpt_optimizer import Optimizer
-from ..pyOpt_utils import (
- ICOL,
- INFINITY,
- IROW,
- convertToCOO,
- extractRows,
- scaleRows,
- try_import_compiled_module_from_path,
-)
-
-# import the compiled module
-THIS_DIR = os.path.dirname(os.path.abspath(__file__))
-pyipoptcore = try_import_compiled_module_from_path("pyipoptcore", THIS_DIR)
+from ..pyOpt_utils import ICOL, INFINITY, IROW, convertToCOO, extractRows, scaleRows
class IPOPT(Optimizer):
@@ -42,9 +35,8 @@ def __init__(self, raiseError=True, options={}):
category = "Local Optimizer"
defOpts = self._getDefaultOptions()
informs = self._getInforms()
-
- if isinstance(pyipoptcore, str) and raiseError:
- raise ImportError(pyipoptcore)
+ if cyipopt is None and raiseError:
+ raise ImportError("Could not import cyipopt")
super().__init__(
name,
@@ -197,8 +189,8 @@ def __call__(
jac = extractRows(jac, indices) # Does reordering
scaleRows(jac, fact) # Perform logical scaling
else:
- blc = np.array(-INFINITY)
- buc = np.array(INFINITY)
+ blc = np.atleast_1d(-INFINITY)
+ buc = np.atleast_1d(INFINITY)
ncon = 1
jac = convertToCOO(jac) # Conver to coo format for IPOPT
@@ -214,73 +206,65 @@ def __call__(
jac["coo"][ICOL].copy().astype("int_"),
)
- # Define the 4 call back functions that ipopt needs:
- def eval_f(x, user_data=None):
- fobj, fail = self._masterFunc(x, ["fobj"])
- if fail == 1:
- fobj = np.array(np.NaN)
- elif fail == 2:
- self.userRequestedTermination = True
- return fobj
-
- def eval_g(x, user_data=None):
- fcon, fail = self._masterFunc(x, ["fcon"])
- if fail == 1:
- fcon = np.array(np.NaN)
- elif fail == 2:
- self.userRequestedTermination = True
- return fcon.copy()
-
- def eval_grad_f(x, user_data=None):
- gobj, fail = self._masterFunc(x, ["gobj"])
- if fail == 1:
- gobj = np.array(np.NaN)
- elif fail == 2:
- self.userRequestedTermination = True
- return gobj.copy()
-
- def eval_jac_g(x, flag, user_data=None):
- if flag:
- return copy.deepcopy(matStruct)
- else:
+ class CyIPOPTProblem:
+ # Define the 4 call back functions that ipopt needs:
+ def objective(_, x):
+ fobj, fail = self._masterFunc(x, ["fobj"])
+ if fail == 1:
+ fobj = np.array(np.nan)
+ elif fail == 2:
+ self.userRequestedTermination = True
+ return fobj
+
+ def constraints(_, x):
+ fcon, fail = self._masterFunc(x, ["fcon"])
+ if fail == 1:
+ fcon = np.full_like(fcon, np.nan)
+ elif fail == 2:
+ self.userRequestedTermination = True
+ return fcon.copy()
+
+ def gradient(_, x):
+ gobj, fail = self._masterFunc(x, ["gobj"])
+ if fail == 1:
+ gobj = np.full_like(gobj, np.nan)
+ elif fail == 2:
+ self.userRequestedTermination = True
+ return gobj.copy()
+
+ def jacobian(_, x):
gcon, fail = self._masterFunc(x, ["gcon"])
if fail == 1:
- gcon = np.array(np.NaN)
+ gcon = np.full_like(gcon, np.nan)
elif fail == 2:
self.userRequestedTermination = True
return gcon.copy()
- # Define intermediate callback. If this method returns false,
- # Ipopt will terminate with the User_Requested_Stop status.
- def eval_intermediate_callback(*args, **kwargs):
- if self.userRequestedTermination is True:
- return False
- else:
- return True
+ def jacobianstructure(_):
+ return copy.deepcopy(matStruct)
+
+ # Define intermediate callback. If this method returns false,
+ # Ipopt will terminate with the User_Requested_Stop status.
+ def intermediate(_, *args, **kwargs):
+ if self.userRequestedTermination is True:
+ return False
+ else:
+ return True
timeA = time.time()
- nnzj = len(matStruct[0])
- nnzh = 0
-
- nlp = pyipoptcore.create(
- len(xs),
- blx,
- bux,
- ncon,
- blc,
- buc,
- nnzj,
- nnzh,
- eval_f,
- eval_grad_f,
- eval_g,
- eval_jac_g,
- )
- nlp.set_intermediate_callback(eval_intermediate_callback)
+ nlp = cyipopt.Problem(
+ n=len(xs),
+ m=ncon,
+ problem_obj=CyIPOPTProblem(),
+ lb=blx,
+ ub=bux,
+ cl=blc,
+ cu=buc,
+ )
self._set_ipopt_options(nlp)
- x, zl, zu, constraint_multipliers, obj, status = nlp.solve(xs)
+ x, info = nlp.solve(xs)
nlp.close()
optTime = time.time() - timeA
@@ -292,11 +276,11 @@ def eval_intermediate_callback(*args, **kwargs):
# Store Results
sol_inform = {}
- sol_inform["value"] = status
- sol_inform["text"] = self.informs[status]
+ sol_inform["value"] = info["status"]
+ sol_inform["text"] = self.informs[info["status"]]
# Create the optimization solution
- sol = self._createSolution(optTime, sol_inform, obj, x, multipliers=constraint_multipliers)
+ sol = self._createSolution(optTime, sol_inform, info["obj_val"], x, multipliers=info["mult_g"])
# Indicate solution finished
self.optProb.comm.bcast(-1, root=0)
@@ -317,11 +301,4 @@ def _set_ipopt_options(self, nlp):
# ---------------------------------------------
for name, value in self.options.items():
- if isinstance(value, str):
- nlp.str_option(name, value)
- elif isinstance(value, float):
- nlp.num_option(name, value)
- elif isinstance(value, int):
- nlp.int_option(name, value)
- else:
- print("invalid option type", type(value))
+ nlp.add_option(name, value)
diff --git a/pyoptsparse/pyIPOPT/src/callback.c b/pyoptsparse/pyIPOPT/src/callback.c
deleted file mode 100644
index 89c6a4301..000000000
--- a/pyoptsparse/pyIPOPT/src/callback.c
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * Copyright (c) 2008, Eric You Xu, Washington University All rights
- * reserved. Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following conditions
- * are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer. * Redistributions in
- * binary form must reproduce the above copyright notice, this list of
- * conditions and the following disclaimer in the documentation and/or other
- * materials provided with the distribution. * Neither the name of the
- * Washington University nor the names of its contributors may be used to
- * endorse or promote products derived from this software without specific
- * prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * Added "eval_intermediate_callback" by
- * OpenMDAO at NASA Glenn Research Center, 2010 and 2011
- *
- * Changed logger from code contributed by alanfalloon
-*/
-
-#include "hook.h"
-
-void logger(const char *fmt, ...)
-{
- if (user_log_level == VERBOSE) {
- va_list ap;
- va_start(ap, fmt);
- PySys_WriteStdout(fmt, ap);
- va_end(ap);
- PySys_WriteStdout("\n");
- }
-}
-
-Bool eval_intermediate_callback(Index alg_mod, /* 0 is regular, 1 is resto */
- Index iter_count, Number obj_value,
- Number inf_pr, Number inf_du,
- Number mu, Number d_norm,
- Number regularization_size,
- Number alpha_du, Number alpha_pr,
- Index ls_trials, UserDataPtr data)
-{
- //logger("[Callback:E]intermediate_callback");
-
- DispatchData *myowndata = (DispatchData *) data;
- UserDataPtr user_data = (UserDataPtr) myowndata->userdata;
-
- long result_as_long;
- Bool result_as_bool;
-
- PyObject *python_algmod = Py_BuildValue("i", alg_mod);
- PyObject *python_iter_count = Py_BuildValue("i", iter_count);
- PyObject *python_obj_value = Py_BuildValue("d", obj_value);
- PyObject *python_inf_pr = Py_BuildValue("d", inf_pr);
- PyObject *python_inf_du = Py_BuildValue("d", inf_du);
- PyObject *python_mu = Py_BuildValue("d", mu);
- PyObject *python_d_norm = Py_BuildValue("d", d_norm);
- PyObject *python_regularization_size =
- Py_BuildValue("d", regularization_size);
- PyObject *python_alpha_du = Py_BuildValue("d", alpha_du);
- PyObject *python_alpha_pr = Py_BuildValue("d", alpha_pr);
- PyObject *python_ls_trials = Py_BuildValue("i", ls_trials);
-
- PyObject *arglist = NULL;
-
- if (user_data != NULL)
- arglist = Py_BuildValue("(OOOOOOOOOOOO)",
- python_algmod,
- python_iter_count,
- python_obj_value,
- python_inf_pr,
- python_inf_du,
- python_mu,
- python_d_norm,
- python_regularization_size,
- python_alpha_du,
- python_alpha_pr,
- python_ls_trials,
- (PyObject *) user_data);
- else
- arglist = Py_BuildValue("(OOOOOOOOOOO)",
- python_algmod,
- python_iter_count,
- python_obj_value,
- python_inf_pr,
- python_inf_du,
- python_mu,
- python_d_norm,
- python_regularization_size,
- python_alpha_du,
- python_alpha_pr, python_ls_trials);
-
- PyObject *result =
- PyObject_CallObject(myowndata->eval_intermediate_callback_python,
- arglist);
-
- if (!result)
- PyErr_Print();
-
- result_as_long = PyLong_AsLong(result);
- result_as_bool = (Bool) result_as_long;
-
- Py_DECREF(result);
- Py_CLEAR(arglist);
- //logger("[Callback:R] intermediate_callback");
- return result_as_bool;
-}
-
-Bool
-eval_f(Index n, Number * x, Bool new_x, Number * obj_value, UserDataPtr data)
-{
- //logger("[Callback:E] eval_f");
-
- npy_intp dims[1];
- dims[0] = n;
-
- DispatchData *myowndata = (DispatchData *) data;
- UserDataPtr user_data = (UserDataPtr) myowndata->userdata;
-
- // import_array ();
-
- import_array1(FALSE);
- PyObject *arrayx =
- PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE, (char *)x);
- if (!arrayx)
- return FALSE;
-
- if (new_x && myowndata->apply_new_python) {
- /* Call the python function to applynew */
- PyObject *arg1;
- arg1 = Py_BuildValue("(O)", arrayx);
- PyObject *tempresult = PyObject_CallObject(
- myowndata->apply_new_python, arg1);
- if (tempresult == NULL) {
- logger("[Error] Python function apply_new returns NULL");
- PyErr_Print();
- Py_DECREF(arg1);
- return FALSE;
- }
- Py_DECREF(arg1);
- Py_DECREF(tempresult);
- }
-
- PyObject *arglist;
- if (user_data != NULL) {
- arglist = Py_BuildValue("(OO)", arrayx, (PyObject *) user_data);
- } else {
- arglist = Py_BuildValue("(O)", arrayx);
- }
-
- PyObject *result = PyObject_CallObject(myowndata->eval_f_python, arglist);
-
- if (result == NULL) {
- logger("[Error] Python function eval_f returns NULL");
- PyErr_Print();
- Py_DECREF(arrayx);
- Py_CLEAR(arglist);
- return FALSE;
- }
-
- *obj_value = PyFloat_AsDouble(result);
-
- if (PyErr_Occurred()) {
- logger("[Error] Python function eval_f returns non-PyFloat");
- PyErr_Print();
- Py_DECREF(result);
- Py_DECREF(arrayx);
- Py_CLEAR(arglist);
- return FALSE;
- }
-
- Py_DECREF(result);
- Py_DECREF(arrayx);
- Py_CLEAR(arglist);
- //logger("[Callback:R] eval_f");
- return TRUE;
-}
-
-Bool
-eval_grad_f(Index n, Number * x, Bool new_x, Number * grad_f, UserDataPtr data)
-{
- //logger("[Callback:E] eval_grad_f");
-
- DispatchData *myowndata = (DispatchData *) data;
- UserDataPtr user_data = (UserDataPtr) myowndata->userdata;
-
- if (myowndata->eval_grad_f_python == NULL)
- PyErr_Print();
-
- /* int dims[1]; */
- npy_intp dims[1];
- dims[0] = n;
- // import_array ();
-
- import_array1(FALSE);
-
- /*
- * PyObject *arrayx = PyArray_FromDimsAndData(1, dims, NPY_DOUBLE
- * , (char*) x);
- */
- PyObject *arrayx =
- PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE, (char *)x);
- if (!arrayx)
- return FALSE;
-
- if (new_x && myowndata->apply_new_python) {
- /* Call the python function to applynew */
- PyObject *arg1 = Py_BuildValue("(O)", arrayx);
- PyObject *tempresult = PyObject_CallObject(
- myowndata->apply_new_python, arg1);
- if (tempresult == NULL) {
- logger("[Error] Python function apply_new returns NULL");
- PyErr_Print();
- Py_DECREF(arg1);
- return FALSE;
- }
- Py_DECREF(arg1);
- Py_DECREF(tempresult);
- }
-
- PyObject *arglist;
- if (user_data != NULL)
- arglist = Py_BuildValue("(OO)", arrayx, (PyObject *) user_data);
- else
- arglist = Py_BuildValue("(O)", arrayx);
-
- PyArrayObject *result = (PyArrayObject *) PyObject_CallObject(
- myowndata->eval_grad_f_python, arglist);
-
- if (result == NULL) {
- logger("[Error] Python function eval_grad_f returns NULL");
- PyErr_Print();
- return FALSE;
- }
-
- if (!PyArray_Check(result)) {
- logger("[Error] Python function eval_grad_f returns non-PyArray");
- Py_DECREF(result);
- return FALSE;
- }
-
- double *tempdata = (double *)result->data;
- int i;
- for (i = 0; i < n; i++)
- grad_f[i] = tempdata[i];
-
- Py_DECREF(result);
- Py_CLEAR(arrayx);
- Py_CLEAR(arglist);
- //logger("[Callback:R] eval_grad_f");
- return TRUE;
-}
-
-Bool
-eval_g(Index n, Number * x, Bool new_x, Index m, Number * g, UserDataPtr data)
-{
-
- //logger("[Callback:E] eval_g");
-
- DispatchData *myowndata = (DispatchData *) data;
- UserDataPtr user_data = (UserDataPtr) myowndata->userdata;
-
- if (myowndata->eval_g_python == NULL)
- PyErr_Print();
- /* int dims[1]; */
- npy_intp dims[1];
- int i;
- double *tempdata;
-
- dims[0] = n;
- // import_array ();
-
- import_array1(FALSE);
-
- /*
- * PyObject *arrayx = PyArray_FromDimsAndData(1, dims, NPY_DOUBLE
- * , (char*) x);
- */
- PyObject *arrayx =
- PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE, (char *)x);
- if (!arrayx)
- return FALSE;
-
- if (new_x && myowndata->apply_new_python) {
- /* Call the python function to applynew */
- PyObject *arg1 = Py_BuildValue("(O)", arrayx);
- PyObject *tempresult = PyObject_CallObject(
- myowndata->apply_new_python, arg1);
- if (tempresult == NULL) {
- logger("[Error] Python function apply_new returns NULL");
- PyErr_Print();
- Py_DECREF(arg1);
- return FALSE;
- }
- Py_DECREF(arg1);
- Py_DECREF(tempresult);
- }
-
- PyObject *arglist;
- if (user_data != NULL)
- arglist = Py_BuildValue("(OO)", arrayx, (PyObject *) user_data);
- else
- arglist = Py_BuildValue("(O)", arrayx);
-
- PyArrayObject *result = (PyArrayObject *) PyObject_CallObject(
- myowndata->eval_g_python, arglist);
-
- if (result == NULL) {
- logger("[Error] Python function eval_g returns NULL");
- PyErr_Print();
- return FALSE;
- }
-
- if (!PyArray_Check(result)) {
- logger("[Error] Python function eval_g returns non-PyArray");
- Py_DECREF(result);
- return FALSE;
- }
-
- tempdata = (double *)result->data;
- for (i = 0; i < m; i++) {
- g[i] = tempdata[i];
- }
-
- Py_DECREF(result);
- Py_CLEAR(arrayx);
- Py_CLEAR(arglist);
- //logger("[Callback:R] eval_g");
- return TRUE;
-}
-
-Bool
-eval_jac_g(Index n, Number * x, Bool new_x,
- Index m, Index nele_jac,
- Index * iRow, Index * jCol, Number * values, UserDataPtr data)
-{
-
- //logger("[Callback:E] eval_jac_g");
-
- DispatchData *myowndata = (DispatchData *) data;
- UserDataPtr user_data = (UserDataPtr) myowndata->userdata;
-
- int i;
- long *rowd = NULL;
- long *cold = NULL;
-
- /* int dims[1]; */
- npy_intp dims[1];
- dims[0] = n;
-
- double *tempdata;
-
- if (myowndata->eval_grad_f_python == NULL) /* Why??? */
- PyErr_Print();
-
- if (values == NULL) {
- /* import_array (); */
- import_array1(FALSE);
-
- PyObject *arrayx =
- PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE,
- (char *)x);
- if (!arrayx)
- return FALSE;
-
- PyObject *arglist;
-
- if (user_data != NULL)
- arglist = Py_BuildValue("(OOO)",
- arrayx, Py_True,
- (PyObject *) user_data);
- else
- arglist = Py_BuildValue("(OO)", arrayx, Py_True);
-
- PyObject *result =
- PyObject_CallObject(myowndata->eval_jac_g_python, arglist);
- if (!result) {
-
- logger("[PyIPOPT] return from eval_jac_g is null\n");
- /* TODO: need to deal with reference counting here */
- return FALSE;
- }
- if (!PyTuple_Check(result)) {
- PyErr_Print();
- }
- PyArrayObject *row =
- (PyArrayObject *) PyTuple_GetItem(result, 0);
- PyArrayObject *col =
- (PyArrayObject *) PyTuple_GetItem(result, 1);
-
- if (!row || !col || !PyArray_Check(row) || !PyArray_Check(col)) {
- logger
- ("[Error] there are problems with row or col in eval_jac_g.\n");
- PyErr_Print();
- }
- rowd = (long *)row->data;
- cold = (long *)col->data;
-
- for (i = 0; i < nele_jac; i++) {
- iRow[i] = (Index) rowd[i];
- jCol[i] = (Index) cold[i];
- }
- Py_CLEAR(arrayx);
- Py_DECREF(result);
- Py_CLEAR(arglist);
- //logger("[Callback:R] eval_jac_g(1)");
- } else {
- PyObject *arrayx =
- PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE,
- (char *)x);
-
- if (!arrayx)
- return FALSE;
-
- if (new_x && myowndata->apply_new_python) {
- /* Call the python function to applynew */
- PyObject *arg1 = Py_BuildValue("(O)", arrayx);
- PyObject *tempresult =
- PyObject_CallObject(myowndata->apply_new_python,
- arg1);
- if (tempresult == NULL) {
- logger("[Error] Python function apply_new returns NULL");
- Py_DECREF(arg1);
- return FALSE;
- }
- Py_DECREF(arg1);
- Py_DECREF(tempresult);
- }
- PyObject *arglist;
- if (user_data != NULL)
- arglist = Py_BuildValue("(OOO)",
- arrayx, Py_False,
- (PyObject *) user_data);
- else
- arglist = Py_BuildValue("(OO)", arrayx, Py_False);
-
- PyArrayObject *result = (PyArrayObject *) PyObject_CallObject(
- myowndata->eval_jac_g_python, arglist);
-
- if (result == NULL) {
- logger("[Error] Python function eval_jac_g returns NULL");
- PyErr_Print();
- return FALSE;
- }
-
- if (!PyArray_Check(result)) {
- logger("[Error] Python function eval_jac_g returns non-PyArray");
- Py_DECREF(result);
- return FALSE;
- }
-
- /*
- * Code is buggy here. We assume that result is a double
- * array
- */
- assert(result->descr->type == 'd');
- tempdata = (double *)result->data;
-
- for (i = 0; i < nele_jac; i++)
- values[i] = tempdata[i];
-
- Py_DECREF(result);
- Py_CLEAR(arrayx);
- Py_CLEAR(arglist);
- //logger("[Callback:R] eval_jac_g(2)");
- }
- //logger("[Callback:R] eval_jac_g");
- return TRUE;
-}
-
-Bool
-eval_h(Index n, Number * x, Bool new_x, Number obj_factor,
- Index m, Number * lambda, Bool new_lambda,
- Index nele_hess, Index * iRow, Index * jCol,
- Number * values, UserDataPtr data)
-{
- //logger("[Callback:E] eval_h");
-
- DispatchData *myowndata = (DispatchData *) data;
- UserDataPtr user_data = (UserDataPtr) myowndata->userdata;
-
- int i;
- npy_intp dims[1];
- npy_intp dims2[1];
-
- if (myowndata->eval_h_python == NULL) {
- logger("[Error] There is no eval_h assigned");
- return FALSE;
- }
- if (values == NULL) {
- //logger("[Callback:E] eval_h (1a)");
- PyObject *newx = Py_True;
- PyObject *objfactor = Py_BuildValue("d", obj_factor);
- PyObject *lagrange = Py_True;
-
- PyObject *arglist;
-
- if (user_data != NULL) {
- arglist = Py_BuildValue(
- "(OOOOO)", newx, lagrange, objfactor, Py_True,
- (PyObject *) user_data);
- } else {
- arglist = Py_BuildValue(
- "(OOOO)", newx, lagrange, objfactor, Py_True);
- }
-
- if (arglist == NULL) {
- logger("[Error] failed to build arglist for eval_h");
- PyErr_Print();
- return FALSE;
- } else {
- logger("[Logspam] built arglist for eval_h");
- }
-
- PyObject *result = PyObject_CallObject(myowndata->eval_h_python, arglist);
-
- if (result == NULL) {
- logger("[Error] Python function eval_h returns NULL");
- PyErr_Print();
- return FALSE;
- } else {
- logger("[Logspam] Python function eval_h returns non-NULL");
- }
-
- int result_size = PyTuple_Size(result);
-
- if (result_size == -1) {
- logger("[Error] Python function eval_h returns non-PyTuple");
- Py_DECREF(result);
- return FALSE;
- }
-
- if (result_size != 2) {
- logger("[Error] Python function eval_h returns a tuple whose len != 2");
- Py_DECREF(result);
- return FALSE;
- }
-
- //logger("[Callback:E] eval_h (tuple is the right length)");
-
- PyArrayObject *row = (PyArrayObject *) PyTuple_GetItem(result, 0);
- PyArrayObject *col = (PyArrayObject *) PyTuple_GetItem(result, 1);
-
- long *rdata = (long *)row->data;
- long *cdata = (long *)col->data;
-
- for (i = 0; i < nele_hess; i++) {
- iRow[i] = (Index) rdata[i];
- jCol[i] = (Index) cdata[i];
- /*
- * logger("PyIPOPT_DEBUG %d, %d\n", iRow[i],
- * jCol[i]);
- */
- }
-
- //logger("[Callback:E] eval_h (clearing stuff now)");
-
- Py_DECREF(objfactor);
- Py_DECREF(result);
- Py_CLEAR(arglist);
- //logger("[Callback:R] eval_h (1b)");
- } else {
- //logger("[Callback:R] eval_h (2a)");
-
- PyObject *objfactor = Py_BuildValue("d", obj_factor);
-
- dims[0] = n;
- PyObject *arrayx =
- PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE,
- (char *)x);
- if (!arrayx)
- return FALSE;
-
- if (new_x && myowndata->apply_new_python) {
- /* Call the python function to applynew */
- PyObject *arg1 = Py_BuildValue("(O)", arrayx);
- PyObject *tempresult = PyObject_CallObject(
- myowndata->apply_new_python, arg1);
- if (tempresult == NULL) {
- logger("[Error] Python function apply_new returns NULL");
- PyErr_Print();
- Py_DECREF(arg1);
- return FALSE;
- }
- Py_DECREF(arg1);
- Py_DECREF(tempresult);
- }
- dims2[0] = m;
- PyObject *lagrangex = PyArray_SimpleNewFromData(
- 1, dims2, NPY_DOUBLE, (char *)lambda);
- if (!lagrangex)
- return FALSE;
-
- PyObject *arglist;
-
- if (user_data != NULL) {
- arglist = Py_BuildValue(
- "(OOOOO)", arrayx, lagrangex, objfactor, Py_False,
- (PyObject *) user_data);
- } else {
- arglist = Py_BuildValue(
- "(OOOO)", arrayx, lagrangex, objfactor, Py_False);
- }
- PyArrayObject *result = (PyArrayObject *) PyObject_CallObject(
- myowndata->eval_h_python, arglist);
-
- if (result == NULL) {
- logger("[Error] Python function eval_h returns NULL");
- PyErr_Print();
- return FALSE;
- }
-
- if (!PyArray_Check(result)) {
- logger("[Error] Python function eval_h returns non-PyArray");
- Py_DECREF(result);
- return FALSE;
- }
-
- double *tempdata = (double *)result->data;
- for (i = 0; i < nele_hess; i++) {
- values[i] = tempdata[i];
- }
- Py_CLEAR(arrayx);
- Py_CLEAR(lagrangex);
- Py_CLEAR(objfactor);
- Py_DECREF(result);
- Py_CLEAR(arglist);
- //logger("[Callback:R] eval_h (2b)");
- }
- return TRUE;
-}
diff --git a/pyoptsparse/pyIPOPT/src/hook.h b/pyoptsparse/pyIPOPT/src/hook.h
deleted file mode 100644
index 54058e41e..000000000
--- a/pyoptsparse/pyIPOPT/src/hook.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Author: Eric Xu
-// Licensed under BSD
-
-#include "Python.h"
-#include "IpStdCInterface.h"
-#include
-#include "numpy/arrayobject.h"
-
-#ifndef PY_IPOPT_HOOK_
-#define PY_IPOPT_HOOK_
-
-// A series of callback functions used by Ipopt C Interface
-Bool eval_f(Index n,
- Number * x, Bool new_x, Number * obj_value, UserDataPtr user_data);
-
-Bool eval_grad_f(Index n,
- Number * x,
- Bool new_x, Number * grad_f, UserDataPtr user_data);
-
-Bool eval_g(Index n,
- Number * x, Bool new_x, Index m, Number * g, UserDataPtr user_data);
-
-Bool eval_jac_g(Index n, Number * x, Bool new_x,
- Index m, Index nele_jac,
- Index * iRow, Index * jCol, Number * values,
- UserDataPtr user_data);
-
-Bool eval_h(Index n, Number * x, Bool new_x, Number obj_factor,
- Index m, Number * lambda, Bool new_lambda,
- Index nele_hess, Index * iRow, Index * jCol,
- Number * values, UserDataPtr user_data);
-
-Bool eval_intermediate_callback(Index alg_mod,
- Index iter_count, Number obj_value,
- Number inf_pr, Number inf_du,
- Number mu, Number d_norm,
- Number regularization_size,
- Number alpha_du, Number alpha_pr,
- Index ls_trials, UserDataPtr data);
-
-typedef struct {
- PyObject *eval_f_python;
- PyObject *eval_grad_f_python;
- PyObject *eval_g_python;
- PyObject *eval_jac_g_python;
- PyObject *eval_h_python;
- PyObject *apply_new_python;
- PyObject *eval_intermediate_callback_python;
- PyObject *userdata;
-} DispatchData;
-
-
-#if PY_MAJOR_VERSION < 3
-PyObject *problem_getattr(PyObject * self, char *attrname);
-#endif
-
-/* Logging */
-#define VERBOSE 2
-#define IPOPT_OUTPUT 1
-#define TERSE 0
-extern int user_log_level;
-void logger(const char *fmt, ...);
-
-typedef struct {
- PyObject_HEAD IpoptProblem nlp;
- DispatchData *data;
- Index n_variables;
- Index m_constraints;
-} problem;
-
-#endif // PY_IPOPT_HOOK_
diff --git a/pyoptsparse/pyIPOPT/src/pyipoptcoremodule.c b/pyoptsparse/pyIPOPT/src/pyipoptcoremodule.c
deleted file mode 100644
index 636e63b31..000000000
--- a/pyoptsparse/pyIPOPT/src/pyipoptcoremodule.c
+++ /dev/null
@@ -1,781 +0,0 @@
-/* Author: Eric Xu */
-/* Licensed under BSD */
-/* */
-/* Modifications on logger made by */
-/* OpenMDAO at NASA Glenn Research Center, 2010 and 2011 */
-/* Modifications on the SAFE_FREE macro made by */
-/* Guillaume Jacquenot, 2012 */
-
-#include "hook.h"
-
-#ifndef SAFE_FREE
-#define SAFE_FREE(p) {if (p) {free(p); (p)= NULL;}}
-#endif
-
-/*
- * Let's put the static char docs at the beginning of this file...
- */
-
-static char PYIPOPT_SOLVE_DOC[] = "solve(x) -> (x, ml, mu, obj)\n \
- \n \
- Call Ipopt to solve problem created before and return \n \
- a tuple that contains final solution x, upper and lower\n \
- bound for multiplier, final objective function obj, \n \
- and the return status of ipopt. \n";
-
-static char PYIPOPT_SET_INTERMEDIATE_CALLBACK_DOC[] =
- "set_intermediate_callback(callback_function)\n \
- \n \
- Set the intermediate callback function. \
- This gets called each iteration.";
-
-static char PYIPOPT_CLOSE_DOC[] = "After all the solving, close the model\n";
-
-static char PYIPOPT_ADD_STR_OPTION_DOC[] =
- "Set the String (char* in C) option for Ipopt. Refer to the Ipopt \n \
- document for more information about Ipopt options, or use \n \
- ipopt --print-options \n \
- to see a list of available options.";
-
-static char PYIPOPT_ADD_INT_OPTION_DOC[] =
- "Set the Int (int in C) option for Ipopt. Refer to the Ipopt \n \
- document for more information about Ipopt options, or use \n \
- ipopt --print-options \n \
- to see a list of available options.";
-
-static char PYIPOPT_ADD_NUM_OPTION_DOC[] =
- "Set the Number (double in C) option for Ipopt. Refer to the Ipopt \n \
- document for more information about Ipopt options, or use \n \
- ipopt --print-options \n \
- to see a list of available options.";
-
-static char PYIPOPT_CREATE_DOC[] =
- "create(n, xl, xu, m, gl, gu, nnzj, nnzh, eval_f, eval_grad_f, eval_g, eval_jac_g) -> Boolean\n \
- \n \
- Create a problem instance and return True if succeed \n \
- \n \
- n is the number of variables, \n \
- xl is the lower bound of x as bounded constraints \n \
- xu is the upper bound of x as bounded constraints \n \
- both xl, xu should be one dimension arrays with length n \n \
- \n \
- m is the number of constraints, \n \
- gl is the lower bound of constraints \n \
- gu is the upper bound of constraints \n \
- both gl, gu should be one dimension arrays with length m \n \
- nnzj is the number of nonzeros in Jacobi matrix \n \
- nnzh is the number of non-zeros in Hessian matrix, you can set it to 0 \n \
- \n \
- eval_f is the call back function to calculate objective value, \n \
- it takes one single argument x as input vector \n \
- eval_grad_f calculates gradient for objective function \n \
- eval_g calculates the constraint values and return an array \n \
- eval_jac_g calculates the Jacobi matrix. It takes two arguments, \n \
- the first is the variable x and the second is a Boolean flag \n \
- if the flag is true, it supposed to return a tuple (row, col) \n \
- to indicate the sparse Jacobi matrix's structure. \n \
- if the flag is false if returns the values of the Jacobi matrix \n \
- with length nnzj \n \
- eval_h calculates the hessian matrix, it's optional. \n \
- if omitted, please set nnzh to 0 and Ipopt will use approximated hessian \n \
- which will make the convergence slower. ";
-
-static char PYIPOPT_LOG_DOC[] = "set_loglevel(level)\n \
- \n \
- Set the log level of PyIPOPT \n \
- levels: \n \
- 0: Terse, no log from pyipopt \n \
- 1: Moderate, logs for ipopt \n \
- 2: Verbose, logs for both ipopt and pyipopt. \n";
-
-
-
-int user_log_level = TERSE;
-
-/* Object Section */
-/* sig of this is void foo(PyO*) */
-static void problem_dealloc(PyObject * self)
-{
- problem *temp = (problem *) self;
- SAFE_FREE(temp->data);
- Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-PyObject *solve(PyObject * self, PyObject * args);
-PyObject *set_intermediate_callback(PyObject * self, PyObject * args);
-PyObject *close_model(PyObject * self, PyObject * args);
-
-static PyObject *add_str_option(PyObject * self, PyObject * args)
-{
- problem *temp = (problem *) self;
- IpoptProblem nlp = (IpoptProblem) (temp->nlp);
- char *param;
- char *value;
- Bool ret;
-
- if (!PyArg_ParseTuple(args, "ss:str_option", ¶m, &value)) {
- return NULL;
- }
- ret = AddIpoptStrOption(nlp, (char *)param, value);
- if (ret) {
- Py_INCREF(Py_True);
- return Py_True;
- } else {
- return PyErr_Format(PyExc_ValueError,
- "%s is not a valid string option", param);
- }
-}
-
-static PyObject *add_int_option(PyObject * self, PyObject * args)
-{
-
- problem *temp = (problem *) self;
- IpoptProblem nlp = (IpoptProblem) (temp->nlp);
-
- char *param;
- int value;
-
- Bool ret;
-
- if (!PyArg_ParseTuple(args, "si:int_option", ¶m, &value)) {
- return NULL;
- }
- ret = AddIpoptIntOption(nlp, (char *)param, value);
- if (ret) {
- Py_INCREF(Py_True);
- return Py_True;
- } else {
- return PyErr_Format(PyExc_ValueError,
- "%s is not a valid int option", param);
- }
-}
-
-static PyObject *add_num_option(PyObject * self, PyObject * args)
-{
- problem *temp = (problem *) self;
- IpoptProblem nlp = (IpoptProblem) (temp->nlp);
-
- char *param;
- double value;
-
- Bool ret;
-
- if (!PyArg_ParseTuple(args, "sd:num_option", ¶m, &value)) {
- return NULL;
- }
- ret = AddIpoptNumOption(nlp, (char *)param, value);
- if (ret) {
- Py_INCREF(Py_True);
- return Py_True;
- } else {
- return PyErr_Format(PyExc_ValueError,
- "%s is not a valid num option", param);
- }
-}
-
-PyMethodDef problem_methods[] = {
- {"solve", solve, METH_VARARGS, PYIPOPT_SOLVE_DOC}
- ,
- {"set_intermediate_callback", set_intermediate_callback, METH_VARARGS,
- PYIPOPT_SET_INTERMEDIATE_CALLBACK_DOC}
- ,
- {"close", close_model, METH_VARARGS, PYIPOPT_CLOSE_DOC}
- ,
- {"int_option", add_int_option, METH_VARARGS, PYIPOPT_ADD_INT_OPTION_DOC}
- ,
- {"str_option", add_str_option, METH_VARARGS, PYIPOPT_ADD_STR_OPTION_DOC}
- ,
- {"num_option", add_num_option, METH_VARARGS, PYIPOPT_ADD_NUM_OPTION_DOC}
- ,
- {NULL, NULL}
- ,
-};
-
-#if PY_MAJOR_VERSION < 3
-PyObject *problem_getattr(PyObject * self, char *attrname)
-{
- PyObject *result = NULL;
- result = Py_FindMethod(problem_methods, self, attrname);
- return result;
-}
-
-
-/*
- * had to replace PyObject_HEAD_INIT(&PyType_Type) in order to get this to
- * compile on Windows
- */
-PyTypeObject IpoptProblemType = {
- PyObject_HEAD_INIT(NULL)
- 0, /* ob_size */
- "pyipoptcore.Problem", /* tp_name */
- sizeof(problem), /* tp_basicsize */
- 0, /* tp_itemsize */
- problem_dealloc, /* tp_dealloc */
- 0, /* tp_print */
- problem_getattr, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_compare */
- 0, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
- 0, /* tp_call */
- 0, /* tp_str */
- 0, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT, /* tp_flags */
- "The IPOPT problem object in python", /* tp_doc */
-};
-
-#else
-
-PyDoc_STRVAR(IpoptProblemType__doc__, "The IPOPT problem object in python");
-
-PyTypeObject IpoptProblemType = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "pyipoptcore.Problem", /* tp_name */
- sizeof(problem), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- /* methods */
- (destructor)problem_dealloc, /*tp_dealloc*/
- (printfunc)0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_reserved*/
- (reprfunc)0, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- (hashfunc)0, /*tp_hash*/
- (ternaryfunc)0, /*tp_call*/
- (reprfunc)0, /*tp_str*/
- (getattrofunc)0, /* tp_getattro */
- (setattrofunc)0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT, /*tp_flags*/
- IpoptProblemType__doc__, /* tp_doc - Documentation string */
- (traverseproc)0, /* tp_traverse */
- (inquiry)0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- problem_methods, /* tp_methods */
-};
-#endif
-
-/*
- * FIXME: use module or package constants for the log levels,
- * either in pyipoptcore or in the parent package.
- * They are currently #defined in a header file.
- */
-static PyObject *set_loglevel(PyObject * obj, PyObject * args)
-{
- int l;
- if (!PyArg_ParseTuple(args, "i", &l)) {
- PySys_WriteStdout("l is %d \n", l);
- return NULL;
- }
- if (l < 0 || l > 2) {
- return NULL;
- }
- user_log_level = l;
- Py_INCREF(Py_True);
- return Py_True;
-}
-
-static PyObject *create(PyObject * obj, PyObject * args)
-{
- PyObject *f = NULL;
- PyObject *gradf = NULL;
- PyObject *g = NULL;
- PyObject *jacg = NULL;
- PyObject *h = NULL;
- PyObject *applynew = NULL;
-
- DispatchData myowndata;
-
- /*
- * I have to create a new python object here, return this python object
- */
-
- int n; /* Number of variables */
- PyArrayObject *xL = NULL;
- PyArrayObject *xU = NULL;
- int m; /* Number of constraints */
- PyArrayObject *gL = NULL;
- PyArrayObject *gU = NULL;
-
- problem *object = NULL;
-
- int nele_jac;
- int nele_hess;
-
- Number *x_L = NULL; /* lower bounds on x */
- Number *x_U = NULL; /* upper bounds on x */
- Number *g_L = NULL; /* lower bounds on g */
- Number *g_U = NULL; /* upper bounds on g */
-
- double *xldata, *xudata;
- double *gldata, *gudata;
-
- int i;
-
- DispatchData *dp = NULL;
-
- PyObject *retval = NULL;
-
- /* Init the myowndata field */
- myowndata.eval_f_python = NULL;
- myowndata.eval_grad_f_python = NULL;
- myowndata.eval_g_python = NULL;
- myowndata.eval_jac_g_python = NULL;
- myowndata.eval_h_python = NULL;
- myowndata.apply_new_python = NULL;
- myowndata.userdata = NULL;
-
- /* "O!", &PyArray_Type &a_x */
- if (!PyArg_ParseTuple(args, "iO!O!iO!O!iiOOOO|OO:pyipoptcreate",
- &n, &PyArray_Type, &xL,
- &PyArray_Type, &xU,
- &m,
- &PyArray_Type, &gL,
- &PyArray_Type, &gU,
- &nele_jac, &nele_hess,
- &f, &gradf, &g, &jacg, &h, &applynew)) {
- retval = NULL;
- SAFE_FREE(x_L);
- SAFE_FREE(x_U);
- SAFE_FREE(g_L);
- SAFE_FREE(g_U);
- return retval;
- }
- if (!PyCallable_Check(f) ||
- !PyCallable_Check(gradf) ||
- !PyCallable_Check(g) || !PyCallable_Check(jacg)) {
- PyErr_SetString(PyExc_TypeError,
- "Need a callable object for callback functions");
- retval = NULL;
- SAFE_FREE(x_L);
- SAFE_FREE(x_U);
- SAFE_FREE(g_L);
- SAFE_FREE(g_U);
- return retval;
- }
- myowndata.eval_f_python = f;
- myowndata.eval_grad_f_python = gradf;
- myowndata.eval_g_python = g;
- myowndata.eval_jac_g_python = jacg;
-
- if (h != NULL) {
- if (PyCallable_Check(h)) {
- myowndata.eval_h_python = h;
- } else {
- PyErr_SetString(PyExc_TypeError,
- "Need a callable object for function h.");
- retval = NULL;
- SAFE_FREE(x_L);
- SAFE_FREE(x_U);
- SAFE_FREE(g_L);
- SAFE_FREE(g_U);
- return retval;
- }
- } else {
- logger("[PyIPOPT] Ipopt will use Hessian approximation.\n");
- }
-
- if (applynew != NULL) {
- if (PyCallable_Check(applynew)) {
- myowndata.apply_new_python = applynew;
- } else {
- PyErr_SetString(PyExc_TypeError,
- "Need a callable object for function applynew.");
- retval = NULL;
- SAFE_FREE(x_L);
- SAFE_FREE(x_U);
- SAFE_FREE(g_L);
- SAFE_FREE(g_U);
- return retval;
- }
- }
- if (m < 0 || n < 0) {
- PyErr_SetString(PyExc_TypeError, "m or n can't be negative");
- retval = NULL;
- SAFE_FREE(x_L);
- SAFE_FREE(x_U);
- SAFE_FREE(g_L);
- SAFE_FREE(g_U);
- return retval;
- }
- x_L = (Number *) malloc(sizeof(Number) * n);
- x_U = (Number *) malloc(sizeof(Number) * n);
- if (!x_L || !x_U) {
- retval = PyErr_NoMemory();
- SAFE_FREE(x_L);
- SAFE_FREE(x_U);
- SAFE_FREE(g_L);
- SAFE_FREE(g_U);
- return retval;
- }
- xldata = (double *)xL->data;
- xudata = (double *)xU->data;
- for (i = 0; i < n; i++) {
- x_L[i] = xldata[i];
- x_U[i] = xudata[i];
- }
-
- g_L = (Number *) malloc(sizeof(Number) * m);
- g_U = (Number *) malloc(sizeof(Number) * m);
- if (!g_L || !g_U)
- PyErr_NoMemory();
-
- gldata = (double *)gL->data;
- gudata = (double *)gU->data;
-
- for (i = 0; i < m; i++) {
- g_L[i] = gldata[i];
- g_U[i] = gudata[i];
- }
-
- /* Grab the callback objects because we want to use them later. */
- Py_XINCREF(f);
- Py_XINCREF(gradf);
- Py_XINCREF(g);
- Py_XINCREF(jacg);
- Py_XINCREF(h);
- Py_XINCREF(applynew);
-
- /* create the Ipopt Problem */
-
- int C_indexstyle = 0;
- IpoptProblem thisnlp = CreateIpoptProblem(n,
- x_L, x_U, m, g_L, g_U,
- nele_jac, nele_hess,
- C_indexstyle,
- &eval_f, &eval_g,
- &eval_grad_f,
- &eval_jac_g, &eval_h);
- logger("[PyIPOPT] Problem created");
- if (!thisnlp) {
- PyErr_SetString(PyExc_MemoryError, "Cannot create IpoptProblem instance");
- retval = NULL;
- SAFE_FREE(x_L);
- SAFE_FREE(x_U);
- SAFE_FREE(g_L);
- SAFE_FREE(g_U);
- return retval;
- }
- object = PyObject_NEW(problem, &IpoptProblemType);
-
- if (object != NULL) {
- object->n_variables = n;
- object->m_constraints = m;
- object->nlp = thisnlp;
- dp = (DispatchData *) malloc(sizeof(DispatchData));
- if (!dp) {
- retval = PyErr_NoMemory();
- SAFE_FREE(x_L);
- SAFE_FREE(x_U);
- SAFE_FREE(g_L);
- SAFE_FREE(g_U);
- return retval;
- }
- memcpy((void *)dp, (void *)&myowndata, sizeof(DispatchData));
- object->data = dp;
- retval = (PyObject *) object;
- SAFE_FREE(x_L);
- SAFE_FREE(x_U);
- SAFE_FREE(g_L);
- SAFE_FREE(g_U);
- return retval;
- } else {
- PyErr_SetString(PyExc_MemoryError, "Can't create a new Problem instance");
- retval = NULL;
- SAFE_FREE(x_L);
- SAFE_FREE(x_U);
- SAFE_FREE(g_L);
- SAFE_FREE(g_U);
- return retval;
- }
-}
-
-PyObject *set_intermediate_callback(PyObject * self, PyObject * args)
-{
- PyObject *intermediate_callback;
- problem *temp = (problem *) self;
- IpoptProblem nlp = (IpoptProblem) (temp->nlp);
- DispatchData myowndata;
- DispatchData *bigfield = (DispatchData *) (temp->data);
-
- /* Init the myowndata field */
- myowndata.eval_intermediate_callback_python = NULL;
-
- if (!PyArg_ParseTuple(args, "O", &intermediate_callback)) {
- return NULL;
- }
- if (!PyCallable_Check(intermediate_callback)) {
- PyErr_SetString(PyExc_TypeError,
- "Need a callable object for function!");
- return NULL;
- } else {
-
- bigfield->eval_intermediate_callback_python =
- intermediate_callback;
-
- /* Put a Python function object into this data structure */
- /*
- * myowndata.eval_intermediate_callback_python =
- * intermediate_callback;
- */
-
- /* DispatchData *dp = malloc(sizeof(DispatchData)); */
- /*
- * memcpy((void*)dp, (void*)&myowndata,
- * sizeof(DispatchData));
- */
- /* bigfield = dp; */
- /*
- * logger( "qqq: inside set_intermediate_callback, bigfield
- * is %p\n", bigfield ) ;
- */
- /*
- * logger("[PyIPOPT] User specified data field to callback
- * function.\n");
- */
-
- SetIntermediateCallback(nlp, eval_intermediate_callback);
- Py_INCREF(Py_True);
- return Py_True;
- }
-}
-
-PyObject *solve(PyObject * self, PyObject * args)
-{
- enum ApplicationReturnStatus status; /* Solve return code */
- int i;
- int n;
-
- /* Return values */
- problem *temp = (problem *) self;
-
- IpoptProblem nlp = (IpoptProblem) (temp->nlp);
- DispatchData *bigfield = (DispatchData *) (temp->data);
- int m = temp->m_constraints;
-
- /* int dX[1]; */
- npy_intp dX[1];
- npy_intp dlambda[1];
-
- PyArrayObject *x = NULL, *mL = NULL, *mU = NULL, *lambda = NULL;
- Number obj; /* objective value */
-
- PyObject *retval = NULL;
- PyArrayObject *x0 = NULL;
-
- PyObject *myuserdata = NULL;
-
- Number *newx0 = NULL;
-
- if (!PyArg_ParseTuple(args, "O!|O", &PyArray_Type, &x0, &myuserdata)) {
- retval = NULL;
- /* clean up and return */
- if (retval == NULL) {
- Py_XDECREF(x);
- Py_XDECREF(mL);
- Py_XDECREF(mU);
- Py_XDECREF(lambda);
- }
- SAFE_FREE(newx0);
- return retval;
- }
- if (x0->nd != 1){ //If x0 is not 1-dimensional then solve will fail and cause a segmentation fault.
- logger("[ERROR] x0 must be a 1-dimensional array");
- Py_XDECREF(x);
- Py_XDECREF(mL);
- Py_XDECREF(mU);
- Py_XDECREF(lambda);
- PyErr_SetString(PyExc_TypeError,
- "x0 passed to solve is not 1-dimensional.");
- return NULL;
- }
-
- if (myuserdata != NULL) {
- bigfield->userdata = myuserdata;
- /*
- * logger("[PyIPOPT] User specified data field to callback
- * function.\n");
- */
- }
- if (nlp == NULL) {
- PyErr_SetString(PyExc_TypeError,
- "nlp objective passed to solve is NULL\n Problem created?\n");
- retval = NULL;
- /* clean up and return */
- if (retval == NULL) {
- Py_XDECREF(x);
- Py_XDECREF(mL);
- Py_XDECREF(mU);
- Py_XDECREF(lambda);
- }
- SAFE_FREE(newx0);
- return retval;
- }
- if (bigfield->eval_h_python == NULL) {
- AddIpoptStrOption(nlp, "hessian_approximation", "limited-memory");
- /* logger("Can't find eval_h callback function\n"); */
- }
- /* allocate space for the initial point and set the values */
- npy_intp *dim = ((PyArrayObject *) x0)->dimensions;
- n = dim[0];
- dX[0] = n;
-
- x = (PyArrayObject *) PyArray_SimpleNew(1, dX, NPY_DOUBLE);
- if (!x) {
- retval = PyErr_NoMemory();
- /* clean up and return */
- if (retval == NULL) {
- Py_XDECREF(x);
- Py_XDECREF(mL);
- Py_XDECREF(mU);
- Py_XDECREF(lambda);
- }
- SAFE_FREE(newx0);
- return retval;
- }
- newx0 = (Number *) malloc(sizeof(Number) * n);
- if (!newx0) {
- retval = PyErr_NoMemory();
- /* clean up and return */
- if (retval == NULL) {
- Py_XDECREF(x);
- Py_XDECREF(mL);
- Py_XDECREF(mU);
- Py_XDECREF(lambda);
- }
- SAFE_FREE(newx0);
- return retval;
- }
- double *xdata = (double *)x0->data;
- for (i = 0; i < n; i++)
- newx0[i] = xdata[i];
-
- /* Allocate multiplier arrays */
-
- mL = (PyArrayObject *) PyArray_SimpleNew(1, dX, NPY_DOUBLE);
- mU = (PyArrayObject *) PyArray_SimpleNew(1, dX, NPY_DOUBLE);
- dlambda[0] = m;
- lambda = (PyArrayObject *) PyArray_SimpleNew(1, dlambda,
- NPY_DOUBLE);
-
- /* For status code, see IpReturnCodes_inc.h in Ipopt */
-
- status =
- IpoptSolve(nlp, newx0, NULL, &obj, (double *)lambda->data,
- (double *)mL->data, (double *)mU->data,
- (UserDataPtr) bigfield);
- double *return_x_data = (double *)x->data;
- for (i = 0; i < n; i++) {
- return_x_data[i] = newx0[i];
- }
- retval = Py_BuildValue("OOOOdi",
- PyArray_Return(x),
- PyArray_Return(mL),
- PyArray_Return(mU),
- PyArray_Return(lambda),
- obj, status
- );
- /* clean up and return */
-
- Py_XDECREF(x);
- Py_XDECREF(mL);
- Py_XDECREF(mU);
- Py_XDECREF(lambda);
-
- SAFE_FREE(newx0);
- return retval;
-}
-
-PyObject *close_model(PyObject * self, PyObject * args)
-{
- problem *obj = (problem *) self;
- DispatchData *dp = obj->data;
-
- /* Ungrab the callback functions because we do not need them anymore. */
- Py_XDECREF(dp->eval_f_python);
- Py_XDECREF(dp->eval_grad_f_python);
- Py_XDECREF(dp->eval_g_python);
- Py_XDECREF(dp->eval_jac_g_python);
- Py_XDECREF(dp->eval_h_python);
- Py_XDECREF(dp->apply_new_python);
-
- FreeIpoptProblem(obj->nlp);
- obj->nlp = NULL;
- Py_INCREF(Py_True);
- return Py_True;
-}
-
-/* static char PYTEST[] = "TestCreate\n"; */
-
-/* static PyObject *test(PyObject *self, PyObject *args) */
-/* { */
-/* IpoptProblem thisnlp = NULL; */
-/* problem *object = NULL; */
-/* object = PyObject_NEW(problem , &IpoptProblemType); */
-/* if (object != NULL) */
-/* object->nlp = thisnlp; */
-/* /\* problem *object = problem_new(thisnlp); *\/ */
-/* return (PyObject *)object; */
-/* } */
-
-/* Begin Python Module code section */
-static PyMethodDef ipoptMethods[] = {
- /* { "solve", solve, METH_VARARGS, PYIPOPT_SOLVE_DOC}, */
- {"create", create, METH_VARARGS, PYIPOPT_CREATE_DOC},
- /* { "close", close_model, METH_VARARGS, PYIPOPT_CLOSE_DOC}, */
- /* { "test", test, METH_VARARGS, PYTEST}, */
- {"set_loglevel", set_loglevel, METH_VARARGS, PYIPOPT_LOG_DOC},
- {NULL, NULL}
-};
-
-#if PY_MAJOR_VERSION >= 3
- #define MOD_ERROR_VAL NULL
- #define MOD_SUCCESS_VAL(val) val
- #define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
- #define MOD_DEF(ob, name, doc, methods) \
- static struct PyModuleDef moduledef = { \
- PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \
- ob = PyModule_Create(&moduledef);
-#else
- #define MOD_ERROR_VAL
- #define MOD_SUCCESS_VAL(val)
- #define MOD_INIT(name) void init##name(void)
- #define MOD_DEF(ob, name, doc, methods) \
- ob = Py_InitModule3(name, methods, doc);
-#endif
-
-MOD_INIT(pyipoptcore)
-{
- PyObject * m;
- /* Finish initialization of the problem type */
- if (PyType_Ready(&IpoptProblemType) < 0) {
- return MOD_ERROR_VAL;
- }
-
- MOD_DEF(m, "pyipoptcore", "A hook between Ipopt and Python", ipoptMethods)
-
- if (m == NULL)
- return MOD_ERROR_VAL;
-
- /* Initialize numpy. */
- /* A segfault will occur if I use numarray without this.. */
- import_array();
- if (PyErr_Occurred()) {
- Py_FatalError("Unable to initialize module pyipoptcore");
- }
-
- return MOD_SUCCESS_VAL(m);
-}
-
-/* End Python Module code section */
diff --git a/setup.py b/setup.py
index 9f068765e..d2e6863a5 100644
--- a/setup.py
+++ b/setup.py
@@ -6,11 +6,6 @@
def run_meson_build():
- # check if ipopt dir is specified
- ipopt_dir_opt = ""
- if "IPOPT_DIR" in os.environ:
- ipopt_dir = os.environ["IPOPT_DIR"]
- ipopt_dir_opt = f"-Dipopt_dir={ipopt_dir}"
prefix = os.path.join(os.getcwd(), staging_dir)
purelibdir = "."
@@ -18,15 +13,12 @@ def run_meson_build():
meson_args = ""
if "MESON_ARGS" in os.environ:
meson_args = os.environ["MESON_ARGS"]
- # check to make sure ipopt dir isnt specified twice
- if "-Dipopt_dir" in meson_args and ipopt_dir_opt != "":
- raise RuntimeError("IPOPT_DIR environment variable is set and '-Dipopt_dir' in MESON_ARGS")
# configure
meson_path = shutil.which("meson")
meson_call = (
f"{meson_path} setup {staging_dir} --prefix={prefix} "
- + f"-Dpython.purelibdir={purelibdir} -Dpython.platlibdir={purelibdir} {ipopt_dir_opt} {meson_args}"
+ + f"-Dpython.purelibdir={purelibdir} -Dpython.platlibdir={purelibdir} {meson_args}"
)
sysargs = meson_call.split(" ")
sysargs = [arg for arg in sysargs if arg != ""]
@@ -113,7 +105,7 @@ def copy_shared_libraries():
"matplotlib",
],
"docs": docs_require,
- "testing": ["testflo>=1.4.5", "parameterized"],
+ "testing": ["testflo>=1.4.5", "parameterized", "cyipopt"],
},
classifiers=[
"Development Status :: 5 - Production/Stable",
diff --git a/tests/test_hs015.py b/tests/test_hs015.py
index 60b73d723..ffc15e7c7 100644
--- a/tests/test_hs015.py
+++ b/tests/test_hs015.py
@@ -278,22 +278,83 @@ def test_snopt_work_arrays_save(self):
os.remove(pickleFile)
os.remove(histFile)
- def test_snopt_failed_initial(self):
+ @parameterized.expand(["SNOPT", "IPOPT"])
+ def test_failed_initial_func(self, optName):
+ # Test if we get the correct inform when the initial function call fails
def failed_fun(x_dict):
funcs = {"obj": 0.0, "con": [np.nan, np.nan]}
fail = True
return funcs, fail
- self.optName = "SNOPT"
+ self.optName = optName
self.setup_optProb()
# swap obj to report NaN
self.optProb.objFun = failed_fun
sol = self.optimize(optOptions={}, storeHistory=True)
- self.assert_inform_equal(sol, optInform=61)
+ if self.optName == "SNOPT":
+ inform_ref = 61
+ elif self.optName == "IPOPT":
+ inform_ref = -13
+ self.assert_inform_equal(sol, optInform=inform_ref)
# make sure empty history does not error out
hist = History(self.histFileName, flag="r")
hist.getValues()
+ @parameterized.expand(["SNOPT", "IPOPT"])
+ def test_failed_initial_sens(self, optName):
+ # Test if we get the correct inform when the initial sensitivity call fails
+ def failed_sens(xdict, funcs):
+ funcsSens = {}
+ funcsSens["obj"] = {"xvars": [np.nan, np.nan]}
+ funcsSens["con"] = {"xvars": [[np.nan, np.nan], [np.nan, np.nan]]}
+ fail = True
+ return funcsSens, fail
+
+ self.optName = optName
+ self.setup_optProb()
+ sol = self.optimize(sens=failed_sens, optOptions={}, storeHistory=True)
+ if self.optName == "SNOPT":
+ inform_ref = 61
+ elif self.optName == "IPOPT":
+ inform_ref = -13
+ self.assert_inform_equal(sol, optInform=inform_ref)
+ # make sure empty history does not error out
+ hist = History(self.histFileName, flag="r")
+ hist.getValues()
+
+ @parameterized.expand(["SNOPT", "IPOPT"])
+ def test_func_eval_failure(self, optName):
+ # Test if optimizer back-tracks after function evaluation failure and keeps going
+
+ # This function is the same as original except it will fail at 3rd call
+ def objFun_eval_failure(x_dict):
+ self.nf += 1
+ x = x_dict["xvars"]
+ funcs = {}
+ funcs["obj"] = [100 * (x[1] - x[0] ** 2) ** 2 + (1 - x[0]) ** 2]
+ conval = np.zeros(2, "D")
+ conval[0] = x[0] * x[1]
+ conval[1] = x[0] + x[1] ** 2
+ funcs["con"] = conval
+ # extra keys
+ funcs["extra1"] = 0.0
+ funcs["extra2"] = 1.0
+ fail = False
+ if self.nf == 3:
+ funcs["obj"] = [np.nan]
+ funcs["con"] = [np.nan, np.nan]
+ fail = True
+ return funcs, fail
+
+ self.optName = optName
+ self.setup_optProb()
+ # swap obj to simulate eval failure
+ self.optProb.objFun = objFun_eval_failure
+ sol = self.optimize(optOptions={}, storeHistory=True)
+ # check solution and informs
+ self.assert_solution_allclose(sol, 1e-5)
+ self.assert_inform_equal(sol)
+
if __name__ == "__main__":
unittest.main()