/[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 1504 - (show annotations) (download) (as text)
Sat Jun 23 14:12:34 2007 UTC (16 years, 5 months ago) by jpye
File MIME type: text/x-csrc
File size: 15065 byte(s)
Moving solvers to their own directory
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 ,{NULL}
213 #if 0
214 /* {0,"SLV",&slv0_register} */
215 /* ,{0,"MINOS",&slv1_register} */
216 /* ,{0,"CSLV",&slv4_register} */
217 /* ,{0,"LSSLV",&slv5_register} */
218 /* ,{0,"MPS",&slv6_register} */
219 /* ,{0,"NGSLV",&slv7_register} */
220 /* ,{0,"OPTSQP",&slv2_register} */
221 ,{HAVE_CONOPT,"CONOPT",&slv8_register}
222 ,{HAVE_LRSLV,"LRSLV",&slv9a_register}
223 ,{HAVE_CMSLV,"CMSLV",&slv9_register}
224 ,{0,NULL,NULL}
225 #endif
226 };
227
228 int SlvRegisterStandardClients(void){
229 int nclients = 0;
230 //int newclient=0;
231 int error;
232 int i;
233
234 /* CONSOLE_DEBUG("REGISTERING STANDARD SOLVER ENGINES"); */
235 for(i=0; slv_reg[i].importname!=NULL;++i){
236 error = LoadArchiveLibrary(slv_reg[i].importname,NULL);
237 if(error){
238 ERROR_REPORTER_HERE(ASC_PROG_ERR
239 ,"Unable to register solver '%s' (error %d)."
240 ,slv_reg[i].importname,error
241 );
242 }else{
243 CONSOLE_DEBUG("Solver '%s' registered OK",slv_reg[i].importname);
244 nclients++;
245 }
246 }
247 return nclients;
248 }
249
250 /*------------------------------------------------------*/
251
252 static void printwarning(const char * fname, slv_system_t sys)
253 {
254 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,
255 "%s called with bad registered client (%s).",fname,
256 slv_solver_name(slv_get_selected_solver(sys)));
257 }
258
259 static void printinfo(slv_system_t sys, const char *rname){
260 asc_assert(sys->internals);
261 if(sys->internals->name) {
262 ERROR_REPORTER_NOLINE(ASC_PROG_NOTE,
263 "Client %s does not support function '%s'.",
264 sys->internals->name,rname);
265 }
266 }
267
268 /*-----------------------------------------------------------
269 These macros do some more elimination of repetition. Here we're
270 trying to replace some more complex 'method-like' calls on
271 slv_system_t:
272
273 These macros use macro-argument-concatenation and macro stringification.
274 Verified that the former works with Visual C++.
275 http://www.codeproject.com/macro/metamacros.asp
276 */
277
278 /** Define a method like 'void slv_METHODNAME(sys)' */
279 #define DEFINE_SLV_PROXY_METHOD_VOID(METHOD) \
280 void slv_ ## METHOD (slv_system_t sys){ \
281 if(CF(sys,METHOD)==NULL){ \
282 printwarning(#METHOD,sys); \
283 return; \
284 } \
285 SF(sys,METHOD)(sys,sys->ct); \
286 }
287
288 /** Define a method like 'RETURNTYPE slv_METHOD(sys)'; */
289 #define DEFINE_SLV_PROXY_METHOD(METHOD,PROP,RETTYPE,ERRVAL) \
290 RETTYPE slv_ ## METHOD (slv_system_t sys){ \
291 /* CONSOLE_DEBUG("slv_" #METHOD);*/ \
292 asc_assert(sys->internals); \
293 /*CONSOLE_DEBUG("internals OK");*/ \
294 if(sys->internals->PROP==NULL){ \
295 /*CONSOLE_DEBUG("method is NULL");*/ \
296 printinfo(sys, #METHOD); \
297 return ERRVAL; \
298 } \
299 /*CONSOLE_DEBUG("running method " #PROP " in solver %d",sys->internals->number);*/ \
300 return (sys->internals->PROP)(sys,sys->ct); \
301 }
302
303 /** Define a method like 'void slv_METHOD(sys,TYPE PARAMNAME)'; */
304 #define DEFINE_SLV_PROXY_METHOD_PARAM(METHOD,PROP,PARAMTYPE,PARAMNAME) \
305 void slv_ ## METHOD (slv_system_t sys, PARAMTYPE PARAMNAME){ \
306 if(!sys->internals || !sys->internals->PROP){ \
307 printwarning(#METHOD,sys); \
308 return; \
309 } \
310 (sys->internals->PROP)(sys,sys->ct, PARAMNAME); \
311 }
312
313 DEFINE_SLV_PROXY_METHOD_PARAM(get_parameters,get_parameters,slv_parameters_t*,parameters) /*;*/
314
315 void slv_set_parameters(slv_system_t sys,slv_parameters_t *parameters)
316 {
317 asc_assert(sys->internals);
318 if(sys->internals->setparam == NULL ) {
319 printwarning("slv_set_parameters",sys);
320 return;
321 }
322 if (parameters->whose != sys->solver) {
323 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,
324 "slv_set_parameters cannot pass parameters from one solver to a"
325 " another.");
326 return;
327 }
328 (sys->internals->setparam)(sys,sys->ct,parameters);
329 }
330
331 int slv_get_status(slv_system_t sys, slv_status_t *status){
332 if(!sys->internals)return -1;
333 if(sys->internals->getstatus==NULL){printinfo(sys,"get_status");return -1;}
334 return (sys->internals->getstatus)(sys,sys->ct,status);
335 }
336
337 DEFINE_SLV_PROXY_METHOD_PARAM(dump_internals,dumpinternals,int,level) /*;*/
338
339 DEFINE_SLV_PROXY_METHOD(get_linsolqr_sys, getlinsys, linsolqr_system_t, NULL) /*;*/
340
341 DEFINE_SLV_PROXY_METHOD(get_sys_mtx, get_sys_mtx, mtx_matrix_t, NULL) /*;*/
342 DEFINE_SLV_PROXY_METHOD(presolve,presolve,int,-1) /*;*/
343 DEFINE_SLV_PROXY_METHOD(resolve,resolve,int,-1) /*;*/
344 DEFINE_SLV_PROXY_METHOD(iterate,iterate,int,-1) /*;*/
345 DEFINE_SLV_PROXY_METHOD(solve,solve,int,-1) /*;*/
346
347 int slv_eligible_solver(slv_system_t sys)
348 {
349 asc_assert(sys->internals);
350 if(sys->internals->celigible == NULL ) {
351 printwarning("slv_eligible_solver",sys);
352 return 0;
353 }
354 return (sys->internals->celigible)(sys);
355 }
356
357 //-------------
358
359
360 int slv_select_solver(slv_system_t sys,int solver){
361
362 int status_index;
363 SlvClientDestroyF *destroy;
364 const SlvFunctionsT *S;
365
366 if(sys ==NULL) {
367 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_select_solver called with NULL system.");
368 return -1;
369 }
370
371 CONSOLE_DEBUG("CHECKING FOR SOLVER %d", solver);
372
373 if(solver_engine(solver)){
374 CONSOLE_DEBUG("SOLVER FOUND");
375 if(sys->ct != NULL && solver != sys->solver){
376 CONSOLE_DEBUG("DIFFERENT SOLVER");
377 //CONSOLE_DEBUG("g_SlvNumberOfRegisteredClients = %d, sys->solver = %d", g_SlvNumberOfRegisteredClients, sys->solver);
378 asc_assert(sys->solver >= -1);
379 //asc_assert(g_SlvNumberOfRegisteredClients > 0);
380 //asc_assert(sys->solver < g_SlvNumberOfRegisteredClients);
381 S = solver_engine(sys->solver);
382 destroy = S->cdestroy;
383 if(destroy!=NULL) {
384 (destroy)(sys,sys->ct);
385 sys->ct = NULL;
386 }else{
387 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_select_solver: 'cdestroy' is undefined on solver '%s' (index %d).",
388 S->name, sys->solver);
389 }
390 }
391
392 CONSOLE_DEBUG("PREVIOUS SOLVER IS CLEAR");
393
394 if(sys->ct != NULL) {
395 CONSOLE_DEBUG("CURRENT SOLVER UNCHANGED");
396 return sys->solver;
397 }
398
399 status_index = solver;
400 sys->solver = solver;
401 sys->internals = solver_engine(solver);
402 if(sys->internals->ccreate != NULL){
403 sys->ct = (sys->internals->ccreate)(sys,&status_index);
404 }else{
405 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"slv_select_solver create failed due to bad client '%s'.",
406 slv_solver_name(sys->solver));
407 return sys->solver;
408 }
409 if(sys->ct==NULL){
410 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"SlvClientCreate failed in slv_select_solver.");
411 sys->solver = -1;
412 }else{
413 if (status_index){
414 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"SlvClientCreate succeeded with warning %d %s.",
415 status_index," in slv_select_solver");
416 }
417 /* we could do a better job explaining the client warnings... */
418 sys->solver = solver;
419 }
420 }else{
421 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_select_solver: invalid solver index '%d'.",
422 solver);
423 return -1;
424 }
425 return sys->solver;
426 }
427
428 /**
429 @TODO looks buggy
430 */
431 int slv_switch_solver(slv_system_t sys,int solver)
432 {
433 int status_index;
434
435 if(sys ==NULL){
436 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_switch_solver called with NULL system.");
437 return -1;
438 }
439
440 CONSOLE_DEBUG("CHECKING FOR SOLVER %d", solver);
441
442 if(solver_engine(solver)){
443 status_index = solver;
444 sys->solver = solver;
445 sys->internals = solver_engine(solver);
446 CONSOLE_DEBUG("SWITCHING TO SOLVER '%s'",sys->internals->name);
447 if(sys->internals->ccreate != NULL) {
448 sys->ct = (sys->internals->ccreate)(sys,&status_index);
449 } else {
450 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_switch_solver create failed due to bad client '%s'.",
451 slv_solver_name(sys->solver));
452 return sys->solver;
453 }
454 if (sys->ct==NULL) {
455 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"SlvClientCreate failed in slv_switch_solver.");
456 sys->solver = -1;
457 }else{
458 if (status_index) {
459 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"SlvClientCreate succeeded with warning %d %s.",
460 status_index," in slv_switch_solver");
461 }
462 sys->solver = solver;
463 }
464 }else{
465 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Unknown client '%d'.",solver);
466 return -1;
467 }
468 return sys->solver;
469 }
470
471 /*--------------------------------*/
472
473 int slv_get_selected_solver(slv_system_t sys){
474 if (sys!=NULL) return sys->solver;
475 return -1;
476 }
477
478
479 int32 slv_get_default_parameters(int sindex,
480 slv_parameters_t *parameters)
481 {
482 SlvFunctionsT *S;
483 S = solver_engine(sindex);
484 if(S){
485 if(S->getdefparam == NULL ) {
486 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"slv_get_default_parameters called with parameterless index.");
487 return 0;
488 }else{
489 /* send NULL system when setting up interface */
490 (S->getdefparam)(NULL,NULL,parameters);
491 return 1;
492 }
493 }else{
494 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"slv_get_default_parameters called with unregistered index.");
495 return 0;
496 }
497 }
498
499
500 /*-----------------------------------------------------------*/
501
502 SlvClientToken slv_get_client_token(slv_system_t sys)
503 {
504 if (sys==NULL) {
505 FPRINTF(stderr,"slv_get_client_token called with NULL system.");
506 return NULL;
507 }
508 return sys->ct;
509 }
510
511
512 void slv_set_client_token(slv_system_t sys, SlvClientToken ct)
513 {
514 if (sys==NULL) {
515 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"slv_set_client_token called with NULL system.");
516 return;
517 }
518 sys->ct = ct;
519 }
520
521 void slv_set_solver_index(slv_system_t sys, int solver)
522 {
523 if (sys==NULL) {
524 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"slv_set_solver_index called with NULL system.");
525 return;
526 }
527 sys->solver = solver;
528 sys->internals = solver_engine(solver);
529 }
530
531
532 const char *slv_solver_name(int sindex){
533 static char errname[] = "ErrorSolver";
534 const SlvFunctionsT *S = solver_engine(sindex);
535 if(S!=NULL){
536 if(S->name == NULL) {
537 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"unnamed solver: index='%d'",sindex);
538 return errname;
539 }else{
540 return S->name;
541 }
542 }else{
543 ERROR_REPORTER_HERE(ASC_PROG_ERROR,"invalid solver index '%d'", sindex);
544 return errname;
545 }
546 }
547

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