From f274b8f903818ad5b6b76ba5220c439eac787dfe Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Fri, 10 Nov 2023 14:07:04 +1100 Subject: [PATCH] NL Writer C API example structure #30 Finished high-level example code --- nl-writer2/CMakeLists.txt | 3 ++ nl-writer2/examples/c/nlsol_ex_c.c | 26 ++++++++++++---- nl-writer2/examples/c/nlsol_ex_c_model.c | 5 +++ nl-writer2/examples/c/nlsol_ex_c_model.h | 6 ++++ nl-writer2/examples/c/nlsol_ex_c_nl.c | 12 +++++++- nl-writer2/examples/c/nlsol_ex_c_nl.h | 7 ++++- nl-writer2/examples/c/nlsol_ex_c_nlutils.c | 10 ++++++ nl-writer2/examples/c/nlsol_ex_c_nlutils.h | 16 ++++++++++ nl-writer2/examples/c/nlsol_ex_c_sol.c | 10 +++++- nl-writer2/examples/c/nlsol_ex_c_sol.h | 7 ++++- nl-writer2/include/api/c/nl-feeder2-c.h | 20 ++++++++++-- nl-writer2/include/api/c/nl-writer2-misc-c.h | 16 +++++++--- nl-writer2/include/api/c/nlsol-c.h | 17 ++++++----- nl-writer2/include/api/c/sol-handler2-c.h | 9 ++++-- nl-writer2/include/mp/nl-feeder2.h | 3 +- nl-writer2/src/c_api.cc | 32 +++++++++++++++----- 16 files changed, 164 insertions(+), 35 deletions(-) create mode 100644 nl-writer2/examples/c/nlsol_ex_c_nlutils.c create mode 100644 nl-writer2/examples/c/nlsol_ex_c_nlutils.h diff --git a/nl-writer2/CMakeLists.txt b/nl-writer2/CMakeLists.txt index bdcf30f03..40f631ee7 100644 --- a/nl-writer2/CMakeLists.txt +++ b/nl-writer2/CMakeLists.txt @@ -64,6 +64,7 @@ add_library(${NLW2_C_API_LIB_NAME} STATIC ${NLW2_C_API_LIB_FILES} ${NLW2_C_API_INC_FILES}) target_include_directories( ${NLW2_C_API_LIB_NAME} PUBLIC ${NLW2_INCLUDE_PATH}) +target_link_libraries(${NLW2_C_API_LIB_NAME} ${NLW2_LIB_NAME}) ###################################################### # Examples @@ -86,6 +87,8 @@ set(NLSOL_C_EX_FILES ${NLW2_C_EXAMPLE_PATH}/nlsol_ex_c_nl.c ${NLW2_C_EXAMPLE_PATH}/nlsol_ex_c_sol.h ${NLW2_C_EXAMPLE_PATH}/nlsol_ex_c_sol.c + ${NLW2_C_EXAMPLE_PATH}/nlsol_ex_c_nlutils.h + ${NLW2_C_EXAMPLE_PATH}/nlsol_ex_c_nlutils.c ) option(BUILD_EXAMPLES_NLW2 diff --git a/nl-writer2/examples/c/nlsol_ex_c.c b/nl-writer2/examples/c/nlsol_ex_c.c index ca84c5144..8b12d3cc0 100644 --- a/nl-writer2/examples/c/nlsol_ex_c.c +++ b/nl-writer2/examples/c/nlsol_ex_c.c @@ -11,7 +11,7 @@ #include "nlsol_ex_c_model.h" #include "nlsol_ex_c_nl.h" #include "nlsol_ex_c_sol.h" -#include "api/c/nl-writer2-misc-c.h" +#include "nlsol_ex_c_nlutils.h" #include "api/c/nlsol-c.h" /// main() @@ -34,17 +34,31 @@ int main(int argc, const char* const* argv) { int binary = (argc<=3) || strcmp("text", argv[3]); const char* stub = (argc>4) ? argv[4] : "stub"; + // Create custom interface CAPIExample example = MakeCAPIExample_Linear_01(); - NLFeeder2_C feeder = MakeNLFeeder2_C(); - SOLHandler2_C handler = MakeSOLHandler2_C(); - NLUtils_C utils = NLW2_MakeNLUtils_C_Default(); + NLFeeder2_C feeder = MakeNLFeeder2_C(&example, binary); + SOLHandler2_C solhnd = MakeSOLHandler2_C(&example); + NLUtils_C utils = MakeNLUtils_C(); - NLSOL_C nlsol = NLW2_MakeNLSOL_C(&feeder, &handler, &utils); + // Create NLSOL_C + NLSOL_C nlsol = NLW2_MakeNLSOL_C(&feeder, &solhnd, &utils); + + // Solve + NLW2_SetSolver_C(&nlsol, solver); + NLW2_SetSolverOptions_C(&nlsol, sopts); + if (0==NLW2_Solve_C(&nlsol, stub)) { + printf("%s\n", NLW2_GetErrorMessage_C(&nlsol)); + exit(EXIT_FAILURE); + } + PrintSolution_C(&example, stub); // Destroy API-owned objects NLW2_DestroyNLSOL_C(&nlsol); - // Destroy our example data + // Destroy our custom interface and example data + DestroyNLUtils_C(&utils); + DestroySOLHandler2_C(&solhnd); + DestroyNLFeeder2_C(&feeder); DestroyCAPIExample_Linear_01(&example); return 0; diff --git a/nl-writer2/examples/c/nlsol_ex_c_model.c b/nl-writer2/examples/c/nlsol_ex_c_model.c index 8ce257ddf..6156de5da 100644 --- a/nl-writer2/examples/c/nlsol_ex_c_model.c +++ b/nl-writer2/examples/c/nlsol_ex_c_model.c @@ -1,5 +1,6 @@ #include #include +#include #include "nlsol_ex_c_model.h" @@ -70,3 +71,7 @@ void DestroyCAPIExample_Linear_01(CAPIExample* pEx) { // ... } + +void PrintSolution_C(CAPIExample* pex, const char* stub) { + assert(0); +} diff --git a/nl-writer2/examples/c/nlsol_ex_c_model.h b/nl-writer2/examples/c/nlsol_ex_c_model.h index 5023868d2..0aec87c97 100644 --- a/nl-writer2/examples/c/nlsol_ex_c_model.h +++ b/nl-writer2/examples/c/nlsol_ex_c_model.h @@ -97,6 +97,9 @@ typedef struct CAPIExample { int n_obj_nz; const char* obj_name; + /// Some technical stuff + int binary_nl; + } CAPIExample; /// Create linear example data @@ -105,4 +108,7 @@ CAPIExample MakeCAPIExample_Linear_01(); /// Destroy linear example data void DestroyCAPIExample_Linear_01(CAPIExample* ); +/// Print solution +void PrintSolution_C(CAPIExample* pex, const char* stub); + #endif // NLSOL_EX_C_MODEL_H diff --git a/nl-writer2/examples/c/nlsol_ex_c_nl.c b/nl-writer2/examples/c/nlsol_ex_c_nl.c index 71d558ebf..34edaae0f 100644 --- a/nl-writer2/examples/c/nlsol_ex_c_nl.c +++ b/nl-writer2/examples/c/nlsol_ex_c_nl.c @@ -1,7 +1,17 @@ +#include + #include "nlsol_ex_c_nl.h" -NLFeeder2_C MakeNLFeeder2_C() { +NLFeeder2_C MakeNLFeeder2_C( + CAPIExample* pex, int binary) { NLFeeder2_C result; + result.p_user_data_ = pex; + pex->binary_nl = binary; + return result; } + +void DestroyNLFeeder2_C(NLFeeder2_C* pf) { + pf->p_user_data_ = NULL; +} diff --git a/nl-writer2/examples/c/nlsol_ex_c_nl.h b/nl-writer2/examples/c/nlsol_ex_c_nl.h index 817e92fb0..640479168 100644 --- a/nl-writer2/examples/c/nlsol_ex_c_nl.h +++ b/nl-writer2/examples/c/nlsol_ex_c_nl.h @@ -8,7 +8,12 @@ #include "api/c/nl-feeder2-c.h" +#include "nlsol_ex_c_model.h" + /// Fill an NLFeeder2_C for the C API example -NLFeeder2_C MakeNLFeeder2_C(); +NLFeeder2_C MakeNLFeeder2_C(CAPIExample* , int binary); + +/// Destroy custom NLFeeder2_C +void DestroyNLFeeder2_C(NLFeeder2_C* ); #endif // NLSOL_EX_C_NL_H diff --git a/nl-writer2/examples/c/nlsol_ex_c_nlutils.c b/nl-writer2/examples/c/nlsol_ex_c_nlutils.c new file mode 100644 index 000000000..82cd05837 --- /dev/null +++ b/nl-writer2/examples/c/nlsol_ex_c_nlutils.c @@ -0,0 +1,10 @@ +#include "nlsol_ex_c_nlutils.h" + +NLUtils_C MakeNLUtils_C() { + // Just return the API's default config: + return NLW2_MakeNLUtils_C_Default(); +} + +void DestroyNLUtils_C(NLUtils_C* p) { + NLW2_DestroyNLUtils_C_Default(p); +} diff --git a/nl-writer2/examples/c/nlsol_ex_c_nlutils.h b/nl-writer2/examples/c/nlsol_ex_c_nlutils.h new file mode 100644 index 000000000..7b41841b3 --- /dev/null +++ b/nl-writer2/examples/c/nlsol_ex_c_nlutils.h @@ -0,0 +1,16 @@ +/** + * Custom NLUtils_C for the C API example + * + */ +#ifndef NLSOL_EX_C_NLUTILS_H +#define NLSOL_EX_C_NLUTILS_H + +#include "api/c/nl-writer2-misc-c.h" + +/// Fill NLUtils_C for the C API example +NLUtils_C MakeNLUtils_C(); + +/// Destroy custom NLUtils_C +void DestroyNLUtils_C(NLUtils_C* ); + +#endif // NLSOL_EX_C_NLUTILS_H diff --git a/nl-writer2/examples/c/nlsol_ex_c_sol.c b/nl-writer2/examples/c/nlsol_ex_c_sol.c index dda149586..3db9d08ea 100644 --- a/nl-writer2/examples/c/nlsol_ex_c_sol.c +++ b/nl-writer2/examples/c/nlsol_ex_c_sol.c @@ -1,7 +1,15 @@ +#include + #include "nlsol_ex_c_sol.h" -SOLHandler2_C MakeSOLHandler2_C() { +SOLHandler2_C MakeSOLHandler2_C(CAPIExample* pex) { SOLHandler2_C result; + result.p_user_data_ = pex; + return result; } + +void DestroySOLHandler2_C(SOLHandler2_C* p) { + p->p_user_data_ = NULL; +} diff --git a/nl-writer2/examples/c/nlsol_ex_c_sol.h b/nl-writer2/examples/c/nlsol_ex_c_sol.h index cef60b18d..327ae0e8b 100644 --- a/nl-writer2/examples/c/nlsol_ex_c_sol.h +++ b/nl-writer2/examples/c/nlsol_ex_c_sol.h @@ -7,7 +7,12 @@ #include "api/c/sol-handler2-c.h" +#include "nlsol_ex_c_model.h" + /// Fill a SOLHandler2_C for the C API example -SOLHandler2_C MakeSOLHandler2_C(); +SOLHandler2_C MakeSOLHandler2_C(CAPIExample* ); + +/// Destroy custom SOLHandler2_C +void DestroySOLHandler2_C(SOLHandler2_C* ); #endif // NLSOL_EX_C_SOL_H diff --git a/nl-writer2/include/api/c/nl-feeder2-c.h b/nl-writer2/include/api/c/nl-feeder2-c.h index 303112449..1f8eaa8aa 100644 --- a/nl-writer2/include/api/c/nl-feeder2-c.h +++ b/nl-writer2/include/api/c/nl-feeder2-c.h @@ -12,9 +12,25 @@ extern "C" { #endif -/// Wrap mp::NLFeeder2 +/** Wrap mp::NLFeeder2 for C API. + + NLFeeder2_C: writes model details on request + via provided callback objects. + See the examples folder. + + For the NL format, variables and constraints must have certain order. + + **Variable ordering:** + first continuous, then integer. + Some solvers might require more elaborate ordering, see NLHeader_C. + + **Constraint ordering:** + first algebraic (including complementarity), then logical. + Some solvers might require nonlinear constraints first. + */ typedef struct NLFeeder2_C { - void* p_; + /// User data + void* p_user_data_; } NLFeeder2_C; diff --git a/nl-writer2/include/api/c/nl-writer2-misc-c.h b/nl-writer2/include/api/c/nl-writer2-misc-c.h index cdd4baab1..a144535b4 100644 --- a/nl-writer2/include/api/c/nl-writer2-misc-c.h +++ b/nl-writer2/include/api/c/nl-writer2-misc-c.h @@ -12,22 +12,30 @@ extern "C" { #endif -/// Wrap mp::NLUtils. +/// Wrap mp::NLUtils for C API. /// /// NL writer and SOL reader utilities. /// It provides default facilities for logging /// and error handling. /// The default error handler exit()s. typedef struct NLUtils_C { - void* p_; + /// Use this pointer if you need to store your data + void* p_user_data_; + + /// Used by the API + /// with NLW2_MakeNLUtils_C_Default() / + /// NLW2_DestroyNLUtils_C_Default(). + void* p_api_data_; } NLUtils_C; /// Create a default NLUtils_C wrapper object. -/// User application might want to customize -/// using the below functions. +/// User application might change some methods +/// and use the p_user_data_ pointer. NLUtils_C NLW2_MakeNLUtils_C_Default(); +/// Destroy the NLUtils_C object created by the API +void NLW2_DestroyNLUtils_C_Default(NLUtils_C*); #ifdef __cplusplus } // extern "C" diff --git a/nl-writer2/include/api/c/nlsol-c.h b/nl-writer2/include/api/c/nlsol-c.h index 208b7587e..586a8420f 100644 --- a/nl-writer2/include/api/c/nlsol-c.h +++ b/nl-writer2/include/api/c/nlsol-c.h @@ -24,7 +24,8 @@ extern "C" { /// /// Usage: see the C API example and the below API. typedef struct NLSOL_C { - void* p_; + /// Internal data + void* p_api_data_; } NLSOL_C; @@ -37,31 +38,31 @@ NLSOL_C NLW2_MakeNLSOL_C(NLFeeder2_C* , SOLHandler2_C* , NLUtils_C* ); void NLW2_DestroyNLSOL_C(NLSOL_C* ); /// Set solver, such as "gurobi", "highs", "ipopt" -void NLSOL_C_SetSolver(NLSOL_C* , const char* solver); +void NLW2_SetSolver_C(NLSOL_C* , const char* solver); /// Set solver options, such as "outlev=1 lim:time=500" -void NLSOL_C_SetSolverOptions(NLSOL_C* , const char* sopts); +void NLW2_SetSolverOptions_C(NLSOL_C* , const char* sopts); /// Solve. /// @param filestub: filename stub to be used /// for input files (.nl, .col., .row, etc.), /// and output files (.sol). /// @return true if all ok. -int NLSOL_C_Solve(NLSOL_C* , const char* filestub); +int NLW2_Solve_C(NLSOL_C* , const char* filestub); /// Get error message. -const char* NLSOL_C_GetErrorMessage(NLSOL_C* ); +const char* NLW2_GetErrorMessage_C(NLSOL_C* ); /// Substep: write NL and any accompanying files. -int NLSOL_C_WriteNLFile(NLSOL_C* , const char* filestub); +int NLW2_WriteNLFile_C(NLSOL_C* , const char* filestub); /// Substep: invoke chosen solver for \a filestub. -int NLSOL_C_InvokeSolver(NLSOL_C* , const char* filestub); +int NLW2_InvokeSolver_C(NLSOL_C* , const char* filestub); /// Substep: read solution. /// @param filename: complete file name, /// normally (stub).sol. -int NLSOL_C_ReadSolution(NLSOL_C* , const char* filename); +int NLW2_ReadSolution_C(NLSOL_C* , const char* filename); #ifdef __cplusplus diff --git a/nl-writer2/include/api/c/sol-handler2-c.h b/nl-writer2/include/api/c/sol-handler2-c.h index 4c6612c61..b92d429d5 100644 --- a/nl-writer2/include/api/c/sol-handler2-c.h +++ b/nl-writer2/include/api/c/sol-handler2-c.h @@ -12,9 +12,14 @@ extern "C" { #endif -/// Wrap mp::SOLHandler2 +/// Wrap mp::SOLHandler2 for C API. +/// +/// SOLHandler2_C: reads solution details on request +/// via provided callback objects. +/// See the examples folder. typedef struct SOLHandler2_C { - void* p_; + /// User data + void* p_user_data_; } SOLHandler2_C; diff --git a/nl-writer2/include/mp/nl-feeder2.h b/nl-writer2/include/mp/nl-feeder2.h index bb21aa8c9..a872d79b7 100644 --- a/nl-writer2/include/mp/nl-feeder2.h +++ b/nl-writer2/include/mp/nl-feeder2.h @@ -64,7 +64,8 @@ namespace mp { For the NL format, variables and constraints must have certain order. - **Variable ordering:** first continuous, then integer. + **Variable ordering:** + first continuous, then integer. Some solvers might require more elaborate ordering, see NLHeader. **Constraint ordering:** diff --git a/nl-writer2/src/c_api.cc b/nl-writer2/src/c_api.cc index 19853988b..862f36339 100644 --- a/nl-writer2/src/c_api.cc +++ b/nl-writer2/src/c_api.cc @@ -3,6 +3,7 @@ * */ +#include #include #include "api/c/nl-feeder2-c.h" @@ -10,15 +11,24 @@ #include "api/c/nl-writer2-misc-c.h" #include "api/c/nlsol-c.h" +#include "mp/nl-writer2-misc.h" ///////////////////////// NLUtils_C /////////////////////////// NLUtils_C NLW2_MakeNLUtils_C_Default() { NLUtils_C result; + result.p_api_data_ = new mp::NLUtils; + + result.p_user_data_ = NULL; + return result; } +void NLW2_DestroyNLUtils_C_Default(NLUtils_C* pu) { + delete (mp::NLUtils*)pu->p_api_data_; +} + //////////// NLSOL_C API ////////////// @@ -32,29 +42,35 @@ NLSOL_C NLW2_MakeNLSOL_C(NLFeeder2_C* , SOLHandler2_C* , NLUtils_C* ) { void NLW2_DestroyNLSOL_C(NLSOL_C* ) { assert(0); } /// Set solver, such as "gurobi", "highs", "ipopt" -void NLSOL_C_SetSolver(NLSOL_C* , const char* solver); +void NLW2_SetSolver_C(NLSOL_C* , const char* solver) +{ assert(0); } /// Set solver options, such as "outlev=1 lim:time=500" -void NLSOL_C_SetSolverOptions(NLSOL_C* , const char* sopts); +void NLW2_SetSolverOptions_C(NLSOL_C* , const char* sopts) +{ assert(0); } /// Solve. /// @param filestub: filename stub to be used /// for input files (.nl, .col., .row, etc.), /// and output files (.sol). /// @return true if all ok. -int NLSOL_C_Solve(NLSOL_C* , const char* filestub); +int NLW2_Solve_C(NLSOL_C* , const char* filestub) +{ assert(0); } /// Get error message. -const char* NLSOL_C_GetErrorMessage(NLSOL_C* ); +const char* NLW2_GetErrorMessage_C(NLSOL_C* ) +{ assert(0); } /// Substep: write NL and any accompanying files. -int NLSOL_C_WriteNLFile(NLSOL_C* , const char* filestub); +int NLW2_WriteNLFile_C(NLSOL_C* , const char* filestub) +{ assert(0); } /// Substep: invoke chosen solver for \a filestub. -int NLSOL_C_InvokeSolver(NLSOL_C* , const char* filestub); +int NLW2_InvokeSolver_C(NLSOL_C* , const char* filestub) +{ assert(0); } /// Substep: read solution. /// @param filename: complete file name, /// normally (stub).sol. -int NLSOL_C_ReadSolution(NLSOL_C* , const char* filename); - +int NLW2_ReadSolution_C(NLSOL_C* , const char* filename) +{ assert(0); }