/[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 1528 - (show annotations) (download) (as text)
Wed Jul 4 14:33:13 2007 UTC (15 years, 7 months ago) by jpye
File MIME type: text/x-csrc
File size: 15071 byte(s)
placeholder for IPOPT solver.
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 if(!sys->internals)return -1;
334 if(sys->internals->getstatus==NULL){printinfo(sys,"get_status");return -1;}
335 return (sys->internals->getstatus)(sys,sys->ct,status);
336 }
337
338 DEFINE_SLV_PROXY_METHOD_PARAM(dump_internals,dumpinternals,int,level) /*;*/
339
340 DEFINE_SLV_PROXY_METHOD(get_linsolqr_sys, getlinsys, linsolqr_system_t, NULL) /*;*/
341
342 DEFINE_SLV_PROXY_METHOD(get_sys_mtx, get_sys_mtx, mtx_matrix_t, NULL) /*;*/
343 DEFINE_SLV_PROXY_METHOD(presolve,presolve,int,-1) /*;*/
344 DEFINE_SLV_PROXY_METHOD(resolve,resolve,int,-1) /*;*/
345 DEFINE_SLV_PROXY_METHOD(iterate,iterate,int,-1) /*;*/
346 DEFINE_SLV_PROXY_METHOD(solve,solve,int,-1) /*;*/
347
348 int slv_eligible_solver(slv_system_t sys)
349 {
350 asc_assert(sys->internals);
351 if(sys->internals->celigible == NULL ) {
352 printwarning("slv_eligible_solver",sys);
353 return 0;
354 }
355 return (sys->internals->celigible)(sys);
356 }
357
358 //-------------
359
360
361 int slv_select_solver(slv_system_t sys,int solver){
362
363 int status_index;
364 SlvClientDestroyF *destroy;
365 const SlvFunctionsT *S;
366
367 if(sys ==NULL) {
368 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_select_solver called with NULL system.");
369 return -1;
370 }
371
372 CONSOLE_DEBUG("CHECKING FOR SOLVER %d", solver);
373
374 if(solver_engine(solver)){
375 CONSOLE_DEBUG("SOLVER FOUND");
376 if(sys->ct != NULL && solver != sys->solver){
377 CONSOLE_DEBUG("DIFFERENT SOLVER");
378 //CONSOLE_DEBUG("g_SlvNumberOfRegisteredClients = %d, sys->solver = %d", g_SlvNumberOfRegisteredClients, sys->solver);
379 asc_assert(sys->solver >= -1);
380 //asc_assert(g_SlvNumberOfRegisteredClients > 0);
381 //asc_assert(sys->solver < g_SlvNumberOfRegisteredClients);
382 S = solver_engine(sys->solver);
383 destroy = S->cdestroy;
384 if(destroy!=NULL) {
385 (destroy)(sys,sys->ct);
386 sys->ct = NULL;
387 }else{
388 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_select_solver: 'cdestroy' is undefined on solver '%s' (index %d).",
389 S->name, sys->solver);
390 }
391 }
392
393 CONSOLE_DEBUG("PREVIOUS SOLVER IS CLEAR");
394
395 if(sys->ct != NULL) {
396 CONSOLE_DEBUG("CURRENT SOLVER UNCHANGED");
397 return sys->solver;
398 }
399
400 status_index = solver;
401 sys->solver = solver;
402 sys->internals = solver_engine(solver);
403 if(sys->internals->ccreate != NULL){
404 sys->ct = (sys->internals->ccreate)(sys,&status_index);
405 }else{
406 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"slv_select_solver create failed due to bad client '%s'.",
407 slv_solver_name(sys->solver));
408 return sys->solver;
409 }
410 if(sys->ct==NULL){
411 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"SlvClientCreate failed in slv_select_solver.");
412 sys->solver = -1;
413 }else{
414 if (status_index){
415 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"SlvClientCreate succeeded with warning %d %s.",
416 status_index," in slv_select_solver");
417 }
418 /* we could do a better job explaining the client warnings... */
419 sys->solver = solver;
420 }
421 }else{
422 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_select_solver: invalid solver index '%d'.",
423 solver);
424 return -1;
425 }
426 return sys->solver;
427 }
428
429 /**
430 @TODO looks buggy
431 */
432 int slv_switch_solver(slv_system_t sys,int solver)
433 {
434 int status_index;
435
436 if(sys ==NULL){
437 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_switch_solver called with NULL system.");
438 return -1;
439 }
440
441 CONSOLE_DEBUG("CHECKING FOR SOLVER %d", solver);
442
443 if(solver_engine(solver)){
444 status_index = solver;
445 sys->solver = solver;
446 sys->internals = solver_engine(solver);
447 CONSOLE_DEBUG("SWITCHING TO SOLVER '%s'",sys->internals->name);
448 if(sys->internals->ccreate != NULL) {
449 sys->ct = (sys->internals->ccreate)(sys,&status_index);
450 } else {
451 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_switch_solver create failed due to bad client '%s'.",
452 slv_solver_name(sys->solver));
453 return sys->solver;
454 }
455 if (sys->ct==NULL) {
456 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"SlvClientCreate failed in slv_switch_solver.");
457 sys->solver = -1;
458 }else{
459 if (status_index) {
460 ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"SlvClientCreate succeeded with warning %d %s.",
461 status_index," in slv_switch_solver");
462 }
463 sys->solver = solver;
464 }
465 }else{
466 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Unknown client '%d'.",solver);
467 return -1;
468 }
469 return sys->solver;
470 }
471
472 /*--------------------------------*/
473
474 int slv_get_selected_solver(slv_system_t sys){
475 if (sys!=NULL) return sys->solver;
476 return -1;
477 }
478
479
480 int32 slv_get_default_parameters(int sindex,
481 slv_parameters_t *parameters)
482 {
483 SlvFunctionsT *S;
484 S = solver_engine(sindex);
485 if(S){
486 if(S->getdefparam == NULL ) {
487 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"slv_get_default_parameters called with parameterless index.");
488 return 0;
489 }else{
490 /* send NULL system when setting up interface */
491 (S->getdefparam)(NULL,NULL,parameters);
492 return 1;
493 }
494 }else{
495 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"slv_get_default_parameters called with unregistered index.");
496 return 0;
497 }
498 }
499
500
501 /*-----------------------------------------------------------*/
502
503 SlvClientToken slv_get_client_token(slv_system_t sys)
504 {
505 if (sys==NULL) {
506 FPRINTF(stderr,"slv_get_client_token called with NULL system.");
507 return NULL;
508 }
509 return sys->ct;
510 }
511
512
513 void slv_set_client_token(slv_system_t sys, SlvClientToken ct)
514 {
515 if (sys==NULL) {
516 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"slv_set_client_token called with NULL system.");
517 return;
518 }
519 sys->ct = ct;
520 }
521
522 void slv_set_solver_index(slv_system_t sys, int solver)
523 {
524 if (sys==NULL) {
525 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"slv_set_solver_index called with NULL system.");
526 return;
527 }
528 sys->solver = solver;
529 sys->internals = solver_engine(solver);
530 }
531
532
533 const char *slv_solver_name(int sindex){
534 static char errname[] = "ErrorSolver";
535 const SlvFunctionsT *S = solver_engine(sindex);
536 if(S!=NULL){
537 if(S->name == NULL) {
538 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"unnamed solver: index='%d'",sindex);
539 return errname;
540 }else{
541 return S->name;
542 }
543 }else{
544 ERROR_REPORTER_HERE(ASC_PROG_ERROR,"invalid solver index '%d'", sindex);
545 return errname;
546 }
547 }
548

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