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

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