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

Contents of /trunk/ascend/solver/solver.c

Parent Directory Parent Directory | Revision Log Revision Log


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

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