├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── include └── csip.h ├── src └── csip.c └── test ├── minunit.h └── test.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | *.su 34 | lib/ 35 | /test/test 36 | *.orig 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Felipe Serrano, Miles Lubin, Robert Schwarz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # is not easy to make makefiles :( 2 | # it is so ugly... 3 | # always needed reminder: 4 | # rules/targets are of the form 5 | # target: dependency1 dependency2 ... dependencyN 6 | # command_to_execute 7 | # @other_command_to_execute 8 | # if a command starts with @, it doesn't print the command, it just executes it 9 | # $@ is the target's name 10 | # $< is the first dependency 11 | # $^ are all dependencies 12 | # $? are all dependencies that are newer than target 13 | 14 | # DEFINITIONS 15 | CSIPDIR = $(realpath .) 16 | 17 | CSIPSRCDIR = $(CSIPDIR)/src 18 | CSIPINC = $(CSIPDIR)/include 19 | CSIPLIBDIR = $(CSIPDIR)/lib 20 | 21 | CFLAGS = -std=c99 -Wall -pedantic 22 | 23 | SCIPSRC = $(CSIPLIBDIR)/include 24 | SCIPLIB = -lscip 25 | 26 | FLAGS = -I$(SCIPSRC) -I$(CSIPINC) 27 | LFLAGS = -L$(CSIPLIBDIR) 28 | LINKFLAGS = -Wl,-rpath,$(CSIPLIBDIR) 29 | 30 | TESTDIR = $(CSIPDIR)/test 31 | TESTFLAGS = -I$(CSIPINC) -g 32 | TESTLIBS = -lm -lcsip -lscip 33 | LINKTESTFLAGS = $(LINKFLAGS) 34 | LINKTESTFLAGS += -Wl,-rpath,$(CSIPLIBDIR) 35 | LTESTFLAGS = -L$(CSIPLIBDIR) 36 | 37 | TESTSRC = $(TESTDIR)/test.c 38 | TESTBIN = $(TESTDIR)/test # don't know where to put the test executable 39 | 40 | CSIPHEADER = $(CSIPINC)/csip.h 41 | CSIPSRC = $(CSIPSRCDIR)/csip.c 42 | CSIPOBJ = $(CSIPSRCDIR)/csip.o 43 | CSIPLIB = $(CSIPLIBDIR)/libcsip.so 44 | 45 | # RULES 46 | .PHONY: all 47 | all: $(CSIPLIBDIR) $(CSIPLIB) 48 | 49 | .PHONY: clean 50 | clean: 51 | @echo "removing $(CSIPOBJ), $(CSIPLIB), $(TESTBIN)" 52 | @rm -f $(CSIPOBJ) 53 | @rm -f $(CSIPLIB) 54 | @rm -f $(TESTBIN) 55 | 56 | .PHONY: clean-links 57 | clean-links: 58 | @echo "removing symlinks" 59 | @rm -rf $(CSIPLIBDIR) 60 | 61 | .PHONY: test 62 | test: 63 | @make $(TESTBIN) 64 | $(TESTBIN) 65 | 66 | .PHONY: links 67 | links: 68 | @echo "Creating symbolic links to headers and library within SCIPOPTDIR ($(SCIPOPTDIR))." 69 | @bash -c 'cd lib; \ 70 | if [ -e $(SCIPOPTDIR)/include/scip/scip.h ] ; \ 71 | then \ 72 | ln -s $(SCIPOPTDIR)/include; \ 73 | else \ 74 | echo "ERROR: no SCIP headers found in SCIPOPTDIR!" ; \ 75 | cd ..; \ 76 | rmdir lib; \ 77 | exit 1; \ 78 | fi ; \ 79 | cd ..;\ 80 | ' 81 | @bash -c 'cd lib; \ 82 | if [ -e $(SCIPOPTDIR)/lib/libscip.so ] ; \ 83 | then \ 84 | ln -s $(SCIPOPTDIR)/lib/libscip* .; \ 85 | else \ 86 | echo "ERROR: no SCIP library found in SCIPOPTDIR!" ; \ 87 | cd ..; \ 88 | rmdir lib; \ 89 | exit 1; \ 90 | fi ; \ 91 | cd ..;\ 92 | ' 93 | 94 | $(CSIPLIBDIR): 95 | mkdir -p $@ 96 | make links 97 | 98 | $(CSIPLIB): $(CSIPSRC) 99 | gcc $(CFLAGS) $(FLAGS) -c $< $(LFLAGS) $(LINKFLAGS) $(SCIPLIB) -fPIC -o $(CSIPOBJ) 100 | gcc $(CFLAGS) $(CSIPOBJ) $(LFLAGS) $(LINKFLAGS) $(SCIPLIB) -fPIC -shared -o $@ 101 | 102 | $(TESTBIN): $(TESTSRC) $(CSIPLIB) 103 | @echo "compiling test" 104 | gcc $(CFLAGS) $(TESTFLAGS) $< $(LINKTESTFLAGS) $(TESTLIBS) $(LTESTFLAGS) -o $@ 105 | 106 | ASTYLEOPTS = --style=allman --indent=spaces=4 --indent-cases --pad-oper --pad-header --unpad-paren --align-pointer=name --add-brackets --max-code-length=80 107 | 108 | .PHONY: style 109 | style: 110 | @astyle -q $(ASTYLEOPTS) $(CSIPHEADER) $(CSIPSRC) $(TESTSRC) 111 | 112 | .PHONY: valgrind 113 | valgrind: 114 | @valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all $(TESTBIN) 115 | 116 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSIP 2 | 3 | An opinionated interface to the [SCIP](http://scip.zib.de/) solver in 4 | the C language. A restricted subset of the features is chosen, with 5 | the goal of making SCIP more accessible to novice users and other 6 | programming languages. 7 | 8 | The following constraint types are supported: 9 | [linear](http://scip.zib.de/doc/html/cons__linear_8h.php), 10 | [quadratic](http://scip.zib.de/doc/html/cons__quadratic_8h.php), 11 | [SOS1](http://scip.zib.de/doc/html/cons__sos1_8h.php) and 12 | [SOS2](http://scip.zib.de/doc/html/cons__sos2_8h.php). 13 | 14 | Furthermore, users can implement a lazy constraint by implementing a 15 | single callback function. 16 | 17 | ## Update (March 2019) 18 | 19 | This package was initially developed as a crutch for [SCIP.jl](https://github.com/SCIP-Interfaces/SCIP.jl), but is no longer used there. From now on, CSIP can be consideren unmaintained. 20 | 21 | ## Installation 22 | 23 | ### SCIP and SoPlex 24 | 25 | CSIP depends on the [SCIP Optimization Suite](http://scip.zib.de/#scipoptsuite). 26 | Starting with release 0.5.0, **CSIP only supports SCIP Optimization Suite 27 | 5.0.0** or newer. 28 | 29 | [Download](http://scip.zib.de/download.php?fname=scipoptsuite-5.0.0.tgz) the 30 | SCIP Optimization Suite and extract the source files. Now choose a destination 31 | path for the installation and set the environment variable `SCIPOPTDIR` there. 32 | Build the shared library (containing SCIP and SoPlex) using `cmake` via 33 | 34 | mkdir build 35 | cd build 36 | cmake -DCMAKE_INSTALL_PREFIX=$SCIPOPTDIR .. 37 | make 38 | make install 39 | 40 | ### CSIP 41 | 42 | Run `make` to build CSIP, which will produce a shared library 43 | `libcsip.so`. 44 | 45 | ### Tests 46 | 47 | To compile and execute the tests, run `make test`. 48 | -------------------------------------------------------------------------------- /include/csip.h: -------------------------------------------------------------------------------- 1 | typedef struct csip_model CSIP_MODEL; 2 | 3 | /* return codes */ 4 | typedef int CSIP_RETCODE; 5 | #define CSIP_RETCODE_OK 0 6 | #define CSIP_RETCODE_ERROR 1 7 | #define CSIP_RETCODE_NOMEMORY 2 8 | 9 | /* solver status */ 10 | typedef int CSIP_STATUS; 11 | #define CSIP_STATUS_OPTIMAL 0 12 | #define CSIP_STATUS_INFEASIBLE 1 13 | #define CSIP_STATUS_UNBOUNDED 2 14 | #define CSIP_STATUS_INFORUNBD 3 15 | #define CSIP_STATUS_NODELIMIT 4 16 | #define CSIP_STATUS_TIMELIMIT 5 17 | #define CSIP_STATUS_MEMLIMIT 6 18 | #define CSIP_STATUS_USERLIMIT 7 19 | #define CSIP_STATUS_UNKNOWN 8 20 | 21 | /* variable types */ 22 | typedef int CSIP_VARTYPE; 23 | #define CSIP_VARTYPE_BINARY 0 24 | #define CSIP_VARTYPE_INTEGER 1 25 | #define CSIP_VARTYPE_IMPLINT 2 26 | #define CSIP_VARTYPE_CONTINUOUS 3 27 | 28 | /* solving context for lazy callbacks */ 29 | typedef int CSIP_LAZY_CONTEXT; 30 | #define CSIP_LAZY_LPRELAX 0 // we have (fractional) LP relaxtion of B&B node 31 | #define CSIP_LAZY_INTEGRALSOL 1 // current candidate is integer feasible 32 | #define CSIP_LAZY_OTHER 2 // e.g., CHECK is called on fractional candidate 33 | 34 | /* nonlinear operators */ 35 | typedef int CSIP_OP; 36 | #define VARIDX 1 37 | #define CONST 2 38 | #define MINUS 9 39 | #define DIV 11 40 | #define OPSQRT 13 41 | #define POW 14 42 | #define EXP 17 43 | #define LOG 18 44 | #define SUM 64 45 | #define PROD 65 46 | 47 | /* parameter types */ 48 | typedef int CSIP_PARAMTYPE; 49 | #define CSIP_PARAMTYPE_NOTAPARAM -1 50 | #define CSIP_PARAMTYPE_BOOL 0 51 | #define CSIP_PARAMTYPE_INT 1 52 | #define CSIP_PARAMTYPE_LONGINT 2 53 | #define CSIP_PARAMTYPE_REAL 3 54 | #define CSIP_PARAMTYPE_CHAR 4 55 | #define CSIP_PARAMTYPE_STRING 5 56 | 57 | // versioning scheme: major.minor.patch 58 | int CSIPmajorVersion(); 59 | int CSIPminorVersion(); 60 | int CSIPpatchVersion(); 61 | 62 | // combined version 63 | int CSIPgetVersion(); 64 | 65 | /* model definition */ 66 | 67 | // Create a new model (and solver). 68 | CSIP_RETCODE CSIPcreateModel(CSIP_MODEL **model); 69 | 70 | // Free all memory of model (and solver). 71 | CSIP_RETCODE CSIPfreeModel(CSIP_MODEL *model); 72 | 73 | // Add new variable to model. 74 | // To omit a bound, use (-)INFINITY. 75 | // The variable index will be assigned to idx; pass NULL if not needed. 76 | CSIP_RETCODE CSIPaddVar( 77 | CSIP_MODEL *model, double lowerbound, double upperbound, 78 | CSIP_VARTYPE vartype, int *idx); 79 | 80 | // Set new lower bounds for a set of variables. 81 | CSIP_RETCODE CSIPchgVarLB( 82 | CSIP_MODEL *model, int numindices, int *indices, double *lowerbounds); 83 | 84 | // Set new lower bounds for a set of variables. 85 | CSIP_RETCODE CSIPchgVarUB( 86 | CSIP_MODEL *model, int numindices, int *indices, double *upperbounds); 87 | 88 | // Set new type for a variable. 89 | CSIP_RETCODE CSIPchgVarType( 90 | CSIP_MODEL *model, int varindex, CSIP_VARTYPE vartype); 91 | 92 | // Get type of a variable. 93 | CSIP_VARTYPE CSIPgetVarType(CSIP_MODEL *model, int varindex); 94 | 95 | // Add new linear constraint to the model, of the form: 96 | // lhs <= sum_i coefs[i] * vars[i] <= rhs 97 | // For one-sided inequalities, use (-)INFINITY for lhs or rhs. 98 | // The constraint index will be assigned to idx; pass NULL if not needed. 99 | CSIP_RETCODE CSIPaddLinCons( 100 | CSIP_MODEL *model, int numindices, int *indices, double *coefs, 101 | double lhs, double rhs, int *idx); 102 | 103 | // Add new quadratic constraint to the model, of the form: 104 | // lhs <= sum_i lincoefs[i] * vars[lin[i]] 105 | // + sum_j quadcoefs[j] * vars[row[j]] * vars[col[j]] <= rhs 106 | // For one-sided inequalities, use (-)INFINITY for lhs or rhs. 107 | // The constraint index will be assigned to idx; pass NULL if not needed. 108 | CSIP_RETCODE CSIPaddQuadCons( 109 | CSIP_MODEL *model, int numlinindices, int *linindices, double *lincoefs, 110 | int numquadterms, int *quadrowindices, int *quadcolindices, 111 | double *quadcoefs, double lhs, double rhs, int *idx); 112 | 113 | // Add new nonlinear constraint to the model, of the form: 114 | // lhs <= expression <= rhs 115 | // For one-sided inequalities, use (-)INFINITY for lhs or rhs. 116 | // The expression is represented as follows: 117 | // An array of operations, an array with the children of each operation 118 | // and an array indicating which children are from which operation (begin): 119 | // The children of op[k] are the ops/vars/values indexed from begin[k] until 120 | // begin[k+1]-1. 121 | // The child of VARIDX represents the index of the variable. 122 | // The child of CONST represents the index of the constant in the value array. 123 | // All others refer to indices in the op array. 124 | // As an example: if we have a problem with variables x_0, x_1, x_2 125 | // and we want to represent x_2^2, then we have operators: 126 | // [VARIDX, CONST, POWER], with children 127 | // [2, 0, 0, 1] and values [2.0]. begin is given by 128 | // [0, 1, 2, 4] which means that the children of 129 | // VARIDX are 2 -> the variables with index 2 (x_2) 130 | // CONST are 0 -> the value with index 0 (2.0) 131 | // POWER are 0, 1 -> the variable and the const (x_2 ^ 2.0) 132 | // The constraint index will be assigned to idx; pass NULL if not needed. 133 | CSIP_RETCODE CSIPaddNonLinCons( 134 | CSIP_MODEL *model, int nops, CSIP_OP *ops, int *children, int *begin, 135 | double *values, double lhs, double rhs, int *idx); 136 | 137 | // Add SOS1 (special ordered set of type 1) constraint on a set of 138 | // variables. That is, at most one variable is allowed to take on a 139 | // nonzero value. 140 | // Use weights to determine variable order, or NULL. 141 | // The constraint index will be assigned to idx; pass NULL if not needed. 142 | CSIP_RETCODE CSIPaddSOS1( 143 | CSIP_MODEL *model, int numindices, int *indices, double *weights, int *idx); 144 | 145 | // Add SOS2 (special ordered set of type 2) constraint on a set of 146 | // variables. That is, at most two consecutive variables are allowed 147 | // to take on nonzero values. 148 | // Use weights to determine variable order, or NULL. 149 | // The constraint index will be assigned to idx; pass NULL if not needed. 150 | CSIP_RETCODE CSIPaddSOS2( 151 | CSIP_MODEL *model, int numindices, int *indices, double *weights, int *idx); 152 | 153 | // Set the linear objective function of the form: sum_i coefs[i] * vars[i] 154 | CSIP_RETCODE CSIPsetObj( 155 | CSIP_MODEL *model, int numindices, int *indices, double *coefs); 156 | 157 | // Set a quadratic objective function 158 | CSIP_RETCODE CSIPsetQuadObj(CSIP_MODEL *model, int numlinindices, 159 | int *linindices, double *lincoefs, int numquadterms, 160 | int *quadrowindices, int *quadcolindices, 161 | double *quadcoefs); 162 | 163 | // Set a nonlinear objective function. See CSIPaddNonLinCons for an explanation 164 | // about the format 165 | CSIP_RETCODE CSIPsetNonlinearObj( 166 | CSIP_MODEL *model, int nops, int *ops, int *children, int *begin, 167 | double *values); 168 | 169 | // Set the optimization sense to minimization. This is the default setting. 170 | CSIP_RETCODE CSIPsetSenseMinimize(CSIP_MODEL *model); 171 | 172 | // Set the optimization sense to maximization. 173 | CSIP_RETCODE CSIPsetSenseMaximize(CSIP_MODEL *model); 174 | 175 | // Solve the model. 176 | CSIP_RETCODE CSIPsolve(CSIP_MODEL *model); 177 | 178 | // Interrupt the solving process. 179 | CSIP_RETCODE CSIPinterrupt(CSIP_MODEL *model); 180 | 181 | // Copy the values of all variables in the best known solution into 182 | // the output array. The user is responsible for memory allocation. 183 | CSIP_RETCODE CSIPgetVarValues(CSIP_MODEL *model, double *output); 184 | 185 | // Get the objective value of the best-known solution. 186 | double CSIPgetObjValue(CSIP_MODEL *model); 187 | 188 | // Get the best known bound on the optimal solution 189 | double CSIPgetObjBound(CSIP_MODEL *model); 190 | 191 | // Get the solving status. 192 | CSIP_STATUS CSIPgetStatus(CSIP_MODEL *model); 193 | 194 | // Get the type of a parameter 195 | CSIP_PARAMTYPE CSIPgetParamType(CSIP_MODEL *model, const char *name); 196 | 197 | // Set the value of an existing boolean parameter 198 | CSIP_RETCODE CSIPsetBoolParam( 199 | CSIP_MODEL *model, const char *name, int value); 200 | 201 | // Set the value of an existing int parameter 202 | CSIP_RETCODE CSIPsetIntParam( 203 | CSIP_MODEL *model, const char *name, int value); 204 | 205 | // Set the value of an existing long int parameter 206 | CSIP_RETCODE CSIPsetLongintParam( 207 | CSIP_MODEL *model, const char *name, long long value); 208 | 209 | // Set the value of an existing real parameter 210 | CSIP_RETCODE CSIPsetRealParam( 211 | CSIP_MODEL *model, const char *name, double value); 212 | 213 | // Set the value of an existing char parameter 214 | CSIP_RETCODE CSIPsetCharParam( 215 | CSIP_MODEL *model, const char *name, char value); 216 | 217 | // Set the value of an existing string parameter 218 | CSIP_RETCODE CSIPsetStringParam( 219 | CSIP_MODEL *model, const char *name, const char *value); 220 | 221 | // Get the number of variables added to the model. 222 | int CSIPgetNumVars(CSIP_MODEL *model); 223 | 224 | // Get the number of constraints added to the model. 225 | // Beware: constraints added by a lazy callbacks are not counted here! 226 | int CSIPgetNumConss(CSIP_MODEL *model); 227 | 228 | // Supply a solution (as a dense array) to be checked at the beginning of the 229 | // solving process. Partial solutions are also supported: Indicate missing 230 | // values with NaN. 231 | CSIP_RETCODE CSIPsetInitialSolution(CSIP_MODEL *model, double *values); 232 | 233 | /* lazy constraint callback functions */ 234 | 235 | typedef struct SCIP_ConshdlrData CSIP_LAZYDATA; 236 | 237 | // Get current context in which callback is called. Relates to the solution that 238 | // is available through CSIPlazyGetVarValues. 239 | CSIP_LAZY_CONTEXT CSIPlazyGetContext(CSIP_LAZYDATA *lazydata); 240 | 241 | // Copy values of current (relaxation) solution to output array. Call 242 | // this function from your lazy constraint callback. 243 | CSIP_RETCODE CSIPlazyGetVarValues(CSIP_LAZYDATA *lazydata, double *output); 244 | 245 | // Add a linear constraint from a lazy constraint callback. 246 | // With islocal, you specify whether the added constraint is only 247 | // valid locally (in the branch-and-bound subtree). 248 | CSIP_RETCODE CSIPlazyAddLinCons( 249 | CSIP_LAZYDATA *lazydata, int numindices, int *indices, double *coefs, 250 | double lhs, double rhs, int islocal); 251 | 252 | typedef CSIP_RETCODE(*CSIP_LAZYCALLBACK)( 253 | CSIP_MODEL *model, CSIP_LAZYDATA *lazydata, void *userdata); 254 | 255 | // Add a lazy constraint callback to the model. 256 | // You may use userdata to pass any data. 257 | CSIP_RETCODE CSIPaddLazyCallback( 258 | CSIP_MODEL *model, CSIP_LAZYCALLBACK lazycb, void *userdata); 259 | 260 | /* heuristic callback functions */ 261 | 262 | typedef struct SCIP_HeurData CSIP_HEURDATA; 263 | 264 | // signature for heuristic callbacks. 265 | // must only call `CSIPheur*` methods from within callback, passing `heurdata`. 266 | typedef CSIP_RETCODE(*CSIP_HEURCALLBACK)( 267 | CSIP_MODEL *model, CSIP_HEURDATA *heurdata, void *userdata); 268 | 269 | // Copy values of solution to output array. Call this function from your 270 | // heuristic callback. Solution is LP relaxation of current node. 271 | CSIP_RETCODE CSIPheurGetVarValues(CSIP_HEURDATA *heurdata, double *output); 272 | 273 | // Supply a solution (as a dense array). Only complete solutions are supported. 274 | CSIP_RETCODE CSIPheurAddSolution(CSIP_HEURDATA *heurdata, double *values); 275 | 276 | // Add a heuristic callback to the model. 277 | // You may use userdata to pass any data. 278 | CSIP_RETCODE CSIPaddHeuristicCallback( 279 | CSIP_MODEL *model, CSIP_HEURCALLBACK heur, void *userdata); 280 | 281 | /* advanced usage */ 282 | 283 | // Get access to the internal SCIP solver. Use at your own risk! 284 | void *CSIPgetInternalSCIP(CSIP_MODEL *model); 285 | 286 | /* additional features (on top of SCIP) */ 287 | 288 | // Set a prefix for all messages. 289 | CSIP_RETCODE CSIPsetMessagePrefix(CSIP_MODEL *model, const char* prefix); 290 | -------------------------------------------------------------------------------- /src/csip.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "csip.h" 4 | #include "nlpi/pub_expr.h" 5 | #include "scip/scip.h" 6 | #include "scip/pub_misc.h" 7 | #include "scip/pub_var.h" 8 | #include "scip/scipdefplugins.h" 9 | 10 | #define CSIP_MAJOR_VERSION 0 11 | #define CSIP_MINOR_VERSION 6 12 | #define CSIP_PATCH_VERSION 0 13 | 14 | /* objective type */ 15 | typedef int CSIP_OBJTYPE; 16 | #define CSIP_OBJTYPE_LINEAR 0 17 | #define CSIP_OBJTYPE_NONLINEAR 1 18 | 19 | // map return codes: SCIP -> CSIP 20 | static inline int retCodeSCIPtoCSIP(int scipRetCode) 21 | { 22 | switch (scipRetCode) 23 | { 24 | case SCIP_OKAY: 25 | return CSIP_RETCODE_OK; 26 | case SCIP_NOMEMORY: 27 | return CSIP_RETCODE_NOMEMORY; 28 | default: // all the same for us 29 | return CSIP_RETCODE_ERROR; 30 | } 31 | } 32 | 33 | // map return codes: CSIP -> SCIP 34 | static inline int retCodeCSIPtoSCIP(int csipRetCode) 35 | { 36 | switch (csipRetCode) 37 | { 38 | case CSIP_RETCODE_OK: 39 | return SCIP_OKAY; 40 | case CSIP_RETCODE_NOMEMORY: 41 | return SCIP_NOMEMORY; 42 | default: // CSIP_RETCODE_ERROR 43 | return SCIP_ERROR; 44 | } 45 | } 46 | 47 | 48 | // catch return code within CSIP, like SCIP_CALL defined in SCIP 49 | #define CSIP_CALL(x) \ 50 | do { \ 51 | CSIP_RETCODE _retcode = (x); \ 52 | if(_retcode != CSIP_RETCODE_OK) \ 53 | { \ 54 | printf("CSIP: failing with retcode %d at %s:%d\n", \ 55 | _retcode, __FILE__, __LINE__); \ 56 | exit(1); \ 57 | return _retcode; \ 58 | } \ 59 | } while(0) 60 | 61 | // catch SCIP return code from CSIP 62 | #define SCIP_in_CSIP(x) CSIP_CALL( retCodeSCIPtoCSIP(x) ) 63 | 64 | // catch CSIP return code from SCIP 65 | #define CSIP_in_SCIP(x) SCIP_CALL( retCodeCSIPtoSCIP(x) ) 66 | 67 | // variable sized arrays 68 | #define INITIALSIZE 64 69 | #define GROWFACTOR 2 70 | 71 | struct csip_model 72 | { 73 | SCIP *scip; 74 | 75 | // variable sized array for variables 76 | int nvars; 77 | int varssize; 78 | SCIP_VAR **vars; 79 | 80 | // variable sized array for constraints 81 | int nconss; 82 | int consssize; 83 | SCIP_CONS **conss; 84 | 85 | // counter for callbacks 86 | int nlazycb; 87 | int nheur; 88 | 89 | // user-defined solution, is checked before solving 90 | SCIP_SOL *initialsol; 91 | 92 | // store objective variable for nonlinear objective: the idea is to add an 93 | // auxiliary constraint and variable to represent nonlinear objectives. If 94 | // the objective gets change, we set to 0 the objective coefficient of this 95 | // variable and relax its bounds so the auxiliary constraint is redundant. 96 | // This is going to mess the model when printed to a file. 97 | SCIP_VAR *objvar; 98 | SCIP_CONS *objcons; 99 | CSIP_OBJTYPE objtype; 100 | 101 | // store message handler to allow for a prefix 102 | SCIP_MESSAGEHDLR* msghdlr; 103 | }; 104 | 105 | /* 106 | * local methods 107 | */ 108 | 109 | static 110 | CSIP_RETCODE createLinCons(CSIP_MODEL *model, int numindices, int *indices, 111 | double *coefs, double lhs, double rhs, SCIP_CONS **cons) 112 | { 113 | SCIP *scip; 114 | SCIP_VAR *var; 115 | int i; 116 | 117 | scip = model->scip; 118 | 119 | SCIP_in_CSIP(SCIPcreateConsBasicLinear(scip, cons, "lincons", 0, NULL, NULL, 120 | lhs, rhs)); 121 | 122 | for (i = 0; i < numindices; ++i) 123 | { 124 | var = model->vars[indices[i]]; 125 | SCIP_in_CSIP(SCIPaddCoefLinear(scip, *cons, var, coefs[i])); 126 | } 127 | 128 | return CSIP_RETCODE_OK; 129 | } 130 | 131 | static 132 | CSIP_RETCODE addCons(CSIP_MODEL *model, SCIP_CONS *cons, int *idx) 133 | { 134 | SCIP *scip; 135 | 136 | scip = model->scip; 137 | 138 | SCIP_in_CSIP(SCIPaddCons(scip, cons)); 139 | 140 | // do we need to resize? 141 | if (model->nconss >= model->consssize) 142 | { 143 | model->consssize = GROWFACTOR * model->consssize; 144 | model->conss = (SCIP_CONS **) realloc( 145 | model->conss, model->consssize * sizeof(SCIP_CONS *)); 146 | if (model->conss == NULL) 147 | { 148 | return CSIP_RETCODE_NOMEMORY; 149 | } 150 | } 151 | 152 | if (idx != NULL) 153 | { 154 | *idx = model->nconss; 155 | model->conss[*idx] = cons; 156 | } 157 | else 158 | { 159 | model->conss[model->nconss] = cons; 160 | } 161 | 162 | ++(model->nconss); 163 | 164 | return CSIP_RETCODE_OK; 165 | } 166 | 167 | static 168 | CSIP_RETCODE createExprtree( 169 | CSIP_MODEL *model, int nops, CSIP_OP *ops, int *children, int *begin, 170 | double *values, SCIP_EXPRTREE **tree) 171 | { 172 | SCIP *scip; 173 | SCIP_EXPR **exprs; 174 | SCIP_VAR **vars; 175 | int varpos; 176 | int i; 177 | int nvars; 178 | 179 | scip = model->scip; 180 | exprs = (SCIP_EXPR **) malloc(nops * sizeof(SCIP_EXPR *)); 181 | nvars = 0; 182 | for (i = 0; i < nops; ++i) 183 | { 184 | exprs[i] = NULL; 185 | nvars += (ops[i] == SCIP_EXPR_VARIDX); 186 | } 187 | vars = (SCIP_VAR **) malloc(nvars * sizeof(SCIP_VAR *)); 188 | 189 | varpos = 0; 190 | for (i = 0; i < nops; ++i) 191 | { 192 | switch (ops[i]) 193 | { 194 | case SCIP_EXPR_VARIDX: 195 | { 196 | int varidx = children[begin[i]]; 197 | assert(1 == begin[i + 1] - begin[i]); 198 | assert(varidx < model->nvars); 199 | SCIP_in_CSIP(SCIPexprCreate(SCIPblkmem(scip), &exprs[i], 200 | ops[i], varpos)); 201 | vars[varpos] = model->vars[varidx]; 202 | ++varpos; 203 | //printf("Seeing variable %d (nchild %d)\n", varidx, begin[i+1] - begin[i]); 204 | } 205 | break; 206 | case SCIP_EXPR_CONST: 207 | assert(1 == begin[i + 1] - begin[i]); 208 | SCIP_in_CSIP(SCIPexprCreate(SCIPblkmem(scip), &exprs[i], 209 | ops[i], values[children[begin[i]]])); 210 | //printf("Seeing constant %g (nchild %d)\n", values[children[begin[i]]], begin[i+1] - begin[i]); 211 | break; 212 | case SCIP_EXPR_MINUS: 213 | // if we have two children it is a proper minus; otherwise just -1 * ... 214 | if (begin[i + 1] - begin[i] == 2) 215 | { 216 | SCIP_in_CSIP(SCIPexprCreate(SCIPblkmem(scip), &exprs[i], 217 | ops[i], exprs[children[begin[i]]], exprs[children[begin[i] + 1]])); 218 | } 219 | else 220 | { 221 | SCIP_EXPR *zeroexpr; 222 | assert(1 == begin[i + 1] - begin[i]); 223 | SCIP_in_CSIP(SCIPexprCreate(SCIPblkmem(scip), &zeroexpr, 224 | SCIP_EXPR_CONST, 0.0)); 225 | // expression is 0 - child 226 | SCIP_in_CSIP(SCIPexprCreate(SCIPblkmem(scip), &exprs[i], 227 | ops[i], zeroexpr, exprs[children[begin[i]]])); 228 | } 229 | //printf("Seeing a minus (nchild %d)\n", begin[i+1] - begin[i]); 230 | break; 231 | case SCIP_EXPR_REALPOWER: 232 | assert(2 == begin[i + 1] - begin[i]); 233 | { 234 | double exponent; 235 | // the second child is the exponent which is a const 236 | exponent = values[children[begin[children[begin[i] + 1]]]]; 237 | //printf("Seeing a power with exponent %g (nchild %d)\n", exponent, begin[i+1] - begin[i]); 238 | SCIP_in_CSIP(SCIPexprCreate(SCIPblkmem(scip), &exprs[i], 239 | ops[i], exprs[children[begin[i]]], exponent)); 240 | } 241 | break; 242 | case SCIP_EXPR_DIV: 243 | assert(2 == begin[i + 1] - begin[i]); 244 | SCIP_in_CSIP(SCIPexprCreate(SCIPblkmem(scip), &exprs[i], 245 | ops[i], exprs[children[begin[i]]], exprs[children[begin[i] + 1]])); 246 | //printf("Seeing a division (nchild %d)\n", begin[i+1] - begin[i]); 247 | break; 248 | case SCIP_EXPR_SQRT: 249 | case SCIP_EXPR_EXP: 250 | case SCIP_EXPR_LOG: 251 | assert(1 == begin[i + 1] - begin[i]); 252 | SCIP_in_CSIP(SCIPexprCreate(SCIPblkmem(scip), &exprs[i], 253 | ops[i], exprs[children[begin[i]]])); 254 | //printf("Seeing a sqrt/exp/log (nchild %d)\n", begin[i+1] - begin[i]); 255 | break; 256 | case SCIP_EXPR_SUM: 257 | case SCIP_EXPR_PRODUCT: 258 | { 259 | SCIP_EXPR **childrenexpr; 260 | int nchildren = begin[i + 1] - begin[i]; 261 | int c; 262 | childrenexpr = (SCIP_EXPR **) malloc(nchildren * sizeof(SCIP_EXPR *)); 263 | for (c = 0; c < nchildren; ++c) 264 | { 265 | childrenexpr[c] = exprs[children[begin[i] + c]]; 266 | } 267 | 268 | SCIP_in_CSIP(SCIPexprCreate(SCIPblkmem(scip), &exprs[i], 269 | ops[i], nchildren, childrenexpr)); 270 | 271 | free(childrenexpr); 272 | //printf("Seeing a sum/product (nchild %d)\n", begin[i+1] - begin[i]); 273 | } 274 | break; 275 | default: // don't support 276 | printf("I don't know what I am seeing %d\n", ops[i]); 277 | return CSIP_RETCODE_ERROR; 278 | } 279 | } 280 | assert(varpos == nvars); 281 | // last expression is root 282 | assert(exprs[nops - 1] != NULL); 283 | SCIP_in_CSIP(SCIPexprtreeCreate(SCIPblkmem(scip), tree, exprs[nops - 1], nvars, 284 | 0, NULL)); 285 | 286 | // assign variables to tree 287 | SCIP_in_CSIP(SCIPexprtreeSetVars(*tree, nvars, vars)); 288 | 289 | // free memory 290 | free(vars); 291 | free(exprs); 292 | 293 | return CSIP_RETCODE_OK; 294 | } 295 | 296 | static 297 | char *strDup(const char *s) { 298 | size_t size = strlen(s) + 1; 299 | char *p = malloc(size); 300 | if (p) 301 | { 302 | memcpy(p, s, size); 303 | } 304 | return p; 305 | } 306 | 307 | /** When the objective is nonlinear we use the epigraph representation. 308 | * However, changing the objective sense is not straightforward in that 309 | * case. The purpose of this function is to change an epigraph objective 310 | * to represent the correct objective sense. *Starting* from a correct 311 | * objective, we only need two modifications to correctly change the sense. 312 | * 0) change sense (this step is not done here) 313 | * 1) multiply objective function by -1 314 | * 2) multiply nonlinear function by -1 315 | * max{ t : f(x) >= t } --> min{ -t : -f(x) >= t } 316 | * min{ t : f(x) <= t } --> max{ -t : -f(x) <= t } 317 | */ 318 | CSIP_RETCODE correctObjectiveFunction(CSIP_MODEL *model) 319 | { 320 | SCIP *scip = model->scip; 321 | SCIP_CONS *objcons = model->objcons; 322 | SCIP_VAR *objvar = model->objvar; 323 | 324 | // we only apply this for nonlinear objectives 325 | if (objvar == NULL) 326 | { 327 | return CSIP_RETCODE_OK; 328 | } 329 | 330 | assert(objcons != NULL); 331 | 332 | // 1) 333 | SCIP_in_CSIP(SCIPchgVarObj(scip, objvar, -1.0 * SCIPvarGetObj(objvar))); 334 | 335 | // 2) 336 | // we need to copy the tree, because SCIPsetExprtreesNonlinear 337 | // is going to delete it 338 | SCIP_EXPRTREE *exprtree; 339 | SCIP_in_CSIP(SCIPexprtreeCopy(SCIPblkmem(scip), &exprtree, 340 | SCIPgetExprtreesNonlinear(scip, objcons)[0])); 341 | SCIP_Real exprtreecoef = -SCIPgetExprtreeCoefsNonlinear(scip, objcons)[0]; 342 | 343 | SCIP_in_CSIP(SCIPsetExprtreesNonlinear(scip, objcons, 1, &exprtree, 344 | &exprtreecoef)); 345 | 346 | return CSIP_RETCODE_OK; 347 | } 348 | 349 | /* 350 | * interface methods 351 | */ 352 | int CSIPmajorVersion() 353 | { 354 | return CSIP_MAJOR_VERSION; 355 | } 356 | 357 | int CSIPminorVersion() 358 | { 359 | return CSIP_MINOR_VERSION; 360 | } 361 | 362 | int CSIPpatchVersion() 363 | { 364 | return CSIP_PATCH_VERSION; 365 | } 366 | 367 | int CSIPgetVersion() 368 | { 369 | return (100 * CSIPmajorVersion() 370 | + 10 * CSIPminorVersion() 371 | + 1 * CSIPpatchVersion()); 372 | } 373 | 374 | CSIP_RETCODE CSIPcreateModel(CSIP_MODEL **modelptr) 375 | { 376 | CSIP_MODEL *model; 377 | 378 | *modelptr = (CSIP_MODEL *)malloc(sizeof(CSIP_MODEL)); 379 | if (*modelptr == NULL) 380 | { 381 | return CSIP_RETCODE_NOMEMORY; 382 | } 383 | 384 | model = *modelptr; 385 | 386 | SCIP_in_CSIP(SCIPcreate(&model->scip)); 387 | SCIP_in_CSIP(SCIPincludeDefaultPlugins(model->scip)); 388 | SCIP_in_CSIP(SCIPcreateProbBasic(model->scip, "name")); 389 | 390 | model->nvars = 0; 391 | model->varssize = INITIALSIZE; 392 | model->vars = (SCIP_VAR **) malloc(INITIALSIZE * sizeof(SCIP_VAR *)); 393 | if (model->vars == NULL) 394 | { 395 | return CSIP_RETCODE_NOMEMORY; 396 | } 397 | 398 | model->nconss = 0; 399 | model->consssize = INITIALSIZE; 400 | model->conss = (SCIP_CONS **) malloc(INITIALSIZE * sizeof(SCIP_CONS *)); 401 | if (model->conss == NULL) 402 | { 403 | return CSIP_RETCODE_NOMEMORY; 404 | } 405 | 406 | model->nlazycb = 0; 407 | model->nheur = 0; 408 | model->initialsol = NULL; 409 | model->objvar = NULL; 410 | model->objcons = NULL; 411 | model->objtype = CSIP_OBJTYPE_LINEAR; 412 | model->msghdlr = NULL; 413 | 414 | return CSIP_RETCODE_OK; 415 | } 416 | 417 | CSIP_RETCODE CSIPfreeModel(CSIP_MODEL *model) 418 | { 419 | int i; 420 | 421 | if (model->initialsol != NULL) // solve was not called? 422 | { 423 | SCIP_in_CSIP(SCIPfreeSol(model->scip, &model->initialsol)); 424 | } 425 | 426 | /* SCIPreleaseVar sets the given pointer to NULL. However, this pointer is 427 | * needed when SCIPfree is called, because it will call the lock method again 428 | * which works on the vars stored at model, so we give another pointer 429 | * TODO: maybe this is still wrong and one should free the transformed problem 430 | * first and then release the vars... we have to check for BMS memory leaks 431 | */ 432 | for (i = 0; i < model->nvars; ++i) 433 | { 434 | SCIP_VAR *var; 435 | var = model->vars[i]; 436 | SCIP_in_CSIP(SCIPreleaseVar(model->scip, &var)); 437 | } 438 | for (i = 0; i < model->nconss; ++i) 439 | { 440 | SCIP_in_CSIP(SCIPreleaseCons(model->scip, &model->conss[i])); 441 | } 442 | if (model->objvar != NULL) 443 | { 444 | assert(model->objcons != NULL); 445 | SCIP_in_CSIP(SCIPreleaseVar(model->scip, &model->objvar)); 446 | SCIP_in_CSIP(SCIPreleaseCons(model->scip, &model->objcons)); 447 | } 448 | SCIP_in_CSIP(SCIPfree(&model->scip)); 449 | 450 | free(model->conss); 451 | free(model->vars); 452 | free(model); 453 | 454 | return CSIP_RETCODE_OK; 455 | } 456 | 457 | CSIP_RETCODE CSIPaddVar(CSIP_MODEL *model, double lowerbound, double upperbound, 458 | int vartype, int *idx) 459 | { 460 | SCIP *scip; 461 | SCIP_VAR *var; 462 | 463 | scip = model->scip; 464 | SCIP_in_CSIP(SCIPfreeTransform(scip)); 465 | 466 | SCIP_in_CSIP(SCIPcreateVarBasic(scip, &var, NULL, lowerbound, upperbound, 0.0, 467 | vartype)); 468 | SCIP_in_CSIP(SCIPaddVar(scip, var)); 469 | 470 | // do we need to resize? 471 | if (model->nvars >= model->varssize) 472 | { 473 | model->varssize = GROWFACTOR * model->varssize; 474 | model->vars = (SCIP_VAR **) realloc( 475 | model->vars, model->varssize * sizeof(SCIP_VAR *)); 476 | if (model->vars == NULL) 477 | { 478 | return CSIP_RETCODE_NOMEMORY; 479 | } 480 | } 481 | 482 | if (idx != NULL) 483 | { 484 | *idx = model->nvars; 485 | model->vars[*idx] = var; 486 | } 487 | else 488 | { 489 | model->vars[model->nvars] = var; 490 | } 491 | ++(model->nvars); 492 | 493 | return CSIP_RETCODE_OK; 494 | } 495 | 496 | CSIP_RETCODE CSIPchgVarLB(CSIP_MODEL *model, int numindices, int *indices, 497 | double *lowerbounds) 498 | { 499 | int i; 500 | SCIP *scip; 501 | SCIP_VAR *var; 502 | 503 | scip = model->scip; 504 | SCIP_in_CSIP(SCIPfreeTransform(scip)); 505 | 506 | for (i = 0; i < numindices; ++i) 507 | { 508 | var = model->vars[indices[i]]; 509 | SCIP_in_CSIP(SCIPchgVarLb(scip, var, lowerbounds[i])); 510 | } 511 | 512 | return CSIP_RETCODE_OK; 513 | } 514 | 515 | CSIP_RETCODE CSIPchgVarUB(CSIP_MODEL *model, int numindices, int *indices, 516 | double *upperbounds) 517 | { 518 | int i; 519 | SCIP *scip; 520 | SCIP_VAR *var; 521 | 522 | scip = model->scip; 523 | SCIP_in_CSIP(SCIPfreeTransform(scip)); 524 | 525 | for (i = 0; i < numindices; ++i) 526 | { 527 | var = model->vars[indices[i]]; 528 | SCIP_in_CSIP(SCIPchgVarUb(scip, var, upperbounds[i])); 529 | } 530 | 531 | return CSIP_RETCODE_OK; 532 | } 533 | 534 | CSIP_RETCODE CSIPchgVarType( 535 | CSIP_MODEL *model, int varindex, CSIP_VARTYPE vartype) 536 | { 537 | SCIP *scip = model->scip; 538 | SCIP_VAR *var = model->vars[varindex]; 539 | SCIP_Bool infeas = FALSE; 540 | 541 | SCIP_in_CSIP(SCIPfreeTransform(scip)); 542 | 543 | SCIP_in_CSIP(SCIPchgVarType(scip, var, vartype, &infeas)); 544 | // TODO: don't ignore `infeas`? 545 | // for SCIP, solving a problem with a binary variable with bounds not in [0,1] produces an error 546 | // here we change them to the correct value, since JuMP seems to expect that behaviour 547 | // see JuMP tests: [probmod] Test bound modification on binaries 548 | if (vartype == CSIP_VARTYPE_BINARY && SCIPvarGetLbLocal(var) < 0.0) 549 | { 550 | SCIP_in_CSIP(SCIPchgVarLb(scip, var, 0.0)); 551 | } 552 | if (vartype == CSIP_VARTYPE_BINARY && SCIPvarGetUbLocal(var) > 1.0) 553 | { 554 | SCIP_in_CSIP(SCIPchgVarUb(scip, var, 1.0)); 555 | } 556 | 557 | return CSIP_RETCODE_OK; 558 | } 559 | 560 | CSIP_VARTYPE CSIPgetVarType(CSIP_MODEL *model, int varindex) 561 | { 562 | assert(varindex >= 0 && varindex < model->nvars); 563 | 564 | switch (SCIPvarGetType(model->vars[varindex])) 565 | { 566 | case SCIP_VARTYPE_BINARY: 567 | return CSIP_VARTYPE_BINARY; 568 | case SCIP_VARTYPE_INTEGER: 569 | return CSIP_VARTYPE_INTEGER; 570 | case SCIP_VARTYPE_IMPLINT: 571 | return CSIP_VARTYPE_IMPLINT; 572 | case SCIP_VARTYPE_CONTINUOUS: 573 | return CSIP_VARTYPE_CONTINUOUS; 574 | } 575 | return -1; 576 | } 577 | 578 | CSIP_RETCODE CSIPaddLinCons(CSIP_MODEL *model, int numindices, int *indices, 579 | double *coefs, double lhs, double rhs, int *idx) 580 | { 581 | SCIP_CONS *cons; 582 | 583 | SCIP_in_CSIP(SCIPfreeTransform(model->scip)); 584 | 585 | CSIP_CALL(createLinCons(model, numindices, indices, coefs, lhs, rhs, &cons)); 586 | CSIP_CALL(addCons(model, cons, idx)); 587 | 588 | return CSIP_RETCODE_OK; 589 | } 590 | 591 | CSIP_RETCODE CSIPaddQuadCons(CSIP_MODEL *model, int numlinindices, 592 | int *linindices, 593 | double *lincoefs, int numquadterms, 594 | int *quadrowindices, int *quadcolindices, 595 | double *quadcoefs, double lhs, double rhs, int *idx) 596 | { 597 | int i; 598 | SCIP *scip; 599 | SCIP_VAR *linvar; 600 | SCIP_VAR *var1; 601 | SCIP_VAR *var2; 602 | SCIP_CONS *cons; 603 | 604 | scip = model->scip; 605 | SCIP_in_CSIP(SCIPfreeTransform(scip)); 606 | 607 | SCIP_in_CSIP(SCIPcreateConsBasicQuadratic(scip, &cons, "quadcons", 0, NULL, 608 | NULL, 0, NULL, NULL, NULL, lhs, rhs)); 609 | 610 | for (i = 0; i < numlinindices; ++i) 611 | { 612 | linvar = model->vars[linindices[i]]; 613 | SCIP_in_CSIP(SCIPaddLinearVarQuadratic(scip, cons, linvar, lincoefs[i])); 614 | } 615 | 616 | for (i = 0; i < numquadterms; ++i) 617 | { 618 | var1 = model->vars[quadrowindices[i]]; 619 | var2 = model->vars[quadcolindices[i]]; 620 | SCIP_in_CSIP(SCIPaddBilinTermQuadratic(scip, cons, var1, var2, quadcoefs[i])); 621 | } 622 | 623 | CSIP_CALL(addCons(model, cons, idx)); 624 | 625 | return CSIP_RETCODE_OK; 626 | } 627 | 628 | // we might be assuming that the indices of the children of op[k] 629 | // are always <= k (when op[k] is not VARIDX nor CONST) 630 | // this implies that the root expression is the last one, which is 631 | // another assumption 632 | CSIP_RETCODE CSIPaddNonLinCons( 633 | CSIP_MODEL *model, int nops, CSIP_OP *ops, int *children, int *begin, 634 | double *values, double lhs, double rhs, int *idx) 635 | { 636 | SCIP *scip; 637 | SCIP_EXPRTREE *tree; 638 | SCIP_CONS *cons; 639 | 640 | CSIP_CALL(createExprtree(model, nops, ops, children, begin, 641 | values, &tree)); 642 | 643 | scip = model->scip; 644 | SCIP_in_CSIP(SCIPfreeTransform(scip)); 645 | 646 | // create nonlinear constraint 647 | SCIP_in_CSIP(SCIPcreateConsBasicNonlinear(scip, &cons, "nonlin", 0, NULL, NULL, 648 | 1, &tree, NULL, lhs, rhs)); 649 | 650 | CSIP_CALL(addCons(model, cons, idx)); 651 | 652 | // free memory 653 | SCIP_in_CSIP(SCIPexprtreeFree(&tree)); 654 | 655 | return CSIP_RETCODE_OK; 656 | } 657 | 658 | CSIP_RETCODE CSIPaddSOS1( 659 | CSIP_MODEL *model, int numindices, int *indices, double *weights, int *idx) 660 | { 661 | SCIP *scip = model->scip; 662 | SCIP_CONS *cons; 663 | SCIP_VAR **vars = (SCIP_VAR **) malloc(numindices * sizeof(SCIP_VAR *)); 664 | double* auxweights = weights; 665 | 666 | SCIP_in_CSIP(SCIPfreeTransform(scip)); 667 | if (vars == NULL) 668 | { 669 | return CSIP_RETCODE_NOMEMORY; 670 | } 671 | for (int i = 0; i < numindices; ++i) 672 | { 673 | vars[i] = model->vars[indices[i]]; 674 | } 675 | 676 | /* give weights to avoid an assert in SCIP */ 677 | if (weights == NULL) 678 | { 679 | auxweights = (double *) malloc(numindices * sizeof(double)); 680 | if (auxweights == NULL) 681 | { 682 | return CSIP_RETCODE_NOMEMORY; 683 | } 684 | for (int i = 0; i < numindices; ++i) 685 | { 686 | auxweights[i] = i; 687 | } 688 | } 689 | 690 | SCIP_in_CSIP(SCIPcreateConsBasicSOS1( 691 | scip, &cons, "sos1", numindices, vars, auxweights)); 692 | CSIP_CALL(addCons(model, cons, idx)); 693 | 694 | if (weights == NULL) 695 | { 696 | free(auxweights); 697 | } 698 | free(vars); 699 | 700 | return CSIP_RETCODE_OK; 701 | } 702 | 703 | CSIP_RETCODE CSIPaddSOS2( 704 | CSIP_MODEL *model, int numindices, int *indices, double *weights, int *idx) 705 | { 706 | SCIP *scip = model->scip; 707 | SCIP_CONS *cons; 708 | SCIP_VAR **vars = (SCIP_VAR **) malloc(numindices * sizeof(SCIP_VAR *)); 709 | double* auxweights = weights; 710 | 711 | SCIP_in_CSIP(SCIPfreeTransform(scip)); 712 | 713 | if (vars == NULL) 714 | { 715 | return CSIP_RETCODE_NOMEMORY; 716 | } 717 | for (int i = 0; i < numindices; ++i) 718 | { 719 | vars[i] = model->vars[indices[i]]; 720 | } 721 | 722 | /* give weights to avoid an assert in SCIP */ 723 | if (weights == NULL) 724 | { 725 | auxweights = (double *) malloc(numindices * sizeof(double)); 726 | if (auxweights == NULL) 727 | { 728 | return CSIP_RETCODE_NOMEMORY; 729 | } 730 | for (int i = 0; i < numindices; ++i) 731 | { 732 | auxweights[i] = i; 733 | } 734 | } 735 | 736 | SCIP_in_CSIP(SCIPcreateConsBasicSOS2( 737 | scip, &cons, "sos2", numindices, vars, auxweights)); 738 | CSIP_CALL(addCons(model, cons, idx)); 739 | 740 | if (weights == NULL) 741 | { 742 | free(auxweights); 743 | } 744 | free(vars); 745 | 746 | return CSIP_RETCODE_OK; 747 | } 748 | 749 | CSIP_RETCODE CSIPsetObj(CSIP_MODEL *model, int numindices, int *indices, 750 | double *coefs) 751 | { 752 | int i; 753 | SCIP *scip; 754 | SCIP_VAR *var; 755 | 756 | scip = model->scip; 757 | SCIP_in_CSIP(SCIPfreeTransform(scip)); 758 | 759 | for (i = 0; i < numindices; ++i) 760 | { 761 | var = model->vars[indices[i]]; 762 | SCIP_in_CSIP(SCIPchgVarObj(scip, var, coefs[i])); 763 | } 764 | model->objtype = CSIP_OBJTYPE_LINEAR; 765 | 766 | // if nonlinear objective was set, remove objvar from objective 767 | // and relax its bounds. This should render the objective constraint 768 | // redundant 769 | if (model->objvar != NULL) 770 | { 771 | SCIP_in_CSIP(SCIPchgVarObj(scip, model->objvar, 0.0)); 772 | SCIP_in_CSIP(SCIPchgVarLb(scip, model->objvar, -SCIPinfinity(scip))); 773 | SCIP_in_CSIP(SCIPchgVarUb(scip, model->objvar, SCIPinfinity(scip))); 774 | 775 | // we do not need to remember this variable anymore nor the objcons 776 | SCIP_in_CSIP(SCIPreleaseVar(scip, &model->objvar)); 777 | SCIP_in_CSIP(SCIPreleaseCons(scip, &model->objcons)); 778 | assert(model->objvar == NULL); 779 | assert(model->objcons == NULL); 780 | } 781 | 782 | return CSIP_RETCODE_OK; 783 | } 784 | 785 | CSIP_RETCODE CSIPsetQuadObj(CSIP_MODEL *model, int numlinindices, 786 | int *linindices, double *lincoefs, int numquadterms, 787 | int *quadrowindices, int *quadcolindices, 788 | double *quadcoefs) 789 | { 790 | int nprods; 791 | int i; 792 | int opidx; 793 | int nops; 794 | int nchildren; 795 | CSIP_OP *ops; 796 | int *children; 797 | int *begin; 798 | int *prodindices; 799 | double *values; 800 | 801 | // build the expression representation of a quadratic 802 | // there are: numlinindices VARIDX, CONST and PROD for the linear part 803 | // nquadterms VARIDX, VARIDX, CONST and PROD for the quadratic part 804 | // So there are 3*numlindices + 4*nquadterms operators plus 1 (the sum) 805 | // Number of children: each VARIDX and CONST contribute with one: 806 | // nchildren >= 2*numlinindices + 3*nquadterms 807 | // Then each PROD in numlinindices contribute with 2 and in nquadterms with 3 808 | // nchildren >= 4*numlinindices + 6*nquadterms 809 | // The final sum has as many children as products are there, nlinind + nquadterms 810 | // nchildren = 5*numlinindices + 7*nquadterms 811 | // NOTE: we will need to store the indices of the PROD operators 812 | // and all coefs in a single array `values` 813 | nprods = numquadterms + numlinindices; 814 | nchildren = 5 * numlinindices + 7 * numquadterms; 815 | nops = 3 * numlinindices + 4 * numquadterms + 1; 816 | 817 | ops = (int *) malloc(nops * sizeof(CSIP_OP)); 818 | children = (int *) malloc(nchildren * sizeof(int)); 819 | begin = (int *) malloc((nops + 1) * sizeof(int)); 820 | values = (double *) malloc(nprods * sizeof(double)); 821 | prodindices = (int *) malloc(nprods * sizeof(int)); 822 | 823 | begin[0] = 0; 824 | opidx = -1; 825 | // linear part 826 | for (i = 0; i < numlinindices; ++i) 827 | { 828 | // variable 829 | ++opidx; 830 | ops[opidx] = VARIDX; 831 | begin[opidx + 1] = begin[opidx] + 1; // where its children end 832 | children[begin[opidx]] = linindices[i]; //children 833 | 834 | // next operator: coef 835 | ++opidx; 836 | ops[opidx] = CONST; 837 | begin[opidx + 1] = begin[opidx] + 1; 838 | children[begin[opidx]] = i; 839 | values[i] = lincoefs[i]; 840 | 841 | // next operator: PROD between coef and variable 842 | ++opidx; 843 | ops[opidx] = PROD; 844 | begin[opidx + 1] = begin[opidx] + 2; 845 | children[begin[opidx]] = opidx - 2; 846 | children[begin[opidx] + 1] = opidx - 1; 847 | 848 | prodindices[i] = opidx; 849 | } 850 | // quadratic part 851 | for (i = 0; i < numquadterms; ++i) 852 | { 853 | // variable 1 854 | ++opidx; 855 | ops[opidx] = VARIDX; 856 | begin[opidx + 1] = begin[opidx] + 1; // where its children end 857 | children[begin[opidx]] = quadrowindices[i]; //children 858 | 859 | // variable 2 860 | ++opidx; 861 | ops[opidx] = VARIDX; 862 | begin[opidx + 1] = begin[opidx] + 1; // where its children end 863 | children[begin[opidx]] = quadcolindices[i]; //children 864 | 865 | // coef 866 | ++opidx; 867 | ops[opidx] = CONST; 868 | begin[opidx + 1] = begin[opidx] + 1; 869 | children[begin[opidx]] = i + numlinindices; 870 | values[i + numlinindices] = quadcoefs[i]; 871 | 872 | // next operator: PROD between var1, var2 and coef 873 | ++opidx; 874 | ops[opidx] = PROD; 875 | begin[opidx + 1] = begin[opidx] + 3; 876 | children[begin[opidx]] = opidx - 3; 877 | children[begin[opidx] + 1] = opidx - 2; 878 | children[begin[opidx] + 2] = opidx - 1; 879 | 880 | prodindices[i + numlinindices] = opidx; 881 | } 882 | 883 | // sum all PRODs 884 | ++opidx; 885 | ops[opidx] = SUM; 886 | begin[opidx + 1] = begin[opidx] + nprods; 887 | for (i = 0; i < nprods; ++i) 888 | { 889 | children[begin[opidx] + i] = prodindices[i]; 890 | } 891 | 892 | CSIP_CALL(CSIPsetNonlinearObj(model, nops, ops, children, begin, values)); 893 | 894 | // free everything 895 | free(ops); 896 | free(children); 897 | free(begin); 898 | free(values); 899 | free(prodindices); 900 | 901 | return CSIP_RETCODE_OK; 902 | } 903 | 904 | CSIP_RETCODE CSIPsetNonlinearObj( 905 | CSIP_MODEL *model, int nops, CSIP_OP *ops, int *children, int *begin, 906 | double *values) 907 | { 908 | SCIP *scip; 909 | SCIP_EXPRTREE *tree; 910 | SCIP_CONS *cons; 911 | 912 | // get scip, free transform and remove old objective if any 913 | scip = model->scip; 914 | SCIP_in_CSIP(SCIPfreeTransform(scip)); 915 | 916 | if (model->objvar != NULL) 917 | { 918 | SCIP_in_CSIP(SCIPchgVarObj(scip, model->objvar, 0.0)); 919 | SCIP_in_CSIP(SCIPchgVarLb(scip, model->objvar, -SCIPinfinity(scip))); 920 | SCIP_in_CSIP(SCIPchgVarUb(scip, model->objvar, SCIPinfinity(scip))); 921 | 922 | // we do not need to remember this variable anymore nor objcons 923 | SCIP_in_CSIP(SCIPreleaseVar(scip, &model->objvar)); 924 | SCIP_in_CSIP(SCIPreleaseCons(scip, &model->objcons)); 925 | } 926 | assert(model->objvar == NULL); 927 | assert(model->objcons == NULL); 928 | 929 | // do nothing more if we received an empty expression tree 930 | assert(nops >= 1); 931 | if( nops == 1 && ops[0] != SCIP_EXPR_VARIDX ) 932 | { 933 | return CSIP_RETCODE_OK; 934 | } 935 | 936 | CSIP_CALL(createExprtree(model, nops, ops, children, begin, 937 | values, &tree)); 938 | 939 | // create nonlinear objective constraint 940 | SCIP_in_CSIP(SCIPcreateConsBasicNonlinear(scip, &cons, 941 | "nonlin_obj", 0, NULL, NULL, 1, &tree, NULL, 942 | -SCIPinfinity(scip), 0.0)); 943 | 944 | // add objvar to nonlinear objective 945 | SCIP_in_CSIP(SCIPcreateVarBasic(scip, &model->objvar, NULL, 946 | -SCIPinfinity(scip), SCIPinfinity(scip), 1.0, 947 | SCIP_VARTYPE_CONTINUOUS)); 948 | SCIP_in_CSIP(SCIPaddVar(scip, model->objvar)); 949 | SCIP_in_CSIP(SCIPaddLinearVarNonlinear(scip, cons, model->objvar, -1.0)); 950 | 951 | // add objective constraint and remember it 952 | SCIP_in_CSIP(SCIPaddCons(scip, cons)); 953 | model->objcons = cons; 954 | model->objtype = CSIP_OBJTYPE_NONLINEAR; 955 | 956 | // the created constraint is correct if sense is minimize, otherwise we 957 | // have to correct it 958 | if (SCIPgetObjsense(model->scip) == SCIP_OBJSENSE_MAXIMIZE) 959 | { 960 | CSIP_CALL(correctObjectiveFunction(model)); 961 | } 962 | 963 | // free memory 964 | SCIP_in_CSIP(SCIPexprtreeFree(&tree)); 965 | 966 | return CSIP_RETCODE_OK; 967 | } 968 | 969 | CSIP_RETCODE CSIPsetSenseMinimize(CSIP_MODEL *model) 970 | { 971 | SCIP_in_CSIP(SCIPfreeTransform(model->scip)); 972 | 973 | if (SCIPgetObjsense(model->scip) != SCIP_OBJSENSE_MINIMIZE) 974 | { 975 | SCIP_in_CSIP(SCIPsetObjsense(model->scip, SCIP_OBJSENSE_MINIMIZE)); 976 | CSIP_CALL(correctObjectiveFunction(model)); 977 | } 978 | 979 | return CSIP_RETCODE_OK; 980 | } 981 | 982 | CSIP_RETCODE CSIPsetSenseMaximize(CSIP_MODEL *model) 983 | { 984 | SCIP_in_CSIP(SCIPfreeTransform(model->scip)); 985 | 986 | if (SCIPgetObjsense(model->scip) != SCIP_OBJSENSE_MAXIMIZE) 987 | { 988 | SCIP_in_CSIP(SCIPsetObjsense(model->scip, SCIP_OBJSENSE_MAXIMIZE)); 989 | CSIP_CALL(correctObjectiveFunction(model)); 990 | } 991 | 992 | return CSIP_RETCODE_OK; 993 | } 994 | 995 | CSIP_RETCODE CSIPsolve(CSIP_MODEL *model) 996 | { 997 | // add initial solution 998 | if (model->initialsol != NULL) 999 | { 1000 | unsigned int stored; 1001 | SCIP_Bool initialsolpartial = 1002 | (SCIPsolGetOrigin(model->initialsol) == SCIP_SOLORIGIN_PARTIAL); 1003 | 1004 | /* if objective is nonlinear, we need to extend the initial sol with 1005 | * the value of objvar. For this we need to change the objective 1006 | * constraint temporarily to an equality constraint. However the 1007 | * nonlinear contraint handler doesn't allow to change sides, so 1008 | * we have to create a brand new constraint to compute the violation 1009 | * 1010 | * This is not true if the user has given a partial sol, because then 1011 | * we can safely leave the value for the objval unspecified. In fact, 1012 | * that's preferred, because computing the violation might fail. 1013 | */ 1014 | if (model->objcons != NULL && !initialsolpartial) 1015 | { 1016 | SCIP_CONS* tempcons; 1017 | SCIP_Real objvarval; 1018 | SCIP_EXPRTREE *tree; 1019 | 1020 | /* we do not care about the sign of the expression tree, since 1021 | * tempcons is equality and this already gives the correct sign for 1022 | * objvarval */ 1023 | tree = SCIPgetExprtreesNonlinear(model->scip, model->objcons)[0]; 1024 | SCIP_in_CSIP(SCIPcreateConsBasicNonlinear(model->scip, &tempcons, 1025 | "temp_nonlin_obj", 0, NULL, NULL, 1, &tree, NULL, 1026 | 0.0, 0.0)); 1027 | SCIP_in_CSIP(SCIPgetViolationNonlinear(model->scip, tempcons, 1028 | model->initialsol, &objvarval) ); 1029 | 1030 | SCIP_in_CSIP(SCIPreleaseCons(model->scip, &tempcons)); 1031 | 1032 | SCIP_in_CSIP(SCIPsetSolVals(model->scip, model->initialsol, 1, 1033 | &model->objvar, &objvarval)); 1034 | } 1035 | 1036 | 1037 | SCIP_in_CSIP(SCIPaddSolFree(model->scip, &model->initialsol, &stored)); 1038 | } 1039 | 1040 | SCIP_in_CSIP(SCIPsolve(model->scip)); 1041 | 1042 | return CSIP_RETCODE_OK; 1043 | } 1044 | 1045 | CSIP_RETCODE CSIPinterrupt(CSIP_MODEL *model) 1046 | { 1047 | SCIP_in_CSIP(SCIPinterruptSolve(model->scip)); 1048 | return CSIP_RETCODE_OK; 1049 | } 1050 | 1051 | CSIP_STATUS CSIPgetStatus(CSIP_MODEL *model) 1052 | { 1053 | switch (SCIPgetStatus(model->scip)) 1054 | { 1055 | case SCIP_STATUS_UNKNOWN: 1056 | return CSIP_STATUS_UNKNOWN; 1057 | case SCIP_STATUS_USERINTERRUPT: 1058 | return CSIP_STATUS_USERLIMIT; 1059 | case SCIP_STATUS_NODELIMIT: 1060 | return CSIP_STATUS_NODELIMIT; 1061 | case SCIP_STATUS_TOTALNODELIMIT: 1062 | return CSIP_STATUS_NODELIMIT; 1063 | case SCIP_STATUS_STALLNODELIMIT: 1064 | return CSIP_STATUS_USERLIMIT; 1065 | case SCIP_STATUS_TIMELIMIT: 1066 | return CSIP_STATUS_TIMELIMIT; 1067 | case SCIP_STATUS_MEMLIMIT: 1068 | return CSIP_STATUS_MEMLIMIT; 1069 | case SCIP_STATUS_GAPLIMIT: 1070 | return CSIP_STATUS_USERLIMIT; 1071 | case SCIP_STATUS_SOLLIMIT: 1072 | return CSIP_STATUS_USERLIMIT; 1073 | case SCIP_STATUS_BESTSOLLIMIT: 1074 | return CSIP_STATUS_USERLIMIT; 1075 | case SCIP_STATUS_RESTARTLIMIT: 1076 | return CSIP_STATUS_USERLIMIT; 1077 | case SCIP_STATUS_OPTIMAL: 1078 | return CSIP_STATUS_OPTIMAL; 1079 | case SCIP_STATUS_INFEASIBLE: 1080 | return CSIP_STATUS_INFEASIBLE; 1081 | case SCIP_STATUS_UNBOUNDED: 1082 | return CSIP_STATUS_UNBOUNDED; 1083 | case SCIP_STATUS_INFORUNBD: 1084 | return CSIP_STATUS_INFORUNBD; 1085 | default: 1086 | return CSIP_STATUS_UNKNOWN; 1087 | } 1088 | } 1089 | 1090 | double CSIPgetObjValue(CSIP_MODEL *model) 1091 | { 1092 | SCIP_SOL *sol = SCIPgetBestSol(model->scip); 1093 | if (sol == NULL) 1094 | { 1095 | return CSIP_RETCODE_ERROR; 1096 | } 1097 | 1098 | return SCIPgetSolOrigObj(model->scip, sol); 1099 | } 1100 | 1101 | double CSIPgetObjBound(CSIP_MODEL *model) 1102 | { 1103 | return SCIPgetDualbound(model->scip); 1104 | } 1105 | 1106 | 1107 | CSIP_RETCODE CSIPgetVarValues(CSIP_MODEL *model, double *output) 1108 | { 1109 | int i; 1110 | SCIP *scip; 1111 | SCIP_VAR *var; 1112 | 1113 | scip = model->scip; 1114 | 1115 | if (SCIPgetBestSol(scip) == NULL) 1116 | { 1117 | return CSIP_RETCODE_ERROR; 1118 | } 1119 | 1120 | for (i = 0; i < model->nvars; ++i) 1121 | { 1122 | var = model->vars[i]; 1123 | output[i] = SCIPgetSolVal(scip, SCIPgetBestSol(scip), var); 1124 | } 1125 | 1126 | return CSIP_RETCODE_OK; 1127 | } 1128 | 1129 | // Get the type of a parameter 1130 | CSIP_PARAMTYPE CSIPgetParamType(CSIP_MODEL *model, const char *name) 1131 | { 1132 | SCIP_PARAM *param; 1133 | 1134 | param = SCIPgetParam(model->scip, name); 1135 | if (param == NULL) 1136 | { 1137 | return CSIP_PARAMTYPE_NOTAPARAM; 1138 | } 1139 | else 1140 | { 1141 | return SCIPparamGetType(param); 1142 | } 1143 | } 1144 | 1145 | CSIP_RETCODE CSIPsetBoolParam( 1146 | CSIP_MODEL *model, const char *name, int value) 1147 | { 1148 | SCIP_in_CSIP(SCIPsetBoolParam(model->scip, name, value)); 1149 | return CSIP_RETCODE_OK; 1150 | } 1151 | 1152 | CSIP_RETCODE CSIPsetIntParam( 1153 | CSIP_MODEL *model, const char *name, int value) 1154 | { 1155 | SCIP_in_CSIP(SCIPsetIntParam(model->scip, name, value)); 1156 | return CSIP_RETCODE_OK; 1157 | } 1158 | 1159 | CSIP_RETCODE CSIPsetLongintParam( 1160 | CSIP_MODEL *model, const char *name, long long value) 1161 | { 1162 | SCIP_in_CSIP(SCIPsetLongintParam(model->scip, name, value)); 1163 | return CSIP_RETCODE_OK; 1164 | } 1165 | 1166 | CSIP_RETCODE CSIPsetRealParam( 1167 | CSIP_MODEL *model, const char *name, double value) 1168 | { 1169 | SCIP_in_CSIP(SCIPsetRealParam(model->scip, name, value)); 1170 | return CSIP_RETCODE_OK; 1171 | } 1172 | 1173 | CSIP_RETCODE CSIPsetCharParam( 1174 | CSIP_MODEL *model, const char *name, char value) 1175 | { 1176 | SCIP_in_CSIP(SCIPsetCharParam(model->scip, name, value)); 1177 | return CSIP_RETCODE_OK; 1178 | } 1179 | 1180 | CSIP_RETCODE CSIPsetStringParam( 1181 | CSIP_MODEL *model, const char *name, const char *value) 1182 | { 1183 | SCIP_in_CSIP(SCIPsetStringParam(model->scip, name, value)); 1184 | return CSIP_RETCODE_OK; 1185 | } 1186 | 1187 | int CSIPgetNumVars(CSIP_MODEL *model) 1188 | { 1189 | return model->nvars; 1190 | } 1191 | 1192 | int CSIPgetNumConss(CSIP_MODEL *model) 1193 | { 1194 | return model->nconss; 1195 | } 1196 | 1197 | CSIP_RETCODE CSIPsetInitialSolution(CSIP_MODEL *model, double *values) 1198 | { 1199 | // are there missing values? 1200 | SCIP_Bool initialsolpartial = FALSE; 1201 | for(int i = 0; i < model->nvars; ++i) 1202 | { 1203 | SCIP_Real val = values[i]; 1204 | if(val != val) // check for NaN 1205 | { 1206 | initialsolpartial = TRUE; 1207 | break; 1208 | } 1209 | } 1210 | 1211 | // was solution already given? 1212 | if (model->initialsol != NULL) 1213 | { 1214 | SCIP_in_CSIP(SCIPfreeSol(model->scip, &model->initialsol)); 1215 | } 1216 | assert(model->initialsol == NULL); 1217 | 1218 | // create new solution object 1219 | if(initialsolpartial) 1220 | { 1221 | SCIP_in_CSIP(SCIPcreatePartialSol(model->scip, &model->initialsol, NULL)); 1222 | 1223 | // give only the proper values, skip NaN 1224 | for(int i = 0; i < model->nvars; ++i) 1225 | { 1226 | SCIP_Real val = values[i]; 1227 | if(val == val) // check for NaN 1228 | { 1229 | SCIP_in_CSIP(SCIPsetSolVal(model->scip, model->initialsol, 1230 | model->vars[i], val)); 1231 | } 1232 | } 1233 | } 1234 | else 1235 | { 1236 | SCIP_in_CSIP(SCIPcreateSol(model->scip, &model->initialsol, NULL)); 1237 | 1238 | // copy the given values 1239 | SCIP_in_CSIP(SCIPsetSolVals(model->scip, model->initialsol, model->nvars, 1240 | model->vars, values)); 1241 | } 1242 | 1243 | // it will be given to SCIP in the CSIPsolve call 1244 | 1245 | return CSIP_RETCODE_OK; 1246 | } 1247 | 1248 | void *CSIPgetInternalSCIP(CSIP_MODEL *model) 1249 | { 1250 | return model->scip; 1251 | } 1252 | 1253 | 1254 | /* 1255 | * Constraint Handler 1256 | */ 1257 | 1258 | /* constraint handler data */ 1259 | struct SCIP_ConshdlrData 1260 | { 1261 | CSIP_MODEL *model; 1262 | CSIP_LAZYCALLBACK callback; 1263 | void *userdata; 1264 | SCIP_Bool checkonly; 1265 | SCIP_Bool feasible; 1266 | SCIP_SOL *sol; 1267 | }; 1268 | 1269 | SCIP_DECL_CONSFREE(consFreeLazy) 1270 | { 1271 | SCIP_CONSHDLRDATA *conshdlrdata; 1272 | 1273 | conshdlrdata = SCIPconshdlrGetData(conshdlr); 1274 | assert(conshdlrdata != NULL); 1275 | 1276 | SCIPfreeMemory(scip, &conshdlrdata); 1277 | 1278 | SCIPconshdlrSetData(conshdlr, NULL); 1279 | 1280 | return SCIP_OKAY; 1281 | } 1282 | 1283 | SCIP_DECL_CONSENFOLP(consEnfolpLazy) 1284 | { 1285 | SCIP_CONSHDLRDATA *conshdlrdata; 1286 | 1287 | *result = SCIP_FEASIBLE; 1288 | 1289 | conshdlrdata = SCIPconshdlrGetData(conshdlr); 1290 | conshdlrdata->checkonly = FALSE; 1291 | conshdlrdata->feasible = TRUE; 1292 | 1293 | CSIP_in_SCIP(conshdlrdata->callback(conshdlrdata->model, 1294 | conshdlrdata, conshdlrdata->userdata)); 1295 | 1296 | if (!conshdlrdata->feasible) 1297 | { 1298 | *result = SCIP_CONSADDED; 1299 | } 1300 | 1301 | return SCIP_OKAY; 1302 | } 1303 | 1304 | /* enfo pseudo solution just call enfo lp solution */ 1305 | SCIP_DECL_CONSENFOPS(consEnfopsLazy) 1306 | { 1307 | return consEnfolpLazy(scip, conshdlr, conss, nconss, nusefulconss, 1308 | solinfeasible, result); 1309 | } 1310 | 1311 | /* check callback */ 1312 | SCIP_DECL_CONSCHECK(consCheckLazy) 1313 | { 1314 | SCIP_CONSHDLRDATA *conshdlrdata; 1315 | 1316 | *result = SCIP_FEASIBLE; 1317 | 1318 | conshdlrdata = SCIPconshdlrGetData(conshdlr); 1319 | conshdlrdata->checkonly = TRUE; 1320 | conshdlrdata->feasible = TRUE; 1321 | conshdlrdata->sol = sol; 1322 | 1323 | CSIP_in_SCIP(conshdlrdata->callback(conshdlrdata->model, 1324 | conshdlrdata, conshdlrdata->userdata)); 1325 | 1326 | if (!conshdlrdata->feasible) 1327 | { 1328 | *result = SCIP_INFEASIBLE; 1329 | } 1330 | 1331 | return SCIP_OKAY; 1332 | } 1333 | 1334 | /* locks callback */ 1335 | SCIP_DECL_CONSLOCK(consLockLazy) 1336 | { 1337 | int i; 1338 | SCIP_VAR *var; 1339 | SCIP_CONSHDLRDATA *conshdlrdata; 1340 | 1341 | conshdlrdata = SCIPconshdlrGetData(conshdlr); 1342 | 1343 | assert(scip == conshdlrdata->model->scip); 1344 | 1345 | for (i = 0; i < conshdlrdata->model->nvars; ++i) 1346 | { 1347 | var = conshdlrdata->model->vars[i]; 1348 | SCIP_CALL(SCIPaddVarLocks(scip, var, nlockspos + nlocksneg, 1349 | nlockspos + nlocksneg)); 1350 | } 1351 | 1352 | return SCIP_OKAY; 1353 | } 1354 | 1355 | /* 1356 | * callback methods 1357 | */ 1358 | 1359 | CSIP_RETCODE CSIPaddLazyCallback(CSIP_MODEL *model, CSIP_LAZYCALLBACK callback, 1360 | void *userdata) 1361 | { 1362 | SCIP_CONSHDLRDATA *conshdlrdata; 1363 | SCIP_CONSHDLR *conshdlr; 1364 | SCIP *scip; 1365 | char name[SCIP_MAXSTRLEN]; 1366 | int enfopriority; 1367 | int checkpriority; 1368 | int eagerfreq; 1369 | SCIP_Bool needscons = FALSE; 1370 | 1371 | scip = model->scip; 1372 | 1373 | /* cons_integral has enfo priority 0 and we want to be checked before */ 1374 | enfopriority = 1; 1375 | /* we want to be checked as rarely as possible. 1376 | * -5000000 is the smallest proper value in SCIP's conshdlrs. */ 1377 | checkpriority = -5000000 - 1; 1378 | /* no eager evaluations?! */ 1379 | eagerfreq = -1; 1380 | 1381 | SCIP_in_CSIP(SCIPallocMemory(scip, &conshdlrdata)); 1382 | 1383 | conshdlrdata->model = model; 1384 | conshdlrdata->callback = callback; 1385 | conshdlrdata->userdata = userdata; 1386 | 1387 | SCIPsnprintf(name, SCIP_MAXSTRLEN, "lazycons_%d", model->nlazycb); 1388 | SCIP_in_CSIP(SCIPincludeConshdlrBasic( 1389 | scip, &conshdlr, name, "lazy constraint callback", 1390 | enfopriority, checkpriority, eagerfreq, needscons, 1391 | consEnfolpLazy, consEnfopsLazy, consCheckLazy, consLockLazy, 1392 | conshdlrdata)); 1393 | 1394 | SCIP_in_CSIP(SCIPsetConshdlrFree(scip, conshdlr, consFreeLazy)); 1395 | model->nlazycb += 1; 1396 | 1397 | return CSIP_RETCODE_OK; 1398 | } 1399 | 1400 | static 1401 | CSIP_RETCODE isSolIntegral(SCIP *scip, SCIP_SOL *sol, SCIP_Bool *integral) 1402 | { 1403 | // inspired by consCheckIntegral in cons_integral.c 1404 | SCIP_VAR **vars; 1405 | SCIP_Real solval; 1406 | int nbin, nint, nimpl, nallinteger; 1407 | 1408 | SCIP_in_CSIP(SCIPgetSolVarsData(scip, sol, &vars, NULL, 1409 | &nbin, &nint, &nimpl, NULL)); 1410 | 1411 | *integral = TRUE; 1412 | nallinteger = nbin + nint + nimpl; 1413 | for (int v = 0; v < nallinteger; ++v) 1414 | { 1415 | solval = SCIPgetSolVal(scip, sol, vars[v]); 1416 | if (!SCIPisFeasIntegral(scip, solval)) 1417 | { 1418 | *integral = FALSE; 1419 | break; 1420 | } 1421 | } 1422 | 1423 | return CSIP_RETCODE_OK; 1424 | } 1425 | 1426 | 1427 | CSIP_LAZY_CONTEXT CSIPlazyGetContext(CSIP_LAZYDATA *lazydata) 1428 | { 1429 | SCIP_Bool check = lazydata->checkonly; 1430 | SCIP_SOL *sol = check ? lazydata->sol : NULL; 1431 | SCIP_Bool integral = FALSE; 1432 | CSIP_CALL(isSolIntegral(lazydata->model->scip, sol, &integral)); 1433 | 1434 | if (integral) 1435 | { 1436 | return CSIP_LAZY_INTEGRALSOL; 1437 | } 1438 | else if (!check) 1439 | { 1440 | return CSIP_LAZY_LPRELAX; 1441 | } 1442 | else 1443 | { 1444 | return CSIP_LAZY_OTHER; 1445 | } 1446 | } 1447 | 1448 | /* returns LP or given solution depending whether we are called from check or enfo */ 1449 | CSIP_RETCODE CSIPlazyGetVarValues(CSIP_LAZYDATA *lazydata, double *output) 1450 | { 1451 | int i; 1452 | SCIP *scip; 1453 | SCIP_VAR *var; 1454 | SCIP_SOL *sol; 1455 | 1456 | scip = lazydata->model->scip; 1457 | sol = lazydata->checkonly ? lazydata->sol : NULL; 1458 | 1459 | for (i = 0; i < lazydata->model->nvars; ++i) 1460 | { 1461 | var = lazydata->model->vars[i]; 1462 | output[i] = SCIPgetSolVal(scip, sol, var); 1463 | } 1464 | 1465 | return CSIP_RETCODE_OK; 1466 | } 1467 | 1468 | CSIP_RETCODE CSIPlazyAddLinCons(CSIP_LAZYDATA *lazydata, int numindices, 1469 | int *indices, 1470 | double *coefs, double lhs, double rhs, int islocal) 1471 | { 1472 | SCIP *scip; 1473 | SCIP_CONS *cons; 1474 | SCIP_SOL *sol; 1475 | SCIP_RESULT result; 1476 | 1477 | scip = lazydata->model->scip; 1478 | 1479 | if (lazydata->checkonly) 1480 | { 1481 | sol = lazydata->sol; 1482 | } 1483 | else 1484 | { 1485 | sol = NULL; 1486 | } 1487 | 1488 | /* Is it reasonable to assume that if we solved the problem, then the lazy constraint 1489 | * is satisfied in the original problem? We get the error: 1490 | * "method cannot be called in the solved stage" 1491 | * and I am guessing this is because SCIP is checking whether the solution found in the 1492 | * presolved problem is feasible for the original problem. It could happen it is not feasible 1493 | * because of numerics mainly, hence the question in the comment 1494 | */ 1495 | if (SCIPgetStage(scip) == SCIP_STAGE_SOLVED) 1496 | { 1497 | assert(lazydata->checkonly); 1498 | lazydata->feasible = TRUE; /* to be very explicit */ 1499 | return CSIP_RETCODE_OK; 1500 | } 1501 | 1502 | if (SCIPgetStage(scip) == SCIP_STAGE_TRANSFORMED) 1503 | { 1504 | // we can't even create a cons in this stage, we trust the user's judgement 1505 | lazydata->feasible = FALSE; 1506 | return CSIP_RETCODE_OK; 1507 | } 1508 | 1509 | CSIP_CALL(createLinCons(lazydata->model, numindices, indices, coefs, lhs, rhs, 1510 | &cons)); 1511 | SCIP_in_CSIP(SCIPsetConsLocal(scip, cons, islocal == 1)); 1512 | SCIP_in_CSIP(SCIPcheckCons(scip, cons, sol, FALSE, FALSE, FALSE, &result)); 1513 | 1514 | if (result == SCIP_INFEASIBLE) 1515 | { 1516 | lazydata->feasible = FALSE; 1517 | } 1518 | 1519 | /* can not add constraints here */ 1520 | if (SCIPgetStage(scip) == SCIP_STAGE_INIT 1521 | || SCIPgetStage(scip) == SCIP_STAGE_TRANSFORMING 1522 | || SCIPgetStage(scip) == SCIP_STAGE_INITSOLVE) 1523 | { 1524 | assert(lazydata->checkonly); 1525 | SCIP_in_CSIP(SCIPreleaseCons(lazydata->model->scip, &cons)); 1526 | return CSIP_RETCODE_OK; 1527 | } 1528 | 1529 | /* we do not store cons, because the original problem does not contain them; 1530 | * and there is an issue when freeTransform is called 1531 | */ 1532 | SCIP_in_CSIP(SCIPaddCons(scip, cons)); 1533 | SCIP_in_CSIP(SCIPreleaseCons(lazydata->model->scip, &cons)); 1534 | 1535 | return CSIP_RETCODE_OK; 1536 | } 1537 | 1538 | /* Heuristic Plugin */ 1539 | 1540 | struct SCIP_HeurData 1541 | { 1542 | CSIP_MODEL *model; 1543 | CSIP_HEURCALLBACK callback; 1544 | void *userdata; 1545 | SCIP_HEUR *heur; 1546 | unsigned int stored_sols; 1547 | }; 1548 | 1549 | static 1550 | SCIP_DECL_HEURFREE(heurFreeUser) 1551 | { 1552 | SCIP_HEURDATA *heurdata; 1553 | 1554 | heurdata = SCIPheurGetData(heur); 1555 | assert(heurdata != NULL); 1556 | 1557 | SCIPfreeMemory(scip, &heurdata); 1558 | SCIPheurSetData(heur, NULL); 1559 | 1560 | return SCIP_OKAY; 1561 | } 1562 | 1563 | static 1564 | SCIP_DECL_HEUREXEC(heurExecUser) 1565 | { 1566 | SCIP_HEURDATA *heurdata = SCIPheurGetData(heur); 1567 | assert(heurdata != NULL); 1568 | 1569 | *result = SCIP_DIDNOTFIND; 1570 | heurdata->stored_sols = 0; 1571 | 1572 | CSIP_in_SCIP(heurdata->callback(heurdata->model, heurdata, 1573 | heurdata->userdata)); 1574 | 1575 | if (heurdata->stored_sols > 0) 1576 | { 1577 | *result = SCIP_FOUNDSOL; 1578 | } 1579 | 1580 | return SCIP_OKAY; 1581 | } 1582 | 1583 | // Copy values of solution to output array. Call this function from your 1584 | // heuristic callback. Solution is LP relaxation of current node. 1585 | CSIP_RETCODE CSIPheurGetVarValues(CSIP_HEURDATA *heurdata, double *output) 1586 | { 1587 | CSIP_MODEL *model = heurdata->model; 1588 | SCIP_in_CSIP(SCIPgetSolVals(model->scip, NULL, model->nvars, model->vars, 1589 | output)); 1590 | return CSIP_RETCODE_OK; 1591 | } 1592 | 1593 | // Supply a solution (as a dense array). Only complete solutions are supported. 1594 | CSIP_RETCODE CSIPheurAddSolution(CSIP_HEURDATA *heurdata, double *values) 1595 | { 1596 | SCIP_SOL *sol; 1597 | CSIP_MODEL *model = heurdata->model; 1598 | SCIP *scip = model->scip; 1599 | unsigned int stored = 0; 1600 | 1601 | SCIP_in_CSIP(SCIPcreateSol(scip, &sol, heurdata->heur)); 1602 | SCIP_in_CSIP(SCIPsetSolVals(scip, sol, model->nvars, model->vars, values)); 1603 | SCIP_in_CSIP(SCIPtrySolFree(scip, &sol, FALSE, FALSE, TRUE, TRUE, TRUE, &stored)); 1604 | 1605 | if (stored > 0) 1606 | { 1607 | heurdata->stored_sols += 1; 1608 | } 1609 | 1610 | return CSIP_RETCODE_OK; 1611 | } 1612 | 1613 | // Add a heuristic callback to the model. 1614 | // You may use userdata to pass any data. 1615 | CSIP_RETCODE CSIPaddHeuristicCallback( 1616 | CSIP_MODEL *model, CSIP_HEURCALLBACK callback, void *userdata) 1617 | { 1618 | SCIP_HEURDATA *heurdata; 1619 | SCIP_HEUR *heur; 1620 | SCIP *scip; 1621 | char name[SCIP_MAXSTRLEN]; 1622 | 1623 | scip = model->scip; 1624 | 1625 | SCIP_in_CSIP(SCIPallocMemory(scip, &heurdata)); 1626 | 1627 | SCIPsnprintf(name, SCIP_MAXSTRLEN, "heur_%d", model->nheur); 1628 | SCIP_in_CSIP(SCIPincludeHeurBasic( 1629 | scip, &heur, name, "heuristic callback", 'x', 1630 | 1, 1, 0, -1, SCIP_HEURTIMING_AFTERNODE, FALSE, 1631 | heurExecUser, heurdata)); 1632 | heurdata->model = model; 1633 | heurdata->callback = callback; 1634 | heurdata->userdata = userdata; 1635 | heurdata->heur = heur; 1636 | heurdata->stored_sols = 0; 1637 | 1638 | SCIP_in_CSIP(SCIPsetHeurFree(scip, heur, heurFreeUser)); 1639 | model->nheur += 1; 1640 | 1641 | return CSIP_RETCODE_OK; 1642 | } 1643 | 1644 | /* 1645 | * Message handler with a prefix 1646 | */ 1647 | 1648 | 1649 | struct SCIP_MessagehdlrData 1650 | { 1651 | char* prefix; 1652 | }; 1653 | 1654 | static void logMessage( 1655 | SCIP_MESSAGEHDLR* messagehdlr, FILE* file, const char* msg) 1656 | { 1657 | SCIP_MESSAGEHDLRDATA* messagehdlrdata; 1658 | messagehdlrdata = SCIPmessagehdlrGetData(messagehdlr); 1659 | 1660 | fputs(messagehdlrdata->prefix, file); 1661 | fputs(msg, file); 1662 | fflush(file); 1663 | return; 1664 | } 1665 | 1666 | static SCIP_DECL_MESSAGEHDLRFREE(messageHdlrFree) 1667 | { 1668 | SCIP_MESSAGEHDLRDATA* messagehdlrdata = SCIPmessagehdlrGetData(messagehdlr); 1669 | free(messagehdlrdata->prefix); 1670 | SCIPfreeMemory(NULL, &messagehdlrdata); 1671 | return SCIP_OKAY; 1672 | } 1673 | 1674 | CSIP_RETCODE CSIPsetMessagePrefix(CSIP_MODEL *model, const char* prefix) 1675 | { 1676 | SCIP_MESSAGEHDLR* messagehdlr = NULL; 1677 | SCIP_MESSAGEHDLRDATA* messagehdlrdata = NULL; 1678 | 1679 | SCIP_in_CSIP(SCIPallocMemory(NULL, &messagehdlrdata)); 1680 | messagehdlrdata->prefix = strDup(prefix); 1681 | SCIP_in_CSIP(SCIPmessagehdlrCreate(&messagehdlr, FALSE, NULL, FALSE, 1682 | logMessage, logMessage, logMessage, 1683 | messageHdlrFree, messagehdlrdata)); 1684 | 1685 | SCIP_in_CSIP(SCIPsetMessagehdlr(model->scip, messagehdlr)); 1686 | SCIP_in_CSIP(SCIPmessagehdlrRelease(&messagehdlr)); 1687 | 1688 | return CSIP_RETCODE_OK; 1689 | } 1690 | -------------------------------------------------------------------------------- /test/minunit.h: -------------------------------------------------------------------------------- 1 | // use MinUnit from http://www.jera.com/techinfo/jtns/jtn002.html 2 | // but change it to use failing assert, instead of just returning the message 3 | 4 | #define TOL 1e-5 5 | 6 | #define mu_assert(message, test) do { \ 7 | if (!(test)) \ 8 | { \ 9 | printf(" failed: %s\n", message); \ 10 | assert(test); \ 11 | } \ 12 | } while (0) 13 | 14 | #define mu_assert_int(message, value, expected) do { \ 15 | if (value != expected) \ 16 | { \ 17 | printf(" failed: %s (got: %d, expected: %d)\n", \ 18 | message, value, expected); \ 19 | assert(value == expected); \ 20 | } \ 21 | } while (0) 22 | 23 | #define mu_assert_near(message, value, expected) do { \ 24 | if (fabs((value) - (expected)) > TOL) \ 25 | { \ 26 | printf(" failed: %s (got: %f, expected: %f)\n", \ 27 | message, value, expected); \ 28 | assert(fabs((value) - (expected)) <= TOL); \ 29 | } \ 30 | } while (0) 31 | 32 | #define mu_run_test(test) do { \ 33 | printf(" %s\n", #test); \ 34 | test(); \ 35 | } while (0) 36 | 37 | // with our additions 38 | #define CHECK(x) mu_assert("CSIP error!", (x) == CSIP_RETCODE_OK) 39 | -------------------------------------------------------------------------------- /test/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "minunit.h" 8 | 9 | static void test_lp() 10 | { 11 | /* 12 | Small LP: 13 | min -x 14 | s.t. 2x + y <= 1.5 15 | x,y >= 0 16 | solution is (0.75,0) with objval -0.75 17 | */ 18 | int numindices = 2; 19 | int indices[] = {0, 1}; 20 | double objcoef[] = { -1.0, 0.0}; 21 | double conscoef[] = {2.0, 1.0}; 22 | double solution[2]; 23 | CSIP_MODEL *m; 24 | 25 | CHECK(CSIPcreateModel(&m)); 26 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 27 | 28 | int x_idx, y_idx; 29 | CHECK(CSIPaddVar(m, 0.0, INFINITY, CSIP_VARTYPE_CONTINUOUS, &x_idx)); 30 | CHECK(CSIPaddVar(m, 0.0, INFINITY, CSIP_VARTYPE_CONTINUOUS, &y_idx)); 31 | mu_assert_int("Wrong var index!", x_idx, 0); 32 | mu_assert_int("Wrong var index!", y_idx, 1); 33 | 34 | CHECK(CSIPsetObj(m, numindices, indices, objcoef)); 35 | int cons_idx; 36 | CHECK(CSIPaddLinCons(m, numindices, indices, conscoef, -INFINITY, 1.5, 37 | &cons_idx)); 38 | mu_assert_int("Wrong cons index!", cons_idx, 0); 39 | 40 | CHECK(CSIPsolve(m)); 41 | 42 | int solvestatus = CSIPgetStatus(m); 43 | mu_assert_int("Wrong status!", solvestatus, CSIP_STATUS_OPTIMAL); 44 | 45 | double objval = CSIPgetObjValue(m); 46 | mu_assert_near("Wrong objective value!", objval, -0.75); 47 | 48 | CHECK(CSIPgetVarValues(m, solution)); 49 | mu_assert_near("Wrong solution!", solution[0], 0.75); 50 | mu_assert_near("Wrong solution!", solution[1], 0.0); 51 | 52 | CHECK(CSIPfreeModel(m)); 53 | } 54 | 55 | static void test_mip() 56 | { 57 | /* 58 | Small MIP: 59 | min -5x_1 - 3x_2 - 2x_3 - 7x_4 - 4x_5 60 | s.t. 2x_1 + 8x_2 + 4x_3 + 2x_4 + 5x_5 <= 10 61 | x Bin 62 | solution is (1,0,0,1,1) with objval -16 63 | */ 64 | int numindices = 5; 65 | int indices[] = {0, 1, 2, 3, 4}; 66 | double objcoef[] = { -5.0, -3.0, -2.0, -7.0, -4.0}; 67 | double conscoef[] = {2.0, 8.0, 4.0, 2.0, 5.0}; 68 | double solution[5]; 69 | CSIP_MODEL *m; 70 | 71 | CHECK(CSIPcreateModel(&m)); 72 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 73 | 74 | int var_idx; 75 | for (int i = 0; i < 5; i++) 76 | { 77 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_BINARY, &var_idx)); 78 | mu_assert_int("Wrong var index!", var_idx, i); 79 | } 80 | 81 | CHECK(CSIPsetObj(m, numindices, indices, objcoef)); 82 | int cons_idx; 83 | CHECK(CSIPaddLinCons(m, numindices, indices, conscoef, -INFINITY, 10.0, 84 | &cons_idx)); 85 | mu_assert_int("Wrong cons index!", cons_idx, 0); 86 | 87 | CHECK(CSIPsolve(m)); 88 | int solvestatus = CSIPgetStatus(m); 89 | mu_assert_int("Wrong status!", solvestatus, CSIP_STATUS_OPTIMAL); 90 | 91 | double objval = CSIPgetObjValue(m); 92 | mu_assert_near("Wrong objective value!", objval, -16.0); 93 | double objbound = CSIPgetObjBound(m); 94 | mu_assert_near("Wrong objective bound!", objbound, -16.0); 95 | 96 | CHECK(CSIPgetVarValues(m, solution)); 97 | mu_assert_near("Wrong solution!", solution[0], 1.0); 98 | mu_assert_near("Wrong solution!", solution[1], 0.0); 99 | mu_assert_near("Wrong solution!", solution[2], 0.0); 100 | mu_assert_near("Wrong solution!", solution[3], 1.0); 101 | mu_assert_near("Wrong solution!", solution[4], 1.0); 102 | 103 | CHECK(CSIPfreeModel(m)); 104 | } 105 | 106 | static void test_mip2() 107 | { 108 | /* 109 | Small unbounded MIP: 110 | min x 111 | x Integer 112 | */ 113 | int numindices = 1; 114 | int indices[] = {0}; 115 | double objcoef[] = {1.0}; 116 | CSIP_MODEL *m; 117 | 118 | CHECK(CSIPcreateModel(&m)); 119 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 120 | 121 | int x_idx; 122 | CHECK(CSIPaddVar(m, -INFINITY, INFINITY, CSIP_VARTYPE_INTEGER, &x_idx)); 123 | mu_assert_int("Wrong var index!", x_idx, 0); 124 | 125 | CHECK(CSIPsetObj(m, numindices, indices, objcoef)); 126 | 127 | CHECK(CSIPsolve(m)); 128 | int solvestatus = CSIPgetStatus(m); 129 | mu_assert("Wrong status!", (solvestatus == CSIP_STATUS_UNBOUNDED 130 | || solvestatus == CSIP_STATUS_INFORUNBD)); 131 | 132 | CHECK(CSIPfreeModel(m)); 133 | } 134 | 135 | static void test_mip3() 136 | { 137 | /* 138 | Small infeasible MIP: 139 | min x 140 | x >= 2 141 | x <= 1 142 | */ 143 | int numindices = 1; 144 | int indices[] = {0}; 145 | double objcoef[] = {1.0}; 146 | double conscoef[] = {1.0}; 147 | CSIP_MODEL *m; 148 | 149 | CHECK(CSIPcreateModel(&m)); 150 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 151 | CHECK(CSIPaddVar(m, -INFINITY, INFINITY, CSIP_VARTYPE_INTEGER, NULL)); 152 | CHECK(CSIPsetObj(m, numindices, indices, objcoef)); 153 | 154 | CHECK(CSIPaddLinCons(m, numindices, indices, conscoef, -INFINITY, 1.0, NULL)); 155 | CHECK(CSIPaddLinCons(m, numindices, indices, conscoef, 2.0, INFINITY, NULL)); 156 | 157 | CHECK(CSIPsolve(m)); 158 | 159 | int solvestatus = CSIPgetStatus(m); 160 | mu_assert_int("Wrong status!", solvestatus, CSIP_STATUS_INFEASIBLE); 161 | 162 | CHECK(CSIPfreeModel(m)); 163 | } 164 | 165 | static void test_socp() 166 | { 167 | /* 168 | min t 169 | s.t. x + y >= 1 170 | x^2 + y^2 <= t^2 171 | t >= 0 172 | */ 173 | 174 | int objindices[] = {0}; 175 | double objcoef[] = {1.0}; 176 | int linindices[] = {1, 2}; 177 | double lincoef[] = {1.0, 1.0}; 178 | int quadi[] = {0, 1, 2}; 179 | int quadj[] = {0, 1, 2}; 180 | double quadcoef[] = { -1.0, 1.0, 1.0}; 181 | double solution[3]; 182 | 183 | CSIP_MODEL *m; 184 | 185 | CHECK(CSIPcreateModel(&m)); 186 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 187 | 188 | // t 189 | CHECK(CSIPaddVar(m, 0.0, INFINITY, CSIP_VARTYPE_CONTINUOUS, NULL)); 190 | // x 191 | CHECK(CSIPaddVar(m, -INFINITY, INFINITY, CSIP_VARTYPE_CONTINUOUS, NULL)); 192 | // y 193 | CHECK(CSIPaddVar(m, -INFINITY, INFINITY, CSIP_VARTYPE_CONTINUOUS, NULL)); 194 | 195 | mu_assert_int("Wrong number of vars!", CSIPgetNumVars(m), 3); 196 | 197 | // sparse objective 198 | CHECK(CSIPsetObj(m, 1, objindices, objcoef)); 199 | 200 | // sparse constraint 201 | CHECK(CSIPaddLinCons(m, 2, linindices, lincoef, 1.0, INFINITY, NULL)); 202 | 203 | CHECK(CSIPaddQuadCons(m, 0, NULL, NULL, 3, quadi, quadj, quadcoef, -INFINITY, 204 | 0.0, NULL)); 205 | 206 | mu_assert_int("Wrong number of conss!", CSIPgetNumConss(m), 2); 207 | 208 | CHECK(CSIPsolve(m)); 209 | 210 | int solvestatus = CSIPgetStatus(m); 211 | mu_assert_int("Wrong status!", solvestatus, CSIP_STATUS_OPTIMAL); 212 | 213 | double objval = CSIPgetObjValue(m); 214 | mu_assert_near("Wrong objective value!", objval, sqrt(0.5)); 215 | 216 | CHECK(CSIPgetVarValues(m, solution)); 217 | 218 | mu_assert_near("Wrong solution!", solution[0], sqrt(0.5)); 219 | // use weaker check, because of nonlinear constraint's abstol 220 | mu_assert("Wrong solution!", fabs(solution[1] - 0.5) < 0.01); 221 | mu_assert("Wrong solution!", fabs(solution[2] - 0.5) < 0.01); 222 | 223 | CHECK(CSIPfreeModel(m)); 224 | } 225 | 226 | static void test_nlp() 227 | { 228 | /* 229 | Small NLP: 230 | max x + y - z^3 231 | s.t. z^2 <= 1 232 | x, y <= 0 233 | solution is 0, 0, -1 234 | */ 235 | int nops = 3; 236 | CSIP_OP ops[] = {VARIDX, CONST, POW}; 237 | int children[] = {2, 0, 0, 1}; 238 | int begin[] = {0, 1, 2, 4}; 239 | double values[] = {2.0}; 240 | double lhs = -INFINITY; 241 | double rhs = 1; 242 | 243 | CSIP_OP obj_ops[] = {VARIDX, VARIDX, VARIDX, CONST, POW, MINUS, SUM}; 244 | int obj_children[] = {0, 1, 2, 0, 2, 3, 4, 0, 1, 5}; 245 | int obj_begin[] = {0, 1, 2, 3, 4, 6, 7, 10}; 246 | double obj_values[] = {3.0}; 247 | 248 | CSIP_MODEL *m; 249 | double solution[3]; 250 | 251 | CHECK(CSIPcreateModel(&m)); 252 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 253 | 254 | int x_idx, y_idx, z_idx; 255 | CHECK(CSIPaddVar(m, -INFINITY, 0.0, CSIP_VARTYPE_CONTINUOUS, &x_idx)); 256 | CHECK(CSIPaddVar(m, -INFINITY, 0.0, CSIP_VARTYPE_CONTINUOUS, &y_idx)); 257 | CHECK(CSIPaddVar(m, -INFINITY, INFINITY, CSIP_VARTYPE_CONTINUOUS, &z_idx)); 258 | mu_assert_int("Wrong var index!", x_idx, 0); 259 | mu_assert_int("Wrong var index!", y_idx, 1); 260 | mu_assert_int("Wrong var index!", z_idx, 2); 261 | 262 | int cons_idx; 263 | CHECK(CSIPaddNonLinCons(m, nops, ops, children, begin, values, lhs, rhs, 264 | &cons_idx)); 265 | mu_assert_int("Wrong cons index!", cons_idx, 0); 266 | 267 | CHECK(CSIPsetNonlinearObj(m, 7, obj_ops, obj_children, obj_begin, obj_values)); 268 | CHECK(CSIPsetSenseMaximize(m)); 269 | CHECK(CSIPsolve(m)); 270 | 271 | int solvestatus = CSIPgetStatus(m); 272 | mu_assert_int("Wrong status!", solvestatus, CSIP_STATUS_OPTIMAL); 273 | 274 | double objval = CSIPgetObjValue(m); 275 | mu_assert_near("Wrong objective value!", objval, 1.0); 276 | 277 | CHECK(CSIPgetVarValues(m, solution)); 278 | mu_assert_near("Wrong solution!", solution[0], 0.0); 279 | mu_assert_near("Wrong solution!", solution[1], 0.0); 280 | mu_assert_near("Wrong solution!", solution[2], -1.0); 281 | 282 | CHECK(CSIPfreeModel(m)); 283 | } 284 | 285 | static void test_nlp_no_obj() 286 | { 287 | /* 288 | Small feasibility NLP: 289 | find z 290 | s.t. z^2 <= 1 291 | */ 292 | int nops = 3; 293 | CSIP_OP ops[] = {VARIDX, CONST, POW}; 294 | int children[] = {0, 0, 0, 1}; 295 | int begin[] = {0, 1, 2, 4}; 296 | double values[] = {2.0}; 297 | double lhs = -INFINITY; 298 | double rhs = 1; 299 | 300 | CSIP_MODEL *m; 301 | double solution[1]; 302 | 303 | CHECK(CSIPcreateModel(&m)); 304 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 305 | 306 | int z_idx; 307 | CHECK(CSIPaddVar(m, -INFINITY, INFINITY, CSIP_VARTYPE_CONTINUOUS, &z_idx)); 308 | mu_assert_int("Wrong var index!", z_idx, 0); 309 | 310 | int cons_idx; 311 | CHECK(CSIPaddNonLinCons(m, nops, ops, children, begin, values, lhs, rhs, 312 | &cons_idx)); 313 | mu_assert_int("Wrong cons index!", cons_idx, 0); 314 | 315 | CHECK(CSIPsolve(m)); 316 | 317 | int solvestatus = CSIPgetStatus(m); 318 | mu_assert_int("Wrong status!", solvestatus, CSIP_STATUS_OPTIMAL); 319 | 320 | CHECK(CSIPgetVarValues(m, solution)); 321 | mu_assert("Violated constraint!", solution[0]*solution[0] <= 1.001); 322 | 323 | CHECK(CSIPfreeModel(m)); 324 | } 325 | 326 | static void test_quadobj() 327 | { 328 | /* 329 | min x^2 + y^2 330 | s.t. x + y >= 1 331 | -> {0.5, 0.5} 332 | */ 333 | 334 | int linindices[] = {0, 1}; 335 | double lincoef[] = {1.0, 1.0}; 336 | int quadi[] = {0, 1}; 337 | int quadj[] = {0, 1}; 338 | double quadcoef[] = {1.0, 1.0}; 339 | double solution[3]; 340 | 341 | CSIP_MODEL *m; 342 | 343 | CHECK(CSIPcreateModel(&m)); 344 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 345 | 346 | // x 347 | CHECK(CSIPaddVar(m, -INFINITY, INFINITY, CSIP_VARTYPE_CONTINUOUS, NULL)); 348 | // y 349 | CHECK(CSIPaddVar(m, -INFINITY, INFINITY, CSIP_VARTYPE_CONTINUOUS, NULL)); 350 | 351 | mu_assert_int("Wrong number of vars!", CSIPgetNumVars(m), 2); 352 | 353 | // sparse quadratic objective 354 | CHECK(CSIPsetQuadObj(m, 0, NULL, NULL, 2, quadi, quadj, quadcoef)); 355 | 356 | // sparse constraint 357 | CHECK(CSIPaddLinCons(m, 2, linindices, lincoef, 1.0, INFINITY, NULL)); 358 | 359 | mu_assert_int("Wrong number of conss!", CSIPgetNumConss(m), 1); 360 | 361 | CHECK(CSIPsolve(m)); 362 | 363 | int solvestatus = CSIPgetStatus(m); 364 | mu_assert_int("Wrong status!", solvestatus, CSIP_STATUS_OPTIMAL); 365 | 366 | double objval = CSIPgetObjValue(m); 367 | mu_assert_near("Wrong objective value!", objval, 0.5); 368 | 369 | CHECK(CSIPgetVarValues(m, solution)); 370 | 371 | // use weaker check, because of nonlinear constraint's abstol 372 | mu_assert("Wrong solution!", fabs(solution[0] - 0.5) < 0.01); 373 | mu_assert("Wrong solution!", fabs(solution[1] - 0.5) < 0.01); 374 | 375 | CHECK(CSIPfreeModel(m)); 376 | } 377 | 378 | struct MyData 379 | { 380 | int foo; 381 | double *storage; 382 | }; 383 | 384 | CSIP_RETCODE lazy_callback(CSIP_MODEL *m, CSIP_LAZYDATA *lazydata, 385 | void *userdata) 386 | { 387 | 388 | struct MyData *data = (struct MyData *) userdata; 389 | if (data->foo != 10) 390 | { 391 | return CSIP_RETCODE_ERROR; 392 | } 393 | int indices[] = {0, 1}; 394 | double coef[] = {1.0, 1.0}; 395 | 396 | CSIPlazyGetVarValues(lazydata, data->storage); 397 | 398 | // enforce x + y <= 3, global cut 399 | if (data->storage[0] + data->storage[1] > 3) 400 | { 401 | CSIPlazyAddLinCons(lazydata, 2, indices, coef, -INFINITY, 3.0, 0); 402 | } 403 | 404 | return CSIP_RETCODE_OK; 405 | } 406 | 407 | /* the problem written originally was: 408 | * min 0.5x + y 409 | * s.t. -inf <= x,y <= 2 410 | * x + y <= 3 (lazy) 411 | * which is unbounded, and for some scip-bug reason, it asserted some stuff 412 | * I am changing the problem to reflect Miles original vision 413 | */ 414 | static void test_lazy() 415 | { 416 | 417 | /* 418 | max 0.5x + y 419 | s.t. 0 <= x,y <= 2 420 | x + y <= 3 (lazy) 421 | solution is (1,2) 422 | */ 423 | 424 | int objindices[] = {0, 1}; 425 | double objcoef[] = {0.5, 1.0}; 426 | double solution[2]; 427 | 428 | CSIP_MODEL *m; 429 | 430 | CHECK(CSIPcreateModel(&m)); 431 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 432 | 433 | // x 434 | CHECK(CSIPaddVar(m, 0.0, 2.0, CSIP_VARTYPE_INTEGER, NULL)); 435 | 436 | // y 437 | CHECK(CSIPaddVar(m, 0.0, 2.0, CSIP_VARTYPE_INTEGER, NULL)); 438 | 439 | CHECK(CSIPsetObj(m, 2, objindices, objcoef)); 440 | 441 | CHECK(CSIPsetSenseMaximize(m)); 442 | 443 | struct MyData userdata = { 10, &solution[0] }; 444 | 445 | CHECK(CSIPaddLazyCallback(m, lazy_callback, &userdata)); 446 | 447 | CHECK(CSIPsolve(m)); 448 | 449 | int solvestatus = CSIPgetStatus(m); 450 | mu_assert_int("Wrong status!", solvestatus, CSIP_STATUS_OPTIMAL); 451 | 452 | double objval = CSIPgetObjValue(m); 453 | mu_assert_near("Wrong objective!", objval, 2.5); 454 | 455 | CHECK(CSIPgetVarValues(m, solution)); 456 | 457 | mu_assert_near("Wrong solution!", solution[0], 1.0); 458 | mu_assert_near("Wrong solution!", solution[1], 2.0); 459 | 460 | CHECK(CSIPfreeModel(m)); 461 | } 462 | 463 | CSIP_RETCODE lazy_callback2(CSIP_MODEL *m, CSIP_LAZYDATA *lazydata, 464 | void *userdata) 465 | { 466 | 467 | struct MyData *data = (struct MyData *) userdata; 468 | mu_assert("userdata failing", data->foo != 0); 469 | 470 | int indices[] = {0}; 471 | double coef[] = {1.0}; 472 | 473 | if (CSIPlazyGetContext(lazydata) == CSIP_LAZY_INTEGRALSOL) 474 | { 475 | CSIPlazyGetVarValues(lazydata, data->storage); 476 | // make sure we didn't get a fractional solution 477 | mu_assert_near("lazy context not working", data->storage[0], 478 | round(data->storage[0])); 479 | } 480 | 481 | // always add the cut x <= 10 482 | CSIPlazyAddLinCons(lazydata, 1, indices, coef, -INFINITY, 10.5, 0); 483 | 484 | return CSIP_RETCODE_OK; 485 | } 486 | 487 | static void test_lazy2() 488 | { 489 | 490 | /* 491 | min -x 492 | s.t. x <= 100.5, integer 493 | x <= 10.5 (lazy) 494 | solution is -10 495 | */ 496 | 497 | int objindices[] = {0}; 498 | double objcoef[] = { -1.0}; 499 | double solution[1]; 500 | 501 | CSIP_MODEL *m; 502 | 503 | CHECK(CSIPcreateModel(&m)); 504 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 505 | 506 | // x 507 | CHECK(CSIPaddVar(m, -INFINITY, 100.5, CSIP_VARTYPE_INTEGER, NULL)); 508 | 509 | CHECK(CSIPsetObj(m, 1, objindices, objcoef)); 510 | 511 | struct MyData userdata = { 10, &solution[0] }; 512 | 513 | CHECK(CSIPaddLazyCallback(m, lazy_callback2, &userdata)); 514 | 515 | CHECK(CSIPsolve(m)); 516 | 517 | int solvestatus = CSIPgetStatus(m); 518 | mu_assert_int("Wrong status!", solvestatus, CSIP_STATUS_OPTIMAL); 519 | 520 | double objval = CSIPgetObjValue(m); 521 | mu_assert_near("Wrong objective value!", objval, -10.0); 522 | 523 | CHECK(CSIPgetVarValues(m, solution)); 524 | mu_assert_near("Wrong solution!", solution[0], 10.0); 525 | 526 | CHECK(CSIPfreeModel(m)); 527 | } 528 | 529 | CSIP_RETCODE lazycb_interrupt(CSIP_MODEL *m, CSIP_LAZYDATA *lazydata, 530 | void *userdata) 531 | { 532 | CHECK(CSIPinterrupt(m)); 533 | return CSIP_RETCODE_OK; 534 | } 535 | 536 | static void test_lazy_interrupt() 537 | { 538 | /* 539 | find x 540 | s.t. x >= 1.5, integer 541 | solution is interrupted 542 | */ 543 | 544 | CSIP_MODEL *m; 545 | 546 | CHECK(CSIPcreateModel(&m)); 547 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 548 | CHECK(CSIPaddVar(m, 1.5, INFINITY, CSIP_VARTYPE_INTEGER, NULL)); 549 | 550 | CHECK(CSIPaddLazyCallback(m, lazycb_interrupt, NULL)); 551 | 552 | CHECK(CSIPsolve(m)); 553 | 554 | int solvestatus = CSIPgetStatus(m); 555 | mu_assert_int("Wrong status!", solvestatus, CSIP_STATUS_USERLIMIT); 556 | 557 | CHECK(CSIPfreeModel(m)); 558 | } 559 | 560 | static void test_objsense() 561 | { 562 | // min/max x 563 | // st. lb <= x <= ub 564 | CSIP_MODEL *m; 565 | int objindices[] = {0}; 566 | double objcoef[] = {1.0}; 567 | double lb = -2.3; 568 | double ub = 4.2; 569 | 570 | CHECK(CSIPcreateModel(&m)); 571 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 572 | CHECK(CSIPaddVar(m, lb, ub, CSIP_VARTYPE_CONTINUOUS, NULL)); 573 | CHECK(CSIPsetObj(m, 1, objindices, objcoef)); 574 | 575 | // default sense is 'minimize' 576 | CHECK(CSIPsolve(m)); 577 | mu_assert_int("Wrong status!", CSIPgetStatus(m), CSIP_STATUS_OPTIMAL); 578 | mu_assert_near("Wrong objective value!", CSIPgetObjValue(m), lb); 579 | 580 | // change sense to 'maximize' 581 | CHECK(CSIPsetSenseMaximize(m)); 582 | CHECK(CSIPsolve(m)); 583 | mu_assert_int("Wrong status!", CSIPgetStatus(m), CSIP_STATUS_OPTIMAL); 584 | mu_assert_near("Wrong objective value!", CSIPgetObjValue(m), ub); 585 | 586 | // change sense to 'minimize' 587 | CHECK(CSIPsetSenseMinimize(m)); 588 | CHECK(CSIPsolve(m)); 589 | mu_assert_int("Wrong status!", CSIPgetStatus(m), CSIP_STATUS_OPTIMAL); 590 | mu_assert_near("Wrong objective value!", CSIPgetObjValue(m), lb); 591 | 592 | CHECK(CSIPfreeModel(m)); 593 | } 594 | 595 | static void test_sos1() 596 | { 597 | // max 2x + 3y + 4z 598 | // SOS1(x, y, z) 599 | // 0 <= x, y, z <= 1 600 | // 601 | // sol -> (0, 0, 1) 602 | 603 | CSIP_MODEL *m; 604 | int objindices[] = {0, 1, 2}; 605 | double objcoef[] = {2.0, 3.0, 4.0}; 606 | 607 | CHECK(CSIPcreateModel(&m)); 608 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 609 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_CONTINUOUS, NULL)); // x 610 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_CONTINUOUS, NULL)); // y 611 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_CONTINUOUS, NULL)); // z 612 | CHECK(CSIPaddSOS1(m, 3, objindices, NULL, NULL)); 613 | CHECK(CSIPsetSenseMaximize(m)); 614 | CHECK(CSIPsetObj(m, 3, objindices, objcoef)); 615 | CHECK(CSIPsolve(m)); 616 | mu_assert_int("Wrong status!", CSIPgetStatus(m), CSIP_STATUS_OPTIMAL); 617 | mu_assert_near("Wrong objective value!", CSIPgetObjValue(m), 4.0); 618 | CHECK(CSIPfreeModel(m)); 619 | } 620 | 621 | static void test_sos2() 622 | { 623 | // max 2x + 3y + 4z 624 | // SOS2(x, y, z) 625 | // 0 <= x, y, z <= 1 626 | // 627 | // sol -> (0, 1, 1) 628 | 629 | CSIP_MODEL *m; 630 | int objindices[] = {0, 1, 2}; 631 | double objcoef[] = {2.0, 3.0, 4.0}; 632 | 633 | CHECK(CSIPcreateModel(&m)); 634 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 635 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_CONTINUOUS, NULL)); // x 636 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_CONTINUOUS, NULL)); // y 637 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_CONTINUOUS, NULL)); // z 638 | CHECK(CSIPaddSOS2(m, 3, objindices, NULL, NULL)); 639 | CHECK(CSIPsetSenseMaximize(m)); 640 | CHECK(CSIPsetObj(m, 3, objindices, objcoef)); 641 | CHECK(CSIPsolve(m)); 642 | mu_assert_int("Wrong status!", CSIPgetStatus(m), CSIP_STATUS_OPTIMAL); 643 | mu_assert_near("Wrong objective value!", CSIPgetObjValue(m), 7.0); 644 | CHECK(CSIPfreeModel(m)); 645 | } 646 | 647 | static void test_sos1_sos2() 648 | { 649 | // max 2x + 3y + 4z 650 | // SOS1(y, z) 651 | // SOS2(x, y, z) 652 | // 0 <= x, y, z <= 1 653 | // 654 | // sol -> (1, 0, 0) 655 | 656 | CSIP_MODEL *m; 657 | int objindices[] = {0, 1, 2}; 658 | double objcoef[] = {2.0, 3.0, 4.0}; 659 | 660 | CHECK(CSIPcreateModel(&m)); 661 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 662 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_CONTINUOUS, NULL)); // x 663 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_CONTINUOUS, NULL)); // y 664 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_CONTINUOUS, NULL)); // z 665 | CHECK(CSIPaddSOS1(m, 2, objindices + 1, NULL, NULL)); 666 | CHECK(CSIPaddSOS2(m, 3, objindices, NULL, NULL)); 667 | CHECK(CSIPsetSenseMaximize(m)); 668 | CHECK(CSIPsetObj(m, 3, objindices, objcoef)); 669 | CHECK(CSIPsolve(m)); 670 | mu_assert_int("Wrong status!", CSIPgetStatus(m), CSIP_STATUS_OPTIMAL); 671 | mu_assert_near("Wrong objective value!", CSIPgetObjValue(m), 5.0); 672 | CHECK(CSIPfreeModel(m)); 673 | } 674 | 675 | static void test_manythings() 676 | { 677 | // add many vars and conss to test variable sized array 678 | CSIP_MODEL *m; 679 | int indices[] = {0}; 680 | double coefs[] = {1.0}; 681 | int n = 9999; 682 | 683 | CHECK(CSIPcreateModel(&m)); 684 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 685 | for (int i = 0; i < n; ++i) 686 | { 687 | CHECK(CSIPaddVar(m, 0.0, i, CSIP_VARTYPE_CONTINUOUS, NULL)); 688 | indices[0] = i; 689 | CHECK(CSIPaddLinCons(m, 1, indices, coefs, 0.0, 1.0, NULL)); 690 | } 691 | CHECK(CSIPfreeModel(m)); 692 | } 693 | 694 | 695 | // store cut data 696 | struct DoubleData 697 | { 698 | int indices[2]; 699 | }; 700 | 701 | CSIP_RETCODE doubly_lazy_cb(CSIP_MODEL *m, CSIP_LAZYDATA *lazydata, 702 | void *userdata) 703 | { 704 | 705 | struct DoubleData *data = (struct DoubleData *) userdata; 706 | double coef[] = {1.0, 1.0}; 707 | 708 | // always add the cut var1 + var2 <= 1 709 | CSIPlazyAddLinCons(lazydata, 2, data->indices, coef, -INFINITY, 1.0, 0); 710 | 711 | return CSIP_RETCODE_OK; 712 | } 713 | 714 | static void test_doublelazy() 715 | { 716 | // max x + 3y + z 717 | // x + y + z <= 2 718 | // x + y <= 1 // lazy1 719 | // y + z <= 1 // lazy2 720 | // 0 <= x, y, z <= 1 721 | // 722 | // sol -> (0, 1, 0) 723 | 724 | CSIP_MODEL *m; 725 | int indices[] = {0, 1, 2}; 726 | double lincoef[] = {1.0, 1.0, 1.0}; 727 | double objcoef[] = {1.0, 3.0, 1.0}; 728 | struct DoubleData data1, data2; 729 | double solution[3]; 730 | 731 | CHECK(CSIPcreateModel(&m)); 732 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 733 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_CONTINUOUS, NULL)); // x 734 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_CONTINUOUS, NULL)); // y 735 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_CONTINUOUS, NULL)); // z 736 | CHECK(CSIPaddLinCons(m, 3, indices, lincoef, -INFINITY, 2.0, NULL)); 737 | CHECK(CSIPsetSenseMaximize(m)); 738 | CHECK(CSIPsetObj(m, 3, indices, objcoef)); 739 | 740 | data1.indices[0] = 0; 741 | data1.indices[1] = 1; 742 | CHECK(CSIPaddLazyCallback(m, doubly_lazy_cb, &data1)); 743 | 744 | data2.indices[0] = 2; 745 | data2.indices[1] = 1; 746 | CHECK(CSIPaddLazyCallback(m, doubly_lazy_cb, &data2)); 747 | 748 | CHECK(CSIPsolve(m)); 749 | mu_assert_int("Wrong status!", CSIPgetStatus(m), CSIP_STATUS_OPTIMAL); 750 | mu_assert_near("Wrong objective value!", CSIPgetObjValue(m), 3.0); 751 | 752 | CHECK(CSIPgetVarValues(m, solution)); 753 | mu_assert_near("Wrong solution!", solution[0], 0.0); 754 | mu_assert_near("Wrong solution!", solution[1], 1.0); 755 | mu_assert_near("Wrong solution!", solution[2], 0.0); 756 | 757 | CHECK(CSIPfreeModel(m)); 758 | } 759 | 760 | static void test_changeprob() 761 | { 762 | // solve two problems in a row: 763 | // 764 | // max x + 2y 765 | // x + y <= 1 766 | // x, y binary 767 | // 768 | // --> (0, 1) 769 | // 770 | // max x + 2y + 2z 771 | // x + y <= 1 772 | // x + y + z <= 2 773 | // y + z <= 1 774 | // x, y, z binary 775 | // 776 | // --> (1, 0, 1) 777 | 778 | CSIP_MODEL *m; 779 | int indices[] = {0, 1, 2}; 780 | double lincoef[] = {1.0, 1.0, 1.0}; 781 | double objcoef[] = {1.0, 2.0, 2.0}; 782 | double solution[3]; 783 | 784 | CHECK(CSIPcreateModel(&m)); 785 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 786 | 787 | // first problem 788 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_BINARY, NULL)); // x 789 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_BINARY, NULL)); // y 790 | CHECK(CSIPaddLinCons(m, 2, indices, lincoef, -INFINITY, 1.0, NULL)); 791 | CHECK(CSIPsetSenseMaximize(m)); 792 | CHECK(CSIPsetObj(m, 2, indices, objcoef)); 793 | 794 | CHECK(CSIPsolve(m)); 795 | mu_assert_int("Wrong status!", CSIPgetStatus(m), CSIP_STATUS_OPTIMAL); 796 | mu_assert_near("Wrong objective value!", CSIPgetObjValue(m), 2.0); 797 | 798 | CHECK(CSIPgetVarValues(m, solution)); 799 | mu_assert_near("Wrong solution!", solution[0], 0.0); 800 | mu_assert_near("Wrong solution!", solution[1], 1.0); 801 | 802 | // second problem, modifying the first 803 | CHECK(CSIPaddVar(m, 0.0, 1.0, CSIP_VARTYPE_BINARY, NULL)); // z 804 | CHECK(CSIPaddLinCons(m, 3, indices, lincoef, -INFINITY, 2.0, NULL)); 805 | CHECK(CSIPaddLinCons(m, 2, indices + 1, lincoef, -INFINITY, 1.0, NULL)); 806 | CHECK(CSIPsetObj(m, 3, indices, objcoef)); 807 | 808 | CHECK(CSIPsolve(m)); 809 | mu_assert_int("Wrong status!", CSIPgetStatus(m), CSIP_STATUS_OPTIMAL); 810 | mu_assert_near("Wrong objective value!", CSIPgetObjValue(m), 3.0); 811 | 812 | CHECK(CSIPgetVarValues(m, solution)); 813 | mu_assert_near("Wrong solution!", solution[0], 1.0); 814 | mu_assert_near("Wrong solution!", solution[1], 0.0); 815 | mu_assert_near("Wrong solution!", solution[2], 1.0); 816 | 817 | CHECK(CSIPfreeModel(m)); 818 | } 819 | 820 | static void test_changequadprob() 821 | { 822 | /* Solve two problems in a row: 823 | min x^2 + y^2 824 | s.t. x + y >= 1 825 | -> {0.5, 0.5} 826 | 827 | max -x^2 + y 828 | x + y == 1 829 | -> (-0.5, 1.5) 830 | */ 831 | 832 | int linindices[] = {0, 1}; 833 | double lincoef[] = {1.0, 1.0}; 834 | int quadi[] = {0, 1}; 835 | int quadj[] = {0, 1}; 836 | double quadcoef[] = {1.0, 1.0}; 837 | double quadcoef2[] = { -1.0}; 838 | double solution[3]; 839 | 840 | CSIP_MODEL *m; 841 | 842 | CHECK(CSIPcreateModel(&m)); 843 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 844 | 845 | // x 846 | CHECK(CSIPaddVar(m, -INFINITY, INFINITY, CSIP_VARTYPE_CONTINUOUS, NULL)); 847 | // y 848 | CHECK(CSIPaddVar(m, -INFINITY, INFINITY, CSIP_VARTYPE_CONTINUOUS, NULL)); 849 | 850 | mu_assert_int("Wrong number of vars!", CSIPgetNumVars(m), 2); 851 | 852 | // sparse quadratic objective 853 | CHECK(CSIPsetQuadObj(m, 0, NULL, NULL, 2, quadi, quadj, quadcoef)); 854 | 855 | // sparse constraint 856 | CHECK(CSIPaddLinCons(m, 2, linindices, lincoef, 1.0, INFINITY, NULL)); 857 | 858 | mu_assert_int("Wrong number of conss!", CSIPgetNumConss(m), 1); 859 | 860 | CHECK(CSIPsolve(m)); 861 | 862 | int solvestatus = CSIPgetStatus(m); 863 | mu_assert_int("Wrong status!", solvestatus, CSIP_STATUS_OPTIMAL); 864 | 865 | double objval = CSIPgetObjValue(m); 866 | mu_assert_near("Wrong objective value!", objval, 0.5); 867 | 868 | CHECK(CSIPgetVarValues(m, solution)); 869 | 870 | // use weaker check, because of nonlinear constraint's abstol 871 | mu_assert("Wrong solution!", fabs(solution[0] - 0.5) < 0.01); 872 | mu_assert("Wrong solution!", fabs(solution[1] - 0.5) < 0.01); 873 | 874 | 875 | // second problem, modifying the first 876 | CHECK(CSIPsetSenseMaximize(m)); 877 | CHECK(CSIPsetQuadObj(m, 1, &linindices[1], &lincoef[1], 1, quadi, quadj, 878 | quadcoef2)); 879 | 880 | // sparse constraint 881 | CHECK(CSIPaddLinCons(m, 2, linindices, lincoef, -INFINITY, 1.0, NULL)); 882 | 883 | CHECK(CSIPsolve(m)); 884 | mu_assert_int("Wrong status!", CSIPgetStatus(m), CSIP_STATUS_OPTIMAL); 885 | mu_assert_near("Wrong objective value!", CSIPgetObjValue(m), 1.25); 886 | 887 | CHECK(CSIPgetVarValues(m, solution)); 888 | // use weaker check, because of nonlinear constraint's abstol 889 | mu_assert("Wrong solution!", fabs(solution[0] + 0.5) < 0.01); 890 | mu_assert("Wrong solution!", fabs(solution[1] - 1.5) < 0.01); 891 | 892 | CHECK(CSIPfreeModel(m)); 893 | } 894 | 895 | 896 | static void test_changevartype() 897 | { 898 | // solve two problems in a row: 899 | // 900 | // min 2x + 3y 901 | // x + y >= 1.5 902 | // 0 <= x,y <= 9 903 | // 904 | // --> (1.5, 0) 905 | // 906 | // and with x integer 907 | // 908 | // --> (1, 0.5) 909 | 910 | 911 | CSIP_MODEL *m; 912 | int indices[] = {0, 1}; 913 | double lincoef[] = {1.0, 1.0}; 914 | double objcoef[] = {2.0, 3.0}; 915 | double solution[2]; 916 | 917 | CHECK(CSIPcreateModel(&m)); 918 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 919 | 920 | // first problem 921 | CHECK(CSIPaddVar(m, 0.0, 9.0, CSIP_VARTYPE_CONTINUOUS, NULL)); // x 922 | CHECK(CSIPaddVar(m, 0.0, 9.0, CSIP_VARTYPE_CONTINUOUS, NULL)); // y 923 | CHECK(CSIPaddLinCons(m, 2, indices, lincoef, 1.5, INFINITY, NULL)); 924 | CHECK(CSIPsetSenseMinimize(m)); 925 | CHECK(CSIPsetObj(m, 2, indices, objcoef)); 926 | 927 | CHECK(CSIPsolve(m)); 928 | mu_assert_int("Wrong status!", CSIPgetStatus(m), CSIP_STATUS_OPTIMAL); 929 | mu_assert_near("Wrong objective value!", CSIPgetObjValue(m), 3.0); 930 | 931 | CHECK(CSIPgetVarValues(m, solution)); 932 | mu_assert_near("Wrong solution!", solution[0], 1.5); 933 | mu_assert_near("Wrong solution!", solution[1], 0.0); 934 | 935 | // second problem, modifying the first 936 | mu_assert_int("Wrong vartype", CSIPgetVarType(m, 0), CSIP_VARTYPE_CONTINUOUS); 937 | mu_assert_int("Wrong vartype", CSIPgetVarType(m, 1), CSIP_VARTYPE_CONTINUOUS); 938 | CHECK(CSIPchgVarType(m, 0, CSIP_VARTYPE_INTEGER)); 939 | mu_assert_int("Wrong vartype", CSIPgetVarType(m, 0), CSIP_VARTYPE_INTEGER); 940 | mu_assert_int("Wrong vartype", CSIPgetVarType(m, 1), CSIP_VARTYPE_CONTINUOUS); 941 | 942 | CHECK(CSIPsolve(m)); 943 | mu_assert_int("Wrong status!", CSIPgetStatus(m), CSIP_STATUS_OPTIMAL); 944 | mu_assert_near("Wrong objective value!", CSIPgetObjValue(m), 3.5); 945 | 946 | CHECK(CSIPgetVarValues(m, solution)); 947 | mu_assert_near("Wrong solution!", solution[0], 1.0); 948 | mu_assert_near("Wrong solution!", solution[1], 0.5); 949 | 950 | CHECK(CSIPfreeModel(m)); 951 | } 952 | 953 | static void test_initialsol() 954 | { 955 | // attempt to solve a problem, but specify limits such that only the 956 | // user-defined initial solution is found 957 | // 958 | // min 2x 959 | // x in [10, 100], integer 960 | 961 | CSIP_MODEL *m; 962 | int indices[] = {0}; 963 | double objcoef[] = {2.0}; 964 | double solution[1]; 965 | 966 | double initialsol[] = {23.0}; 967 | 968 | CHECK(CSIPcreateModel(&m)); 969 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 970 | CHECK(CSIPsetIntParam(m, "limits/solutions", 1)); 971 | CHECK(CSIPsetIntParam(m, "heuristics/trivial/freq", -1)); 972 | 973 | CHECK(CSIPaddVar(m, 10.0, 100.0, CSIP_VARTYPE_INTEGER, NULL)); // x 974 | CHECK(CSIPsetObj(m, 1, indices, objcoef)); 975 | 976 | CHECK(CSIPsetInitialSolution(m, initialsol)); 977 | 978 | 979 | CHECK(CSIPsolve(m)); 980 | mu_assert_int("Wrong status!", CSIPgetStatus(m), CSIP_STATUS_USERLIMIT); 981 | mu_assert_near("Wrong objective value!", CSIPgetObjValue(m), 46.0); 982 | 983 | CHECK(CSIPgetVarValues(m, solution)); 984 | mu_assert_near("Wrong solution!", solution[0], 23.0); 985 | 986 | CHECK(CSIPfreeModel(m)); 987 | } 988 | 989 | static void test_initialsol_nlp() 990 | { 991 | /* 992 | attempt to solve a small NLP problem, but specify limits such that only 993 | the user-defined initial solution is found 994 | 995 | max x + y - z^3 996 | s.t. z^2 <= 1 997 | x, y <= 0 998 | 999 | optimal solution is 0, 0, -1 1000 | initial solution is 0, -1, -0.5 1001 | */ 1002 | int nops = 3; 1003 | CSIP_OP ops[] = {VARIDX, CONST, POW}; 1004 | int children[] = {2, 0, 0, 1}; 1005 | int begin[] = {0, 1, 2, 4}; 1006 | double values[] = {2.0}; 1007 | double lhs = -INFINITY; 1008 | double rhs = 1; 1009 | 1010 | CSIP_OP obj_ops[] = {VARIDX, VARIDX, VARIDX, CONST, POW, MINUS, SUM}; 1011 | int obj_children[] = {0, 1, 2, 0, 2, 3, 4, 0, 1, 5}; 1012 | int obj_begin[] = {0, 1, 2, 3, 4, 6, 7, 10}; 1013 | double obj_values[] = {3.0}; 1014 | 1015 | CSIP_MODEL *m; 1016 | double solution[3]; 1017 | double initialsol[3] = {0, -1, -0.5}; 1018 | 1019 | CHECK(CSIPcreateModel(&m)); 1020 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 1021 | CHECK(CSIPsetIntParam(m, "limits/solutions", 1)); 1022 | CHECK(CSIPsetIntParam(m, "heuristics/trivial/freq", -1)); 1023 | 1024 | int x_idx, y_idx, z_idx; 1025 | CHECK(CSIPaddVar(m, -INFINITY, 0.0, CSIP_VARTYPE_CONTINUOUS, &x_idx)); 1026 | CHECK(CSIPaddVar(m, -INFINITY, 0.0, CSIP_VARTYPE_CONTINUOUS, &y_idx)); 1027 | CHECK(CSIPaddVar(m, -INFINITY, INFINITY, CSIP_VARTYPE_CONTINUOUS, &z_idx)); 1028 | mu_assert_int("Wrong var index!", x_idx, 0); 1029 | mu_assert_int("Wrong var index!", y_idx, 1); 1030 | mu_assert_int("Wrong var index!", z_idx, 2); 1031 | 1032 | int cons_idx; 1033 | CHECK(CSIPaddNonLinCons(m, nops, ops, children, begin, values, lhs, rhs, 1034 | &cons_idx)); 1035 | mu_assert_int("Wrong cons index!", cons_idx, 0); 1036 | 1037 | CHECK(CSIPsetNonlinearObj(m, 7, obj_ops, obj_children, obj_begin, obj_values)); 1038 | CHECK(CSIPsetSenseMaximize(m)); 1039 | 1040 | CHECK(CSIPsetInitialSolution(m, initialsol)); 1041 | 1042 | CHECK(CSIPsolve(m)); 1043 | 1044 | int solvestatus = CSIPgetStatus(m); 1045 | mu_assert_int("Wrong status!", solvestatus, CSIP_STATUS_USERLIMIT); 1046 | 1047 | double objval = CSIPgetObjValue(m); 1048 | mu_assert_near("Wrong objective value!", objval, 0 - 1.0 + 0.125); 1049 | 1050 | CHECK(CSIPgetVarValues(m, solution)); 1051 | mu_assert_near("Wrong solution!", solution[0], 0.0); 1052 | mu_assert_near("Wrong solution!", solution[1], -1.0); 1053 | mu_assert_near("Wrong solution!", solution[2], -0.5); 1054 | 1055 | CHECK(CSIPfreeModel(m)); 1056 | } 1057 | 1058 | static void test_initialsol_partial() 1059 | { 1060 | /* 1061 | attempt to solve a small MIP problem, but specify limits such that only 1062 | the user-defined partial initial solution is found 1063 | 1064 | max x + 2y 1065 | s.t. x + y == 2 1066 | 0 <= x, y <= 2 (integer) 1067 | 1068 | optimal solution is 0, 2 1069 | initial solution is 1, ? (partial) 1070 | */ 1071 | 1072 | CSIP_MODEL *m; 1073 | int indices[2] = {0, 1}; 1074 | double solution[2]; 1075 | double lincoef[2] = {1.0, 1.0}; 1076 | double objcoef[2] = {1.0, 2.0}; 1077 | double mynan = 0.0/0.0; 1078 | double initialsol[2] = {1, mynan}; 1079 | 1080 | mu_assert_int("Not Not a Number!", (mynan != mynan), 1); 1081 | 1082 | CHECK(CSIPcreateModel(&m)); 1083 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 1084 | CHECK(CSIPsetIntParam(m, "limits/solutions", 1)); 1085 | CHECK(CSIPsetIntParam(m, "heuristics/trivial/freq", -1)); 1086 | 1087 | CHECK(CSIPaddVar(m, 0.0, 2.0, CSIP_VARTYPE_INTEGER, NULL)); 1088 | CHECK(CSIPaddVar(m, 0.0, 2.0, CSIP_VARTYPE_INTEGER, NULL)); 1089 | 1090 | CHECK(CSIPaddLinCons(m, 2, indices, lincoef, 2.0, 2.0, NULL)); 1091 | 1092 | CHECK(CSIPsetObj(m, 2, indices, objcoef)); 1093 | CHECK(CSIPsetSenseMaximize(m)); 1094 | 1095 | CHECK(CSIPsetInitialSolution(m, initialsol)); 1096 | 1097 | CHECK(CSIPsolve(m)); 1098 | 1099 | int solvestatus = CSIPgetStatus(m); 1100 | mu_assert_int("Wrong status!", solvestatus, CSIP_STATUS_USERLIMIT); 1101 | 1102 | double objval = CSIPgetObjValue(m); 1103 | mu_assert_near("Wrong objective value!", objval, 1.0*1.0 + 2.0*1.0); 1104 | 1105 | CHECK(CSIPgetVarValues(m, solution)); 1106 | mu_assert_near("Wrong solution!", solution[0], 1.0); 1107 | mu_assert_near("Wrong solution!", solution[1], 1.0); 1108 | 1109 | CHECK(CSIPfreeModel(m)); 1110 | } 1111 | 1112 | static void test_initialsol_nlp_partial() 1113 | { 1114 | /* 1115 | attempt to solve a small NLP problem, but specify limits such that only 1116 | the user-defined, partial initial solution is found 1117 | 1118 | max x + y - z^3 1119 | s.t. z^2 <= 1 1120 | x, y <= 0 1121 | 1122 | optimal solution is 0, 0, -1 1123 | initial solution is ?, -1, -1 1124 | */ 1125 | int nops = 3; 1126 | CSIP_OP ops[] = {VARIDX, CONST, POW}; 1127 | int children[] = {2, 0, 0, 1}; 1128 | int begin[] = {0, 1, 2, 4}; 1129 | double values[] = {2.0}; 1130 | double lhs = -INFINITY; 1131 | double rhs = 1; 1132 | 1133 | CSIP_OP obj_ops[] = {VARIDX, VARIDX, VARIDX, CONST, POW, MINUS, SUM}; 1134 | int obj_children[] = {0, 1, 2, 0, 2, 3, 4, 0, 1, 5}; 1135 | int obj_begin[] = {0, 1, 2, 3, 4, 6, 7, 10}; 1136 | double obj_values[] = {3.0}; 1137 | 1138 | CSIP_MODEL *m; 1139 | double solution[3]; 1140 | double mynan = 0.0/0.0; 1141 | double initialsol[3] = {mynan, -1, -1}; 1142 | 1143 | mu_assert_int("Not Not a Number!", (mynan != mynan), 1); 1144 | 1145 | CHECK(CSIPcreateModel(&m)); 1146 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 1147 | CHECK(CSIPsetIntParam(m, "limits/solutions", 1)); 1148 | CHECK(CSIPsetIntParam(m, "heuristics/trivial/freq", -1)); 1149 | 1150 | int x_idx, y_idx, z_idx; 1151 | CHECK(CSIPaddVar(m, -INFINITY, 0.0, CSIP_VARTYPE_INTEGER, &x_idx)); 1152 | CHECK(CSIPaddVar(m, -INFINITY, 0.0, CSIP_VARTYPE_INTEGER, &y_idx)); 1153 | CHECK(CSIPaddVar(m, -INFINITY, INFINITY, CSIP_VARTYPE_INTEGER, &z_idx)); 1154 | mu_assert_int("Wrong var index!", x_idx, 0); 1155 | mu_assert_int("Wrong var index!", y_idx, 1); 1156 | mu_assert_int("Wrong var index!", z_idx, 2); 1157 | 1158 | int cons_idx; 1159 | CHECK(CSIPaddNonLinCons(m, nops, ops, children, begin, values, lhs, rhs, 1160 | &cons_idx)); 1161 | mu_assert_int("Wrong cons index!", cons_idx, 0); 1162 | 1163 | CHECK(CSIPsetNonlinearObj(m, 7, obj_ops, obj_children, obj_begin, obj_values)); 1164 | CHECK(CSIPsetSenseMaximize(m)); 1165 | 1166 | CHECK(CSIPsetInitialSolution(m, initialsol)); 1167 | 1168 | CHECK(CSIPsolve(m)); 1169 | 1170 | int solvestatus = CSIPgetStatus(m); 1171 | mu_assert_int("Wrong status!", solvestatus, CSIP_STATUS_USERLIMIT); 1172 | 1173 | double objval = CSIPgetObjValue(m); 1174 | mu_assert_near("Wrong objective value!", objval, 0 - 1.0 + 1.0); 1175 | 1176 | CHECK(CSIPgetVarValues(m, solution)); 1177 | mu_assert_near("Wrong solution!", solution[0], 0.0); 1178 | mu_assert_near("Wrong solution!", solution[1], -1.0); 1179 | mu_assert_near("Wrong solution!", solution[2], -1.0); 1180 | 1181 | CHECK(CSIPfreeModel(m)); 1182 | } 1183 | 1184 | CSIP_RETCODE heurcb(CSIP_MODEL *model, CSIP_HEURDATA *heurdata, void *userdata) 1185 | { 1186 | double sol[] = {2.0, 2.0}; 1187 | mu_assert("Invalid userdata", userdata == NULL); 1188 | CHECK(CSIPheurAddSolution(heurdata, sol)); 1189 | return CSIP_RETCODE_OK; 1190 | } 1191 | 1192 | static void test_heurcb() 1193 | { 1194 | // attempt to solve a problem, but specify limits such that only the 1195 | // solution given from the heuristic callback is found 1196 | // 1197 | // min x + y 1198 | // 2x + 3y >= 6 1199 | // 3x + 2y >= 6 1200 | // x,y in [0, 3] integer 1201 | 1202 | CSIP_MODEL *m; 1203 | int indices[] = {0, 1}; 1204 | double objcoef[] = {1.0, 1.0}; 1205 | double coef1[] = {2.0, 3.0}; 1206 | double coef2[] = {3.0, 2.0}; 1207 | double solution[2]; 1208 | 1209 | CHECK(CSIPcreateModel(&m)); 1210 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 1211 | CHECK(CSIPsetIntParam(m, "limits/solutions", 1)); 1212 | CHECK(CSIPsetIntParam(m, "heuristics/feaspump/freq", -1)); 1213 | CHECK(CSIPsetIntParam(m, "heuristics/randrounding/freq", -1)); 1214 | CHECK(CSIPsetIntParam(m, "heuristics/rounding/freq", -1)); 1215 | CHECK(CSIPsetIntParam(m, "heuristics/shiftandpropagate/freq", -1)); 1216 | CHECK(CSIPsetIntParam(m, "heuristics/shifting/freq", -1)); 1217 | CHECK(CSIPsetIntParam(m, "heuristics/simplerounding/freq", -1)); 1218 | CHECK(CSIPsetIntParam(m, "heuristics/trivial/freq", -1)); 1219 | CHECK(CSIPsetIntParam(m, "presolving/maxrounds", 0)); 1220 | CHECK(CSIPsetIntParam(m, "separating/maxroundsroot", 0)); 1221 | 1222 | CHECK(CSIPaddVar(m, 0.0, 3.0, CSIP_VARTYPE_INTEGER, NULL)); // x 1223 | CHECK(CSIPaddVar(m, 0.0, 3.0, CSIP_VARTYPE_INTEGER, NULL)); // y 1224 | CHECK(CSIPaddLinCons(m, 2, indices, coef1, 6.0, INFINITY, NULL)); 1225 | CHECK(CSIPaddLinCons(m, 2, indices, coef2, 6.0, INFINITY, NULL)); 1226 | CHECK(CSIPsetObj(m, 2, indices, objcoef)); 1227 | 1228 | CHECK(CSIPaddHeuristicCallback(m, heurcb, NULL)); 1229 | 1230 | CHECK(CSIPsolve(m)); 1231 | mu_assert_int("Wrong status!", CSIPgetStatus(m), CSIP_STATUS_USERLIMIT); 1232 | mu_assert_near("Wrong objective value!", CSIPgetObjValue(m), 4.0); 1233 | 1234 | CHECK(CSIPgetVarValues(m, solution)); 1235 | mu_assert_near("Wrong solution!", solution[0], 2.0); 1236 | mu_assert_near("Wrong solution!", solution[1], 2.0); 1237 | 1238 | CHECK(CSIPfreeModel(m)); 1239 | } 1240 | 1241 | 1242 | static void test_params() 1243 | { 1244 | CSIP_MODEL *m; 1245 | 1246 | CHECK(CSIPcreateModel(&m)); 1247 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 1248 | mu_assert_int("Wrong param type!", CSIPgetParamType(m, "display/verblevel"), 1249 | CSIP_PARAMTYPE_INT); 1250 | mu_assert_int("Wrong param type!", CSIPgetParamType(m, "what am I?"), 1251 | CSIP_PARAMTYPE_NOTAPARAM); 1252 | 1253 | CHECK(CSIPfreeModel(m)); 1254 | } 1255 | 1256 | static void test_prefix() 1257 | { 1258 | CSIP_MODEL *m; 1259 | 1260 | CHECK(CSIPcreateModel(&m)); 1261 | CHECK(CSIPsetIntParam(m, "display/verblevel", 2)); 1262 | 1263 | char prefix[] = "test!prefix - "; 1264 | CHECK(CSIPsetMessagePrefix(m, prefix)); 1265 | 1266 | CHECK(CSIPsolve(m)); 1267 | CHECK(CSIPfreeModel(m)); 1268 | 1269 | // just checks that nothing crashes 1270 | // need to increase verblevel to see some output 1271 | } 1272 | 1273 | int main(int argc, char **argv) 1274 | { 1275 | printf("Running tests...\n"); 1276 | 1277 | mu_run_test(test_lp); 1278 | mu_run_test(test_mip); 1279 | mu_run_test(test_mip2); 1280 | mu_run_test(test_mip3); 1281 | mu_run_test(test_socp); 1282 | mu_run_test(test_nlp); 1283 | mu_run_test(test_nlp_no_obj); 1284 | mu_run_test(test_quadobj); 1285 | mu_run_test(test_lazy); 1286 | mu_run_test(test_lazy2); 1287 | mu_run_test(test_lazy_interrupt); 1288 | mu_run_test(test_objsense); 1289 | mu_run_test(test_sos1); 1290 | mu_run_test(test_sos2); 1291 | mu_run_test(test_sos1_sos2); 1292 | mu_run_test(test_manythings); 1293 | mu_run_test(test_doublelazy); 1294 | mu_run_test(test_changeprob); 1295 | mu_run_test(test_changequadprob); 1296 | mu_run_test(test_changevartype); 1297 | mu_run_test(test_initialsol); 1298 | mu_run_test(test_initialsol_nlp); 1299 | mu_run_test(test_initialsol_partial); 1300 | mu_run_test(test_initialsol_nlp_partial); 1301 | mu_run_test(test_heurcb); 1302 | mu_run_test(test_params); 1303 | mu_run_test(test_prefix); 1304 | 1305 | printf("All tests passed!\n"); 1306 | return 0; 1307 | } 1308 | --------------------------------------------------------------------------------