/[ascend]/trunk/base/generic/solver/solver.c
ViewVC logotype

Contents of /trunk/base/generic/solver/solver.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1549 - (show annotations) (download) (as text)
Mon Jul 23 14:30:35 2007 UTC (12 years, 11 months ago) by jpye
File MIME type: text/x-csrc
File size: 15214 byte(s)
More work on IPOPT optimizer.
Prevented repeated slv_get_status errors; added exception in getSimulationStatus in C++.
1 /* ASCEND modelling environment
2 Copyright (C) 2007 Carnegie Mellon University
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA.
18 *//**
19 @file
20 Solver API for ASCEND, for solving NLA, LP, NLP, MINLP problems (anything
21 without derivatives).
22 *//*
23 by John Pye, May 2006
24 based on parts of slv_stdcalls.c, modified to provide dynamic loading
25 support, and aiming to improve the separation of 'system' and 'solver',
26 the former being a collection of equations and variables, and the latter
27 being all the methods and data storage that allows a solution to be
28 found and reported.
29 */
30
31 #include "solver.h"
32
33 #include <system/system_impl.h>
34 #include <general/list.h>
35 #include <utilities/ascMalloc.h>
36 #include <utilities/ascPanic.h>
37 #include <compiler/packages.h>
38
39 /**
40 Local function that holds the list of available solvers. The value
41 returned is NOT owned by the called.
42
43 @param free_space if 0, call as normal. if 1, free the list and maybe do some
44 cleaning up etc. Should be called whenever a simulation is destroyed.
45 */
46 static struct gl_list_t *solver_get_list(int free_space){
47 static int init = 0;
48 static struct gl_list_t *L;
49 if(free_space){
50 if(init && L)ASC_FREE(L);
51 init = 0;
52 return NULL;
53 }
54 if(!init){
55 L = gl_create(10);
56 init = 1;
57 }
58 return L;
59 }
60
61 /**
62 Return gl_list of SlvFunctionsT. C++ will use this to produce a
63 nice little list of integrator names that can be used in Python :-/
64 */
65 const struct gl_list_t *solver_get_engines(){
66 return solver_get_list(0);
67 }
68
69 const SlvFunctionsT *solver_engine(const int number){
70 const struct gl_list_t *L = solver_get_engines();
71 int i;
72 const SlvFunctionsT *S, *Sfound=NULL;
73 for(i=1; i <= gl_length(L); ++i){
74 S = gl_fetch(L,i);
75 if(S->number==number){
76 Sfound = S;
77 break;
78 }
79 }
80 return Sfound;
81 }
82
83 const SlvFunctionsT *solver_engine_named(const char *name){
84 const struct gl_list_t *L = solver_get_engines();
85 int i;
86 const SlvFunctionsT *S, *Sfound=NULL;
87 for(i=1; i <= gl_length(L); ++i){
88 S = gl_fetch(L,i);
89 if(strcmp(S->name,name)==0){
90 Sfound = S;
91 break;
92 }
93 }
94 return Sfound;
95 }
96
97 int slv_lookup_client(const char *name){
98 #if 0
99 int i;
100
101 if(name == NULL)return -1;
102
103 for(i = 0; i < NORC; i++) {
104 if(strcmp( SCD(i).name, name)==0) {
105 return i;
106 }
107 }
108 return -1;
109 #endif
110 const struct gl_list_t *L = solver_get_engines();
111 int i;
112 const SlvFunctionsT *S, *Sfound=NULL;
113 for(i=1; i <= gl_length(L); ++i){
114 S = gl_fetch(L,i);
115 if(strcmp(S->name,name)==0){
116 Sfound = S;
117 break;
118 }
119 }
120 if(Sfound){
121 return S->number;
122 }else{
123 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid engine name '%s'",name);
124 return 1;
125 }
126 }
127
128
129 /**
130 Register a new solver.
131
132 @TODO This needs work still, particularly of the dynamic loading
133 sort. it would be good if here we farmed out the dynamic loading
134 to another file so we don't have to crap this one all up.
135
136 old args:
137 (SlvRegistration registerfunc, CONST char *func
138 ,CONST char *file, int *new_client_id
139 */
140 int solver_register(const SlvFunctionsT *solver){
141 #if 0
142 int status;
143
144 status = registerfunc(&( SlvClientsData[NORC]));
145 if (!status) { /* ok */
146 SlvClientsData[NORC].number = NORC;
147 *new_client_id = NORC;
148 NORC++;
149 } else {
150 *new_client_id = -2;
151 ERROR_REPORTER_NOLINE(ASC_PROG_ERR,"Client %d registration failure (%d)!",NORC,status);
152 }
153 return status;
154 #endif
155
156 /* get the current list of registered engines */
157 const struct gl_list_t *L;
158 L = solver_get_engines();
159
160 #if 0
161 CONSOLE_DEBUG("REGISTERING SOLVER");
162 CONSOLE_DEBUG("There were %lu registered solvers", gl_length(solver_get_list(0)));
163 #endif
164
165 int i;
166 const SlvFunctionsT *S;
167 for(i=1; i < gl_length(L); ++i){
168 S = (const SlvFunctionsT *)gl_fetch(L,i);
169 if(strcmp(solver->name,S->name)==0){
170 ERROR_REPORTER_HERE(ASC_USER_WARNING,"Solver with name '%s' is already registered",solver->name);
171 return 0;
172 }
173 if(solver->number == S->number){
174 ERROR_REPORTER_HERE(ASC_USER_WARNING,"Solver with ID '%d' is already registered",solver->number);
175 return 0;
176 }
177 }
178
179 #if 0
180 CONSOLE_DEBUG("Adding engine '%s'",solver->name);
181 #endif
182
183 gl_append_ptr(L,(SlvFunctionsT *)solver);
184
185 #if 0
186 CONSOLE_DEBUG("There are now %lu registered solvers", gl_length(solver_get_list(0)));
187 #endif
188 return 0;
189 }
190
191
192 /*------------------------------------------------------------------------------
193 SOLVER REGISTRATION
194 */
195
196 /* rewrote this stuff to get rid of all the #ifdefs -- JP */
197
198 struct StaticSolverRegistration{
199 const char *importname;
200 };
201
202 /*
203 The names here are only used to provide information in the case where
204 solver registration fails. The definitive solver names are in the slv*.c
205 files.
206 */
207 static const struct StaticSolverRegistration slv_reg[]={
208 {"qrslv"}
209 ,{"conopt"}
210 ,{"lrslv"}
211 ,{"cmslv"}
212 ,{"ipopt"}
213 ,{NULL}
214 #if 0
215 /* {0,"SLV",&slv0_register} */
216 /* ,{0,"MINOS",&slv1_register} */
217 /* ,{0,"CSLV",&slv4_register} */
218 /* ,{0,"LSSLV",&slv5_register} */
219 /* ,{0,"MPS",&slv6_register} */
220 /* ,{0,"NGSLV",&slv7_register} */
221 /* ,{0,"OPTSQP",&slv2_register} */
222 ,{HAVE_CONOPT,"CONOPT",&slv8_register}
223 ,{HAVE_LRSLV,"LRSLV",&slv9a_register}
224 ,{HAVE_CMSLV,"CMSLV",&slv9_register}
225 ,{0,NULL,NULL}
226 #endif
227 };
228
229 int SlvRegisterStandardClients(void){
230 int nclients = 0;
231 //int newclient=0;
232 int error;
233 int i;
234
235 /* CONSOLE_DEBUG("REGISTERING STANDARD SOLVER ENGINES"); */
236 for(i=0; slv_reg[i].importname!=NULL;++i){
237 error = package_load(slv_reg[i].importname,NULL);
238 if(error){
239 ERROR_REPORTER_HERE(ASC_PROG_ERR
240 ,"Unable to register solver '%s' (error %d)."
241 ,slv_reg[i].importname,error
242 );
243 }else{
244 CONSOLE_DEBUG("Solver '%s' registered OK",slv_reg[i].importname);
245 nclients++;
246 }
247 }
248 return nclients;
249 }
250
251 /*------------------------------------------------------*/
252
253 static void printwarning(const char * fname, slv_system_t sys)
254 {
255 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,
256 "%s called with bad registered client (%s).",fname,
257 slv_solver_name(slv_get_selected_solver(sys)));
258 }
259
260 static void printinfo(slv_system_t sys, const char *rname){
261 asc_assert(sys->internals);
262 if(sys->internals->name) {
263 ERROR_REPORTER_NOLINE(ASC_PROG_NOTE,
264 "Client %s does not support function '%s'.",
265 sys->internals->name,rname);
266 }
267 }
268
269 /*-----------------------------------------------------------
270 These macros do some more elimination of repetition. Here we're
271 trying to replace some more complex 'method-like' calls on
272 slv_system_t:
273
274 These macros use macro-argument-concatenation and macro stringification.
275 Verified that the former works with Visual C++.
276 http://www.codeproject.com/macro/metamacros.asp
277 */
278
279 /** Define a method like 'void slv_METHODNAME(sys)' */
280 #define DEFINE_SLV_PROXY_METHOD_VOID(METHOD) \
281 void slv_ ## METHOD (slv_system_t sys){ \
282 if(CF(sys,METHOD)==NULL){ \
283 printwarning(#METHOD,sys); \
284 return; \
285 } \
286 SF(sys,METHOD)(sys,sys->ct); \
287 }
288
289 /** Define a method like 'RETURNTYPE slv_METHOD(sys)'; */
290 #define DEFINE_SLV_PROXY_METHOD(METHOD,PROP,RETTYPE,ERRVAL) \
291 RETTYPE slv_ ## METHOD (slv_system_t sys){ \
292 /* CONSOLE_DEBUG("slv_" #METHOD);*/ \
293 asc_assert(sys->internals); \
294 /*CONSOLE_DEBUG("internals OK");*/ \
295 if(sys->internals->PROP==NULL){ \
296 /*CONSOLE_DEBUG("method is NULL");*/ \
297 printinfo(sys, #METHOD); \
298 return ERRVAL; \
299 } \
300 /*CONSOLE_DEBUG("running method " #PROP " in solver %d",sys->internals->number);*/ \
301 return (sys->internals->PROP)(sys,sys->ct); \
302 }
303
304 /** Define a method like 'void slv_METHOD(sys,TYPE PARAMNAME)'; */
305 #define DEFINE_SLV_PROXY_METHOD_PARAM(METHOD,PROP,PARAMTYPE,PARAMNAME) \
306 void slv_ ## METHOD (slv_system_t sys, PARAMTYPE PARAMNAME){ \
307 if(!sys->internals || !sys->internals->PROP){ \
308 printwarning(#METHOD,sys); \
309 return; \
310 } \
311 (sys->internals->PROP)(sys,sys->ct, PARAMNAME); \
312 }
313
314 DEFINE_SLV_PROXY_METHOD_PARAM(get_parameters,get_parameters,slv_parameters_t*,parameters) /*;*/
315
316 void slv_set_parameters(slv_system_t sys,slv_parameters_t *parameters)
317 {
318 asc_assert(sys->internals);
319 if(sys->internals->setparam == NULL ) {
320 printwarning("slv_set_parameters",sys);
321 return;
322 }
323 if (parameters->whose != sys->solver) {
324 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,
325 "slv_set_parameters cannot pass parameters from one solver to a"
326 " another.");
327 return;
328 }
329 (sys->internals->setparam)(sys,sys->ct,parameters);
330 }
331
332 int slv_get_status(slv_system_t sys, slv_status_t *status){
333 static const SlvFunctionsT *lastsolver = 0;
334 if(!sys->internals)return -1;
335 if(sys->internals->getstatus==NULL){
336 if(lastsolver==0 || lastsolver != sys->internals){
337 printinfo(sys,"get_status");
338 lastsolver = sys->internals;
339 }
340 return -1;
341 }
342 return (sys->internals->getstatus)(sys,sys->ct,status);
343 }
344
345 DEFINE_SLV_PROXY_METHOD_PARAM(dump_internals,dumpinternals,int,level) /*;*/
346
347 DEFINE_SLV_PROXY_METHOD(get_linsolqr_sys, getlinsys, linsolqr_system_t, NULL) /*;*/
348
349 DEFINE_SLV_PROXY_METHOD(get_sys_mtx, get_sys_mtx, mtx_matrix_t, NULL) /*;*/
350 DEFINE_SLV_PROXY_METHOD(presolve,presolve,int,-1) /*;*/
351 DEFINE_SLV_PROXY_METHOD(resolve,resolve,int,-1) /*;*/
352 DEFINE_SLV_PROXY_METHOD(iterate,iterate,int,-1) /*;*/
353 DEFINE_SLV_PROXY_METHOD(solve,solve,int,-1) /*;*/
354
355 int slv_eligible_solver(slv_system_t sys)
356 {
357 asc_assert(sys->internals);
358 if(sys->internals->celigible == NULL ) {
359 printwarning("slv_eligible_solver",sys);
360 return 0;
361 }
362 return (sys->internals->celigible)(sys);
363 }
364
365 //-------------
366
367
368 int slv_select_solver(slv_system_t sys,int solver){
369
370 int status_index;
371 SlvClientDestroyF *destroy;
372 const SlvFunctionsT *S;
373
374 if(sys ==NULL) {
375 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_select_solver called with NULL system.");
376 return -1;
377 }
378
379 CONSOLE_DEBUG("CHECKING FOR SOLVER %d", solver);
380
381 if(solver_engine(solver)){
382 CONSOLE_DEBUG("SOLVER FOUND");
383 if(sys->ct != NULL && solver != sys->solver){
384 CONSOLE_DEBUG("DIFFERENT SOLVER");
385 //CONSOLE_DEBUG("g_SlvNumberOfRegisteredClients = %d, sys->solver = %d", g_SlvNumberOfRegisteredClients, sys->solver);
386 asc_assert(sys->solver >= -1);
387 //asc_assert(g_SlvNumberOfRegisteredClients > 0);
388 //asc_assert(sys->solver < g_SlvNumberOfRegisteredClients);
389 S = solver_engine(sys->solver);
390 destroy = S->cdestroy;
391 if(destroy!=NULL) {
392 (destroy)(sys,sys->ct);
393 sys->ct = NULL;
394 }else{
395 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_select_solver: 'cdestroy' is undefined on solver '%s' (index %d).",
396 S->name, sys->solver);
397 }
398 }
399
400 CONSOLE_DEBUG("PREVIOUS SOLVER IS CLEAR");
401
402 if(sys->ct != NULL) {
403 CONSOLE_DEBUG("CURRENT SOLVER UNCHANGED");
404 return sys->solver;
405 }
406
407 status_index = solver;
408 sys->solver = solver;
409 sys->internals = solver_engine(solver);
410 if(sys->internals->ccreate != NULL){
411 sys->ct = (sys->internals->ccreate)(sys,&status_index);
412 }else{
413 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"slv_select_solver create failed due to bad client '%s'.",
414 slv_solver_name(sys->solver));
415 return sys->solver;
416 }
417 if(sys->ct==NULL){
418 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"SlvClientCreate failed in slv_select_solver.");
419 sys->solver = -1;
420 }else{
421 if (status_index){
422 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"SlvClientCreate succeeded with warning %d %s.",
423 status_index," in slv_select_solver");
424 }
425 /* we could do a better job explaining the client warnings... */
426 sys->solver = solver;
427 }
428 }else{
429 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_select_solver: invalid solver index '%d'.",
430 solver);
431 return -1;
432 }
433 return sys->solver;
434 }
435
436 /**
437 @TODO looks buggy
438 */
439 int slv_switch_solver(slv_system_t sys,int solver)
440 {
441 int status_index;
442
443 if(sys ==NULL){
444 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_switch_solver called with NULL system.");
445 return -1;
446 }
447
448 CONSOLE_DEBUG("CHECKING FOR SOLVER %d", solver);
449
450 if(solver_engine(solver)){
451 status_index = solver;
452 sys->solver = solver;
453 sys->internals = solver_engine(solver);
454 CONSOLE_DEBUG("SWITCHING TO SOLVER '%s'",sys->internals->name);
455 if(sys->internals->ccreate != NULL) {
456 sys->ct = (sys->internals->ccreate)(sys,&status_index);
457 } else {
458 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_switch_solver create failed due to bad client '%s'.",
459 slv_solver_name(sys->solver));
460 return sys->solver;
461 }
462 if (sys->ct==NULL) {
463 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"SlvClientCreate failed in slv_switch_solver.");
464 sys->solver = -1;
465 }else{
466 if (status_index) {
467 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"SlvClientCreate succeeded with warning %d %s.",
468 status_index," in slv_switch_solver");
469 }
470 sys->solver = solver;
471 }
472 }else{
473 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Unknown client '%d'.",solver);
474 return -1;
475 }
476 return sys->solver;
477 }
478
479 /*--------------------------------*/
480
481 int slv_get_selected_solver(slv_system_t sys){
482 if (sys!=NULL) return sys->solver;
483 return -1;
484 }
485
486
487 int32 slv_get_default_parameters(int sindex,
488 slv_parameters_t *parameters)
489 {
490 SlvFunctionsT *S;
491 S = solver_engine(sindex);
492 if(S){
493 if(S->getdefparam == NULL ) {
494 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"slv_get_default_parameters called with parameterless index.");
495 return 0;
496 }else{
497 /* send NULL system when setting up interface */
498 (S->getdefparam)(NULL,NULL,parameters);
499 return 1;
500 }
501 }else{
502 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"slv_get_default_parameters called with unregistered index.");
503 return 0;
504 }
505 }
506
507
508 /*-----------------------------------------------------------*/
509
510 SlvClientToken slv_get_client_token(slv_system_t sys)
511 {
512 if (sys==NULL) {
513 FPRINTF(stderr,"slv_get_client_token called with NULL system.");
514 return NULL;
515 }
516 return sys->ct;
517 }
518
519
520 void slv_set_client_token(slv_system_t sys, SlvClientToken ct)
521 {
522 if (sys==NULL) {
523 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"slv_set_client_token called with NULL system.");
524 return;
525 }
526 sys->ct = ct;
527 }
528
529 void slv_set_solver_index(slv_system_t sys, int solver)
530 {
531 if (sys==NULL) {
532 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"slv_set_solver_index called with NULL system.");
533 return;
534 }
535 sys->solver = solver;
536 sys->internals = solver_engine(solver);
537 }
538
539
540 const char *slv_solver_name(int sindex){
541 static char errname[] = "ErrorSolver";
542 const SlvFunctionsT *S = solver_engine(sindex);
543 if(S!=NULL){
544 if(S->name == NULL) {
545 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"unnamed solver: index='%d'",sindex);
546 return errname;
547 }else{
548 return S->name;
549 }
550 }else{
551 ERROR_REPORTER_HERE(ASC_PROG_ERROR,"invalid solver index '%d'", sindex);
552 return errname;
553 }
554 }
555

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22