From 70cff2d6aa53bbc07c7bf6e011238b5017ffe1ac Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Fri, 6 Feb 2026 20:28:56 -0500 Subject: [PATCH 01/23] add knitro --- CMakeLists.txt | 26 + include/pyoptinterface/core.hpp | 3 +- include/pyoptinterface/knitro_model.hpp | 465 +++ lib/knitro_model.cpp | 988 ++++++ lib/knitro_model_ext.cpp | 241 ++ lib/knitro_model_ext_constants.cpp | 122 + src/pyoptinterface/_src/knitro.py | 412 +++ src/pyoptinterface/knitro.py | 14 + tests/conftest.py | 7 +- thirdparty/solvers/knitro/knitro.h | 3710 +++++++++++++++++++++++ 10 files changed, 5986 insertions(+), 2 deletions(-) create mode 100644 include/pyoptinterface/knitro_model.hpp create mode 100644 lib/knitro_model.cpp create mode 100644 lib/knitro_model_ext.cpp create mode 100644 lib/knitro_model_ext_constants.cpp create mode 100644 src/pyoptinterface/_src/knitro.py create mode 100644 src/pyoptinterface/knitro.py create mode 100644 thirdparty/solvers/knitro/knitro.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0484f0f8..47929f4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -264,6 +264,25 @@ if(DEFINED ENV{XPRESSDIR}) target_include_directories(xpress_model_ext PRIVATE $ENV{XPRESSDIR}/include) endif() +# KNITRO +add_library(knitro_model STATIC) +target_sources(knitro_model PRIVATE + include/pyoptinterface/knitro_model.hpp + lib/knitro_model.cpp +) +target_link_libraries(knitro_model PUBLIC core cppad_interface) + +nanobind_add_module( + knitro_model_ext + + STABLE_ABI NB_STATIC NB_DOMAIN pyoptinterface + + lib/knitro_model_ext.cpp + lib/knitro_model_ext_constants.cpp +) +target_link_libraries(knitro_model_ext PUBLIC knitro_model) +install(TARGETS knitro_model_ext LIBRARY DESTINATION ${POI_INSTALL_DIR}) + # stub nanobind_add_stub( core_ext_stub @@ -342,6 +361,13 @@ nanobind_add_stub( OUTPUT ${POI_INSTALL_DIR}/xpress_model_ext.pyi ) +nanobind_add_stub( + knitro_model_ext_stub + INSTALL_TIME + MODULE pyoptinterface._src.knitro_model_ext + OUTPUT ${POI_INSTALL_DIR}/knitro_model_ext.pyi +) + set(ENABLE_TEST_MAIN OFF BOOL "Enable test c++ function with a main.cpp") if(ENABLE_TEST_MAIN) add_executable(test_main lib/main.cpp) diff --git a/include/pyoptinterface/core.hpp b/include/pyoptinterface/core.hpp index 132b6920..a01d0f21 100644 --- a/include/pyoptinterface/core.hpp +++ b/include/pyoptinterface/core.hpp @@ -274,7 +274,8 @@ enum class ConstraintType COPT_ExpCone, COPT_NL, IPOPT_NL, - Xpress_Nlp + Xpress_Nlp, + KNITRO_NL, }; enum class SOSType diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp new file mode 100644 index 00000000..c791cba3 --- /dev/null +++ b/include/pyoptinterface/knitro_model.hpp @@ -0,0 +1,465 @@ +#pragma once + +#include +#include +#include +#include + +#include "solvers/knitro/knitro.h" + +#include "pyoptinterface/core.hpp" +#include "pyoptinterface/container.hpp" +#include "pyoptinterface/cppad_interface.hpp" +#define USE_NLMIXIN +#include "pyoptinterface/solver_common.hpp" +#include "pyoptinterface/dylib.hpp" + +// Define Knitro C APIs to be dynamically loaded +#define APILIST \ + B(KN_new); \ + B(KN_free); \ + B(KN_update); \ + B(KN_reset_params_to_defaults); \ + B(KN_load_param_file); \ + B(KN_save_param_file); \ + B(KN_get_param_id); \ + B(KN_get_param_type); \ + B(KN_set_int_param); \ + B(KN_set_char_param); \ + B(KN_set_double_param); \ + B(KN_get_int_param); \ + B(KN_get_double_param); \ + B(KN_add_vars); \ + B(KN_add_var); \ + B(KN_add_cons); \ + B(KN_add_con); \ + B(KN_set_var_lobnd); \ + B(KN_set_var_upbnd); \ + B(KN_get_var_lobnd); \ + B(KN_get_var_upbnd); \ + B(KN_set_var_type); \ + B(KN_get_var_type); \ + B(KN_set_var_name); \ + B(KN_get_var_name); \ + B(KN_set_con_lobnd); \ + B(KN_set_con_upbnd); \ + B(KN_set_con_eqbnd); \ + B(KN_get_con_lobnd); \ + B(KN_get_con_upbnd); \ + B(KN_get_con_eqbnd); \ + B(KN_set_con_name); \ + B(KN_get_con_name); \ + B(KN_set_obj_goal); \ + B(KN_get_obj_goal); \ + B(KN_set_var_primal_init_value); \ + B(KN_add_obj_constant); \ + B(KN_del_obj_constant); \ + B(KN_add_obj_linear_struct); \ + B(KN_del_obj_linear_struct); \ + B(KN_del_obj_linear_struct_all); \ + B(KN_add_obj_quadratic_struct); \ + B(KN_del_obj_quadratic_struct); \ + B(KN_del_obj_quadratic_struct_all); \ + B(KN_add_con_constant); \ + B(KN_add_con_linear_struct); \ + B(KN_add_con_linear_struct_one); \ + B(KN_add_con_linear_term); \ + B(KN_add_con_quadratic_struct); \ + B(KN_add_con_quadratic_struct_one); \ + B(KN_add_con_quadratic_term); \ + B(KN_add_con_L2norm); \ + B(KN_del_con_linear_struct); \ + B(KN_del_con_quadratic_struct); \ + B(KN_chg_con_linear_term); \ + B(KN_add_eval_callback); \ + B(KN_del_obj_eval_callback_all); \ + B(KN_del_eval_callbacks); \ + B(KN_set_cb_user_params); \ + B(KN_set_cb_grad); \ + B(KN_set_cb_hess); \ + B(KN_solve); \ + B(KN_get_solution); \ + B(KN_get_obj_value); \ + B(KN_get_number_cons); \ + B(KN_get_number_vars); \ + B(KN_get_var_primal_values); \ + B(KN_get_var_primal_values_all); \ + B(KN_get_var_dual_values); \ + B(KN_get_var_dual_values_all); \ + B(KN_get_con_values); \ + B(KN_get_con_values_all); \ + B(KN_get_con_dual_values); \ + B(KN_get_con_dual_values_all); + +namespace knitro +{ +#define B DYLIB_EXTERN_DECLARE +APILIST +#undef B + +bool is_library_loaded(); + +bool load_library(const std::string &path); +} // namespace knitro + +struct KnitroFreeProblemT +{ + void operator()(KN_context *kc) const + { + if (kc != nullptr) + { + knitro::KN_free(&kc); + } + } +}; + +struct KnitroResult +{ + bool is_valid = false; + int status = 0; + double obj_val = 0.0; + std::vector x; + std::vector lambda; + std::vector con_values; + std::vector con_duals; +}; + +enum ObjectiveFlags +{ + OBJ_CONSTANT = 1 << 0, // 0x01 + OBJ_LINEAR = 1 << 1, // 0x02 + OBJ_QUADRATIC = 1 << 2, // 0x04 + OBJ_NONLINEAR = 1 << 3 // 0x08 +}; + +enum ConstraintSenseFlags +{ + CON_LOBND = 1 << 0, // 0x01 + CON_UPBND = 1 << 1, // 0x02 +}; + +struct NLCallbackData +{ + size_t idx; + CppAD::ADFun function; + std::vector indexVars; + std::vector> jac_pattern; + std::vector jac_rows; + std::vector jac_cols; + CppAD::sparse_jacobian_work jac_work; +}; + +class KnitroModel : public OnesideLinearConstraintMixin, + public TwosideLinearConstraintMixin, + public OnesideQuadraticConstraintMixin, + public TwosideQuadraticConstraintMixin, + public TwosideNLConstraintMixin, + public LinearObjectiveMixin, + public PPrintMixin, + public GetValueMixin +{ + public: + KnitroModel(); + void init(); + void close(); + + double get_infinity() const; + KNINT _variable_index(const VariableIndex &variable); + KNINT _constraint_index(const ConstraintIndex &constraint); + + VariableIndex add_variable(VariableDomain domain = VariableDomain::Continuous, + double lb = -KN_INFINITY, double ub = KN_INFINITY, + const char *name = nullptr); + void delete_variable(const VariableIndex &variable); + + double get_variable_lb(const VariableIndex &variable); + double get_variable_ub(const VariableIndex &variable); + void set_variable_lb(const VariableIndex &variable, double lb); + void set_variable_ub(const VariableIndex &variable, double ub); + void set_variable_bounds(const VariableIndex &variable, double lb, double ub); + double get_variable_value(const VariableIndex &variable); + void set_variable_start(const VariableIndex &variable, double start); + std::string get_variable_name(const VariableIndex &variable); + void set_variable_name(const VariableIndex &variable, const std::string &name); + void set_variable_domain(const VariableIndex &variable, VariableDomain domain); + double get_variable_rc(const VariableIndex &variable); + + std::string pprint_variable(const VariableIndex &variable); + + ConstraintIndex add_linear_constraint(const ScalarAffineFunction &f, ConstraintSense sense, + double rhs, const char *name = nullptr); + ConstraintIndex add_linear_constraint(const ScalarAffineFunction &f, + const std::tuple &interval, + const char *name = nullptr); + ConstraintIndex add_quadratic_constraint(const ScalarQuadraticFunction &f, + ConstraintSense sense, double rhs, + const char *name = nullptr); + ConstraintIndex add_quadratic_constraint(const ScalarQuadraticFunction &f, + const std::tuple &interval, + const char *name = nullptr); + ConstraintIndex add_second_order_cone_constraint(const Vector &variables, + const char *name, bool rotated); + ConstraintIndex add_single_nl_constraint(ExpressionGraph &graph, const ExpressionHandle &result, + const std::tuple &interval, + const char *name = nullptr); + + void delete_constraint(ConstraintIndex constraint); + void set_constraint_name(const ConstraintIndex &constraint, const std::string &name); + std::string get_constraint_name(const ConstraintIndex &constraint); + + double get_constraint_primal(const ConstraintIndex &constraint); + double get_constraint_dual(const ConstraintIndex &constraint); + + void set_normalized_rhs(const ConstraintIndex &constraint, double rhs); + double get_normalized_rhs(const ConstraintIndex &constraint); + void set_normalized_coefficient(const ConstraintIndex &constraint, + const VariableIndex &variable, double coefficient); + + // Objective functions + void set_objective(const ScalarAffineFunction &f, ObjectiveSense sense); + void set_objective(const ScalarQuadraticFunction &f, ObjectiveSense sense); + void set_objective(const ExprBuilder &expr, ObjectiveSense sense); + void add_single_nl_objective(ExpressionGraph &graph, const ExpressionHandle &result); + double get_obj_value(); + + void optimize(); + void _optimize(); + + template + void set_raw_parameter(const std::string &name, T value) + { + int error; + int param_id; + error = knitro::KN_get_param_id(m_kc.get(), name.c_str(), ¶m_id); + check_error(error); + set_raw_parameter(param_id, value); + } + + template + void set_raw_parameter(int param_id, T value) + { + int error; + if constexpr (std::is_same_v) + { + error = knitro::KN_set_int_param(m_kc.get(), param_id, value); + } + else if constexpr (std::is_same_v) + { + error = knitro::KN_set_double_param(m_kc.get(), param_id, value); + } + else if constexpr (std::is_same_v || std::is_same_v) + { + if constexpr (std::is_same_v) + { + error = knitro::KN_set_char_param(m_kc.get(), param_id, value.c_str()); + } + else + { + error = knitro::KN_set_char_param(m_kc.get(), param_id, value); + } + } + else + { + static_assert(std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v, + "T must be int, double, std::string, or const char*"); + } + check_error(error); + } + + template + T get_raw_parameter(const std::string &name) + { + int error; + int param_id; + error = knitro::KN_get_param_id(m_kc.get(), name.c_str(), ¶m_id); + check_error(error); + return get_raw_parameter(param_id); + } + + template + T get_raw_parameter(int param_id) + { + int error; + T value; + if constexpr (std::is_same_v) + { + error = knitro::KN_get_int_param(m_kc.get(), param_id, &value); + } + else if constexpr (std::is_same_v) + { + error = knitro::KN_get_double_param(m_kc.get(), param_id, &value); + } + else + { + static_assert(std::is_same_v || std::is_same_v, + "T must be int or double for get_raw_parameter"); + } + check_error(error); + return value; + } + + void check_error(int error) const; + + std::unique_ptr m_kc = nullptr; + + // Basic model statistics + size_t n_vars = 0; + size_t n_cons = 0; + size_t n_lincons = 0; + size_t n_quadcons = 0; + size_t n_soccons = 0; + + // Auxiliary data for constraints and objectives + std::unordered_map>> m_aux_cons; + std::unordered_map m_con_sense_flags; + uint8_t m_obj_flag = 0; + + // Store NL functions to keep them alive for callbacks + // Use unique_ptr for pointer stability (unordered_map can rehash and invalidate references) + std::unordered_map> m_con_nl_data_map; + std::vector> m_obj_nl_data; + + // Solution and solve status + KnitroResult m_result; + bool m_is_dirty = true; + int m_solve_status = 0; + + static bool is_name_empty(const char *name) + { + return name == nullptr || name[0] == '\0'; + } + + static int knitro_var_type(VariableDomain domain) + { + switch (domain) + { + case VariableDomain::Continuous: + return KN_VARTYPE_CONTINUOUS; + case VariableDomain::Integer: + return KN_VARTYPE_INTEGER; + case VariableDomain::Binary: + return KN_VARTYPE_BINARY; + default: + throw std::runtime_error("Unknown variable domain"); + } + } + + static int knitro_obj_goal(ObjectiveSense sense) + { + switch (sense) + { + case ObjectiveSense::Minimize: + return KN_OBJGOAL_MINIMIZE; + case ObjectiveSense::Maximize: + return KN_OBJGOAL_MAXIMIZE; + default: + throw std::runtime_error("Unknown objective sense"); + } + } + + private: + std::tuple _sense_to_interval(ConstraintSense sense, double rhs); + void _update_con_sense_flags(const ConstraintIndex &constraint, ConstraintSense sense); + + void _set_linear_constraint(const ConstraintIndex &constraint, const ScalarAffineFunction &f); + void _set_quadratic_constraint(const ConstraintIndex &constraint, + const ScalarQuadraticFunction &f); + void _set_second_order_cone_constraint(const ConstraintIndex &constraint, + const Vector &variables); + void _set_second_order_cone_constraint_rotated(const ConstraintIndex &constraint, + const Vector &variables); + void _set_nonlinear_constraint(const ConstraintIndex &constraint, ExpressionGraph &graph); + void _set_linear_objective(const ScalarAffineFunction &f); + void _set_quadratic_objective(const ScalarQuadraticFunction &f); + void _add_nonlinear_objective(ExpressionGraph &graph); + void _reset_objective(); + + template + ConstraintIndex _add_constraint_impl(ConstraintType type, + const std::tuple &interval, + const char *name, size_t *np, const F &setter) + { + KNINT indexCon; + int error = knitro::KN_add_con(m_kc.get(), &indexCon); + check_error(error); + + IndexT index = indexCon; + ConstraintIndex constraint(type, index); + + double lb = std::get<0>(interval); + double ub = std::get<1>(interval); + + error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon, lb); + check_error(error); + error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon, ub); + check_error(error); + + setter(constraint); + + if (!is_name_empty(name)) + { + error = knitro::KN_set_con_name(m_kc.get(), indexCon, name); + check_error(error); + } + + m_con_sense_flags[indexCon] = CON_UPBND; + + n_cons++; + if (np != nullptr) + { + (*np)++; + } + m_is_dirty = true; + + return constraint; + } + + template + ConstraintIndex _add_constraint_with_sense(const S &function, ConstraintSense sense, double rhs, + const char *name, const F &add) + { + auto interval = _sense_to_interval(sense, rhs); + auto constraint = add(function, interval, name); + _update_con_sense_flags(constraint, sense); + return constraint; + } + + template + void _set_objective_impl(ObjectiveSense sense, const F &setter) + { + _reset_objective(); + setter(); + int goal = knitro_obj_goal(sense); + int error = knitro::KN_set_obj_goal(m_kc.get(), goal); + check_error(error); + m_is_dirty = true; + m_result.is_valid = false; + } + + template + std::string _get_name(KNINT index, F get, const char *prefix) const + { + char name[1024]; + name[0] = '\0'; + int error = get(m_kc.get(), index, 1024, name); + check_error(error); + + if (name[0] != '\0') + { + return std::string(name); + } + else + { + return fmt::format("{}{}", prefix, index); + } + } + + template + void _set_name(KNINT index, const std::string &name, F set) + { + int error = set(m_kc.get(), index, name.c_str()); + check_error(error); + m_is_dirty = true; + } +}; diff --git a/lib/knitro_model.cpp b/lib/knitro_model.cpp new file mode 100644 index 00000000..8c20411b --- /dev/null +++ b/lib/knitro_model.cpp @@ -0,0 +1,988 @@ +#include "pyoptinterface/knitro_model.hpp" +#include "pyoptinterface/solver_common.hpp" + +#include "fmt/core.h" +#include "fmt/ranges.h" +#include +#include +#include + +namespace knitro +{ +#define B DYLIB_DECLARE +APILIST +#undef B + +static DynamicLibrary lib; +static bool is_loaded = false; + +bool is_library_loaded() +{ + return is_loaded; +} + +bool load_library(const std::string &path) +{ + bool success = lib.try_load(path.c_str()); + if (!success) + { + return false; + } + + DYLIB_LOAD_INIT; + +#define B DYLIB_LOAD_FUNCTION + APILIST +#undef B + + if (IS_DYLIB_LOAD_SUCCESS) + { +#define B DYLIB_SAVE_FUNCTION + APILIST +#undef B + is_loaded = true; + return true; + } + else + { + return false; + } +} +} // namespace knitro + +KnitroModel::KnitroModel() +{ + init(); +} + +void KnitroModel::init() +{ + if (!knitro::is_library_loaded()) + { + throw std::runtime_error("KNITRO library is not loaded"); + } + + KN_context *kc_ptr = nullptr; + int error = knitro::KN_new(&kc_ptr); + check_error(error); + + m_kc = std::unique_ptr(kc_ptr); +} + +void KnitroModel::close() +{ + m_kc.reset(); +} + +void KnitroModel::check_error(int error) const +{ + if (error != 0) + { + throw std::runtime_error(fmt::format("KNITRO error code: {}", error)); + } +} + +double KnitroModel::get_infinity() const +{ + return KN_INFINITY; +} + +VariableIndex KnitroModel::add_variable(VariableDomain domain, double lb, double ub, + const char *name) +{ + KNINT indexVar; + int error = knitro::KN_add_var(m_kc.get(), &indexVar); + check_error(error); + + VariableIndex variable(indexVar); + + int var_type = knitro_var_type(domain); + error = knitro::KN_set_var_type(m_kc.get(), indexVar, var_type); + check_error(error); + + if (var_type == KN_VARTYPE_BINARY) + { + lb = (lb < 0.0) ? 0.0 : lb; + ub = (ub > 1.0) ? 1.0 : ub; + } + + error = knitro::KN_set_var_lobnd(m_kc.get(), indexVar, lb); + check_error(error); + error = knitro::KN_set_var_upbnd(m_kc.get(), indexVar, ub); + check_error(error); + + if (!is_name_empty(name)) + { + int error = knitro::KN_set_var_name(m_kc.get(), indexVar, name); + check_error(error); + } + + n_vars++; + m_is_dirty = true; + + return variable; +} + +double KnitroModel::get_variable_lb(const VariableIndex &variable) +{ + KNINT indexVar = _variable_index(variable); + int error; + double lb; + + error = knitro::KN_get_var_lobnd(m_kc.get(), indexVar, &lb); + check_error(error); + return lb; +} + +double KnitroModel::get_variable_ub(const VariableIndex &variable) +{ + KNINT indexVar = _variable_index(variable); + int error; + double ub; + + error = knitro::KN_get_var_upbnd(m_kc.get(), indexVar, &ub); + check_error(error); + return ub; +} + +void KnitroModel::set_variable_lb(const VariableIndex &variable, double lb) +{ + KNINT indexVar = _variable_index(variable); + int error = knitro::KN_set_var_lobnd(m_kc.get(), indexVar, lb); + check_error(error); + m_is_dirty = true; +} + +void KnitroModel::set_variable_ub(const VariableIndex &variable, double ub) +{ + KNLONG indexVar = _variable_index(variable); + int error = knitro::KN_set_var_upbnd(m_kc.get(), indexVar, ub); + check_error(error); + m_is_dirty = true; +} + +void KnitroModel::set_variable_bounds(const VariableIndex &variable, double lb, double ub) +{ + set_variable_lb(variable, lb); + set_variable_ub(variable, ub); +} + +double KnitroModel::get_variable_value(const VariableIndex &variable) +{ + if (!m_result.is_valid) + { + throw std::runtime_error("No solution available"); + } + return m_result.x[_variable_index(variable)]; +} + +void KnitroModel::set_variable_start(const VariableIndex &variable, double start) +{ + KNINT indexVar = _variable_index(variable); + int error = knitro::KN_set_var_primal_init_value(m_kc.get(), indexVar, start); + check_error(error); +} + +std::string KnitroModel::get_variable_name(const VariableIndex &variable) +{ + KNINT indexVar = _variable_index(variable); + return _get_name(indexVar, knitro::KN_get_var_name, "x"); +} + +void KnitroModel::set_variable_name(const VariableIndex &variable, const std::string &name) +{ + KNINT indexVar = _variable_index(variable); + _set_name(indexVar, name, knitro::KN_set_var_name); +} + +void KnitroModel::set_variable_domain(const VariableIndex &variable, VariableDomain domain) +{ + KNINT indexVar = _variable_index(variable); + int var_type = knitro_var_type(domain); + int error; + double lb = -KN_INFINITY; + double ub = KN_INFINITY; + + if (var_type == KN_VARTYPE_BINARY) + { + error = knitro::KN_get_var_lobnd(m_kc.get(), indexVar, &lb); + check_error(error); + error = knitro::KN_get_var_upbnd(m_kc.get(), indexVar, &ub); + check_error(error); + } + + error = knitro::KN_set_var_type(m_kc.get(), indexVar, var_type); + check_error(error); + + if (var_type == KN_VARTYPE_BINARY) + { + lb = (lb < 0.0) ? 0.0 : lb; + ub = (ub > 1.0) ? 1.0 : ub; + error = knitro::KN_set_var_lobnd(m_kc.get(), indexVar, lb); + check_error(error); + error = knitro::KN_set_var_upbnd(m_kc.get(), indexVar, ub); + check_error(error); + } + + m_is_dirty = true; +} + +double KnitroModel::get_variable_rc(const VariableIndex &variable) +{ + if (!m_result.is_valid) + { + throw std::runtime_error("No solution available"); + } + KNINT indexVar = _variable_index(variable); + return m_result.lambda[indexVar]; +} + +void KnitroModel::delete_variable(const VariableIndex &variable) +{ + int indexVar = _variable_index(variable); + int error; + + error = knitro::KN_set_var_type(m_kc.get(), indexVar, KN_VARTYPE_CONTINUOUS); + check_error(error); + error = knitro::KN_set_var_lobnd(m_kc.get(), indexVar, -KN_INFINITY); + check_error(error); + error = knitro::KN_set_var_upbnd(m_kc.get(), indexVar, KN_INFINITY); + check_error(error); + + n_vars--; + m_is_dirty = true; +} + +std::string KnitroModel::pprint_variable(const VariableIndex &variable) +{ + return get_variable_name(variable); +} + +KNINT KnitroModel::_variable_index(const VariableIndex &variable) +{ + return variable.index; +} + +KNINT KnitroModel::_constraint_index(const ConstraintIndex &constraint) +{ + return constraint.index; +} + +ConstraintIndex KnitroModel::add_linear_constraint(const ScalarAffineFunction &function, + ConstraintSense sense, double rhs, + const char *name) +{ + auto add = [this](const ScalarAffineFunction &f, const std::tuple &interval, + const char *n) { return add_linear_constraint(f, interval, n); }; + return _add_constraint_with_sense(function, sense, rhs, name, add); +} + +ConstraintIndex KnitroModel::add_linear_constraint(const ScalarAffineFunction &function, + const std::tuple &interval, + const char *name) +{ + auto setter = [this, &function](const ConstraintIndex &constraint) { + _set_linear_constraint(constraint, function); + }; + return _add_constraint_impl(ConstraintType::Linear, interval, name, &n_lincons, setter); +} + +ConstraintIndex KnitroModel::add_quadratic_constraint(const ScalarQuadraticFunction &function, + ConstraintSense sense, double rhs, + const char *name) +{ + auto add = [this](const ScalarQuadraticFunction &f, const std::tuple &interval, + const char *n) { return add_quadratic_constraint(f, interval, n); }; + return _add_constraint_with_sense(function, sense, rhs, name, add); +} + +ConstraintIndex KnitroModel::add_quadratic_constraint(const ScalarQuadraticFunction &function, + const std::tuple &interval, + const char *name) +{ + auto setter = [this, &function](const ConstraintIndex &constraint) { + _set_quadratic_constraint(constraint, function); + }; + return _add_constraint_impl(ConstraintType::Quadratic, interval, name, &n_quadcons, setter); +} + +ConstraintIndex KnitroModel::add_second_order_cone_constraint( + const Vector &variables, const char *name, bool rotated) +{ + + if (rotated) + { + auto setter = [this, &variables](const ConstraintIndex &constraint) { + _set_second_order_cone_constraint_rotated(constraint, variables); + }; + std::pair interval = {0.0, KN_INFINITY}; + return _add_constraint_impl(ConstraintType::Cone, interval, name, nullptr, setter); + } + else + { + auto setter = [this, &variables](const ConstraintIndex &constraint) { + _set_second_order_cone_constraint(constraint, variables); + }; + std::pair interval = {0.0, KN_INFINITY}; + return _add_constraint_impl(ConstraintType::Cone, interval, name, nullptr, setter); + } +} + +ConstraintIndex KnitroModel::add_single_nl_constraint(ExpressionGraph &graph, + const ExpressionHandle &result, + const std::tuple &interval, + const char *name) +{ + graph.add_constraint_output(result); + auto setter = [this, &graph](const ConstraintIndex &constraint) { + _set_nonlinear_constraint(constraint, graph); + }; + + return _add_constraint_impl(ConstraintType::KNITRO_NL, interval, name, nullptr, setter); +} + +std::tuple KnitroModel::_sense_to_interval(ConstraintSense sense, double rhs) +{ + switch (sense) + { + case ConstraintSense::LessEqual: + return {-KN_INFINITY, rhs}; + case ConstraintSense::Equal: + return {rhs, rhs}; + case ConstraintSense::GreaterEqual: + return {rhs, KN_INFINITY}; + default: + throw std::runtime_error("Unknown constraint sense"); + } +} + +void KnitroModel::_update_con_sense_flags(const ConstraintIndex &constraint, ConstraintSense sense) +{ + KNINT indexCon = _constraint_index(constraint); + switch (sense) + { + case ConstraintSense::Equal: + m_con_sense_flags[indexCon] |= CON_LOBND; + break; + case ConstraintSense::GreaterEqual: + m_con_sense_flags[indexCon] = CON_LOBND; + break; + default: + break; + } +} + +void KnitroModel::_set_second_order_cone_constraint(const ConstraintIndex &constraint, + const Vector &variables) +{ + KNINT indexCon = _constraint_index(constraint); + int error; + + KNINT indexCon0; + error = knitro::KN_add_con(m_kc.get(), &indexCon0); + check_error(error); + + KNINT indexVar0 = _variable_index(variables[0]); + error = knitro::KN_add_con_linear_term(m_kc.get(), indexCon0, indexVar0, 1.0); + check_error(error); + + error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon0, 0.0); + check_error(error); + error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon0, KN_INFINITY); + check_error(error); + + error = knitro::KN_add_con_quadratic_term(m_kc.get(), indexCon, indexVar0, indexVar0, 1.0); + check_error(error); + size_t nnz = variables.size() - 1; + std::vector indexVars(nnz); + for (size_t i = 0; i < nnz; ++i) + { + indexVars[i] = _variable_index(variables[i + 1]); + } + std::vector coefs(nnz, -1.0); + error = knitro::KN_add_con_quadratic_struct_one(m_kc.get(), nnz, indexCon, indexVars.data(), + indexVars.data(), coefs.data()); + check_error(error); + + m_aux_cons[indexCon] = indexCon0; +} + +void KnitroModel::_set_second_order_cone_constraint_rotated(const ConstraintIndex &constraint, + const Vector &variables) +{ + KNINT indexCon = _constraint_index(constraint); + int error; + + KNINT indexCon0, indexCon1; + error = knitro::KN_add_con(m_kc.get(), &indexCon0); + check_error(error); + error = knitro::KN_add_con(m_kc.get(), &indexCon1); + check_error(error); + + KNINT indexVar0 = _variable_index(variables[0]); + KNINT indexVar1 = _variable_index(variables[1]); + error = knitro::KN_add_con_linear_term(m_kc.get(), indexCon0, indexVar0, 1.0); + check_error(error); + error = knitro::KN_add_con_linear_term(m_kc.get(), indexCon1, indexVar1, 1.0); + check_error(error); + + error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon0, 0.0); + check_error(error); + error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon0, KN_INFINITY); + check_error(error); + error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon1, 0.0); + check_error(error); + error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon1, KN_INFINITY); + check_error(error); + + size_t nnz = variables.size() - 2; + std::vector indexVars(nnz); + for (size_t i = 0; i < nnz; ++i) + { + indexVars[i] = _variable_index(variables[i + 2]); + } + std::vector coefs(nnz, -1.0); + error = knitro::KN_add_con_quadratic_struct_one(m_kc.get(), nnz, indexCon, indexVars.data(), + indexVars.data(), coefs.data()); + check_error(error); + error = knitro::KN_add_con_quadratic_term(m_kc.get(), indexCon, indexVar0, indexVar1, 2.0); + check_error(error); + + m_aux_cons[indexCon] = std::make_pair(indexCon0, indexCon1); +} + +void KnitroModel::_set_nonlinear_constraint(const ConstraintIndex &constraint, ExpressionGraph &graph) +{ + KNINT indexCon = _constraint_index(constraint); + int error; + + auto& cb_data_ptr = m_con_nl_data_map[indexCon]; + cb_data_ptr = std::make_unique(); + NLCallbackData* cb_data = cb_data_ptr.get(); + + cb_data->idx = graph.m_constraint_outputs.size() - 1; + cb_data->function = cppad_trace_graph_constraints(graph); + cb_data->function.optimize(); + + size_t m = cb_data->function.Range(); + std::vector> jac_sparsity(m); + jac_sparsity[cb_data->idx].insert(cb_data->idx); + cb_data->jac_pattern = cb_data->function.RevSparseJac(m, jac_sparsity); + for (size_t j : cb_data->jac_pattern[cb_data->idx]) + { + cb_data->jac_rows.push_back(cb_data->idx); + cb_data->jac_cols.push_back(j); + } + + cb_data->indexVars.resize(graph.n_variables()); + for (size_t i = 0; i < graph.m_variables.size(); i++) + { + cb_data->indexVars[i] = _variable_index(graph.m_variables[i]); + } + + CB_context* cb = nullptr; + auto cb_eval = [](KN_context*, CB_context*, KN_eval_request* req, KN_eval_result* res, void* data) -> int { + NLCallbackData *cb_data = static_cast(data); + CppAD::ADFun &fun = cb_data->function; + const std::vector &indexVars = cb_data->indexVars; + + std::vector x(indexVars.size()); + for (size_t i = 0; i < indexVars.size(); i++) + { + x[i] = req->x[indexVars[i]]; + } + auto y = fun.Forward(0, x); + res->c[0] = y[cb_data->idx]; + return 0; + }; + + auto cb_grad = [](KN_context*, CB_context*, KN_eval_request* req, KN_eval_result* res, void* data) -> int { + NLCallbackData *cb_data = static_cast(data); + CppAD::ADFun &fun = cb_data->function; + const std::vector &indexVars = cb_data->indexVars; + std::vector x(indexVars.size()); + for (size_t i = 0; i < indexVars.size(); i++) + { + x[i] = req->x[indexVars[i]]; + } + std::vector jac(cb_data->jac_pattern[cb_data->idx].size()); + fun.SparseJacobianReverse(x, cb_data->jac_pattern, cb_data->jac_rows, cb_data->jac_cols, jac, cb_data->jac_work); + for (size_t i = 0; i < jac.size(); i++) + { + res->jac[i] = jac[i]; + } + return 0; + }; + + error = knitro::KN_add_eval_callback(m_kc.get(), FALSE, 1, &indexCon, cb_eval, &cb); + check_error(error); + + error = knitro::KN_set_cb_user_params(m_kc.get(), cb, cb_data); + check_error(error); + + std::vector jacIndexCons(cb_data->jac_rows.size(), indexCon); + std::vector jacIndexVars(cb_data->jac_cols.size()); + for (size_t i = 0; i < cb_data->jac_cols.size(); i++) + { + jacIndexVars[i] = _variable_index(graph.m_variables[cb_data->jac_cols[i]]); + } + error = knitro::KN_set_cb_grad(m_kc.get(), cb, 0, NULL, jacIndexCons.size(), + jacIndexCons.data(), jacIndexVars.data(), cb_grad); + check_error(error); +} + +void KnitroModel::delete_constraint(ConstraintIndex constraint) +{ + KNINT indexCon = _constraint_index(constraint); + int error; + + error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon, -KN_INFINITY); + check_error(error); + error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon, KN_INFINITY); + check_error(error); + + n_cons--; + switch (constraint.type) + { + case ConstraintType::Linear: + n_lincons--; + break; + case ConstraintType::Quadratic: + n_quadcons--; + break; + default: + break; + } + + auto it = m_aux_cons.find(indexCon); + if (it != m_aux_cons.end()) + { + std::vector aux_cons; + if (std::holds_alternative(it->second)) + { + aux_cons.push_back(std::get(it->second)); + } + else if (std::holds_alternative>(it->second)) + { + auto p = std::get>(it->second); + aux_cons.push_back(p.first); + aux_cons.push_back(p.second); + } + + for (auto const &con : aux_cons) + { + error = knitro::KN_set_con_lobnd(m_kc.get(), con, -KN_INFINITY); + check_error(error); + error = knitro::KN_set_con_upbnd(m_kc.get(), con, KN_INFINITY); + check_error(error); + } + + m_aux_cons.erase(it); + } + + m_is_dirty = true; +} + +void KnitroModel::set_constraint_name(const ConstraintIndex &constraint, const std::string &name) +{ + KNINT indexCon = _constraint_index(constraint); + _set_name(indexCon, name, knitro::KN_set_con_name); +} + +std::string KnitroModel::get_constraint_name(const ConstraintIndex &constraint) +{ + KNINT indexCon = _constraint_index(constraint); + return _get_name(indexCon, knitro::KN_get_con_name, "c"); +} + +double KnitroModel::get_constraint_primal(const ConstraintIndex &constraint) +{ + if (!m_result.is_valid) + { + throw std::runtime_error("No solution available"); + } + + KNINT indexCon = _constraint_index(constraint); + return m_result.con_values[indexCon]; +} + +double KnitroModel::get_constraint_dual(const ConstraintIndex &constraint) +{ + if (!m_result.is_valid) + { + throw std::runtime_error("No solution available"); + } + + KNINT indexCon = _constraint_index(constraint); + return m_result.con_duals[indexCon]; +} + +void KnitroModel::set_normalized_rhs(const ConstraintIndex &constraint, double rhs) +{ + KNINT indexCon = _constraint_index(constraint); + auto flag = m_con_sense_flags[indexCon]; + + int error; + + if (flag & CON_LOBND) + { + error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon, rhs); + check_error(error); + } + + if (flag & CON_UPBND) + { + error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon, rhs); + check_error(error); + } + + m_is_dirty = true; +} + +double KnitroModel::get_normalized_rhs(const ConstraintIndex &constraint) +{ + KNINT indexCon = _constraint_index(constraint); + auto flag = m_con_sense_flags[indexCon]; + double rhs; + int error; + if (flag & CON_UPBND) + { + error = knitro::KN_get_con_upbnd(m_kc.get(), indexCon, &rhs); + check_error(error); + } + else + { + error = knitro::KN_get_con_lobnd(m_kc.get(), indexCon, &rhs); + check_error(error); + } + return rhs; +} + +void KnitroModel::set_normalized_coefficient(const ConstraintIndex &constraint, + const VariableIndex &variable, double coefficient) +{ + KNINT indexCon = _constraint_index(constraint); + KNINT indexVar = _variable_index(variable); + int error; + + error = knitro::KN_update(m_kc.get()); + check_error(error); + error = knitro::KN_chg_con_linear_term(m_kc.get(), indexCon, indexVar, coefficient); + check_error(error); + m_is_dirty = true; +} + +void KnitroModel::_set_linear_constraint(const ConstraintIndex &constraint, + const ScalarAffineFunction &function) +{ + KNINT indexCon = _constraint_index(constraint); + int error; + + if (function.constant.has_value()) + { + double constant = function.constant.value(); + if (constant != 0.0) + { + error = knitro::KN_add_con_constant(m_kc.get(), indexCon, constant); + check_error(error); + } + } + + AffineFunctionPtrForm ptr_form; + ptr_form.make(this, function); + + KNLONG nnz = ptr_form.numnz; + if (nnz > 0) + { + KNINT *indexVars = ptr_form.index; + double *coefs = ptr_form.value; + + error = knitro::KN_add_con_linear_struct_one(m_kc.get(), nnz, indexCon, indexVars, coefs); + check_error(error); + } +} + +void KnitroModel::_set_quadratic_constraint(const ConstraintIndex &constraint, + const ScalarQuadraticFunction &function) +{ + KNINT indexCon = _constraint_index(constraint); + int error; + + if (function.affine_part.has_value()) + { + _set_linear_constraint(constraint, function.affine_part.value()); + } + + QuadraticFunctionPtrForm ptr_form; + ptr_form.make(this, function); + KNLONG nnz = ptr_form.numnz; + if (nnz > 0) + { + KNINT *indexVars1 = ptr_form.row; + KNINT *indexVars2 = ptr_form.col; + double *coefs = ptr_form.value; + error = knitro::KN_add_con_quadratic_struct_one(m_kc.get(), nnz, indexCon, indexVars1, + indexVars2, coefs); + check_error(error); + } +} + +void KnitroModel::set_objective(const ScalarAffineFunction &function, ObjectiveSense sense) +{ + _set_objective_impl(sense, [this, &function]() { _set_linear_objective(function); }); +} + +void KnitroModel::set_objective(const ScalarQuadraticFunction &function, ObjectiveSense sense) +{ + _set_objective_impl(sense, [this, &function]() { _set_quadratic_objective(function); }); +} + +void KnitroModel::set_objective(const ExprBuilder &expr, ObjectiveSense sense) +{ + auto degree = expr.degree(); + if (degree <= 1) + { + ScalarAffineFunction linear(expr); + set_objective(linear, sense); + } + else if (degree == 2) + { + ScalarQuadraticFunction quadratic(expr); + set_objective(quadratic, sense); + } + else + { + throw std::runtime_error("Objective must be linear or quadratic"); + } +} + +void KnitroModel::add_single_nl_objective(ExpressionGraph &graph, const ExpressionHandle &result) +{ + graph.add_objective_output(result); + _add_nonlinear_objective(graph); + m_is_dirty = true; + m_result.is_valid = false; +} + +void KnitroModel::_reset_objective() +{ + int error; + + if (m_obj_flag & OBJ_CONSTANT) + { + error = knitro::KN_del_obj_constant(m_kc.get()); + check_error(error); + } + if (m_obj_flag & OBJ_LINEAR) + { + error = knitro::KN_del_obj_linear_struct_all(m_kc.get()); + check_error(error); + } + if (m_obj_flag & OBJ_QUADRATIC) + { + error = knitro::KN_del_obj_quadratic_struct_all(m_kc.get()); + check_error(error); + } + if (m_obj_flag & OBJ_NONLINEAR) + { + error = knitro::KN_del_obj_eval_callback_all(m_kc.get()); + check_error(error); + } + + m_obj_flag = 0; + + error = knitro::KN_update(m_kc.get()); + check_error(error); +} + +void KnitroModel::_set_linear_objective(const ScalarAffineFunction &function) +{ + int error; + + if (function.constant.has_value()) + { + double constant = function.constant.value(); + if (constant != 0.0) + { + error = knitro::KN_add_obj_constant(m_kc.get(), constant); + check_error(error); + m_obj_flag |= OBJ_CONSTANT; + } + } + + AffineFunctionPtrForm ptr_form; + ptr_form.make(this, function); + KNLONG nnz = ptr_form.numnz; + if (nnz > 0) + { + KNINT *indexVars = ptr_form.index; + double *coefs = ptr_form.value; + error = knitro::KN_add_obj_linear_struct(m_kc.get(), nnz, indexVars, coefs); + check_error(error); + m_obj_flag |= OBJ_LINEAR; + } +} + +void KnitroModel::_set_quadratic_objective(const ScalarQuadraticFunction &function) +{ + int error; + + if (function.affine_part.has_value()) + { + _set_linear_objective(function.affine_part.value()); + } + + QuadraticFunctionPtrForm ptr_form; + ptr_form.make(this, function); + KNLONG nnz = ptr_form.numnz; + if (nnz > 0) + { + KNINT *indexVars1 = ptr_form.row; + KNINT *indexVars2 = ptr_form.col; + double *coefs = ptr_form.value; + error = knitro::KN_add_obj_quadratic_struct(m_kc.get(), nnz, indexVars1, indexVars2, coefs); + check_error(error); + m_obj_flag |= OBJ_QUADRATIC; + } +} + +void KnitroModel::_add_nonlinear_objective(ExpressionGraph &graph) +{ + int error; + auto cb_data_ptr = std::make_unique(); + m_obj_nl_data.push_back(std::move(cb_data_ptr)); + NLCallbackData* cb_data = m_obj_nl_data.back().get(); + + cb_data->idx = graph.m_objective_outputs.size() - 1; + cb_data->function = cppad_trace_graph_objective(graph); + cb_data->function.optimize(); + + size_t m = cb_data->function.Range(); + std::vector> jac_sparsity(m); + jac_sparsity[cb_data->idx].insert(cb_data->idx); + cb_data->jac_pattern = cb_data->function.RevSparseJac(m, jac_sparsity); + for (size_t j : cb_data->jac_pattern[cb_data->idx]) + { + cb_data->jac_rows.push_back(cb_data->idx); + cb_data->jac_cols.push_back(j); + } + + cb_data->indexVars.resize(graph.n_variables()); + for (size_t i = 0; i < graph.m_variables.size(); i++) + { + cb_data->indexVars[i] = _variable_index(graph.m_variables[i]); + } + + CB_context* cb = nullptr; + auto cb_eval = [](KN_context*, CB_context*, KN_eval_request* req, KN_eval_result* res, void* data) -> int { + NLCallbackData *cb_data = static_cast(data); + CppAD::ADFun &fun = cb_data->function; + const std::vector &indexVars = cb_data->indexVars; + + std::vector x(indexVars.size()); + for (size_t i = 0; i < indexVars.size(); i++) + { + x[i] = req->x[indexVars[i]]; + } + *res->obj = fun.Forward(0, x)[cb_data->idx]; + return 0; + }; + + auto cb_grad = [](KN_context*, CB_context*, KN_eval_request* req, KN_eval_result* res, void* data) -> int { + NLCallbackData *cb_data = static_cast(data); + CppAD::ADFun &fun = cb_data->function; + const std::vector &indexVars = cb_data->indexVars; + std::vector x(indexVars.size()); + for (size_t i = 0; i < indexVars.size(); i++) + { + x[i] = req->x[indexVars[i]]; + } + std::vector jac(cb_data->jac_pattern[cb_data->idx].size()); + fun.SparseJacobianReverse(x, cb_data->jac_pattern, cb_data->jac_rows, cb_data->jac_cols, jac, cb_data->jac_work); + for (size_t i = 0; i < jac.size(); i++) + { + res->objGrad[i] = jac[i]; + } + return 0; + }; + + error = knitro::KN_add_eval_callback(m_kc.get(), TRUE, 0, NULL, cb_eval, &cb); + check_error(error); + + error = knitro::KN_set_cb_user_params(m_kc.get(), cb, cb_data); + check_error(error); + + std::vector objGradIndexVars(cb_data->jac_cols.size()); + for (size_t i = 0; i < cb_data->jac_cols.size(); i++) + { + objGradIndexVars[i] = _variable_index(graph.m_variables[cb_data->jac_cols[i]]); + } + error = knitro::KN_set_cb_grad(m_kc.get(), cb, objGradIndexVars.size(), objGradIndexVars.data(), + 0, NULL, NULL, cb_grad); + check_error(error); + + m_obj_flag |= OBJ_NONLINEAR; +} + +double KnitroModel::get_obj_value() +{ + if (!m_result.is_valid) + { + throw std::runtime_error("No solution available"); + } + return m_result.obj_val; +} + +void KnitroModel::optimize() +{ + _optimize(); +} + +void KnitroModel::_optimize() +{ + int error = knitro::KN_solve(m_kc.get()); + // Note: KN_solve returns solve status, not an error code + m_solve_status = error; + + // Get solution + KNINT nV, nC; + error = knitro::KN_get_number_vars(m_kc.get(), &nV); + check_error(error); + error = knitro::KN_get_number_cons(m_kc.get(), &nC); + check_error(error); + + m_result.x.resize(nV); + m_result.lambda.resize(nV); + m_result.con_values.resize(nC); + m_result.con_duals.resize(nC); + + error = knitro::KN_get_var_primal_values_all(m_kc.get(), m_result.x.data()); + check_error(error); + + error = knitro::KN_get_var_dual_values_all(m_kc.get(), m_result.lambda.data()); + check_error(error); + for (size_t i = 0; i < nV; i++) + { + m_result.lambda[i] = -m_result.lambda[i]; + } + + if (nC > 0) + { + error = knitro::KN_get_con_values_all(m_kc.get(), m_result.con_values.data()); + check_error(error); + + error = knitro::KN_get_con_dual_values_all(m_kc.get(), m_result.con_duals.data()); + check_error(error); + for (size_t i = 0; i < nC; i++) + { + m_result.con_duals[i] = -m_result.con_duals[i]; + } + } + + error = knitro::KN_get_obj_value(m_kc.get(), &m_result.obj_val); + check_error(error); + + m_result.status = m_solve_status; + m_result.is_valid = true; + m_is_dirty = false; +} diff --git a/lib/knitro_model_ext.cpp b/lib/knitro_model_ext.cpp new file mode 100644 index 00000000..b7c73837 --- /dev/null +++ b/lib/knitro_model_ext.cpp @@ -0,0 +1,241 @@ +#include +#include +#include +#include + +#include "pyoptinterface/knitro_model.hpp" + +namespace nb = nanobind; + +extern void bind_knitro_constants(nb::module_ &m); + +NB_MODULE(knitro_model_ext, m) +{ + m.import_("pyoptinterface._src.core_ext"); + + m.def("is_library_loaded", &knitro::is_library_loaded); + m.def("load_library", &knitro::load_library); + + bind_knitro_constants(m); + +#define BIND_F(f) .def(#f, &KnitroModel::f) + nb::class_(m, "RawModel") + .def(nb::init<>()) + // clang-format off + BIND_F(init) + BIND_F(close) + BIND_F(get_infinity) + // clang-format on + .def_ro("n_vars", &KnitroModel::n_vars) + .def_ro("n_cons", &KnitroModel::n_cons) + .def_ro("n_lincons", &KnitroModel::n_lincons) + .def_ro("n_quadcons", &KnitroModel::n_quadcons) + + .def("add_variable", &KnitroModel::add_variable, + nb::arg("domain") = VariableDomain::Continuous, nb::arg("lb") = -KN_INFINITY, + nb::arg("ub") = KN_INFINITY, nb::arg("name") = "") + + // clang-format off + BIND_F(get_variable_lb) + BIND_F(get_variable_ub) + BIND_F(set_variable_lb) + BIND_F(set_variable_ub) + // clang-format on + .def("set_variable_bounds", &KnitroModel::set_variable_bounds, nb::arg("variable"), + nb::arg("lb"), nb::arg("ub")) + + // clang-format off + BIND_F(set_variable_start) + BIND_F(get_variable_name) + BIND_F(set_variable_name) + BIND_F(set_variable_domain) + // clang-format on + .def("get_variable_rc", &KnitroModel::get_variable_rc, nb::arg("variable")) + .def("delete_variable", &KnitroModel::delete_variable, nb::arg("variable")) + + .def("get_value", &KnitroModel::get_variable_value) + .def("get_value", + nb::overload_cast(&KnitroModel::get_expression_value)) + .def("get_value", + nb::overload_cast(&KnitroModel::get_expression_value)) + .def("get_value", + nb::overload_cast(&KnitroModel::get_expression_value)) + + .def("pprint", &KnitroModel::pprint_variable) + .def("pprint", + nb::overload_cast(&KnitroModel::pprint_expression), + nb::arg("expr"), nb::arg("precision") = 4) + .def("pprint", + nb::overload_cast( + &KnitroModel::pprint_expression), + nb::arg("expr"), nb::arg("precision") = 4) + .def("pprint", nb::overload_cast(&KnitroModel::pprint_expression), + nb::arg("expr"), nb::arg("precision") = 4) + + // clang-format off + BIND_F(set_constraint_name) + BIND_F(get_constraint_name) + BIND_F(get_constraint_primal) + BIND_F(get_constraint_dual) + BIND_F(set_normalized_rhs) + BIND_F(get_normalized_rhs) + BIND_F(set_normalized_coefficient) + // clang-format on + + .def("_add_linear_constraint", + nb::overload_cast( + &KnitroModel::add_linear_constraint), + nb::arg("expr"), nb::arg("sense"), nb::arg("rhs"), nb::arg("name") = "") + .def("_add_linear_constraint", + nb::overload_cast &, + const char *>(&KnitroModel::add_linear_constraint), + nb::arg("expr"), nb::arg("interval"), nb::arg("name") = "") + .def("_add_linear_constraint", &KnitroModel::add_linear_constraint_from_var, + nb::arg("expr"), nb::arg("sense"), nb::arg("rhs"), nb::arg("name") = "") + .def("_add_linear_constraint", &KnitroModel::add_linear_interval_constraint_from_var, + nb::arg("expr"), nb::arg("interval"), nb::arg("name") = "") + .def("_add_linear_constraint", &KnitroModel::add_linear_constraint_from_expr, + nb::arg("expr"), nb::arg("sense"), nb::arg("rhs"), nb::arg("name") = "") + .def("_add_linear_constraint", &KnitroModel::add_linear_interval_constraint_from_expr, + nb::arg("expr"), nb::arg("interval"), nb::arg("name") = "") + + .def("_add_quadratic_constraint", + nb::overload_cast(&KnitroModel::add_quadratic_constraint), + nb::arg("expr"), nb::arg("sense"), nb::arg("rhs"), nb::arg("name") = "") + .def("_add_quadratic_constraint", + nb::overload_cast &, + const char *>(&KnitroModel::add_quadratic_constraint), + nb::arg("expr"), nb::arg("interval"), nb::arg("name") = "") + .def("_add_quadratic_constraint", &KnitroModel::add_quadratic_constraint_from_expr, + nb::arg("expr"), nb::arg("sense"), nb::arg("rhs"), nb::arg("name") = "") + .def("_add_quadratic_constraint", &KnitroModel::add_quadratic_interval_constraint_from_expr, + nb::arg("expr"), nb::arg("interval"), nb::arg("name") = "") + + .def("add_second_order_cone_constraint", &KnitroModel::add_second_order_cone_constraint, + nb::arg("variables"), nb::arg("name") = "", nb::arg("rotated") = false) + + .def("_add_single_nl_constraint", &KnitroModel::add_single_nl_constraint, + nb::arg("graph"), nb::arg("result"), + nb::arg("interval"), nb::arg("name") = "") + .def("_add_single_nl_constraint", + &KnitroModel::add_single_nl_constraint_sense_rhs, + nb::arg("graph"), nb::arg("result"), + nb::arg("sense"), nb::arg("rhs"), nb::arg("name") = "") + .def("_add_single_nl_constraint", + &KnitroModel::add_single_nl_constraint_from_comparison, + nb::arg("graph"), nb::arg("expr"), nb::arg("name") = "") + + .def("delete_constraint", &KnitroModel::delete_constraint, nb::arg("constraint")) + + .def("set_objective", + nb::overload_cast( + &KnitroModel::set_objective), + nb::arg("expr"), nb::arg("sense") = ObjectiveSense::Minimize) + .def("set_objective", + nb::overload_cast( + &KnitroModel::set_objective), + nb::arg("expr"), nb::arg("sense") = ObjectiveSense::Minimize) + .def("set_objective", + nb::overload_cast(&KnitroModel::set_objective), + nb::arg("expr"), nb::arg("sense") = ObjectiveSense::Minimize) + .def("set_objective", + nb::overload_cast( + &KnitroModel::set_objective_as_variable), + nb::arg("expr"), nb::arg("sense") = ObjectiveSense::Minimize) + .def("set_objective", + nb::overload_cast(&KnitroModel::set_objective_as_constant), + nb::arg("expr"), nb::arg("sense") = ObjectiveSense::Minimize) + .def("_add_single_nl_objective", &KnitroModel::add_single_nl_objective, + nb::arg("graph"), nb::arg("result")) + + // clang-format off + BIND_F(get_obj_value) + // clang-format on + + .def("_optimize", &KnitroModel::_optimize, nb::call_guard()) + + .def( + "set_raw_parameter", + [](KnitroModel &m, const std::string &name, int value) { + m.set_raw_parameter(name, value); + }, + nb::arg("name"), nb::arg("value")) + .def( + "set_raw_parameter", + [](KnitroModel &m, const std::string &name, double value) { + m.set_raw_parameter(name, value); + }, + nb::arg("name"), nb::arg("value")) + .def( + "set_raw_parameter", + [](KnitroModel &m, const std::string &name, const std::string &value) { + m.set_raw_parameter(name, value); + }, + nb::arg("name"), nb::arg("value")) + .def( + "set_raw_parameter", + [](KnitroModel &m, int param_id, int value) { + m.set_raw_parameter(param_id, value); + }, + nb::arg("param_id"), nb::arg("value")) + .def( + "set_raw_parameter", + [](KnitroModel &m, int param_id, double value) { + m.set_raw_parameter(param_id, value); + }, + nb::arg("param_id"), nb::arg("value")) + .def( + "set_raw_parameter", + [](KnitroModel &m, int param_id, const std::string &value) { + m.set_raw_parameter(param_id, value); + }, + nb::arg("param_id"), nb::arg("value")) + + .def( + "get_raw_parameter", + [](KnitroModel &m, const std::string &name) -> int { + return m.get_raw_parameter(name); + }, + nb::arg("name")) + .def( + "get_raw_parameter", + [](KnitroModel &m, const std::string &name) -> double { + return m.get_raw_parameter(name); + }, + nb::arg("name")) + .def( + "get_raw_parameter", + [](KnitroModel &m, int param_id) -> int { return m.get_raw_parameter(param_id); }, + nb::arg("param_id")) + .def( + "get_raw_parameter", + [](KnitroModel &m, int param_id) -> double { + return m.get_raw_parameter(param_id); + }, + nb::arg("param_id")) + + .def("get_value", &KnitroModel::get_variable_value) + .def("get_value", + nb::overload_cast(&KnitroModel::get_expression_value)) + .def("get_value", + nb::overload_cast(&KnitroModel::get_expression_value)) + .def("get_value", + nb::overload_cast(&KnitroModel::get_expression_value)) + + .def("pprint", &KnitroModel::pprint_variable) + .def("pprint", + nb::overload_cast(&KnitroModel::pprint_expression), + nb::arg("expr"), nb::arg("precision") = 4) + .def("pprint", + nb::overload_cast( + &KnitroModel::pprint_expression), + nb::arg("expr"), nb::arg("precision") = 4) + .def("pprint", nb::overload_cast(&KnitroModel::pprint_expression), + nb::arg("expr"), nb::arg("precision") = 4) + + .def_rw("m_is_dirty", &KnitroModel::m_is_dirty) + .def_ro("m_solve_status", &KnitroModel::m_solve_status); + +#undef BIND_F +} diff --git a/lib/knitro_model_ext_constants.cpp b/lib/knitro_model_ext_constants.cpp new file mode 100644 index 00000000..de3720a3 --- /dev/null +++ b/lib/knitro_model_ext_constants.cpp @@ -0,0 +1,122 @@ +#include +#include "pyoptinterface/knitro_model.hpp" + +namespace nb = nanobind; + +void bind_knitro_constants(nb::module_ &m) +{ + nb::module_ KN = m.def_submodule("KN"); + + // Objective goal + KN.attr("OBJGOAL_MINIMIZE") = KN_OBJGOAL_MINIMIZE; + KN.attr("OBJGOAL_MAXIMIZE") = KN_OBJGOAL_MAXIMIZE; + + // Variable types + KN.attr("VARTYPE_CONTINUOUS") = KN_VARTYPE_CONTINUOUS; + KN.attr("VARTYPE_INTEGER") = KN_VARTYPE_INTEGER; + KN.attr("VARTYPE_BINARY") = KN_VARTYPE_BINARY; + + // Infinity + KN.attr("INFINITY") = KN_INFINITY; + + // Return codes - Optimal/Satisfactory + KN.attr("RC_OPTIMAL_OR_SATISFACTORY") = KN_RC_OPTIMAL_OR_SATISFACTORY; + KN.attr("RC_OPTIMAL") = KN_RC_OPTIMAL; + + // Return codes - Feasible + KN.attr("RC_NEAR_OPT") = KN_RC_NEAR_OPT; + KN.attr("RC_FEAS_XTOL") = KN_RC_FEAS_XTOL; + KN.attr("RC_FEAS_NO_IMPROVE") = KN_RC_FEAS_NO_IMPROVE; + KN.attr("RC_FEAS_FTOL") = KN_RC_FEAS_FTOL; + KN.attr("RC_FEAS_BEST") = KN_RC_FEAS_BEST; + KN.attr("RC_FEAS_MULTISTART") = KN_RC_FEAS_MULTISTART; + + // Return codes - Infeasible + KN.attr("RC_INFEASIBLE") = KN_RC_INFEASIBLE; + KN.attr("RC_INFEAS_XTOL") = KN_RC_INFEAS_XTOL; + KN.attr("RC_INFEAS_NO_IMPROVE") = KN_RC_INFEAS_NO_IMPROVE; + KN.attr("RC_INFEAS_MULTISTART") = KN_RC_INFEAS_MULTISTART; + KN.attr("RC_INFEAS_CON_BOUNDS") = KN_RC_INFEAS_CON_BOUNDS; + KN.attr("RC_INFEAS_VAR_BOUNDS") = KN_RC_INFEAS_VAR_BOUNDS; + + // Return codes - Unbounded + KN.attr("RC_UNBOUNDED") = KN_RC_UNBOUNDED; + KN.attr("RC_UNBOUNDED_OR_INFEAS") = KN_RC_UNBOUNDED_OR_INFEAS; + + // Return codes - Limit exceeded (feasible) + KN.attr("RC_ITER_LIMIT_FEAS") = KN_RC_ITER_LIMIT_FEAS; + KN.attr("RC_TIME_LIMIT_FEAS") = KN_RC_TIME_LIMIT_FEAS; + KN.attr("RC_FEVAL_LIMIT_FEAS") = KN_RC_FEVAL_LIMIT_FEAS; + KN.attr("RC_MIP_EXH_FEAS") = KN_RC_MIP_EXH_FEAS; + KN.attr("RC_MIP_TERM_FEAS") = KN_RC_MIP_TERM_FEAS; + KN.attr("RC_MIP_SOLVE_LIMIT_FEAS") = KN_RC_MIP_SOLVE_LIMIT_FEAS; + KN.attr("RC_MIP_NODE_LIMIT_FEAS") = KN_RC_MIP_NODE_LIMIT_FEAS; + + // Return codes - Limit exceeded (infeasible) + KN.attr("RC_ITER_LIMIT_INFEAS") = KN_RC_ITER_LIMIT_INFEAS; + KN.attr("RC_TIME_LIMIT_INFEAS") = KN_RC_TIME_LIMIT_INFEAS; + KN.attr("RC_FEVAL_LIMIT_INFEAS") = KN_RC_FEVAL_LIMIT_INFEAS; + KN.attr("RC_MIP_EXH_INFEAS") = KN_RC_MIP_EXH_INFEAS; + KN.attr("RC_MIP_SOLVE_LIMIT_INFEAS") = KN_RC_MIP_SOLVE_LIMIT_INFEAS; + KN.attr("RC_MIP_NODE_LIMIT_INFEAS") = KN_RC_MIP_NODE_LIMIT_INFEAS; + + // Return codes - Other failures + KN.attr("RC_CALLBACK_ERR") = KN_RC_CALLBACK_ERR; + KN.attr("RC_LP_SOLVER_ERR") = KN_RC_LP_SOLVER_ERR; + KN.attr("RC_EVAL_ERR") = KN_RC_EVAL_ERR; + KN.attr("RC_OUT_OF_MEMORY") = KN_RC_OUT_OF_MEMORY; + KN.attr("RC_USER_TERMINATION") = KN_RC_USER_TERMINATION; + KN.attr("RC_OPEN_FILE_ERR") = KN_RC_OPEN_FILE_ERR; + + // Return codes - Problem definition errors + KN.attr("RC_BAD_N_OR_F") = KN_RC_BAD_N_OR_F; + KN.attr("RC_BAD_CONSTRAINT") = KN_RC_BAD_CONSTRAINT; + KN.attr("RC_BAD_JACOBIAN") = KN_RC_BAD_JACOBIAN; + KN.attr("RC_BAD_HESSIAN") = KN_RC_BAD_HESSIAN; + KN.attr("RC_BAD_CON_INDEX") = KN_RC_BAD_CON_INDEX; + KN.attr("RC_BAD_JAC_INDEX") = KN_RC_BAD_JAC_INDEX; + KN.attr("RC_BAD_HESS_INDEX") = KN_RC_BAD_HESS_INDEX; + KN.attr("RC_BAD_CON_BOUNDS") = KN_RC_BAD_CON_BOUNDS; + KN.attr("RC_BAD_VAR_BOUNDS") = KN_RC_BAD_VAR_BOUNDS; + KN.attr("RC_ILLEGAL_CALL") = KN_RC_ILLEGAL_CALL; + KN.attr("RC_BAD_KCPTR") = KN_RC_BAD_KCPTR; + KN.attr("RC_NULL_POINTER") = KN_RC_NULL_POINTER; + KN.attr("RC_BAD_INIT_VALUE") = KN_RC_BAD_INIT_VALUE; + KN.attr("RC_LICENSE_ERROR") = KN_RC_LICENSE_ERROR; + KN.attr("RC_BAD_PARAMINPUT") = KN_RC_BAD_PARAMINPUT; + KN.attr("RC_LINEAR_SOLVER_ERR") = KN_RC_LINEAR_SOLVER_ERR; + KN.attr("RC_DERIV_CHECK_FAILED") = KN_RC_DERIV_CHECK_FAILED; + KN.attr("RC_DERIV_CHECK_TERMINATE") = KN_RC_DERIV_CHECK_TERMINATE; + KN.attr("RC_OVERFLOW_ERR") = KN_RC_OVERFLOW_ERR; + KN.attr("RC_BAD_SIZE") = KN_RC_BAD_SIZE; + KN.attr("RC_BAD_VARIABLE") = KN_RC_BAD_VARIABLE; + KN.attr("RC_BAD_VAR_INDEX") = KN_RC_BAD_VAR_INDEX; + KN.attr("RC_BAD_OBJECTIVE") = KN_RC_BAD_OBJECTIVE; + KN.attr("RC_BAD_OBJ_INDEX") = KN_RC_BAD_OBJ_INDEX; + KN.attr("RC_BAD_RESIDUAL") = KN_RC_BAD_RESIDUAL; + KN.attr("RC_BAD_RSD_INDEX") = KN_RC_BAD_RSD_INDEX; + KN.attr("RC_INTERNAL_ERROR") = KN_RC_INTERNAL_ERROR; + + // Callback request codes + KN.attr("RC_EVALFC") = KN_RC_EVALFC; + KN.attr("RC_EVALGA") = KN_RC_EVALGA; + KN.attr("RC_EVALH") = KN_RC_EVALH; + KN.attr("RC_EVALHV") = KN_RC_EVALHV; + KN.attr("RC_EVALH_NO_F") = KN_RC_EVALH_NO_F; + KN.attr("RC_EVALHV_NO_F") = KN_RC_EVALHV_NO_F; + KN.attr("RC_EVALR") = KN_RC_EVALR; + KN.attr("RC_EVALRJ") = KN_RC_EVALRJ; + KN.attr("RC_EVALFCGA") = KN_RC_EVALFCGA; + + // Parameter types + KN.attr("PARAMTYPE_INTEGER") = KN_PARAMTYPE_INTEGER; + KN.attr("PARAMTYPE_FLOAT") = KN_PARAMTYPE_FLOAT; + KN.attr("PARAMTYPE_STRING") = KN_PARAMTYPE_STRING; + + // Common parameters (selection from knitro.h) + KN.attr("PARAM_ALG") = KN_PARAM_ALG; + KN.attr("PARAM_HONORBNDS") = KN_PARAM_HONORBNDS; + KN.attr("PARAM_MAXIT") = KN_PARAM_MAXIT; + KN.attr("PARAM_OUTLEV") = KN_PARAM_OUTLEV; + KN.attr("PARAM_OUTMODE") = KN_PARAM_OUTMODE; +} diff --git a/src/pyoptinterface/_src/knitro.py b/src/pyoptinterface/_src/knitro.py new file mode 100644 index 00000000..8f5e18bf --- /dev/null +++ b/src/pyoptinterface/_src/knitro.py @@ -0,0 +1,412 @@ +import os +import platform +from pathlib import Path +import re +import logging +from typing import Tuple, Union, overload + +from .knitro_model_ext import RawModel, KN, load_library +from .matrix import add_matrix_constraints +from .attributes import ( + VariableAttribute, + ConstraintAttribute, + ModelAttribute, + ResultStatusCode, + TerminationStatusCode, +) +from .core_ext import ( + VariableIndex, + ConstraintIndex, + ConstraintType, + ConstraintSense, + ScalarAffineFunction, + ScalarQuadraticFunction, + ExprBuilder, +) +from .comparison_constraint import ComparisonConstraint +from .solver_common import ( + _get_model_attribute, + _set_model_attribute, + _get_entity_attribute, + _set_entity_attribute, +) +from .aml import make_variable_ndarray, make_variable_tupledict +from .nlexpr_ext import ExpressionHandle +from .nlfunc import ExpressionGraphContext, convert_to_expressionhandle + + +def detected_libraries(): + libs = [] + + subdir = { + "Linux": "lib", + "Darwin": "lib", + "Windows": "bin", + }[platform.system()] + libname_pattern = { + "Linux": r"libknitro\.so.*", + "Darwin": r"libknitro\.dylib.*", + "Windows": r"knitro\d*\.dll", + }[platform.system()] + suffix_pattern = { + "Linux": "*.so*", + "Darwin": "*.dylib*", + "Windows": "*.dll", + }[platform.system()] + + # Environment variable + knitro_dir = os.environ.get("KNITRODIR", None) + if knitro_dir and os.path.exists(knitro_dir): + dir = Path(knitro_dir) / subdir + if dir.exists(): + for path in dir.glob(suffix_pattern): + match = re.match(libname_pattern, path.name) + if match: + libs.append(str(path)) + + # Default library names + default_libname = { + "Linux": ["libknitro.so"], + "Darwin": ["libknitro.dylib"], + "Windows": ["knitro.dll"], + }[platform.system()] + libs.extend(default_libname) + + return libs + + +def autoload_library(): + libs = detected_libraries() + for lib in libs: + ret = load_library(lib) + if ret: + logging.info(f"Loaded KNITRO library: {lib}") + return True + return False + + +autoload_library() + + +# Variable Attribute +variable_attribute_get_func_map = { + VariableAttribute.Value: lambda model, v: model.get_value(v), + VariableAttribute.LowerBound: lambda model, v: model.get_variable_lb(v), + VariableAttribute.UpperBound: lambda model, v: model.get_variable_ub(v), + VariableAttribute.Name: lambda model, v: model.get_variable_name(v), + VariableAttribute.ReducedCost: lambda model, v: model.get_variable_rc(v) +} + +variable_attribute_set_func_map = { + VariableAttribute.LowerBound: lambda model, v, x: model.set_variable_lb(v, x), + VariableAttribute.UpperBound: lambda model, v, x: model.set_variable_ub(v, x), + VariableAttribute.PrimalStart: lambda model, v, x: model.set_variable_start(v, x), + VariableAttribute.Name: lambda model, v, x: model.set_variable_name(v, x), + VariableAttribute.Domain: lambda model, v, x: model.set_variable_domain(v, x), +} + +# Constraint Attribute +constraint_attribute_get_func_map = { + ConstraintAttribute.Primal: lambda model, c: model.get_constraint_primal(c), + ConstraintAttribute.Dual: lambda model, c: model.get_constraint_dual(c), + ConstraintAttribute.Name: lambda model, c: model.get_constraint_name(c), +} + +constraint_attribute_set_func_map = { + ConstraintAttribute.Name: lambda model, c, x: model.set_constraint_name(c, x), +} + +# Status code mapping +_RAW_STATUS_STRINGS = [ # TerminationStatus, RawStatusCode + (TerminationStatusCode.OPTIMAL, KN.RC_OPTIMAL), + (TerminationStatusCode.OPTIMAL, KN.RC_OPTIMAL_OR_SATISFACTORY), + (TerminationStatusCode.LOCALLY_SOLVED, KN.RC_NEAR_OPT), + (TerminationStatusCode.LOCALLY_SOLVED, KN.RC_FEAS_XTOL), + (TerminationStatusCode.LOCALLY_SOLVED, KN.RC_FEAS_NO_IMPROVE), + (TerminationStatusCode.LOCALLY_SOLVED, KN.RC_FEAS_FTOL), + (TerminationStatusCode.LOCALLY_SOLVED, KN.RC_FEAS_BEST), + (TerminationStatusCode.LOCALLY_SOLVED, KN.RC_FEAS_MULTISTART), + (TerminationStatusCode.INFEASIBLE, KN.RC_INFEASIBLE), + (TerminationStatusCode.INFEASIBLE, KN.RC_INFEAS_XTOL), + (TerminationStatusCode.INFEASIBLE, KN.RC_INFEAS_NO_IMPROVE), + (TerminationStatusCode.INFEASIBLE, KN.RC_INFEAS_MULTISTART), + (TerminationStatusCode.INFEASIBLE, KN.RC_INFEAS_CON_BOUNDS), + (TerminationStatusCode.INFEASIBLE, KN.RC_INFEAS_VAR_BOUNDS), + (TerminationStatusCode.DUAL_INFEASIBLE, KN.RC_UNBOUNDED), + (TerminationStatusCode.INFEASIBLE_OR_UNBOUNDED, KN.RC_UNBOUNDED_OR_INFEAS), + (TerminationStatusCode.ITERATION_LIMIT, KN.RC_ITER_LIMIT_FEAS), + (TerminationStatusCode.ITERATION_LIMIT, KN.RC_ITER_LIMIT_INFEAS), + (TerminationStatusCode.TIME_LIMIT, KN.RC_TIME_LIMIT_FEAS), + (TerminationStatusCode.TIME_LIMIT, KN.RC_TIME_LIMIT_INFEAS), + (TerminationStatusCode.OTHER_LIMIT, KN.RC_FEVAL_LIMIT_FEAS), + (TerminationStatusCode.OTHER_LIMIT, KN.RC_FEVAL_LIMIT_INFEAS), + (TerminationStatusCode.SOLUTION_LIMIT, KN.RC_MIP_EXH_FEAS), + (TerminationStatusCode.SOLUTION_LIMIT, KN.RC_MIP_EXH_INFEAS), + (TerminationStatusCode.NODE_LIMIT, KN.RC_MIP_NODE_LIMIT_FEAS), + (TerminationStatusCode.NODE_LIMIT, KN.RC_MIP_NODE_LIMIT_INFEAS), + (TerminationStatusCode.INTERRUPTED, KN.RC_USER_TERMINATION), + (TerminationStatusCode.NUMERICAL_ERROR, KN.RC_EVAL_ERR), + (TerminationStatusCode.MEMORY_LIMIT, KN.RC_OUT_OF_MEMORY), + (TerminationStatusCode.OTHER_ERROR, KN.RC_CALLBACK_ERR), + (TerminationStatusCode.OTHER_ERROR, KN.RC_LP_SOLVER_ERR), + (TerminationStatusCode.OTHER_ERROR, KN.RC_LINEAR_SOLVER_ERR), + (TerminationStatusCode.OTHER_ERROR, KN.RC_INTERNAL_ERROR), +] + + +def _termination_status_knitro(model: "Model"): + status_code = model.m_solve_status + for ts, rs in _RAW_STATUS_STRINGS: + if status_code == rs: + return ts + return TerminationStatusCode.OTHER_ERROR + + +def _result_status_knitro(model: "Model"): + status_code = model.m_solve_status + if status_code == KN.RC_OPTIMAL or status_code == KN.RC_OPTIMAL_OR_SATISFACTORY: + return ResultStatusCode.FEASIBLE_POINT + elif status_code in [KN.RC_NEAR_OPT, KN.RC_FEAS_XTOL, KN.RC_FEAS_NO_IMPROVE, + KN.RC_FEAS_FTOL, KN.RC_FEAS_BEST, KN.RC_FEAS_MULTISTART]: + return ResultStatusCode.FEASIBLE_POINT + elif status_code in [KN.RC_INFEASIBLE, KN.RC_INFEAS_XTOL, KN.RC_INFEAS_NO_IMPROVE, + KN.RC_INFEAS_MULTISTART, KN.RC_INFEAS_CON_BOUNDS, KN.RC_INFEAS_VAR_BOUNDS]: + return ResultStatusCode.INFEASIBLE_POINT + elif status_code in [KN.RC_ITER_LIMIT_FEAS, KN.RC_TIME_LIMIT_FEAS, KN.RC_FEVAL_LIMIT_FEAS, + KN.RC_MIP_EXH_FEAS, KN.RC_MIP_TERM_FEAS, KN.RC_MIP_SOLVE_LIMIT_FEAS, + KN.RC_MIP_NODE_LIMIT_FEAS]: + return ResultStatusCode.FEASIBLE_POINT + elif status_code in [KN.RC_ITER_LIMIT_INFEAS, KN.RC_TIME_LIMIT_INFEAS, KN.RC_FEVAL_LIMIT_INFEAS, + KN.RC_MIP_EXH_INFEAS, KN.RC_MIP_SOLVE_LIMIT_INFEAS, KN.RC_MIP_NODE_LIMIT_INFEAS]: + return ResultStatusCode.INFEASIBLE_POINT + return ResultStatusCode.NO_SOLUTION + + +model_attribute_get_func_map = { + ModelAttribute.ObjectiveValue: lambda model: model.get_obj_value(), + ModelAttribute.TerminationStatus: _termination_status_knitro, + ModelAttribute.RawStatusString: lambda model: f"KNITRO status code: {model.m_solve_status}", + ModelAttribute.DualStatus: _result_status_knitro, + ModelAttribute.PrimalStatus: _result_status_knitro, +} + + +class Model(RawModel): + """ + KNITRO model class for PyOptInterface. + """ + + def __init__(self): + super().__init__() + self._variable_tupledict_maker = None + self._constraint_tupledict_maker = None + + @staticmethod + def supports_variable_attribute(attribute: VariableAttribute, setable: bool = False) -> bool: + if setable: + return attribute in variable_attribute_set_func_map + else: + return attribute in variable_attribute_get_func_map + + @staticmethod + def supports_constraint_attribute(attribute: ConstraintAttribute, setable: bool = False) -> bool: + if setable: + return attribute in constraint_attribute_set_func_map + else: + return attribute in constraint_attribute_get_func_map + + def number_of_variables(self): + return self.n_vars + + def number_of_constraints(self, constraint_type: Union[ConstraintType, None] = None): + if constraint_type is None: + return self.n_cons + elif constraint_type == ConstraintType.Linear: + return self.n_lincons + elif constraint_type == ConstraintType.Quadratic: + return self.n_quadcons + else: + raise ValueError(f"Unknown constraint type: {constraint_type}") + + @overload + def add_linear_constraint( + self, + expr: Union[VariableIndex, ScalarAffineFunction, ExprBuilder], + sense: ConstraintSense, + rhs: float, + name: str = "", + ): ... + + @overload + def add_linear_constraint( + self, + expr: Union[VariableIndex, ScalarAffineFunction, ExprBuilder], + interval: Tuple[float, float], + name: str = "", + ): ... + + @overload + def add_linear_constraint( + self, + con: ComparisonConstraint, + name: str = "", + ): ... + + def add_linear_constraint(self, arg, *args, **kwargs): + if isinstance(arg, ComparisonConstraint): + return self._add_linear_constraint( + arg.lhs, arg.sense, arg.rhs, *args, **kwargs + ) + else: + return self._add_linear_constraint(arg, *args, **kwargs) + + @overload + def add_quadratic_constraint( + self, + expr: Union[ScalarQuadraticFunction, ExprBuilder], + sense: ConstraintSense, + rhs: float, + name: str = "", + ): ... + + @overload + def add_quadratic_constraint( + self, + con: ComparisonConstraint, + name: str = "", + ): ... + + def add_quadratic_constraint(self, arg, *args, **kwargs): + if isinstance(arg, ComparisonConstraint): + return self._add_quadratic_constraint( + arg.lhs, arg.sense, arg.rhs, *args, **kwargs + ) + else: + return self._add_quadratic_constraint(arg, *args, **kwargs) + + @overload + def add_nl_constraint( + self, + expr, + sense: ConstraintSense, + rhs: float, + /, + name: str = "", + ): ... + + @overload + def add_nl_constraint( + self, + expr, + interval: Tuple[float, float], + /, + name: str = "", + ): ... + + @overload + def add_nl_constraint( + self, + con, + /, + name: str = "", + ): ... + + def add_nl_constraint(self, expr, *args, **kwargs): + graph = ExpressionGraphContext.current_graph() + expr = convert_to_expressionhandle(graph, expr) + if not isinstance(expr, ExpressionHandle): + raise ValueError("Expression should be convertible to ExpressionHandle") + return self._add_single_nl_constraint(graph, expr, *args, **kwargs) + + def add_nl_objective(self, expr): + graph = ExpressionGraphContext.current_graph() + expr = convert_to_expressionhandle(graph, expr) + if not isinstance(expr, ExpressionHandle): + raise ValueError("Expression should be convertible to ExpressionHandle") + self._add_single_nl_objective(graph, expr) + + def get_model_attribute(self, attr: ModelAttribute): + def e(attribute): + raise ValueError(f"Unknown model attribute to get: {attribute}") + + return _get_model_attribute( + self, attr, model_attribute_get_func_map, {}, e + ) + + def set_model_attribute(self, attr: ModelAttribute, value): + def e(attribute): + raise ValueError(f"Unknown model attribute to set: {attribute}") + + _set_model_attribute(self, attr, value, {}, {}, e) + + def get_variable_attribute( + self, variable: VariableIndex, attr: VariableAttribute + ): + def e(attribute): + raise ValueError(f"Unknown variable attribute to get: {attribute}") + + return _get_entity_attribute( + self, + variable, + attr, + variable_attribute_get_func_map, + {}, + e, + ) + + def set_variable_attribute( + self, variable: VariableIndex, attr: VariableAttribute, value + ): + def e(attribute): + raise ValueError(f"Unknown variable attribute to set: {attribute}") + + _set_entity_attribute( + self, + variable, + attr, + value, + variable_attribute_set_func_map, + {}, + e, + ) + + def get_constraint_attribute( + self, constraint: ConstraintIndex, attr: ConstraintAttribute + ): + def e(attribute): + raise ValueError(f"Unknown constraint attribute to get: {attribute}") + + return _get_entity_attribute( + self, + constraint, + attr, + constraint_attribute_get_func_map, + {}, + e, + ) + + def set_constraint_attribute( + self, constraint: ConstraintIndex, attr: ConstraintAttribute, value + ): + def e(attribute): + raise ValueError(f"Unknown constraint attribute to set: {attribute}") + + _set_entity_attribute( + self, + constraint, + attr, + value, + constraint_attribute_set_func_map, + {}, + ) + + def optimize(self): + self._optimize() + + def is_empty(self): + return not self or self.m_is_dirty + + +Model.add_variables = make_variable_tupledict +Model.add_m_variables = make_variable_ndarray +Model.add_m_linear_constraints = add_matrix_constraints diff --git a/src/pyoptinterface/knitro.py b/src/pyoptinterface/knitro.py new file mode 100644 index 00000000..75c20d81 --- /dev/null +++ b/src/pyoptinterface/knitro.py @@ -0,0 +1,14 @@ +from pyoptinterface._src.knitro import Model, autoload_library +from pyoptinterface._src.knitro_model_ext import ( + KN, + load_library, + is_library_loaded, +) + +__all__ = [ + "Model", + "KN", + "autoload_library", + "load_library", + "is_library_loaded", +] diff --git a/tests/conftest.py b/tests/conftest.py index 7f932bda..1ea753cc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ import pytest import platform -from pyoptinterface import gurobi, xpress, copt, mosek, highs, ipopt +from pyoptinterface import gurobi, xpress, copt, mosek, highs, ipopt, knitro nlp_model_dict = {} @@ -23,6 +23,8 @@ def c(): if copt.is_library_loaded(): nlp_model_dict["copt"] = copt.Model +if knitro.is_library_loaded(): + nlp_model_dict["knitro"] = knitro.Model @pytest.fixture(params=nlp_model_dict.keys()) def nlp_model_ctor(request): @@ -43,6 +45,8 @@ def nlp_model_ctor(request): model_interface_dict["mosek"] = mosek.Model if highs.is_library_loaded(): model_interface_dict["highs"] = highs.Model +if knitro.is_library_loaded(): + model_interface_dict["knitro"] = knitro.Model @pytest.fixture(params=model_interface_dict.keys()) @@ -50,3 +54,4 @@ def model_interface(request): name = request.param model_interface_class = model_interface_dict[name] return model_interface_class() + diff --git a/thirdparty/solvers/knitro/knitro.h b/thirdparty/solvers/knitro/knitro.h new file mode 100644 index 00000000..73dc3091 --- /dev/null +++ b/thirdparty/solvers/knitro/knitro.h @@ -0,0 +1,3710 @@ +/*******************************************************/ +/* Copyright (c) 2025 by Artelys */ +/* All Rights Reserved */ +/*******************************************************/ + +/* Artelys Knitro 15.1.0 application interface header file. */ + +#ifndef KNITRO_H__ +#define KNITRO_H__ + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#include +#include + +/*------------------------------------------------------------------*/ +/* EXPORT MACROS */ +/*------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 + #ifdef MAKE_KNITRO_DLL + #define KNITRO_API __declspec(dllexport) __stdcall + #else + #define KNITRO_API __stdcall + #endif +#else + #if (__GNUC__ >= 4) || (__INTEL_COMPILER) + #define KNITRO_API __attribute__ ((visibility ("default"))) + #else + #define KNITRO_API + #endif +#endif + +/* Define Knitro integer types. Use KNLONG to support 64-bit integers. */ +typedef int KNINT; +#ifdef _WIN64 + typedef __int64 KNLONG; +#elif _WIN32 + typedef int KNLONG; +#else + typedef long long KNLONG; +#endif +typedef KNINT KNBOOL; +#define KNTRUE 1 +#define KNFALSE 0 + +/*------------------------------------------------------------------*/ +/* FORWARD DECLARATION TYPE DEFINITIONS */ +/*------------------------------------------------------------------*/ + +/** Type declaration for the Knitro solver context object. + * All methods pass a pointer to the solver. + * Applications must not modify any part of the solver context, + * except by making Knitro API calls. + */ +typedef struct KN_context KN_context, *KN_context_ptr; + +/** Type declaration for the Artelys License Manager context object. + * Applications must not modify any part of the context. + */ +typedef struct LM_context LM_context, *LM_context_ptr; + +/*------------------------------------------------------------------*/ +/* FUNCTION DECLARATIONS */ +/*------------------------------------------------------------------*/ + +/** Copy the Knitro release name into "release". This variable must be + * preallocated to have "length" elements, including the string termination + * character. For compatibility with future releases, please allocate at + * least 15 characters. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_release (const int length, + char * const release); + +/* ----- Creating and destroying solver objects ----- */ + +/** Call KN_new first. This routine returns a pointer to the + * solver object that is used in all other Knitro API calls in the + * argument "kc". The "kc" pointer is set to NULL if there is an error. + * A new Knitro license is acquired and held until KN_free has been + * called, or until the calling program ends. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_new (KN_context_ptr * kc); + + +/** Free all memory and release any Knitro license acquired by calling + * KN_new. The address of the pointer is set to NULL after freeing + * memory, to help avoid mistakes. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_free (KN_context_ptr * kc); + +/* ----- Creating and destroying solvers in high volume ----- */ + +/** High volume applications construct new Knitro instances repeatedly, + * each lasting only a short time. Special functions allow a single + * license to be checked out once for a sequence of Knitro instances. + * Re-using a license saves time in the Knitro solver, and improves the + * performance of the Artelys License Manager server. + * + * The typical calling sequence is: + * KN_checkout_license + * KN_new_lm + * KN_add* (setup problem structure) + * KN_solve + * KN_free + * KN_new_lm + * KN_add* (setup problem structure) + * KN_solve + * KN_free + * ... + * KN_release_license + * + * Please see the Artelys License Manager user manual for more information. + */ + +/** Allocate memory for a license from the Artelys License Manager for high + * volume Knitro applications. The license object is returned in the "lmc" + * argument and will be set to NULL if there is an error. Otherwise, the + * license will be checked out the first time KN_new_lm is called. The + * license must be checked in later by calling KN_release_license. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_checkout_license (LM_context_ptr * lmc); + +/** Returns a pointer to the solver object that is used in all other Knitro + * API calls in the argument "kc". The "kc" pointer is set to NULL if + * there is an error. Pass in the license object "lmc" acquired by first + * calling KN_checkout_license. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_new_lm (LM_context_ptr lmc, + KN_context_ptr * kc); + +/** Release the Knitro license and free allocated memory. + * Knitro will set the address of the pointer to NULL after freeing + * memory, to help avoid mistakes. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_release_license (LM_context_ptr * lmc); + + +/* ----- Changing and reading solver parameters ----- */ + +/** All methods return 0 if OK, nonzero if there was an error. + * In most cases, parameter values are not validated until + * KN_solve is called. + */ + +/** Reset all parameters to default values. + */ +int KNITRO_API KN_reset_params_to_defaults (KN_context_ptr kc); + + +/** Set all parameters specified in the given file. */ +int KNITRO_API KN_load_param_file + (KN_context_ptr kc, const char * const filename); + +/** Similar to KN_load_param_file (defined above) but specifically allows + * the user to specify a file of options (and option values) to explore for + * the Knitro-Tuner. + */ +int KNITRO_API KN_load_tuner_file + (KN_context_ptr kc, const char * const filename); + +/** Write all current parameter values to a file. */ +int KNITRO_API KN_save_param_file + (KN_context_ptr kc, const char * const filename); + +/** Set a parameter using its string name. */ +int KNITRO_API KN_set_int_param_by_name + (KN_context_ptr kc, const char * const name, const int value); +int KNITRO_API KN_set_char_param_by_name + (KN_context_ptr kc, const char * const name, const char * const value); +int KNITRO_API KN_set_double_param_by_name + (KN_context_ptr kc, const char * const name, const double value); +/** Set an integer or double parameter by name. */ +int KNITRO_API KN_set_param_by_name + (KN_context_ptr kc, const char * const name, const double value); + +/** Set a parameter using its integer identifier KN_PARAM_x + * (defined at the end of this file). */ +int KNITRO_API KN_set_int_param + (KN_context_ptr kc, const int param_id, const int value); +int KNITRO_API KN_set_char_param + (KN_context_ptr kc, const int param_id, const char * const value); +int KNITRO_API KN_set_double_param + (KN_context_ptr kc, const int param_id, const double value); + +/** Get a parameter using its string name. */ +int KNITRO_API KN_get_int_param_by_name + (KN_context_ptr kc, const char * const name, int * const value); +int KNITRO_API KN_get_double_param_by_name + (KN_context_ptr kc, const char * const name, double * const value); + +/** Get a parameter using its integer identifier KN_PARAM_x + * (defined at the end of this file). */ +int KNITRO_API KN_get_int_param + (KN_context_ptr kc, const int param_id, int * const value); +int KNITRO_API KN_get_double_param + (KN_context_ptr kc, const int param_id, double * const value); + +/** Sets the string param_name with the name of parameter indexed by + * param_id and returns 0. Returns an error if param_id does not + * correspond to any parameter, or if the parameter output_size + * (the size of char array param_name) is less than the size of the + * parameter's description. + */ +int KNITRO_API KN_get_param_name( KN_context_ptr kc, + const int param_id, + char * const param_name, + const size_t output_size); + +/** Sets the string description with the description of the parameter + * indexed by param_id and its possible values and returns 0. Returns an + * error if param_id does not correspond to any parameter, or if the + * parameter output_size (the size of char array description) is less than + * the size of the parameter's description. + */ +int KNITRO_API KN_get_param_doc( KN_context_ptr kc, + const int param_id, + char * const description, + const size_t output_size); + +/** Sets the int * param_type to the parameter type of parameter indexed + * by param_id. Possible values are KN_PARAMTYPE_INTEGER, KN_PARAMTYPE_FLOAT, + * KN_PARAMTYPE_STRING. Returns an error if param_id does not correspond to + * any parameter. + */ +int KNITRO_API KN_get_param_type( KN_context_ptr kc, + const int param_id, + int * const param_type); + +/** Set the int * num_param_values to the number of possible parameter + * values for parameter indexed by param_id and returns 0. If there is + * not a finite number of possible values, num_param_values will be zero. + * Returns an error if param_id does not correspond to any parameter. + */ +int KNITRO_API KN_get_num_param_values( KN_context_ptr kc, + const int param_id, + int * const num_param_values); + +/** Set string param_value_string to the description of value value_id for the + * parameter param_id. Returns an error if param_id does not + * correspond to any parameter, or if value_id is not among possible parameter + * values, or if there are not a finite number of possible parameter values, + * or if the parameter output_size (the size of char array param_value_string) + * is less than the size of the parameter's description. + */ +int KNITRO_API KN_get_param_value_doc( KN_context_ptr kc, + const int param_id, + const int value_id, + char * const param_value_string, + const size_t output_size); + +/** Set string param_value_string to the description of the i-th possible value + * for the parameter param_id. Returns an error if param_id does not + * correspond to any parameter, or if value_index is greater than the number + * of possible parameter values (see KN_get_num_param_values), or if there are + * not a finite number of possible parameter values, or if the parameter + * output_size (the size of char array param_value_string) is less than the + * size of the parameter's description. + */ +int KNITRO_API KN_get_param_value_doc_from_index( KN_context_ptr kc, + const int param_id, + const int value_index, + char * const param_value_string, + const size_t output_size); + +/** Gets the integer value corresponding to the parameter name input and + * copies it into param_id input. Returns zero if successful and an error + * code otherwise. + */ +int KNITRO_API KN_get_param_id( KN_context_ptr kc, + const char * const name, + int * const param_id); + +/** + * Sets the integer param_id with the id of the parameter indexed by param_index + * (between 0 and the number of existing parameters). Returns an error if the + * param_index is non-positive or greater or equal than the number of existing + * parameters. + */ +int KNITRO_API KN_get_param_id_from_index( KN_context_ptr kc, + int* param_id, + const int param_index); + +/** + * Write a JSON file describing all available parameters. + * Returns zero if successful and an error code otherwise. + */ +int KNITRO_API KN_write_param_desc_file( KN_context_ptr kc, + const char * const filepath); + + +/* ----- Problem construction ----- */ + +/** The Knitro API is designed to provide the user maximum flexibility and + * ease-of-use in building a model. In addition, it is designed to provide + * Knitro detailed structural information about the user model, so that + * Knitro can exploit special structures wherever possible to improve + * performance. The user can use the API to build up a model in pieces in + * the way that is most convenient and intuitive for the user. + * + * The API is designed so the user can load constant, linear, quadratic, + * and conic structures as well as complementarity constraints separately. + * This allows Knitro to mark these structure types internally and provide + * special care to different structure types. In addition, the more + * structural information Knitro has, the more extensive presolve operations + * Knitro can perform to try to simplify the model internally. For this + * reason we always recommend making use of the API functions below to provide + * as much fine-grained structural information as possible to Knitro. More + * general nonlinear structure must be handled through callback evaluation + * routines. + * + * Structures of the same type can be added in individual pieces, in groups, + * or all together. Likewise, general nonlinear structures can all be handled + * by one callback object or broken up into separate callback objects if there + * are natural groupings that the user wants to treat differently. + * + * The overhead costs for loading the user model by pieces using the API + * functions that follow should be trivial in most cases -- even for large + * models. However, if not, it is always possible, and most efficient, to load + * all the structures of one type together in one API function call. + * + * Problem structure is passed to Knitro using KN API functions. + * The problem is solved by calling KN_solve. Applications must + * provide a means of evaluating the nonlinear objective, constraints, + * first derivatives, and (optionally) second derivatives. (First + * derivatives are also optional, but highly recommended.) + * + * The typical calling sequence is: + * KN_new + * KN_add_vars/KN_add_cons/KN_set_*bnds, etc. (problem setup) + * KN_add_*_linear_struct/KN_add_*_quadratic_struct (add special structures) + * KN_add_eval_callback (add callback for nonlinear evaluations if needed) + * KN_set_cb_* (set properties for nonlinear evaluation callbacks) + * KN_set_xxx_param (set any number of parameters/user options) + * KN_solve + * KN_free + * + * Generally, as long as no nonlinear structural changes are made to the + * model, KN_solve can be called in succession to re-solve a model after + * changes. For example, user options, variable bounds, and constraint + * bounds can be changed between calls to KN_solve, without having to call + * KN_free and reload the model from scratch. In addition, constant and + * linear structures in the model may be added, deleted, or changed without + * having to reconstruct the model. More extensive additions or changes to + * the model (such as adding quadratic structures or callbacks) require + * freeing the existing Knitro solver object and rebuilding the model from + * scratch. + */ + +/** Add variables to the model. The parameter indexVars may be set to NULL. + * Otherwise, on return it holds the global indices associated with the + * variables that were added (indices are typically allocated sequentially). + * Parameter indexVars can then be passed into other API routines that operate + * on the set of variables added through a particular call to KN_add_var*. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_add_vars ( KN_context_ptr kc, + const KNINT nV, + KNINT * const indexVars); +int KNITRO_API KN_add_var ( KN_context_ptr kc, + KNINT * const indexVar); + +/** Add constraints to the model. The parameter indexCons may be set to NULL. + * Otherwise, on return it holds the global indices associated with the + * constraints that were added (indices are typically allocated sequentially). + * Parameter indexCons can then be passed into other API routines that operate + * on the set of constraints added through a particular call to KN_add_con*. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_add_cons ( KN_context_ptr kc, + const KNINT nC, + KNINT * const indexCons); +int KNITRO_API KN_add_con ( KN_context_ptr kc, + KNINT * const indexCon); + +/** Add residuals for least squares optimization. The parameter indexRsds may + * be set to NULL. Otherwise, on return it holds the global indices associated + * with the residuals that were added (indices are typically allocated sequentially). + * Parameter indexRsds can then be passed into other API routines that operate + * on the set of residuals added through a particular call to KN_add_rsd*. + * Note that the current Knitro API does not support adding both constraints + * and residuals. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_add_rsds ( KN_context_ptr kc, + const KNINT nR, + KNINT * const indexRsds); +int KNITRO_API KN_add_rsd ( KN_context_ptr kc, + KNINT * const indexRsd); + +/** Set lower, upper or fixed bounds on variables. + * If not set, variables are assumed to be unbounded (e.g. lower bounds + * are assumed to be -KN_INFINITY and upper bounds are assumed to be + * KN_INFINITY). + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_var_lobnds ( KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + const double * const xLoBnds); +int KNITRO_API KN_set_var_lobnds_all ( KN_context_ptr kc, + const double * const xLoBnds); +int KNITRO_API KN_set_var_lobnd ( KN_context_ptr kc, + const KNINT indexVar, + const double xLoBnd); +int KNITRO_API KN_set_var_upbnds ( KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + const double * const xUpBnds); +int KNITRO_API KN_set_var_upbnds_all ( KN_context_ptr kc, + const double * const xUpBnds); +int KNITRO_API KN_set_var_upbnd ( KN_context_ptr kc, + const KNINT indexVar, + const double xUpBnd); +int KNITRO_API KN_set_var_fxbnds ( KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + const double * const xFxBnds); +int KNITRO_API KN_set_var_fxbnds_all ( KN_context_ptr kc, + const double * const xFxBnds); +int KNITRO_API KN_set_var_fxbnd ( KN_context_ptr kc, + const KNINT indexVar, + const double xFxBnd); + +/** Get lower, upper or fixed bounds on variables. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_var_lobnds (const KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + double * const xLoBnds); +int KNITRO_API KN_get_var_lobnds_all (const KN_context_ptr kc, + double * const xLoBnds); +int KNITRO_API KN_get_var_lobnd (const KN_context_ptr kc, + const KNINT indexVar, + double * const xLoBnd); + +int KNITRO_API KN_get_var_upbnds (const KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + double * const xUpBnds); +int KNITRO_API KN_get_var_upbnds_all (const KN_context_ptr kc, + double * const xUpBnds); +int KNITRO_API KN_get_var_upbnd (const KN_context_ptr kc, + const KNINT indexVar, + double * const xUpBnd); +int KNITRO_API KN_get_var_fxbnds (const KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + double * const xFxBnds); +int KNITRO_API KN_get_var_fxbnds_all (const KN_context_ptr kc, + double * const xFxBnds); +int KNITRO_API KN_get_var_fxbnd (const KN_context_ptr kc, + const KNINT indexVar, + double * const xFxBnd); + +/** Set variable types (e.g. KN_VARTYPE_CONTINUOUS, KN_VARTYPE_BINARY, + * KN_VARTYPE_INTEGER). If not set, variables are assumed to be continuous. + * When a variable is set as binary, its lower bound is automatically + * set to 0 and its upper bound is automatically set to 1 at the same time. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_var_types ( KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + const int * const xTypes); +int KNITRO_API KN_set_var_types_all ( KN_context_ptr kc, + const int * const xTypes); +int KNITRO_API KN_set_var_type ( KN_context_ptr kc, + const KNINT indexVar, + const int xType); + +/** Return the variable types. The array "xTypes" must be allocated + * by the user. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_var_types (const KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + int * const xTypes); +int KNITRO_API KN_get_var_types_all (const KN_context_ptr kc, + int * const xTypes); +int KNITRO_API KN_get_var_type (const KN_context_ptr kc, + const KNINT indexVar, + int * const xType); + +/** Specify some properties of the variables. Currently + * this API routine is only used to mark variables as linear, + * but other variable properties will be added in the future. + * Note: use bit-wise specification of the features: + * bit value meaning + * 0 1 KN_VAR_LINEAR + * default = 0 (variables are assumed to be nonlinear) + * + * If a variable only appears linearly in the model, it can be very + * helpful to mark this by enabling bit 0. This information can then + * be used by Knitro to perform more extensive preprocessing. If a + * variable appears nonlinearly in any constraint or the objective (or + * if the user does not know) then it should not be marked as linear. + * Variables are assumed to be nonlinear variables by default. + * Knitro makes a local copy of all inputs, so the application may + * free memory after the call. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_var_properties ( KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + const int * const xProperties); +int KNITRO_API KN_set_var_properties_all ( KN_context_ptr kc, + const int * const xProperties); +int KNITRO_API KN_set_var_property ( KN_context_ptr kc, + const KNINT indexVar, + const int xProperty); + +/** Set lower, upper or equality bounds on constraints. + * If not set, constraints are assumed to be unbounded (e.g. lower bounds + * are assumed to be -KN_INFINITY and upper bounds are assumed to be + * KN_INFINITY). + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_con_lobnds ( KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + const double * const cLoBnds); +int KNITRO_API KN_set_con_lobnds_all ( KN_context_ptr kc, + const double * const cLoBnds); +int KNITRO_API KN_set_con_lobnd ( KN_context_ptr kc, + const KNINT indexCon, + const double cLoBnd); +int KNITRO_API KN_set_con_upbnds ( KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + const double * const cUpBnds); +int KNITRO_API KN_set_con_upbnds_all ( KN_context_ptr kc, + const double * const cUpBnds); +int KNITRO_API KN_set_con_upbnd ( KN_context_ptr kc, + const KNINT indexCon, + const double cUpBnd); +int KNITRO_API KN_set_con_eqbnds ( KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + const double * const cEqBnds); +int KNITRO_API KN_set_con_eqbnds_all ( KN_context_ptr kc, + const double * const cEqBnds); +int KNITRO_API KN_set_con_eqbnd ( KN_context_ptr kc, + const KNINT indexCon, + const double cEqBnd); + +/** Get lower, upper or equality bounds on constraints. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_con_lobnds (const KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + double * const cLoBnds); +int KNITRO_API KN_get_con_lobnds_all (const KN_context_ptr kc, + double * const cLoBnds); +int KNITRO_API KN_get_con_lobnd (const KN_context_ptr kc, + const KNINT indexCon, + double * const cLoBnd); +int KNITRO_API KN_get_con_upbnds (const KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + double * const cUpBnds); +int KNITRO_API KN_get_con_upbnds_all (const KN_context_ptr kc, + double * const cUpBnds); +int KNITRO_API KN_get_con_upbnd (const KN_context_ptr kc, + const KNINT indexCon, + double * const cUpBnd); +int KNITRO_API KN_get_con_eqbnds (const KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + double * const cEqBnds); +int KNITRO_API KN_get_con_eqbnds_all (const KN_context_ptr kc, + double * const cEqBnds); +int KNITRO_API KN_get_con_eqbnd (const KN_context_ptr kc, + const KNINT indexCon, + double * const cEqBnd); + +/** Specify some properties of the objective and constraint functions. + * Note: use bit-wise specification of the features: + * bit value meaning + * 0 1 KN_OBJ_CONVEX/KN_CON_CONVEX + * 1 2 KN_OBJ_CONCAVE/KN_CON_CONCAVE + * 2 4 KN_OBJ_CONTINUOUS/KN_CON_CONTINUOUS + * 3 8 KN_OBJ_DIFFERENTIABLE/KN_CON_DIFFERENTIABLE + * 4 16 KN_OBJ_TWICE_DIFFERENTIABLE/KN_CON_TWICE_DIFFERENTIABLE + * 5 32 KN_OBJ_NOISY/KN_CON_NOISY + * 6 64 KN_OBJ_NONDETERMINISTIC/KN_CON_NONDETERMINISTIC + * default = 28 (bits 2-4 enabled: e.g. continuous, differentiable, twice-differentiable) + */ +int KNITRO_API KN_set_obj_property ( KN_context_ptr kc, + const int objProperty); +int KNITRO_API KN_set_con_properties ( KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + const int * const cProperties); +int KNITRO_API KN_set_con_properties_all ( KN_context_ptr kc, + const int * const cProperties); +int KNITRO_API KN_set_con_property ( KN_context_ptr kc, + const KNINT indexCon, + const int cProperty); + +/** Set the objective goal (KN_OBJGOAL_MINIMIZE or KN_OBJGOAL_MAXIMIZE). + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_obj_goal ( KN_context_ptr kc, + const int objGoal); + +/** Get the objective goal (KN_OBJGOAL_MINIMIZE or KN_OBJGOAL_MAXIMIZE). + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_obj_goal ( KN_context_ptr kc, + int * const objGoal); + +/** Set initial values for primal variables. If not set, variables + * may be initialized as 0 or initialized by Knitro based on some + * initialization strategy (perhaps determined by a user option). + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_var_primal_init_values ( KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + const double * const xInitVals); +int KNITRO_API KN_set_var_primal_init_values_all ( KN_context_ptr kc, + const double * const xInitVals); +int KNITRO_API KN_set_var_primal_init_value ( KN_context_ptr kc, + const KNINT indexVar, + const double xInitVal); + +/** Set initial values for dual variables (i.e. the Lagrange multipliers + * corresponding to the potentially bounded variables). If not set, dual + * variables may be initialized as 0 or initialized by Knitro based on some + * initialization strategy (perhaps determined by a user option). + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_var_dual_init_values ( KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + const double * const lambdaInitVals); +int KNITRO_API KN_set_var_dual_init_values_all ( KN_context_ptr kc, + const double * const lambdaInitVals); +int KNITRO_API KN_set_var_dual_init_value ( KN_context_ptr kc, + const KNINT indexVar, + const double lambdaInitVal); + +/** Set initial values for constraint dual variables (i.e. the Lagrange + * multipliers for the constraints). If not set, constraint dual + * variables may be initialized as 0 or initialized by Knitro based on some + * initialization strategy (perhaps determined by a user option). + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_con_dual_init_values ( KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + const double * const lambdaInitVals); +int KNITRO_API KN_set_con_dual_init_values_all ( KN_context_ptr kc, + const double * const lambdaInitVals); +int KNITRO_API KN_set_con_dual_init_value ( KN_context_ptr kc, + const KNINT indexCon, + const double lambdaInitVal); + +/*----- Adding/removing/changing constant structure ----- */ + +/** Add a constant to the objective function. */ +int KNITRO_API KN_add_obj_constant ( KN_context_ptr kc, + const double constant); + +/** Delete all constant terms from the objective function. + * Only constant terms existing from a previous solve (i.e. added before the most + * recent call to "KN_solve") may be removed. + */ +int KNITRO_API KN_del_obj_constant ( KN_context_ptr kc); + +/** Change constant term in the objective function. + * Equivalent to calling KN_del_obj_constant() + KN_add_obj_constant(). + */ +int KNITRO_API KN_chg_obj_constant ( KN_context_ptr kc, + const double constant); + +/** Add constants to the body of constraint functions. + * Each component i of arrays indexCons and constants adds a constant term + * constants[i] to the constraint c[indexCons[i]]. + */ +int KNITRO_API KN_add_con_constants ( KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, /* size = nC */ + const double * const constants); /* size = nC */ +int KNITRO_API KN_add_con_constants_all ( KN_context_ptr kc, + const double * const constants); +int KNITRO_API KN_add_con_constant ( KN_context_ptr kc, + const KNINT indexCon, + const double constant); + +/** Delete constant terms from the body of constraint functions. + * Each component i of array indexCons deletes all constant terms + * from the constraint c[indexCons[i]]. + * Only constant terms existing from a previous solve (i.e. added before the most + * recent call to "KN_solve") may be removed. + */ +int KNITRO_API KN_del_con_constants ( KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons); /* size = nC */ +int KNITRO_API KN_del_con_constants_all ( KN_context_ptr kc); +int KNITRO_API KN_del_con_constant ( KN_context_ptr kc, + const KNINT indexCon); + +/** Change constant terms in the body of constraint functions. + * Each component i of arrays indexCons and constants changes the constant term + * in the constraint c[indexCons[i]] to constants[i]. + * Equivalent to calling KN_del_con_constants() + KN_add_con_constants(). + */ +int KNITRO_API KN_chg_con_constants ( KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, /* size = nC */ + const double * const constants); /* size = nC */ +int KNITRO_API KN_chg_con_constants_all ( KN_context_ptr kc, + const double * const constants); +int KNITRO_API KN_chg_con_constant ( KN_context_ptr kc, + const KNINT indexCon, + const double constant); + +/** Add constants to the body of residual functions. + * Each component i of arrays indexRsds and constants adds a constant term + * constants[i] to the residual r[indexRsds[i]]. + */ +int KNITRO_API KN_add_rsd_constants ( KN_context_ptr kc, + const KNINT nR, + const KNINT * const indexRsds, /* size = nR */ + const double * const constants); /* size = nR */ +int KNITRO_API KN_add_rsd_constants_all ( KN_context_ptr kc, + const double * const constants); +int KNITRO_API KN_add_rsd_constant ( KN_context_ptr kc, + const KNINT indexRsd, + const double constant); + +/*----- Adding/removing/changing linear structure ----- */ + +/** Add linear structure to the objective function. + * Each component i of arrays indexVars and coefs adds a linear term + * coefs[i]*x[indexVars[i]] + * to the objective. + * + * Use "KN_add_obj_linear_struct()" to add several linear objective structures + * at once, and "KN_add_obj_linear_term()" to add a single linear objective + * term. + */ +int KNITRO_API KN_add_obj_linear_struct ( KN_context_ptr kc, + const KNINT nnz, + const KNINT * const indexVars, /* size = nnz */ + const double * const coefs); /* size = nnz */ +int KNITRO_API KN_add_obj_linear_term ( KN_context_ptr kc, + const KNINT indexVar, + const double coef); + +/** Delete linear structure from the objective function. + * Each component i of array indexVars removes the linear objective terms + * corresponding to variable x[indexVars[i]]. + * Only linear terms existing from the latest model update (i.e. added before + * the most recent call to "KN_solve" or "KN_update") will be removed. + * + * Use "KN_del_obj_linear_struct()" to delete several linear objective structures + * at once, "KN_del_obj_linear_term()" to delete a single linear objective + * term, "KN_del_obj_linear_struct_all()" to delete all linear objective terms. + */ +int KNITRO_API KN_del_obj_linear_struct ( KN_context_ptr kc, + const KNINT nnz, + const KNINT * const indexVars); /* size = nnz */ +int KNITRO_API KN_del_obj_linear_term ( KN_context_ptr kc, + const KNINT indexVar); +int KNITRO_API KN_del_obj_linear_struct_all (KN_context_ptr kc); + +/** Change linear structure in the objective function. + * Each component i of arrays indexVars and coefs changes the coefficient + * value of the linear term + * coefs[i]*x[indexVars[i]] + * in the objective. + * Equivalent to calling KN_del_obj_linear_struct() + KN_add_obj_linear_struct(). + * + * Use "KN_chg_obj_linear_struct()" to change several linear objective structures + * at once, and "KN_chg_obj_linear_term()" to change a single linear objective + * term. + */ +int KNITRO_API KN_chg_obj_linear_struct ( KN_context_ptr kc, + const KNINT nnz, + const KNINT * const indexVars, /* size = nnz */ + const double * const coefs); /* size = nnz */ +int KNITRO_API KN_chg_obj_linear_term ( KN_context_ptr kc, + const KNINT indexVar, + const double coef); + +/** Add linear structure to the constraint functions. + * Each component i of arrays indexCons, indexVars and coefs adds a linear + * term: + * coefs[i]*x[indexVars[i]] + * to constraint c[indexCons[i]]. + * + * Use "KN_add_con_linear_struct()" to add linear structure for a group of + * constraints at once, "KN_add_con_linear_struct_one()" to add linear + * structure for just one constraint, and "KN_add_con_linear_term()" to + * add a single linear constraint term. + */ +int KNITRO_API KN_add_con_linear_struct ( KN_context_ptr kc, + const KNLONG nnz, + const KNINT * const indexCons, /* size = nnz */ + const KNINT * const indexVars, /* size = nnz */ + const double * const coefs); /* size = nnz */ +int KNITRO_API KN_add_con_linear_struct_one ( KN_context_ptr kc, + const KNLONG nnz, + const KNINT indexCon, + const KNINT * const indexVars, /* size = nnz */ + const double * const coefs); /* size = nnz */ +int KNITRO_API KN_add_con_linear_term ( KN_context_ptr kc, + const KNINT indexCon, + const KNINT indexVar, + const double coef); + +/** Delete linear structure from the constraint functions. + * Each component i of arrays indexCons and indexVars removes the linear constraint + * terms corresponding to variable x[indexVars[i]] in constraint c[indexCons[i]]. + * Only linear terms existing from the latest model update (i.e. added before + * the most recent call to "KN_solve" or "KN_update") will be removed. + * + * Use "KN_del_con_linear_struct()" to delete linear structure from a group of + * constraints at once, "KN_del_con_linear_struct_one()" to delete linear + * structure from just one constraint, "KN_del_con_linear_term()" to + * delete a single linear constraint term and KN_del_con_linear_struct_all() + * to delete all linear terms from a set of constraints. + */ +int KNITRO_API KN_del_con_linear_struct ( KN_context_ptr kc, + const KNLONG nnz, + const KNINT * const indexCons, /* size = nnz */ + const KNINT * const indexVars); /* size = nnz */ +int KNITRO_API KN_del_con_linear_struct_one ( KN_context_ptr kc, + const KNLONG nnz, + const KNINT indexCon, + const KNINT * const indexVars); /* size = nnz */ +int KNITRO_API KN_del_con_linear_struct_all ( KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons); /* size = nC */ +int KNITRO_API KN_del_con_linear_term ( KN_context_ptr kc, + const KNINT indexCon, + const KNINT indexVar); + +/** Change linear structure in the constraint functions. + * Each component i of arrays indexCons, indexVars and coefs changes the coefficient + * value of the linear term: + * coefs[i]*x[indexVars[i]] + * in constraint c[indexCons[i]]. + * Equivalent to calling KN_del_con_linear_struct() + KN_add_con_linear_struct(). + * + * Use "KN_chg_con_linear_struct()" to change linear structure for a group of + * constraints at once, "KN_chg_con_linear_struct_one()" to change linear + * structure for just one constraint, and "KN_chg_con_linear_term()" to + * change a single linear constraint term. + */ +int KNITRO_API KN_chg_con_linear_struct ( KN_context_ptr kc, + const KNLONG nnz, + const KNINT * const indexCons, /* size = nnz */ + const KNINT * const indexVars, /* size = nnz */ + const double * const coefs); /* size = nnz */ +int KNITRO_API KN_chg_con_linear_struct_one ( KN_context_ptr kc, + const KNLONG nnz, + const KNINT indexCon, + const KNINT * const indexVars, /* size = nnz */ + const double * const coefs); /* size = nnz */ +int KNITRO_API KN_chg_con_linear_term ( KN_context_ptr kc, + const KNINT indexCon, + const KNINT indexVar, + const double coef); + +/** Add linear structure to the residual functions. + * Each component i of arrays indexRsds, indexVars and coefs adds a linear + * term: + * coefs[i]*x[indexVars[i]] + * to residual r[indexRsds[i]]. + * + * Use "KN_add_rsd_linear_struct()" to add linear structure for a group of + * residuals at once, "KN_add_rsd_linear_struct_one()" to add linear + * structure for just one residual, and "KN_add_rsd_linear_term()" to + * add a single linear residual term. + */ +int KNITRO_API KN_add_rsd_linear_struct ( KN_context_ptr kc, + const KNLONG nnz, + const KNINT * const indexRsds, /* size = nnz */ + const KNINT * const indexVars, /* size = nnz */ + const double * const coefs); /* size = nnz */ +int KNITRO_API KN_add_rsd_linear_struct_one ( KN_context_ptr kc, + const KNLONG nnz, + const KNINT indexRsd, + const KNINT * const indexVars, /* size = nnz */ + const double * const coefs); /* size = nnz */ +int KNITRO_API KN_add_rsd_linear_term ( KN_context_ptr kc, + const KNINT indexRsd, + const KNINT indexVar, + const double coef); + +/*----- Adding quadratic structure ----- */ + +/** Add quadratic structure to the objective function. + * Each component i of arrays indexVars1, indexVars2 and coefs adds a + * quadratic term + * coefs[i]*x[indexVars1[i]]*x[indexVars2[i]] + * to the objective. + * + * Use "KN_add_obj_quadratic_struct()" to add several quadratic objective + * structures at once, and "KN_add_obj_quadratic_term()" to add a single + * quadratic objective term. + * + * Note: if indexVars2[i] is < 0 then it adds a linear term + * coefs[i]*x[indexVars1[i]] instead. + */ +int KNITRO_API KN_add_obj_quadratic_struct ( KN_context_ptr kc, + const KNLONG nnz, + const KNINT * const indexVars1, /* size = nnz */ + const KNINT * const indexVars2, /* size = nnz */ + const double * const coefs); /* size = nnz */ +int KNITRO_API KN_add_obj_quadratic_term ( KN_context_ptr kc, + const KNINT indexVar1, + const KNINT indexVar2, + const double coef); + +/** Delete quadratic structure from the objective function. + * + * "KN_del_obj_quadratic_struct" will remove each component i of arrays + * indexVars1 and indexVars2 from the objective. That is any coefficient c: + * c*indexVars1[i]*indexVars2[i] + * "KN_del_obj_quadratic_struct_all" will delete all quadratic structure from + * the objective function. + * + * Only quadratic terms existing from the latest model update (i.e. added + * before the most recent call to "KN_solve" or "KN_update") will be removed. + */ +int KNITRO_API KN_del_obj_quadratic_struct( KN_context_ptr kc, + const KNLONG nnz, + const KNINT * const indexVars1, /* size = nnz */ + const KNINT * const indexVars2); /* size = nnz */ +int KNITRO_API KN_del_obj_quadratic_struct_all (KN_context_ptr kc); + +/** Add quadratic structure to the constraint functions. + * Each component i of arrays indexCons, indexVars1, indexVars2 and coefs adds a + * quadratic term: + * coefs[i]*x[indexVars1[i]]*x[indexVars2[i]] + * to the constraint c[indexCons[i]]. + * + * Use "KN_add_con_quadratic_struct()" to add quadratic structure for a group of + * constraints at once, "KN_add_con_quadratic_struct_one()" to add quadratic + * structure for just one constraint, and "KN_add_con_quadratic_term()" to + * add a single quadratic constraint term. + * + * Note: if indexVars2[i] is < 0 then it adds a linear term + * coefs[i]*x[indexVars1[i]] instead. + */ +int KNITRO_API KN_add_con_quadratic_struct ( KN_context_ptr kc, + const KNLONG nnz, + const KNINT * const indexCons, /* size = nnz */ + const KNINT * const indexVars1, /* size = nnz */ + const KNINT * const indexVars2, /* size = nnz */ + const double * const coefs); /* size = nnz */ +int KNITRO_API KN_add_con_quadratic_struct_one ( KN_context_ptr kc, + const KNLONG nnz, + const KNINT indexCon, + const KNINT * const indexVars1, /* size = nnz */ + const KNINT * const indexVars2, /* size = nnz */ + const double * const coefs); /* size = nnz */ +int KNITRO_API KN_add_con_quadratic_term ( KN_context_ptr kc, + const KNINT indexCon, + const KNINT indexVar1, + const KNINT indexVar2, + const double coef); + +/** Delete quadratric structure from the constraint functions. + * Each component i of arrays indexCons, indexVars1 and indexVars2 removes the + * quadratic constraint terms corresponding to variables x[indexVars1[i]] and + * x[indexVars2[i]] in constraint c[indexCons[i]]. + * Only quadratic terms existing from the latest model update (i.e. added + * before the most recent call to "KN_solve" or "KN_update") will be removed. + */ +int KNITRO_API KN_del_con_quadratic_struct( KN_context_ptr kc, + const KNLONG nnz, + const KNINT * const indexCons, /* size = nnz */ + const KNINT * const indexVars1, /* size = nnz */ + const KNINT * const indexVars2); /* size = nnz */ + +/*----- Adding conic structure ----- */ + +/** Add L2 norm structure of the form ||Ax + b||_2 to a constraint. + * indexCon: The constraint index that the L2 norm term will be added to. + * nCoords: The number of rows in "A" (or dimension of "b") + * nnz: The number of sparse non-zero elements in "A" + * indexCoords: The coordinate (row) index for each non-zero element in "A". + * indexVars: The variable (column) index for each non-zero element in "A" + * coefs: The coefficient value for each non-zero element in "A" + * constants: The array "b" - may be set to NULL to ignore "b" + * + * Note: L2 norm structure can currently only be added to constraints that + * otherwise only have linear (or constant) structure. In this way + * they can be used to define conic constraints of the form + * ||Ax + b|| <= c'x + d. The "c" coefficients should be added through + * "KN_add_con_linear_struct()" and "d" can be set as a constraint bound + * or through "KN_add_con_constants()". + * + * Note: Models with L2 norm structure are currently only handled by the + * Interior/Direct (KN_ALG_BAR_DIRECT) algorithm in Knitro. Any model + * with structure defined with KN_add_con_L2norm() will automatically be + * forced to use this algorithm. + */ +int KNITRO_API KN_add_con_L2norm ( KN_context_ptr kc, + const KNINT indexCon, + const KNINT nCoords, + const KNLONG nnz, + const KNINT * const indexCoords, /* size = nnz */ + const KNINT * const indexVars, /* size = nnz */ + const double * const coefs, /* size = nnz */ + const double * const constants); /* size = nCoords or NULL */ + +/*----- Adding complementarity constraints ----- */ + +/** This function adds complementarity constraints to the problem. + * The parameter indexCompCons may be set to NULL. Otherwise, on return it + * holds the global indices associated with the complementarity constraints + * that were added (indices are typically allocated sequentially). Parameter + * indexCompCons can then be passed into other API routines that operate + * on the set of complementarity constraints added through a particular call + * to KN_add_compcon*. + * The two lists indexComps1/indexComps2 are of equal length, and contain + * nCC matching pairs of variable indices. Each pair defines a complementarity + * constraint between the two variables. + * The array ccTypes specifies the type of complementarity: + * KN_CCTYPE_VARVAR: two (non-negative) variables + * KN_CCTYPE_VARCON: a variable and a constraint + * KN_CCTYPE_CONCON: two constraints + * Note: Currently only KN_CCTYPE_VARVAR is supported. The other + * ccTypes will be added in future releases. + * Returns 0 if OK, or a negative value on error. + */ +int KNITRO_API KN_add_compcons ( KN_context_ptr kc, + const KNINT nCC, + const int * const ccTypes, + const KNINT * const indexComps1, + const KNINT * const indexComps2, + KNINT * const indexCompCons); +int KNITRO_API KN_add_compcon ( KN_context_ptr kc, + const int ccType, + const KNINT indexComp1, + const KNINT indexComp2, + KNINT * const indexCompCon); + +/** This function sets all the complementarity constraints for the + * problem in one call. The function can only be called once. + * Returns 0 if OK, or a negative value on error. + * This function has been superseded by the KN_add_compcons function. + */ +int KNITRO_API KN_set_compcons ( KN_context_ptr kc, + const KNINT nCC, + const int * const ccTypes, + const KNINT * const indexComps1, + const KNINT * const indexComps2); + +/* ----- Loading MPS file ----- */ + +/** This function loads the problem specified in the MPS file "filename". + * The number of variables and constraints is specified in the MPS file, + * as well as the nature of the objective and the different constraints + * (should they be linear or quadratic). + * Return 0 if OK, or a negative value on error. + */ +int KNITRO_API KN_load_mps_file ( KN_context_ptr kc, + const char * const filename); + +int KNITRO_API KN_write_mps_file (KN_context_ptr kc, + const char * filename); + +/** + * This function loads a problem from a file. The file format is deduced from + * the file extension (case insensitive) or from the "read_options" flags. + * Supported file formats are: + * MPS files with extension ".mps" + * CBF files with extension ".cbf" (experimental) + * LP files with extension ".lp" + * Additional reading options can be given as a list of characters with + * "read_options": + * 'm': parsing MPS file without interpreting file extension + * 'c': parsing CBF file without interpreting file extension + * 'l': parsing LP file without interpreting file extension + * + * Return 0 if OK, or a negative value on error. + */ +int KNITRO_API KN_read_problem( KN_context_ptr kc, + const char * const filename, + const char * const read_options); + + +/** + * This function writes a problem to a file. The file format is deduced from + * the file extension (case insensitive) or from the "write_options" flags. + * Supported file formats are: + * MPS files with extension ".mps" + * Additional writing options can be given as a list of characters with + * "write_options": + * 'm': write in MPS format without interpreting file extension + * 'l': write in LP format without interpreting file extension + * 'b': write bounds at the beginning of the file. This option is only + * available when writing in LP format. + * + * Return 0 if OK, or a negative value on error. + */ +int KNITRO_API KN_write_problem( KN_context_ptr kc, + const char * const filename, + const char * const write_options); + +/* ----- Callbacks ----- */ + +/** Applications may define functions for evaluating problem elements + * at a trial point. The functions must match the prototype defined + * below, and passed to Knitro with the appropriate KN_set_cb_* call. + * Knitro may request different types of evaluation information, + * as specified in "evalRequest.type": + * KN_RC_EVALFC - return objective and constraint function values + * KN_RC_EVALGA - return first derivative values in "objGrad" and "jac" + * KN_RC_EVALFCGA - return objective and constraint function values + * AND first derivative "objGrad" and "jac" + * KN_RC_EVALH - return second derivative values in "hessian" + * KN_RC_EVALH_NO_F (this version excludes the objective term) + * KN_RC_EVALHV - return a Hessian-vector product in "hessVector" + * KN_RC_EVALHV_NO_F (this version excludes the objective term) + * KN_RC_EVALR - return residual function values for least squares + * KN_RC_EVALRJ - return residual Jacobian values for least squares + * + * The argument "lambda" is not defined when requesting EVALFC, EVALGA, + * EVALFCGA, EVALR or EVALRJ. + * Usually, applications for standard optimization models define three + * callback functions: one for EVALFC, one for EVALGA, and one for EVALH / EVALHV. + * The last function is only used when providing the Hessian (as opposed to + * using one of the Knitro options to approximate it) and evaluates H or HV + * depending on the value of "evalRequest.type". For least squares models, + * the application defines the two callback functions for EVALR and EVALRJ + * (instead of EVALFC and EVALGA). Least squares applications do not provide + * a callback for the Hessian as it is always approximated. + * It is possible in most cases to combine EVALFC and EVALGA into a single + * callback function. This may be advantageous if the application evaluates + * functions and their derivatives at the same time. In order to do this, set + * the user option eval_fcga=KN_EVAL_FCGA_YES, and define one callback set in + * "KN_add_eval_callback()" that evaluates BOTH the functions and gradients + * (i.e. have it populate "obj", "c", "objGrad", and "jac" in the "evalResult" + * structure), and do not set a callback in "KN_set_cb_grad()". Whenever Knitro + * needs a function + gradient evaluation, it will callback to the function + * passed to "KN_add_eval_callback()" with an EVALFCGA request. + * Combining function and gradient evaluations in one callback is not currently + * allowed if hessopt=KN_HESSOPT_PRODUCT_FINDIFF. It is not possible to combine + * EVALH / EVALHV because "lambda" may change after the EVALFC call. Generally + * it is most efficient to separate function and gradient callbacks, since a + * gradient evaluation is not needed at every "x" value where functions are + * evaluated. + * + * The "userParams" argument is an arbitrary pointer passed from the Knitro + * KN_solve call to the callback. It should be used to pass parameters + * defined and controlled by the application, or left null if not used. + * Knitro does not modify or dereference the "userParams" pointer. + * + * For simplicity, the following user-defined evaluation callback functions + * all use the same "KN_eval_callback()" function prototype defined below. + * + * funcCallback + * gradCallback + * hessCallback + * rsdCallback (for least squares) + * rsdJacCallback (for least squares) + * + * Other user callbacks that aren't involved in evaluations use the + * "KN_user_callback" function prototype or some other function protoype + * defined below that is specific to that callback. These include: + * + * KN_set_newpt_callback + * KN_set_mip_node_callback + * KN_set_mip_usercuts_callback + * KN_set_mip_lazyconstraints_callback + * KN_set_ms_callback + * KN_set_ms_process_callback + * KN_set_ms_initpt_callback + * KN_set_puts_callback + * KN_set_linsolver_callback + * + * Callbacks should return 0 if successful, a negative error code if not. + * Possible unsuccessful (negative) error codes for the func/grad/hess/rsd/rsdJac + * callback functions include: + * + * KN_RC_CALLBACK_ERR (for generic callback errors) + * KN_RC_EVAL_ERR (for evaluation errors, e.g log(-1)) + * + * The "linsolver" callback may use the return code + * + * KN_RC_LINEAR_SOLVER_ERR + * + * if it encounters a linear system it is unable to solve or prefers Knitro + * to handle. In this case, Knitro will revert to its own internal linear + * solver for that linear system. + * + * In addition, for the "func", "newpoint", "ms_process", "mip_node", + * "mip_usercuts" and "mip_lazyconstraints" callbacks, the user may set the + * following return code to force Knitro + * to terminate based on some user-defined condition. + * + * KN_RC_USER_TERMINATION (to use a callback routine + * for user specified termination) + */ + +/** Structure used to pass back evaluation information for evaluation callbacks. + * + * type: - indicates the type of evaluation requested + * threadID: - the thread ID associated with this evaluation request; + * useful for multi-threaded, concurrent evaluations + * x: - values of unknown (primal) variables used for all evaluations + * lambda: - values of unknown dual variables/Lagrange multipliers + * used for the evaluation of the Hessian + * sigma: - scalar multiplier for the objective component of the Hessian + * vec: - vector array value for Hessian-vector products (only used + * when user option hessopt=KN_HESSOPT_PRODUCT) + */ +typedef struct KN_eval_request { + int type; + int threadID; + const double * x; + const double * lambda; + const double * sigma; + const double * vec; +} KN_eval_request, *KN_eval_request_ptr; + +/** Structure used to return results information for evaluation callbacks. + * The arrays (and their indices and sizes) returned in this structure are + * local to the specific callback structure used for the evaluation. + * + * obj: - objective function evaluated at "x" for EVALFC or + * EVALFCGA request (funcCallback) + * c: - (length nC) constraint values evaluated at "x" for + * EVALFC or EVALFCGA request (funcCallback) + * objGrad: - (length nV) objective gradient evaluated at "x" for + * EVALGA request (gradCallback) or EVALFCGA request (funcCallback) + * jac: - (length nnzJ) constraint Jacobian evaluated at "x" for + * EVALGA request (gradCallback) or EVALFCGA request (funcCallback) + * hess: - (length nnzH) Hessian evaluated at "x", "lambda", "sigma" + * for EVALH or EVALH_NO_F request (hessCallback) + * hessVec: - (length n=number variables in the model) Hessian-vector + * product evaluated at "x", "lambda", "sigma" + * for EVALHV or EVALHV_NO_F request (hessCallback) + * rsd: - (length nR) residual values evaluated at "x" for EVALR + * request (rsdCallback) + * rsdJac: - (length nnzJ) residual Jacobian evaluated at "x" for + * EVALRJ request (rsdJacCallback) + */ +typedef struct KN_eval_result { + double * obj; + double * c; + double * objGrad; + double * jac; + double * hess; + double * hessVec; + double * rsd; + double * rsdJac; +} KN_eval_result, *KN_eval_result_ptr; + +/** The callback structure/object. Note the CB_context_ptr is allocated and + * managed by Knitro: the user does not have to free it. + */ +typedef struct CB_context CB_context, *CB_context_ptr; + +/** Function prototype for evaluation callbacks. */ +typedef int KN_eval_callback (KN_context_ptr kc, + CB_context_ptr cb, + KN_eval_request_ptr const evalRequest, + KN_eval_result_ptr const evalResult, + void * const userParams); + +/** This is the routine for adding a callback for (nonlinear) evaluations + * of objective and constraint functions. This routine can be called + * multiple times to add more than one callback structure (e.g. to create + * different callback structures to handle different blocks of constraints). + * This routine specifies the minimal information needed for a callback, and + * creates the callback structure "cb", which can then be passed to other + * callback functions to set additional information for that callback. + * + * evalObj - boolean indicating whether or not any part of the objective + * function is evaluated in the callback + * nC - number of constraints evaluated in the callback + * indexCons - (length nC) index of constraints evaluated in the callback + * (set to NULL if nC=0) + * funcCallback - a pointer to a function that evaluates the objective parts + * (if evalObj=KNTRUE) and any constraint parts (specified by + * nC and indexCons) involved in this callback; when + * eval_fcga=KN_EVAL_FCGA_YES, this callback should also evaluate + * the relevant first derivatives/gradients + * cb - (output) the callback structure that gets created by + * calling this function; all the memory for this structure is + * handled by Knitro + * + * After a callback is created by "KN_add_eval_callback()", the user can then specify + * gradient information and structure through "KN_set_cb_grad()" and Hessian + * information and structure through "KN_set_cb_hess()". If not set, Knitro will + * approximate these. However, it is highly recommended to provide a callback routine + * to specify the gradients if at all possible as this will greatly improve the + * performance of Knitro. Even if a gradient callback is not provided, it is still + * helpful to provide the sparse Jacobian structure through "KN_set_cb_grad()" to + * improve the efficiency of the finite-difference gradient approximations. + * Other optional information can also be set via "KN_set_cb_*() functions as + * detailed below. + * + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_add_eval_callback ( KN_context_ptr kc, + const KNBOOL evalObj, + const KNINT nC, + const KNINT * const indexCons, /* nullable if nC=0 */ + KN_eval_callback * const funcCallback, + CB_context_ptr * const cb); + +/** Version of KN_add_eval_callback to create a callback that applies to the + * objective function and all constraints. + */ +int KNITRO_API KN_add_eval_callback_all ( KN_context_ptr kc, + KN_eval_callback * const funcCallback, + CB_context_ptr * const cb); + +/** Version of KN_add_eval_callback to create a callback that only applies to a + * single objective function or constraint. Set index to the corresponding + * constraint index or use -1 for the objective. + */ +int KNITRO_API KN_add_eval_callback_one ( KN_context_ptr kc, + const KNINT index, /* -1 for obj */ + KN_eval_callback * const funcCallback, + CB_context_ptr * const cb); + +/** Add an evaluation callback for a least-squares models. Similar to KN_add_eval_callback() + * above, but for least-squares models. + * + * nR - number of residuals evaluated in the callback + * indexRsds - (length nR) index of residuals evaluated in the callback + * rsdCallback - a pointer to a function that evaluates any residual parts + * (specified by nR and indexRsds) involved in this callback + * cb - (output) the callback structure that gets created by + * calling this function; all the memory for this structure is + * handled by Knitro + * + * After a callback is created by "KN_add_lsq_eval_callback()", the user can then + * specify residual Jacobian information and structure through "KN_set_cb_rsd_jac()". + * If not set, Knitro will approximate the residual Jacobian. However, it is highly + * recommended to provide a callback routine to specify the residual Jacobian if at all + * possible as this will greatly improve the performance of Knitro. Even if a callback + * for the residual Jacobian is not provided, it is still helpful to provide the sparse + * Jacobian structure for the residuals through "KN_set_cb_rsd_jac()" to improve the + * efficiency of the finite-difference Jacobian approximation. Other optional + * information can also be set via "KN_set_cb_*() functions as detailed below. + * + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_add_lsq_eval_callback ( KN_context_ptr kc, + const KNINT nR, + const KNINT * const indexRsds, + KN_eval_callback * const rsdCallback, + CB_context_ptr * const cb); + +/** Version of KN_add_lsq_eval_callback to create a callback that applies to + * all residual functions. + */ +int KNITRO_API KN_add_lsq_eval_callback_all ( KN_context_ptr kc, + KN_eval_callback * const rsdCallback, + CB_context_ptr * const cb); + +/** Version of KN_add_lsq_eval_callback to create a callback that only applies to + * a single residual function. Set indexRsd to the corresponding residual index. + */ +int KNITRO_API KN_add_lsq_eval_callback_one ( KN_context_ptr kc, + const KNINT indexRsd, + KN_eval_callback * const rsdCallback, + CB_context_ptr * const cb); + +/** This API function is used to set the objective gradient and constraint Jacobian + * structure and also (optionally) a callback function to evaluate the objective + * gradient and constraint Jacobian provided through this callback. + * + * cb - a callback structure created from a previous call to + * KN_add_eval_callback() + * nV - number of nonzero components in the objective gradient + * for this callback if providing in sparse form; set to + * KN_DENSE to provide the full objective gradient + * objGradIndexVars - (length nV) the nonzero indices of the objective gradient; + * set to NULL if nV=KN_DENSE or nV=0 (i.e. evalObj=KNFALSE) + * nnzJ - number of nonzeroes in the sparse constraint Jacobian + * computed through this callback; set to KN_DENSE_ROWMAJOR to + * provide the full Jacobian in row major order (i.e. ordered + * by rows/constraints), or KN_DENSE_COLMAJOR to provide the full + * Jacobian in column major order (i.e. ordered by columns/ + * variables) + * jacIndexCons - (length nnzJ) constraint index (row) of each nonzero; + * set to NULL if nnzJ=KN_DENSE_ROWMAJOR/KN_DENSE_COLMAJOR or nnzJ=0 + * jacIndexVars - (length nnzJ) variable index (column) of each nonzero; + * set to NULL if nnzJ=KN_DENSE_ROWMAJOR/KN_DENSE_COLMAJOR or nnzJ=0 + * gradCallback - a pointer to a function that evaluates the objective gradient + * parts and any constraint Jacobian parts involved in this + * callback; set to NULL if using finite-difference gradient + * approximations (specified via KN_set_cb_gradopt()), or if + * gradients and functions are provided together in the + * funcCallback (i.e. eval_fcga=KN_EVAL_FCGA_YES). + * + * The user should generally always try to define the sparsity structure + * for the Jacobian ("nnzJ", "jacIndexCons", "jacIndexVars"). Even when + * using finite-difference approximations to compute the gradients, knowing the + * sparse structure of the Jacobian can allow Knitro to compute these + * finite-difference approximations faster. However, if the user is unable to + * provide this sparsity structure, then one can set "nnzJ" to KN_DENSE_ROWMAJOR or + * KN_DENSE_COLMAJOR and set "jacIndexCons" and "jacIndexVars" to NULL. + */ +int KNITRO_API KN_set_cb_grad ( KN_context_ptr kc, + CB_context_ptr cb, + const KNINT nV, /* or KN_DENSE */ + const KNINT * const objGradIndexVars, + const KNLONG nnzJ, /* or KN_DENSE_* */ + const KNINT * const jacIndexCons, + const KNINT * const jacIndexVars, + KN_eval_callback * const gradCallback); /* nullable */ + +/** This API function is used to set the structure and a callback function to + * evaluate the components of the Hessian of the Lagrangian provided through this + * callback. KN_set_cb_hess() should only be used when defining a user-supplied + * Hessian callback function (via the "hessopt=KN_HESSOPT_EXACT" user option), or + * a callback function to compute the Hessian-vector product array (via the + * "hessopt=KN_HESSOPT_PRODUCT" user option). When providing a callback function + * for Hessian-vector products, the Hessian is not stored or used internally, so + * in this case set "nnzH"=0, "hessIndexVars1"=NULL, and "hessIndexVars2"=NULL. + * When Knitro is approximating the Hessian, it cannot make use of the Hessian + * sparsity structure. + * + * cb - a callback structure created from a previous call to + * KN_add_eval_callback() + * nnzH - number of nonzeroes in the sparse Hessian of the Lagrangian + * computed through this callback; set to KN_DENSE_ROWMAJOR to + * provide the full upper triangular Hessian in row major order, + * or KN_DENSE_COLMAJOR to provide the full upper triangular Hessian + * in column major order. Note that the Hessian is symmetric, so + * the lower triangular components are the same as the upper + * triangular components with row and column indices swapped. + * hessIndexVars1 - (length nnzH) first variable index of each nonzero; + * set to NULL if nnzH=KN_DENSE_ROWMAJOR/KN_DENSE_COLMAJOR + * hessIndexVars2 - (length nnzH) second variable index of each nonzero; + * set to NULL if nnzH=KN_DENSE_ROWMAJOR/KN_DENSE_COLMAJOR + * hessCallback - a pointer to a function that evaluates the components of the + * Hessian of the Lagrangian (or Hessian-vector product array) + * provided in this callback + */ +int KNITRO_API KN_set_cb_hess ( KN_context_ptr kc, + CB_context_ptr cb, + const KNLONG nnzH, /* or KN_DENSE_* */ + const KNINT * const hessIndexVars1, + const KNINT * const hessIndexVars2, + KN_eval_callback * const hessCallback); + + +/** This API function is used to set the residual Jacobian structure and also + * (optionally) a callback function to evaluate the residual Jacobian provided + * through this callback. + * + * cb - a callback structure created from a previous call to + * KN_add_lsq_eval_callback() + * nnzJ - number of nonzeroes in the sparse residual Jacobian + * computed through this callback; set to KN_DENSE_ROWMAJOR to + * provide the full Jacobian in row major order (i.e. ordered + * by rows/residuals), or KN_DENSE_COLMAJOR to provide the full + * Jacobian in column major order (i.e. ordered by columns/ + * variables) + * jacIndexRsds - (length nnzJ) residual index (row) of each nonzero; + * set to NULL if nnzJ=KN_DENSE_ROWMAJOR/KN_DENSE_COLMAJOR or nnzJ=0 + * jacIndexVars - (length nnzJ) variable index (column) of each nonzero; + * set to NULL if nnzJ=KN_DENSE_ROWMAJOR/KN_DENSE_COLMAJOR or nnzJ=0 + * rsdJacCallback - a pointer to a function that evaluates any residual Jacobian + * parts involved in this callback; set to NULL if using a finite- + * difference Jacobian approximation (specified via KN_set_cb_gradopt()) + * + * The user should generally always try to define the sparsity structure + * for the Jacobian ("nnzJ", "jacIndexRsds", "jacIndexVars"). Even when + * using a finite-difference approximation to compute the Jacobian, knowing the + * sparse structure of the Jacobian can allow Knitro to compute this + * finite-difference approximation faster. However, if the user is unable to + * provide this sparsity structure, then one can set "nnzJ" to KN_DENSE_ROWMAJOR or + * KN_DENSE_COLMAJOR and set "jacIndexRsds" and "jacIndexVars" to NULL. + */ +int KNITRO_API KN_set_cb_rsd_jac ( KN_context_ptr kc, + CB_context_ptr cb, + const KNLONG nnzJ, /* or KN_DENSE_* */ + const KNINT * const jacIndexRsds, + const KNINT * const jacIndexVars, + KN_eval_callback * const rsdJacCallback); /* nullable */ + + +/** Define a userParams structure for an evaluation callback. */ +int KNITRO_API KN_set_cb_user_params (KN_context_ptr kc, + CB_context_ptr cb, + void * const userParams); + +/** Specify which gradient option "gradopt" will be used to evaluate + * the first derivatives of the callback functions. If gradopt=KN_GRADOPT_EXACT + * then a gradient evaluation callback must be set by "KN_set_cb_grad()" + * (or "KN_set_cb_rsd_jac()" for least squares). + */ +int KNITRO_API KN_set_cb_gradopt ( KN_context_ptr kc, + CB_context_ptr cb, + const int gradopt); + +/** Set an array of relative stepsizes to use for the finite-difference + * gradient/Jacobian computations when using finite-difference + * first derivatives. The user option KN_PARAM_FINDIFF_RELSTEPSIZE + * can be used to set the relative stepsizes for ALL variables. This + * routine takes precedence over the setting of KN_PARAM_FINDIFF_RELSTEPSIZE + * and is used to customize the settings for individual variables. + * Finite-difference step sizes "delta" in Knitro are computed as: + * delta[i] = relStepSizes[i]*max(abs(x[i]),1); + * The default relative step sizes for each component of "x" are sqrt(eps) + * for forward finite differences, and eps^(1/3) for central finite + * differences. Use this function to overwrite the default values. + * Any zero values will use Knitro default values, while non-zero values + * will overwrite default values. Knitro makes a local copy of all inputs, + * so the application may free memory after the call. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_cb_relstepsizes ( KN_context_ptr kc, + CB_context_ptr cb, + const KNINT nV, + const KNINT * const indexVars, + const double * const xRelStepSizes); +int KNITRO_API KN_set_cb_relstepsizes_all ( KN_context_ptr kc, + CB_context_ptr cb, + const double * const xRelStepSizes); +int KNITRO_API KN_set_cb_relstepsize ( KN_context_ptr kc, + CB_context_ptr cb, + const KNINT indexVar, + const double xRelStepSize); + +/** These API functions can be used to retrieve some information specific to evaluation + * callbacks. */ + +/** Retrieve the number of constraints "nC" being evaluated through callback "cb". + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_cb_number_cons (const KN_context_ptr kc, + const CB_context_ptr cb, + KNINT * const nC); + +/** Retrieve the number of residuals "nR" being evaluated through callback "cb". + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_cb_number_rsds (const KN_context_ptr kc, + const CB_context_ptr cb, + KNINT * const nR); + +/** Retrieve the number of non-zero objective gradient elements "nnz" + * evaluated through callback "cb". + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_cb_objgrad_nnz (const KN_context_ptr kc, + const CB_context_ptr cb, + KNINT * const nnz); + +/** Retrieve the number of non-zero Jacobian elements "nnz" + * evaluated through callback "cb". + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_cb_jacobian_nnz (const KN_context_ptr kc, + const CB_context_ptr cb, + KNLONG * const nnz); + +/** Retrieve the number of non-zero residual Jacobian elements "nnz" + * evaluated through callback "cb". + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_cb_rsd_jacobian_nnz (const KN_context_ptr kc, + const CB_context_ptr cb, + KNLONG * const nnz); + +/** Retrieve the number of non-zero Hessian elements "nnz" being + * evaluated through callback "cb". + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_cb_hessian_nnz (const KN_context_ptr kc, + const CB_context_ptr cb, + KNLONG * const nnz); + +/** Delete all evaluation callbacks previously added with KN_add_eval_callback. + * + * Note: all previously used CB_context_ptr will automatically be freed and must + * not be used after this call. + */ +int KNITRO_API KN_del_eval_callbacks(const KN_context_ptr kc); + +/** Delete the objective terms of an evaluation callback. + * + * The callback functions are not modified and will still be called, but the + * objective components will not be used in the evaluations. + * + * Note: For the Hessian evaluations, the associated callback must multiply the + * objective components of the hessian by the evalRequest.sigma value + * given in the KN_eval_request struct or use the evalRequest.type ( + * KN_RC_EVALH_NO_F or KN_RC_EVALHV_NO_F) to omit the objective components of + * the hessian. + * + * KN_del_obj_eval_callback will remove the objective term of one particular + * callback while KN_del_obj_eval_callback_all will remove objective terms for + * all callbacks. + */ +int KNITRO_API KN_del_obj_eval_callback(const KN_context_ptr kc, + const CB_context_ptr cb); +int KNITRO_API KN_del_obj_eval_callback_all(const KN_context_ptr kc); + +/** Type declaration for several non-evaluation user callbacks defined + * below. + */ +typedef int KN_user_callback ( KN_context_ptr kc, + const double * const x, + const double * const lambda, + void * const userParams); + +/** Set the callback function that is invoked after Knitro computes a + * new estimate of the solution point (i.e., after every iteration). + * The function should not modify any Knitro arguments. + * Argument "kc" passed to the callback from inside Knitro is the + * context pointer for the current problem being solved inside Knitro + * (either the main single-solve problem, or a subproblem when using + * multi-start, Tuner, etc.). + * Arguments "x" and "lambda" contain the latest solution estimates. + * Other values (such as objective, constraint, jacobian, etc.) can be + * queried using the corresponding KN_get_XXX_values methods. + * Note: Currently only active for continuous models. + * Return 0 if successful, a negative error code if not. + */ +int KNITRO_API KN_set_newpt_callback (KN_context_ptr kc, + KN_user_callback * const fnPtr, + void * const userParams); + +/** This callback function is for mixed integer (MIP) problems only. + * Set the callback function that is invoked after Knitro finishes + * processing a node on the branch-and-bound tree (i.e., after a relaxed + * subproblem solve in the branch-and-bound procedure). + * Argument "kc" passed to the callback from inside Knitro is the + * context pointer for the last node subproblem solved inside Knitro. + * The function should not modify any Knitro arguments. + * Arguments "x" and "lambda" contain the solution from the node solve. + * Return 0 if successful, a negative error code if not. + */ +int KNITRO_API KN_set_mip_node_callback (KN_context_ptr kc, + KN_user_callback * const fnPtr, + void * const userParams); + +/** This callback function is for mixed integer (MIP) problems only. + * Set the callback function that returns user cuts. + * This callback is invoked when Knitro search for cutting planes. + * It needs to add new cutting planes to argument "kc". + * Only linear cutting planes are supported. + * Arguments "x" and "lambda" contain the solution from the node solve. + */ +int KNITRO_API KN_set_mip_usercuts_callback (KN_context_ptr kc, + KN_user_callback * const fnPtr, + void * const userParams); + +/** This callback function is for mixed integer (MIP) problems only. + * Set the callback function that returns lazy constraints. + * This callback is invoked after all subproblem solves. + * It needs to add new (lazy) constraints to argument "kc". + * Only linear cutting planes are supported. + * Arguments "x" and "lambda" contain the solution from the node solve. + */ +int KNITRO_API KN_set_mip_lazyconstraints_callback (KN_context_ptr kc, + KN_user_callback * const fnPtr, + void * const userParams); + +/** This callback function is for multistart (MS) problems only. + * Set the callback function that is invoked regularly during the search. + * With parallelization, this callback is never called multiple times + * simultaneously. Argument "kc" passed to the callback from inside + * Knitro is the original context. The function should not modify any Knitro + * arguments. Arguments "x" and "lambda" contain the solution from the + * current best solve. + * Return 0 if successful, a negative error code if not. + */ +int KNITRO_API KN_set_ms_callback (KN_context_ptr kc, + KN_user_callback * const fnPtr, + void * const userParams); + +/** This callback function is for multistart (MS) problems only. + * Set the callback function that is invoked after Knitro finishes + * processing a multistart solve. Argument "kc" passed to the callback + * from inside Knitro is the context pointer for the last multistart + * subproblem solved inside Knitro. The function should not modify any + * Knitro arguments. Arguments "x" and "lambda" contain the solution from + * the last solve. + * Return 0 if successful, a negative error code if not. + */ +int KNITRO_API KN_set_ms_process_callback (KN_context_ptr kc, + KN_user_callback * const fnPtr, + void * const userParams); + +/** Type declaration for the callback that allows applications to + * specify an initial point before each local solve in the multistart + * procedure. On input, arguments "x" and "lambda" are the randomly + * generated initial points determined by Knitro, which can be overwritten + * by the user. The argument "nSolveNumber" is the number of the + * multistart solve. Return 0 if successful, a negative error code if not. + * Use KN_set_ms_initpt_callback to set this callback function. + */ +typedef int KN_ms_initpt_callback ( KN_context_ptr kc, + const KNINT nSolveNumber, + double * const x, + double * const lambda, + void * const userParams); + +/** Return 0 if successful, a negative error code if not. + */ +int KNITRO_API KN_set_ms_initpt_callback (KN_context_ptr kc, + KN_ms_initpt_callback * const fnPtr, + void * const userParams); + +/** Type declaration for the callback that allows applications to handle + * output. Applications can set a "put string" callback function to handle + * output generated by the Knitro solver. By default Knitro prints to + * stdout or a file named "knitro.log", as determined by KN_PARAM_OUTMODE. + * The KN_puts function takes a "userParams" argument which is a pointer + * passed directly from KN_solve. The function should return the number of + * characters that were printed. + * Use KN_set_puts_callback to set this callback function. + */ +typedef int KN_puts (const char * const str, + void * const userParams); + +/** Return 0 if successful, a negative error code if not. + */ +int KNITRO_API KN_set_puts_callback (KN_context_ptr kc, + KN_puts * const fnPtr, + void * const userParams); + +/* ----- Callback for linear system solves ----- */ + +/** Applications may define a function for solving sparse linear systems + * of equations M*x=b, which need to be solved internally by Knitro. The + * solution of these linear systems may be a significant cost for some + * algorithms (e.g. interior-point algorithms), especially for large + * problems. The symmetric n-by-n coefficient matrix "M" for the linear system + * uses compressed sparse column (CSC) format and only the lower triangle + * plus diagonal is stored. Row indices are not necessarily ordered within + * each column. The matrix M may often have the following generic 2-by-2 block + * structure: + * + * M = | H A' | + * | A D | + * + * where H is a n11-by-n11 symmetric matrix, A is a (n-n11)-by-n11 matrix, + * A' is the transpose of A, and D is a diagonal matrix of dimension (n-n11). + * The Knitro callback for linear system solves provides the sparse structure + * for the full matrix "M", and also provides the (1,1) block dimension n11, + * which can be used to access sub-blocks. + */ + +/** Possible linsolver phases. + */ +#define KN_LINSOLVER_PHASE_INIT 0 +#define KN_LINSOLVER_PHASE_ANALYZE 1 +#define KN_LINSOLVER_PHASE_FACTOR 2 +#define KN_LINSOLVER_PHASE_SOLVE 3 +#define KN_LINSOLVER_PHASE_FREE 4 + +/** Structure used to pass back information for linear system solver callbacks. + * + * phase: - indicates the linear system solve phase + * (e.g., init, analyze, factor, solve, free) + * linsysID: - the linear system ID associated with the request + * in the current local optimization + * threadID: - the thread ID associated with this request; + * threadID (along with linsysID) can be used to + * identify a unique linear system, which can be + * useful for multi-threaded, concurrent solves + * n: - dimension of M + * n11: - dimension of (1,1) block H inside M; useful for + * extracting submatrices H, A, and D for 2-by-2 + * block structure (n11=n if no block structure) + * rhs: - right-hand-side values needed for "solve" phase only + * values: - coefficient matrix values + * indexRows: - coefficient matrix row indices + * ptrCols: - coefficient matrix column pointers; + * number of nonzero elements (nnz) = ptrCols[n] + */ +typedef struct KN_linsolver_request { + int phase; + int linsysID; + int threadID; + KNINT n; /* dimension of coef matrix */ + KNINT n11; /* dimension of (1,1) block */ + const double * rhs; /* right-hand side vector (size = n) */ + const double * values; /* coefficient matrix values (size = nnz) */ + const KNINT * indexRows; /* coefficient matrix row indices (size = nnz) */ + const KNLONG * ptrCols; /* coefficient matrix column pointers (size = n+1) */ +} KN_linsolver_request, *KN_linsolver_request_ptr; + +/** Structure used to return results for linear system solver callbacks. + * + * solution: - solution values (required for "solve" phase) + * negeig: - number of negative eigenvalues (required for "factor" phase) + * poseig: - number of positive eigenvalues (optional for "factor" phase) + * rank: - coefficient matrix rank (optional for "factor" phase) + */ +typedef struct KN_linsolver_result { + double * solution; + KNINT negeig; + KNINT poseig; + KNINT rank; +} KN_linsolver_result, *KN_linsolver_result_ptr; + +/** Function prototype for linear system solve callbacks. + * Information/values needed for the linear system solve are provided in + * "linsolverRequest". Solution values should be returned in "linsolverResult". + * + * Return 0 if successful, a negative error code if not. + * KN_RC_CALLBACK_ERR (use for fatal callback errors) + * KN_RC_LINEAR_SOLVER_ERR (non-fatal for analyze phase; + * causes Knitro to revert to one + * of its own internal linear solvers) + */ +typedef int KN_linsolver_callback (KN_context_ptr kc, + KN_linsolver_request_ptr const linsolverRequest, + KN_linsolver_result_ptr const linsolverResult, + void * const userParams); + +/** This callback function is for solving linear systems inside Knitro. + * Return 0 if successful, a negative error code if not. + */ +int KNITRO_API KN_set_linsolver_callback (KN_context_ptr kc, + KN_linsolver_callback * const fnPtr, + void * const userParams); + + +/* ----- Loading full models ----- */ + +/** This function loads all the basic data for a linear program (LP) + * in a single function call. It can only be called on a newly created + * Knitro problem object. After calling this function, the model may + * be augmented through additional Knitro API calls before solving. + * Returns 0 if OK, or a negative value on error. + */ +int KNITRO_API KN_load_lp ( KN_context_ptr kc, + const KNINT n, + const double * const lobjCoefs, /* size = n */ + const double * const xLoBnds, /* size = n */ + const double * const xUpBnds, /* size = n */ + const KNINT m, + const double * const cLoBnds, /* size = m */ + const double * const cUpBnds, /* size = m */ + const KNLONG nnzJ, + const KNINT * const ljacIndexCons, /* size = nnzJ */ + const KNINT * const ljacIndexVars, /* size = nnzJ */ + const double * const ljacCoefs); /* size = nnzJ */ + +/** This function loads all the data for a quadratic program (QP) + * in a single function call. It can only be called on a newly created + * Knitro problem object. After calling this function, the model may + * be augmented through additional Knitro API calls before solving. + * Returns 0 if OK, or a negative value on error. + */ +int KNITRO_API KN_load_qp ( KN_context_ptr kc, + const KNINT n, + const double * const lobjCoefs, /* size = n */ + const double * const xLoBnds, /* size = n */ + const double * const xUpBnds, /* size = n */ + const KNINT m, + const double * const cLoBnds, /* size = m */ + const double * const cUpBnds, /* size = m */ + const KNLONG nnzJ, + const KNINT * const ljacIndexCons, /* size = nnzJ */ + const KNINT * const ljacIndexVars, /* size = nnzJ */ + const double * const ljacCoefs, /* size = nnzJ */ + const KNLONG nnzH, + const KNINT * const qobjIndexVars1, /* size = nnzH */ + const KNINT * const qobjIndexVars2, /* size = nnzH */ + const double * const qobjCoefs); /* size = nnzH */ + +/** This function loads all the data for a quadratically constrained + * quadratic program (QCQP) in a single function call. It can only + * be called on a newly created Knitro problem object. After calling + * this function, the model may be augmented through additional + * Knitro API calls before solving. + * Returns 0 if OK, or a negative value on error. + */ +int KNITRO_API KN_load_qcqp ( KN_context_ptr kc, + const KNINT n, + const double * const lobjCoefs, /* size = n */ + const double * const xLoBnds, /* size = n */ + const double * const xUpBnds, /* size = n */ + const KNINT m, + const double * const cLoBnds, /* size = m */ + const double * const cUpBnds, /* size = m */ + const KNLONG nnzJ, + const KNINT * const ljacIndexCons, /* size = nnzJ */ + const KNINT * const ljacIndexVars, /* size = nnzJ */ + const double * const ljacCoefs, /* size = nnzJ */ + const KNLONG nnzH, + const KNINT * const qobjIndexVars1, /* size = nnzH */ + const KNINT * const qobjIndexVars2, /* size = nnzH */ + const double * const qobjCoefs, /* size = nnzH */ + const KNLONG nnzQ, + const KNINT * const qconIndexCons, /* size = nnzQ */ + const KNINT * const qconIndexVars1, /* size = nnzQ */ + const KNINT * const qconIndexVars2, /* size = nnzQ */ + const double * const qconCoefs); /* size = nnzQ */ + +/* ----- Algorithmic/modeling features ----- */ + +/** Set custom absolute feasibility tolerances to use for the + * termination tests. + * The user options KN_PARAM_FEASTOL/KN_PARAM_FEASTOLABS define + * a single tolerance that is applied equally to every constraint + * and variable. This API function allows the user to specify + * separate feasibility termination tolerances for each constraint + * and variable. Values specified through this function will override + * the value determined by KN_PARAM_FEASTOL/KN_PARAM_FEASTOLABS. The + * tolerances should be positive values. If a non-positive value is + * specified, that constraint or variable will use the standard tolerances + * based on KN_PARAM_FEASTOL/KN_PARAM_FEASTOLABS. + * The variables are considered to be satisfied when + * x[i] - xUpBnds[i] <= xFeasTols[i] for all i=1..n, and + * xLoBnds[i] - x[i] <= xFeasTols[i] for all i=1..n + * The regular constraints are considered to be satisfied when + * c[i] - cUpBnds[i] <= cFeasTols[i] for all i=1..m, and + * cLoBnds[i] - c[i] <= cFeasTols[i] for all i=1..m + * The complementarity constraints are considered to be satisfied when + * min(x1_i, x2_i) <= ccFeasTols[i] for all i=1..ncc, + * where x1 and x2 are the arrays of complementary pairs. + * Knitro makes a local copy of all inputs, so the application + * may free memory after the call. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_var_feastols ( KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + const double * const xFeasTols); +int KNITRO_API KN_set_var_feastols_all ( KN_context_ptr kc, + const double * const xFeasTols); +int KNITRO_API KN_set_var_feastol ( KN_context_ptr kc, + const KNINT indexVar, + const double xFeasTol); + +int KNITRO_API KN_set_con_feastols ( KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + const double * const cFeasTols); +int KNITRO_API KN_set_con_feastols_all ( KN_context_ptr kc, + const double * const cFeasTols); +int KNITRO_API KN_set_con_feastol ( KN_context_ptr kc, + const KNINT indexCon, + const double cFeasTol); + +int KNITRO_API KN_set_compcon_feastols ( KN_context_ptr kc, + const KNINT nCC, + const KNINT * const indexCompCons, + const double * const ccFeasTols); +int KNITRO_API KN_set_compcon_feastols_all ( KN_context_ptr kc, + const double * const ccFeasTols); +int KNITRO_API KN_set_compcon_feastol ( KN_context_ptr kc, + const KNINT indexCompCon, + const double ccFeasTol); + +/** Set an array of variable scaling and centering values to + * perform a linear scaling + * x[i] = xScaleFactors[i] * xScaled[i] + xScaleCenters[i] + * for each variable. These scaling factors should try to + * represent the "typical" values of the "x" variables so that the + * scaled variables ("xScaled") used internally by Knitro are close + * to one. The values for xScaleFactors should be positive. + * If a non-positive value is specified, that variable will not + * be scaled. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_var_scalings ( KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + const double * const xScaleFactors, + const double * const xScaleCenters); +int KNITRO_API KN_set_var_scalings_all ( KN_context_ptr kc, + const double * const xScaleFactors, + const double * const xScaleCenters); +int KNITRO_API KN_set_var_scaling ( KN_context_ptr kc, + const KNINT indexVar, + const double xScaleFactor, + const double xScaleCenter); + +/** Set an array of constraint scaling values to perform a scaling + * cScaled[i] = cScaleFactors[i] * c[i] + * for each constraint. These scaling factors should try to + * represent the "typical" values of the inverse of the constraint + * values "c" so that the scaled constraints ("cScaled") used + * internally by Knitro are close to one. Scaling factors for + * standard constraints can be provided with "cScaleFactors", while + * scalings for complementarity constraints can be specified with + * "ccScaleFactors". The values for cScaleFactors/ccScaleFactors + * should be positive. If a non-positive value is specified, that + * constraint will use either the standard Knitro scaling + * (KN_SCALE_USER_INTERNAL), or no scaling (KN_SCALE_USER_NONE). + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_con_scalings ( KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + const double * const cScaleFactors); +int KNITRO_API KN_set_con_scalings_all ( KN_context_ptr kc, + const double * const cScaleFactors); +int KNITRO_API KN_set_con_scaling ( KN_context_ptr kc, + const KNINT indexCon, + const double cScaleFactor); + +int KNITRO_API KN_set_compcon_scalings ( KN_context_ptr kc, + const KNINT nCC, + const KNINT * const indexCompCons, + const double * const ccScaleFactors); +int KNITRO_API KN_set_compcon_scalings_all ( KN_context_ptr kc, + const double * const ccScaleFactors); +int KNITRO_API KN_set_compcon_scaling ( KN_context_ptr kc, + const KNINT indexCompCons, + const double ccScaleFactor); + +/** Set a scaling value for the objective function + * objScaled = objScaleFactor * obj + * This scaling factor should try to represent the "typical" + * value of the inverse of the objective function value "obj" so + * that the scaled objective ("objScaled") used internally by + * Knitro is close to one. The value for objScaleFactor + * should be positive. If a non-positive value is specified, then + * the objective will use either the standard Knitro scaling + * (KN_SCALE_USER_INTERNAL), or no scaling (KN_SCALE_USER_NONE). + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_obj_scaling ( KN_context_ptr kc, + const double objScaleFactor); + +/** Set names for model components passed in by the user/modeling + * language so that Knitro can internally print out these names. + * Knitro makes a local copy of all inputs, so the application may + * free memory after the call. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_var_names ( KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + const char * const xNames[]); +int KNITRO_API KN_set_var_names_all ( KN_context_ptr kc, + const char * const xNames[]); +int KNITRO_API KN_set_var_name ( KN_context_ptr kc, + const KNINT indexVars, + const char * const xName); + +int KNITRO_API KN_set_con_names ( KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + const char * const cNames[]); +int KNITRO_API KN_set_con_names_all ( KN_context_ptr kc, + const char * const cNames[]); +int KNITRO_API KN_set_con_name ( KN_context_ptr kc, + const KNINT indexCon, + const char * const cName); + +int KNITRO_API KN_set_compcon_names ( KN_context_ptr kc, + const KNINT nCC, + const KNINT * const indexCompCons, + const char * const ccNames[]); +int KNITRO_API KN_set_compcon_names_all ( KN_context_ptr kc, + const char * const ccNames[]); +int KNITRO_API KN_set_compcon_name ( KN_context_ptr kc, + const int indexCompCon, + const char * const ccName); + +int KNITRO_API KN_set_obj_name ( KN_context_ptr kc, + const char * const objName); + +/** Get names for model components. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_var_names (const KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + const KNINT nBufferSize, + char * const xNames[]); +int KNITRO_API KN_get_var_names_all (const KN_context_ptr kc, + const KNINT nBufferSize, + char * const xNames[]); +int KNITRO_API KN_get_var_name (const KN_context_ptr kc, + const KNINT indexVars, + const KNINT nBufferSize, + char * const xName); + +int KNITRO_API KN_get_con_names (const KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + const KNINT nBufferSize, + char * const cNames[]); +int KNITRO_API KN_get_con_names_all (const KN_context_ptr kc, + const KNINT nBufferSize, + char * const cNames[]); +int KNITRO_API KN_get_con_name (const KN_context_ptr kc, + const KNINT indexCons, + const KNINT nBufferSize, + char * const cName); +int KNITRO_API KN_get_obj_name (const KN_context_ptr kc, + const KNINT nBufferSize, + char * const objName); + +/** This API function can be used to identify which variables + * should satisfy their variable bounds throughout the optimization + * process (KN_HONORBNDS_ALWAYS). The user option KN_PARAM_HONORBNDS + * can be used to set ALL variables to honor their bounds. This + * routine takes precedence over the setting of KN_PARAM_HONORBNDS + * and is used to customize the settings for individual variables. + * Knitro makes a local copy of all inputs, so the application may + * free memory after the call. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_var_honorbnds ( KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + const int * const xHonorBnds); +int KNITRO_API KN_set_var_honorbnds_all ( KN_context_ptr kc, + const int * const xHonorBnds); +int KNITRO_API KN_set_var_honorbnd ( KN_context_ptr kc, + const KNINT indexVar, + const int xHonorBnd); + +/** This API function can be used to identify which constraints + * should satisfy their bounds throughout the optimization process. + * Note that this feature currently only applies to INEQUALITY + * constraints when using one of the barrier/interior-point algorithms. + * The user option KN_PARAM_BAR_FEASIBLE can be used to set ALL + * inequality constraints to honor their bounds (i.e. stay feasible). + * This routine takes precedence over the setting of KN_PARAM_BAR_FEASIBLE + * and is used to customize the settings for individual inequality + * constraints. + * + * The initial point must satisfy the inequality constraints specified + * through this API function to a sufficient degree; if not, Knitro may + * generate infeasible iterates and does not enable the "honorbnds" + * procedure until a sufficiently feasible point is found. Sufficient + * satisfaction occurs at a point x if it is true for all specified + * inequalities constraints that + * cl + tol \leq c(x) \leq cu - tol + * The constant "tol" is determined by the user option bar_feasmodetol. + * + * Knitro makes a local copy of all inputs, so the application may + * free memory after the call. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_con_honorbnds ( KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + const int * const cHonorBnds); +int KNITRO_API KN_set_con_honorbnds_all ( KN_context_ptr kc, + const int * const cHonorBnds); +int KNITRO_API KN_set_con_honorbnd ( KN_context_ptr kc, + const KNINT indexCon, + const int cHonorBnd); + +/* ----- MIP-specific settings ----- */ + +/** Set initial primal variables values for MIP. This point may be used to + * search for an initial feasible point and may be different from the starting + * point for the root relaxation subproblem. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_mip_var_primal_init_values ( KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + const double * const xInitVals); +int KNITRO_API KN_set_mip_var_primal_init_values_all ( KN_context_ptr kc, + const double * const xInitVals); +int KNITRO_API KN_set_mip_var_primal_init_value ( KN_context_ptr kc, + const KNINT indexVar, + const double xInitVal); + +/** Set the branching priorities for integer variables. Must first + * set the types of variables (e.g. by calling KN_set_var_types) before + * calling this function. Priorities must be positive numbers + * (variables with non-positive values are ignored). Variables with + * higher priority values will be considered for branching before + * variables with lower priority values. When priorities for a subset + * of variables are equal, the branching rule is applied as a tiebreaker. + * Values for continuous variables are ignored. Knitro makes a local + * copy of all inputs, so the application may free memory after the call. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_mip_branching_priorities + ( KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + const int * const xPriorities); +int KNITRO_API KN_set_mip_branching_priorities_all + ( KN_context_ptr kc, + const int * const xPriorities); +int KNITRO_API KN_set_mip_branching_priority + ( KN_context_ptr kc, + const KNINT indexVar, + const int xPriority); + +/** Set strategies for dealing with individual integer variables. Possible + * strategy values include: + * KN_MIP_INTVAR_STRATEGY_NONE 0 (default) + * KN_MIP_INTVAR_STRATEGY_RELAX 1 + * KN_MIP_INTVAR_STRATEGY_MPEC 2 (binary variables only) + * indexVars should be an index value corresponding to an integer variable + * (nothing is done if the index value corresponds to a continuous variable), + * and xStrategies should correspond to one of the strategy values listed above. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_set_mip_intvar_strategies + ( KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + const int * const xStrategies); +int KNITRO_API KN_set_mip_intvar_strategies_all + ( KN_context_ptr kc, + const int * const xStrategies); +int KNITRO_API KN_set_mip_intvar_strategy + ( KN_context_ptr kc, + const KNINT indexVar, + const int xStrategy); + +/* ----- Solving ----- */ + +/** Call Knitro to solve the problem. The return value indicates + * the solution status: + * 0: the final solution is optimal to specified tolerances; + * -100 to -109: a feasible solution was found (but not verified optimal); + * -200 to -209: Knitro terminated at an infeasible point; + * -300 to -301: the problem was determined to be unbounded; + * -400 to -409: Knitro terminated because it reached a pre-defined limit + * (a feasible point was found before reaching the limit); + * -410 to -419: Knitro terminated because it reached a pre-defined limit + * (no feasible point was found before reaching the limit); + * -500 to -599: Knitro terminated with an input error or some non-standard error. + * Refer to the Knitro manual section on Return Codes for more details. All + * possible return code values are defined at the bottom of this file. + */ +int KNITRO_API KN_solve (KN_context_ptr kc); + +/** Call Knitro to update the problem. + * If the model has been modified (e.g. via adding, changing, deleting + * structures), since the last solve, calling KN_update will update the + * internal model to incorporate all the changes since the most recent + * solve, but will not proceed with solving. Typically, for efficiency, + * the model is not actually updated with the most recent modifications + * until KN_solve is called, but KN_update allows for updating of the model + * without solving. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_update (KN_context_ptr kc); + +/* ----- Reading model/solution properties ----- */ + +/** Retrieve the number of variables "nV" in the model. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_number_vars (const KN_context_ptr kc, + KNINT * const nV); + +/** Retrieve the number of constraints "nC" in the model. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_number_cons (const KN_context_ptr kc, + KNINT * const nC); + +/** Retrieve the number of complementarity constraints "nCC" in the model. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_number_compcons (const KN_context_ptr kc, + KNINT * const nCC); + +/** Retrieve the number of residuals "nR" in the model. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_number_rsds (const KN_context_ptr kc, + KNINT * const nR); + +/** Return the number of function callback evaluations requested by KN_solve + * in "numFCevals". One evaluation count includes a single evaluation of the + * objective and all the constraints defined via callbacks (whether evaluated + * altogether in one callback or evaluated using several separate callbacks). + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_number_FC_evals (const KN_context_ptr kc, + int * const numFCevals); + +/** Return the number of gradient callback evaluations requested by KN_solve + * in "numGAevals". One evaluation count includes a single evaluation of the + * first derivatives of the objective and all the constraints defined via + * gradient callbacks (whether evaluated altogether in one callback or + * evaluated using several separate callbacks). + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_number_GA_evals (const KN_context_ptr kc, + int * const numGAevals); + +/** Return the number of Hessian callback evaluations requested by KN_solve + * in "numHevals". One evaluation count includes a single evaluation of all + * the components of the Hessian of the Lagrangian matrix defined via + * callbacks (whether evaluated altogether in one callback or evaluated using + * several separate callbacks). + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_number_H_evals (const KN_context_ptr kc, + int * const numHevals); + +/** Return the number of Hessian-vector callback evaluations requested + * by KN_solve in "numHVevals". One evaluation count includes a single + * evaluation of the product of the Hessian of the Lagrangian matrix with a + * vector submitted by Knitro (whether evaluated altogether in one callback + * or evaluated using several separate callbacks). + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_number_HV_evals (const KN_context_ptr kc, + int * const numHVevals); + +/** Retrieve the Knitro solve time either as CPU time or real time. + */ +int KNITRO_API KN_get_solve_time_cpu(const KN_context_ptr kc, + double * const time); +int KNITRO_API KN_get_solve_time_real(const KN_context_ptr kc, + double * const time); + +/** Return the solution status, objective, primal and dual variables. + * The status and objective value scalars are returned as pointers + * that need to be de-referenced to get their values. The arrays + * "x" and "lambda" must be allocated by the user. + * Returns 0 if call is successful; + * <0 if there is an error. + */ +int KNITRO_API KN_get_solution (const KN_context_ptr kc, + int * const status, + double * const obj, + double * const x, + double * const lambda); + +/** Return the objective, primal and dual variables, and constraint + * values for the best feasible iterate encountered throughout the + * optimization (i.e. the feasible iterate with the best objective + * value). The (absolute) feasibility error computed at this point + * is also returned. This point may not always correspond to the + * default final solution returned by Knitro. If no feasible point was + * found, then the least infeasible point found is returned. + * The "feasError" and "obj" value scalars are returned as pointers + * that need to be de-referenced to get their values. The arrays + * "x", "lambda", and "c" must be allocated by the user. + * Returns 0 if feasible point found and call is successful; + * 1 if no feasible point was found; + * <0 if there is an error. + */ +int KNITRO_API KN_get_best_feasible_iterate (const KN_context_ptr kc, + double * const feasError, + double * const obj, + double * const x, + double * const lambda, + double * const c); + +/** Return the value of the objective obj(x) in "obj". + * Returns 0 if call is successful; + * <0 if there is an error. + */ +int KNITRO_API KN_get_obj_value (const KN_context_ptr kc, + double * const obj); + +/** Return the type (e.g. KN_OBJTYPE_GENERAL, KN_OBJTYPE_LINEAR, + * KN_OBJTYPE_QUADRATIC, etc.) of the objective obj(x) in "objType". + * Returns 0 if call is successful; + * <0 if there is an error. + */ +int KNITRO_API KN_get_obj_type (const KN_context_ptr kc, + int * const objType); + +/** Return the primal ("x") or dual ("lambda") variables. + * Returns 0 if call is successful; + * <0 if there is an error. + */ +int KNITRO_API KN_get_var_primal_values (const KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + double * const x); +int KNITRO_API KN_get_var_primal_values_all (const KN_context_ptr kc, + double * const x); +int KNITRO_API KN_get_var_primal_value (const KN_context_ptr kc, + const KNINT indexVar, + double * const x); + +int KNITRO_API KN_get_var_dual_values (const KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + double * const lambda); +int KNITRO_API KN_get_var_dual_values_all (const KN_context_ptr kc, + double * const lambda); +int KNITRO_API KN_get_var_dual_value (const KN_context_ptr kc, + const KNINT indexVar, + double * const lambda); + +int KNITRO_API KN_get_con_dual_values (const KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + double * const lambda); +int KNITRO_API KN_get_con_dual_values_all (const KN_context_ptr kc, + double * const lambda); +int KNITRO_API KN_get_con_dual_value (const KN_context_ptr kc, + const KNINT indexCons, + double * const lambda); + +/** Return the values of the constraint vector c(x) in "c". + * The array "c" must be allocated by the user. + * Returns 0 if call is successful; + * <0 if there is an error. + */ +int KNITRO_API KN_get_con_values (const KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + double * const c); +int KNITRO_API KN_get_con_values_all (const KN_context_ptr kc, + double * const c); +int KNITRO_API KN_get_con_value (const KN_context_ptr kc, + const KNINT indexCon, + double * const c); + +/** Return the types (e.g. KN_CONTYPE_GENERAL, KN_CONTYPE_LINEAR, + * KN_CONTYPE_QUADRATIC, etc.) of the constraint vector c(x) in "cTypes". + * The array "cTypes" must be allocated by the user. + * Returns 0 if call is successful; + * <0 if there is an error. + */ +int KNITRO_API KN_get_con_types (const KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + int * const cTypes); +int KNITRO_API KN_get_con_types_all (const KN_context_ptr kc, + int * const cTypes); +int KNITRO_API KN_get_con_type (const KN_context_ptr kc, + const KNINT indexCon, + int * const cType); + +/** Return the values of the residual vector r(x) in "r". + * The array "r" must be allocated by the user. + * Returns 0 if call is successful; + * <0 if there is an error. + */ +int KNITRO_API KN_get_rsd_values (const KN_context_ptr kc, + const KNINT nR, + const KNINT * const indexRsds, + double * const r); +int KNITRO_API KN_get_rsd_values_all (const KN_context_ptr kc, + double * const r); +int KNITRO_API KN_get_rsd_value (const KN_context_ptr kc, + const KNINT indexRsd, + double * const r); + +/** Return information about the feasibility of the variables x after + * solving. + * + * The integer array "bndInfeas" indicates whether or not the variable + * was deemed to be infeasible with respect to its bounds by the Knitro + * termination test. + * Possible values of "bndInfeas" are: + * -1: The variable is considered infeasible with respect to + * its lower bound (or is less than its fixed value if fixed). + * 0: The variable is considered feasible with respect to the + * feasibility tolerances. + * 1: The variable is considered infeasible with respect to + * its upper bound (or is greater than its fixed value if fixed). + * Note that even if the variable is deemed feasible it may still have a + * non-zero violation returned in "viols" since the feasibility + * tolerances allow for small violations. + * + * The integer array "intInfeas" indicates whether or not an integer + * variable was deemed to be infeasible with respect to integrality. + * Possible values of "intInfeas" are: + * -1: The integer variable is considered infeasible with respect to + * the integrality tolerance and is closer to its floor. + * 0: The integer variable is considered feasible with respect to + * the integrality tolerance (or the variable is continuous). + * 1: The integer variable is considered infeasible with respect to + * the integrality tolerance and is closer to its ceiling. + * + * The non-negative array "viols" returns the amount by which the + * variable violates its lower or upper bound. If the variable is an + * integer variable that satisfies its lower and upper bounds, "viols" + * returns the integrality error (i.e. the distance to the nearest + * integer value inside its bounds). + * + * The arrays "bndInfeas", "intInfeas" and "viols" must be allocated by + * the user to retrieve these values. Any of these arrays set to NULL + * will not be returned. + * + * Returns 0 if call is successful; + * <0 if there is an error. + */ +int KNITRO_API KN_get_var_viols (const KN_context_ptr kc, + const KNINT nV, + const KNINT * const indexVars, + KNINT * const bndInfeas, + KNINT * const intInfeas, + double * const viols); +int KNITRO_API KN_get_var_viols_all (const KN_context_ptr kc, + KNINT * const bndInfeas, + KNINT * const intInfeas, + double * const viols); +int KNITRO_API KN_get_var_viol (const KN_context_ptr kc, + const KNINT indexVar, + KNINT * const bndInfeas, + KNINT * const intInfeas, + double * const viol); + +/** Return information about the feasibility of the constraints c(x) + * after solving. + * + * The integer array "infeas" indicates whether or not the constraint + * was deemed to be infeasible by the Knitro termination test. + * Possible values of "infeas" are: + * -1: The constraint is considered infeasible with respect to + * its lower bound (or is less than the right-hand side if + * it is an equality). + * 0: The constraint is considered feasible with respect to the + * feasibility tolerances. + * 1: The constraint is considered infeasible with respect to + * its upper bound (or is greater than the right-hand side if + * it is an equality). + * Note that even if the constraint is deemed feasible it may still + * have a non-zero violation returned in "viols" since the feasibility + * tolerances allow for small violations. + * + * The non-negative array "viols" returns the amount by which the + * constraint violates its lower or upper bound. + * + * The arrays "infeas" and "viols" must be allocated by the user to + * retrieve these values. Any of these arrays set to NULL will not be + * returned. + * + * Returns 0 if call is successful; + * <0 if there is an error. + */ +int KNITRO_API KN_get_con_viols (const KN_context_ptr kc, + const KNINT nC, + const KNINT * const indexCons, + KNINT * const infeas, + double * const viols); +int KNITRO_API KN_get_con_viols_all (const KN_context_ptr kc, + KNINT * const infeas, + double * const viols); +int KNITRO_API KN_get_con_viol (const KN_context_ptr kc, + const KNINT indexCon, + KNINT * const infeas, + double * const viol); + +/** Return information about any errors detected by the Knitro presolver. + * Parameters have the following values. + * + * component: The component involved in the error + * (e.g., KN_COMPONENT_VAR, KN_COMPONENT_OBJ + * KN_COMPONENT_CON, KN_COMPONENT_RSD). + * Return value of 0 if no error. + * + * index: The component index involved in the error (is set to -1 if no + * error). + * + * error: Return code associated with the error + * (e.g., KN_RC_INFEAS_VAR_BOUNDS, KN_RC_INFEAS_CON_BOUNDS, + * KN_RC_UNBOUNDED_OR_INFEAS). Return value of 0 if no error. + * + * viol: If there was an infeasibility error, it returns the + * amount of infeasibility deduced by the presolve for the + * given component (otherwise is set to 0). + * + * Returns 0 if no presolve errors were detected; + * 1 if an error was detected in the presolve phase; + * <0 if there is an error in the function call. + */ +int KNITRO_API KN_get_presolve_error (const KN_context_ptr kc, + KNINT * const component, + KNINT * const index, + KNINT * const error, + double * const viol); + +/* ----- Solution properties for continuous problems only ----- */ + +/** Return the number of iterations made by KN_solve in "numIters". + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_number_iters (const KN_context_ptr kc, + int * const numIters); + +/** Return the number of conjugate gradient (CG) iterations made by + * KN_solve in "numCGiters". + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_number_cg_iters (const KN_context_ptr kc, + int * const numCGiters); + +/** Return the absolute feasibility error at the solution in "absFeasError". + * Refer to the Knitro manual section on Termination Tests for a + * detailed definition of this quantity. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_abs_feas_error (const KN_context_ptr kc, + double * const absFeasError); + +/** Return the relative feasibility error at the solution in "relFeasError". + * Refer to the Knitro manual section on Termination Tests for a + * detailed definition of this quantity. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_rel_feas_error (const KN_context_ptr kc, + double * const relFeasError); + +/** Return the absolute optimality error at the solution in "absOptError". + * Refer to the Knitro manual section on Termination Tests for a + * detailed definition of this quantity. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_abs_opt_error (const KN_context_ptr kc, + double * const absOptError); + +/** Return the relative optimality error at the solution in "relOptError". + * Refer to the Knitro manual section on Termination Tests for a + * detailed definition of this quantity. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_rel_opt_error (const KN_context_ptr kc, + double * const relOptError); + +/** Return the values of the objective gradient vector in "indexVars" + * and "objGrad". The objective gradient values returned correspond + * to the non-zero sparse objective gradient indices provided by the user. + * The arrays "indexVars" and "objGrad" must be allocated by the user. + * The size of these arrays is obtained by first calling + * "KN_get_objgrad_nnz()". + * Returns 0 if call is successful; + * <0 if there is an error. + */ +int KNITRO_API KN_get_objgrad_nnz (const KN_context_ptr kc, + KNINT * const nnz); +int KNITRO_API KN_get_objgrad_values (const KN_context_ptr kc, + KNINT * const indexVars, + double * const objGrad); +/** Return the values of the full (dense) objective gradient in "objGrad". + * The array "objGrad" must be allocated by the user (the size is equal + * to the total number of variables in the problem). + * Returns 0 if call is successful; + * <0 if there is an error. + */ +int KNITRO_API KN_get_objgrad_values_all (const KN_context_ptr kc, + double * const objGrad); + +/** Return the values of the constraint Jacobian in "indexCons", "indexVars", + * and "jac". The Jacobian values returned correspond to the non-zero + * sparse Jacobian indices provided by the user. + * The arrays "indexCons", "indexVars", and "jac" must be allocated by + * the user. The size of these arrays is obtained by first calling + * "KN_get_jacobian_nnz()". + * + * Use "KN_get_jacobian_nnz_one()" / "KN_get_jacobian_values_one()" to + * get the gradient/Jacobian values corresponding to just one constraint. + * + * Returns 0 if call is successful; + * <0 if there is an error. + */ +int KNITRO_API KN_get_jacobian_nnz (const KN_context_ptr kc, + KNLONG * const nnz); +int KNITRO_API KN_get_jacobian_values (const KN_context_ptr kc, + KNINT * const indexCons, + KNINT * const indexVars, + double * const jac); +int KNITRO_API KN_get_jacobian_nnz_one (const KN_context_ptr kc, + KNINT indexCon, + KNINT * const nnz); +int KNITRO_API KN_get_jacobian_values_one (const KN_context_ptr kc, + KNINT indexCon, + KNINT * const indexVars, + double * const jac); + +/** Return the values of the residual Jacobian in "indexRsds", "indexVars", + * and "rsdJac". The Jacobian values returned correspond to the non-zero + * sparse Jacobian indices provided by the user. + * The arrays "indexRsds", "indexVars" and "rsdJac" must be allocated + * by the user. The size of these arrays is obtained by first calling + * "KN_get_rsd_jacobian_nnz()". + * Returns 0 if call is successful; + * <0 if there is an error. + */ +int KNITRO_API KN_get_rsd_jacobian_nnz (const KN_context_ptr kc, + KNLONG * const nnz); +int KNITRO_API KN_get_rsd_jacobian_values (const KN_context_ptr kc, + KNINT * const indexRsds, + KNINT * const indexVars, + double * const rsdJac); + +/** Return the values of the Hessian (or possibly Hessian + * approximation) in "hess". This routine is currently only valid + * if one of the following cases holds: + * 1) KN_HESSOPT_EXACT (presolver on or off), or; + * 2) KN_HESSOPT_BFGS or KN_HESSOPT_SR1, but only with the + * Knitro presolver off (i.e. KN_PRESOLVE_NONE). + * 3) Solving a least squares model with the Gauss-Newton Hessian + * and the Gauss-Newton Hessian is explicitly computed and + * stored in Knitro. + * + * In all other cases, either Knitro does not have an internal + * representation of the Hessian (or Hessian approximation), + * or the internal Hessian approximation corresponds only to + * the presolved problem form and may not be valid for the + * original problem form. In these cases "indexVars1", "indexVars2", + * and "hess" are left unmodified, and the routine has return code 1. + * + * Note that in case 2 above (KN_HESSOPT_BFGS or KN_HESSOPT_SR1) + * the values returned in "hess" are the upper triangular values + * of the dense quasi-Newton Hessian approximation stored row-wise. + * There are ((n*n - n)/2 + n) such values (where "n" is the number + * of variables in the problem. These values may be quite different + * from the values of the exact Hessian. + * + * When KN_HESSOPT_EXACT (case 1 above) the Hessian values + * returned correspond to the non-zero sparse Hessian indices + * provided by the user. + * + * The arrays "indexVars1", "indexVars2" and "hess" must be allocated + * by the user. The size of these arrays is obtained by first calling + * "KN_get_hessian_nnz()". + * Returns 0 if call is successful; + * 1 if "hess" was not set because Knitro does not + * have a valid Hessian for the model stored. + * <0 if there is an error. + */ +int KNITRO_API KN_get_hessian_nnz (const KN_context_ptr kc, + KNLONG * const nnz); +int KNITRO_API KN_get_hessian_values (const KN_context_ptr kc, + KNINT * const indexVars1, + KNINT * const indexVars2, + double * const hess); + + +/* ----- Solution properties for MIP problems only ----- */ + +/** Return the number of nodes processed in the MIP solve + * in "numNodes". + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_mip_number_nodes (const KN_context_ptr kc, + int * const numNodes); + +/** Return the number of continuous subproblems processed in the + * MIP solve in "numSolves". + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_mip_number_solves (const KN_context_ptr kc, + int * const numSolves); + +/** Return the final absolute optimality gap in the MIP solve + * in "absGap". Refer to the Knitro manual section on Termination + * Tests for a detailed definition of this quantity. Set to + * KN_INFINITY if no incumbent (i.e., integer feasible) point found. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_mip_abs_gap (const KN_context_ptr kc, + double * const absGap); + +/** Return the final absolute optimality gap in the MIP solve + * int "relGap". Refer to the Knitro manual section on Termination + * Tests for a detailed definition of this quantity. Set to + * KN_INFINITY if no incumbent (i.e., integer feasible) point found. + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_mip_rel_gap (const KN_context_ptr kc, + double * const relGap); + +/** Return the objective value of the MIP incumbent solution in + * "incumbentObj". Set to KN_INFINITY if no incumbent (i.e., integer + * feasible) point found. + * Returns 0 if incumbent solution exists and call is successful; + * 1 if no incumbent (i.e., integer feasible) exists; + * <0 if there is an error. + */ +int KNITRO_API KN_get_mip_incumbent_obj (const KN_context_ptr kc, + double * const incumbentObj); + +/** Return the value of the current MIP relaxation bound in "relaxBound". + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_mip_relaxation_bnd (const KN_context_ptr kc, + double * const relaxBound); + +/** Return the objective value of the most recently solved MIP + * node subproblem in "lastNodeObj". + * Returns 0 if OK, nonzero if error. + */ +int KNITRO_API KN_get_mip_lastnode_obj (const KN_context_ptr kc, + double * const lastNodeObj); + +/** Return the MIP incumbent solution in "x" if one exists. + * Returns 0 if incumbent solution exists and call is successful; + * 1 if no incumbent (i.e., integer feasible) point exists + * and leaves "x" unmodified; + * <0 if there is an error. + */ +int KNITRO_API KN_get_mip_incumbent_x (const KN_context_ptr kc, + double * const x); + + +/*------------------------------------------------------------------*/ +/* DEFINES */ +/*------------------------------------------------------------------*/ + +/** Use KN_INFINITY to set infinite variable and constraint bounds + * in Knitro. + */ +#define KN_INFINITY DBL_MAX + +/** Possible parameter types. + */ +#define KN_PARAMTYPE_INTEGER 0 +#define KN_PARAMTYPE_FLOAT 1 +#define KN_PARAMTYPE_STRING 2 + +/** Possible model components. + */ +#define KN_COMPONENT_VAR 1 +#define KN_COMPONENT_OBJ 2 +#define KN_COMPONENT_CON 3 +#define KN_COMPONENT_RSD 4 + +/** Possible objective goals for the solver (objGoal in KN_set_obj_goal). + */ +#define KN_OBJGOAL_MINIMIZE 0 +#define KN_OBJGOAL_MAXIMIZE 1 + +/** Possible values for the objective type. + */ +#define KN_OBJTYPE_CONSTANT -1 +#define KN_OBJTYPE_GENERAL 0 +#define KN_OBJTYPE_LINEAR 1 +#define KN_OBJTYPE_QUADRATIC 2 + +/** Possible values for the constraint type. + */ +#define KN_CONTYPE_CONSTANT -1 +#define KN_CONTYPE_GENERAL 0 +#define KN_CONTYPE_LINEAR 1 +#define KN_CONTYPE_QUADRATIC 2 +#define KN_CONTYPE_CONIC 3 + +/** Possible values for the residual type. + */ +#define KN_RSDTYPE_CONSTANT -1 +#define KN_RSDTYPE_GENERAL 0 +#define KN_RSDTYPE_LINEAR 1 + +/** Possible values for the complementarity constraint type + * (ccTypes in KN_set_compcons). Currently only KN_CCTYPE_VARVAR + * is supported. + */ +#define KN_CCTYPE_VARVAR 0 +#define KN_CCTYPE_VARCON 1 /* NOT SUPPORTED YET */ +#define KN_CCTYPE_CONCON 2 /* NOT SUPPORTED YET */ + +/** Possible values for the variable type (xTypes in KN_set_var_types). + */ +#define KN_VARTYPE_CONTINUOUS 0 +#define KN_VARTYPE_INTEGER 1 +#define KN_VARTYPE_BINARY 2 + +/** Possible values for enabling bits to set variable properties + * via KN_set_var_properties(). + */ +#define KN_VAR_LINEAR 1 /*-- LINEAR ONLY EVERYWHERE */ + +/** Possible values for bit flags used to set objective and + * constraint function properties via KN_set_obj_properties() + * and KN_set_con_properties(). + */ +#define KN_OBJ_CONVEX 1 /*-- CONVEX OBJECTIVE */ +#define KN_OBJ_CONCAVE 2 /*-- CONCAVE OBJECTIVE */ +#define KN_OBJ_CONTINUOUS 4 /*-- OBJECTIVE IS CONTINUOUS */ +#define KN_OBJ_DIFFERENTIABLE 8 /*-- (ONCE) DIFFERENTIABLE OBJECTIVE */ +#define KN_OBJ_TWICE_DIFFERENTIABLE 16 /*-- TWICE DIFFERENTIABLE OBJECTIVE */ +#define KN_OBJ_NOISY 32 /*-- OBJECTIVE FUNCTION IS NOISY */ +#define KN_OBJ_NONDETERMINISTIC 64 /*-- OBJECTIVE IS NONDETERMINISTIC */ + +#define KN_CON_CONVEX 1 /*-- CONVEX CONSTRAINT */ +#define KN_CON_CONCAVE 2 /*-- CONCAVE CONSTRAINT */ +#define KN_CON_CONTINUOUS 4 /*-- CONSTRAINT IS CONTINUOUS */ +#define KN_CON_DIFFERENTIABLE 8 /*-- (ONCE) DIFFERENTIABLE CONSTRAINT */ +#define KN_CON_TWICE_DIFFERENTIABLE 16 /*-- TWICE DIFFERENTIABLE CONSTRAINT */ +#define KN_CON_NOISY 32 /*-- CONSTRAINT FUNCTION IS NOISY */ +#define KN_CON_NONDETERMINISTIC 64 /*-- CONSTRAINT IS NONDETERMINISTIC */ + +/** Possible values for dense arrays or matrices. + */ +#define KN_DENSE -1 /*-- GENERIC DENSE (e.g. FOR ARRAYS) */ +#define KN_DENSE_ROWMAJOR -2 /*-- DENSE MATRIX IN ROW MAJOR ORDER */ +#define KN_DENSE_COLMAJOR -3 /*-- DENSE MATRIX IN COLUMN MAJOR ORDER */ + +/** Evaluation request codes + */ +#define KN_RC_EVALFC 1 /*-- OBJECTIVE AND CONSTRAINT FUNCTIONS */ +#define KN_RC_EVALGA 2 /*-- OBJ. GRADIENT AND CONSTRAINT JACOBIAN */ +#define KN_RC_EVALH 3 /*-- HESSIAN OF THE LAGRANGIAN */ +#define KN_RC_EVALHV 7 /*-- HESSIAN-VECTOR PRODUCT */ +#define KN_RC_EVALH_NO_F 8 /*-- NO OBJECTIVE COMPONENT INCLUDED */ +#define KN_RC_EVALHV_NO_F 9 /*-- NO OBJECTIVE COMPONENT INCLUDED */ +#define KN_RC_EVALR 10 /*-- RESIDUAL FUNCTIONS (LEAST SQUARES) */ +#define KN_RC_EVALRJ 11 /*-- RESIDUAL JACOBIAN (LEAST SQUARES) */ +#define KN_RC_EVALFCGA 12 /*-- BOTH FUNCTIONS AND GRADIENTS */ + +/** Return codes when Knitro terminates. + */ +#define KN_RC_OPTIMAL_OR_SATISFACTORY 0 /*-- OPTIMAL CODE */ +#define KN_RC_OPTIMAL 0 +#define KN_RC_NEAR_OPT -100 /*-- FEASIBLE CODES */ +#define KN_RC_FEAS_XTOL -101 +#define KN_RC_FEAS_NO_IMPROVE -102 +#define KN_RC_FEAS_FTOL -103 +#define KN_RC_FEAS_BEST -104 +#define KN_RC_FEAS_MULTISTART -105 +#define KN_RC_INFEASIBLE -200 /*-- INFEASIBLE CODES */ +#define KN_RC_INFEAS_XTOL -201 +#define KN_RC_INFEAS_NO_IMPROVE -202 +#define KN_RC_INFEAS_MULTISTART -203 +#define KN_RC_INFEAS_CON_BOUNDS -204 +#define KN_RC_INFEAS_VAR_BOUNDS -205 +#define KN_RC_UNBOUNDED -300 /*-- UNBOUNDED CODES */ +#define KN_RC_UNBOUNDED_OR_INFEAS -301 +#define KN_RC_ITER_LIMIT_FEAS -400 /*-- LIMIT EXCEEDED CODES (FEASIBLE) */ +#define KN_RC_TIME_LIMIT_FEAS -401 +#define KN_RC_FEVAL_LIMIT_FEAS -402 +#define KN_RC_MIP_EXH_FEAS -403 +#define KN_RC_MIP_TERM_FEAS -404 +#define KN_RC_MIP_SOLVE_LIMIT_FEAS -405 +#define KN_RC_MIP_NODE_LIMIT_FEAS -406 +#define KN_RC_ITER_LIMIT_INFEAS -410 /*-- LIMIT EXCEEDED CODES (INFEASIBLE) */ +#define KN_RC_TIME_LIMIT_INFEAS -411 +#define KN_RC_FEVAL_LIMIT_INFEAS -412 +#define KN_RC_MIP_EXH_INFEAS -413 +#define KN_RC_MIP_SOLVE_LIMIT_INFEAS -415 +#define KN_RC_MIP_NODE_LIMIT_INFEAS -416 +#define KN_RC_CALLBACK_ERR -500 /*-- OTHER FAILURES */ +#define KN_RC_LP_SOLVER_ERR -501 +#define KN_RC_EVAL_ERR -502 +#define KN_RC_OUT_OF_MEMORY -503 +#define KN_RC_USER_TERMINATION -504 +#define KN_RC_OPEN_FILE_ERR -505 +#define KN_RC_BAD_N_OR_F -506 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_BAD_CONSTRAINT -507 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_BAD_JACOBIAN -508 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_BAD_HESSIAN -509 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_BAD_CON_INDEX -510 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_BAD_JAC_INDEX -511 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_BAD_HESS_INDEX -512 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_BAD_CON_BOUNDS -513 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_BAD_VAR_BOUNDS -514 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_ILLEGAL_CALL -515 /*-- KNITRO CALL IS OUT OF SEQUENCE */ +#define KN_RC_BAD_KCPTR -516 /*-- KNITRO PASSED A BAD KC POINTER */ +#define KN_RC_NULL_POINTER -517 /*-- KNITRO PASSED A NULL ARGUMENT */ +#define KN_RC_BAD_INIT_VALUE -518 /*-- APPLICATION INITIAL POINT IS BAD */ +#define KN_RC_LICENSE_ERROR -520 /*-- LICENSE CHECK FAILED */ +#define KN_RC_BAD_PARAMINPUT -521 /*-- INVALID PARAMETER INPUT DETECTED */ +#define KN_RC_LINEAR_SOLVER_ERR -522 /*-- ERROR IN LINEAR SOLVER */ +#define KN_RC_DERIV_CHECK_FAILED -523 /*-- DERIVATIVE CHECK FAILED */ +#define KN_RC_DERIV_CHECK_TERMINATE -524 /*-- DERIVATIVE CHECK TERMINATE */ +#define KN_RC_OVERFLOW_ERR -525 /*-- INTEGER OVERFLOW ERROR */ +#define KN_RC_BAD_SIZE -526 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_BAD_VARIABLE -527 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_BAD_VAR_INDEX -528 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_BAD_OBJECTIVE -529 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_BAD_OBJ_INDEX -530 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_BAD_RESIDUAL -531 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_BAD_RSD_INDEX -532 /*-- PROBLEM DEFINITION ERROR */ +#define KN_RC_INTERNAL_ERROR -600 /*-- CONTACT ARTELYS SUPPORT */ + +/** Parameter IDs used in functions KN_get_xxx_param and KN_set_xxx_param. + * In some cases, parameter values are defined underneath the parameter ID. + */ +#define KN_PARAM_NEWPOINT 1001 +# define KN_NEWPOINT_NONE 0 +# define KN_NEWPOINT_SAVEONE 1 +# define KN_NEWPOINT_SAVEALL 2 +#define KN_PARAM_HONORBNDS 1002 +# define KN_HONORBNDS_AUTO -1 +# define KN_HONORBNDS_NO 0 +# define KN_HONORBNDS_ALWAYS 1 +# define KN_HONORBNDS_INITPT 2 +#define KN_PARAM_NLP_ALGORITHM 1003 +# define KN_NLP_ALG_AUTOMATIC 0 +# define KN_NLP_ALG_AUTO 0 +# define KN_NLP_ALG_BAR_DIRECT 1 +# define KN_NLP_ALG_BAR_CG 2 +# define KN_NLP_ALG_ACT_CG 3 +# define KN_NLP_ALG_ACT_SQP 4 +# define KN_NLP_ALG_MULTI 5 +# define KN_NLP_ALG_AL 6 +#define KN_PARAM_ALGORITHM 1003 /** PRE-15.0 OPTION NAME */ +#define KN_PARAM_ALG 1003 +# define KN_ALG_AUTOMATIC 0 +# define KN_ALG_AUTO 0 +# define KN_ALG_BAR_DIRECT 1 +# define KN_ALG_BAR_CG 2 +# define KN_ALG_ACT_CG 3 +# define KN_ALG_ACT_SQP 4 +# define KN_ALG_MULTI 5 +# define KN_ALG_AL 6 +#define KN_PARAM_BAR_MURULE 1004 +# define KN_BAR_MURULE_AUTOMATIC 0 +# define KN_BAR_MURULE_AUTO 0 +# define KN_BAR_MURULE_MONOTONE 1 +# define KN_BAR_MURULE_ADAPTIVE 2 +# define KN_BAR_MURULE_PROBING 3 +# define KN_BAR_MURULE_DAMPMPC 4 +# define KN_BAR_MURULE_FULLMPC 5 +# define KN_BAR_MURULE_QUALITY 6 +#define KN_PARAM_BAR_FEASIBLE 1006 +# define KN_BAR_FEASIBLE_NO 0 +# define KN_BAR_FEASIBLE_STAY 1 +# define KN_BAR_FEASIBLE_GET 2 +# define KN_BAR_FEASIBLE_GET_STAY 3 +#define KN_PARAM_GRADOPT 1007 +# define KN_GRADOPT_EXACT 1 +# define KN_GRADOPT_FORWARD 2 +# define KN_GRADOPT_CENTRAL 3 +# define KN_GRADOPT_USER_FORWARD 4 +# define KN_GRADOPT_USER_CENTRAL 5 +#define KN_PARAM_HESSOPT 1008 +# define KN_HESSOPT_AUTO 0 +# define KN_HESSOPT_EXACT 1 +# define KN_HESSOPT_BFGS 2 +# define KN_HESSOPT_SR1 3 +# define KN_HESSOPT_PRODUCT_FINDIFF 4 +# define KN_HESSOPT_PRODUCT 5 +# define KN_HESSOPT_LBFGS 6 +# define KN_HESSOPT_GAUSS_NEWTON 7 +#define KN_PARAM_BAR_INITPT 1009 +# define KN_BAR_INITPT_AUTO 0 +# define KN_BAR_INITPT_CONVEX 1 +# define KN_BAR_INITPT_NEARBND 2 +# define KN_BAR_INITPT_CENTRAL 3 +#define KN_PARAM_ACT_LPSOLVER 1012 +# define KN_ACT_LPSOLVER_INTERNAL 1 +# define KN_ACT_LPSOLVER_CPLEX 2 +# define KN_ACT_LPSOLVER_XPRESS 3 +#define KN_PARAM_CG_MAXIT 1013 +#define KN_PARAM_MAXIT 1014 +#define KN_PARAM_OUTLEV 1015 +# define KN_OUTLEV_NONE 0 +# define KN_OUTLEV_SUMMARY 1 +# define KN_OUTLEV_ITER_10 2 +# define KN_OUTLEV_ITER 3 +# define KN_OUTLEV_ITER_VERBOSE 4 +# define KN_OUTLEV_ITER_X 5 +# define KN_OUTLEV_ALL 6 +#define KN_PARAM_OUTMODE 1016 +# define KN_OUTMODE_SCREEN 0 +# define KN_OUTMODE_FILE 1 +# define KN_OUTMODE_BOTH 2 +#define KN_PARAM_SCALE 1017 +# define KN_SCALE_NEVER 0 +# define KN_SCALE_NO 0 +# define KN_SCALE_USER_INTERNAL 1 +# define KN_SCALE_USER_NONE 2 +# define KN_SCALE_INTERNAL 3 +#define KN_PARAM_SOC 1019 +# define KN_SOC_NO 0 +# define KN_SOC_MAYBE 1 +# define KN_SOC_YES 2 +#define KN_PARAM_DELTA 1020 +#define KN_PARAM_BAR_FEASMODETOL 1021 +#define KN_PARAM_FEASTOL 1022 +#define KN_PARAM_FEASTOLABS 1023 +#define KN_PARAM_MAXTIMECPU 1024 +#define KN_PARAM_BAR_INITMU 1025 +#define KN_PARAM_OBJRANGE 1026 +#define KN_PARAM_OPTTOL 1027 +#define KN_PARAM_OPTTOLABS 1028 +#define KN_PARAM_LINSOLVER_PIVOTTOL 1029 +#define KN_PARAM_XTOL 1030 +#define KN_PARAM_DEBUG 1031 +# define KN_DEBUG_NONE 0 +# define KN_DEBUG_PROBLEM 1 +# define KN_DEBUG_EXECUTION 2 +#define KN_PARAM_MULTISTART 1033 +#define KN_PARAM_MSENABLE 1033 +#define KN_PARAM_MS_ENABLE 1033 +# define KN_MULTISTART_NO 0 +# define KN_MS_ENABLE_NO 0 +# define KN_MULTISTART_YES 1 +# define KN_MS_ENABLE_YES 1 +#define KN_PARAM_MSMAXSOLVES 1034 +#define KN_PARAM_MS_MAXSOLVES 1034 +#define KN_PARAM_MSMAXBNDRANGE 1035 +#define KN_PARAM_MS_MAXBNDRANGE 1035 +#define KN_PARAM_MSMAXTIMECPU 1036 +#define KN_PARAM_MS_MAXTIMECPU 1036 +#define KN_PARAM_MSMAXTIMEREAL 1037 +#define KN_PARAM_MS_MAXTIMEREAL 1037 +#define KN_PARAM_LMSIZE 1038 +#define KN_PARAM_BAR_MAXCROSSIT 1039 +#define KN_PARAM_MAXTIMEREAL 1040 +#define KN_PARAM_CG_PRECOND 1041 +# define KN_CG_PRECOND_NONE 0 +# define KN_CG_PRECOND_CHOL 1 +#define KN_PARAM_BLASOPTION 1042 +# define KN_BLASOPTION_AUTO -1 +# define KN_BLASOPTION_KNITRO 0 +# define KN_BLASOPTION_INTEL 1 +# define KN_BLASOPTION_DYNAMIC 2 +# define KN_BLASOPTION_BLIS 3 +# define KN_BLASOPTION_APPLE 4 +#define KN_PARAM_BAR_MAXREFACTOR 1043 +#define KN_PARAM_LINESEARCH_MAXTRIALS 1044 +#define KN_PARAM_BLASOPTIONLIB 1045 +#define KN_PARAM_OUTAPPEND 1046 +# define KN_OUTAPPEND_NO 0 +# define KN_OUTAPPEND_YES 1 +#define KN_PARAM_OUTDIR 1047 +#define KN_PARAM_CPLEXLIB 1048 +#define KN_PARAM_BAR_PENRULE 1049 +# define KN_BAR_PENRULE_AUTO 0 +# define KN_BAR_PENRULE_SINGLE 1 +# define KN_BAR_PENRULE_FLEX 2 +#define KN_PARAM_BAR_PENCONS 1050 +# define KN_BAR_PENCONS_AUTO -1 +# define KN_BAR_PENCONS_NONE 0 +# define KN_BAR_PENCONS_ALL 2 +# define KN_BAR_PENCONS_EQUALITIES 3 +# define KN_BAR_PENCONS_INFEAS 4 /*-- DEPRECATED */ +#define KN_PARAM_MSNUMTOSAVE 1051 +#define KN_PARAM_MS_NUMTOSAVE 1051 +#define KN_PARAM_MSSAVETOL 1052 +#define KN_PARAM_MS_SAVETOL 1052 +#define KN_PARAM_PRESOLVEDEBUG 1053 /*-- FOR AMPL ONLY */ +# define KN_PRESOLVEDBG_NONE 0 +# define KN_PRESOLVEDBG_BASIC 1 +# define KN_PRESOLVEDBG_VERBOSE 2 +# define KN_PRESOLVEDBG_DETAIL 3 +#define KN_PARAM_MSTERMINATE 1054 +#define KN_PARAM_MS_TERMINATE 1054 +# define KN_MSTERMINATE_MAXSOLVES 0 +# define KN_MS_TERMINATE_MAXSOLVES 0 +# define KN_MSTERMINATE_OPTIMAL 1 +# define KN_MS_TERMINATE_OPTIMAL 1 +# define KN_MSTERMINATE_FEASIBLE 2 +# define KN_MS_TERMINATE_FEASIBLE 2 +# define KN_MSTERMINATE_ANY 3 +# define KN_MS_TERMINATE_ANY 3 +# define KN_MSTERMINATE_RULEBASED 4 +# define KN_MS_TERMINATE_RULEBASED 4 +#define KN_PARAM_MSSTARTPTRANGE 1055 +#define KN_PARAM_MS_STARTPTRANGE 1055 +#define KN_PARAM_INFEASTOL 1056 +#define KN_PARAM_LINSOLVER 1057 +# define KN_LINSOLVER_AUTO 0 +# define KN_LINSOLVER_INTERNAL 1 +# define KN_LINSOLVER_HYBRID 2 +# define KN_LINSOLVER_DENSEQR 3 +# define KN_LINSOLVER_MA27 4 +# define KN_LINSOLVER_MA57 5 +# define KN_LINSOLVER_MKLPARDISO 6 +# define KN_LINSOLVER_MA97 7 +# define KN_LINSOLVER_MA86 8 +# define KN_LINSOLVER_APPLE 9 +#define KN_PARAM_BAR_DIRECTINTERVAL 1058 +#define KN_PARAM_PRESOLVE 1059 +# define KN_PRESOLVE_NO 0 +# define KN_PRESOLVE_NONE 0 /*-- DEPRECATED */ +# define KN_PRESOLVE_YES 1 +# define KN_PRESOLVE_BASIC 1 /*-- DEPRECATED */ +# define KN_PRESOLVE_ADVANCED 2 /*-- DEPRECATED */ +#define KN_PARAM_PRESOLVE_TOL 1060 +#define KN_PARAM_BAR_SWITCHRULE 1061 +# define KN_BAR_SWITCHRULE_AUTO -1 +# define KN_BAR_SWITCHRULE_NEVER 0 +# define KN_BAR_SWITCHRULE_MODERATE 2 +# define KN_BAR_SWITCHRULE_AGGRESSIVE 3 +#define KN_PARAM_HESSIAN_NO_F 1062 +# define KN_HESSIAN_NO_F_FORBID 0 +# define KN_HESSIAN_NO_F_ALLOW 1 +#define KN_PARAM_MA_TERMINATE 1063 +# define KN_MA_TERMINATE_ALL 0 +# define KN_MA_TERMINATE_OPTIMAL 1 +# define KN_MA_TERMINATE_FEASIBLE 2 +# define KN_MA_TERMINATE_ANY 3 +#define KN_PARAM_MA_MAXTIMECPU 1064 +#define KN_PARAM_MA_MAXTIMEREAL 1065 +#define KN_PARAM_MSSEED 1066 +#define KN_PARAM_MS_SEED 1066 +#define KN_PARAM_MA_OUTSUB 1067 +# define KN_MA_OUTSUB_NONE 0 +# define KN_MA_OUTSUB_YES 1 +#define KN_PARAM_MS_OUTSUB 1068 +# define KN_MS_OUTSUB_NONE 0 +# define KN_MS_OUTSUB_YES 1 +#define KN_PARAM_XPRESSLIB 1069 +#define KN_PARAM_TUNER 1070 +# define KN_TUNER_OFF 0 +# define KN_TUNER_ON 1 +#define KN_PARAM_TUNER_OPTIONSFILE 1071 +#define KN_PARAM_TUNER_MAXTIMECPU 1072 +#define KN_PARAM_TUNER_MAXTIMEREAL 1073 +#define KN_PARAM_TUNER_OUTSUB 1074 +# define KN_TUNER_OUTSUB_NONE 0 +# define KN_TUNER_OUTSUB_SUMMARY 1 +# define KN_TUNER_OUTSUB_ALL 2 +#define KN_PARAM_TUNER_TERMINATE 1075 +# define KN_TUNER_TERMINATE_ALL 0 +# define KN_TUNER_TERMINATE_OPTIMAL 1 +# define KN_TUNER_TERMINATE_FEASIBLE 2 +# define KN_TUNER_TERMINATE_ANY 3 +#define KN_PARAM_LINSOLVER_OOC 1076 +# define KN_LINSOLVER_OOC_NO 0 +# define KN_LINSOLVER_OOC_MAYBE 1 +# define KN_LINSOLVER_OOC_YES 2 +#define KN_PARAM_BAR_RELAXCONS 1077 +# define KN_BAR_RELAXCONS_NONE 0 +# define KN_BAR_RELAXCONS_EQS 1 +# define KN_BAR_RELAXCONS_INEQS 2 +# define KN_BAR_RELAXCONS_ALL 3 +#define KN_PARAM_MSDETERMINISTIC 1078 +#define KN_PARAM_MS_DETERMINISTIC 1078 +# define KN_MSDETERMINISTIC_NO 0 +# define KN_MS_DETERMINISTIC_NO 0 +# define KN_MSDETERMINISTIC_YES 1 +# define KN_MS_DETERMINISTIC_YES 1 +#define KN_PARAM_BAR_REFINEMENT 1079 +# define KN_BAR_REFINEMENT_NO 0 +# define KN_BAR_REFINEMENT_YES 1 +#define KN_PARAM_DERIVCHECK 1080 +# define KN_DERIVCHECK_NONE 0 +# define KN_DERIVCHECK_FIRST 1 +# define KN_DERIVCHECK_SECOND 2 +# define KN_DERIVCHECK_ALL 3 +#define KN_PARAM_DERIVCHECK_TYPE 1081 +# define KN_DERIVCHECK_FORWARD 1 +# define KN_DERIVCHECK_CENTRAL 2 +#define KN_PARAM_DERIVCHECK_TOL 1082 +#define KN_PARAM_LINSOLVER_INEXACT 1083 /*-- UNDOCUMENTED/EXPERIMENTAL */ +# define KN_LINSOLVER_INEXACT_NO 0 +# define KN_LINSOLVER_INEXACT_YES 1 +#define KN_PARAM_LINSOLVER_INEXACTTOL 1084 /*-- UNDOCUMENTED/EXPERIMENTAL */ +#define KN_PARAM_MAXFEVALS 1085 +#define KN_PARAM_FSTOPVAL 1086 +#define KN_PARAM_DATACHECK 1087 +# define KN_DATACHECK_NO 0 +# define KN_DATACHECK_YES 1 +#define KN_PARAM_DERIVCHECK_TERMINATE 1088 +# define KN_DERIVCHECK_STOPERROR 1 +# define KN_DERIVCHECK_STOPALWAYS 2 +#define KN_PARAM_BAR_WATCHDOG 1089 +# define KN_BAR_WATCHDOG_NO 0 +# define KN_BAR_WATCHDOG_YES 1 +#define KN_PARAM_FTOL 1090 +#define KN_PARAM_FTOL_ITERS 1091 +#define KN_PARAM_ACT_QPALG 1092 +# define KN_ACT_QPALG_AUTO 0 +# define KN_ACT_QPALG_BAR_DIRECT 1 +# define KN_ACT_QPALG_BAR_CG 2 +# define KN_ACT_QPALG_ACT_CG 3 +#define KN_PARAM_BAR_INITPI_MPEC 1093 +#define KN_PARAM_XTOL_ITERS 1094 +#define KN_PARAM_LINESEARCH 1095 +# define KN_LINESEARCH_AUTO 0 +# define KN_LINESEARCH_BACKTRACK 1 +# define KN_LINESEARCH_INTERPOLATE 2 +# define KN_LINESEARCH_WEAKWOLFE 3 +#define KN_PARAM_OUT_CSVINFO 1096 +# define KN_OUT_CSVINFO_NO 0 +# define KN_OUT_CSVINFO_YES 1 +#define KN_PARAM_INITPENALTY 1097 +#define KN_PARAM_ACT_LPFEASTOL 1098 +#define KN_PARAM_CG_STOPTOL 1099 +#define KN_PARAM_RESTARTS 1100 +#define KN_PARAM_RESTARTS_MAXIT 1101 +#define KN_PARAM_BAR_SLACKBOUNDPUSH 1102 +#define KN_PARAM_CG_PMEM 1103 +#define KN_PARAM_BAR_SWITCHOBJ 1104 +# define KN_BAR_SWITCHOBJ_NONE 0 +# define KN_BAR_SWITCHOBJ_SCALARPROX 1 +# define KN_BAR_SWITCHOBJ_DIAGPROX 2 +#define KN_PARAM_OUTNAME 1105 +#define KN_PARAM_OUT_CSVNAME 1106 +#define KN_PARAM_ACT_PARAMETRIC 1107 +# define KN_ACT_PARAMETRIC_NO 0 +# define KN_ACT_PARAMETRIC_MAYBE 1 +# define KN_ACT_PARAMETRIC_YES 2 +#define KN_PARAM_ACT_LPDUMPMPS 1108 +# define KN_ACT_LPDUMPMPS_NO 0 +# define KN_ACT_LPDUMPMPS_YES 1 +#define KN_PARAM_ACT_LPALG 1109 +# define KN_ACT_LPALG_DEFAULT 0 +# define KN_ACT_LPALG_PRIMAL 1 +# define KN_ACT_LPALG_PRIMALSIMPLEX 1 +# define KN_ACT_LPALG_DUAL 2 +# define KN_ACT_LPALG_DUALSIMPLEX 2 +# define KN_ACT_LPALG_BARRIER 3 +#define KN_PARAM_ACT_LPPRESOLVE 1110 +# define KN_ACT_LPPRESOLVE_OFF 0 +# define KN_ACT_LPPRESOLVE_ON 1 +#define KN_PARAM_ACT_LPPENALTY 1111 +# define KN_ACT_LPPENALTY_ALL 1 +# define KN_ACT_LPPENALTY_NONLINEAR 2 +# define KN_ACT_LPPENALTY_DYNAMIC 3 +#define KN_PARAM_BNDRANGE 1112 +#define KN_PARAM_BAR_CONIC_ENABLE 1113 +# define KN_BAR_CONIC_ENABLE_AUTO -1 +# define KN_BAR_CONIC_ENABLE_NONE 0 +# define KN_BAR_CONIC_ENABLE_SOC 1 +#define KN_PARAM_CONVEX 1114 +# define KN_CONVEX_AUTO -1 +# define KN_CONVEX_NO 0 +# define KN_CONVEX_YES 1 +#define KN_PARAM_OUT_HINTS 1115 +# define KN_OUT_HINTS_NO 0 +# define KN_OUT_HINTS_YES 1 +#define KN_PARAM_EVAL_FCGA 1116 +# define KN_EVAL_FCGA_NO 0 +# define KN_EVAL_FCGA_YES 1 +#define KN_PARAM_BAR_MAXCORRECTORS 1117 +#define KN_PARAM_STRAT_WARM_START 1118 +# define KN_STRAT_WARM_START_NO 0 +# define KN_STRAT_WARM_START_YES 1 +#define KN_PARAM_FINDIFF_TERMINATE 1119 +# define KN_FINDIFF_TERMINATE_NONE 0 +# define KN_FINDIFF_TERMINATE_ERREST 1 +#define KN_PARAM_CPUPLATFORM 1120 +# define KN_CPUPLATFORM_AUTO -1 +# define KN_CPUPLATFORM_COMPATIBLE 1 +# define KN_CPUPLATFORM_SSE2 2 +# define KN_CPUPLATFORM_AVX 3 +# define KN_CPUPLATFORM_AVX2 4 +# define KN_CPUPLATFORM_AVX512 5 /*-- EXPERIMENTAL */ +#define KN_PARAM_PRESOLVE_PASSES 1121 +#define KN_PARAM_PRESOLVE_LEVEL 1122 +# define KN_PRESOLVE_LEVEL_AUTO -1 +# define KN_PRESOLVE_LEVEL_1 1 +# define KN_PRESOLVE_LEVEL_2 2 +#define KN_PARAM_FINDIFF_RELSTEPSIZE 1123 +#define KN_PARAM_INFEASTOL_ITERS 1124 +#define KN_PARAM_PRESOLVEOP_TIGHTEN 1125 +# define KN_PRESOLVEOP_TIGHTEN_AUTO -1 +# define KN_PRESOLVEOP_TIGHTEN_NONE 0 +# define KN_PRESOLVEOP_TIGHTEN_VARBND 1 +# define KN_PRESOLVEOP_TIGHTEN_COEF 2 /*-- DEPRECATED */ +# define KN_PRESOLVEOP_TIGHTEN_ALL 3 /*-- DEPRECATED */ +#define KN_PARAM_BAR_LINSYS 1126 +# define KN_BAR_LINSYS_AUTO -1 +# define KN_BAR_LINSYS_FULL 0 +# define KN_BAR_LINSYS_COMPACT1 1 /*-- DEPRECATED */ +# define KN_BAR_LINSYS_ELIMINATE_SLACKS 1 +# define KN_BAR_LINSYS_COMPACT2 2 /*-- DEPRECATED */ +# define KN_BAR_LINSYS_ELIMINATE_BOUNDS 2 +# define KN_BAR_LINSYS_ELIMINATE_INEQS 3 +#define KN_PARAM_PRESOLVE_INITPT 1127 +# define KN_PRESOLVE_INITPT_AUTO -1 +# define KN_PRESOLVE_INITPT_NOSHIFT 0 +# define KN_PRESOLVE_INITPT_LINSHIFT 1 +# define KN_PRESOLVE_INITPT_ANYSHIFT 2 +#define KN_PARAM_ACT_QPPENALTY 1128 +# define KN_ACT_QPPENALTY_AUTO -1 +# define KN_ACT_QPPENALTY_NONE 0 +# define KN_ACT_QPPENALTY_ALL 1 +#define KN_PARAM_BAR_LINSYS_STORAGE 1129 +# define KN_BAR_LINSYS_STORAGE_AUTO -1 +# define KN_BAR_LINSYS_STORAGE_LOWMEM 1 +# define KN_BAR_LINSYS_STORAGE_NORMAL 2 +#define KN_PARAM_LINSOLVER_MAXITREF 1130 +#define KN_PARAM_BFGS_SCALING 1131 +# define KN_BFGS_SCALING_DYNAMIC 0 +# define KN_BFGS_SCALING_INVHESS 1 +# define KN_BFGS_SCALING_HESS 2 +#define KN_PARAM_BAR_INITSHIFTTOL 1132 +#define KN_PARAM_NUMTHREADS 1133 +#define KN_PARAM_CONCURRENT_EVALS 1134 +# define KN_CONCURRENT_EVALS_NO 0 +# define KN_CONCURRENT_EVALS_YES 1 +#define KN_PARAM_BLAS_NUMTHREADS 1135 +#define KN_PARAM_LINSOLVER_NUMTHREADS 1136 +#define KN_PARAM_MS_NUMTHREADS 1137 +#define KN_PARAM_CONIC_NUMTHREADS 1138 +#define KN_PARAM_NCVX_QCQP_INIT 1139 +# define KN_NCVX_QCQP_INIT_AUTO -1 +# define KN_NCVX_QCQP_INIT_NONE 0 +# define KN_NCVX_QCQP_INIT_LINEAR 1 +# define KN_NCVX_QCQP_INIT_HYBRID 2 +# define KN_NCVX_QCQP_INIT_PENALTY 3 +# define KN_NCVX_QCQP_INIT_CVXQUAD 4 +#define KN_PARAM_FINDIFF_ESTNOISE 1140 +# define KN_FINDIFF_ESTNOISE_NO 0 +# define KN_FINDIFF_ESTNOISE_YES 1 +# define KN_FINDIFF_ESTNOISE_WITHCURV 2 +#define KN_PARAM_FINDIFF_NUMTHREADS 1141 +#define KN_PARAM_BAR_MPEC_HEURISTIC 1142 +# define KN_BAR_MPEC_HEURISTIC_NO 0 +# define KN_BAR_MPEC_HEURISTIC_YES 1 +#define KN_PARAM_PRESOLVEOP_REDUNDANT 1143 +# define KN_PRESOLVEOP_REDUNDANT_NONE 0 +# define KN_PRESOLVEOP_REDUNDANT_DUPCON 1 +# define KN_PRESOLVEOP_REDUNDANT_DEPCON 2 +#define KN_PARAM_LINSOLVER_ORDERING 1144 +# define KN_LINSOLVER_ORDERING_AUTO -1 +# define KN_LINSOLVER_ORDERING_BEST 0 +# define KN_LINSOLVER_ORDERING_AMD 1 +# define KN_LINSOLVER_ORDERING_METIS 2 +#define KN_PARAM_LINSOLVER_NODEAMALG 1145 +#define KN_PARAM_PRESOLVEOP_SUBSTITUTION 1146 +# define KN_PRESOLVEOP_SUBSTITUTION_AUTO -1 +# define KN_PRESOLVEOP_SUBSTITUTION_NONE 0 +# define KN_PRESOLVEOP_SUBSTITUTION_SIMPLE 1 +# define KN_PRESOLVEOP_SUBSTITUTION_ALL 2 +#define KN_PARAM_PRESOLVEOP_SUBSTITUTION_TOL 1147 +#define KN_PARAM_MS_INITPT_CLUSTER 1149 +# define KN_MS_INITPT_CLUSTER_NONE 0 +# define KN_MS_INITPT_CLUSTER_SL 1 +#define KN_PARAM_SCALE_VARS 1153 +# define KN_SCALE_VARS_NONE 0 +# define KN_SCALE_VARS_BNDS 1 +#define KN_PARAM_BAR_MAXMU 1154 +#define KN_PARAM_BAR_GLOBALIZE 1155 +# define KN_BAR_GLOBALIZE_NONE 0 +# define KN_BAR_GLOBALIZE_KKT 1 +# define KN_BAR_GLOBALIZE_FILTER 2 +#define KN_PARAM_LINSOLVER_SCALING 1156 +# define KN_LINSOLVER_SCALING_NONE 0 +# define KN_LINSOLVER_SCALING_ALWAYS 1 +# define KN_LINSOLVER_SCALING_DYNAMIC 2 +#define KN_PARAM_INITPT_STRATEGY 1158 +# define KN_INITPT_STRATEGY_AUTO -1 +# define KN_INITPT_STRATEGY_BASIC 1 +# define KN_INITPT_STRATEGY_ADVANCED 2 +#define KN_PARAM_EVAL_COST 1159 +# define KN_EVAL_COST_UNSPECIFIED 0 +# define KN_EVAL_COST_INEXPENSIVE 1 +# define KN_EVAL_COST_EXPENSIVE 2 +#define KN_PARAM_MS_TERMINATERULE_TOL 1160 +#define KN_PARAM_SOLTYPE 1161 +# define KN_SOLTYPE_FINAL 0 +# define KN_SOLTYPE_BESTFEAS 1 +#define KN_PARAM_MAXTIME 1163 +#define KN_PARAM_MA_SUB_MAXTIME 1164 +#define KN_PARAM_MS_SUB_MAXTIME 1165 +#define KN_PARAM_TUNER_SUB_MAXTIME 1166 +#define KN_PARAM_INITPTFILE 1167 +#define KN_PARAM_LP_ALGORITHM 1170 +#define KN_PARAM_LP_ALG 1170 +# define KN_LP_ALG_AUTO -1 +# define KN_LP_ALG_NLPALGORITHM 0 +# define KN_LP_ALG_PRIMALSIMPLEX 1 +# define KN_LP_ALG_DUALSIMPLEX 2 +# define KN_LP_ALG_BARRIER 3 +# define KN_LP_ALG_PDLP 4 +#define KN_PARAM_AL_INITPENALTY 1171 +#define KN_PARAM_AL_MAXPENALTY 1172 +#define KN_PARAM_PRESOLVEOP_PROBING 1174 +# define KN_PRESOLVEOP_PROBING_AUTO -1 +# define KN_PRESOLVEOP_PROBING_OFF 0 +# define KN_PRESOLVEOP_PROBING_LIGHT 1 +# define KN_PRESOLVEOP_PROBING_FULL 2 +#define KN_PARAM_PRESOLVEOP_CLIQUE_MERGING 1176 +# define KN_PRESOLVEOP_CLIQUE_MERGING_AUTO -1 +# define KN_PRESOLVEOP_CLIQUE_MERGING_OFF 0 +# define KN_PRESOLVEOP_CLIQUE_MERGING_ON 1 + +/* ----- MIP-specific parameters ----- */ +#define KN_PARAM_MIP_METHOD 2001 +# define KN_MIP_METHOD_AUTO 0 +# define KN_MIP_METHOD_BB 1 +# define KN_MIP_METHOD_HQG 2 +# define KN_MIP_METHOD_MISQP 3 +#define KN_PARAM_MIP_BRANCHRULE 2002 +# define KN_MIP_BRANCH_AUTO 0 +# define KN_MIP_BRANCH_MOSTFRAC 1 +# define KN_MIP_BRANCH_PSEUDOCOST 2 +# define KN_MIP_BRANCH_STRONG 3 +#define KN_PARAM_MIP_SELECTRULE 2003 +# define KN_MIP_SEL_AUTO 0 +# define KN_MIP_SEL_DEPTHFIRST 1 +# define KN_MIP_SEL_BESTBOUND 2 +# define KN_MIP_SEL_COMBO_1 3 +#define KN_PARAM_MIP_INTGAPABS 2004 /*-- DEPRECATED */ +#define KN_PARAM_MIP_OPTGAPABS 2004 +#define KN_PARAM_MIP_INTGAPREL 2005 /*-- DEPRECATED */ +#define KN_PARAM_MIP_OPTGAPREL 2005 +#define KN_PARAM_MIP_MAXTIMECPU 2006 +#define KN_PARAM_MIP_MAXTIMEREAL 2007 +#define KN_PARAM_MIP_MAXSOLVES 2008 +#define KN_PARAM_MIP_INTEGERTOL 2009 +#define KN_PARAM_MIP_OUTLEVEL 2010 +# define KN_MIP_OUTLEVEL_NONE 0 +# define KN_MIP_OUTLEVEL_ITERS 1 +# define KN_MIP_OUTLEVEL_ITERSTIME 2 +# define KN_MIP_OUTLEVEL_ROOT 3 +#define KN_PARAM_MIP_OUTINTERVAL 2011 +#define KN_PARAM_MIP_OUTSUB 2012 +# define KN_MIP_OUTSUB_NONE 0 +# define KN_MIP_OUTSUB_YES 1 +# define KN_MIP_OUTSUB_YESPROB 2 +#define KN_PARAM_MIP_DEBUG 2013 +# define KN_MIP_DEBUG_NONE 0 +# define KN_MIP_DEBUG_ALL 1 +#define KN_PARAM_MIP_IMPLICATNS 2014 /*-- USE LOGICAL IMPLICATIONS */ +#define KN_PARAM_MIP_IMPLICATIONS 2014 +# define KN_MIP_IMPLICATNS_NO 0 +# define KN_MIP_IMPLICATIONS_NO 0 +# define KN_MIP_IMPLICATNS_YES 1 +# define KN_MIP_IMPLICATIONS_YES 1 +#define KN_PARAM_MIP_GUB_BRANCH 2015 /*-- BRANCH ON GENERALIZED BOUNDS */ +# define KN_MIP_GUB_BRANCH_NO 0 +# define KN_MIP_GUB_BRANCH_YES 1 +#define KN_PARAM_MIP_KNAPSACK 2016 /*-- KNAPSACK CUTS */ +# define KN_MIP_KNAPSACK_AUTO -1 +# define KN_MIP_KNAPSACK_NO 0 +# define KN_MIP_KNAPSACK_NONE 0 /*-- NONE */ +# define KN_MIP_KNAPSACK_ROOT 1 /*-- IN THE ROOT */ +# define KN_MIP_KNAPSACK_TREE 2 /*-- IN THE WHOLE TREE */ +# define KN_MIP_KNAPSACK_INEQ 1 /*-- DEPRECATED */ +# define KN_MIP_KNAPSACK_LIFTED 2 /*-- DEPRECATED */ +# define KN_MIP_KNAPSACK_ALL 3 /*-- DEPRECATED */ +#define KN_PARAM_MIP_ROUNDING 2017 +# define KN_MIP_ROUND_AUTO -1 +# define KN_MIP_ROUND_NONE 0 /*-- DO NOT ATTEMPT ROUNDING */ +# define KN_MIP_ROUND_HEURISTIC 2 /*-- USE FAST HEURISTIC */ +# define KN_MIP_ROUND_NLP_SOME 3 /*-- SOLVE NLP IF LIKELY TO WORK */ +# define KN_MIP_ROUND_NLP_ALWAYS 4 /*-- SOLVE NLP ALWAYS */ +#define KN_PARAM_MIP_ROOT_NLPALG 2018 +#define KN_PARAM_MIP_ROOTALG 2018 /*-- DEPRECATED */ +# define KN_MIP_ROOT_NLPALG_AUTO 0 +# define KN_MIP_ROOTALG_AUTO 0 /*-- DEPRECATED */ +# define KN_MIP_ROOT_NLPALG_BAR_DIRECT 1 +# define KN_MIP_ROOTALG_BAR_DIRECT 1 /*-- DEPRECATED */ +# define KN_MIP_ROOT_NLPALG_BAR_CG 2 +# define KN_MIP_ROOTALG_BAR_CG 2 /*-- DEPRECATED */ +# define KN_MIP_ROOT_NLPALG_ACT_CG 3 +# define KN_MIP_ROOTALG_ACT_CG 3 /*-- DEPRECATED */ +# define KN_MIP_ROOT_NLPALG_ACT_SQP 4 +# define KN_MIP_ROOTALG_ACT_SQP 4 /*-- DEPRECATED */ +# define KN_MIP_ROOTALG_MULTI 5 /*-- DEPRECATED */ +#define KN_PARAM_MIP_LPALG 2019 /*-- DEPRECATED: USE */ +# define KN_MIP_LPALG_AUTO 0 /*-- KN_PARAM_MIP_ROOT_LPALG OR */ +# define KN_MIP_LPALG_BAR_DIRECT 1 /*-- KN_PARAM_MIP_NODE_LPALG */ +# define KN_MIP_LPALG_BAR_CG 2 +# define KN_MIP_LPALG_ACT_CG 3 +#define KN_PARAM_MIP_TERMINATE 2020 +# define KN_MIP_TERMINATE_OPTIMAL 0 +# define KN_MIP_TERMINATE_FEASIBLE 1 +#define KN_PARAM_MIP_MAXNODES 2021 +#define KN_PARAM_MIP_HEURISTIC 2022 /*-- DEPRECATED */ +#define KN_PARAM_MIP_HEUR_MAXIT 2023 +#define KN_PARAM_MIP_HEUR_MAXTIMECPU 2024 /*-- INACTIVE */ +#define KN_PARAM_MIP_HEUR_MAXTIMEREAL 2025 /*-- INACTIVE */ +#define KN_PARAM_MIP_PSEUDOINIT 2026 +# define KN_MIP_PSEUDOINIT_AUTO 0 +# define KN_MIP_PSEUDOINIT_AVE 1 +# define KN_MIP_PSEUDOINIT_STRONG 2 +#define KN_PARAM_MIP_STRONG_MAXIT 2027 +#define KN_PARAM_MIP_STRONG_CANDLIM 2028 +#define KN_PARAM_MIP_STRONG_LEVEL 2029 +#define KN_PARAM_MIP_INTVAR_STRATEGY 2030 +# define KN_MIP_INTVAR_STRATEGY_NONE 0 +# define KN_MIP_INTVAR_STRATEGY_RELAX 1 +# define KN_MIP_INTVAR_STRATEGY_MPEC 2 +#define KN_PARAM_MIP_RELAXABLE 2031 +# define KN_MIP_RELAXABLE_NONE 0 +# define KN_MIP_RELAXABLE_ALL 1 +#define KN_PARAM_MIP_NODE_NLPALG 2032 +#define KN_PARAM_MIP_NODEALG 2032 /*-- DEPRECATED */ +# define KN_MIP_NODE_NLPALG_AUTO 0 +# define KN_MIP_NODEALG_AUTO 0 /*-- DEPRECATED */ +# define KN_MIP_NODE_NLPALG_BAR_DIRECT 1 +# define KN_MIP_NODEALG_BAR_DIRECT 1 /*-- DEPRECATED */ +# define KN_MIP_NODE_NLPALG_BAR_CG 2 +# define KN_MIP_NODEALG_BAR_CG 2 /*-- DEPRECATED */ +# define KN_MIP_NODE_NLPALG_ACT_CG 3 +# define KN_MIP_NODEALG_ACT_CG 3 /*-- DEPRECATED */ +# define KN_MIP_NODE_NLPALG_ACT_SQP 4 +# define KN_MIP_NODEALG_ACT_SQP 4 /*-- DEPRECATED */ +# define KN_MIP_NODEALG_MULTI 5 /*-- DEPRECATED */ +#define KN_PARAM_MIP_HEUR_TERMINATE 2033 +# define KN_MIP_HEUR_TERMINATE_FEASIBLE 1 +# define KN_MIP_HEUR_TERMINATE_LIMIT 2 +#define KN_PARAM_MIP_SELECTDIR 2034 +# define KN_MIP_SELECTDIR_DOWN 0 +# define KN_MIP_SELECTDIR_UP 1 +#define KN_PARAM_MIP_CUTFACTOR 2035 +#define KN_PARAM_MIP_ZEROHALF 2036 /*-- ZEROHALF CUTS */ +# define KN_MIP_ZEROHALF_AUTO -1 +# define KN_MIP_ZEROHALF_NONE 0 /*-- NONE */ +# define KN_MIP_ZEROHALF_ROOT 1 /*-- IN THE ROOT */ +# define KN_MIP_ZEROHALF_TREE 2 /*-- IN THE WHOLE TREE */ +# define KN_MIP_ZEROHALF_ALL 3 /*-- DEPRECATED */ +#define KN_PARAM_MIP_MIR 2037 /*-- MIR CUTS */ +# define KN_MIP_MIR_AUTO -1 +# define KN_MIP_MIR_NONE 0 /*-- NONE */ +# define KN_MIP_MIR_ROOT 1 /*-- IN THE ROOT */ +# define KN_MIP_MIR_TREE 2 /*-- IN THE WHOLE TREE */ +# define KN_MIP_MIR_NLP 2 /*-- DEPRECATED*/ +#define KN_PARAM_MIP_CLIQUE 2038 /*-- CLIQUE CUTS */ +# define KN_MIP_CLIQUE_AUTO -1 +# define KN_MIP_CLIQUE_NONE 0 /*-- NONE */ +# define KN_MIP_CLIQUE_ROOT 1 /*-- IN THE ROOT */ +# define KN_MIP_CLIQUE_TREE 2 /*-- IN THE WHOLE TREE */ +# define KN_MIP_CLIQUE_ALL 3 /*-- DEPRECATED */ +#define KN_PARAM_MIP_HEUR_STRATEGY 2039 +# define KN_MIP_HEUR_STRATEGY_AUTO -1 +# define KN_MIP_HEUR_STRATEGY_NONE 0 +# define KN_MIP_HEUR_STRATEGY_BASIC 1 +# define KN_MIP_HEUR_STRATEGY_ADVANCED 2 +# define KN_MIP_HEUR_STRATEGY_EXTENSIVE 3 +#define KN_PARAM_MIP_HEUR_FEASPUMP 2040 +# define KN_MIP_HEUR_FEASPUMP_AUTO -1 +# define KN_MIP_HEUR_FEASPUMP_OFF 0 +# define KN_MIP_HEUR_FEASPUMP_ON 1 +#define KN_PARAM_MIP_HEUR_MPEC 2041 +# define KN_MIP_HEUR_MPEC_AUTO -1 +# define KN_MIP_HEUR_MPEC_OFF 0 +# define KN_MIP_HEUR_MPEC_ON 1 +#define KN_PARAM_MIP_HEUR_DIVING 2042 +#define KN_PARAM_MIP_CUTTINGPLANE 2043 /*-- CUTTING PLANE */ +# define KN_MIP_CUTTINGPLANE_NONE 0 /*-- NONE */ +# define KN_MIP_CUTTINGPLANE_ROOT 1 /*-- IN THE ROOT */ +#define KN_PARAM_MIP_CUTOFF 2044 +#define KN_PARAM_MIP_HEUR_LNS 2045 +#define KN_PARAM_MIP_MULTISTART 2046 +# define KN_MIP_MULTISTART_OFF 0 +# define KN_MIP_MULTISTART_ON 1 +#define KN_PARAM_MIP_LIFTPROJECT 2047 /*-- LIFT&PROJECT CUTS */ +# define KN_MIP_LIFTPROJECT_AUTO -1 +# define KN_MIP_LIFTPROJECT_NONE 0 /*-- NONE */ +# define KN_MIP_LIFTPROJECT_ROOT 1 /*-- IN THE ROOT */ +#define KN_PARAM_MIP_NUMTHREADS 2048 +#define KN_PARAM_MIP_HEUR_MISQP 2049 +# define KN_MIP_HEUR_MISQP_AUTO -1 +# define KN_MIP_HEUR_MISQP_OFF 0 +# define KN_MIP_HEUR_MISQP_ON 1 +#define KN_PARAM_MIP_RESTART 2050 +# define KN_MIP_RESTART_OFF 0 +# define KN_MIP_RESTART_ON 1 +#define KN_PARAM_MIP_GOMORY 2051 /*-- GOMORY CUTS */ +# define KN_MIP_GOMORY_AUTO -1 +# define KN_MIP_GOMORY_NONE 0 /*-- NONE */ +# define KN_MIP_GOMORY_ROOT 1 /*-- IN THE ROOT ONLY */ +# define KN_MIP_GOMORY_TREE 2 /*-- IN THE WHOLE TREE */ +#define KN_PARAM_MIP_CUT_PROBING 2052 /*-- PROBING CUTS */ +# define KN_MIP_CUT_PROBING_AUTO -1 +# define KN_MIP_CUT_PROBING_NONE 0 /*-- NONE */ +# define KN_MIP_CUT_PROBING_ROOT 1 /*-- IN THE ROOT ONLY */ +# define KN_MIP_CUT_PROBING_TREE 2 /*-- IN THE WHOLE TREE */ +#define KN_PARAM_MIP_CUT_FLOWCOVER 2053 /*-- FLOW COVER CUTS */ +# define KN_MIP_CUT_FLOWCOVER_AUTO -1 +# define KN_MIP_CUT_FLOWCOVER_NONE 0 /*-- NONE */ +# define KN_MIP_CUT_FLOWCOVER_ROOT 1 /*-- IN THE ROOT ONLY */ +# define KN_MIP_CUT_FLOWCOVER_TREE 2 /*-- IN THE WHOLE TREE */ +#define KN_PARAM_MIP_HEUR_LOCALSEARCH 2054 +# define KN_MIP_HEUR_LOCALSEARCH_AUTO -1 +# define KN_MIP_HEUR_LOCALSEARCH_OFF 0 +# define KN_MIP_HEUR_LOCALSEARCH_ON 1 +#define KN_PARAM_MIP_SUB_MAXTIME 2055 +#define KN_PARAM_MIP_INITPTFILE 2056 +#define KN_PARAM_MIP_ROOT_LPALG 2057 +# define KN_MIP_ROOT_LPALG_AUTO -1 +# define KN_MIP_ROOT_LPALG_NLPALGORITHM 0 +# define KN_MIP_ROOT_LPALG_PRIMALSIMPLEX 1 +# define KN_MIP_ROOT_LPALG_DUALSIMPLEX 2 +# define KN_MIP_ROOT_LPALG_BARRIER 3 +# define KN_MIP_ROOT_LPALG_PDLP 4 +#define KN_PARAM_MIP_NODE_LPALG 2058 +# define KN_MIP_NODE_LPALG_AUTO -1 +# define KN_MIP_NODE_LPALG_NLPALGORITHM 0 +# define KN_MIP_NODE_LPALG_PRIMALSIMPLEX 1 +# define KN_MIP_NODE_LPALG_DUALSIMPLEX 2 +# define KN_MIP_NODE_LPALG_BARRIER 3 +# define KN_MIP_NODE_LPALG_PDLP 4 +#define KN_PARAM_MIP_CUTOFFABS 2059 +#define KN_PARAM_MIP_CUTOFFREL 2060 +#define KN_PARAM_MIP_HEUR_FIXPROPAGATE 2061 +# define KN_MIP_HEUR_FIXPROPAGATE_AUTO -1 +# define KN_MIP_HEUR_FIXPROPAGATE_OFF 0 +# define KN_MIP_HEUR_FIXPROPAGATE_ON 1 + +/*-- THE BELOW ARE DEPRECATED! */ +#define KN_PARAM_PAR_NUMTHREADS 3001 /*-- USE KN_PARAM_NUMTHREADS */ +#define KN_PARAM_PAR_CONCURRENT_EVALS 3002 /*-- USE KN_PARAM_CONCURRENT_EVALS */ +# define KN_PAR_CONCURRENT_EVALS_NO 0 /*-- USE KN_CONCURRENT_EVALS_NO */ +# define KN_PAR_CONCURRENT_EVALS_YES 1 /*-- USE KN_CONCURRENT_EVALS_YES */ +#define KN_PARAM_PAR_BLASNUMTHREADS 3003 /*-- USE KN_PARAM_BLAS_NUMTHREADS */ +#define KN_PARAM_PAR_LSNUMTHREADS 3004 /*-- USE KN_PARAM_LINSOLVER_NUMTHREADS */ +#define KN_PARAM_PAR_MSNUMTHREADS 3005 /*-- USE KN_PARAM_MS_NUMTHREADS */ +# define KN_PAR_MSNUMTHREADS_AUTO 0 /*-- DEPRECATED */ +#define KN_PARAM_PAR_CONICNUMTHREADS 3006 /*-- USE KN_PARAM_CONIC_NUMTHREADS */ + +#ifdef __cplusplus +} +#endif + +#endif /*-- KNITRO_H__ */ From d762c2f68db54f463bc84131fb1f3972ac239500 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Fri, 6 Feb 2026 20:29:20 -0500 Subject: [PATCH 02/23] set initial values positive to avoid numeric errors. --- tests/test_nlp.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_nlp.py b/tests/test_nlp.py index 316a1f19..f31d9612 100644 --- a/tests/test_nlp.py +++ b/tests/test_nlp.py @@ -10,6 +10,8 @@ def test_easy_nlp(nlp_model_ctor): x = model.add_variable(lb=0.1, ub=10.0) y = model.add_variable(lb=0.1, ub=10.0) + model.set_variable_start(x, 0.5) + model.set_variable_start(y, 0.5) model.add_linear_constraint(x + y, poi.Eq, 1.0) @@ -101,6 +103,7 @@ def test_nlfunc_ifelse(nlp_model_ctor): assert x_value == pytest.approx(x_) +@pytest.mark.skipif(not ipopt.is_library_loaded(), reason="IPOPT library not available") def test_ipopt_optimizer_not_called(): model = ipopt.Model() From 9f838c195adbf43a742ed4566821c2fb9dc508ef Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Fri, 6 Feb 2026 20:29:32 -0500 Subject: [PATCH 03/23] refine assertions in test_nlp_reopt for numerical stability --- tests/test_nlp_multiple_run.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_nlp_multiple_run.py b/tests/test_nlp_multiple_run.py index b5394f2f..86b5bace 100644 --- a/tests/test_nlp_multiple_run.py +++ b/tests/test_nlp_multiple_run.py @@ -18,8 +18,8 @@ def test_nlp_reopt(nlp_model_ctor): model.optimize() - assert model.get_value(x) == pytest.approx(0.1) - assert model.get_value(y) == pytest.approx(0.1) + assert model.get_value(x) == pytest.approx(0.1, rel=1e-5) + assert model.get_value(y) == pytest.approx(0.1, rel=1e-5) z = model.add_variable(lb=0.2) with nl.graph(): From 4cc48d648880e50f81e98f30bd0453211701470d Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Sat, 7 Feb 2026 18:20:11 -0500 Subject: [PATCH 04/23] refactor KNITRO. --- include/pyoptinterface/knitro_model.hpp | 75 ++-- lib/knitro_model.cpp | 521 ++++++++++++++---------- lib/knitro_model_ext.cpp | 135 +++--- src/pyoptinterface/_src/knitro.py | 12 +- 4 files changed, 427 insertions(+), 316 deletions(-) diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp index c791cba3..a6f5eec9 100644 --- a/include/pyoptinterface/knitro_model.hpp +++ b/include/pyoptinterface/knitro_model.hpp @@ -102,7 +102,7 @@ bool is_library_loaded(); bool load_library(const std::string &path); } // namespace knitro -struct KnitroFreeProblemT +struct KNITROFreeProblemT { void operator()(KN_context *kc) const { @@ -113,7 +113,7 @@ struct KnitroFreeProblemT } }; -struct KnitroResult +struct KNITROResult { bool is_valid = false; int status = 0; @@ -138,28 +138,45 @@ enum ConstraintSenseFlags CON_UPBND = 1 << 1, // 0x02 }; -struct NLCallbackData +struct KNITROCallbackLoad { - size_t idx; - CppAD::ADFun function; std::vector indexVars; + std::vector indexCons; + + CppAD::ADFun fun; + + std::vector fun_rows; std::vector> jac_pattern; std::vector jac_rows; std::vector jac_cols; CppAD::sparse_jacobian_work jac_work; + std::vector> hess_pattern; + std::vector hess_rows; + std::vector hess_cols; + CppAD::sparse_hessian_work hess_work; + + std::vector x; + std::vector jac; + std::vector hess; +}; + +struct KNITROGraph +{ + std::vector m_objs; + std::unordered_map m_cons; }; -class KnitroModel : public OnesideLinearConstraintMixin, - public TwosideLinearConstraintMixin, - public OnesideQuadraticConstraintMixin, - public TwosideQuadraticConstraintMixin, - public TwosideNLConstraintMixin, - public LinearObjectiveMixin, - public PPrintMixin, - public GetValueMixin +class KNITROModel : public OnesideLinearConstraintMixin, + public TwosideLinearConstraintMixin, + public OnesideQuadraticConstraintMixin, + public TwosideQuadraticConstraintMixin, + public TwosideNLConstraintMixin, + public LinearObjectiveMixin, + public PPrintMixin, + public GetValueMixin { public: - KnitroModel(); + KNITROModel(); void init(); void close(); @@ -202,6 +219,10 @@ class KnitroModel : public OnesideLinearConstraintMixin, ConstraintIndex add_single_nl_constraint(ExpressionGraph &graph, const ExpressionHandle &result, const std::tuple &interval, const char *name = nullptr); + ConstraintIndex add_single_nl_constraint_sense_rhs(ExpressionGraph &graph, + const ExpressionHandle &result, + ConstraintSense sense, double rhs, + const char *name = nullptr); void delete_constraint(ConstraintIndex constraint); void set_constraint_name(const ConstraintIndex &constraint, const std::string &name); @@ -215,7 +236,6 @@ class KnitroModel : public OnesideLinearConstraintMixin, void set_normalized_coefficient(const ConstraintIndex &constraint, const VariableIndex &variable, double coefficient); - // Objective functions void set_objective(const ScalarAffineFunction &f, ObjectiveSense sense); void set_objective(const ScalarQuadraticFunction &f, ObjectiveSense sense); void set_objective(const ExprBuilder &expr, ObjectiveSense sense); @@ -223,7 +243,6 @@ class KnitroModel : public OnesideLinearConstraintMixin, double get_obj_value(); void optimize(); - void _optimize(); template void set_raw_parameter(const std::string &name, T value) @@ -301,27 +320,25 @@ class KnitroModel : public OnesideLinearConstraintMixin, void check_error(int error) const; - std::unique_ptr m_kc = nullptr; + std::unique_ptr m_kc = nullptr; - // Basic model statistics size_t n_vars = 0; size_t n_cons = 0; size_t n_lincons = 0; size_t n_quadcons = 0; - size_t n_soccons = 0; + size_t n_coniccons = 0; + size_t n_nlcons = 0; - // Auxiliary data for constraints and objectives std::unordered_map>> m_aux_cons; std::unordered_map m_con_sense_flags; uint8_t m_obj_flag = 0; - // Store NL functions to keep them alive for callbacks - // Use unique_ptr for pointer stability (unordered_map can rehash and invalidate references) - std::unordered_map> m_con_nl_data_map; - std::vector> m_obj_nl_data; + std::unordered_map m_graphs; + std::vector> m_loads; + bool m_need_to_add_callbacks = false; // Solution and solve status - KnitroResult m_result; + KNITROResult m_result; bool m_is_dirty = true; int m_solve_status = 0; @@ -369,11 +386,15 @@ class KnitroModel : public OnesideLinearConstraintMixin, const Vector &variables); void _set_second_order_cone_constraint_rotated(const ConstraintIndex &constraint, const Vector &variables); - void _set_nonlinear_constraint(const ConstraintIndex &constraint, ExpressionGraph &graph); void _set_linear_objective(const ScalarAffineFunction &f); void _set_quadratic_objective(const ScalarQuadraticFunction &f); - void _add_nonlinear_objective(ExpressionGraph &graph); void _reset_objective(); + void _add_graph(ExpressionGraph &graph); + void _add_callbacks(); + void _update(); + void _pre_solve(); + void _solve(); + void _post_solve(); template ConstraintIndex _add_constraint_impl(ConstraintType type, diff --git a/lib/knitro_model.cpp b/lib/knitro_model.cpp index 8c20411b..049f804b 100644 --- a/lib/knitro_model.cpp +++ b/lib/knitro_model.cpp @@ -50,12 +50,12 @@ bool load_library(const std::string &path) } } // namespace knitro -KnitroModel::KnitroModel() +KNITROModel::KNITROModel() { init(); } -void KnitroModel::init() +void KNITROModel::init() { if (!knitro::is_library_loaded()) { @@ -66,15 +66,15 @@ void KnitroModel::init() int error = knitro::KN_new(&kc_ptr); check_error(error); - m_kc = std::unique_ptr(kc_ptr); + m_kc = std::unique_ptr(kc_ptr); } -void KnitroModel::close() +void KNITROModel::close() { m_kc.reset(); } -void KnitroModel::check_error(int error) const +void KNITROModel::check_error(int error) const { if (error != 0) { @@ -82,12 +82,12 @@ void KnitroModel::check_error(int error) const } } -double KnitroModel::get_infinity() const +double KNITROModel::get_infinity() const { return KN_INFINITY; } -VariableIndex KnitroModel::add_variable(VariableDomain domain, double lb, double ub, +VariableIndex KNITROModel::add_variable(VariableDomain domain, double lb, double ub, const char *name) { KNINT indexVar; @@ -123,7 +123,7 @@ VariableIndex KnitroModel::add_variable(VariableDomain domain, double lb, double return variable; } -double KnitroModel::get_variable_lb(const VariableIndex &variable) +double KNITROModel::get_variable_lb(const VariableIndex &variable) { KNINT indexVar = _variable_index(variable); int error; @@ -134,7 +134,7 @@ double KnitroModel::get_variable_lb(const VariableIndex &variable) return lb; } -double KnitroModel::get_variable_ub(const VariableIndex &variable) +double KNITROModel::get_variable_ub(const VariableIndex &variable) { KNINT indexVar = _variable_index(variable); int error; @@ -145,7 +145,7 @@ double KnitroModel::get_variable_ub(const VariableIndex &variable) return ub; } -void KnitroModel::set_variable_lb(const VariableIndex &variable, double lb) +void KNITROModel::set_variable_lb(const VariableIndex &variable, double lb) { KNINT indexVar = _variable_index(variable); int error = knitro::KN_set_var_lobnd(m_kc.get(), indexVar, lb); @@ -153,7 +153,7 @@ void KnitroModel::set_variable_lb(const VariableIndex &variable, double lb) m_is_dirty = true; } -void KnitroModel::set_variable_ub(const VariableIndex &variable, double ub) +void KNITROModel::set_variable_ub(const VariableIndex &variable, double ub) { KNLONG indexVar = _variable_index(variable); int error = knitro::KN_set_var_upbnd(m_kc.get(), indexVar, ub); @@ -161,13 +161,13 @@ void KnitroModel::set_variable_ub(const VariableIndex &variable, double ub) m_is_dirty = true; } -void KnitroModel::set_variable_bounds(const VariableIndex &variable, double lb, double ub) +void KNITROModel::set_variable_bounds(const VariableIndex &variable, double lb, double ub) { set_variable_lb(variable, lb); set_variable_ub(variable, ub); } -double KnitroModel::get_variable_value(const VariableIndex &variable) +double KNITROModel::get_variable_value(const VariableIndex &variable) { if (!m_result.is_valid) { @@ -176,26 +176,26 @@ double KnitroModel::get_variable_value(const VariableIndex &variable) return m_result.x[_variable_index(variable)]; } -void KnitroModel::set_variable_start(const VariableIndex &variable, double start) +void KNITROModel::set_variable_start(const VariableIndex &variable, double start) { KNINT indexVar = _variable_index(variable); int error = knitro::KN_set_var_primal_init_value(m_kc.get(), indexVar, start); check_error(error); } -std::string KnitroModel::get_variable_name(const VariableIndex &variable) +std::string KNITROModel::get_variable_name(const VariableIndex &variable) { KNINT indexVar = _variable_index(variable); return _get_name(indexVar, knitro::KN_get_var_name, "x"); } -void KnitroModel::set_variable_name(const VariableIndex &variable, const std::string &name) +void KNITROModel::set_variable_name(const VariableIndex &variable, const std::string &name) { KNINT indexVar = _variable_index(variable); _set_name(indexVar, name, knitro::KN_set_var_name); } -void KnitroModel::set_variable_domain(const VariableIndex &variable, VariableDomain domain) +void KNITROModel::set_variable_domain(const VariableIndex &variable, VariableDomain domain) { KNINT indexVar = _variable_index(variable); int var_type = knitro_var_type(domain); @@ -227,7 +227,7 @@ void KnitroModel::set_variable_domain(const VariableIndex &variable, VariableDom m_is_dirty = true; } -double KnitroModel::get_variable_rc(const VariableIndex &variable) +double KNITROModel::get_variable_rc(const VariableIndex &variable) { if (!m_result.is_valid) { @@ -237,7 +237,7 @@ double KnitroModel::get_variable_rc(const VariableIndex &variable) return m_result.lambda[indexVar]; } -void KnitroModel::delete_variable(const VariableIndex &variable) +void KNITROModel::delete_variable(const VariableIndex &variable) { int indexVar = _variable_index(variable); int error; @@ -253,22 +253,22 @@ void KnitroModel::delete_variable(const VariableIndex &variable) m_is_dirty = true; } -std::string KnitroModel::pprint_variable(const VariableIndex &variable) +std::string KNITROModel::pprint_variable(const VariableIndex &variable) { return get_variable_name(variable); } -KNINT KnitroModel::_variable_index(const VariableIndex &variable) +KNINT KNITROModel::_variable_index(const VariableIndex &variable) { return variable.index; } -KNINT KnitroModel::_constraint_index(const ConstraintIndex &constraint) +KNINT KNITROModel::_constraint_index(const ConstraintIndex &constraint) { return constraint.index; } -ConstraintIndex KnitroModel::add_linear_constraint(const ScalarAffineFunction &function, +ConstraintIndex KNITROModel::add_linear_constraint(const ScalarAffineFunction &function, ConstraintSense sense, double rhs, const char *name) { @@ -277,7 +277,7 @@ ConstraintIndex KnitroModel::add_linear_constraint(const ScalarAffineFunction &f return _add_constraint_with_sense(function, sense, rhs, name, add); } -ConstraintIndex KnitroModel::add_linear_constraint(const ScalarAffineFunction &function, +ConstraintIndex KNITROModel::add_linear_constraint(const ScalarAffineFunction &function, const std::tuple &interval, const char *name) { @@ -287,7 +287,7 @@ ConstraintIndex KnitroModel::add_linear_constraint(const ScalarAffineFunction &f return _add_constraint_impl(ConstraintType::Linear, interval, name, &n_lincons, setter); } -ConstraintIndex KnitroModel::add_quadratic_constraint(const ScalarQuadraticFunction &function, +ConstraintIndex KNITROModel::add_quadratic_constraint(const ScalarQuadraticFunction &function, ConstraintSense sense, double rhs, const char *name) { @@ -296,7 +296,7 @@ ConstraintIndex KnitroModel::add_quadratic_constraint(const ScalarQuadraticFunct return _add_constraint_with_sense(function, sense, rhs, name, add); } -ConstraintIndex KnitroModel::add_quadratic_constraint(const ScalarQuadraticFunction &function, +ConstraintIndex KNITROModel::add_quadratic_constraint(const ScalarQuadraticFunction &function, const std::tuple &interval, const char *name) { @@ -306,7 +306,7 @@ ConstraintIndex KnitroModel::add_quadratic_constraint(const ScalarQuadraticFunct return _add_constraint_impl(ConstraintType::Quadratic, interval, name, &n_quadcons, setter); } -ConstraintIndex KnitroModel::add_second_order_cone_constraint( +ConstraintIndex KNITROModel::add_second_order_cone_constraint( const Vector &variables, const char *name, bool rotated) { @@ -316,7 +316,7 @@ ConstraintIndex KnitroModel::add_second_order_cone_constraint( _set_second_order_cone_constraint_rotated(constraint, variables); }; std::pair interval = {0.0, KN_INFINITY}; - return _add_constraint_impl(ConstraintType::Cone, interval, name, nullptr, setter); + return _add_constraint_impl(ConstraintType::Cone, interval, name, &n_coniccons, setter); } else { @@ -324,24 +324,38 @@ ConstraintIndex KnitroModel::add_second_order_cone_constraint( _set_second_order_cone_constraint(constraint, variables); }; std::pair interval = {0.0, KN_INFINITY}; - return _add_constraint_impl(ConstraintType::Cone, interval, name, nullptr, setter); + return _add_constraint_impl(ConstraintType::Cone, interval, name, &n_coniccons, setter); } } -ConstraintIndex KnitroModel::add_single_nl_constraint(ExpressionGraph &graph, - const ExpressionHandle &result, - const std::tuple &interval, - const char *name) +ConstraintIndex KNITROModel::add_single_nl_constraint(ExpressionGraph &graph, + const ExpressionHandle &result, + const std::tuple &interval, + const char *name) { + _add_graph(graph); graph.add_constraint_output(result); auto setter = [this, &graph](const ConstraintIndex &constraint) { - _set_nonlinear_constraint(constraint, graph); + size_t i = graph.m_constraint_outputs.size() - 1; + m_graphs[&graph].m_cons[i] = constraint; + m_need_to_add_callbacks = true; }; + return _add_constraint_impl(ConstraintType::KNITRO_NL, interval, name, &n_nlcons, setter); +} - return _add_constraint_impl(ConstraintType::KNITRO_NL, interval, name, nullptr, setter); +ConstraintIndex KNITROModel::add_single_nl_constraint_sense_rhs(ExpressionGraph &graph, + const ExpressionHandle &result, + ConstraintSense sense, double rhs, + const char *name) +{ + auto add = [this, &graph, &result](void *, const std::tuple &interval, + const char *n) { + return add_single_nl_constraint(graph, result, interval, n); + }; + return _add_constraint_with_sense(nullptr, sense, rhs, name, add); } -std::tuple KnitroModel::_sense_to_interval(ConstraintSense sense, double rhs) +std::tuple KNITROModel::_sense_to_interval(ConstraintSense sense, double rhs) { switch (sense) { @@ -356,7 +370,7 @@ std::tuple KnitroModel::_sense_to_interval(ConstraintSense sense } } -void KnitroModel::_update_con_sense_flags(const ConstraintIndex &constraint, ConstraintSense sense) +void KNITROModel::_update_con_sense_flags(const ConstraintIndex &constraint, ConstraintSense sense) { KNINT indexCon = _constraint_index(constraint); switch (sense) @@ -372,7 +386,7 @@ void KnitroModel::_update_con_sense_flags(const ConstraintIndex &constraint, Con } } -void KnitroModel::_set_second_order_cone_constraint(const ConstraintIndex &constraint, +void KNITROModel::_set_second_order_cone_constraint(const ConstraintIndex &constraint, const Vector &variables) { KNINT indexCon = _constraint_index(constraint); @@ -407,7 +421,7 @@ void KnitroModel::_set_second_order_cone_constraint(const ConstraintIndex &const m_aux_cons[indexCon] = indexCon0; } -void KnitroModel::_set_second_order_cone_constraint_rotated(const ConstraintIndex &constraint, +void KNITROModel::_set_second_order_cone_constraint_rotated(const ConstraintIndex &constraint, const Vector &variables) { KNINT indexCon = _constraint_index(constraint); @@ -451,87 +465,7 @@ void KnitroModel::_set_second_order_cone_constraint_rotated(const ConstraintInde m_aux_cons[indexCon] = std::make_pair(indexCon0, indexCon1); } -void KnitroModel::_set_nonlinear_constraint(const ConstraintIndex &constraint, ExpressionGraph &graph) -{ - KNINT indexCon = _constraint_index(constraint); - int error; - - auto& cb_data_ptr = m_con_nl_data_map[indexCon]; - cb_data_ptr = std::make_unique(); - NLCallbackData* cb_data = cb_data_ptr.get(); - - cb_data->idx = graph.m_constraint_outputs.size() - 1; - cb_data->function = cppad_trace_graph_constraints(graph); - cb_data->function.optimize(); - - size_t m = cb_data->function.Range(); - std::vector> jac_sparsity(m); - jac_sparsity[cb_data->idx].insert(cb_data->idx); - cb_data->jac_pattern = cb_data->function.RevSparseJac(m, jac_sparsity); - for (size_t j : cb_data->jac_pattern[cb_data->idx]) - { - cb_data->jac_rows.push_back(cb_data->idx); - cb_data->jac_cols.push_back(j); - } - - cb_data->indexVars.resize(graph.n_variables()); - for (size_t i = 0; i < graph.m_variables.size(); i++) - { - cb_data->indexVars[i] = _variable_index(graph.m_variables[i]); - } - - CB_context* cb = nullptr; - auto cb_eval = [](KN_context*, CB_context*, KN_eval_request* req, KN_eval_result* res, void* data) -> int { - NLCallbackData *cb_data = static_cast(data); - CppAD::ADFun &fun = cb_data->function; - const std::vector &indexVars = cb_data->indexVars; - - std::vector x(indexVars.size()); - for (size_t i = 0; i < indexVars.size(); i++) - { - x[i] = req->x[indexVars[i]]; - } - auto y = fun.Forward(0, x); - res->c[0] = y[cb_data->idx]; - return 0; - }; - - auto cb_grad = [](KN_context*, CB_context*, KN_eval_request* req, KN_eval_result* res, void* data) -> int { - NLCallbackData *cb_data = static_cast(data); - CppAD::ADFun &fun = cb_data->function; - const std::vector &indexVars = cb_data->indexVars; - std::vector x(indexVars.size()); - for (size_t i = 0; i < indexVars.size(); i++) - { - x[i] = req->x[indexVars[i]]; - } - std::vector jac(cb_data->jac_pattern[cb_data->idx].size()); - fun.SparseJacobianReverse(x, cb_data->jac_pattern, cb_data->jac_rows, cb_data->jac_cols, jac, cb_data->jac_work); - for (size_t i = 0; i < jac.size(); i++) - { - res->jac[i] = jac[i]; - } - return 0; - }; - - error = knitro::KN_add_eval_callback(m_kc.get(), FALSE, 1, &indexCon, cb_eval, &cb); - check_error(error); - - error = knitro::KN_set_cb_user_params(m_kc.get(), cb, cb_data); - check_error(error); - - std::vector jacIndexCons(cb_data->jac_rows.size(), indexCon); - std::vector jacIndexVars(cb_data->jac_cols.size()); - for (size_t i = 0; i < cb_data->jac_cols.size(); i++) - { - jacIndexVars[i] = _variable_index(graph.m_variables[cb_data->jac_cols[i]]); - } - error = knitro::KN_set_cb_grad(m_kc.get(), cb, 0, NULL, jacIndexCons.size(), - jacIndexCons.data(), jacIndexVars.data(), cb_grad); - check_error(error); -} - -void KnitroModel::delete_constraint(ConstraintIndex constraint) +void KNITROModel::delete_constraint(ConstraintIndex constraint) { KNINT indexCon = _constraint_index(constraint); int error; @@ -550,6 +484,12 @@ void KnitroModel::delete_constraint(ConstraintIndex constraint) case ConstraintType::Quadratic: n_quadcons--; break; + case ConstraintType::Cone: + n_coniccons--; + break; + case ConstraintType::KNITRO_NL: + n_nlcons--; + break; default: break; } @@ -583,19 +523,19 @@ void KnitroModel::delete_constraint(ConstraintIndex constraint) m_is_dirty = true; } -void KnitroModel::set_constraint_name(const ConstraintIndex &constraint, const std::string &name) +void KNITROModel::set_constraint_name(const ConstraintIndex &constraint, const std::string &name) { KNINT indexCon = _constraint_index(constraint); _set_name(indexCon, name, knitro::KN_set_con_name); } -std::string KnitroModel::get_constraint_name(const ConstraintIndex &constraint) +std::string KNITROModel::get_constraint_name(const ConstraintIndex &constraint) { KNINT indexCon = _constraint_index(constraint); return _get_name(indexCon, knitro::KN_get_con_name, "c"); } -double KnitroModel::get_constraint_primal(const ConstraintIndex &constraint) +double KNITROModel::get_constraint_primal(const ConstraintIndex &constraint) { if (!m_result.is_valid) { @@ -606,7 +546,7 @@ double KnitroModel::get_constraint_primal(const ConstraintIndex &constraint) return m_result.con_values[indexCon]; } -double KnitroModel::get_constraint_dual(const ConstraintIndex &constraint) +double KNITROModel::get_constraint_dual(const ConstraintIndex &constraint) { if (!m_result.is_valid) { @@ -617,7 +557,7 @@ double KnitroModel::get_constraint_dual(const ConstraintIndex &constraint) return m_result.con_duals[indexCon]; } -void KnitroModel::set_normalized_rhs(const ConstraintIndex &constraint, double rhs) +void KNITROModel::set_normalized_rhs(const ConstraintIndex &constraint, double rhs) { KNINT indexCon = _constraint_index(constraint); auto flag = m_con_sense_flags[indexCon]; @@ -639,7 +579,7 @@ void KnitroModel::set_normalized_rhs(const ConstraintIndex &constraint, double r m_is_dirty = true; } -double KnitroModel::get_normalized_rhs(const ConstraintIndex &constraint) +double KNITROModel::get_normalized_rhs(const ConstraintIndex &constraint) { KNINT indexCon = _constraint_index(constraint); auto flag = m_con_sense_flags[indexCon]; @@ -658,21 +598,22 @@ double KnitroModel::get_normalized_rhs(const ConstraintIndex &constraint) return rhs; } -void KnitroModel::set_normalized_coefficient(const ConstraintIndex &constraint, +void KNITROModel::set_normalized_coefficient(const ConstraintIndex &constraint, const VariableIndex &variable, double coefficient) { KNINT indexCon = _constraint_index(constraint); KNINT indexVar = _variable_index(variable); int error; - error = knitro::KN_update(m_kc.get()); - check_error(error); + // NOTE: To make sure the coefficient is updated correctly, + // we need to call KN_update before changing the linear term + _update(); error = knitro::KN_chg_con_linear_term(m_kc.get(), indexCon, indexVar, coefficient); check_error(error); m_is_dirty = true; } -void KnitroModel::_set_linear_constraint(const ConstraintIndex &constraint, +void KNITROModel::_set_linear_constraint(const ConstraintIndex &constraint, const ScalarAffineFunction &function) { KNINT indexCon = _constraint_index(constraint); @@ -702,7 +643,7 @@ void KnitroModel::_set_linear_constraint(const ConstraintIndex &constraint, } } -void KnitroModel::_set_quadratic_constraint(const ConstraintIndex &constraint, +void KNITROModel::_set_quadratic_constraint(const ConstraintIndex &constraint, const ScalarQuadraticFunction &function) { KNINT indexCon = _constraint_index(constraint); @@ -727,17 +668,17 @@ void KnitroModel::_set_quadratic_constraint(const ConstraintIndex &constraint, } } -void KnitroModel::set_objective(const ScalarAffineFunction &function, ObjectiveSense sense) +void KNITROModel::set_objective(const ScalarAffineFunction &function, ObjectiveSense sense) { _set_objective_impl(sense, [this, &function]() { _set_linear_objective(function); }); } -void KnitroModel::set_objective(const ScalarQuadraticFunction &function, ObjectiveSense sense) +void KNITROModel::set_objective(const ScalarQuadraticFunction &function, ObjectiveSense sense) { _set_objective_impl(sense, [this, &function]() { _set_quadratic_objective(function); }); } -void KnitroModel::set_objective(const ExprBuilder &expr, ObjectiveSense sense) +void KNITROModel::set_objective(const ExprBuilder &expr, ObjectiveSense sense) { auto degree = expr.degree(); if (degree <= 1) @@ -756,15 +697,27 @@ void KnitroModel::set_objective(const ExprBuilder &expr, ObjectiveSense sense) } } -void KnitroModel::add_single_nl_objective(ExpressionGraph &graph, const ExpressionHandle &result) +void KNITROModel::add_single_nl_objective(ExpressionGraph &graph, const ExpressionHandle &result) { + _add_graph(graph); graph.add_objective_output(result); - _add_nonlinear_objective(graph); + size_t i = graph.m_objective_outputs.size() - 1; + m_graphs[&graph].m_objs.push_back(i); + m_need_to_add_callbacks = true; + m_obj_flag |= OBJ_NONLINEAR; m_is_dirty = true; m_result.is_valid = false; } -void KnitroModel::_reset_objective() +void KNITROModel::_add_graph(ExpressionGraph &graph) +{ + if (m_graphs.find(&graph) == m_graphs.end()) + { + m_graphs[&graph] = KNITROGraph(); + } +} + +void KNITROModel::_reset_objective() { int error; @@ -791,11 +744,10 @@ void KnitroModel::_reset_objective() m_obj_flag = 0; - error = knitro::KN_update(m_kc.get()); - check_error(error); + _update(); } -void KnitroModel::_set_linear_objective(const ScalarAffineFunction &function) +void KNITROModel::_set_linear_objective(const ScalarAffineFunction &function) { int error; @@ -823,7 +775,7 @@ void KnitroModel::_set_linear_objective(const ScalarAffineFunction &function) } } -void KnitroModel::_set_quadratic_objective(const ScalarQuadraticFunction &function) +void KNITROModel::_set_quadratic_objective(const ScalarQuadraticFunction &function) { int error; @@ -846,105 +798,239 @@ void KnitroModel::_set_quadratic_objective(const ScalarQuadraticFunction &functi } } -void KnitroModel::_add_nonlinear_objective(ExpressionGraph &graph) +double KNITROModel::get_obj_value() { - int error; - auto cb_data_ptr = std::make_unique(); - m_obj_nl_data.push_back(std::move(cb_data_ptr)); - NLCallbackData* cb_data = m_obj_nl_data.back().get(); - - cb_data->idx = graph.m_objective_outputs.size() - 1; - cb_data->function = cppad_trace_graph_objective(graph); - cb_data->function.optimize(); - - size_t m = cb_data->function.Range(); - std::vector> jac_sparsity(m); - jac_sparsity[cb_data->idx].insert(cb_data->idx); - cb_data->jac_pattern = cb_data->function.RevSparseJac(m, jac_sparsity); - for (size_t j : cb_data->jac_pattern[cb_data->idx]) + if (!m_result.is_valid) { - cb_data->jac_rows.push_back(cb_data->idx); - cb_data->jac_cols.push_back(j); + throw std::runtime_error("No solution available"); } + return m_result.obj_val; +} - cb_data->indexVars.resize(graph.n_variables()); - for (size_t i = 0; i < graph.m_variables.size(); i++) +void KNITROModel::_add_callbacks() +{ + if (!m_need_to_add_callbacks) { - cb_data->indexVars[i] = _variable_index(graph.m_variables[i]); + return; } - CB_context* cb = nullptr; - auto cb_eval = [](KN_context*, CB_context*, KN_eval_request* req, KN_eval_result* res, void* data) -> int { - NLCallbackData *cb_data = static_cast(data); - CppAD::ADFun &fun = cb_data->function; - const std::vector &indexVars = cb_data->indexVars; - - std::vector x(indexVars.size()); - for (size_t i = 0; i < indexVars.size(); i++) + for (const auto &[graph, pending] : m_graphs) + { + if (graph->has_constraint_output() && !pending.m_cons.empty()) { - x[i] = req->x[indexVars[i]]; + auto f = cppad_trace_graph_constraints(*graph); + f.optimize(); + + size_t n = f.Domain(); + size_t m = pending.m_cons.size(); + auto load_ptr = std::make_unique(); + KNITROCallbackLoad *load = load_ptr.get(); + load->fun = f; + load->indexVars.resize(n); + load->indexCons.resize(m); + load->fun_rows.resize(m); + for (size_t i = 0; i < n; i++) + { + load->indexVars[i] = _variable_index(graph->m_variables[i]); + } + auto it = pending.m_cons.begin(); + for (size_t k = 0; k < m; k++, it++) + { + auto &[j, constraint] = *it; + load->fun_rows[k] = j; + load->indexCons[k] = _constraint_index(constraint); + } + std::vector> jac_sparsity(m); + for (size_t k = 0; k < m; k++) + { + jac_sparsity[k].insert(load->fun_rows[k]); + } + load->jac_pattern = load->fun.RevSparseJac(jac_sparsity.size(), jac_sparsity); + for (size_t k = 0; k < load->jac_pattern.size(); k++) + { + for (size_t i : load->jac_pattern[k]) + { + load->jac_rows.push_back(load->fun_rows[k]); + load->jac_cols.push_back(i); + } + } + std::vector jacIndexCons(load->jac_rows.size()); + std::vector jacIndexVars(load->jac_cols.size()); + size_t idx = 0; + for (size_t k = 0; k < load->jac_pattern.size(); k++) + { + for (size_t i : load->jac_pattern[k]) + { + jacIndexCons[idx] = load->indexCons[k]; + jacIndexVars[idx] = load->indexVars[i]; + idx++; + } + } + load->x.resize(n); + load->jac.resize(load->jac_cols.size()); + auto cb_eval = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, + void *data) -> int { + KNITROCallbackLoad *load = static_cast(data); + for (size_t i = 0; i < load->indexVars.size(); i++) + { + load->x[i] = req->x[load->indexVars[i]]; + } + auto y = load->fun.Forward(0, load->x); + for (size_t k = 0; k < load->fun_rows.size(); k++) + { + res->c[k] = y[load->fun_rows[k]]; + } + return 0; + }; + auto cb_grad = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, + void *data) -> int { + KNITROCallbackLoad *load = static_cast(data); + for (size_t i = 0; i < load->indexVars.size(); i++) + { + load->x[i] = req->x[load->indexVars[i]]; + } + load->fun.SparseJacobianReverse(load->x, load->jac_pattern, load->jac_rows, + load->jac_cols, load->jac, load->jac_work); + for (size_t i = 0; i < load->jac.size(); i++) + { + res->jac[i] = load->jac[i]; + } + return 0; + }; + CB_context *cb = nullptr; + int error; + error = knitro::KN_add_eval_callback(m_kc.get(), FALSE, load->indexCons.size(), + load->indexCons.data(), cb_eval, &cb); + check_error(error); + error = knitro::KN_set_cb_user_params(m_kc.get(), cb, load); + check_error(error); + error = knitro::KN_set_cb_grad(m_kc.get(), cb, 0, NULL, jacIndexCons.size(), + jacIndexCons.data(), jacIndexVars.data(), cb_grad); + check_error(error); + m_loads.push_back(std::move(load_ptr)); } - *res->obj = fun.Forward(0, x)[cb_data->idx]; - return 0; - }; - auto cb_grad = [](KN_context*, CB_context*, KN_eval_request* req, KN_eval_result* res, void* data) -> int { - NLCallbackData *cb_data = static_cast(data); - CppAD::ADFun &fun = cb_data->function; - const std::vector &indexVars = cb_data->indexVars; - std::vector x(indexVars.size()); - for (size_t i = 0; i < indexVars.size(); i++) + if (graph->has_objective_output() && !pending.m_objs.empty()) { - x[i] = req->x[indexVars[i]]; - } - std::vector jac(cb_data->jac_pattern[cb_data->idx].size()); - fun.SparseJacobianReverse(x, cb_data->jac_pattern, cb_data->jac_rows, cb_data->jac_cols, jac, cb_data->jac_work); - for (size_t i = 0; i < jac.size(); i++) - { - res->objGrad[i] = jac[i]; + auto f = cppad_trace_graph_objective(*graph); + f.optimize(); + + size_t n = f.Domain(); + size_t m = pending.m_objs.size(); + auto load_ptr = std::make_unique(); + KNITROCallbackLoad *load = load_ptr.get(); + load->fun = f; + load->indexVars.resize(n); + load->indexCons.resize(0); + load->fun_rows.resize(m); + for (size_t i = 0; i < n; i++) + { + load->indexVars[i] = _variable_index(graph->m_variables[i]); + } + auto it = pending.m_objs.begin(); + for (size_t k = 0; k < m; k++, it++) + { + auto &j = *it; + load->fun_rows[k] = j; + } + std::vector> jac_sparsity(m); + for (size_t k = 0; k < m; k++) + { + jac_sparsity[k].insert(load->fun_rows[k]); + } + load->jac_pattern = load->fun.RevSparseJac(jac_sparsity.size(), jac_sparsity); + for (size_t k = 0; k < load->jac_pattern.size(); k++) + { + for (size_t i : load->jac_pattern[k]) + { + load->jac_rows.push_back(load->fun_rows[k]); + load->jac_cols.push_back(i); + } + } + std::vector objGradIndexVars(load->jac_cols.size()); + size_t idx = 0; + for (size_t k = 0; k < load->jac_pattern.size(); k++) + { + for (size_t i : load->jac_pattern[k]) + { + objGradIndexVars[idx] = load->indexVars[i]; + idx++; + } + } + load->x.resize(n); + load->jac.resize(load->jac_cols.size()); + auto cb_eval = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, + void *data) -> int { + KNITROCallbackLoad *load = static_cast(data); + for (size_t i = 0; i < load->indexVars.size(); i++) + { + load->x[i] = req->x[load->indexVars[i]]; + } + auto y = load->fun.Forward(0, load->x); + res->obj[0] = 0.0; + for (size_t k = 0; k < load->fun_rows.size(); k++) + { + size_t j = load->fun_rows[k]; + res->obj[0] += y[j]; + } + return 0; + }; + auto cb_grad = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, + void *data) -> int { + KNITROCallbackLoad *load = static_cast(data); + for (size_t i = 0; i < load->indexVars.size(); i++) + { + load->x[i] = req->x[load->indexVars[i]]; + } + load->fun.SparseJacobianReverse(load->x, load->jac_pattern, load->jac_rows, + load->jac_cols, load->jac, load->jac_work); + for (size_t i = 0; i < load->jac.size(); i++) + { + res->objGrad[i] = load->jac[i]; + } + return 0; + }; + + CB_context *cb = nullptr; + int error; + error = knitro::KN_add_eval_callback(m_kc.get(), TRUE, 0, NULL, cb_eval, &cb); + check_error(error); + error = knitro::KN_set_cb_user_params(m_kc.get(), cb, load); + check_error(error); + error = knitro::KN_set_cb_grad(m_kc.get(), cb, objGradIndexVars.size(), + objGradIndexVars.data(), 0, NULL, NULL, cb_grad); + check_error(error); + m_loads.push_back(std::move(load_ptr)); } - return 0; - }; - - error = knitro::KN_add_eval_callback(m_kc.get(), TRUE, 0, NULL, cb_eval, &cb); - check_error(error); - - error = knitro::KN_set_cb_user_params(m_kc.get(), cb, cb_data); - check_error(error); - - std::vector objGradIndexVars(cb_data->jac_cols.size()); - for (size_t i = 0; i < cb_data->jac_cols.size(); i++) - { - objGradIndexVars[i] = _variable_index(graph.m_variables[cb_data->jac_cols[i]]); } - error = knitro::KN_set_cb_grad(m_kc.get(), cb, objGradIndexVars.size(), objGradIndexVars.data(), - 0, NULL, NULL, cb_grad); - check_error(error); - m_obj_flag |= OBJ_NONLINEAR; + m_graphs.clear(); + m_need_to_add_callbacks = false; } -double KnitroModel::get_obj_value() +void KNITROModel::_update() { - if (!m_result.is_valid) - { - throw std::runtime_error("No solution available"); - } - return m_result.obj_val; + _add_callbacks(); + int error = knitro::KN_update(m_kc.get()); + check_error(error); } -void KnitroModel::optimize() +void KNITROModel::_pre_solve() { - _optimize(); + _add_callbacks(); } -void KnitroModel::_optimize() +void KNITROModel::_solve() { int error = knitro::KN_solve(m_kc.get()); - // Note: KN_solve returns solve status, not an error code + // NOTE: KN_solve returns solve status, not an error code m_solve_status = error; +} + +void KNITROModel::_post_solve() +{ + int error; - // Get solution KNINT nV, nC; error = knitro::KN_get_number_vars(m_kc.get(), &nV); check_error(error); @@ -984,5 +1070,12 @@ void KnitroModel::_optimize() m_result.status = m_solve_status; m_result.is_valid = true; +} + +void KNITROModel::optimize() +{ + _pre_solve(); + _solve(); + _post_solve(); m_is_dirty = false; } diff --git a/lib/knitro_model_ext.cpp b/lib/knitro_model_ext.cpp index b7c73837..55c17582 100644 --- a/lib/knitro_model_ext.cpp +++ b/lib/knitro_model_ext.cpp @@ -18,20 +18,20 @@ NB_MODULE(knitro_model_ext, m) bind_knitro_constants(m); -#define BIND_F(f) .def(#f, &KnitroModel::f) - nb::class_(m, "RawModel") +#define BIND_F(f) .def(#f, &KNITROModel::f) + nb::class_(m, "RawModel") .def(nb::init<>()) // clang-format off BIND_F(init) BIND_F(close) BIND_F(get_infinity) // clang-format on - .def_ro("n_vars", &KnitroModel::n_vars) - .def_ro("n_cons", &KnitroModel::n_cons) - .def_ro("n_lincons", &KnitroModel::n_lincons) - .def_ro("n_quadcons", &KnitroModel::n_quadcons) + .def_ro("n_vars", &KNITROModel::n_vars) + .def_ro("n_cons", &KNITROModel::n_cons) + .def_ro("n_lincons", &KNITROModel::n_lincons) + .def_ro("n_quadcons", &KNITROModel::n_quadcons) - .def("add_variable", &KnitroModel::add_variable, + .def("add_variable", &KNITROModel::add_variable, nb::arg("domain") = VariableDomain::Continuous, nb::arg("lb") = -KN_INFINITY, nb::arg("ub") = KN_INFINITY, nb::arg("name") = "") @@ -41,7 +41,7 @@ NB_MODULE(knitro_model_ext, m) BIND_F(set_variable_lb) BIND_F(set_variable_ub) // clang-format on - .def("set_variable_bounds", &KnitroModel::set_variable_bounds, nb::arg("variable"), + .def("set_variable_bounds", &KNITROModel::set_variable_bounds, nb::arg("variable"), nb::arg("lb"), nb::arg("ub")) // clang-format off @@ -50,26 +50,26 @@ NB_MODULE(knitro_model_ext, m) BIND_F(set_variable_name) BIND_F(set_variable_domain) // clang-format on - .def("get_variable_rc", &KnitroModel::get_variable_rc, nb::arg("variable")) - .def("delete_variable", &KnitroModel::delete_variable, nb::arg("variable")) + .def("get_variable_rc", &KNITROModel::get_variable_rc, nb::arg("variable")) + .def("delete_variable", &KNITROModel::delete_variable, nb::arg("variable")) - .def("get_value", &KnitroModel::get_variable_value) + .def("get_value", &KNITROModel::get_variable_value) .def("get_value", - nb::overload_cast(&KnitroModel::get_expression_value)) + nb::overload_cast(&KNITROModel::get_expression_value)) .def("get_value", - nb::overload_cast(&KnitroModel::get_expression_value)) + nb::overload_cast(&KNITROModel::get_expression_value)) .def("get_value", - nb::overload_cast(&KnitroModel::get_expression_value)) + nb::overload_cast(&KNITROModel::get_expression_value)) - .def("pprint", &KnitroModel::pprint_variable) + .def("pprint", &KNITROModel::pprint_variable) .def("pprint", - nb::overload_cast(&KnitroModel::pprint_expression), + nb::overload_cast(&KNITROModel::pprint_expression), nb::arg("expr"), nb::arg("precision") = 4) .def("pprint", nb::overload_cast( - &KnitroModel::pprint_expression), + &KNITROModel::pprint_expression), nb::arg("expr"), nb::arg("precision") = 4) - .def("pprint", nb::overload_cast(&KnitroModel::pprint_expression), + .def("pprint", nb::overload_cast(&KNITROModel::pprint_expression), nb::arg("expr"), nb::arg("precision") = 4) // clang-format off @@ -84,158 +84,155 @@ NB_MODULE(knitro_model_ext, m) .def("_add_linear_constraint", nb::overload_cast( - &KnitroModel::add_linear_constraint), + &KNITROModel::add_linear_constraint), nb::arg("expr"), nb::arg("sense"), nb::arg("rhs"), nb::arg("name") = "") .def("_add_linear_constraint", nb::overload_cast &, - const char *>(&KnitroModel::add_linear_constraint), + const char *>(&KNITROModel::add_linear_constraint), nb::arg("expr"), nb::arg("interval"), nb::arg("name") = "") - .def("_add_linear_constraint", &KnitroModel::add_linear_constraint_from_var, + .def("_add_linear_constraint", &KNITROModel::add_linear_constraint_from_var, nb::arg("expr"), nb::arg("sense"), nb::arg("rhs"), nb::arg("name") = "") - .def("_add_linear_constraint", &KnitroModel::add_linear_interval_constraint_from_var, + .def("_add_linear_constraint", &KNITROModel::add_linear_interval_constraint_from_var, nb::arg("expr"), nb::arg("interval"), nb::arg("name") = "") - .def("_add_linear_constraint", &KnitroModel::add_linear_constraint_from_expr, + .def("_add_linear_constraint", &KNITROModel::add_linear_constraint_from_expr, nb::arg("expr"), nb::arg("sense"), nb::arg("rhs"), nb::arg("name") = "") - .def("_add_linear_constraint", &KnitroModel::add_linear_interval_constraint_from_expr, + .def("_add_linear_constraint", &KNITROModel::add_linear_interval_constraint_from_expr, nb::arg("expr"), nb::arg("interval"), nb::arg("name") = "") .def("_add_quadratic_constraint", nb::overload_cast(&KnitroModel::add_quadratic_constraint), + const char *>(&KNITROModel::add_quadratic_constraint), nb::arg("expr"), nb::arg("sense"), nb::arg("rhs"), nb::arg("name") = "") .def("_add_quadratic_constraint", nb::overload_cast &, - const char *>(&KnitroModel::add_quadratic_constraint), + const char *>(&KNITROModel::add_quadratic_constraint), nb::arg("expr"), nb::arg("interval"), nb::arg("name") = "") - .def("_add_quadratic_constraint", &KnitroModel::add_quadratic_constraint_from_expr, + .def("_add_quadratic_constraint", &KNITROModel::add_quadratic_constraint_from_expr, nb::arg("expr"), nb::arg("sense"), nb::arg("rhs"), nb::arg("name") = "") - .def("_add_quadratic_constraint", &KnitroModel::add_quadratic_interval_constraint_from_expr, + .def("_add_quadratic_constraint", &KNITROModel::add_quadratic_interval_constraint_from_expr, nb::arg("expr"), nb::arg("interval"), nb::arg("name") = "") - .def("add_second_order_cone_constraint", &KnitroModel::add_second_order_cone_constraint, + .def("add_second_order_cone_constraint", &KNITROModel::add_second_order_cone_constraint, nb::arg("variables"), nb::arg("name") = "", nb::arg("rotated") = false) - .def("_add_single_nl_constraint", &KnitroModel::add_single_nl_constraint, - nb::arg("graph"), nb::arg("result"), - nb::arg("interval"), nb::arg("name") = "") - .def("_add_single_nl_constraint", - &KnitroModel::add_single_nl_constraint_sense_rhs, - nb::arg("graph"), nb::arg("result"), - nb::arg("sense"), nb::arg("rhs"), nb::arg("name") = "") - .def("_add_single_nl_constraint", - &KnitroModel::add_single_nl_constraint_from_comparison, + .def("_add_single_nl_constraint", &KNITROModel::add_single_nl_constraint, nb::arg("graph"), + nb::arg("result"), nb::arg("interval"), nb::arg("name") = "") + .def("_add_single_nl_constraint", &KNITROModel::add_single_nl_constraint_sense_rhs, + nb::arg("graph"), nb::arg("result"), nb::arg("sense"), nb::arg("rhs"), + nb::arg("name") = "") + .def("_add_single_nl_constraint", &KNITROModel::add_single_nl_constraint_from_comparison, nb::arg("graph"), nb::arg("expr"), nb::arg("name") = "") - .def("delete_constraint", &KnitroModel::delete_constraint, nb::arg("constraint")) + .def("delete_constraint", &KNITROModel::delete_constraint, nb::arg("constraint")) .def("set_objective", nb::overload_cast( - &KnitroModel::set_objective), + &KNITROModel::set_objective), nb::arg("expr"), nb::arg("sense") = ObjectiveSense::Minimize) - .def("set_objective", + .def("set_objective", nb::overload_cast( - &KnitroModel::set_objective), + &KNITROModel::set_objective), nb::arg("expr"), nb::arg("sense") = ObjectiveSense::Minimize) - .def("set_objective", - nb::overload_cast(&KnitroModel::set_objective), + .def("set_objective", + nb::overload_cast(&KNITROModel::set_objective), nb::arg("expr"), nb::arg("sense") = ObjectiveSense::Minimize) .def("set_objective", nb::overload_cast( - &KnitroModel::set_objective_as_variable), + &KNITROModel::set_objective_as_variable), nb::arg("expr"), nb::arg("sense") = ObjectiveSense::Minimize) .def("set_objective", - nb::overload_cast(&KnitroModel::set_objective_as_constant), + nb::overload_cast(&KNITROModel::set_objective_as_constant), nb::arg("expr"), nb::arg("sense") = ObjectiveSense::Minimize) - .def("_add_single_nl_objective", &KnitroModel::add_single_nl_objective, - nb::arg("graph"), nb::arg("result")) + .def("_add_single_nl_objective", &KNITROModel::add_single_nl_objective, nb::arg("graph"), + nb::arg("result")) // clang-format off BIND_F(get_obj_value) // clang-format on - .def("_optimize", &KnitroModel::_optimize, nb::call_guard()) + .def("optimize", &KNITROModel::optimize, nb::call_guard()) .def( "set_raw_parameter", - [](KnitroModel &m, const std::string &name, int value) { + [](KNITROModel &m, const std::string &name, int value) { m.set_raw_parameter(name, value); }, nb::arg("name"), nb::arg("value")) .def( "set_raw_parameter", - [](KnitroModel &m, const std::string &name, double value) { + [](KNITROModel &m, const std::string &name, double value) { m.set_raw_parameter(name, value); }, nb::arg("name"), nb::arg("value")) .def( "set_raw_parameter", - [](KnitroModel &m, const std::string &name, const std::string &value) { + [](KNITROModel &m, const std::string &name, const std::string &value) { m.set_raw_parameter(name, value); }, nb::arg("name"), nb::arg("value")) .def( "set_raw_parameter", - [](KnitroModel &m, int param_id, int value) { + [](KNITROModel &m, int param_id, int value) { m.set_raw_parameter(param_id, value); }, nb::arg("param_id"), nb::arg("value")) .def( "set_raw_parameter", - [](KnitroModel &m, int param_id, double value) { + [](KNITROModel &m, int param_id, double value) { m.set_raw_parameter(param_id, value); }, nb::arg("param_id"), nb::arg("value")) .def( "set_raw_parameter", - [](KnitroModel &m, int param_id, const std::string &value) { + [](KNITROModel &m, int param_id, const std::string &value) { m.set_raw_parameter(param_id, value); }, nb::arg("param_id"), nb::arg("value")) .def( "get_raw_parameter", - [](KnitroModel &m, const std::string &name) -> int { + [](KNITROModel &m, const std::string &name) -> int { return m.get_raw_parameter(name); }, nb::arg("name")) .def( "get_raw_parameter", - [](KnitroModel &m, const std::string &name) -> double { + [](KNITROModel &m, const std::string &name) -> double { return m.get_raw_parameter(name); }, nb::arg("name")) .def( "get_raw_parameter", - [](KnitroModel &m, int param_id) -> int { return m.get_raw_parameter(param_id); }, + [](KNITROModel &m, int param_id) -> int { return m.get_raw_parameter(param_id); }, nb::arg("param_id")) .def( "get_raw_parameter", - [](KnitroModel &m, int param_id) -> double { + [](KNITROModel &m, int param_id) -> double { return m.get_raw_parameter(param_id); }, nb::arg("param_id")) - .def("get_value", &KnitroModel::get_variable_value) + .def("get_value", &KNITROModel::get_variable_value) .def("get_value", - nb::overload_cast(&KnitroModel::get_expression_value)) + nb::overload_cast(&KNITROModel::get_expression_value)) .def("get_value", - nb::overload_cast(&KnitroModel::get_expression_value)) + nb::overload_cast(&KNITROModel::get_expression_value)) .def("get_value", - nb::overload_cast(&KnitroModel::get_expression_value)) + nb::overload_cast(&KNITROModel::get_expression_value)) - .def("pprint", &KnitroModel::pprint_variable) + .def("pprint", &KNITROModel::pprint_variable) .def("pprint", - nb::overload_cast(&KnitroModel::pprint_expression), + nb::overload_cast(&KNITROModel::pprint_expression), nb::arg("expr"), nb::arg("precision") = 4) .def("pprint", nb::overload_cast( - &KnitroModel::pprint_expression), + &KNITROModel::pprint_expression), nb::arg("expr"), nb::arg("precision") = 4) - .def("pprint", nb::overload_cast(&KnitroModel::pprint_expression), + .def("pprint", nb::overload_cast(&KNITROModel::pprint_expression), nb::arg("expr"), nb::arg("precision") = 4) - .def_rw("m_is_dirty", &KnitroModel::m_is_dirty) - .def_ro("m_solve_status", &KnitroModel::m_solve_status); + .def_rw("m_is_dirty", &KNITROModel::m_is_dirty) + .def_ro("m_solve_status", &KNITROModel::m_solve_status); #undef BIND_F } diff --git a/src/pyoptinterface/_src/knitro.py b/src/pyoptinterface/_src/knitro.py index 8f5e18bf..8dd04fd9 100644 --- a/src/pyoptinterface/_src/knitro.py +++ b/src/pyoptinterface/_src/knitro.py @@ -31,7 +31,7 @@ _set_entity_attribute, ) from .aml import make_variable_ndarray, make_variable_tupledict -from .nlexpr_ext import ExpressionHandle +from .nlexpr_ext import ExpressionHandle, ExpressionGraph from .nlfunc import ExpressionGraphContext, convert_to_expressionhandle @@ -198,8 +198,7 @@ class Model(RawModel): def __init__(self): super().__init__() - self._variable_tupledict_maker = None - self._constraint_tupledict_maker = None + self.graph_map: dict[ExpressionGraph, int] = {} @staticmethod def supports_variable_attribute(attribute: VariableAttribute, setable: bool = False) -> bool: @@ -316,6 +315,8 @@ def add_nl_constraint(self, expr, *args, **kwargs): expr = convert_to_expressionhandle(graph, expr) if not isinstance(expr, ExpressionHandle): raise ValueError("Expression should be convertible to ExpressionHandle") + if graph not in self.graph_map: + self.graph_map[graph] = len(self.graph_map) return self._add_single_nl_constraint(graph, expr, *args, **kwargs) def add_nl_objective(self, expr): @@ -323,6 +324,8 @@ def add_nl_objective(self, expr): expr = convert_to_expressionhandle(graph, expr) if not isinstance(expr, ExpressionHandle): raise ValueError("Expression should be convertible to ExpressionHandle") + if graph not in self.graph_map: + self.graph_map[graph] = len(self.graph_map) self._add_single_nl_objective(graph, expr) def get_model_attribute(self, attr: ModelAttribute): @@ -400,9 +403,6 @@ def e(attribute): {}, ) - def optimize(self): - self._optimize() - def is_empty(self): return not self or self.m_is_dirty From b5bc164b775796fa07e5195cfd7bfdcab5430dbd Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Sun, 8 Feb 2026 02:02:01 -0500 Subject: [PATCH 05/23] refactor KNITRO callback handling and data structures --- include/pyoptinterface/knitro_model.hpp | 210 ++++++++++++++++++- lib/knitro_model.cpp | 256 ++++++------------------ 2 files changed, 267 insertions(+), 199 deletions(-) diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp index a6f5eec9..35891d77 100644 --- a/include/pyoptinterface/knitro_model.hpp +++ b/include/pyoptinterface/knitro_model.hpp @@ -138,7 +138,17 @@ enum ConstraintSenseFlags CON_UPBND = 1 << 1, // 0x02 }; -struct KNITROCallbackLoad +struct KNITROCallbackPattern +{ + std::vector indexCons; + std::vector objGradIndexVars; + std::vector jacIndexCons; + std::vector jacIndexVars; + std::vector hessIndexVars1; + std::vector hessIndexVars2; +}; + +struct KNITROADEvaluator { std::vector indexVars; std::vector indexCons; @@ -157,13 +167,153 @@ struct KNITROCallbackLoad std::vector x; std::vector jac; + std::vector w; std::vector hess; + + void setup() + { + fun.optimize(); + size_t m = fun_rows.size(); + std::vector> jac_sparsity(m); + for (size_t k = 0; k < m; k++) + { + jac_sparsity[k].insert(fun_rows[k]); + } + jac_pattern = fun.RevSparseJac(jac_sparsity.size(), jac_sparsity); + for (size_t k = 0; k < jac_pattern.size(); k++) + { + for (size_t i : jac_pattern[k]) + { + jac_rows.push_back(fun_rows[k]); + jac_cols.push_back(i); + } + } + std::vector> r_hess_sparsity(fun.Domain()); + for (size_t i = 0; i < fun.Domain(); i++) + { + r_hess_sparsity[i].insert(i); + } + fun.ForSparseJac(fun.Domain(), r_hess_sparsity); + std::vector> hess_sparsity(1); + for (size_t k = 0; k < m; k++) + { + hess_sparsity[0].insert(fun_rows[k]); + } + hess_pattern = fun.RevSparseHes(r_hess_sparsity.size(), hess_sparsity); + for (size_t k = 0; k < hess_pattern.size(); k++) + { + for (size_t i : hess_pattern[k]) + { + hess_rows.push_back(k); + hess_cols.push_back(i); + } + } + x.resize(indexVars.size()); + jac.resize(jac_rows.size()); + w.resize(fun.Range(), 0.0); + hess.resize(hess_rows.size()); + } + + void eval_fun(const double *req_x, double *res_y, bool aggregate = false) + { + for (size_t i = 0; i < indexVars.size(); i++) + { + x[i] = req_x[indexVars[i]]; + } + auto y = fun.Forward(0, x); + for (size_t k = 0; k < fun_rows.size(); k++) + { + if (aggregate) + { + res_y[0] += y[fun_rows[k]]; + } + else + { + res_y[k] = y[fun_rows[k]]; + } + } + } + + void eval_jac(const double *req_x, double *res_jac) + { + for (size_t i = 0; i < indexVars.size(); i++) + { + x[i] = req_x[indexVars[i]]; + } + fun.SparseJacobianReverse(x, jac_pattern, jac_rows, jac_cols, jac, jac_work); + for (size_t i = 0; i < jac.size(); i++) + { + res_jac[i] = jac[i]; + } + } + + void eval_hess(const double *req_x, const double *req_w, double *res_hess, + bool aggregate = false) + { + for (size_t i = 0; i < indexVars.size(); i++) + { + x[i] = req_x[indexVars[i]]; + } + for (size_t k = 0; k < fun_rows.size(); k++) + { + if (aggregate) + { + w[fun_rows[k]] = req_w[0]; + } + else + { + w[fun_rows[k]] = req_w[indexCons[k]]; + } + } + fun.SparseHessian(x, w, hess_pattern, hess_rows, hess_cols, hess, hess_work); + for (size_t i = 0; i < hess.size(); i++) + { + res_hess[i] = hess[i]; + } + } + + KNITROCallbackPattern get_callback_pattern() const + { + KNITROCallbackPattern pattern; + pattern.indexCons = indexCons; + if (indexCons.size() > 0) + { + for (size_t k = 0; k < jac_pattern.size(); k++) + { + for (size_t i : jac_pattern[k]) + { + pattern.jacIndexCons.push_back(indexCons[k]); + pattern.jacIndexVars.push_back(indexVars[i]); + } + } + } + else + { + for (size_t k = 0; k < jac_pattern.size(); k++) + { + for (size_t i : jac_pattern[k]) + { + pattern.objGradIndexVars.push_back(indexVars[i]); + } + } + } + for (size_t k = 0; k < hess_pattern.size(); k++) + { + for (size_t i : hess_pattern[k]) + { + pattern.hessIndexVars1.push_back(indexVars[k]); + pattern.hessIndexVars2.push_back(indexVars[i]); + } + } + return pattern; + } }; -struct KNITROGraph +struct KNITROPendingOutputs { - std::vector m_objs; - std::unordered_map m_cons; + std::vector m_obj_idxs; + std::vector m_con_idxs; + std::vector m_cons; }; class KNITROModel : public OnesideLinearConstraintMixin, @@ -333,11 +483,9 @@ class KNITROModel : public OnesideLinearConstraintMixin, std::unordered_map m_con_sense_flags; uint8_t m_obj_flag = 0; - std::unordered_map m_graphs; - std::vector> m_loads; + std::unordered_map m_pending_outputs; + std::vector> m_evaluators; bool m_need_to_add_callbacks = false; - - // Solution and solve status KNITROResult m_result; bool m_is_dirty = true; int m_solve_status = 0; @@ -391,6 +539,8 @@ class KNITROModel : public OnesideLinearConstraintMixin, void _reset_objective(); void _add_graph(ExpressionGraph &graph); void _add_callbacks(); + void _add_constraint_callback(ExpressionGraph *graph, const KNITROPendingOutputs &outputs); + void _add_objective_callback(ExpressionGraph *graph, const KNITROPendingOutputs &outputs); void _update(); void _pre_solve(); void _solve(); @@ -458,6 +608,50 @@ class KNITROModel : public OnesideLinearConstraintMixin, m_result.is_valid = false; } + template + void _register_callback(KNITROADEvaluator *evaluator, const F f, const G g, const H h) + { + CB_context *cb = nullptr; + auto p = evaluator->get_callback_pattern(); + int error; + error = knitro::KN_add_eval_callback(m_kc.get(), p.indexCons.empty(), p.indexCons.size(), + p.indexCons.data(), f, &cb); + check_error(error); + error = knitro::KN_set_cb_user_params(m_kc.get(), cb, evaluator); + check_error(error); + error = knitro::KN_set_cb_grad(m_kc.get(), cb, p.objGradIndexVars.size(), + p.objGradIndexVars.data(), p.jacIndexCons.size(), + p.jacIndexCons.data(), p.jacIndexVars.data(), g); + check_error(error); + error = knitro::KN_set_cb_hess(m_kc.get(), cb, p.hessIndexVars1.size(), + p.hessIndexVars1.data(), p.hessIndexVars2.data(), h); + check_error(error); + } + + template + void _add_callback_impl(const ExpressionGraph &graph, const std::vector &rows, + const std::vector cons, const T &trace, const F f, + const G g, const H h) + { + auto evaluator_ptr = std::make_unique(); + auto *evaluator = evaluator_ptr.get(); + evaluator->fun = trace(graph); + evaluator->fun_rows = rows; + evaluator->indexVars.resize(graph.n_variables()); + for (size_t i = 0; i < graph.n_variables(); i++) + { + evaluator->indexVars[i] = _variable_index(graph.m_variables[i]); + } + evaluator->indexCons.resize(cons.size()); + for (size_t i = 0; i < cons.size(); i++) + { + evaluator->indexCons[i] = _constraint_index(cons[i]); + } + evaluator->setup(); + _register_callback(evaluator, f, g, h); + m_evaluators.push_back(std::move(evaluator_ptr)); + } + template std::string _get_name(KNINT index, F get, const char *prefix) const { diff --git a/lib/knitro_model.cpp b/lib/knitro_model.cpp index 049f804b..b972089a 100644 --- a/lib/knitro_model.cpp +++ b/lib/knitro_model.cpp @@ -335,9 +335,10 @@ ConstraintIndex KNITROModel::add_single_nl_constraint(ExpressionGraph &graph, { _add_graph(graph); graph.add_constraint_output(result); + size_t i = graph.m_constraint_outputs.size() - 1; + m_pending_outputs[&graph].m_con_idxs.push_back(i); auto setter = [this, &graph](const ConstraintIndex &constraint) { - size_t i = graph.m_constraint_outputs.size() - 1; - m_graphs[&graph].m_cons[i] = constraint; + m_pending_outputs[&graph].m_cons.push_back(constraint); m_need_to_add_callbacks = true; }; return _add_constraint_impl(ConstraintType::KNITRO_NL, interval, name, &n_nlcons, setter); @@ -702,7 +703,7 @@ void KNITROModel::add_single_nl_objective(ExpressionGraph &graph, const Expressi _add_graph(graph); graph.add_objective_output(result); size_t i = graph.m_objective_outputs.size() - 1; - m_graphs[&graph].m_objs.push_back(i); + m_pending_outputs[&graph].m_obj_idxs.push_back(i); m_need_to_add_callbacks = true; m_obj_flag |= OBJ_NONLINEAR; m_is_dirty = true; @@ -711,9 +712,9 @@ void KNITROModel::add_single_nl_objective(ExpressionGraph &graph, const Expressi void KNITROModel::_add_graph(ExpressionGraph &graph) { - if (m_graphs.find(&graph) == m_graphs.end()) + if (m_pending_outputs.find(&graph) == m_pending_outputs.end()) { - m_graphs[&graph] = KNITROGraph(); + m_pending_outputs[&graph] = KNITROPendingOutputs(); } } @@ -740,10 +741,12 @@ void KNITROModel::_reset_objective() { error = knitro::KN_del_obj_eval_callback_all(m_kc.get()); check_error(error); + for (auto &[graph, outputs] : m_pending_outputs) + { + outputs.m_obj_idxs.clear(); + } } - m_obj_flag = 0; - _update(); } @@ -807,6 +810,55 @@ double KNITROModel::get_obj_value() return m_result.obj_val; } +void KNITROModel::_add_constraint_callback(ExpressionGraph *graph, const KNITROPendingOutputs &outputs) +{ + auto f = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, + void *data) -> int { + KNITROADEvaluator *evaluator = static_cast(data); + evaluator->eval_fun(req->x, res->c); + return 0; + }; + auto g = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, + void *data) -> int { + KNITROADEvaluator *evaluator = static_cast(data); + evaluator->eval_jac(req->x, res->jac); + return 0; + }; + auto h = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, + void *data) -> int { + KNITROADEvaluator *evaluator = static_cast(data); + evaluator->eval_hess(req->x, req->lambda, res->hess); + return 0; + }; + auto trace = cppad_trace_graph_constraints; + _add_callback_impl(*graph, outputs.m_con_idxs, outputs.m_cons, trace, f, g, h); +} + +void KNITROModel::_add_objective_callback(ExpressionGraph *graph, const KNITROPendingOutputs &outputs) +{ + auto f = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, + void *data) -> int { + KNITROADEvaluator *evaluator = static_cast(data); + res->obj[0] = 0.0; + evaluator->eval_fun(req->x, res->obj, true); + return 0; + }; + auto g = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, + void *data) -> int { + KNITROADEvaluator *evaluator = static_cast(data); + evaluator->eval_jac(req->x, res->objGrad); + return 0; + }; + auto h = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, + void *data) -> int { + KNITROADEvaluator *evaluator = static_cast(data); + evaluator->eval_hess(req->x, req->sigma, res->hess, true); + return 0; + }; + auto trace = cppad_trace_graph_objective; + _add_callback_impl(*graph, outputs.m_obj_idxs, {}, trace, f, g, h); +} + void KNITROModel::_add_callbacks() { if (!m_need_to_add_callbacks) @@ -814,197 +866,19 @@ void KNITROModel::_add_callbacks() return; } - for (const auto &[graph, pending] : m_graphs) + for (const auto &[graph, outputs] : m_pending_outputs) { - if (graph->has_constraint_output() && !pending.m_cons.empty()) + if (graph->has_constraint_output() && !outputs.m_con_idxs.empty()) { - auto f = cppad_trace_graph_constraints(*graph); - f.optimize(); - - size_t n = f.Domain(); - size_t m = pending.m_cons.size(); - auto load_ptr = std::make_unique(); - KNITROCallbackLoad *load = load_ptr.get(); - load->fun = f; - load->indexVars.resize(n); - load->indexCons.resize(m); - load->fun_rows.resize(m); - for (size_t i = 0; i < n; i++) - { - load->indexVars[i] = _variable_index(graph->m_variables[i]); - } - auto it = pending.m_cons.begin(); - for (size_t k = 0; k < m; k++, it++) - { - auto &[j, constraint] = *it; - load->fun_rows[k] = j; - load->indexCons[k] = _constraint_index(constraint); - } - std::vector> jac_sparsity(m); - for (size_t k = 0; k < m; k++) - { - jac_sparsity[k].insert(load->fun_rows[k]); - } - load->jac_pattern = load->fun.RevSparseJac(jac_sparsity.size(), jac_sparsity); - for (size_t k = 0; k < load->jac_pattern.size(); k++) - { - for (size_t i : load->jac_pattern[k]) - { - load->jac_rows.push_back(load->fun_rows[k]); - load->jac_cols.push_back(i); - } - } - std::vector jacIndexCons(load->jac_rows.size()); - std::vector jacIndexVars(load->jac_cols.size()); - size_t idx = 0; - for (size_t k = 0; k < load->jac_pattern.size(); k++) - { - for (size_t i : load->jac_pattern[k]) - { - jacIndexCons[idx] = load->indexCons[k]; - jacIndexVars[idx] = load->indexVars[i]; - idx++; - } - } - load->x.resize(n); - load->jac.resize(load->jac_cols.size()); - auto cb_eval = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, - void *data) -> int { - KNITROCallbackLoad *load = static_cast(data); - for (size_t i = 0; i < load->indexVars.size(); i++) - { - load->x[i] = req->x[load->indexVars[i]]; - } - auto y = load->fun.Forward(0, load->x); - for (size_t k = 0; k < load->fun_rows.size(); k++) - { - res->c[k] = y[load->fun_rows[k]]; - } - return 0; - }; - auto cb_grad = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, - void *data) -> int { - KNITROCallbackLoad *load = static_cast(data); - for (size_t i = 0; i < load->indexVars.size(); i++) - { - load->x[i] = req->x[load->indexVars[i]]; - } - load->fun.SparseJacobianReverse(load->x, load->jac_pattern, load->jac_rows, - load->jac_cols, load->jac, load->jac_work); - for (size_t i = 0; i < load->jac.size(); i++) - { - res->jac[i] = load->jac[i]; - } - return 0; - }; - CB_context *cb = nullptr; - int error; - error = knitro::KN_add_eval_callback(m_kc.get(), FALSE, load->indexCons.size(), - load->indexCons.data(), cb_eval, &cb); - check_error(error); - error = knitro::KN_set_cb_user_params(m_kc.get(), cb, load); - check_error(error); - error = knitro::KN_set_cb_grad(m_kc.get(), cb, 0, NULL, jacIndexCons.size(), - jacIndexCons.data(), jacIndexVars.data(), cb_grad); - check_error(error); - m_loads.push_back(std::move(load_ptr)); + _add_constraint_callback(graph, outputs); } - if (graph->has_objective_output() && !pending.m_objs.empty()) + if (graph->has_objective_output() && !outputs.m_obj_idxs.empty()) { - auto f = cppad_trace_graph_objective(*graph); - f.optimize(); - - size_t n = f.Domain(); - size_t m = pending.m_objs.size(); - auto load_ptr = std::make_unique(); - KNITROCallbackLoad *load = load_ptr.get(); - load->fun = f; - load->indexVars.resize(n); - load->indexCons.resize(0); - load->fun_rows.resize(m); - for (size_t i = 0; i < n; i++) - { - load->indexVars[i] = _variable_index(graph->m_variables[i]); - } - auto it = pending.m_objs.begin(); - for (size_t k = 0; k < m; k++, it++) - { - auto &j = *it; - load->fun_rows[k] = j; - } - std::vector> jac_sparsity(m); - for (size_t k = 0; k < m; k++) - { - jac_sparsity[k].insert(load->fun_rows[k]); - } - load->jac_pattern = load->fun.RevSparseJac(jac_sparsity.size(), jac_sparsity); - for (size_t k = 0; k < load->jac_pattern.size(); k++) - { - for (size_t i : load->jac_pattern[k]) - { - load->jac_rows.push_back(load->fun_rows[k]); - load->jac_cols.push_back(i); - } - } - std::vector objGradIndexVars(load->jac_cols.size()); - size_t idx = 0; - for (size_t k = 0; k < load->jac_pattern.size(); k++) - { - for (size_t i : load->jac_pattern[k]) - { - objGradIndexVars[idx] = load->indexVars[i]; - idx++; - } - } - load->x.resize(n); - load->jac.resize(load->jac_cols.size()); - auto cb_eval = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, - void *data) -> int { - KNITROCallbackLoad *load = static_cast(data); - for (size_t i = 0; i < load->indexVars.size(); i++) - { - load->x[i] = req->x[load->indexVars[i]]; - } - auto y = load->fun.Forward(0, load->x); - res->obj[0] = 0.0; - for (size_t k = 0; k < load->fun_rows.size(); k++) - { - size_t j = load->fun_rows[k]; - res->obj[0] += y[j]; - } - return 0; - }; - auto cb_grad = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, - void *data) -> int { - KNITROCallbackLoad *load = static_cast(data); - for (size_t i = 0; i < load->indexVars.size(); i++) - { - load->x[i] = req->x[load->indexVars[i]]; - } - load->fun.SparseJacobianReverse(load->x, load->jac_pattern, load->jac_rows, - load->jac_cols, load->jac, load->jac_work); - for (size_t i = 0; i < load->jac.size(); i++) - { - res->objGrad[i] = load->jac[i]; - } - return 0; - }; - - CB_context *cb = nullptr; - int error; - error = knitro::KN_add_eval_callback(m_kc.get(), TRUE, 0, NULL, cb_eval, &cb); - check_error(error); - error = knitro::KN_set_cb_user_params(m_kc.get(), cb, load); - check_error(error); - error = knitro::KN_set_cb_grad(m_kc.get(), cb, objGradIndexVars.size(), - objGradIndexVars.data(), 0, NULL, NULL, cb_grad); - check_error(error); - m_loads.push_back(std::move(load_ptr)); + _add_objective_callback(graph, outputs); } } - - m_graphs.clear(); + m_pending_outputs.clear(); m_need_to_add_callbacks = false; } From 80c18b118afc353dc96c47eea2b659a358a93c1c Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Sun, 8 Feb 2026 11:02:19 -0500 Subject: [PATCH 06/23] refine tests. --- lib/knitro_model.cpp | 6 ++++-- tests/test_nlp_clnlbeam.py | 30 +++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/lib/knitro_model.cpp b/lib/knitro_model.cpp index b972089a..31355ec3 100644 --- a/lib/knitro_model.cpp +++ b/lib/knitro_model.cpp @@ -810,7 +810,8 @@ double KNITROModel::get_obj_value() return m_result.obj_val; } -void KNITROModel::_add_constraint_callback(ExpressionGraph *graph, const KNITROPendingOutputs &outputs) +void KNITROModel::_add_constraint_callback(ExpressionGraph *graph, + const KNITROPendingOutputs &outputs) { auto f = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, void *data) -> int { @@ -834,7 +835,8 @@ void KNITROModel::_add_constraint_callback(ExpressionGraph *graph, const KNITROP _add_callback_impl(*graph, outputs.m_con_idxs, outputs.m_cons, trace, f, g, h); } -void KNITROModel::_add_objective_callback(ExpressionGraph *graph, const KNITROPendingOutputs &outputs) +void KNITROModel::_add_objective_callback(ExpressionGraph *graph, + const KNITROPendingOutputs &outputs) { auto f = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, void *data) -> int { diff --git a/tests/test_nlp_clnlbeam.py b/tests/test_nlp_clnlbeam.py index 87b77fb3..2235db3a 100644 --- a/tests/test_nlp_clnlbeam.py +++ b/tests/test_nlp_clnlbeam.py @@ -3,6 +3,8 @@ import pytest +import numpy as np + def test_clnlbeam(nlp_model_ctor): model = nlp_model_ctor() @@ -30,6 +32,32 @@ def test_clnlbeam(nlp_model_ctor): model.optimize() + tv = np.zeros(N + 1) + xv = np.zeros(N + 1) + uv = np.zeros(N + 1) + for i in range(N + 1): + tv[i] = model.get_value(t[i]) + xv[i] = model.get_value(x[i]) + uv[i] = model.get_value(u[i]) + + con_expr_val = np.zeros(N) + for i in range(N): + con_expr_val[i] = xv[i + 1] - xv[i] - 0.5 * h * (np.sin(tv[i]) + np.sin(tv[i + 1])) + + assert np.allclose(con_expr_val, 0.0, atol=1e-8) + + lin_con_expr_val = np.zeros(N) + for i in range(N): + lin_con_expr_val[i] = tv[i + 1] - tv[i] - 0.5 * h * uv[i + 1] - 0.5 * h * uv[i] + + assert np.allclose(lin_con_expr_val, 0.0, atol=1e-8) + + obj_expr_val = 0.0 + for i in range(N): + obj_expr_val += 0.5 * h * (uv[i] * uv[i] + uv[i + 1] * uv[i + 1]) + obj_expr_val += 0.5 * alpha * h * (np.cos(tv[i]) + np.cos(tv[i + 1])) + objective_value = model.get_model_attribute(poi.ModelAttribute.ObjectiveValue) - assert objective_value == pytest.approx(350.0, abs=1e-8) + assert np.isclose(obj_expr_val, objective_value, atol=1e-8) + assert objective_value == pytest.approx(328.0967, abs=1e-4) From 923baa8d376c32c115a785703bb9b07693ff73a9 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Sun, 8 Feb 2026 11:05:38 -0500 Subject: [PATCH 07/23] remove start vars initialization. --- tests/test_nlp.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_nlp.py b/tests/test_nlp.py index f31d9612..42544536 100644 --- a/tests/test_nlp.py +++ b/tests/test_nlp.py @@ -10,8 +10,6 @@ def test_easy_nlp(nlp_model_ctor): x = model.add_variable(lb=0.1, ub=10.0) y = model.add_variable(lb=0.1, ub=10.0) - model.set_variable_start(x, 0.5) - model.set_variable_start(y, 0.5) model.add_linear_constraint(x + y, poi.Eq, 1.0) From 65db2dc7df6d81aa289ede4feabf5395a6739674 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Sun, 8 Feb 2026 11:55:45 -0500 Subject: [PATCH 08/23] refactor KNITRO model structures and callback. --- include/pyoptinterface/knitro_model.hpp | 59 ++++++++++++++----------- lib/knitro_model.cpp | 44 +++++++++--------- lib/knitro_model_ext.cpp | 41 +++++------------ src/pyoptinterface/_src/knitro.py | 4 +- 4 files changed, 69 insertions(+), 79 deletions(-) diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp index 35891d77..c4480bc0 100644 --- a/include/pyoptinterface/knitro_model.hpp +++ b/include/pyoptinterface/knitro_model.hpp @@ -138,7 +138,7 @@ enum ConstraintSenseFlags CON_UPBND = 1 << 1, // 0x02 }; -struct KNITROCallbackPattern +struct CallbackPattern { std::vector indexCons; std::vector objGradIndexVars; @@ -148,12 +148,13 @@ struct KNITROCallbackPattern std::vector hessIndexVars2; }; -struct KNITROADEvaluator +template +struct CallbackEvaluator { std::vector indexVars; std::vector indexCons; - CppAD::ADFun fun; + CppAD::ADFun fun; std::vector fun_rows; std::vector> jac_pattern; @@ -165,10 +166,10 @@ struct KNITROADEvaluator std::vector hess_cols; CppAD::sparse_hessian_work hess_work; - std::vector x; - std::vector jac; - std::vector w; - std::vector hess; + std::vector x; + std::vector jac; + std::vector w; + std::vector hess; void setup() { @@ -214,7 +215,7 @@ struct KNITROADEvaluator hess.resize(hess_rows.size()); } - void eval_fun(const double *req_x, double *res_y, bool aggregate = false) + void eval_fun(const V *req_x, V *res_y, bool aggregate = false) { for (size_t i = 0; i < indexVars.size(); i++) { @@ -234,7 +235,7 @@ struct KNITROADEvaluator } } - void eval_jac(const double *req_x, double *res_jac) + void eval_jac(const V *req_x, V *res_jac) { for (size_t i = 0; i < indexVars.size(); i++) { @@ -247,7 +248,7 @@ struct KNITROADEvaluator } } - void eval_hess(const double *req_x, const double *req_w, double *res_hess, + void eval_hess(const V *req_x, const V *req_w, V *res_hess, bool aggregate = false) { for (size_t i = 0; i < indexVars.size(); i++) @@ -272,18 +273,18 @@ struct KNITROADEvaluator } } - KNITROCallbackPattern get_callback_pattern() const + CallbackPattern get_callback_pattern() const { - KNITROCallbackPattern pattern; + CallbackPattern pattern; pattern.indexCons = indexCons; - if (indexCons.size() > 0) + + if (indexCons.empty()) { for (size_t k = 0; k < jac_pattern.size(); k++) { for (size_t i : jac_pattern[k]) { - pattern.jacIndexCons.push_back(indexCons[k]); - pattern.jacIndexVars.push_back(indexVars[i]); + pattern.objGradIndexVars.push_back(indexVars[i]); } } } @@ -293,10 +294,12 @@ struct KNITROADEvaluator { for (size_t i : jac_pattern[k]) { - pattern.objGradIndexVars.push_back(indexVars[i]); + pattern.jacIndexCons.push_back(indexCons[k]); + pattern.jacIndexVars.push_back(indexVars[i]); } } } + for (size_t k = 0; k < hess_pattern.size(); k++) { for (size_t i : hess_pattern[k]) @@ -305,15 +308,16 @@ struct KNITROADEvaluator pattern.hessIndexVars2.push_back(indexVars[i]); } } + return pattern; } }; -struct KNITROPendingOutputs +struct Outputs { - std::vector m_obj_idxs; - std::vector m_con_idxs; - std::vector m_cons; + std::vector obj_idxs; + std::vector con_idxs; + std::vector cons; }; class KNITROModel : public OnesideLinearConstraintMixin, @@ -479,13 +483,14 @@ class KNITROModel : public OnesideLinearConstraintMixin, size_t n_coniccons = 0; size_t n_nlcons = 0; - std::unordered_map>> m_aux_cons; + std::unordered_map>> m_soc_aux_cons; std::unordered_map m_con_sense_flags; uint8_t m_obj_flag = 0; - std::unordered_map m_pending_outputs; - std::vector> m_evaluators; + std::unordered_map m_pending_outputs; + std::vector>> m_evaluators; bool m_need_to_add_callbacks = false; + KNITROResult m_result; bool m_is_dirty = true; int m_solve_status = 0; @@ -539,8 +544,8 @@ class KNITROModel : public OnesideLinearConstraintMixin, void _reset_objective(); void _add_graph(ExpressionGraph &graph); void _add_callbacks(); - void _add_constraint_callback(ExpressionGraph *graph, const KNITROPendingOutputs &outputs); - void _add_objective_callback(ExpressionGraph *graph, const KNITROPendingOutputs &outputs); + void _add_constraint_callback(ExpressionGraph *graph, const Outputs &outputs); + void _add_objective_callback(ExpressionGraph *graph, const Outputs &outputs); void _update(); void _pre_solve(); void _solve(); @@ -609,7 +614,7 @@ class KNITROModel : public OnesideLinearConstraintMixin, } template - void _register_callback(KNITROADEvaluator *evaluator, const F f, const G g, const H h) + void _register_callback(CallbackEvaluator *evaluator, const F f, const G g, const H h) { CB_context *cb = nullptr; auto p = evaluator->get_callback_pattern(); @@ -633,7 +638,7 @@ class KNITROModel : public OnesideLinearConstraintMixin, const std::vector cons, const T &trace, const F f, const G g, const H h) { - auto evaluator_ptr = std::make_unique(); + auto evaluator_ptr = std::make_unique>(); auto *evaluator = evaluator_ptr.get(); evaluator->fun = trace(graph); evaluator->fun_rows = rows; diff --git a/lib/knitro_model.cpp b/lib/knitro_model.cpp index 31355ec3..c466a66a 100644 --- a/lib/knitro_model.cpp +++ b/lib/knitro_model.cpp @@ -336,9 +336,9 @@ ConstraintIndex KNITROModel::add_single_nl_constraint(ExpressionGraph &graph, _add_graph(graph); graph.add_constraint_output(result); size_t i = graph.m_constraint_outputs.size() - 1; - m_pending_outputs[&graph].m_con_idxs.push_back(i); + m_pending_outputs[&graph].con_idxs.push_back(i); auto setter = [this, &graph](const ConstraintIndex &constraint) { - m_pending_outputs[&graph].m_cons.push_back(constraint); + m_pending_outputs[&graph].cons.push_back(constraint); m_need_to_add_callbacks = true; }; return _add_constraint_impl(ConstraintType::KNITRO_NL, interval, name, &n_nlcons, setter); @@ -419,7 +419,7 @@ void KNITROModel::_set_second_order_cone_constraint(const ConstraintIndex &const indexVars.data(), coefs.data()); check_error(error); - m_aux_cons[indexCon] = indexCon0; + m_soc_aux_cons[indexCon] = indexCon0; } void KNITROModel::_set_second_order_cone_constraint_rotated(const ConstraintIndex &constraint, @@ -463,7 +463,7 @@ void KNITROModel::_set_second_order_cone_constraint_rotated(const ConstraintInde error = knitro::KN_add_con_quadratic_term(m_kc.get(), indexCon, indexVar0, indexVar1, 2.0); check_error(error); - m_aux_cons[indexCon] = std::make_pair(indexCon0, indexCon1); + m_soc_aux_cons[indexCon] = std::make_pair(indexCon0, indexCon1); } void KNITROModel::delete_constraint(ConstraintIndex constraint) @@ -495,8 +495,8 @@ void KNITROModel::delete_constraint(ConstraintIndex constraint) break; } - auto it = m_aux_cons.find(indexCon); - if (it != m_aux_cons.end()) + auto it = m_soc_aux_cons.find(indexCon); + if (it != m_soc_aux_cons.end()) { std::vector aux_cons; if (std::holds_alternative(it->second)) @@ -518,7 +518,7 @@ void KNITROModel::delete_constraint(ConstraintIndex constraint) check_error(error); } - m_aux_cons.erase(it); + m_soc_aux_cons.erase(it); } m_is_dirty = true; @@ -703,7 +703,7 @@ void KNITROModel::add_single_nl_objective(ExpressionGraph &graph, const Expressi _add_graph(graph); graph.add_objective_output(result); size_t i = graph.m_objective_outputs.size() - 1; - m_pending_outputs[&graph].m_obj_idxs.push_back(i); + m_pending_outputs[&graph].obj_idxs.push_back(i); m_need_to_add_callbacks = true; m_obj_flag |= OBJ_NONLINEAR; m_is_dirty = true; @@ -714,7 +714,7 @@ void KNITROModel::_add_graph(ExpressionGraph &graph) { if (m_pending_outputs.find(&graph) == m_pending_outputs.end()) { - m_pending_outputs[&graph] = KNITROPendingOutputs(); + m_pending_outputs[&graph] = Outputs(); } } @@ -743,7 +743,7 @@ void KNITROModel::_reset_objective() check_error(error); for (auto &[graph, outputs] : m_pending_outputs) { - outputs.m_obj_idxs.clear(); + outputs.obj_idxs.clear(); } } m_obj_flag = 0; @@ -811,54 +811,54 @@ double KNITROModel::get_obj_value() } void KNITROModel::_add_constraint_callback(ExpressionGraph *graph, - const KNITROPendingOutputs &outputs) + const Outputs &outputs) { auto f = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, void *data) -> int { - KNITROADEvaluator *evaluator = static_cast(data); + auto *evaluator = static_cast *>(data); evaluator->eval_fun(req->x, res->c); return 0; }; auto g = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, void *data) -> int { - KNITROADEvaluator *evaluator = static_cast(data); + auto *evaluator = static_cast *>(data); evaluator->eval_jac(req->x, res->jac); return 0; }; auto h = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, void *data) -> int { - KNITROADEvaluator *evaluator = static_cast(data); + auto *evaluator = static_cast *>(data); evaluator->eval_hess(req->x, req->lambda, res->hess); return 0; }; auto trace = cppad_trace_graph_constraints; - _add_callback_impl(*graph, outputs.m_con_idxs, outputs.m_cons, trace, f, g, h); + _add_callback_impl(*graph, outputs.con_idxs, outputs.cons, trace, f, g, h); } void KNITROModel::_add_objective_callback(ExpressionGraph *graph, - const KNITROPendingOutputs &outputs) + const Outputs &outputs) { auto f = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, void *data) -> int { - KNITROADEvaluator *evaluator = static_cast(data); + auto *evaluator = static_cast *>(data); res->obj[0] = 0.0; evaluator->eval_fun(req->x, res->obj, true); return 0; }; auto g = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, void *data) -> int { - KNITROADEvaluator *evaluator = static_cast(data); + auto *evaluator = static_cast *>(data); evaluator->eval_jac(req->x, res->objGrad); return 0; }; auto h = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, void *data) -> int { - KNITROADEvaluator *evaluator = static_cast(data); + auto *evaluator = static_cast *>(data); evaluator->eval_hess(req->x, req->sigma, res->hess, true); return 0; }; auto trace = cppad_trace_graph_objective; - _add_callback_impl(*graph, outputs.m_obj_idxs, {}, trace, f, g, h); + _add_callback_impl(*graph, outputs.obj_idxs, {}, trace, f, g, h); } void KNITROModel::_add_callbacks() @@ -870,12 +870,12 @@ void KNITROModel::_add_callbacks() for (const auto &[graph, outputs] : m_pending_outputs) { - if (graph->has_constraint_output() && !outputs.m_con_idxs.empty()) + if (graph->has_constraint_output() && !outputs.con_idxs.empty()) { _add_constraint_callback(graph, outputs); } - if (graph->has_objective_output() && !outputs.m_obj_idxs.empty()) + if (graph->has_objective_output() && !outputs.obj_idxs.empty()) { _add_objective_callback(graph, outputs); } diff --git a/lib/knitro_model_ext.cpp b/lib/knitro_model_ext.cpp index 55c17582..362176d6 100644 --- a/lib/knitro_model_ext.cpp +++ b/lib/knitro_model_ext.cpp @@ -21,15 +21,19 @@ NB_MODULE(knitro_model_ext, m) #define BIND_F(f) .def(#f, &KNITROModel::f) nb::class_(m, "RawModel") .def(nb::init<>()) - // clang-format off + + // clang-format off BIND_F(init) BIND_F(close) BIND_F(get_infinity) // clang-format on + .def_ro("n_vars", &KNITROModel::n_vars) .def_ro("n_cons", &KNITROModel::n_cons) .def_ro("n_lincons", &KNITROModel::n_lincons) .def_ro("n_quadcons", &KNITROModel::n_quadcons) + .def_ro("n_coniccons", &KNITROModel::n_coniccons) + .def_ro("n_nlcons", &KNITROModel::n_nlcons) .def("add_variable", &KNITROModel::add_variable, nb::arg("domain") = VariableDomain::Continuous, nb::arg("lb") = -KN_INFINITY, @@ -40,18 +44,14 @@ NB_MODULE(knitro_model_ext, m) BIND_F(get_variable_ub) BIND_F(set_variable_lb) BIND_F(set_variable_ub) - // clang-format on - .def("set_variable_bounds", &KNITROModel::set_variable_bounds, nb::arg("variable"), - nb::arg("lb"), nb::arg("ub")) - - // clang-format off - BIND_F(set_variable_start) + BIND_F(set_variable_bounds) + BIND_F(set_variable_start) BIND_F(get_variable_name) BIND_F(set_variable_name) BIND_F(set_variable_domain) + BIND_F(get_variable_rc) + BIND_F(delete_variable) // clang-format on - .def("get_variable_rc", &KNITROModel::get_variable_rc, nb::arg("variable")) - .def("delete_variable", &KNITROModel::delete_variable, nb::arg("variable")) .def("get_value", &KNITROModel::get_variable_value) .def("get_value", @@ -123,7 +123,9 @@ NB_MODULE(knitro_model_ext, m) .def("_add_single_nl_constraint", &KNITROModel::add_single_nl_constraint_from_comparison, nb::arg("graph"), nb::arg("expr"), nb::arg("name") = "") - .def("delete_constraint", &KNITROModel::delete_constraint, nb::arg("constraint")) + // clang-format off + BIND_F(delete_constraint) + // clang-format on .def("set_objective", nb::overload_cast( @@ -212,25 +214,6 @@ NB_MODULE(knitro_model_ext, m) }, nb::arg("param_id")) - .def("get_value", &KNITROModel::get_variable_value) - .def("get_value", - nb::overload_cast(&KNITROModel::get_expression_value)) - .def("get_value", - nb::overload_cast(&KNITROModel::get_expression_value)) - .def("get_value", - nb::overload_cast(&KNITROModel::get_expression_value)) - - .def("pprint", &KNITROModel::pprint_variable) - .def("pprint", - nb::overload_cast(&KNITROModel::pprint_expression), - nb::arg("expr"), nb::arg("precision") = 4) - .def("pprint", - nb::overload_cast( - &KNITROModel::pprint_expression), - nb::arg("expr"), nb::arg("precision") = 4) - .def("pprint", nb::overload_cast(&KNITROModel::pprint_expression), - nb::arg("expr"), nb::arg("precision") = 4) - .def_rw("m_is_dirty", &KNITROModel::m_is_dirty) .def_ro("m_solve_status", &KNITROModel::m_solve_status); diff --git a/src/pyoptinterface/_src/knitro.py b/src/pyoptinterface/_src/knitro.py index 8dd04fd9..6fe42496 100644 --- a/src/pyoptinterface/_src/knitro.py +++ b/src/pyoptinterface/_src/knitro.py @@ -155,6 +155,8 @@ def autoload_library(): def _termination_status_knitro(model: "Model"): + if model.m_is_dirty: + return TerminationStatusCode.OPTIMIZE_NOT_CALLED status_code = model.m_solve_status for ts, rs in _RAW_STATUS_STRINGS: if status_code == rs: @@ -404,7 +406,7 @@ def e(attribute): ) def is_empty(self): - return not self or self.m_is_dirty + return self.n_vars == 0 Model.add_variables = make_variable_tupledict From bb92a262e477657a7ae026eb4d4acdee3e9d81b2 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Sun, 8 Feb 2026 12:23:15 -0500 Subject: [PATCH 09/23] clean up. --- lib/knitro_model_ext_constants.cpp | 7 -- src/pyoptinterface/_src/knitro.py | 122 +++++++++++++++++++---------- 2 files changed, 82 insertions(+), 47 deletions(-) diff --git a/lib/knitro_model_ext_constants.cpp b/lib/knitro_model_ext_constants.cpp index de3720a3..9bdfb787 100644 --- a/lib/knitro_model_ext_constants.cpp +++ b/lib/knitro_model_ext_constants.cpp @@ -112,11 +112,4 @@ void bind_knitro_constants(nb::module_ &m) KN.attr("PARAMTYPE_INTEGER") = KN_PARAMTYPE_INTEGER; KN.attr("PARAMTYPE_FLOAT") = KN_PARAMTYPE_FLOAT; KN.attr("PARAMTYPE_STRING") = KN_PARAMTYPE_STRING; - - // Common parameters (selection from knitro.h) - KN.attr("PARAM_ALG") = KN_PARAM_ALG; - KN.attr("PARAM_HONORBNDS") = KN_PARAM_HONORBNDS; - KN.attr("PARAM_MAXIT") = KN_PARAM_MAXIT; - KN.attr("PARAM_OUTLEV") = KN_PARAM_OUTLEV; - KN.attr("PARAM_OUTMODE") = KN_PARAM_OUTMODE; } diff --git a/src/pyoptinterface/_src/knitro.py b/src/pyoptinterface/_src/knitro.py index 6fe42496..5667fcc8 100644 --- a/src/pyoptinterface/_src/knitro.py +++ b/src/pyoptinterface/_src/knitro.py @@ -1,38 +1,38 @@ +import logging import os import platform -from pathlib import Path import re -import logging +from pathlib import Path from typing import Tuple, Union, overload -from .knitro_model_ext import RawModel, KN, load_library -from .matrix import add_matrix_constraints +from .aml import make_variable_ndarray, make_variable_tupledict from .attributes import ( - VariableAttribute, ConstraintAttribute, ModelAttribute, ResultStatusCode, TerminationStatusCode, + VariableAttribute, ) +from .comparison_constraint import ComparisonConstraint from .core_ext import ( - VariableIndex, ConstraintIndex, - ConstraintType, ConstraintSense, + ConstraintType, + ExprBuilder, ScalarAffineFunction, ScalarQuadraticFunction, - ExprBuilder, + VariableIndex, ) -from .comparison_constraint import ComparisonConstraint +from .knitro_model_ext import KN, RawModel, load_library +from .matrix import add_matrix_constraints +from .nlexpr_ext import ExpressionGraph, ExpressionHandle +from .nlfunc import ExpressionGraphContext, convert_to_expressionhandle from .solver_common import ( - _get_model_attribute, - _set_model_attribute, _get_entity_attribute, + _get_model_attribute, _set_entity_attribute, + _set_model_attribute, ) -from .aml import make_variable_ndarray, make_variable_tupledict -from .nlexpr_ext import ExpressionHandle, ExpressionGraph -from .nlfunc import ExpressionGraphContext, convert_to_expressionhandle def detected_libraries(): @@ -64,6 +64,18 @@ def detected_libraries(): if match: libs.append(str(path)) + try: + import knitro + + dir = Path(knitro.__path__[0]) + dir = dir / "lib" + for path in dir.glob(suffix_pattern): + match = re.match(libname_pattern, path.name) + if match: + libs.append(str(path)) + except ImportError: + pass + # Default library names default_libname = { "Linux": ["libknitro.so"], @@ -94,7 +106,7 @@ def autoload_library(): VariableAttribute.LowerBound: lambda model, v: model.get_variable_lb(v), VariableAttribute.UpperBound: lambda model, v: model.get_variable_ub(v), VariableAttribute.Name: lambda model, v: model.get_variable_name(v), - VariableAttribute.ReducedCost: lambda model, v: model.get_variable_rc(v) + VariableAttribute.ReducedCost: lambda model, v: model.get_variable_rc(v), } variable_attribute_set_func_map = { @@ -116,8 +128,7 @@ def autoload_library(): ConstraintAttribute.Name: lambda model, c, x: model.set_constraint_name(c, x), } -# Status code mapping -_RAW_STATUS_STRINGS = [ # TerminationStatus, RawStatusCode +_RAW_STATUS_STRINGS = [ (TerminationStatusCode.OPTIMAL, KN.RC_OPTIMAL), (TerminationStatusCode.OPTIMAL, KN.RC_OPTIMAL_OR_SATISFACTORY), (TerminationStatusCode.LOCALLY_SOLVED, KN.RC_NEAR_OPT), @@ -166,20 +177,43 @@ def _termination_status_knitro(model: "Model"): def _result_status_knitro(model: "Model"): status_code = model.m_solve_status - if status_code == KN.RC_OPTIMAL or status_code == KN.RC_OPTIMAL_OR_SATISFACTORY: - return ResultStatusCode.FEASIBLE_POINT - elif status_code in [KN.RC_NEAR_OPT, KN.RC_FEAS_XTOL, KN.RC_FEAS_NO_IMPROVE, - KN.RC_FEAS_FTOL, KN.RC_FEAS_BEST, KN.RC_FEAS_MULTISTART]: - return ResultStatusCode.FEASIBLE_POINT - elif status_code in [KN.RC_INFEASIBLE, KN.RC_INFEAS_XTOL, KN.RC_INFEAS_NO_IMPROVE, - KN.RC_INFEAS_MULTISTART, KN.RC_INFEAS_CON_BOUNDS, KN.RC_INFEAS_VAR_BOUNDS]: - return ResultStatusCode.INFEASIBLE_POINT - elif status_code in [KN.RC_ITER_LIMIT_FEAS, KN.RC_TIME_LIMIT_FEAS, KN.RC_FEVAL_LIMIT_FEAS, - KN.RC_MIP_EXH_FEAS, KN.RC_MIP_TERM_FEAS, KN.RC_MIP_SOLVE_LIMIT_FEAS, - KN.RC_MIP_NODE_LIMIT_FEAS]: + + feasible = { + KN.RC_OPTIMAL, + KN.RC_OPTIMAL_OR_SATISFACTORY, + KN.RC_NEAR_OPT, + KN.RC_FEAS_XTOL, + KN.RC_FEAS_NO_IMPROVE, + KN.RC_FEAS_FTOL, + KN.RC_FEAS_BEST, + KN.RC_FEAS_MULTISTART, + KN.RC_ITER_LIMIT_FEAS, + KN.RC_TIME_LIMIT_FEAS, + KN.RC_FEVAL_LIMIT_FEAS, + KN.RC_MIP_EXH_FEAS, + KN.RC_MIP_TERM_FEAS, + KN.RC_MIP_SOLVE_LIMIT_FEAS, + KN.RC_MIP_NODE_LIMIT_FEAS, + } + + infeasible = { + KN.RC_INFEASIBLE, + KN.RC_INFEAS_XTOL, + KN.RC_INFEAS_NO_IMPROVE, + KN.RC_INFEAS_MULTISTART, + KN.RC_INFEAS_CON_BOUNDS, + KN.RC_INFEAS_VAR_BOUNDS, + KN.RC_ITER_LIMIT_INFEAS, + KN.RC_TIME_LIMIT_INFEAS, + KN.RC_FEVAL_LIMIT_INFEAS, + KN.RC_MIP_EXH_INFEAS, + KN.RC_MIP_SOLVE_LIMIT_INFEAS, + KN.RC_MIP_NODE_LIMIT_INFEAS, + } + + if status_code in feasible: return ResultStatusCode.FEASIBLE_POINT - elif status_code in [KN.RC_ITER_LIMIT_INFEAS, KN.RC_TIME_LIMIT_INFEAS, KN.RC_FEVAL_LIMIT_INFEAS, - KN.RC_MIP_EXH_INFEAS, KN.RC_MIP_SOLVE_LIMIT_INFEAS, KN.RC_MIP_NODE_LIMIT_INFEAS]: + if status_code in infeasible: return ResultStatusCode.INFEASIBLE_POINT return ResultStatusCode.NO_SOLUTION @@ -187,7 +221,9 @@ def _result_status_knitro(model: "Model"): model_attribute_get_func_map = { ModelAttribute.ObjectiveValue: lambda model: model.get_obj_value(), ModelAttribute.TerminationStatus: _termination_status_knitro, - ModelAttribute.RawStatusString: lambda model: f"KNITRO status code: {model.m_solve_status}", + ModelAttribute.RawStatusString: lambda model: ( + f"KNITRO status code: {model.m_solve_status}" + ), ModelAttribute.DualStatus: _result_status_knitro, ModelAttribute.PrimalStatus: _result_status_knitro, } @@ -203,14 +239,18 @@ def __init__(self): self.graph_map: dict[ExpressionGraph, int] = {} @staticmethod - def supports_variable_attribute(attribute: VariableAttribute, setable: bool = False) -> bool: + def supports_variable_attribute( + attribute: VariableAttribute, setable: bool = False + ) -> bool: if setable: return attribute in variable_attribute_set_func_map else: return attribute in variable_attribute_get_func_map @staticmethod - def supports_constraint_attribute(attribute: ConstraintAttribute, setable: bool = False) -> bool: + def supports_constraint_attribute( + attribute: ConstraintAttribute, setable: bool = False + ) -> bool: if setable: return attribute in constraint_attribute_set_func_map else: @@ -219,13 +259,19 @@ def supports_constraint_attribute(attribute: ConstraintAttribute, setable: bool def number_of_variables(self): return self.n_vars - def number_of_constraints(self, constraint_type: Union[ConstraintType, None] = None): + def number_of_constraints( + self, constraint_type: Union[ConstraintType, None] = None + ): if constraint_type is None: return self.n_cons elif constraint_type == ConstraintType.Linear: return self.n_lincons elif constraint_type == ConstraintType.Quadratic: return self.n_quadcons + elif constraint_type == ConstraintType.Cone: + return self.n_coniccons + elif constraint_type == ConstraintType.KNITRO_NL: + return self.n_nlcons else: raise ValueError(f"Unknown constraint type: {constraint_type}") @@ -334,9 +380,7 @@ def get_model_attribute(self, attr: ModelAttribute): def e(attribute): raise ValueError(f"Unknown model attribute to get: {attribute}") - return _get_model_attribute( - self, attr, model_attribute_get_func_map, {}, e - ) + return _get_model_attribute(self, attr, model_attribute_get_func_map, {}, e) def set_model_attribute(self, attr: ModelAttribute, value): def e(attribute): @@ -344,9 +388,7 @@ def e(attribute): _set_model_attribute(self, attr, value, {}, {}, e) - def get_variable_attribute( - self, variable: VariableIndex, attr: VariableAttribute - ): + def get_variable_attribute(self, variable: VariableIndex, attr: VariableAttribute): def e(attribute): raise ValueError(f"Unknown variable attribute to get: {attribute}") From 46be0f9b2651b3e6306f6164dc3fb781f3960180 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Sun, 8 Feb 2026 13:07:22 -0500 Subject: [PATCH 10/23] add doc --- docs/source/api/pyoptinterface.knitro.rst | 8 + docs/source/attribute/knitro.md | 122 ++++++++ docs/source/getting_started.md | 25 +- docs/source/knitro.md | 118 ++++++++ include/pyoptinterface/knitro_model.hpp | 2 + lib/knitro_model.cpp | 26 ++ lib/knitro_model_ext.cpp | 2 + lib/knitro_model_ext_constants.cpp | 347 ++++++++++++++++++++++ src/pyoptinterface/_src/knitro.py | 21 +- 9 files changed, 666 insertions(+), 5 deletions(-) create mode 100644 docs/source/api/pyoptinterface.knitro.rst create mode 100644 docs/source/attribute/knitro.md create mode 100644 docs/source/knitro.md diff --git a/docs/source/api/pyoptinterface.knitro.rst b/docs/source/api/pyoptinterface.knitro.rst new file mode 100644 index 00000000..d382abf5 --- /dev/null +++ b/docs/source/api/pyoptinterface.knitro.rst @@ -0,0 +1,8 @@ +pyoptinterface.knitro package +==================================== + +.. automodule:: pyoptinterface.knitro + :members: + :inherited-members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/attribute/knitro.md b/docs/source/attribute/knitro.md new file mode 100644 index 00000000..67e8fefb --- /dev/null +++ b/docs/source/attribute/knitro.md @@ -0,0 +1,122 @@ +### Supported [model attribute](#pyoptinterface.ModelAttribute) + +:::{list-table} +:header-rows: 1 + +* - Attribute + - Get + - Set +* - Name + - ❌ + - ❌ +* - ObjectiveSense + - ✅ + - ✅ +* - DualStatus + - ✅ + - ❌ +* - PrimalStatus + - ✅ + - ❌ +* - RawStatusString + - ✅ + - ❌ +* - TerminationStatus + - ✅ + - ❌ +* - BarrierIterations + - ❌ + - ❌ +* - DualObjectiveValue + - ❌ + - ❌ +* - NodeCount + - ❌ + - ❌ +* - NumberOfThreads + - ✅ + - ✅ +* - ObjectiveBound + - ❌ + - ❌ +* - ObjectiveValue + - ✅ + - ❌ +* - RelativeGap + - ❌ + - ❌ +* - Silent + - ❌ + - ✅ +* - SimplexIterations + - ❌ + - ❌ +* - SolverName + - ❌ + - ❌ +* - SolverVersion + - ❌ + - ❌ +* - SolveTimeSec + - ❌ + - ❌ +* - TimeLimitSec + - ✅ + - ✅ +::: + +### Supported [variable attribute](#pyoptinterface.VariableAttribute) + +:::{list-table} +:header-rows: 1 + +* - Attribute + - Get + - Set +* - Value + - ✅ + - ❌ +* - LowerBound + - ✅ + - ✅ +* - UpperBound + - ✅ + - ✅ +* - Domain + - ❌ + - ✅ +* - PrimalStart + - ❌ + - ✅ +* - Name + - ✅ + - ✅ +* - IISLowerBound + - ❌ + - ❌ +* - IISUpperBound + - ❌ + - ❌ +* - ReducedCost + - ✅ + - ❌ +::: + +### Supported [constraint attribute](#pyoptinterface.ConstraintAttribute) + +:::{list-table} +:header-rows: 1 + +* - Attribute + - Get + - Set +* - Name + - ✅ + - ✅ +* - Primal + - ✅ + - ❌ +* - Dual + - ✅ + - ❌ +::: diff --git a/docs/source/getting_started.md b/docs/source/getting_started.md index 319c7497..a7862aae 100644 --- a/docs/source/getting_started.md +++ b/docs/source/getting_started.md @@ -82,7 +82,7 @@ The typical paths where the dynamic library of optimizers are located are as fol - `/opt/gurobi1100/macos_universal2/lib` - `/opt/gurobi1100/macos_universal2/lib` * - Xpress - - `C:\xpressmp\bin` + - `C:\xpressmp\bin` - `/opt/xpressmp/lib` - `/Applications/FICO Xpress/xpressmp/lib` - `/Applications/FICO Xpress/xpressmp/lib` @@ -101,7 +101,11 @@ The typical paths where the dynamic library of optimizers are located are as fol - `/opt/highs/lib` - `/opt/highs/lib` - `/opt/highs/lib` - +* - KNITRO + - `C:\Program Files\Artelys\KNITRO 16.0\lib` + - `/opt/knitro/16.0/lib` + - `/opt/knitro/16.0/lib` + - `/opt/knitro/16.0/lib` ::: ### Gurobi @@ -158,6 +162,15 @@ For Ipopt, the automatic detection looks for the following things in order: We recommend using the official binary from [GitHub](https://github.com/coin-or/Ipopt/releases) if you work on Windows, since compiling Ipopt on Windows from source is not an easy task. +### KNITRO + +The currently supported version is **16.0.x**. Other versions may work but are not tested. + +For KNITRO, the automatic detection looks for the following things in order: +1. The environment variable `KNITRODIR` set by the installer of KNITRO +2. `knitro.dll`/`libknitro.so`/`libknitro.dylib` in the system loadable path +3. The installation of `knitro` PyPI package. + ## Manually specifying the path of the dynamic library of optimizer If the automatic detection fails or you want to use the optimizer in a customized location, you can manually specify the path of the dynamic library of the optimizer. @@ -211,7 +224,7 @@ The typical paths where the dynamic library of optimizers are located are as fol - `/opt/copt72/lib/libcopt.dylib` - `/opt/copt72/lib/libcopt.dylib` * - Xpress - - `C:\xpressmp\bin\xprs.dll` + - `C:\xpressmp\bin\xprs.dll` - `/opt/xpressmp/lib/libxprs.so` - `/Applications/FICO Xpress/xpressmp/lib/libxprs.dylib` - `/Applications/FICO Xpress/xpressmp/lib/libxprs.dylib` @@ -225,7 +238,11 @@ The typical paths where the dynamic library of optimizers are located are as fol - `/opt/highs/lib/libhighs.so` - `/opt/highs/lib/libhighs.dylib` - `/opt/highs/lib/libhighs.dylib` - +* - KNITRO + - `C:\Program Files\Artelys\KNITRO 16.0\lib\knitro.dll` + - `/opt/knitro/16.0/lib/libknitro.so` + - `/opt/knitro/16.0/lib/libknitro.dylib` + - `/opt/knitro/16.0/lib/libknitro.dylib` ::: ## Let's build a simple model and solve it diff --git a/docs/source/knitro.md b/docs/source/knitro.md new file mode 100644 index 00000000..77f20670 --- /dev/null +++ b/docs/source/knitro.md @@ -0,0 +1,118 @@ +# KNITRO + +## Initial setup + +```python +from pyoptinterface import knitro + +model = knitro.Model() +``` + +You need to follow the instructions in [Getting Started](getting_started.md#knitro) to set up the optimizer correctly. + +## The capability of `knitro.Model` + +### Supported constraints + +:::{list-table} +:header-rows: 1 +* - Constraint + - Supported +* - + - ✅ +* - + - ✅ +* - + - ✅ +* - + - ❌ +* - + - ❌ +* - + - ✅ +::: + +```{include} attribute/knitro.md +``` + +## Solver-specific operations + +### Parameters + +For [solver-specific parameters](https://www.artelys.com/app/docs/knitro/2_userGuide/knitroOptions.html), we provide `get_raw_parameter` and `set_raw_parameter` methods to get and set the parameters. + +```python +model = knitro.Model() + +# Set the value of a parameter by name +model.set_raw_parameter("algorithm", 1) +model.set_raw_parameter("feastol", 1e-8) +model.set_raw_parameter("opttol", 1e-8) + +# Set the value of a parameter by ID (using knitro.KN constants) +model.set_raw_parameter(knitro.KN.PARAM_MAXIT, 1000) +``` + +We also provide `knitro.KN` to contain common constants from the KNITRO C API. + +```python +# Using constants for parameter IDs +model.set_raw_parameter(knitro.KN.PARAM_FEASTOL, 1e-6) + +# Algorithm selection +model.set_raw_parameter(knitro.KN.PARAM_NLP_ALGORITHM, knitro.KN.NLP_ALG_BAR_DIRECT) + +# Output level +model.set_raw_parameter(knitro.KN.PARAM_OUTLEV, knitro.KN.OUTLEV_ITER) + +# MIP parameters +model.set_raw_parameter(knitro.KN.PARAM_MIP_METHOD, knitro.KN.MIP_METHOD_BB) +model.set_raw_parameter(knitro.KN.PARAM_MIP_OPTGAPREL, 1e-4) +``` + +### Variable and Constraint Properties + +Common variable and constraint properties are provided through PyOptInterface dedicated methods: + +**Variable methods:** +- **Bounds**: `set_variable_lb`, `get_variable_lb`, `set_variable_ub`, `get_variable_ub` +- **Type and name**: `set_variable_name`, `get_variable_name`, `set_variable_domain` +- **Starting point**: `set_variable_start` +- **Solution values**: `get_value`, `get_variable_rc` + +**Constraint methods:** +- **Name**: `set_constraint_name`, `get_constraint_name` +- **Solution values**: `get_constraint_primal`, `get_constraint_dual` + +**Usage examples:** + +Variable properties: +```python +# Bounds +model.set_variable_lb(variable, 0.0) +lb = model.get_variable_lb(variable) +model.set_variable_ub(variable, 10.0) +ub = model.get_variable_ub(variable) + +# Type and name +model.set_variable_name(variable, "x") +name = model.get_variable_name(variable) + +# Starting point +model.set_variable_start(variable, 1.0) + +# Solution values +value = model.get_value(variable) +rc = model.get_variable_rc(variable) +``` + +Constraint properties: +```python +# Name +model.set_constraint_name(constraint, "c1") +name = model.get_constraint_name(constraint) + +# Solution values +primal = model.get_constraint_primal(constraint) +dual = model.get_constraint_dual(constraint) +``` diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp index c4480bc0..c9d2835e 100644 --- a/include/pyoptinterface/knitro_model.hpp +++ b/include/pyoptinterface/knitro_model.hpp @@ -395,6 +395,8 @@ class KNITROModel : public OnesideLinearConstraintMixin, void set_objective(const ExprBuilder &expr, ObjectiveSense sense); void add_single_nl_objective(ExpressionGraph &graph, const ExpressionHandle &result); double get_obj_value(); + void set_obj_sense(ObjectiveSense sense); + ObjectiveSense get_obj_sense(); void optimize(); diff --git a/lib/knitro_model.cpp b/lib/knitro_model.cpp index c466a66a..9ae8e7b6 100644 --- a/lib/knitro_model.cpp +++ b/lib/knitro_model.cpp @@ -710,6 +710,32 @@ void KNITROModel::add_single_nl_objective(ExpressionGraph &graph, const Expressi m_result.is_valid = false; } +void KNITROModel::set_obj_sense(ObjectiveSense sense) +{ + int goal = knitro_obj_goal(sense); + int error = knitro::KN_set_obj_goal(m_kc.get(), goal); + check_error(error); +} + +ObjectiveSense KNITROModel::get_obj_sense() +{ + int goal; + int error = knitro::KN_get_obj_goal(m_kc.get(), &goal); + check_error(error); + if (goal == KN_OBJGOAL_MINIMIZE) + { + return ObjectiveSense::Minimize; + } + else if (goal == KN_OBJGOAL_MAXIMIZE) + { + return ObjectiveSense::Maximize; + } + else + { + throw std::runtime_error("Unknown objective goal"); + } +} + void KNITROModel::_add_graph(ExpressionGraph &graph) { if (m_pending_outputs.find(&graph) == m_pending_outputs.end()) diff --git a/lib/knitro_model_ext.cpp b/lib/knitro_model_ext.cpp index 362176d6..f67e8039 100644 --- a/lib/knitro_model_ext.cpp +++ b/lib/knitro_model_ext.cpp @@ -150,6 +150,8 @@ NB_MODULE(knitro_model_ext, m) // clang-format off BIND_F(get_obj_value) + BIND_F(set_obj_sense) + BIND_F(get_obj_sense) // clang-format on .def("optimize", &KNITROModel::optimize, nb::call_guard()) diff --git a/lib/knitro_model_ext_constants.cpp b/lib/knitro_model_ext_constants.cpp index 9bdfb787..0774a7bf 100644 --- a/lib/knitro_model_ext_constants.cpp +++ b/lib/knitro_model_ext_constants.cpp @@ -112,4 +112,351 @@ void bind_knitro_constants(nb::module_ &m) KN.attr("PARAMTYPE_INTEGER") = KN_PARAMTYPE_INTEGER; KN.attr("PARAMTYPE_FLOAT") = KN_PARAMTYPE_FLOAT; KN.attr("PARAMTYPE_STRING") = KN_PARAMTYPE_STRING; + + // Parameter IDs + KN.attr("PARAM_NEWPOINT") = KN_PARAM_NEWPOINT; + KN.attr("PARAM_HONORBNDS") = KN_PARAM_HONORBNDS; + KN.attr("PARAM_ALGORITHM") = KN_PARAM_ALGORITHM; + KN.attr("PARAM_ALG") = KN_PARAM_ALG; + KN.attr("PARAM_NLP_ALGORITHM") = KN_PARAM_NLP_ALGORITHM; + KN.attr("PARAM_BAR_MURULE") = KN_PARAM_BAR_MURULE; + KN.attr("PARAM_BAR_FEASIBLE") = KN_PARAM_BAR_FEASIBLE; + KN.attr("PARAM_GRADOPT") = KN_PARAM_GRADOPT; + KN.attr("PARAM_HESSOPT") = KN_PARAM_HESSOPT; + KN.attr("PARAM_BAR_INITPT") = KN_PARAM_BAR_INITPT; + KN.attr("PARAM_ACT_LPSOLVER") = KN_PARAM_ACT_LPSOLVER; + KN.attr("PARAM_CG_MAXIT") = KN_PARAM_CG_MAXIT; + KN.attr("PARAM_MAXIT") = KN_PARAM_MAXIT; + KN.attr("PARAM_OUTLEV") = KN_PARAM_OUTLEV; + KN.attr("PARAM_OUTMODE") = KN_PARAM_OUTMODE; + KN.attr("PARAM_SCALE") = KN_PARAM_SCALE; + KN.attr("PARAM_SOC") = KN_PARAM_SOC; + KN.attr("PARAM_DELTA") = KN_PARAM_DELTA; + KN.attr("PARAM_BAR_FEASMODETOL") = KN_PARAM_BAR_FEASMODETOL; + KN.attr("PARAM_FEASTOL") = KN_PARAM_FEASTOL; + KN.attr("PARAM_FEASTOLABS") = KN_PARAM_FEASTOLABS; + KN.attr("PARAM_MAXTIMECPU") = KN_PARAM_MAXTIMECPU; + KN.attr("PARAM_BAR_INITMU") = KN_PARAM_BAR_INITMU; + KN.attr("PARAM_OBJRANGE") = KN_PARAM_OBJRANGE; + KN.attr("PARAM_OPTTOL") = KN_PARAM_OPTTOL; + KN.attr("PARAM_OPTTOLABS") = KN_PARAM_OPTTOLABS; + KN.attr("PARAM_LINSOLVER_PIVOTTOL") = KN_PARAM_LINSOLVER_PIVOTTOL; + KN.attr("PARAM_XTOL") = KN_PARAM_XTOL; + KN.attr("PARAM_DEBUG") = KN_PARAM_DEBUG; + KN.attr("PARAM_MULTISTART") = KN_PARAM_MULTISTART; + KN.attr("PARAM_MS_ENABLE") = KN_PARAM_MS_ENABLE; + KN.attr("PARAM_MS_MAXSOLVES") = KN_PARAM_MS_MAXSOLVES; + KN.attr("PARAM_MS_MAXBNDRANGE") = KN_PARAM_MS_MAXBNDRANGE; + KN.attr("PARAM_MS_MAXTIMECPU") = KN_PARAM_MS_MAXTIMECPU; + KN.attr("PARAM_MS_MAXTIMEREAL") = KN_PARAM_MS_MAXTIMEREAL; + KN.attr("PARAM_LMSIZE") = KN_PARAM_LMSIZE; + KN.attr("PARAM_BAR_MAXCROSSIT") = KN_PARAM_BAR_MAXCROSSIT; + KN.attr("PARAM_MAXTIMEREAL") = KN_PARAM_MAXTIMEREAL; + KN.attr("PARAM_CG_PRECOND") = KN_PARAM_CG_PRECOND; + KN.attr("PARAM_BLASOPTION") = KN_PARAM_BLASOPTION; + KN.attr("PARAM_BAR_MAXREFACTOR") = KN_PARAM_BAR_MAXREFACTOR; + KN.attr("PARAM_LINESEARCH_MAXTRIALS") = KN_PARAM_LINESEARCH_MAXTRIALS; + KN.attr("PARAM_BLASOPTIONLIB") = KN_PARAM_BLASOPTIONLIB; + KN.attr("PARAM_OUTAPPEND") = KN_PARAM_OUTAPPEND; + KN.attr("PARAM_OUTDIR") = KN_PARAM_OUTDIR; + KN.attr("PARAM_CPLEXLIB") = KN_PARAM_CPLEXLIB; + KN.attr("PARAM_BAR_PENRULE") = KN_PARAM_BAR_PENRULE; + KN.attr("PARAM_BAR_PENCONS") = KN_PARAM_BAR_PENCONS; + KN.attr("PARAM_MS_NUMTOSAVE") = KN_PARAM_MS_NUMTOSAVE; + KN.attr("PARAM_MS_SAVETOL") = KN_PARAM_MS_SAVETOL; + KN.attr("PARAM_MS_TERMINATE") = KN_PARAM_MS_TERMINATE; + KN.attr("PARAM_MS_STARTPTRANGE") = KN_PARAM_MS_STARTPTRANGE; + KN.attr("PARAM_INFEASTOL") = KN_PARAM_INFEASTOL; + KN.attr("PARAM_LINSOLVER") = KN_PARAM_LINSOLVER; + KN.attr("PARAM_BAR_DIRECTINTERVAL") = KN_PARAM_BAR_DIRECTINTERVAL; + KN.attr("PARAM_PRESOLVE") = KN_PARAM_PRESOLVE; + KN.attr("PARAM_PRESOLVE_TOL") = KN_PARAM_PRESOLVE_TOL; + KN.attr("PARAM_BAR_SWITCHRULE") = KN_PARAM_BAR_SWITCHRULE; + KN.attr("PARAM_HESSIAN_NO_F") = KN_PARAM_HESSIAN_NO_F; + KN.attr("PARAM_MA_TERMINATE") = KN_PARAM_MA_TERMINATE; + KN.attr("PARAM_MA_MAXTIMECPU") = KN_PARAM_MA_MAXTIMECPU; + KN.attr("PARAM_MA_MAXTIMEREAL") = KN_PARAM_MA_MAXTIMEREAL; + KN.attr("PARAM_MS_SEED") = KN_PARAM_MS_SEED; + KN.attr("PARAM_MA_OUTSUB") = KN_PARAM_MA_OUTSUB; + KN.attr("PARAM_MS_OUTSUB") = KN_PARAM_MS_OUTSUB; + KN.attr("PARAM_XPRESSLIB") = KN_PARAM_XPRESSLIB; + KN.attr("PARAM_TUNER") = KN_PARAM_TUNER; + KN.attr("PARAM_TUNER_OPTIONSFILE") = KN_PARAM_TUNER_OPTIONSFILE; + KN.attr("PARAM_TUNER_MAXTIMECPU") = KN_PARAM_TUNER_MAXTIMECPU; + KN.attr("PARAM_TUNER_MAXTIMEREAL") = KN_PARAM_TUNER_MAXTIMEREAL; + KN.attr("PARAM_TUNER_OUTSUB") = KN_PARAM_TUNER_OUTSUB; + KN.attr("PARAM_TUNER_TERMINATE") = KN_PARAM_TUNER_TERMINATE; + KN.attr("PARAM_LINSOLVER_OOC") = KN_PARAM_LINSOLVER_OOC; + KN.attr("PARAM_BAR_RELAXCONS") = KN_PARAM_BAR_RELAXCONS; + KN.attr("PARAM_MS_DETERMINISTIC") = KN_PARAM_MS_DETERMINISTIC; + KN.attr("PARAM_BAR_REFINEMENT") = KN_PARAM_BAR_REFINEMENT; + KN.attr("PARAM_DERIVCHECK") = KN_PARAM_DERIVCHECK; + KN.attr("PARAM_DERIVCHECK_TYPE") = KN_PARAM_DERIVCHECK_TYPE; + KN.attr("PARAM_DERIVCHECK_TOL") = KN_PARAM_DERIVCHECK_TOL; + KN.attr("PARAM_MAXFEVALS") = KN_PARAM_MAXFEVALS; + KN.attr("PARAM_FSTOPVAL") = KN_PARAM_FSTOPVAL; + KN.attr("PARAM_DATACHECK") = KN_PARAM_DATACHECK; + KN.attr("PARAM_DERIVCHECK_TERMINATE") = KN_PARAM_DERIVCHECK_TERMINATE; + KN.attr("PARAM_BAR_WATCHDOG") = KN_PARAM_BAR_WATCHDOG; + KN.attr("PARAM_FTOL") = KN_PARAM_FTOL; + KN.attr("PARAM_FTOL_ITERS") = KN_PARAM_FTOL_ITERS; + KN.attr("PARAM_ACT_QPALG") = KN_PARAM_ACT_QPALG; + KN.attr("PARAM_BAR_INITPI_MPEC") = KN_PARAM_BAR_INITPI_MPEC; + KN.attr("PARAM_XTOL_ITERS") = KN_PARAM_XTOL_ITERS; + KN.attr("PARAM_LINESEARCH") = KN_PARAM_LINESEARCH; + KN.attr("PARAM_OUT_CSVINFO") = KN_PARAM_OUT_CSVINFO; + KN.attr("PARAM_INITPENALTY") = KN_PARAM_INITPENALTY; + KN.attr("PARAM_ACT_LPFEASTOL") = KN_PARAM_ACT_LPFEASTOL; + KN.attr("PARAM_CG_STOPTOL") = KN_PARAM_CG_STOPTOL; + KN.attr("PARAM_RESTARTS") = KN_PARAM_RESTARTS; + KN.attr("PARAM_RESTARTS_MAXIT") = KN_PARAM_RESTARTS_MAXIT; + KN.attr("PARAM_BAR_SLACKBOUNDPUSH") = KN_PARAM_BAR_SLACKBOUNDPUSH; + KN.attr("PARAM_CG_PMEM") = KN_PARAM_CG_PMEM; + KN.attr("PARAM_BAR_SWITCHOBJ") = KN_PARAM_BAR_SWITCHOBJ; + KN.attr("PARAM_OUTNAME") = KN_PARAM_OUTNAME; + KN.attr("PARAM_OUT_CSVNAME") = KN_PARAM_OUT_CSVNAME; + KN.attr("PARAM_ACT_PARAMETRIC") = KN_PARAM_ACT_PARAMETRIC; + KN.attr("PARAM_ACT_LPDUMPMPS") = KN_PARAM_ACT_LPDUMPMPS; + KN.attr("PARAM_ACT_LPALG") = KN_PARAM_ACT_LPALG; + KN.attr("PARAM_ACT_LPPRESOLVE") = KN_PARAM_ACT_LPPRESOLVE; + KN.attr("PARAM_ACT_LPPENALTY") = KN_PARAM_ACT_LPPENALTY; + KN.attr("PARAM_BNDRANGE") = KN_PARAM_BNDRANGE; + KN.attr("PARAM_BAR_CONIC_ENABLE") = KN_PARAM_BAR_CONIC_ENABLE; + KN.attr("PARAM_CONVEX") = KN_PARAM_CONVEX; + KN.attr("PARAM_OUT_HINTS") = KN_PARAM_OUT_HINTS; + KN.attr("PARAM_EVAL_FCGA") = KN_PARAM_EVAL_FCGA; + KN.attr("PARAM_BAR_MAXCORRECTORS") = KN_PARAM_BAR_MAXCORRECTORS; + KN.attr("PARAM_STRAT_WARM_START") = KN_PARAM_STRAT_WARM_START; + KN.attr("PARAM_FINDIFF_TERMINATE") = KN_PARAM_FINDIFF_TERMINATE; + KN.attr("PARAM_CPUPLATFORM") = KN_PARAM_CPUPLATFORM; + KN.attr("PARAM_PRESOLVE_PASSES") = KN_PARAM_PRESOLVE_PASSES; + KN.attr("PARAM_PRESOLVE_LEVEL") = KN_PARAM_PRESOLVE_LEVEL; + KN.attr("PARAM_FINDIFF_RELSTEPSIZE") = KN_PARAM_FINDIFF_RELSTEPSIZE; + KN.attr("PARAM_INFEASTOL_ITERS") = KN_PARAM_INFEASTOL_ITERS; + KN.attr("PARAM_PRESOLVEOP_TIGHTEN") = KN_PARAM_PRESOLVEOP_TIGHTEN; + KN.attr("PARAM_BAR_LINSYS") = KN_PARAM_BAR_LINSYS; + KN.attr("PARAM_PRESOLVE_INITPT") = KN_PARAM_PRESOLVE_INITPT; + KN.attr("PARAM_ACT_QPPENALTY") = KN_PARAM_ACT_QPPENALTY; + KN.attr("PARAM_BAR_LINSYS_STORAGE") = KN_PARAM_BAR_LINSYS_STORAGE; + KN.attr("PARAM_LINSOLVER_MAXITREF") = KN_PARAM_LINSOLVER_MAXITREF; + KN.attr("PARAM_BFGS_SCALING") = KN_PARAM_BFGS_SCALING; + KN.attr("PARAM_BAR_INITSHIFTTOL") = KN_PARAM_BAR_INITSHIFTTOL; + KN.attr("PARAM_NUMTHREADS") = KN_PARAM_NUMTHREADS; + KN.attr("PARAM_CONCURRENT_EVALS") = KN_PARAM_CONCURRENT_EVALS; + KN.attr("PARAM_BLAS_NUMTHREADS") = KN_PARAM_BLAS_NUMTHREADS; + KN.attr("PARAM_LINSOLVER_NUMTHREADS") = KN_PARAM_LINSOLVER_NUMTHREADS; + KN.attr("PARAM_MS_NUMTHREADS") = KN_PARAM_MS_NUMTHREADS; + KN.attr("PARAM_CONIC_NUMTHREADS") = KN_PARAM_CONIC_NUMTHREADS; + KN.attr("PARAM_NCVX_QCQP_INIT") = KN_PARAM_NCVX_QCQP_INIT; + KN.attr("PARAM_FINDIFF_ESTNOISE") = KN_PARAM_FINDIFF_ESTNOISE; + KN.attr("PARAM_FINDIFF_NUMTHREADS") = KN_PARAM_FINDIFF_NUMTHREADS; + KN.attr("PARAM_BAR_MPEC_HEURISTIC") = KN_PARAM_BAR_MPEC_HEURISTIC; + KN.attr("PARAM_PRESOLVEOP_REDUNDANT") = KN_PARAM_PRESOLVEOP_REDUNDANT; + KN.attr("PARAM_LINSOLVER_ORDERING") = KN_PARAM_LINSOLVER_ORDERING; + KN.attr("PARAM_LINSOLVER_NODEAMALG") = KN_PARAM_LINSOLVER_NODEAMALG; + KN.attr("PARAM_PRESOLVEOP_SUBSTITUTION") = KN_PARAM_PRESOLVEOP_SUBSTITUTION; + KN.attr("PARAM_PRESOLVEOP_SUBSTITUTION_TOL") = KN_PARAM_PRESOLVEOP_SUBSTITUTION_TOL; + KN.attr("PARAM_MS_INITPT_CLUSTER") = KN_PARAM_MS_INITPT_CLUSTER; + KN.attr("PARAM_SCALE_VARS") = KN_PARAM_SCALE_VARS; + KN.attr("PARAM_BAR_MAXMU") = KN_PARAM_BAR_MAXMU; + KN.attr("PARAM_BAR_GLOBALIZE") = KN_PARAM_BAR_GLOBALIZE; + KN.attr("PARAM_LINSOLVER_SCALING") = KN_PARAM_LINSOLVER_SCALING; + KN.attr("PARAM_INITPT_STRATEGY") = KN_PARAM_INITPT_STRATEGY; + KN.attr("PARAM_EVAL_COST") = KN_PARAM_EVAL_COST; + KN.attr("PARAM_MS_TERMINATERULE_TOL") = KN_PARAM_MS_TERMINATERULE_TOL; + KN.attr("PARAM_SOLTYPE") = KN_PARAM_SOLTYPE; + KN.attr("PARAM_MAXTIME") = KN_PARAM_MAXTIME; + KN.attr("PARAM_LP_ALGORITHM") = KN_PARAM_LP_ALGORITHM; + KN.attr("PARAM_LP_ALG") = KN_PARAM_LP_ALG; + KN.attr("PARAM_AL_INITPENALTY") = KN_PARAM_AL_INITPENALTY; + KN.attr("PARAM_AL_MAXPENALTY") = KN_PARAM_AL_MAXPENALTY; + KN.attr("PARAM_PRESOLVEOP_PROBING") = KN_PARAM_PRESOLVEOP_PROBING; + + // Algorithm values + KN.attr("ALG_AUTOMATIC") = KN_ALG_AUTOMATIC; + KN.attr("ALG_AUTO") = KN_ALG_AUTO; + KN.attr("ALG_BAR_DIRECT") = KN_ALG_BAR_DIRECT; + KN.attr("ALG_BAR_CG") = KN_ALG_BAR_CG; + KN.attr("ALG_ACT_CG") = KN_ALG_ACT_CG; + KN.attr("ALG_ACT_SQP") = KN_ALG_ACT_SQP; + KN.attr("ALG_MULTI") = KN_ALG_MULTI; + KN.attr("ALG_AL") = KN_ALG_AL; + + // NLP Algorithm values + KN.attr("NLP_ALG_AUTOMATIC") = KN_NLP_ALG_AUTOMATIC; + KN.attr("NLP_ALG_AUTO") = KN_NLP_ALG_AUTO; + KN.attr("NLP_ALG_BAR_DIRECT") = KN_NLP_ALG_BAR_DIRECT; + KN.attr("NLP_ALG_BAR_CG") = KN_NLP_ALG_BAR_CG; + KN.attr("NLP_ALG_ACT_CG") = KN_NLP_ALG_ACT_CG; + KN.attr("NLP_ALG_ACT_SQP") = KN_NLP_ALG_ACT_SQP; + KN.attr("NLP_ALG_MULTI") = KN_NLP_ALG_MULTI; + KN.attr("NLP_ALG_AL") = KN_NLP_ALG_AL; + + // Bar murule values + KN.attr("BAR_MURULE_AUTOMATIC") = KN_BAR_MURULE_AUTOMATIC; + KN.attr("BAR_MURULE_AUTO") = KN_BAR_MURULE_AUTO; + KN.attr("BAR_MURULE_MONOTONE") = KN_BAR_MURULE_MONOTONE; + KN.attr("BAR_MURULE_ADAPTIVE") = KN_BAR_MURULE_ADAPTIVE; + KN.attr("BAR_MURULE_PROBING") = KN_BAR_MURULE_PROBING; + KN.attr("BAR_MURULE_DAMPMPC") = KN_BAR_MURULE_DAMPMPC; + KN.attr("BAR_MURULE_FULLMPC") = KN_BAR_MURULE_FULLMPC; + KN.attr("BAR_MURULE_QUALITY") = KN_BAR_MURULE_QUALITY; + + // Gradient options + KN.attr("GRADOPT_EXACT") = KN_GRADOPT_EXACT; + KN.attr("GRADOPT_FORWARD") = KN_GRADOPT_FORWARD; + KN.attr("GRADOPT_CENTRAL") = KN_GRADOPT_CENTRAL; + KN.attr("GRADOPT_USER_FORWARD") = KN_GRADOPT_USER_FORWARD; + KN.attr("GRADOPT_USER_CENTRAL") = KN_GRADOPT_USER_CENTRAL; + + // Hessian options + KN.attr("HESSOPT_AUTO") = KN_HESSOPT_AUTO; + KN.attr("HESSOPT_EXACT") = KN_HESSOPT_EXACT; + KN.attr("HESSOPT_BFGS") = KN_HESSOPT_BFGS; + KN.attr("HESSOPT_SR1") = KN_HESSOPT_SR1; + KN.attr("HESSOPT_PRODUCT_FINDIFF") = KN_HESSOPT_PRODUCT_FINDIFF; + KN.attr("HESSOPT_PRODUCT") = KN_HESSOPT_PRODUCT; + KN.attr("HESSOPT_LBFGS") = KN_HESSOPT_LBFGS; + KN.attr("HESSOPT_GAUSS_NEWTON") = KN_HESSOPT_GAUSS_NEWTON; + + // Output level values + KN.attr("OUTLEV_NONE") = KN_OUTLEV_NONE; + KN.attr("OUTLEV_SUMMARY") = KN_OUTLEV_SUMMARY; + KN.attr("OUTLEV_ITER_10") = KN_OUTLEV_ITER_10; + KN.attr("OUTLEV_ITER") = KN_OUTLEV_ITER; + KN.attr("OUTLEV_ITER_VERBOSE") = KN_OUTLEV_ITER_VERBOSE; + KN.attr("OUTLEV_ITER_X") = KN_OUTLEV_ITER_X; + KN.attr("OUTLEV_ALL") = KN_OUTLEV_ALL; + + // Linear solver values + KN.attr("LINSOLVER_AUTO") = KN_LINSOLVER_AUTO; + KN.attr("LINSOLVER_INTERNAL") = KN_LINSOLVER_INTERNAL; + KN.attr("LINSOLVER_HYBRID") = KN_LINSOLVER_HYBRID; + KN.attr("LINSOLVER_DENSEQR") = KN_LINSOLVER_DENSEQR; + KN.attr("LINSOLVER_MA27") = KN_LINSOLVER_MA27; + KN.attr("LINSOLVER_MA57") = KN_LINSOLVER_MA57; + KN.attr("LINSOLVER_MKLPARDISO") = KN_LINSOLVER_MKLPARDISO; + KN.attr("LINSOLVER_MA97") = KN_LINSOLVER_MA97; + KN.attr("LINSOLVER_MA86") = KN_LINSOLVER_MA86; + KN.attr("LINSOLVER_APPLE") = KN_LINSOLVER_APPLE; + + // Presolve values + KN.attr("PRESOLVE_NO") = KN_PRESOLVE_NO; + KN.attr("PRESOLVE_YES") = KN_PRESOLVE_YES; + + // Derivative check values + KN.attr("DERIVCHECK_NONE") = KN_DERIVCHECK_NONE; + KN.attr("DERIVCHECK_FIRST") = KN_DERIVCHECK_FIRST; + KN.attr("DERIVCHECK_SECOND") = KN_DERIVCHECK_SECOND; + KN.attr("DERIVCHECK_ALL") = KN_DERIVCHECK_ALL; + + // Linesearch values + KN.attr("LINESEARCH_AUTO") = KN_LINESEARCH_AUTO; + KN.attr("LINESEARCH_BACKTRACK") = KN_LINESEARCH_BACKTRACK; + KN.attr("LINESEARCH_INTERPOLATE") = KN_LINESEARCH_INTERPOLATE; + KN.attr("LINESEARCH_WEAKWOLFE") = KN_LINESEARCH_WEAKWOLFE; + + // LP Algorithm values + KN.attr("LP_ALG_AUTO") = KN_LP_ALG_AUTO; + KN.attr("LP_ALG_NLPALGORITHM") = KN_LP_ALG_NLPALGORITHM; + KN.attr("LP_ALG_PRIMALSIMPLEX") = KN_LP_ALG_PRIMALSIMPLEX; + KN.attr("LP_ALG_DUALSIMPLEX") = KN_LP_ALG_DUALSIMPLEX; + KN.attr("LP_ALG_BARRIER") = KN_LP_ALG_BARRIER; + KN.attr("LP_ALG_PDLP") = KN_LP_ALG_PDLP; + + // MIP parameter IDs + KN.attr("PARAM_MIP_METHOD") = KN_PARAM_MIP_METHOD; + KN.attr("PARAM_MIP_BRANCHRULE") = KN_PARAM_MIP_BRANCHRULE; + KN.attr("PARAM_MIP_SELECTRULE") = KN_PARAM_MIP_SELECTRULE; + KN.attr("PARAM_MIP_OPTGAPABS") = KN_PARAM_MIP_OPTGAPABS; + KN.attr("PARAM_MIP_OPTGAPREL") = KN_PARAM_MIP_OPTGAPREL; + KN.attr("PARAM_MIP_MAXTIMECPU") = KN_PARAM_MIP_MAXTIMECPU; + KN.attr("PARAM_MIP_MAXTIMEREAL") = KN_PARAM_MIP_MAXTIMEREAL; + KN.attr("PARAM_MIP_MAXSOLVES") = KN_PARAM_MIP_MAXSOLVES; + KN.attr("PARAM_MIP_INTEGERTOL") = KN_PARAM_MIP_INTEGERTOL; + KN.attr("PARAM_MIP_OUTLEVEL") = KN_PARAM_MIP_OUTLEVEL; + KN.attr("PARAM_MIP_OUTINTERVAL") = KN_PARAM_MIP_OUTINTERVAL; + KN.attr("PARAM_MIP_OUTSUB") = KN_PARAM_MIP_OUTSUB; + KN.attr("PARAM_MIP_DEBUG") = KN_PARAM_MIP_DEBUG; + KN.attr("PARAM_MIP_IMPLICATIONS") = KN_PARAM_MIP_IMPLICATIONS; + KN.attr("PARAM_MIP_GUB_BRANCH") = KN_PARAM_MIP_GUB_BRANCH; + KN.attr("PARAM_MIP_KNAPSACK") = KN_PARAM_MIP_KNAPSACK; + KN.attr("PARAM_MIP_ROUNDING") = KN_PARAM_MIP_ROUNDING; + KN.attr("PARAM_MIP_ROOT_NLPALG") = KN_PARAM_MIP_ROOT_NLPALG; + KN.attr("PARAM_MIP_TERMINATE") = KN_PARAM_MIP_TERMINATE; + KN.attr("PARAM_MIP_MAXNODES") = KN_PARAM_MIP_MAXNODES; + KN.attr("PARAM_MIP_HEUR_MAXIT") = KN_PARAM_MIP_HEUR_MAXIT; + KN.attr("PARAM_MIP_PSEUDOINIT") = KN_PARAM_MIP_PSEUDOINIT; + KN.attr("PARAM_MIP_STRONG_MAXIT") = KN_PARAM_MIP_STRONG_MAXIT; + KN.attr("PARAM_MIP_STRONG_CANDLIM") = KN_PARAM_MIP_STRONG_CANDLIM; + KN.attr("PARAM_MIP_STRONG_LEVEL") = KN_PARAM_MIP_STRONG_LEVEL; + KN.attr("PARAM_MIP_INTVAR_STRATEGY") = KN_PARAM_MIP_INTVAR_STRATEGY; + KN.attr("PARAM_MIP_RELAXABLE") = KN_PARAM_MIP_RELAXABLE; + KN.attr("PARAM_MIP_NODE_NLPALG") = KN_PARAM_MIP_NODE_NLPALG; + KN.attr("PARAM_MIP_HEUR_TERMINATE") = KN_PARAM_MIP_HEUR_TERMINATE; + KN.attr("PARAM_MIP_SELECTDIR") = KN_PARAM_MIP_SELECTDIR; + KN.attr("PARAM_MIP_CUTFACTOR") = KN_PARAM_MIP_CUTFACTOR; + KN.attr("PARAM_MIP_ZEROHALF") = KN_PARAM_MIP_ZEROHALF; + KN.attr("PARAM_MIP_MIR") = KN_PARAM_MIP_MIR; + KN.attr("PARAM_MIP_CLIQUE") = KN_PARAM_MIP_CLIQUE; + KN.attr("PARAM_MIP_HEUR_STRATEGY") = KN_PARAM_MIP_HEUR_STRATEGY; + KN.attr("PARAM_MIP_HEUR_FEASPUMP") = KN_PARAM_MIP_HEUR_FEASPUMP; + KN.attr("PARAM_MIP_HEUR_MPEC") = KN_PARAM_MIP_HEUR_MPEC; + KN.attr("PARAM_MIP_HEUR_DIVING") = KN_PARAM_MIP_HEUR_DIVING; + KN.attr("PARAM_MIP_CUTTINGPLANE") = KN_PARAM_MIP_CUTTINGPLANE; + KN.attr("PARAM_MIP_CUTOFF") = KN_PARAM_MIP_CUTOFF; + KN.attr("PARAM_MIP_HEUR_LNS") = KN_PARAM_MIP_HEUR_LNS; + KN.attr("PARAM_MIP_MULTISTART") = KN_PARAM_MIP_MULTISTART; + KN.attr("PARAM_MIP_LIFTPROJECT") = KN_PARAM_MIP_LIFTPROJECT; + KN.attr("PARAM_MIP_NUMTHREADS") = KN_PARAM_MIP_NUMTHREADS; + KN.attr("PARAM_MIP_HEUR_MISQP") = KN_PARAM_MIP_HEUR_MISQP; + KN.attr("PARAM_MIP_RESTART") = KN_PARAM_MIP_RESTART; + KN.attr("PARAM_MIP_GOMORY") = KN_PARAM_MIP_GOMORY; + KN.attr("PARAM_MIP_CUT_PROBING") = KN_PARAM_MIP_CUT_PROBING; + KN.attr("PARAM_MIP_CUT_FLOWCOVER") = KN_PARAM_MIP_CUT_FLOWCOVER; + KN.attr("PARAM_MIP_HEUR_LOCALSEARCH") = KN_PARAM_MIP_HEUR_LOCALSEARCH; + KN.attr("PARAM_MIP_ROOT_LPALG") = KN_PARAM_MIP_ROOT_LPALG; + KN.attr("PARAM_MIP_NODE_LPALG") = KN_PARAM_MIP_NODE_LPALG; + + // MIP method values + KN.attr("MIP_METHOD_AUTO") = KN_MIP_METHOD_AUTO; + KN.attr("MIP_METHOD_BB") = KN_MIP_METHOD_BB; + KN.attr("MIP_METHOD_HQG") = KN_MIP_METHOD_HQG; + KN.attr("MIP_METHOD_MISQP") = KN_MIP_METHOD_MISQP; + + // MIP branch rule values + KN.attr("MIP_BRANCH_AUTO") = KN_MIP_BRANCH_AUTO; + KN.attr("MIP_BRANCH_MOSTFRAC") = KN_MIP_BRANCH_MOSTFRAC; + KN.attr("MIP_BRANCH_PSEUDOCOST") = KN_MIP_BRANCH_PSEUDOCOST; + KN.attr("MIP_BRANCH_STRONG") = KN_MIP_BRANCH_STRONG; + + // MIP select rule values + KN.attr("MIP_SEL_AUTO") = KN_MIP_SEL_AUTO; + KN.attr("MIP_SEL_DEPTHFIRST") = KN_MIP_SEL_DEPTHFIRST; + KN.attr("MIP_SEL_BESTBOUND") = KN_MIP_SEL_BESTBOUND; + KN.attr("MIP_SEL_COMBO_1") = KN_MIP_SEL_COMBO_1; + + // MIP terminate values + KN.attr("MIP_TERMINATE_OPTIMAL") = KN_MIP_TERMINATE_OPTIMAL; + KN.attr("MIP_TERMINATE_FEASIBLE") = KN_MIP_TERMINATE_FEASIBLE; + + // MIP output level values + KN.attr("MIP_OUTLEVEL_NONE") = KN_MIP_OUTLEVEL_NONE; + KN.attr("MIP_OUTLEVEL_ITERS") = KN_MIP_OUTLEVEL_ITERS; + KN.attr("MIP_OUTLEVEL_ITERSTIME") = KN_MIP_OUTLEVEL_ITERSTIME; + KN.attr("MIP_OUTLEVEL_ROOT") = KN_MIP_OUTLEVEL_ROOT; + + // MIP rounding values + KN.attr("MIP_ROUND_AUTO") = KN_MIP_ROUND_AUTO; + KN.attr("MIP_ROUND_NONE") = KN_MIP_ROUND_NONE; + KN.attr("MIP_ROUND_HEURISTIC") = KN_MIP_ROUND_HEURISTIC; + KN.attr("MIP_ROUND_NLP_SOME") = KN_MIP_ROUND_NLP_SOME; + KN.attr("MIP_ROUND_NLP_ALWAYS") = KN_MIP_ROUND_NLP_ALWAYS; + + // MIP heuristic strategy values + KN.attr("MIP_HEUR_STRATEGY_AUTO") = KN_MIP_HEUR_STRATEGY_AUTO; + KN.attr("MIP_HEUR_STRATEGY_NONE") = KN_MIP_HEUR_STRATEGY_NONE; + KN.attr("MIP_HEUR_STRATEGY_BASIC") = KN_MIP_HEUR_STRATEGY_BASIC; + KN.attr("MIP_HEUR_STRATEGY_ADVANCED") = KN_MIP_HEUR_STRATEGY_ADVANCED; + KN.attr("MIP_HEUR_STRATEGY_EXTENSIVE") = KN_MIP_HEUR_STRATEGY_EXTENSIVE; } diff --git a/src/pyoptinterface/_src/knitro.py b/src/pyoptinterface/_src/knitro.py index 5667fcc8..603e2a8e 100644 --- a/src/pyoptinterface/_src/knitro.py +++ b/src/pyoptinterface/_src/knitro.py @@ -220,12 +220,22 @@ def _result_status_knitro(model: "Model"): model_attribute_get_func_map = { ModelAttribute.ObjectiveValue: lambda model: model.get_obj_value(), + ModelAttribute.ObjectiveSense: lambda model: model.get_obj_sense(), ModelAttribute.TerminationStatus: _termination_status_knitro, ModelAttribute.RawStatusString: lambda model: ( f"KNITRO status code: {model.m_solve_status}" ), ModelAttribute.DualStatus: _result_status_knitro, ModelAttribute.PrimalStatus: _result_status_knitro, + ModelAttribute.NumberOfThreads: lambda model: model.get_raw_parameter(KN.PARAM_THREADS), + ModelAttribute.TimeLimitSec: lambda model: model.get_raw_parameter(KN.PARAM_TIME_LIMIT), +} + +model_attribute_set_func_map = { + ModelAttribute.ObjectiveSense: lambda model, x: model.set_obj_sense(x), + ModelAttribute.NumberOfThreads: lambda model, x: model.set_raw_parameter(KN.PARAM_THREADS, x), + ModelAttribute.Silent: lambda model, x: model.set_raw_parameter(KN.PARAM_OUTLEV, KN.OUTLEV_NONE if x else KN.OUTLEV_ITER), + ModelAttribute.TimeLimitSec: lambda model, x: model.set_raw_parameter(KN.PARAM_TIME_LIMIT, x), } @@ -256,6 +266,15 @@ def supports_constraint_attribute( else: return attribute in constraint_attribute_get_func_map + @staticmethod + def supports_model_attribute( + attribute: ModelAttribute, setable: bool = False + ) -> bool: + if setable: + return attribute in model_attribute_set_func_map + else: + return attribute in model_attribute_get_func_map + def number_of_variables(self): return self.n_vars @@ -386,7 +405,7 @@ def set_model_attribute(self, attr: ModelAttribute, value): def e(attribute): raise ValueError(f"Unknown model attribute to set: {attribute}") - _set_model_attribute(self, attr, value, {}, {}, e) + _set_model_attribute(self, attr, value, model_attribute_set_func_map, {}, e) def get_variable_attribute(self, variable: VariableIndex, attr: VariableAttribute): def e(attribute): From 48363bcce9b9ae9a9da17ed5b658cbb9082dbb9a Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Mon, 9 Feb 2026 09:19:59 -0500 Subject: [PATCH 11/23] clean: remove unused KNITRO API. --- include/pyoptinterface/knitro_model.hpp | 14 -------------- lib/knitro_model.cpp | 25 ++++++++++++++----------- lib/knitro_model_ext.cpp | 10 +++++----- 3 files changed, 19 insertions(+), 30 deletions(-) diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp index c9d2835e..505b33d5 100644 --- a/include/pyoptinterface/knitro_model.hpp +++ b/include/pyoptinterface/knitro_model.hpp @@ -19,9 +19,6 @@ B(KN_new); \ B(KN_free); \ B(KN_update); \ - B(KN_reset_params_to_defaults); \ - B(KN_load_param_file); \ - B(KN_save_param_file); \ B(KN_get_param_id); \ B(KN_get_param_type); \ B(KN_set_int_param); \ @@ -46,7 +43,6 @@ B(KN_set_con_eqbnd); \ B(KN_get_con_lobnd); \ B(KN_get_con_upbnd); \ - B(KN_get_con_eqbnd); \ B(KN_set_con_name); \ B(KN_get_con_name); \ B(KN_set_obj_goal); \ @@ -62,14 +58,9 @@ B(KN_del_obj_quadratic_struct_all); \ B(KN_add_con_constant); \ B(KN_add_con_linear_struct); \ - B(KN_add_con_linear_struct_one); \ B(KN_add_con_linear_term); \ B(KN_add_con_quadratic_struct); \ - B(KN_add_con_quadratic_struct_one); \ B(KN_add_con_quadratic_term); \ - B(KN_add_con_L2norm); \ - B(KN_del_con_linear_struct); \ - B(KN_del_con_quadratic_struct); \ B(KN_chg_con_linear_term); \ B(KN_add_eval_callback); \ B(KN_del_obj_eval_callback_all); \ @@ -78,17 +69,12 @@ B(KN_set_cb_grad); \ B(KN_set_cb_hess); \ B(KN_solve); \ - B(KN_get_solution); \ B(KN_get_obj_value); \ B(KN_get_number_cons); \ B(KN_get_number_vars); \ - B(KN_get_var_primal_values); \ B(KN_get_var_primal_values_all); \ - B(KN_get_var_dual_values); \ B(KN_get_var_dual_values_all); \ - B(KN_get_con_values); \ B(KN_get_con_values_all); \ - B(KN_get_con_dual_values); \ B(KN_get_con_dual_values_all); namespace knitro diff --git a/lib/knitro_model.cpp b/lib/knitro_model.cpp index 9ae8e7b6..a2168214 100644 --- a/lib/knitro_model.cpp +++ b/lib/knitro_model.cpp @@ -409,14 +409,15 @@ void KNITROModel::_set_second_order_cone_constraint(const ConstraintIndex &const error = knitro::KN_add_con_quadratic_term(m_kc.get(), indexCon, indexVar0, indexVar0, 1.0); check_error(error); size_t nnz = variables.size() - 1; + std::vector indexCons(nnz, indexCon); std::vector indexVars(nnz); for (size_t i = 0; i < nnz; ++i) { indexVars[i] = _variable_index(variables[i + 1]); } std::vector coefs(nnz, -1.0); - error = knitro::KN_add_con_quadratic_struct_one(m_kc.get(), nnz, indexCon, indexVars.data(), - indexVars.data(), coefs.data()); + error = knitro::KN_add_con_quadratic_struct(m_kc.get(), nnz, indexCons.data(), indexVars.data(), + indexVars.data(), coefs.data()); check_error(error); m_soc_aux_cons[indexCon] = indexCon0; @@ -451,14 +452,15 @@ void KNITROModel::_set_second_order_cone_constraint_rotated(const ConstraintInde check_error(error); size_t nnz = variables.size() - 2; + std::vector indexCons(nnz, indexCon); std::vector indexVars(nnz); for (size_t i = 0; i < nnz; ++i) { indexVars[i] = _variable_index(variables[i + 2]); } std::vector coefs(nnz, -1.0); - error = knitro::KN_add_con_quadratic_struct_one(m_kc.get(), nnz, indexCon, indexVars.data(), - indexVars.data(), coefs.data()); + error = knitro::KN_add_con_quadratic_struct(m_kc.get(), nnz, indexCons.data(), indexVars.data(), + indexVars.data(), coefs.data()); check_error(error); error = knitro::KN_add_con_quadratic_term(m_kc.get(), indexCon, indexVar0, indexVar1, 2.0); check_error(error); @@ -636,10 +638,12 @@ void KNITROModel::_set_linear_constraint(const ConstraintIndex &constraint, KNLONG nnz = ptr_form.numnz; if (nnz > 0) { + std::vector indexCons(nnz, indexCon); KNINT *indexVars = ptr_form.index; double *coefs = ptr_form.value; - error = knitro::KN_add_con_linear_struct_one(m_kc.get(), nnz, indexCon, indexVars, coefs); + error = + knitro::KN_add_con_linear_struct(m_kc.get(), nnz, indexCons.data(), indexVars, coefs); check_error(error); } } @@ -660,11 +664,12 @@ void KNITROModel::_set_quadratic_constraint(const ConstraintIndex &constraint, KNLONG nnz = ptr_form.numnz; if (nnz > 0) { + std::vector indexCons(nnz, indexCon); KNINT *indexVars1 = ptr_form.row; KNINT *indexVars2 = ptr_form.col; double *coefs = ptr_form.value; - error = knitro::KN_add_con_quadratic_struct_one(m_kc.get(), nnz, indexCon, indexVars1, - indexVars2, coefs); + error = knitro::KN_add_con_quadratic_struct(m_kc.get(), nnz, indexCons.data(), indexVars1, + indexVars2, coefs); check_error(error); } } @@ -836,8 +841,7 @@ double KNITROModel::get_obj_value() return m_result.obj_val; } -void KNITROModel::_add_constraint_callback(ExpressionGraph *graph, - const Outputs &outputs) +void KNITROModel::_add_constraint_callback(ExpressionGraph *graph, const Outputs &outputs) { auto f = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, void *data) -> int { @@ -861,8 +865,7 @@ void KNITROModel::_add_constraint_callback(ExpressionGraph *graph, _add_callback_impl(*graph, outputs.con_idxs, outputs.cons, trace, f, g, h); } -void KNITROModel::_add_objective_callback(ExpressionGraph *graph, - const Outputs &outputs) +void KNITROModel::_add_objective_callback(ExpressionGraph *graph, const Outputs &outputs) { auto f = [](KN_context *, CB_context *, KN_eval_request *req, KN_eval_result *res, void *data) -> int { diff --git a/lib/knitro_model_ext.cpp b/lib/knitro_model_ext.cpp index f67e8039..61bd9ae3 100644 --- a/lib/knitro_model_ext.cpp +++ b/lib/knitro_model_ext.cpp @@ -22,7 +22,7 @@ NB_MODULE(knitro_model_ext, m) nb::class_(m, "RawModel") .def(nb::init<>()) - // clang-format off + // clang-format off BIND_F(init) BIND_F(close) BIND_F(get_infinity) @@ -32,8 +32,8 @@ NB_MODULE(knitro_model_ext, m) .def_ro("n_cons", &KNITROModel::n_cons) .def_ro("n_lincons", &KNITROModel::n_lincons) .def_ro("n_quadcons", &KNITROModel::n_quadcons) - .def_ro("n_coniccons", &KNITROModel::n_coniccons) - .def_ro("n_nlcons", &KNITROModel::n_nlcons) + .def_ro("n_coniccons", &KNITROModel::n_coniccons) + .def_ro("n_nlcons", &KNITROModel::n_nlcons) .def("add_variable", &KNITROModel::add_variable, nb::arg("domain") = VariableDomain::Continuous, nb::arg("lb") = -KN_INFINITY, @@ -123,9 +123,9 @@ NB_MODULE(knitro_model_ext, m) .def("_add_single_nl_constraint", &KNITROModel::add_single_nl_constraint_from_comparison, nb::arg("graph"), nb::arg("expr"), nb::arg("name") = "") - // clang-format off + // clang-format off BIND_F(delete_constraint) - // clang-format on + // clang-format on .def("set_objective", nb::overload_cast( From e119018def6bf95b51908e995effa3bfd0ada914 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Mon, 9 Feb 2026 09:23:33 -0500 Subject: [PATCH 12/23] doc: use KNITRO 15.1 --- docs/source/getting_started.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/source/getting_started.md b/docs/source/getting_started.md index a7862aae..d4f8f1b2 100644 --- a/docs/source/getting_started.md +++ b/docs/source/getting_started.md @@ -102,10 +102,10 @@ The typical paths where the dynamic library of optimizers are located are as fol - `/opt/highs/lib` - `/opt/highs/lib` * - KNITRO - - `C:\Program Files\Artelys\KNITRO 16.0\lib` - - `/opt/knitro/16.0/lib` - - `/opt/knitro/16.0/lib` - - `/opt/knitro/16.0/lib` + - `C:\Program Files\Artelys\KNITRO 15.1\lib` + - `/opt/knitro/15.1/lib` + - `/opt/knitro/15.1/lib` + - `/opt/knitro/15.1/lib` ::: ### Gurobi @@ -164,7 +164,7 @@ We recommend using the official binary from [GitHub](https://github.com/coin-or/ ### KNITRO -The currently supported version is **16.0.x**. Other versions may work but are not tested. +The currently supported version is **15.1.x**. Other versions may work but are not tested. For KNITRO, the automatic detection looks for the following things in order: 1. The environment variable `KNITRODIR` set by the installer of KNITRO @@ -239,10 +239,10 @@ The typical paths where the dynamic library of optimizers are located are as fol - `/opt/highs/lib/libhighs.dylib` - `/opt/highs/lib/libhighs.dylib` * - KNITRO - - `C:\Program Files\Artelys\KNITRO 16.0\lib\knitro.dll` - - `/opt/knitro/16.0/lib/libknitro.so` - - `/opt/knitro/16.0/lib/libknitro.dylib` - - `/opt/knitro/16.0/lib/libknitro.dylib` + - `C:\Program Files\Artelys\KNITRO 15.1\lib\knitro.dll` + - `/opt/knitro/15.1/lib/libknitro.so` + - `/opt/knitro/15.1/lib/libknitro.dylib` + - `/opt/knitro/15.1/lib/libknitro.dylib` ::: ## Let's build a simple model and solve it From 7da7b77f40b4218f248e274ef3a5299a35579858 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Mon, 9 Feb 2026 13:06:20 -0500 Subject: [PATCH 13/23] update model attributes getter and setter. --- docs/source/attribute/knitro.md | 16 +++---- include/pyoptinterface/knitro_model.hpp | 18 ++++++-- lib/knitro_model.cpp | 58 +++++++++++++++++++++++++ lib/knitro_model_ext.cpp | 7 +++ src/pyoptinterface/_src/knitro.py | 29 ++++++++----- 5 files changed, 106 insertions(+), 22 deletions(-) diff --git a/docs/source/attribute/knitro.md b/docs/source/attribute/knitro.md index 67e8fefb..0329cee2 100644 --- a/docs/source/attribute/knitro.md +++ b/docs/source/attribute/knitro.md @@ -13,7 +13,7 @@ - ✅ - ✅ * - DualStatus - - ✅ + - ❌ - ❌ * - PrimalStatus - ✅ @@ -25,25 +25,25 @@ - ✅ - ❌ * - BarrierIterations - - ❌ + - ✅ - ❌ * - DualObjectiveValue - ❌ - ❌ * - NodeCount - - ❌ + - ✅ - ❌ * - NumberOfThreads - ✅ - ✅ * - ObjectiveBound - - ❌ + - ✅ - ❌ * - ObjectiveValue - ✅ - ❌ * - RelativeGap - - ❌ + - ✅ - ❌ * - Silent - ❌ @@ -52,13 +52,13 @@ - ❌ - ❌ * - SolverName - - ❌ + - ✅ - ❌ * - SolverVersion - - ❌ + - ✅ - ❌ * - SolveTimeSec - - ❌ + - ✅ - ❌ * - TimeLimitSec - ✅ diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp index 505b33d5..e257a150 100644 --- a/include/pyoptinterface/knitro_model.hpp +++ b/include/pyoptinterface/knitro_model.hpp @@ -75,7 +75,13 @@ B(KN_get_var_primal_values_all); \ B(KN_get_var_dual_values_all); \ B(KN_get_con_values_all); \ - B(KN_get_con_dual_values_all); + B(KN_get_con_dual_values_all); \ + B(KN_get_number_iters); \ + B(KN_get_mip_number_nodes); \ + B(KN_get_mip_relaxation_bnd); \ + B(KN_get_mip_rel_gap); \ + B(KN_get_solve_time_real); \ + B(KN_get_release); namespace knitro { @@ -234,8 +240,7 @@ struct CallbackEvaluator } } - void eval_hess(const V *req_x, const V *req_w, V *res_hess, - bool aggregate = false) + void eval_hess(const V *req_x, const V *req_w, V *res_hess, bool aggregate = false) { for (size_t i = 0; i < indexVars.size(); i++) { @@ -323,6 +328,13 @@ class KNITROModel : public OnesideLinearConstraintMixin, double get_infinity() const; KNINT _variable_index(const VariableIndex &variable); KNINT _constraint_index(const ConstraintIndex &constraint); + size_t get_number_iterations(); + size_t get_mip_node_count(); + double get_obj_bound(); + double get_mip_relative_gap(); + double get_solve_time(); + std::string get_solver_name(); + std::string get_release(); VariableIndex add_variable(VariableDomain domain = VariableDomain::Continuous, double lb = -KN_INFINITY, double ub = KN_INFINITY, diff --git a/lib/knitro_model.cpp b/lib/knitro_model.cpp index a2168214..bb462f9f 100644 --- a/lib/knitro_model.cpp +++ b/lib/knitro_model.cpp @@ -87,6 +87,64 @@ double KNITROModel::get_infinity() const return KN_INFINITY; } +size_t KNITROModel::get_number_iterations() +{ + int error; + int iters; + + error = knitro::KN_get_number_iters(m_kc.get(), &iters); + check_error(error); + return static_cast(iters); +} + +size_t KNITROModel::get_mip_node_count() +{ + int error; + int nodes; + + error = knitro::KN_get_mip_number_nodes(m_kc.get(), &nodes); + check_error(error); + return static_cast(nodes); +} + +double KNITROModel::get_obj_bound() +{ + double bound; + int error = knitro::KN_get_mip_relaxation_bnd(m_kc.get(), &bound); + check_error(error); + return bound; +} + +double KNITROModel::get_mip_relative_gap() +{ + double gap; + int error = knitro::KN_get_mip_rel_gap(m_kc.get(), &gap); + check_error(error); + return gap; +} + +double KNITROModel::get_solve_time() +{ + double time; + int error = knitro::KN_get_solve_time_real(m_kc.get(), &time); + check_error(error); + return time; +} + +std::string KNITROModel::get_solver_name() +{ + return std::string("KNITRO"); +} + +std::string KNITROModel::get_release() +{ + constexpr int buf_size = 20; + char release[buf_size]; + int error = knitro::KN_get_release(buf_size, release); + check_error(error); + return std::string(release); +} + VariableIndex KNITROModel::add_variable(VariableDomain domain, double lb, double ub, const char *name) { diff --git a/lib/knitro_model_ext.cpp b/lib/knitro_model_ext.cpp index 61bd9ae3..a93835c7 100644 --- a/lib/knitro_model_ext.cpp +++ b/lib/knitro_model_ext.cpp @@ -26,6 +26,13 @@ NB_MODULE(knitro_model_ext, m) BIND_F(init) BIND_F(close) BIND_F(get_infinity) + BIND_F(get_number_iterations) + BIND_F(get_mip_node_count) + BIND_F(get_obj_bound) + BIND_F(get_mip_relative_gap) + BIND_F(get_solve_time) + BIND_F(get_solver_name) + BIND_F(get_release) // clang-format on .def_ro("n_vars", &KNITROModel::n_vars) diff --git a/src/pyoptinterface/_src/knitro.py b/src/pyoptinterface/_src/knitro.py index 603e2a8e..ddf6e1fc 100644 --- a/src/pyoptinterface/_src/knitro.py +++ b/src/pyoptinterface/_src/knitro.py @@ -131,16 +131,16 @@ def autoload_library(): _RAW_STATUS_STRINGS = [ (TerminationStatusCode.OPTIMAL, KN.RC_OPTIMAL), (TerminationStatusCode.OPTIMAL, KN.RC_OPTIMAL_OR_SATISFACTORY), - (TerminationStatusCode.LOCALLY_SOLVED, KN.RC_NEAR_OPT), - (TerminationStatusCode.LOCALLY_SOLVED, KN.RC_FEAS_XTOL), - (TerminationStatusCode.LOCALLY_SOLVED, KN.RC_FEAS_NO_IMPROVE), - (TerminationStatusCode.LOCALLY_SOLVED, KN.RC_FEAS_FTOL), + (TerminationStatusCode.ALMOST_OPTIMAL, KN.RC_NEAR_OPT), + (TerminationStatusCode.ALMOST_OPTIMAL, KN.RC_FEAS_XTOL), + (TerminationStatusCode.ALMOST_OPTIMAL, KN.RC_FEAS_NO_IMPROVE), + (TerminationStatusCode.ALMOST_OPTIMAL, KN.RC_FEAS_FTOL), (TerminationStatusCode.LOCALLY_SOLVED, KN.RC_FEAS_BEST), (TerminationStatusCode.LOCALLY_SOLVED, KN.RC_FEAS_MULTISTART), (TerminationStatusCode.INFEASIBLE, KN.RC_INFEASIBLE), - (TerminationStatusCode.INFEASIBLE, KN.RC_INFEAS_XTOL), - (TerminationStatusCode.INFEASIBLE, KN.RC_INFEAS_NO_IMPROVE), - (TerminationStatusCode.INFEASIBLE, KN.RC_INFEAS_MULTISTART), + (TerminationStatusCode.LOCALLY_INFEASIBLE, KN.RC_INFEAS_XTOL), + (TerminationStatusCode.LOCALLY_INFEASIBLE, KN.RC_INFEAS_NO_IMPROVE), + (TerminationStatusCode.LOCALLY_INFEASIBLE, KN.RC_INFEAS_MULTISTART), (TerminationStatusCode.INFEASIBLE, KN.RC_INFEAS_CON_BOUNDS), (TerminationStatusCode.INFEASIBLE, KN.RC_INFEAS_VAR_BOUNDS), (TerminationStatusCode.DUAL_INFEASIBLE, KN.RC_UNBOUNDED), @@ -151,8 +151,8 @@ def autoload_library(): (TerminationStatusCode.TIME_LIMIT, KN.RC_TIME_LIMIT_INFEAS), (TerminationStatusCode.OTHER_LIMIT, KN.RC_FEVAL_LIMIT_FEAS), (TerminationStatusCode.OTHER_LIMIT, KN.RC_FEVAL_LIMIT_INFEAS), - (TerminationStatusCode.SOLUTION_LIMIT, KN.RC_MIP_EXH_FEAS), - (TerminationStatusCode.SOLUTION_LIMIT, KN.RC_MIP_EXH_INFEAS), + (TerminationStatusCode.OTHER_LIMIT, KN.RC_MIP_EXH_FEAS), + (TerminationStatusCode.OTHER_LIMIT, KN.RC_MIP_EXH_INFEAS), (TerminationStatusCode.NODE_LIMIT, KN.RC_MIP_NODE_LIMIT_FEAS), (TerminationStatusCode.NODE_LIMIT, KN.RC_MIP_NODE_LIMIT_INFEAS), (TerminationStatusCode.INTERRUPTED, KN.RC_USER_TERMINATION), @@ -225,16 +225,23 @@ def _result_status_knitro(model: "Model"): ModelAttribute.RawStatusString: lambda model: ( f"KNITRO status code: {model.m_solve_status}" ), - ModelAttribute.DualStatus: _result_status_knitro, ModelAttribute.PrimalStatus: _result_status_knitro, ModelAttribute.NumberOfThreads: lambda model: model.get_raw_parameter(KN.PARAM_THREADS), ModelAttribute.TimeLimitSec: lambda model: model.get_raw_parameter(KN.PARAM_TIME_LIMIT), + # TODO: Bind this in C++ + ModelAttribute.BarrierIterations: lambda model: model.get_number_iterations(), + ModelAttribute.NodeCount: lambda model: model.get_mip_node_count(), + ModelAttribute.ObjectiveBound: lambda model: model.get_obj_bound(), + ModelAttribute.RelativeGap: lambda model: model.get_mip_relative_gap(), + ModelAttribute.SolverName: lambda model: model.get_solver_name(), + ModelAttribute.SolverVersion: lambda model: model.get_release(), + ModelAttribute.SolveTimeSec: lambda model: model.get_solve_time(), } model_attribute_set_func_map = { ModelAttribute.ObjectiveSense: lambda model, x: model.set_obj_sense(x), ModelAttribute.NumberOfThreads: lambda model, x: model.set_raw_parameter(KN.PARAM_THREADS, x), - ModelAttribute.Silent: lambda model, x: model.set_raw_parameter(KN.PARAM_OUTLEV, KN.OUTLEV_NONE if x else KN.OUTLEV_ITER), + ModelAttribute.Silent: lambda model, x: model.set_raw_parameter(KN.PARAM_OUTLEV, KN.OUTLEV_NONE if x else KN.OUTLEV_ITER_10), ModelAttribute.TimeLimitSec: lambda model, x: model.set_raw_parameter(KN.PARAM_TIME_LIMIT, x), } From da95378632b17f6fc7b4210cd30158ed029bb6b0 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Mon, 9 Feb 2026 15:14:36 -0500 Subject: [PATCH 14/23] use sparse CppAD structures. --- include/pyoptinterface/knitro_model.hpp | 176 +++++++++++++++--------- 1 file changed, 114 insertions(+), 62 deletions(-) diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp index e257a150..22eebb8b 100644 --- a/include/pyoptinterface/knitro_model.hpp +++ b/include/pyoptinterface/knitro_model.hpp @@ -143,68 +143,99 @@ struct CallbackPattern template struct CallbackEvaluator { + constexpr static std::string jac_coloring_ = "cppad"; + constexpr static std::string hess_coloring_ = "cppad"; std::vector indexVars; std::vector indexCons; CppAD::ADFun fun; std::vector fun_rows; - std::vector> jac_pattern; - std::vector jac_rows; - std::vector jac_cols; - CppAD::sparse_jacobian_work jac_work; - std::vector> hess_pattern; - std::vector hess_rows; - std::vector hess_cols; - CppAD::sparse_hessian_work hess_work; + CppAD::sparse_rc> jac_pattern_; + CppAD::sparse_rcv, std::vector> jac_; + CppAD::sparse_jac_work jac_work_; + CppAD::sparse_rc> hess_pattern_; + CppAD::sparse_rcv, std::vector> hess_; + CppAD::sparse_hes_work hess_work_; + + // std::vector> jac_pattern; + // std::vector jac_rows; + // std::vector jac_cols; + // CppAD::sparse_jacobian_work jac_work; + // std::vector> hess_pattern; + // std::vector hess_rows; + // std::vector hess_cols; + // CppAD::sparse_hessian_work hess_work; std::vector x; - std::vector jac; std::vector w; - std::vector hess; + + // std::vector jac; + // std::vector hess; void setup() { fun.optimize(); - size_t m = fun_rows.size(); - std::vector> jac_sparsity(m); - for (size_t k = 0; k < m; k++) + CppAD::sparse_rc> jac_pattern_in(fun.Range(), fun_rows.size(), fun_rows.size()); + for (size_t k = 0; k < fun_rows.size(); k++) { - jac_sparsity[k].insert(fun_rows[k]); + jac_pattern_in.set(k, fun_rows[k], fun_rows[k]); } - jac_pattern = fun.RevSparseJac(jac_sparsity.size(), jac_sparsity); - for (size_t k = 0; k < jac_pattern.size(); k++) - { - for (size_t i : jac_pattern[k]) - { - jac_rows.push_back(fun_rows[k]); - jac_cols.push_back(i); - } - } - std::vector> r_hess_sparsity(fun.Domain()); + fun.rev_jac_sparsity(jac_pattern_in, false, false, true, jac_pattern_); + jac_pattern_in.resize(fun.Domain(), fun.Domain(), fun.Domain()); for (size_t i = 0; i < fun.Domain(); i++) { - r_hess_sparsity[i].insert(i); - } - fun.ForSparseJac(fun.Domain(), r_hess_sparsity); - std::vector> hess_sparsity(1); - for (size_t k = 0; k < m; k++) - { - hess_sparsity[0].insert(fun_rows[k]); + jac_pattern_in.set(i, i, i); } - hess_pattern = fun.RevSparseHes(r_hess_sparsity.size(), hess_sparsity); - for (size_t k = 0; k < hess_pattern.size(); k++) + CppAD::sparse_rc> jac_pattern_out; + fun.for_jac_sparsity(jac_pattern_in, false, false, true, jac_pattern_out); + std::vector select_rows(fun.Range(), false); + for (size_t k = 0; k < fun_rows.size(); k++) { - for (size_t i : hess_pattern[k]) - { - hess_rows.push_back(k); - hess_cols.push_back(i); - } + select_rows[fun_rows[k]] = true; } - x.resize(indexVars.size()); - jac.resize(jac_rows.size()); + fun.rev_hes_sparsity(select_rows, false, true, hess_pattern_); + // size_t m = fun_rows.size(); + // std::vector> jac_sparsity(m); + // for (size_t k = 0; k < m; k++) + // { + // jac_sparsity[k].insert(fun_rows[k]); + // } + // jac_pattern = fun.RevSparseJac(jac_sparsity.size(), jac_sparsity); + // for (size_t k = 0; k < jac_pattern.size(); k++) + // { + // for (size_t i : jac_pattern[k]) + // { + // jac_rows.push_back(fun_rows[k]); + // jac_cols.push_back(i); + // } + // } + // std::vector> r_hess_sparsity(fun.Domain()); + // for (size_t i = 0; i < fun.Domain(); i++) + // { + // r_hess_sparsity[i].insert(i); + // } + // fun.ForSparseJac(fun.Domain(), r_hess_sparsity); + // std::vector> hess_sparsity(1); + // for (size_t k = 0; k < m; k++) + // { + // hess_sparsity[0].insert(fun_rows[k]); + // } + // hess_pattern = fun.RevSparseHes(r_hess_sparsity.size(), hess_sparsity); + // for (size_t k = 0; k < hess_pattern.size(); k++) + // { + // for (size_t i : hess_pattern[k]) + // { + // hess_rows.push_back(k); + // hess_cols.push_back(i); + // } + // } + x.resize(fun.Domain(), 0.0); w.resize(fun.Range(), 0.0); - hess.resize(hess_rows.size()); + jac_ = CppAD::sparse_rcv, std::vector>(jac_pattern_); + hess_ = CppAD::sparse_rcv, std::vector>(hess_pattern_); + // jac.resize(jac_rows.size()); + // hess.resize(hess_rows.size()); } void eval_fun(const V *req_x, V *res_y, bool aggregate = false) @@ -233,8 +264,9 @@ struct CallbackEvaluator { x[i] = req_x[indexVars[i]]; } - fun.SparseJacobianReverse(x, jac_pattern, jac_rows, jac_cols, jac, jac_work); - for (size_t i = 0; i < jac.size(); i++) + fun.sparse_jac_rev(x, jac_, jac_pattern_, jac_coloring_, jac_work_); + auto jac = jac_.val(); + for (size_t i = 0; i < jac_.nnz(); i++) { res_jac[i] = jac[i]; } @@ -257,8 +289,9 @@ struct CallbackEvaluator w[fun_rows[k]] = req_w[indexCons[k]]; } } - fun.SparseHessian(x, w, hess_pattern, hess_rows, hess_cols, hess, hess_work); - for (size_t i = 0; i < hess.size(); i++) + fun.sparse_hes(x, w, hess_, hess_pattern_, hess_coloring_, hess_work_); + auto hess = hess_.val(); + for (size_t i = 0; i < hess_.nnz(); i++) { res_hess[i] = hess[i]; } @@ -269,37 +302,56 @@ struct CallbackEvaluator CallbackPattern pattern; pattern.indexCons = indexCons; + auto jac_rows = jac_pattern_.row(); + auto jac_cols = jac_pattern_.col(); if (indexCons.empty()) { - for (size_t k = 0; k < jac_pattern.size(); k++) + for (size_t k = 0; k < jac_pattern_.nnz(); k++) { - for (size_t i : jac_pattern[k]) - { - pattern.objGradIndexVars.push_back(indexVars[i]); - } + pattern.objGradIndexVars.push_back(indexVars[jac_cols[k]]); } + // for (size_t k = 0; k < jac_pattern.size(); k++) + // { + // for (size_t i : jac_pattern[k]) + // { + // pattern.objGradIndexVars.push_back(indexVars[i]); + // } + // } } else { - for (size_t k = 0; k < jac_pattern.size(); k++) + for (size_t k = 0; k < jac_pattern_.nnz(); k++) { - for (size_t i : jac_pattern[k]) - { - pattern.jacIndexCons.push_back(indexCons[k]); - pattern.jacIndexVars.push_back(indexVars[i]); - } + pattern.jacIndexCons.push_back(indexCons[jac_rows[k]]); + pattern.jacIndexVars.push_back(indexVars[jac_cols[k]]); } + // for (size_t k = 0; k < jac_pattern.size(); k++) + // { + // for (size_t i : jac_pattern[k]) + // { + // pattern.jacIndexCons.push_back(indexCons[k]); + // pattern.jacIndexVars.push_back(indexVars[i]); + // } + // } } - for (size_t k = 0; k < hess_pattern.size(); k++) + auto hess_rows = hess_pattern_.row(); + auto hess_cols = hess_pattern_.col(); + for (size_t k = 0; k < hess_pattern_.nnz(); k++) { - for (size_t i : hess_pattern[k]) - { - pattern.hessIndexVars1.push_back(indexVars[k]); - pattern.hessIndexVars2.push_back(indexVars[i]); - } + pattern.hessIndexVars1.push_back(indexVars[hess_rows[k]]); + pattern.hessIndexVars2.push_back(indexVars[hess_cols[k]]); } + // for (size_t k = 0; k < hess_pattern.size(); k++) + // { + // for (size_t i : hess_pattern[k]) + // { + // pattern.hessIndexVars1.push_back(indexVars[k]); + // pattern.hessIndexVars2.push_back(indexVars[i]); + // } + // } + return pattern; } }; From 1ccb630b38df1b61d925ae0b187c5b3b84aa3c5a Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Mon, 9 Feb 2026 15:15:51 -0500 Subject: [PATCH 15/23] clean: remove commented-out code in CallbackEvaluator --- include/pyoptinterface/knitro_model.hpp | 49 ------------------------- 1 file changed, 49 deletions(-) diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp index 22eebb8b..b3e66f3f 100644 --- a/include/pyoptinterface/knitro_model.hpp +++ b/include/pyoptinterface/knitro_model.hpp @@ -158,21 +158,9 @@ struct CallbackEvaluator CppAD::sparse_rcv, std::vector> hess_; CppAD::sparse_hes_work hess_work_; - // std::vector> jac_pattern; - // std::vector jac_rows; - // std::vector jac_cols; - // CppAD::sparse_jacobian_work jac_work; - // std::vector> hess_pattern; - // std::vector hess_rows; - // std::vector hess_cols; - // CppAD::sparse_hessian_work hess_work; - std::vector x; std::vector w; - // std::vector jac; - // std::vector hess; - void setup() { fun.optimize(); @@ -195,47 +183,10 @@ struct CallbackEvaluator select_rows[fun_rows[k]] = true; } fun.rev_hes_sparsity(select_rows, false, true, hess_pattern_); - // size_t m = fun_rows.size(); - // std::vector> jac_sparsity(m); - // for (size_t k = 0; k < m; k++) - // { - // jac_sparsity[k].insert(fun_rows[k]); - // } - // jac_pattern = fun.RevSparseJac(jac_sparsity.size(), jac_sparsity); - // for (size_t k = 0; k < jac_pattern.size(); k++) - // { - // for (size_t i : jac_pattern[k]) - // { - // jac_rows.push_back(fun_rows[k]); - // jac_cols.push_back(i); - // } - // } - // std::vector> r_hess_sparsity(fun.Domain()); - // for (size_t i = 0; i < fun.Domain(); i++) - // { - // r_hess_sparsity[i].insert(i); - // } - // fun.ForSparseJac(fun.Domain(), r_hess_sparsity); - // std::vector> hess_sparsity(1); - // for (size_t k = 0; k < m; k++) - // { - // hess_sparsity[0].insert(fun_rows[k]); - // } - // hess_pattern = fun.RevSparseHes(r_hess_sparsity.size(), hess_sparsity); - // for (size_t k = 0; k < hess_pattern.size(); k++) - // { - // for (size_t i : hess_pattern[k]) - // { - // hess_rows.push_back(k); - // hess_cols.push_back(i); - // } - // } x.resize(fun.Domain(), 0.0); w.resize(fun.Range(), 0.0); jac_ = CppAD::sparse_rcv, std::vector>(jac_pattern_); hess_ = CppAD::sparse_rcv, std::vector>(hess_pattern_); - // jac.resize(jac_rows.size()); - // hess.resize(hess_rows.size()); } void eval_fun(const V *req_x, V *res_y, bool aggregate = false) From 2591433e3b2d44db4cad6600559997c70e55fd46 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Mon, 9 Feb 2026 15:22:28 -0500 Subject: [PATCH 16/23] add knitro to missing tests. --- tests/test_nlp_multiple_run.py | 3 ++- tests/test_nlp_opf.py | 3 ++- tests/test_nlp_rocket.py | 5 +++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/test_nlp_multiple_run.py b/tests/test_nlp_multiple_run.py index 86b5bace..ddfe5725 100644 --- a/tests/test_nlp_multiple_run.py +++ b/tests/test_nlp_multiple_run.py @@ -1,4 +1,4 @@ -from pyoptinterface import copt, ipopt, nl +from pyoptinterface import copt, ipopt, knitro, nl import pytest import math @@ -52,3 +52,4 @@ def llvm(): test_nlp_reopt(llvm) test_nlp_reopt(copt.Model) + test_nlp_reopt(knitro.Model) diff --git a/tests/test_nlp_opf.py b/tests/test_nlp_opf.py index 1e1c767b..a6225f6b 100644 --- a/tests/test_nlp_opf.py +++ b/tests/test_nlp_opf.py @@ -192,7 +192,8 @@ def test_acopf(nlp_model_ctor): if __name__ == "__main__": - from pyoptinterface import ipopt, copt + from pyoptinterface import ipopt, copt, knitro test_acopf(ipopt.Model) test_acopf(copt.Model) + test_acopf(knitro.Model) diff --git a/tests/test_nlp_rocket.py b/tests/test_nlp_rocket.py index 3d88c025..83331744 100644 --- a/tests/test_nlp_rocket.py +++ b/tests/test_nlp_rocket.py @@ -1,4 +1,3 @@ -import pyoptinterface as poi from pyoptinterface import nl import math @@ -72,7 +71,7 @@ def test_rocket(nlp_model_ctor): if __name__ == "__main__": - from pyoptinterface import copt, ipopt + from pyoptinterface import copt, ipopt, knitro def c(): return ipopt.Model(jit="C") @@ -85,3 +84,5 @@ def llvm(): test_rocket(llvm) test_rocket(copt.Model) + + test_rocket(knitro.Model) From 03da0771ed78feeb0ac124916cbe0c0a93b97461 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Mon, 9 Feb 2026 19:42:50 -0500 Subject: [PATCH 17/23] clean: remove commented-out code in CallbackEvaluator --- include/pyoptinterface/knitro_model.hpp | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp index b3e66f3f..689abb6d 100644 --- a/include/pyoptinterface/knitro_model.hpp +++ b/include/pyoptinterface/knitro_model.hpp @@ -261,13 +261,6 @@ struct CallbackEvaluator { pattern.objGradIndexVars.push_back(indexVars[jac_cols[k]]); } - // for (size_t k = 0; k < jac_pattern.size(); k++) - // { - // for (size_t i : jac_pattern[k]) - // { - // pattern.objGradIndexVars.push_back(indexVars[i]); - // } - // } } else { @@ -276,14 +269,6 @@ struct CallbackEvaluator pattern.jacIndexCons.push_back(indexCons[jac_rows[k]]); pattern.jacIndexVars.push_back(indexVars[jac_cols[k]]); } - // for (size_t k = 0; k < jac_pattern.size(); k++) - // { - // for (size_t i : jac_pattern[k]) - // { - // pattern.jacIndexCons.push_back(indexCons[k]); - // pattern.jacIndexVars.push_back(indexVars[i]); - // } - // } } auto hess_rows = hess_pattern_.row(); @@ -294,15 +279,6 @@ struct CallbackEvaluator pattern.hessIndexVars2.push_back(indexVars[hess_cols[k]]); } - // for (size_t k = 0; k < hess_pattern.size(); k++) - // { - // for (size_t i : hess_pattern[k]) - // { - // pattern.hessIndexVars1.push_back(indexVars[k]); - // pattern.hessIndexVars2.push_back(indexVars[i]); - // } - // } - return pattern; } }; From 4af2b896bb58ab47cc65e90715e8f112ec131ac4 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Mon, 9 Feb 2026 20:06:42 -0500 Subject: [PATCH 18/23] remove result object --- include/pyoptinterface/knitro_model.hpp | 85 +++--- lib/knitro_model.cpp | 330 +++++++++++------------- 2 files changed, 190 insertions(+), 225 deletions(-) diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp index 689abb6d..30f14935 100644 --- a/include/pyoptinterface/knitro_model.hpp +++ b/include/pyoptinterface/knitro_model.hpp @@ -72,10 +72,10 @@ B(KN_get_obj_value); \ B(KN_get_number_cons); \ B(KN_get_number_vars); \ - B(KN_get_var_primal_values_all); \ - B(KN_get_var_dual_values_all); \ - B(KN_get_con_values_all); \ - B(KN_get_con_dual_values_all); \ + B(KN_get_var_primal_value); \ + B(KN_get_var_dual_value); \ + B(KN_get_con_value); \ + B(KN_get_con_dual_value); \ B(KN_get_number_iters); \ B(KN_get_mip_number_nodes); \ B(KN_get_mip_relaxation_bnd); \ @@ -105,17 +105,6 @@ struct KNITROFreeProblemT } }; -struct KNITROResult -{ - bool is_valid = false; - int status = 0; - double obj_val = 0.0; - std::vector x; - std::vector lambda; - std::vector con_values; - std::vector con_duals; -}; - enum ObjectiveFlags { OBJ_CONSTANT = 1 << 0, // 0x01 @@ -144,7 +133,7 @@ template struct CallbackEvaluator { constexpr static std::string jac_coloring_ = "cppad"; - constexpr static std::string hess_coloring_ = "cppad"; + constexpr static std::string hess_coloring_ = "cppad.symmetric"; std::vector indexVars; std::vector indexCons; @@ -300,26 +289,21 @@ class KNITROModel : public OnesideLinearConstraintMixin, public GetValueMixin { public: + // Constructor/Init/Close KNITROModel(); void init(); void close(); + // Model information double get_infinity() const; - KNINT _variable_index(const VariableIndex &variable); - KNINT _constraint_index(const ConstraintIndex &constraint); - size_t get_number_iterations(); - size_t get_mip_node_count(); - double get_obj_bound(); - double get_mip_relative_gap(); - double get_solve_time(); std::string get_solver_name(); std::string get_release(); + // Variable functions VariableIndex add_variable(VariableDomain domain = VariableDomain::Continuous, double lb = -KN_INFINITY, double ub = KN_INFINITY, const char *name = nullptr); void delete_variable(const VariableIndex &variable); - double get_variable_lb(const VariableIndex &variable); double get_variable_ub(const VariableIndex &variable); void set_variable_lb(const VariableIndex &variable, double lb); @@ -331,9 +315,9 @@ class KNITROModel : public OnesideLinearConstraintMixin, void set_variable_name(const VariableIndex &variable, const std::string &name); void set_variable_domain(const VariableIndex &variable, VariableDomain domain); double get_variable_rc(const VariableIndex &variable); - std::string pprint_variable(const VariableIndex &variable); + // Constraint functions ConstraintIndex add_linear_constraint(const ScalarAffineFunction &f, ConstraintSense sense, double rhs, const char *name = nullptr); ConstraintIndex add_linear_constraint(const ScalarAffineFunction &f, @@ -354,19 +338,17 @@ class KNITROModel : public OnesideLinearConstraintMixin, const ExpressionHandle &result, ConstraintSense sense, double rhs, const char *name = nullptr); - void delete_constraint(ConstraintIndex constraint); void set_constraint_name(const ConstraintIndex &constraint, const std::string &name); std::string get_constraint_name(const ConstraintIndex &constraint); - double get_constraint_primal(const ConstraintIndex &constraint); double get_constraint_dual(const ConstraintIndex &constraint); - void set_normalized_rhs(const ConstraintIndex &constraint, double rhs); double get_normalized_rhs(const ConstraintIndex &constraint); void set_normalized_coefficient(const ConstraintIndex &constraint, const VariableIndex &variable, double coefficient); + // Objective functions void set_objective(const ScalarAffineFunction &f, ObjectiveSense sense); void set_objective(const ScalarQuadraticFunction &f, ObjectiveSense sense); void set_objective(const ExprBuilder &expr, ObjectiveSense sense); @@ -375,15 +357,23 @@ class KNITROModel : public OnesideLinearConstraintMixin, void set_obj_sense(ObjectiveSense sense); ObjectiveSense get_obj_sense(); + // Solve functions void optimize(); + // Solve information + size_t get_number_iterations(); + size_t get_mip_node_count(); + double get_obj_bound(); + double get_mip_relative_gap(); + double get_solve_time(); + template void set_raw_parameter(const std::string &name, T value) { int error; int param_id; error = knitro::KN_get_param_id(m_kc.get(), name.c_str(), ¶m_id); - check_error(error); + _check_error(error); set_raw_parameter(param_id, value); } @@ -416,7 +406,7 @@ class KNITROModel : public OnesideLinearConstraintMixin, std::is_same_v || std::is_same_v, "T must be int, double, std::string, or const char*"); } - check_error(error); + _check_error(error); } template @@ -425,7 +415,7 @@ class KNITROModel : public OnesideLinearConstraintMixin, int error; int param_id; error = knitro::KN_get_param_id(m_kc.get(), name.c_str(), ¶m_id); - check_error(error); + _check_error(error); return get_raw_parameter(param_id); } @@ -447,12 +437,17 @@ class KNITROModel : public OnesideLinearConstraintMixin, static_assert(std::is_same_v || std::is_same_v, "T must be int or double for get_raw_parameter"); } - check_error(error); + _check_error(error); return value; } - void check_error(int error) const; + // Internal helpers + void _check_error(int error) const; + void _check_dirty() const; + KNINT _variable_index(const VariableIndex &variable); + KNINT _constraint_index(const ConstraintIndex &constraint); + // Member variables std::unique_ptr m_kc = nullptr; size_t n_vars = 0; @@ -470,7 +465,6 @@ class KNITROModel : public OnesideLinearConstraintMixin, std::vector>> m_evaluators; bool m_need_to_add_callbacks = false; - KNITROResult m_result; bool m_is_dirty = true; int m_solve_status = 0; @@ -537,7 +531,7 @@ class KNITROModel : public OnesideLinearConstraintMixin, { KNINT indexCon; int error = knitro::KN_add_con(m_kc.get(), &indexCon); - check_error(error); + _check_error(error); IndexT index = indexCon; ConstraintIndex constraint(type, index); @@ -546,16 +540,16 @@ class KNITROModel : public OnesideLinearConstraintMixin, double ub = std::get<1>(interval); error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon, lb); - check_error(error); + _check_error(error); error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon, ub); - check_error(error); + _check_error(error); setter(constraint); if (!is_name_empty(name)) { error = knitro::KN_set_con_name(m_kc.get(), indexCon, name); - check_error(error); + _check_error(error); } m_con_sense_flags[indexCon] = CON_UPBND; @@ -587,9 +581,8 @@ class KNITROModel : public OnesideLinearConstraintMixin, setter(); int goal = knitro_obj_goal(sense); int error = knitro::KN_set_obj_goal(m_kc.get(), goal); - check_error(error); + _check_error(error); m_is_dirty = true; - m_result.is_valid = false; } template @@ -600,16 +593,16 @@ class KNITROModel : public OnesideLinearConstraintMixin, int error; error = knitro::KN_add_eval_callback(m_kc.get(), p.indexCons.empty(), p.indexCons.size(), p.indexCons.data(), f, &cb); - check_error(error); + _check_error(error); error = knitro::KN_set_cb_user_params(m_kc.get(), cb, evaluator); - check_error(error); + _check_error(error); error = knitro::KN_set_cb_grad(m_kc.get(), cb, p.objGradIndexVars.size(), p.objGradIndexVars.data(), p.jacIndexCons.size(), p.jacIndexCons.data(), p.jacIndexVars.data(), g); - check_error(error); + _check_error(error); error = knitro::KN_set_cb_hess(m_kc.get(), cb, p.hessIndexVars1.size(), p.hessIndexVars1.data(), p.hessIndexVars2.data(), h); - check_error(error); + _check_error(error); } template @@ -642,7 +635,7 @@ class KNITROModel : public OnesideLinearConstraintMixin, char name[1024]; name[0] = '\0'; int error = get(m_kc.get(), index, 1024, name); - check_error(error); + _check_error(error); if (name[0] != '\0') { @@ -658,7 +651,7 @@ class KNITROModel : public OnesideLinearConstraintMixin, void _set_name(KNINT index, const std::string &name, F set) { int error = set(m_kc.get(), index, name.c_str()); - check_error(error); + _check_error(error); m_is_dirty = true; } }; diff --git a/lib/knitro_model.cpp b/lib/knitro_model.cpp index bb462f9f..fff8ea95 100644 --- a/lib/knitro_model.cpp +++ b/lib/knitro_model.cpp @@ -64,7 +64,7 @@ void KNITROModel::init() KN_context *kc_ptr = nullptr; int error = knitro::KN_new(&kc_ptr); - check_error(error); + _check_error(error); m_kc = std::unique_ptr(kc_ptr); } @@ -74,7 +74,7 @@ void KNITROModel::close() m_kc.reset(); } -void KNITROModel::check_error(int error) const +void KNITROModel::_check_error(int error) const { if (error != 0) { @@ -82,55 +82,12 @@ void KNITROModel::check_error(int error) const } } +// Model information double KNITROModel::get_infinity() const { return KN_INFINITY; } -size_t KNITROModel::get_number_iterations() -{ - int error; - int iters; - - error = knitro::KN_get_number_iters(m_kc.get(), &iters); - check_error(error); - return static_cast(iters); -} - -size_t KNITROModel::get_mip_node_count() -{ - int error; - int nodes; - - error = knitro::KN_get_mip_number_nodes(m_kc.get(), &nodes); - check_error(error); - return static_cast(nodes); -} - -double KNITROModel::get_obj_bound() -{ - double bound; - int error = knitro::KN_get_mip_relaxation_bnd(m_kc.get(), &bound); - check_error(error); - return bound; -} - -double KNITROModel::get_mip_relative_gap() -{ - double gap; - int error = knitro::KN_get_mip_rel_gap(m_kc.get(), &gap); - check_error(error); - return gap; -} - -double KNITROModel::get_solve_time() -{ - double time; - int error = knitro::KN_get_solve_time_real(m_kc.get(), &time); - check_error(error); - return time; -} - std::string KNITROModel::get_solver_name() { return std::string("KNITRO"); @@ -141,22 +98,23 @@ std::string KNITROModel::get_release() constexpr int buf_size = 20; char release[buf_size]; int error = knitro::KN_get_release(buf_size, release); - check_error(error); + _check_error(error); return std::string(release); } +// Variable functions VariableIndex KNITROModel::add_variable(VariableDomain domain, double lb, double ub, const char *name) { KNINT indexVar; int error = knitro::KN_add_var(m_kc.get(), &indexVar); - check_error(error); + _check_error(error); VariableIndex variable(indexVar); int var_type = knitro_var_type(domain); error = knitro::KN_set_var_type(m_kc.get(), indexVar, var_type); - check_error(error); + _check_error(error); if (var_type == KN_VARTYPE_BINARY) { @@ -165,14 +123,14 @@ VariableIndex KNITROModel::add_variable(VariableDomain domain, double lb, double } error = knitro::KN_set_var_lobnd(m_kc.get(), indexVar, lb); - check_error(error); + _check_error(error); error = knitro::KN_set_var_upbnd(m_kc.get(), indexVar, ub); - check_error(error); + _check_error(error); if (!is_name_empty(name)) { int error = knitro::KN_set_var_name(m_kc.get(), indexVar, name); - check_error(error); + _check_error(error); } n_vars++; @@ -188,7 +146,7 @@ double KNITROModel::get_variable_lb(const VariableIndex &variable) double lb; error = knitro::KN_get_var_lobnd(m_kc.get(), indexVar, &lb); - check_error(error); + _check_error(error); return lb; } @@ -199,7 +157,7 @@ double KNITROModel::get_variable_ub(const VariableIndex &variable) double ub; error = knitro::KN_get_var_upbnd(m_kc.get(), indexVar, &ub); - check_error(error); + _check_error(error); return ub; } @@ -207,7 +165,7 @@ void KNITROModel::set_variable_lb(const VariableIndex &variable, double lb) { KNINT indexVar = _variable_index(variable); int error = knitro::KN_set_var_lobnd(m_kc.get(), indexVar, lb); - check_error(error); + _check_error(error); m_is_dirty = true; } @@ -215,7 +173,7 @@ void KNITROModel::set_variable_ub(const VariableIndex &variable, double ub) { KNLONG indexVar = _variable_index(variable); int error = knitro::KN_set_var_upbnd(m_kc.get(), indexVar, ub); - check_error(error); + _check_error(error); m_is_dirty = true; } @@ -227,18 +185,19 @@ void KNITROModel::set_variable_bounds(const VariableIndex &variable, double lb, double KNITROModel::get_variable_value(const VariableIndex &variable) { - if (!m_result.is_valid) - { - throw std::runtime_error("No solution available"); - } - return m_result.x[_variable_index(variable)]; + _check_dirty(); + KNINT indexVar = _variable_index(variable); + double value; + int error = knitro::KN_get_var_primal_value(m_kc.get(), indexVar, &value); + _check_error(error); + return value; } void KNITROModel::set_variable_start(const VariableIndex &variable, double start) { KNINT indexVar = _variable_index(variable); int error = knitro::KN_set_var_primal_init_value(m_kc.get(), indexVar, start); - check_error(error); + _check_error(error); } std::string KNITROModel::get_variable_name(const VariableIndex &variable) @@ -264,22 +223,22 @@ void KNITROModel::set_variable_domain(const VariableIndex &variable, VariableDom if (var_type == KN_VARTYPE_BINARY) { error = knitro::KN_get_var_lobnd(m_kc.get(), indexVar, &lb); - check_error(error); + _check_error(error); error = knitro::KN_get_var_upbnd(m_kc.get(), indexVar, &ub); - check_error(error); + _check_error(error); } error = knitro::KN_set_var_type(m_kc.get(), indexVar, var_type); - check_error(error); + _check_error(error); if (var_type == KN_VARTYPE_BINARY) { lb = (lb < 0.0) ? 0.0 : lb; ub = (ub > 1.0) ? 1.0 : ub; error = knitro::KN_set_var_lobnd(m_kc.get(), indexVar, lb); - check_error(error); + _check_error(error); error = knitro::KN_set_var_upbnd(m_kc.get(), indexVar, ub); - check_error(error); + _check_error(error); } m_is_dirty = true; @@ -287,25 +246,25 @@ void KNITROModel::set_variable_domain(const VariableIndex &variable, VariableDom double KNITROModel::get_variable_rc(const VariableIndex &variable) { - if (!m_result.is_valid) - { - throw std::runtime_error("No solution available"); - } + _check_dirty(); KNINT indexVar = _variable_index(variable); - return m_result.lambda[indexVar]; + double value; + int error = knitro::KN_get_var_dual_value(m_kc.get(), indexVar, &value); + _check_error(error); + return -value; } void KNITROModel::delete_variable(const VariableIndex &variable) { - int indexVar = _variable_index(variable); + KNINT indexVar = _variable_index(variable); int error; error = knitro::KN_set_var_type(m_kc.get(), indexVar, KN_VARTYPE_CONTINUOUS); - check_error(error); + _check_error(error); error = knitro::KN_set_var_lobnd(m_kc.get(), indexVar, -KN_INFINITY); - check_error(error); + _check_error(error); error = knitro::KN_set_var_upbnd(m_kc.get(), indexVar, KN_INFINITY); - check_error(error); + _check_error(error); n_vars--; m_is_dirty = true; @@ -316,16 +275,7 @@ std::string KNITROModel::pprint_variable(const VariableIndex &variable) return get_variable_name(variable); } -KNINT KNITROModel::_variable_index(const VariableIndex &variable) -{ - return variable.index; -} - -KNINT KNITROModel::_constraint_index(const ConstraintIndex &constraint) -{ - return constraint.index; -} - +// Constraint functions ConstraintIndex KNITROModel::add_linear_constraint(const ScalarAffineFunction &function, ConstraintSense sense, double rhs, const char *name) @@ -453,19 +403,19 @@ void KNITROModel::_set_second_order_cone_constraint(const ConstraintIndex &const KNINT indexCon0; error = knitro::KN_add_con(m_kc.get(), &indexCon0); - check_error(error); + _check_error(error); KNINT indexVar0 = _variable_index(variables[0]); error = knitro::KN_add_con_linear_term(m_kc.get(), indexCon0, indexVar0, 1.0); - check_error(error); + _check_error(error); error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon0, 0.0); - check_error(error); + _check_error(error); error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon0, KN_INFINITY); - check_error(error); + _check_error(error); error = knitro::KN_add_con_quadratic_term(m_kc.get(), indexCon, indexVar0, indexVar0, 1.0); - check_error(error); + _check_error(error); size_t nnz = variables.size() - 1; std::vector indexCons(nnz, indexCon); std::vector indexVars(nnz); @@ -476,7 +426,7 @@ void KNITROModel::_set_second_order_cone_constraint(const ConstraintIndex &const std::vector coefs(nnz, -1.0); error = knitro::KN_add_con_quadratic_struct(m_kc.get(), nnz, indexCons.data(), indexVars.data(), indexVars.data(), coefs.data()); - check_error(error); + _check_error(error); m_soc_aux_cons[indexCon] = indexCon0; } @@ -489,25 +439,25 @@ void KNITROModel::_set_second_order_cone_constraint_rotated(const ConstraintInde KNINT indexCon0, indexCon1; error = knitro::KN_add_con(m_kc.get(), &indexCon0); - check_error(error); + _check_error(error); error = knitro::KN_add_con(m_kc.get(), &indexCon1); - check_error(error); + _check_error(error); KNINT indexVar0 = _variable_index(variables[0]); KNINT indexVar1 = _variable_index(variables[1]); error = knitro::KN_add_con_linear_term(m_kc.get(), indexCon0, indexVar0, 1.0); - check_error(error); + _check_error(error); error = knitro::KN_add_con_linear_term(m_kc.get(), indexCon1, indexVar1, 1.0); - check_error(error); + _check_error(error); error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon0, 0.0); - check_error(error); + _check_error(error); error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon0, KN_INFINITY); - check_error(error); + _check_error(error); error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon1, 0.0); - check_error(error); + _check_error(error); error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon1, KN_INFINITY); - check_error(error); + _check_error(error); size_t nnz = variables.size() - 2; std::vector indexCons(nnz, indexCon); @@ -519,9 +469,9 @@ void KNITROModel::_set_second_order_cone_constraint_rotated(const ConstraintInde std::vector coefs(nnz, -1.0); error = knitro::KN_add_con_quadratic_struct(m_kc.get(), nnz, indexCons.data(), indexVars.data(), indexVars.data(), coefs.data()); - check_error(error); + _check_error(error); error = knitro::KN_add_con_quadratic_term(m_kc.get(), indexCon, indexVar0, indexVar1, 2.0); - check_error(error); + _check_error(error); m_soc_aux_cons[indexCon] = std::make_pair(indexCon0, indexCon1); } @@ -532,9 +482,9 @@ void KNITROModel::delete_constraint(ConstraintIndex constraint) int error; error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon, -KN_INFINITY); - check_error(error); + _check_error(error); error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon, KN_INFINITY); - check_error(error); + _check_error(error); n_cons--; switch (constraint.type) @@ -573,9 +523,9 @@ void KNITROModel::delete_constraint(ConstraintIndex constraint) for (auto const &con : aux_cons) { error = knitro::KN_set_con_lobnd(m_kc.get(), con, -KN_INFINITY); - check_error(error); + _check_error(error); error = knitro::KN_set_con_upbnd(m_kc.get(), con, KN_INFINITY); - check_error(error); + _check_error(error); } m_soc_aux_cons.erase(it); @@ -598,24 +548,22 @@ std::string KNITROModel::get_constraint_name(const ConstraintIndex &constraint) double KNITROModel::get_constraint_primal(const ConstraintIndex &constraint) { - if (!m_result.is_valid) - { - throw std::runtime_error("No solution available"); - } - + _check_dirty(); KNINT indexCon = _constraint_index(constraint); - return m_result.con_values[indexCon]; + double value; + int error = knitro::KN_get_con_value(m_kc.get(), indexCon, &value); + _check_error(error); + return value; } double KNITROModel::get_constraint_dual(const ConstraintIndex &constraint) { - if (!m_result.is_valid) - { - throw std::runtime_error("No solution available"); - } - + _check_dirty(); KNINT indexCon = _constraint_index(constraint); - return m_result.con_duals[indexCon]; + double value; + int error = knitro::KN_get_con_dual_value(m_kc.get(), indexCon, &value); + _check_error(error); + return -value; } void KNITROModel::set_normalized_rhs(const ConstraintIndex &constraint, double rhs) @@ -628,13 +576,13 @@ void KNITROModel::set_normalized_rhs(const ConstraintIndex &constraint, double r if (flag & CON_LOBND) { error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon, rhs); - check_error(error); + _check_error(error); } if (flag & CON_UPBND) { error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon, rhs); - check_error(error); + _check_error(error); } m_is_dirty = true; @@ -649,12 +597,12 @@ double KNITROModel::get_normalized_rhs(const ConstraintIndex &constraint) if (flag & CON_UPBND) { error = knitro::KN_get_con_upbnd(m_kc.get(), indexCon, &rhs); - check_error(error); + _check_error(error); } else { error = knitro::KN_get_con_lobnd(m_kc.get(), indexCon, &rhs); - check_error(error); + _check_error(error); } return rhs; } @@ -670,7 +618,7 @@ void KNITROModel::set_normalized_coefficient(const ConstraintIndex &constraint, // we need to call KN_update before changing the linear term _update(); error = knitro::KN_chg_con_linear_term(m_kc.get(), indexCon, indexVar, coefficient); - check_error(error); + _check_error(error); m_is_dirty = true; } @@ -686,7 +634,7 @@ void KNITROModel::_set_linear_constraint(const ConstraintIndex &constraint, if (constant != 0.0) { error = knitro::KN_add_con_constant(m_kc.get(), indexCon, constant); - check_error(error); + _check_error(error); } } @@ -702,7 +650,7 @@ void KNITROModel::_set_linear_constraint(const ConstraintIndex &constraint, error = knitro::KN_add_con_linear_struct(m_kc.get(), nnz, indexCons.data(), indexVars, coefs); - check_error(error); + _check_error(error); } } @@ -728,10 +676,11 @@ void KNITROModel::_set_quadratic_constraint(const ConstraintIndex &constraint, double *coefs = ptr_form.value; error = knitro::KN_add_con_quadratic_struct(m_kc.get(), nnz, indexCons.data(), indexVars1, indexVars2, coefs); - check_error(error); + _check_error(error); } } +// Objective functions void KNITROModel::set_objective(const ScalarAffineFunction &function, ObjectiveSense sense) { _set_objective_impl(sense, [this, &function]() { _set_linear_objective(function); }); @@ -770,21 +719,20 @@ void KNITROModel::add_single_nl_objective(ExpressionGraph &graph, const Expressi m_need_to_add_callbacks = true; m_obj_flag |= OBJ_NONLINEAR; m_is_dirty = true; - m_result.is_valid = false; } void KNITROModel::set_obj_sense(ObjectiveSense sense) { int goal = knitro_obj_goal(sense); int error = knitro::KN_set_obj_goal(m_kc.get(), goal); - check_error(error); + _check_error(error); } ObjectiveSense KNITROModel::get_obj_sense() { int goal; int error = knitro::KN_get_obj_goal(m_kc.get(), &goal); - check_error(error); + _check_error(error); if (goal == KN_OBJGOAL_MINIMIZE) { return ObjectiveSense::Minimize; @@ -814,22 +762,22 @@ void KNITROModel::_reset_objective() if (m_obj_flag & OBJ_CONSTANT) { error = knitro::KN_del_obj_constant(m_kc.get()); - check_error(error); + _check_error(error); } if (m_obj_flag & OBJ_LINEAR) { error = knitro::KN_del_obj_linear_struct_all(m_kc.get()); - check_error(error); + _check_error(error); } if (m_obj_flag & OBJ_QUADRATIC) { error = knitro::KN_del_obj_quadratic_struct_all(m_kc.get()); - check_error(error); + _check_error(error); } if (m_obj_flag & OBJ_NONLINEAR) { error = knitro::KN_del_obj_eval_callback_all(m_kc.get()); - check_error(error); + _check_error(error); for (auto &[graph, outputs] : m_pending_outputs) { outputs.obj_idxs.clear(); @@ -849,7 +797,7 @@ void KNITROModel::_set_linear_objective(const ScalarAffineFunction &function) if (constant != 0.0) { error = knitro::KN_add_obj_constant(m_kc.get(), constant); - check_error(error); + _check_error(error); m_obj_flag |= OBJ_CONSTANT; } } @@ -862,7 +810,7 @@ void KNITROModel::_set_linear_objective(const ScalarAffineFunction &function) KNINT *indexVars = ptr_form.index; double *coefs = ptr_form.value; error = knitro::KN_add_obj_linear_struct(m_kc.get(), nnz, indexVars, coefs); - check_error(error); + _check_error(error); m_obj_flag |= OBJ_LINEAR; } } @@ -885,18 +833,18 @@ void KNITROModel::_set_quadratic_objective(const ScalarQuadraticFunction &functi KNINT *indexVars2 = ptr_form.col; double *coefs = ptr_form.value; error = knitro::KN_add_obj_quadratic_struct(m_kc.get(), nnz, indexVars1, indexVars2, coefs); - check_error(error); + _check_error(error); m_obj_flag |= OBJ_QUADRATIC; } } double KNITROModel::get_obj_value() { - if (!m_result.is_valid) - { - throw std::runtime_error("No solution available"); - } - return m_result.obj_val; + _check_dirty(); + double value; + int error = knitro::KN_get_obj_value(m_kc.get(), &value); + _check_error(error); + return value; } void KNITROModel::_add_constraint_callback(ExpressionGraph *graph, const Outputs &outputs) @@ -971,11 +919,12 @@ void KNITROModel::_add_callbacks() m_need_to_add_callbacks = false; } +// Solve functions void KNITROModel::_update() { _add_callbacks(); int error = knitro::KN_update(m_kc.get()); - check_error(error); + _check_error(error); } void KNITROModel::_pre_solve() @@ -991,54 +940,77 @@ void KNITROModel::_solve() } void KNITROModel::_post_solve() +{ +} + +void KNITROModel::optimize() +{ + _pre_solve(); + _solve(); + _post_solve(); + m_is_dirty = false; +} + +// Solve information +size_t KNITROModel::get_number_iterations() { int error; + int iters; - KNINT nV, nC; - error = knitro::KN_get_number_vars(m_kc.get(), &nV); - check_error(error); - error = knitro::KN_get_number_cons(m_kc.get(), &nC); - check_error(error); + error = knitro::KN_get_number_iters(m_kc.get(), &iters); + _check_error(error); + return static_cast(iters); +} - m_result.x.resize(nV); - m_result.lambda.resize(nV); - m_result.con_values.resize(nC); - m_result.con_duals.resize(nC); +size_t KNITROModel::get_mip_node_count() +{ + int error; + int nodes; - error = knitro::KN_get_var_primal_values_all(m_kc.get(), m_result.x.data()); - check_error(error); + error = knitro::KN_get_mip_number_nodes(m_kc.get(), &nodes); + _check_error(error); + return static_cast(nodes); +} - error = knitro::KN_get_var_dual_values_all(m_kc.get(), m_result.lambda.data()); - check_error(error); - for (size_t i = 0; i < nV; i++) - { - m_result.lambda[i] = -m_result.lambda[i]; - } +double KNITROModel::get_obj_bound() +{ + double bound; + int error = knitro::KN_get_mip_relaxation_bnd(m_kc.get(), &bound); + _check_error(error); + return bound; +} - if (nC > 0) - { - error = knitro::KN_get_con_values_all(m_kc.get(), m_result.con_values.data()); - check_error(error); +double KNITROModel::get_mip_relative_gap() +{ + double gap; + int error = knitro::KN_get_mip_rel_gap(m_kc.get(), &gap); + _check_error(error); + return gap; +} - error = knitro::KN_get_con_dual_values_all(m_kc.get(), m_result.con_duals.data()); - check_error(error); - for (size_t i = 0; i < nC; i++) - { - m_result.con_duals[i] = -m_result.con_duals[i]; - } - } +double KNITROModel::get_solve_time() +{ + double time; + int error = knitro::KN_get_solve_time_real(m_kc.get(), &time); + _check_error(error); + return time; +} - error = knitro::KN_get_obj_value(m_kc.get(), &m_result.obj_val); - check_error(error); +// Internal helpers +void KNITROModel::_check_dirty() const +{ + if (m_is_dirty) + { + throw std::runtime_error("No solution available"); + } +} - m_result.status = m_solve_status; - m_result.is_valid = true; +KNINT KNITROModel::_variable_index(const VariableIndex &variable) +{ + return variable.index; } -void KNITROModel::optimize() +KNINT KNITROModel::_constraint_index(const ConstraintIndex &constraint) { - _pre_solve(); - _solve(); - _post_solve(); - m_is_dirty = false; + return constraint.index; } From 4a4d59c69f1f6bd3d6b30255e687d86ee612939a Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Mon, 9 Feb 2026 21:01:02 -0500 Subject: [PATCH 19/23] clean up and refactor --- include/pyoptinterface/knitro_model.hpp | 104 ++++++--- lib/knitro_model.cpp | 292 ++++++++---------------- 2 files changed, 171 insertions(+), 225 deletions(-) diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp index 30f14935..248bcf52 100644 --- a/include/pyoptinterface/knitro_model.hpp +++ b/include/pyoptinterface/knitro_model.hpp @@ -63,19 +63,16 @@ B(KN_add_con_quadratic_term); \ B(KN_chg_con_linear_term); \ B(KN_add_eval_callback); \ - B(KN_del_obj_eval_callback_all); \ - B(KN_del_eval_callbacks); \ B(KN_set_cb_user_params); \ B(KN_set_cb_grad); \ B(KN_set_cb_hess); \ + B(KN_del_obj_eval_callback_all); \ B(KN_solve); \ - B(KN_get_obj_value); \ - B(KN_get_number_cons); \ - B(KN_get_number_vars); \ B(KN_get_var_primal_value); \ B(KN_get_var_dual_value); \ B(KN_get_con_value); \ B(KN_get_con_dual_value); \ + B(KN_get_obj_value); \ B(KN_get_number_iters); \ B(KN_get_mip_number_nodes); \ B(KN_get_mip_relaxation_bnd); \ @@ -153,7 +150,8 @@ struct CallbackEvaluator void setup() { fun.optimize(); - CppAD::sparse_rc> jac_pattern_in(fun.Range(), fun_rows.size(), fun_rows.size()); + CppAD::sparse_rc> jac_pattern_in(fun.Range(), fun_rows.size(), + fun_rows.size()); for (size_t k = 0; k < fun_rows.size(); k++) { jac_pattern_in.set(k, fun_rows[k], fun_rows[k]); @@ -296,26 +294,26 @@ class KNITROModel : public OnesideLinearConstraintMixin, // Model information double get_infinity() const; - std::string get_solver_name(); - std::string get_release(); + std::string get_solver_name() const; + std::string get_release() const; // Variable functions VariableIndex add_variable(VariableDomain domain = VariableDomain::Continuous, double lb = -KN_INFINITY, double ub = KN_INFINITY, const char *name = nullptr); void delete_variable(const VariableIndex &variable); - double get_variable_lb(const VariableIndex &variable); - double get_variable_ub(const VariableIndex &variable); + double get_variable_lb(const VariableIndex &variable) const; + double get_variable_ub(const VariableIndex &variable) const; void set_variable_lb(const VariableIndex &variable, double lb); void set_variable_ub(const VariableIndex &variable, double ub); void set_variable_bounds(const VariableIndex &variable, double lb, double ub); - double get_variable_value(const VariableIndex &variable); + double get_variable_value(const VariableIndex &variable) const; void set_variable_start(const VariableIndex &variable, double start); - std::string get_variable_name(const VariableIndex &variable); + std::string get_variable_name(const VariableIndex &variable) const; void set_variable_name(const VariableIndex &variable, const std::string &name); void set_variable_domain(const VariableIndex &variable, VariableDomain domain); - double get_variable_rc(const VariableIndex &variable); - std::string pprint_variable(const VariableIndex &variable); + double get_variable_rc(const VariableIndex &variable) const; + std::string pprint_variable(const VariableIndex &variable) const; // Constraint functions ConstraintIndex add_linear_constraint(const ScalarAffineFunction &f, ConstraintSense sense, @@ -338,13 +336,13 @@ class KNITROModel : public OnesideLinearConstraintMixin, const ExpressionHandle &result, ConstraintSense sense, double rhs, const char *name = nullptr); - void delete_constraint(ConstraintIndex constraint); + void delete_constraint(const ConstraintIndex &constraint); void set_constraint_name(const ConstraintIndex &constraint, const std::string &name); - std::string get_constraint_name(const ConstraintIndex &constraint); - double get_constraint_primal(const ConstraintIndex &constraint); - double get_constraint_dual(const ConstraintIndex &constraint); + std::string get_constraint_name(const ConstraintIndex &constraint) const; + double get_constraint_primal(const ConstraintIndex &constraint) const; + double get_constraint_dual(const ConstraintIndex &constraint) const; void set_normalized_rhs(const ConstraintIndex &constraint, double rhs); - double get_normalized_rhs(const ConstraintIndex &constraint); + double get_normalized_rhs(const ConstraintIndex &constraint) const; void set_normalized_coefficient(const ConstraintIndex &constraint, const VariableIndex &variable, double coefficient); @@ -353,19 +351,19 @@ class KNITROModel : public OnesideLinearConstraintMixin, void set_objective(const ScalarQuadraticFunction &f, ObjectiveSense sense); void set_objective(const ExprBuilder &expr, ObjectiveSense sense); void add_single_nl_objective(ExpressionGraph &graph, const ExpressionHandle &result); - double get_obj_value(); + double get_obj_value() const; void set_obj_sense(ObjectiveSense sense); - ObjectiveSense get_obj_sense(); + ObjectiveSense get_obj_sense() const; // Solve functions void optimize(); // Solve information - size_t get_number_iterations(); - size_t get_mip_node_count(); - double get_obj_bound(); - double get_mip_relative_gap(); - double get_solve_time(); + size_t get_number_iterations() const; + size_t get_mip_node_count() const; + double get_obj_bound() const; + double get_mip_relative_gap() const; + double get_solve_time() const; template void set_raw_parameter(const std::string &name, T value) @@ -444,8 +442,8 @@ class KNITROModel : public OnesideLinearConstraintMixin, // Internal helpers void _check_error(int error) const; void _check_dirty() const; - KNINT _variable_index(const VariableIndex &variable); - KNINT _constraint_index(const ConstraintIndex &constraint); + KNINT _variable_index(const VariableIndex &variable) const; + KNINT _constraint_index(const ConstraintIndex &constraint) const; // Member variables std::unique_ptr m_kc = nullptr; @@ -579,9 +577,7 @@ class KNITROModel : public OnesideLinearConstraintMixin, { _reset_objective(); setter(); - int goal = knitro_obj_goal(sense); - int error = knitro::KN_set_obj_goal(m_kc.get(), goal); - _check_error(error); + set_obj_sense(sense); m_is_dirty = true; } @@ -629,8 +625,49 @@ class KNITROModel : public OnesideLinearConstraintMixin, m_evaluators.push_back(std::move(evaluator_ptr)); } + template + using Getter = std::function; + template + using Setter = std::function; + template + using IndexGetter = std::function; + template + using IndexSetter = std::function; + + template + V _get_value(IndexGetter get, KNINT index) const + { + V value; + int error = get(m_kc.get(), index, &value); + _check_error(error); + return value; + } + + template + void _set_value(IndexSetter set, KNINT index, V value) + { + int error = set(m_kc.get(), index, value); + _check_error(error); + } + + template + V _get_value(Getter get) const + { + V value; + int error = get(m_kc.get(), &value); + _check_error(error); + return value; + } + + template + void _set_value(Setter set, V value) + { + int error = set(m_kc.get(), value); + _check_error(error); + } + template - std::string _get_name(KNINT index, F get, const char *prefix) const + std::string _get_name(F get, KNINT index, const char *prefix) const { char name[1024]; name[0] = '\0'; @@ -648,10 +685,9 @@ class KNITROModel : public OnesideLinearConstraintMixin, } template - void _set_name(KNINT index, const std::string &name, F set) + void _set_name(F set, KNINT index, const std::string &name) { int error = set(m_kc.get(), index, name.c_str()); _check_error(error); - m_is_dirty = true; } }; diff --git a/lib/knitro_model.cpp b/lib/knitro_model.cpp index fff8ea95..16cf9283 100644 --- a/lib/knitro_model.cpp +++ b/lib/knitro_model.cpp @@ -88,12 +88,12 @@ double KNITROModel::get_infinity() const return KN_INFINITY; } -std::string KNITROModel::get_solver_name() +std::string KNITROModel::get_solver_name() const { return std::string("KNITRO"); } -std::string KNITROModel::get_release() +std::string KNITROModel::get_release() const { constexpr int buf_size = 20; char release[buf_size]; @@ -113,8 +113,7 @@ VariableIndex KNITROModel::add_variable(VariableDomain domain, double lb, double VariableIndex variable(indexVar); int var_type = knitro_var_type(domain); - error = knitro::KN_set_var_type(m_kc.get(), indexVar, var_type); - _check_error(error); + _set_value(knitro::KN_set_var_type, indexVar, var_type); if (var_type == KN_VARTYPE_BINARY) { @@ -122,15 +121,12 @@ VariableIndex KNITROModel::add_variable(VariableDomain domain, double lb, double ub = (ub > 1.0) ? 1.0 : ub; } - error = knitro::KN_set_var_lobnd(m_kc.get(), indexVar, lb); - _check_error(error); - error = knitro::KN_set_var_upbnd(m_kc.get(), indexVar, ub); - _check_error(error); + _set_value(knitro::KN_set_var_lobnd, indexVar, lb); + _set_value(knitro::KN_set_var_upbnd, indexVar, ub); if (!is_name_empty(name)) { - int error = knitro::KN_set_var_name(m_kc.get(), indexVar, name); - _check_error(error); + _set_name(knitro::KN_set_var_name, indexVar, name); } n_vars++; @@ -139,42 +135,28 @@ VariableIndex KNITROModel::add_variable(VariableDomain domain, double lb, double return variable; } -double KNITROModel::get_variable_lb(const VariableIndex &variable) +double KNITROModel::get_variable_lb(const VariableIndex &variable) const { KNINT indexVar = _variable_index(variable); - int error; - double lb; - - error = knitro::KN_get_var_lobnd(m_kc.get(), indexVar, &lb); - _check_error(error); - return lb; + return _get_value(knitro::KN_get_var_lobnd, indexVar); } -double KNITROModel::get_variable_ub(const VariableIndex &variable) +double KNITROModel::get_variable_ub(const VariableIndex &variable) const { KNINT indexVar = _variable_index(variable); - int error; - double ub; - - error = knitro::KN_get_var_upbnd(m_kc.get(), indexVar, &ub); - _check_error(error); - return ub; + return _get_value(knitro::KN_get_var_upbnd, indexVar); } void KNITROModel::set_variable_lb(const VariableIndex &variable, double lb) { KNINT indexVar = _variable_index(variable); - int error = knitro::KN_set_var_lobnd(m_kc.get(), indexVar, lb); - _check_error(error); - m_is_dirty = true; + _set_value(knitro::KN_set_var_lobnd, indexVar, lb); } void KNITROModel::set_variable_ub(const VariableIndex &variable, double ub) { - KNLONG indexVar = _variable_index(variable); - int error = knitro::KN_set_var_upbnd(m_kc.get(), indexVar, ub); - _check_error(error); - m_is_dirty = true; + KNINT indexVar = _variable_index(variable); + _set_value(knitro::KN_set_var_upbnd, indexVar, ub); } void KNITROModel::set_variable_bounds(const VariableIndex &variable, double lb, double ub) @@ -183,94 +165,76 @@ void KNITROModel::set_variable_bounds(const VariableIndex &variable, double lb, set_variable_ub(variable, ub); } -double KNITROModel::get_variable_value(const VariableIndex &variable) +double KNITROModel::get_variable_value(const VariableIndex &variable) const { _check_dirty(); KNINT indexVar = _variable_index(variable); - double value; - int error = knitro::KN_get_var_primal_value(m_kc.get(), indexVar, &value); - _check_error(error); - return value; + return _get_value(knitro::KN_get_var_primal_value, indexVar); } void KNITROModel::set_variable_start(const VariableIndex &variable, double start) { KNINT indexVar = _variable_index(variable); - int error = knitro::KN_set_var_primal_init_value(m_kc.get(), indexVar, start); - _check_error(error); + _set_value(knitro::KN_set_var_primal_init_value, indexVar, start); } -std::string KNITROModel::get_variable_name(const VariableIndex &variable) +std::string KNITROModel::get_variable_name(const VariableIndex &variable) const { KNINT indexVar = _variable_index(variable); - return _get_name(indexVar, knitro::KN_get_var_name, "x"); + return _get_name(knitro::KN_get_var_name, indexVar, "x"); } void KNITROModel::set_variable_name(const VariableIndex &variable, const std::string &name) { KNINT indexVar = _variable_index(variable); - _set_name(indexVar, name, knitro::KN_set_var_name); + _set_name(knitro::KN_set_var_name, indexVar, name); } void KNITROModel::set_variable_domain(const VariableIndex &variable, VariableDomain domain) { KNINT indexVar = _variable_index(variable); int var_type = knitro_var_type(domain); - int error; - double lb = -KN_INFINITY; - double ub = KN_INFINITY; + double lb = -get_infinity(); + double ub = get_infinity(); if (var_type == KN_VARTYPE_BINARY) { - error = knitro::KN_get_var_lobnd(m_kc.get(), indexVar, &lb); - _check_error(error); - error = knitro::KN_get_var_upbnd(m_kc.get(), indexVar, &ub); - _check_error(error); + lb = _get_value(knitro::KN_get_var_lobnd, indexVar); + ub = _get_value(knitro::KN_get_var_upbnd, indexVar); } - error = knitro::KN_set_var_type(m_kc.get(), indexVar, var_type); - _check_error(error); + _set_value(knitro::KN_set_var_type, indexVar, var_type); if (var_type == KN_VARTYPE_BINARY) { lb = (lb < 0.0) ? 0.0 : lb; ub = (ub > 1.0) ? 1.0 : ub; - error = knitro::KN_set_var_lobnd(m_kc.get(), indexVar, lb); - _check_error(error); - error = knitro::KN_set_var_upbnd(m_kc.get(), indexVar, ub); - _check_error(error); + _set_value(knitro::KN_set_var_lobnd, indexVar, lb); + _set_value(knitro::KN_set_var_upbnd, indexVar, ub); } m_is_dirty = true; } -double KNITROModel::get_variable_rc(const VariableIndex &variable) +double KNITROModel::get_variable_rc(const VariableIndex &variable) const { _check_dirty(); KNINT indexVar = _variable_index(variable); - double value; - int error = knitro::KN_get_var_dual_value(m_kc.get(), indexVar, &value); - _check_error(error); - return -value; + double dual = _get_value(knitro::KN_get_var_dual_value, indexVar); + return -dual; } void KNITROModel::delete_variable(const VariableIndex &variable) { KNINT indexVar = _variable_index(variable); - int error; - - error = knitro::KN_set_var_type(m_kc.get(), indexVar, KN_VARTYPE_CONTINUOUS); - _check_error(error); - error = knitro::KN_set_var_lobnd(m_kc.get(), indexVar, -KN_INFINITY); - _check_error(error); - error = knitro::KN_set_var_upbnd(m_kc.get(), indexVar, KN_INFINITY); - _check_error(error); - + _set_value(knitro::KN_set_var_type, indexVar, KN_VARTYPE_CONTINUOUS); + _set_value(knitro::KN_set_var_lobnd, indexVar, -get_infinity()); + _set_value(knitro::KN_set_var_upbnd, indexVar, get_infinity()); n_vars--; m_is_dirty = true; } -std::string KNITROModel::pprint_variable(const VariableIndex &variable) +std::string KNITROModel::pprint_variable(const VariableIndex &variable) const { return get_variable_name(variable); } @@ -323,7 +287,7 @@ ConstraintIndex KNITROModel::add_second_order_cone_constraint( auto setter = [this, &variables](const ConstraintIndex &constraint) { _set_second_order_cone_constraint_rotated(constraint, variables); }; - std::pair interval = {0.0, KN_INFINITY}; + std::pair interval = {0.0, get_infinity()}; return _add_constraint_impl(ConstraintType::Cone, interval, name, &n_coniccons, setter); } else @@ -331,7 +295,7 @@ ConstraintIndex KNITROModel::add_second_order_cone_constraint( auto setter = [this, &variables](const ConstraintIndex &constraint) { _set_second_order_cone_constraint(constraint, variables); }; - std::pair interval = {0.0, KN_INFINITY}; + std::pair interval = {0.0, get_infinity()}; return _add_constraint_impl(ConstraintType::Cone, interval, name, &n_coniccons, setter); } } @@ -369,11 +333,11 @@ std::tuple KNITROModel::_sense_to_interval(ConstraintSense sense switch (sense) { case ConstraintSense::LessEqual: - return {-KN_INFINITY, rhs}; + return {-get_infinity(), rhs}; case ConstraintSense::Equal: return {rhs, rhs}; case ConstraintSense::GreaterEqual: - return {rhs, KN_INFINITY}; + return {rhs, get_infinity()}; default: throw std::runtime_error("Unknown constraint sense"); } @@ -399,20 +363,17 @@ void KNITROModel::_set_second_order_cone_constraint(const ConstraintIndex &const const Vector &variables) { KNINT indexCon = _constraint_index(constraint); - int error; KNINT indexCon0; - error = knitro::KN_add_con(m_kc.get(), &indexCon0); + int error = knitro::KN_add_con(m_kc.get(), &indexCon0); _check_error(error); KNINT indexVar0 = _variable_index(variables[0]); error = knitro::KN_add_con_linear_term(m_kc.get(), indexCon0, indexVar0, 1.0); _check_error(error); - error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon0, 0.0); - _check_error(error); - error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon0, KN_INFINITY); - _check_error(error); + _set_value(knitro::KN_set_con_lobnd, indexCon0, 0.0); + _set_value(knitro::KN_set_con_upbnd, indexCon0, KN_INFINITY); error = knitro::KN_add_con_quadratic_term(m_kc.get(), indexCon, indexVar0, indexVar0, 1.0); _check_error(error); @@ -435,10 +396,9 @@ void KNITROModel::_set_second_order_cone_constraint_rotated(const ConstraintInde const Vector &variables) { KNINT indexCon = _constraint_index(constraint); - int error; KNINT indexCon0, indexCon1; - error = knitro::KN_add_con(m_kc.get(), &indexCon0); + int error = knitro::KN_add_con(m_kc.get(), &indexCon0); _check_error(error); error = knitro::KN_add_con(m_kc.get(), &indexCon1); _check_error(error); @@ -450,14 +410,10 @@ void KNITROModel::_set_second_order_cone_constraint_rotated(const ConstraintInde error = knitro::KN_add_con_linear_term(m_kc.get(), indexCon1, indexVar1, 1.0); _check_error(error); - error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon0, 0.0); - _check_error(error); - error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon0, KN_INFINITY); - _check_error(error); - error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon1, 0.0); - _check_error(error); - error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon1, KN_INFINITY); - _check_error(error); + _set_value(knitro::KN_set_con_lobnd, indexCon0, 0.0); + _set_value(knitro::KN_set_con_upbnd, indexCon0, KN_INFINITY); + _set_value(knitro::KN_set_con_lobnd, indexCon1, 0.0); + _set_value(knitro::KN_set_con_upbnd, indexCon1, KN_INFINITY); size_t nnz = variables.size() - 2; std::vector indexCons(nnz, indexCon); @@ -476,15 +432,11 @@ void KNITROModel::_set_second_order_cone_constraint_rotated(const ConstraintInde m_soc_aux_cons[indexCon] = std::make_pair(indexCon0, indexCon1); } -void KNITROModel::delete_constraint(ConstraintIndex constraint) +void KNITROModel::delete_constraint(const ConstraintIndex &constraint) { KNINT indexCon = _constraint_index(constraint); - int error; - - error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon, -KN_INFINITY); - _check_error(error); - error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon, KN_INFINITY); - _check_error(error); + _set_value(knitro::KN_set_con_lobnd, indexCon, -get_infinity()); + _set_value(knitro::KN_set_con_upbnd, indexCon, get_infinity()); n_cons--; switch (constraint.type) @@ -522,10 +474,8 @@ void KNITROModel::delete_constraint(ConstraintIndex constraint) for (auto const &con : aux_cons) { - error = knitro::KN_set_con_lobnd(m_kc.get(), con, -KN_INFINITY); - _check_error(error); - error = knitro::KN_set_con_upbnd(m_kc.get(), con, KN_INFINITY); - _check_error(error); + _set_value(knitro::KN_set_con_lobnd, con, -get_infinity()); + _set_value(knitro::KN_set_con_upbnd, con, get_infinity()); } m_soc_aux_cons.erase(it); @@ -537,33 +487,27 @@ void KNITROModel::delete_constraint(ConstraintIndex constraint) void KNITROModel::set_constraint_name(const ConstraintIndex &constraint, const std::string &name) { KNINT indexCon = _constraint_index(constraint); - _set_name(indexCon, name, knitro::KN_set_con_name); + _set_name(knitro::KN_set_con_name, indexCon, name); } -std::string KNITROModel::get_constraint_name(const ConstraintIndex &constraint) +std::string KNITROModel::get_constraint_name(const ConstraintIndex &constraint) const { KNINT indexCon = _constraint_index(constraint); - return _get_name(indexCon, knitro::KN_get_con_name, "c"); + return _get_name(knitro::KN_get_con_name, indexCon, "c"); } -double KNITROModel::get_constraint_primal(const ConstraintIndex &constraint) +double KNITROModel::get_constraint_primal(const ConstraintIndex &constraint) const { _check_dirty(); KNINT indexCon = _constraint_index(constraint); - double value; - int error = knitro::KN_get_con_value(m_kc.get(), indexCon, &value); - _check_error(error); - return value; + return _get_value(knitro::KN_get_con_value, indexCon); } -double KNITROModel::get_constraint_dual(const ConstraintIndex &constraint) +double KNITROModel::get_constraint_dual(const ConstraintIndex &constraint) const { _check_dirty(); KNINT indexCon = _constraint_index(constraint); - double value; - int error = knitro::KN_get_con_dual_value(m_kc.get(), indexCon, &value); - _check_error(error); - return -value; + return _get_value(knitro::KN_get_con_dual_value, indexCon); } void KNITROModel::set_normalized_rhs(const ConstraintIndex &constraint, double rhs) @@ -571,38 +515,32 @@ void KNITROModel::set_normalized_rhs(const ConstraintIndex &constraint, double r KNINT indexCon = _constraint_index(constraint); auto flag = m_con_sense_flags[indexCon]; - int error; - if (flag & CON_LOBND) { - error = knitro::KN_set_con_lobnd(m_kc.get(), indexCon, rhs); - _check_error(error); + _set_value(knitro::KN_set_con_lobnd, indexCon, rhs); } if (flag & CON_UPBND) { - error = knitro::KN_set_con_upbnd(m_kc.get(), indexCon, rhs); - _check_error(error); + _set_value(knitro::KN_set_con_upbnd, indexCon, rhs); } m_is_dirty = true; } -double KNITROModel::get_normalized_rhs(const ConstraintIndex &constraint) +double KNITROModel::get_normalized_rhs(const ConstraintIndex &constraint) const { KNINT indexCon = _constraint_index(constraint); - auto flag = m_con_sense_flags[indexCon]; + auto it = m_con_sense_flags.find(indexCon); + uint8_t flag = (it != m_con_sense_flags.end()) ? it->second : CON_UPBND; double rhs; - int error; if (flag & CON_UPBND) { - error = knitro::KN_get_con_upbnd(m_kc.get(), indexCon, &rhs); - _check_error(error); + rhs = _get_value(knitro::KN_get_con_upbnd, indexCon); } else { - error = knitro::KN_get_con_lobnd(m_kc.get(), indexCon, &rhs); - _check_error(error); + rhs = _get_value(knitro::KN_get_con_lobnd, indexCon); } return rhs; } @@ -612,12 +550,11 @@ void KNITROModel::set_normalized_coefficient(const ConstraintIndex &constraint, { KNINT indexCon = _constraint_index(constraint); KNINT indexVar = _variable_index(variable); - int error; // NOTE: To make sure the coefficient is updated correctly, // we need to call KN_update before changing the linear term _update(); - error = knitro::KN_chg_con_linear_term(m_kc.get(), indexCon, indexVar, coefficient); + int error = knitro::KN_chg_con_linear_term(m_kc.get(), indexCon, indexVar, coefficient); _check_error(error); m_is_dirty = true; } @@ -626,14 +563,13 @@ void KNITROModel::_set_linear_constraint(const ConstraintIndex &constraint, const ScalarAffineFunction &function) { KNINT indexCon = _constraint_index(constraint); - int error; if (function.constant.has_value()) { double constant = function.constant.value(); if (constant != 0.0) { - error = knitro::KN_add_con_constant(m_kc.get(), indexCon, constant); + int error = knitro::KN_add_con_constant(m_kc.get(), indexCon, constant); _check_error(error); } } @@ -648,7 +584,7 @@ void KNITROModel::_set_linear_constraint(const ConstraintIndex &constraint, KNINT *indexVars = ptr_form.index; double *coefs = ptr_form.value; - error = + int error = knitro::KN_add_con_linear_struct(m_kc.get(), nnz, indexCons.data(), indexVars, coefs); _check_error(error); } @@ -658,7 +594,6 @@ void KNITROModel::_set_quadratic_constraint(const ConstraintIndex &constraint, const ScalarQuadraticFunction &function) { KNINT indexCon = _constraint_index(constraint); - int error; if (function.affine_part.has_value()) { @@ -674,8 +609,8 @@ void KNITROModel::_set_quadratic_constraint(const ConstraintIndex &constraint, KNINT *indexVars1 = ptr_form.row; KNINT *indexVars2 = ptr_form.col; double *coefs = ptr_form.value; - error = knitro::KN_add_con_quadratic_struct(m_kc.get(), nnz, indexCons.data(), indexVars1, - indexVars2, coefs); + int error = knitro::KN_add_con_quadratic_struct(m_kc.get(), nnz, indexCons.data(), + indexVars1, indexVars2, coefs); _check_error(error); } } @@ -724,15 +659,12 @@ void KNITROModel::add_single_nl_objective(ExpressionGraph &graph, const Expressi void KNITROModel::set_obj_sense(ObjectiveSense sense) { int goal = knitro_obj_goal(sense); - int error = knitro::KN_set_obj_goal(m_kc.get(), goal); - _check_error(error); + _set_value(knitro::KN_set_obj_goal, goal); } -ObjectiveSense KNITROModel::get_obj_sense() +ObjectiveSense KNITROModel::get_obj_sense() const { - int goal; - int error = knitro::KN_get_obj_goal(m_kc.get(), &goal); - _check_error(error); + int goal = _get_value(knitro::KN_get_obj_goal); if (goal == KN_OBJGOAL_MINIMIZE) { return ObjectiveSense::Minimize; @@ -757,26 +689,24 @@ void KNITROModel::_add_graph(ExpressionGraph &graph) void KNITROModel::_reset_objective() { - int error; - if (m_obj_flag & OBJ_CONSTANT) { - error = knitro::KN_del_obj_constant(m_kc.get()); + int error = knitro::KN_del_obj_constant(m_kc.get()); _check_error(error); } if (m_obj_flag & OBJ_LINEAR) { - error = knitro::KN_del_obj_linear_struct_all(m_kc.get()); + int error = knitro::KN_del_obj_linear_struct_all(m_kc.get()); _check_error(error); } if (m_obj_flag & OBJ_QUADRATIC) { - error = knitro::KN_del_obj_quadratic_struct_all(m_kc.get()); + int error = knitro::KN_del_obj_quadratic_struct_all(m_kc.get()); _check_error(error); } if (m_obj_flag & OBJ_NONLINEAR) { - error = knitro::KN_del_obj_eval_callback_all(m_kc.get()); + int error = knitro::KN_del_obj_eval_callback_all(m_kc.get()); _check_error(error); for (auto &[graph, outputs] : m_pending_outputs) { @@ -789,14 +719,12 @@ void KNITROModel::_reset_objective() void KNITROModel::_set_linear_objective(const ScalarAffineFunction &function) { - int error; - if (function.constant.has_value()) { double constant = function.constant.value(); if (constant != 0.0) { - error = knitro::KN_add_obj_constant(m_kc.get(), constant); + int error = knitro::KN_add_obj_constant(m_kc.get(), constant); _check_error(error); m_obj_flag |= OBJ_CONSTANT; } @@ -809,7 +737,7 @@ void KNITROModel::_set_linear_objective(const ScalarAffineFunction &function) { KNINT *indexVars = ptr_form.index; double *coefs = ptr_form.value; - error = knitro::KN_add_obj_linear_struct(m_kc.get(), nnz, indexVars, coefs); + int error = knitro::KN_add_obj_linear_struct(m_kc.get(), nnz, indexVars, coefs); _check_error(error); m_obj_flag |= OBJ_LINEAR; } @@ -817,8 +745,6 @@ void KNITROModel::_set_linear_objective(const ScalarAffineFunction &function) void KNITROModel::_set_quadratic_objective(const ScalarQuadraticFunction &function) { - int error; - if (function.affine_part.has_value()) { _set_linear_objective(function.affine_part.value()); @@ -832,19 +758,17 @@ void KNITROModel::_set_quadratic_objective(const ScalarQuadraticFunction &functi KNINT *indexVars1 = ptr_form.row; KNINT *indexVars2 = ptr_form.col; double *coefs = ptr_form.value; - error = knitro::KN_add_obj_quadratic_struct(m_kc.get(), nnz, indexVars1, indexVars2, coefs); + int error = + knitro::KN_add_obj_quadratic_struct(m_kc.get(), nnz, indexVars1, indexVars2, coefs); _check_error(error); m_obj_flag |= OBJ_QUADRATIC; } } -double KNITROModel::get_obj_value() +double KNITROModel::get_obj_value() const { _check_dirty(); - double value; - int error = knitro::KN_get_obj_value(m_kc.get(), &value); - _check_error(error); - return value; + return _get_value(knitro::KN_get_obj_value); } void KNITROModel::_add_constraint_callback(ExpressionGraph *graph, const Outputs &outputs) @@ -934,9 +858,7 @@ void KNITROModel::_pre_solve() void KNITROModel::_solve() { - int error = knitro::KN_solve(m_kc.get()); - // NOTE: KN_solve returns solve status, not an error code - m_solve_status = error; + m_solve_status = knitro::KN_solve(m_kc.get()); } void KNITROModel::_post_solve() @@ -952,48 +874,36 @@ void KNITROModel::optimize() } // Solve information -size_t KNITROModel::get_number_iterations() +size_t KNITROModel::get_number_iterations() const { - int error; - int iters; - - error = knitro::KN_get_number_iters(m_kc.get(), &iters); - _check_error(error); + _check_dirty(); + int iters = _get_value(knitro::KN_get_number_iters); return static_cast(iters); } -size_t KNITROModel::get_mip_node_count() +size_t KNITROModel::get_mip_node_count() const { - int error; - int nodes; - - error = knitro::KN_get_mip_number_nodes(m_kc.get(), &nodes); - _check_error(error); + _check_dirty(); + int nodes = _get_value(knitro::KN_get_mip_number_nodes); return static_cast(nodes); } -double KNITROModel::get_obj_bound() +double KNITROModel::get_obj_bound() const { - double bound; - int error = knitro::KN_get_mip_relaxation_bnd(m_kc.get(), &bound); - _check_error(error); - return bound; + _check_dirty(); + return _get_value(knitro::KN_get_mip_relaxation_bnd); } -double KNITROModel::get_mip_relative_gap() +double KNITROModel::get_mip_relative_gap() const { - double gap; - int error = knitro::KN_get_mip_rel_gap(m_kc.get(), &gap); - _check_error(error); - return gap; + _check_dirty(); + return _get_value(knitro::KN_get_mip_rel_gap); } -double KNITROModel::get_solve_time() +double KNITROModel::get_solve_time() const { - double time; - int error = knitro::KN_get_solve_time_real(m_kc.get(), &time); - _check_error(error); - return time; + _check_dirty(); + return _get_value(knitro::KN_get_solve_time_real); } // Internal helpers @@ -1001,16 +911,16 @@ void KNITROModel::_check_dirty() const { if (m_is_dirty) { - throw std::runtime_error("No solution available"); + throw std::runtime_error("Model has been modified since last solve. Call optimize()..."); } } -KNINT KNITROModel::_variable_index(const VariableIndex &variable) +KNINT KNITROModel::_variable_index(const VariableIndex &variable) const { return variable.index; } -KNINT KNITROModel::_constraint_index(const ConstraintIndex &constraint) +KNINT KNITROModel::_constraint_index(const ConstraintIndex &constraint) const { return constraint.index; } From c00a6fc91a4b72e9f9f64e6e6a42cdad45a98da8 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Mon, 9 Feb 2026 21:48:03 -0500 Subject: [PATCH 20/23] clean and refactor. --- include/pyoptinterface/knitro_model.hpp | 142 +++++++++++++----------- lib/knitro_model.cpp | 85 ++++++-------- lib/knitro_model_ext.cpp | 2 +- 3 files changed, 113 insertions(+), 116 deletions(-) diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp index 248bcf52..830f1013 100644 --- a/include/pyoptinterface/knitro_model.hpp +++ b/include/pyoptinterface/knitro_model.hpp @@ -277,6 +277,52 @@ struct Outputs std::vector cons; }; +inline bool is_name_empty(const char *name) +{ + return name == nullptr || name[0] == '\0'; +} + +inline int knitro_var_type(VariableDomain domain) +{ + switch (domain) + { + case VariableDomain::Continuous: + return KN_VARTYPE_CONTINUOUS; + case VariableDomain::Integer: + return KN_VARTYPE_INTEGER; + case VariableDomain::Binary: + return KN_VARTYPE_BINARY; + default: + throw std::runtime_error("Unknown variable domain"); + } +} + +inline int knitro_obj_goal(ObjectiveSense sense) +{ + switch (sense) + { + case ObjectiveSense::Minimize: + return KN_OBJGOAL_MINIMIZE; + case ObjectiveSense::Maximize: + return KN_OBJGOAL_MAXIMIZE; + default: + throw std::runtime_error("Unknown objective sense"); + } +} + +inline ObjectiveSense knitro_obj_sense(int goal) +{ + switch (goal) + { + case KN_OBJGOAL_MINIMIZE: + return ObjectiveSense::Minimize; + case KN_OBJGOAL_MAXIMIZE: + return ObjectiveSense::Maximize; + default: + throw std::runtime_error("Unknown objective goal"); + } +} + class KNITROModel : public OnesideLinearConstraintMixin, public TwosideLinearConstraintMixin, public OnesideQuadraticConstraintMixin, @@ -368,34 +414,30 @@ class KNITROModel : public OnesideLinearConstraintMixin, template void set_raw_parameter(const std::string &name, T value) { - int error; - int param_id; - error = knitro::KN_get_param_id(m_kc.get(), name.c_str(), ¶m_id); - _check_error(error); + int param_id = _get_value(knitro::KN_get_param_id, name.c_str()); set_raw_parameter(param_id, value); } template void set_raw_parameter(int param_id, T value) { - int error; if constexpr (std::is_same_v) { - error = knitro::KN_set_int_param(m_kc.get(), param_id, value); + _set_value(knitro::KN_set_int_param, param_id, value); } else if constexpr (std::is_same_v) { - error = knitro::KN_set_double_param(m_kc.get(), param_id, value); + _set_value(knitro::KN_set_double_param, param_id, value); } else if constexpr (std::is_same_v || std::is_same_v) { if constexpr (std::is_same_v) { - error = knitro::KN_set_char_param(m_kc.get(), param_id, value.c_str()); + _set_value(knitro::KN_set_char_param, param_id, value.c_str()); } else { - error = knitro::KN_set_char_param(m_kc.get(), param_id, value); + _set_value(knitro::KN_set_char_param, param_id, value); } } else @@ -404,39 +446,31 @@ class KNITROModel : public OnesideLinearConstraintMixin, std::is_same_v || std::is_same_v, "T must be int, double, std::string, or const char*"); } - _check_error(error); } template T get_raw_parameter(const std::string &name) { - int error; - int param_id; - error = knitro::KN_get_param_id(m_kc.get(), name.c_str(), ¶m_id); - _check_error(error); + int param_id = _get_value(knitro::KN_get_param_id, name.c_str()); return get_raw_parameter(param_id); } template T get_raw_parameter(int param_id) { - int error; - T value; if constexpr (std::is_same_v) { - error = knitro::KN_get_int_param(m_kc.get(), param_id, &value); + return _get_value(knitro::KN_get_int_param, param_id); } else if constexpr (std::is_same_v) { - error = knitro::KN_get_double_param(m_kc.get(), param_id, &value); + return _get_value(knitro::KN_get_double_param, param_id); } else { static_assert(std::is_same_v || std::is_same_v, "T must be int or double for get_raw_parameter"); } - _check_error(error); - return value; } // Internal helpers @@ -466,39 +500,6 @@ class KNITROModel : public OnesideLinearConstraintMixin, bool m_is_dirty = true; int m_solve_status = 0; - static bool is_name_empty(const char *name) - { - return name == nullptr || name[0] == '\0'; - } - - static int knitro_var_type(VariableDomain domain) - { - switch (domain) - { - case VariableDomain::Continuous: - return KN_VARTYPE_CONTINUOUS; - case VariableDomain::Integer: - return KN_VARTYPE_INTEGER; - case VariableDomain::Binary: - return KN_VARTYPE_BINARY; - default: - throw std::runtime_error("Unknown variable domain"); - } - } - - static int knitro_obj_goal(ObjectiveSense sense) - { - switch (sense) - { - case ObjectiveSense::Minimize: - return KN_OBJGOAL_MINIMIZE; - case ObjectiveSense::Maximize: - return KN_OBJGOAL_MAXIMIZE; - default: - throw std::runtime_error("Unknown objective sense"); - } - } - private: std::tuple _sense_to_interval(ConstraintSense sense, double rhs); void _update_con_sense_flags(const ConstraintIndex &constraint, ConstraintSense sense); @@ -629,40 +630,41 @@ class KNITROModel : public OnesideLinearConstraintMixin, using Getter = std::function; template using Setter = std::function; - template - using IndexGetter = std::function; - template - using IndexSetter = std::function; template - V _get_value(IndexGetter get, KNINT index) const + V _get_value(Getter get) const { V value; - int error = get(m_kc.get(), index, &value); + int error = get(m_kc.get(), &value); _check_error(error); return value; } template - void _set_value(IndexSetter set, KNINT index, V value) + void _set_value(Setter set, V value) { - int error = set(m_kc.get(), index, value); + int error = set(m_kc.get(), value); _check_error(error); } - template - V _get_value(Getter get) const + template + using GetterMap = std::function; + template + using SetterMap = std::function; + + template + V _get_value(GetterMap get, K key) const { V value; - int error = get(m_kc.get(), &value); + int error = get(m_kc.get(), key, &value); _check_error(error); return value; } - template - void _set_value(Setter set, V value) + template + void _set_value(SetterMap set, K key, V value) { - int error = set(m_kc.get(), value); + int error = set(m_kc.get(), key, value); _check_error(error); } @@ -690,4 +692,10 @@ class KNITROModel : public OnesideLinearConstraintMixin, int error = set(m_kc.get(), index, name.c_str()); _check_error(error); } + + template + KNINT _get_index(const I &index) const + { + return index.index; + } }; diff --git a/lib/knitro_model.cpp b/lib/knitro_model.cpp index 16cf9283..bcdf7839 100644 --- a/lib/knitro_model.cpp +++ b/lib/knitro_model.cpp @@ -113,7 +113,7 @@ VariableIndex KNITROModel::add_variable(VariableDomain domain, double lb, double VariableIndex variable(indexVar); int var_type = knitro_var_type(domain); - _set_value(knitro::KN_set_var_type, indexVar, var_type); + _set_value(knitro::KN_set_var_type, indexVar, var_type); if (var_type == KN_VARTYPE_BINARY) { @@ -121,8 +121,8 @@ VariableIndex KNITROModel::add_variable(VariableDomain domain, double lb, double ub = (ub > 1.0) ? 1.0 : ub; } - _set_value(knitro::KN_set_var_lobnd, indexVar, lb); - _set_value(knitro::KN_set_var_upbnd, indexVar, ub); + _set_value(knitro::KN_set_var_lobnd, indexVar, lb); + _set_value(knitro::KN_set_var_upbnd, indexVar, ub); if (!is_name_empty(name)) { @@ -138,25 +138,25 @@ VariableIndex KNITROModel::add_variable(VariableDomain domain, double lb, double double KNITROModel::get_variable_lb(const VariableIndex &variable) const { KNINT indexVar = _variable_index(variable); - return _get_value(knitro::KN_get_var_lobnd, indexVar); + return _get_value(knitro::KN_get_var_lobnd, indexVar); } double KNITROModel::get_variable_ub(const VariableIndex &variable) const { KNINT indexVar = _variable_index(variable); - return _get_value(knitro::KN_get_var_upbnd, indexVar); + return _get_value(knitro::KN_get_var_upbnd, indexVar); } void KNITROModel::set_variable_lb(const VariableIndex &variable, double lb) { KNINT indexVar = _variable_index(variable); - _set_value(knitro::KN_set_var_lobnd, indexVar, lb); + _set_value(knitro::KN_set_var_lobnd, indexVar, lb); } void KNITROModel::set_variable_ub(const VariableIndex &variable, double ub) { KNINT indexVar = _variable_index(variable); - _set_value(knitro::KN_set_var_upbnd, indexVar, ub); + _set_value(knitro::KN_set_var_upbnd, indexVar, ub); } void KNITROModel::set_variable_bounds(const VariableIndex &variable, double lb, double ub) @@ -169,13 +169,13 @@ double KNITROModel::get_variable_value(const VariableIndex &variable) const { _check_dirty(); KNINT indexVar = _variable_index(variable); - return _get_value(knitro::KN_get_var_primal_value, indexVar); + return _get_value(knitro::KN_get_var_primal_value, indexVar); } void KNITROModel::set_variable_start(const VariableIndex &variable, double start) { KNINT indexVar = _variable_index(variable); - _set_value(knitro::KN_set_var_primal_init_value, indexVar, start); + _set_value(knitro::KN_set_var_primal_init_value, indexVar, start); } std::string KNITROModel::get_variable_name(const VariableIndex &variable) const @@ -199,18 +199,18 @@ void KNITROModel::set_variable_domain(const VariableIndex &variable, VariableDom if (var_type == KN_VARTYPE_BINARY) { - lb = _get_value(knitro::KN_get_var_lobnd, indexVar); - ub = _get_value(knitro::KN_get_var_upbnd, indexVar); + lb = _get_value(knitro::KN_get_var_lobnd, indexVar); + ub = _get_value(knitro::KN_get_var_upbnd, indexVar); } - _set_value(knitro::KN_set_var_type, indexVar, var_type); + _set_value(knitro::KN_set_var_type, indexVar, var_type); if (var_type == KN_VARTYPE_BINARY) { lb = (lb < 0.0) ? 0.0 : lb; ub = (ub > 1.0) ? 1.0 : ub; - _set_value(knitro::KN_set_var_lobnd, indexVar, lb); - _set_value(knitro::KN_set_var_upbnd, indexVar, ub); + _set_value(knitro::KN_set_var_lobnd, indexVar, lb); + _set_value(knitro::KN_set_var_upbnd, indexVar, ub); } m_is_dirty = true; @@ -220,16 +220,16 @@ double KNITROModel::get_variable_rc(const VariableIndex &variable) const { _check_dirty(); KNINT indexVar = _variable_index(variable); - double dual = _get_value(knitro::KN_get_var_dual_value, indexVar); + double dual = _get_value(knitro::KN_get_var_dual_value, indexVar); return -dual; } void KNITROModel::delete_variable(const VariableIndex &variable) { KNINT indexVar = _variable_index(variable); - _set_value(knitro::KN_set_var_type, indexVar, KN_VARTYPE_CONTINUOUS); - _set_value(knitro::KN_set_var_lobnd, indexVar, -get_infinity()); - _set_value(knitro::KN_set_var_upbnd, indexVar, get_infinity()); + _set_value(knitro::KN_set_var_type, indexVar, KN_VARTYPE_CONTINUOUS); + _set_value(knitro::KN_set_var_lobnd, indexVar, -get_infinity()); + _set_value(knitro::KN_set_var_upbnd, indexVar, get_infinity()); n_vars--; m_is_dirty = true; } @@ -372,8 +372,8 @@ void KNITROModel::_set_second_order_cone_constraint(const ConstraintIndex &const error = knitro::KN_add_con_linear_term(m_kc.get(), indexCon0, indexVar0, 1.0); _check_error(error); - _set_value(knitro::KN_set_con_lobnd, indexCon0, 0.0); - _set_value(knitro::KN_set_con_upbnd, indexCon0, KN_INFINITY); + _set_value(knitro::KN_set_con_lobnd, indexCon0, 0.0); + _set_value(knitro::KN_set_con_upbnd, indexCon0, KN_INFINITY); error = knitro::KN_add_con_quadratic_term(m_kc.get(), indexCon, indexVar0, indexVar0, 1.0); _check_error(error); @@ -410,10 +410,10 @@ void KNITROModel::_set_second_order_cone_constraint_rotated(const ConstraintInde error = knitro::KN_add_con_linear_term(m_kc.get(), indexCon1, indexVar1, 1.0); _check_error(error); - _set_value(knitro::KN_set_con_lobnd, indexCon0, 0.0); - _set_value(knitro::KN_set_con_upbnd, indexCon0, KN_INFINITY); - _set_value(knitro::KN_set_con_lobnd, indexCon1, 0.0); - _set_value(knitro::KN_set_con_upbnd, indexCon1, KN_INFINITY); + _set_value(knitro::KN_set_con_lobnd, indexCon0, 0.0); + _set_value(knitro::KN_set_con_upbnd, indexCon0, KN_INFINITY); + _set_value(knitro::KN_set_con_lobnd, indexCon1, 0.0); + _set_value(knitro::KN_set_con_upbnd, indexCon1, KN_INFINITY); size_t nnz = variables.size() - 2; std::vector indexCons(nnz, indexCon); @@ -435,8 +435,8 @@ void KNITROModel::_set_second_order_cone_constraint_rotated(const ConstraintInde void KNITROModel::delete_constraint(const ConstraintIndex &constraint) { KNINT indexCon = _constraint_index(constraint); - _set_value(knitro::KN_set_con_lobnd, indexCon, -get_infinity()); - _set_value(knitro::KN_set_con_upbnd, indexCon, get_infinity()); + _set_value(knitro::KN_set_con_lobnd, indexCon, -get_infinity()); + _set_value(knitro::KN_set_con_upbnd, indexCon, get_infinity()); n_cons--; switch (constraint.type) @@ -474,8 +474,8 @@ void KNITROModel::delete_constraint(const ConstraintIndex &constraint) for (auto const &con : aux_cons) { - _set_value(knitro::KN_set_con_lobnd, con, -get_infinity()); - _set_value(knitro::KN_set_con_upbnd, con, get_infinity()); + _set_value(knitro::KN_set_con_lobnd, con, -get_infinity()); + _set_value(knitro::KN_set_con_upbnd, con, get_infinity()); } m_soc_aux_cons.erase(it); @@ -500,14 +500,14 @@ double KNITROModel::get_constraint_primal(const ConstraintIndex &constraint) con { _check_dirty(); KNINT indexCon = _constraint_index(constraint); - return _get_value(knitro::KN_get_con_value, indexCon); + return _get_value(knitro::KN_get_con_value, indexCon); } double KNITROModel::get_constraint_dual(const ConstraintIndex &constraint) const { _check_dirty(); KNINT indexCon = _constraint_index(constraint); - return _get_value(knitro::KN_get_con_dual_value, indexCon); + return _get_value(knitro::KN_get_con_dual_value, indexCon); } void KNITROModel::set_normalized_rhs(const ConstraintIndex &constraint, double rhs) @@ -517,12 +517,12 @@ void KNITROModel::set_normalized_rhs(const ConstraintIndex &constraint, double r if (flag & CON_LOBND) { - _set_value(knitro::KN_set_con_lobnd, indexCon, rhs); + _set_value(knitro::KN_set_con_lobnd, indexCon, rhs); } if (flag & CON_UPBND) { - _set_value(knitro::KN_set_con_upbnd, indexCon, rhs); + _set_value(knitro::KN_set_con_upbnd, indexCon, rhs); } m_is_dirty = true; @@ -536,11 +536,11 @@ double KNITROModel::get_normalized_rhs(const ConstraintIndex &constraint) const double rhs; if (flag & CON_UPBND) { - rhs = _get_value(knitro::KN_get_con_upbnd, indexCon); + rhs = _get_value(knitro::KN_get_con_upbnd, indexCon); } else { - rhs = _get_value(knitro::KN_get_con_lobnd, indexCon); + rhs = _get_value(knitro::KN_get_con_lobnd, indexCon); } return rhs; } @@ -665,18 +665,7 @@ void KNITROModel::set_obj_sense(ObjectiveSense sense) ObjectiveSense KNITROModel::get_obj_sense() const { int goal = _get_value(knitro::KN_get_obj_goal); - if (goal == KN_OBJGOAL_MINIMIZE) - { - return ObjectiveSense::Minimize; - } - else if (goal == KN_OBJGOAL_MAXIMIZE) - { - return ObjectiveSense::Maximize; - } - else - { - throw std::runtime_error("Unknown objective goal"); - } + return knitro_obj_sense(goal); } void KNITROModel::_add_graph(ExpressionGraph &graph) @@ -917,10 +906,10 @@ void KNITROModel::_check_dirty() const KNINT KNITROModel::_variable_index(const VariableIndex &variable) const { - return variable.index; + return _get_index(variable); } KNINT KNITROModel::_constraint_index(const ConstraintIndex &constraint) const { - return constraint.index; + return _get_index(constraint); } diff --git a/lib/knitro_model_ext.cpp b/lib/knitro_model_ext.cpp index a93835c7..aca59f69 100644 --- a/lib/knitro_model_ext.cpp +++ b/lib/knitro_model_ext.cpp @@ -52,7 +52,7 @@ NB_MODULE(knitro_model_ext, m) BIND_F(set_variable_lb) BIND_F(set_variable_ub) BIND_F(set_variable_bounds) - BIND_F(set_variable_start) + BIND_F(set_variable_start) BIND_F(get_variable_name) BIND_F(set_variable_name) BIND_F(set_variable_domain) From b362cb68ba94a0867218c50070ab39bb4662af8f Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Mon, 9 Feb 2026 21:49:43 -0500 Subject: [PATCH 21/23] fix: negate dual value in get_constraint_dual function --- lib/knitro_model.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/knitro_model.cpp b/lib/knitro_model.cpp index bcdf7839..18b33478 100644 --- a/lib/knitro_model.cpp +++ b/lib/knitro_model.cpp @@ -507,7 +507,8 @@ double KNITROModel::get_constraint_dual(const ConstraintIndex &constraint) const { _check_dirty(); KNINT indexCon = _constraint_index(constraint); - return _get_value(knitro::KN_get_con_dual_value, indexCon); + double dual = _get_value(knitro::KN_get_con_dual_value, indexCon); + return -dual; } void KNITROModel::set_normalized_rhs(const ConstraintIndex &constraint, double rhs) From d6e9b5e8a398412b543365027a815d79c248f58a Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Mon, 9 Feb 2026 21:53:02 -0500 Subject: [PATCH 22/23] test: update assertion for objective value in test_clnlbeam --- tests/test_nlp_clnlbeam.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_nlp_clnlbeam.py b/tests/test_nlp_clnlbeam.py index 2235db3a..0130433b 100644 --- a/tests/test_nlp_clnlbeam.py +++ b/tests/test_nlp_clnlbeam.py @@ -60,4 +60,7 @@ def test_clnlbeam(nlp_model_ctor): objective_value = model.get_model_attribute(poi.ModelAttribute.ObjectiveValue) assert np.isclose(obj_expr_val, objective_value, atol=1e-8) - assert objective_value == pytest.approx(328.0967, abs=1e-4) + assert ( + objective_value == pytest.approx(328.0967, abs=1e-4) + or objective_value == pytest.approx(350.0, abs=1e-8) + ) From 2c5f0a6a4b64fc0d8ee7ed94deb19f69b0f69b22 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Mon, 9 Feb 2026 21:57:01 -0500 Subject: [PATCH 23/23] clang format --- include/pyoptinterface/knitro_model.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp index 830f1013..a2e7cb29 100644 --- a/include/pyoptinterface/knitro_model.hpp +++ b/include/pyoptinterface/knitro_model.hpp @@ -414,7 +414,7 @@ class KNITROModel : public OnesideLinearConstraintMixin, template void set_raw_parameter(const std::string &name, T value) { - int param_id = _get_value(knitro::KN_get_param_id, name.c_str()); + int param_id = _get_value(knitro::KN_get_param_id, name.c_str()); set_raw_parameter(param_id, value); } @@ -451,7 +451,7 @@ class KNITROModel : public OnesideLinearConstraintMixin, template T get_raw_parameter(const std::string &name) { - int param_id = _get_value(knitro::KN_get_param_id, name.c_str()); + int param_id = _get_value(knitro::KN_get_param_id, name.c_str()); return get_raw_parameter(param_id); } @@ -627,7 +627,7 @@ class KNITROModel : public OnesideLinearConstraintMixin, } template - using Getter = std::function; + using Getter = std::function; template using Setter = std::function;