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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1775 - (hide annotations) (download) (as text)
Wed May 21 09:51:59 2008 UTC (14 years, 6 months ago) by jpye
File MIME type: text/x-csrc
File size: 15405 byte(s)
Silenced som runtime debug output for datareader and solver.
Added first shot at canvas gui code using Gaphas.
1 jpye 1487 /* 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 jpye 1491 #include <compiler/packages.h>
38 jpye 1487
39 jpye 1775 #define SOLVER_DEBUG 0
40    
41 jpye 1487 /**
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)ASC_FREE(L);
53     init = 0;
54     return NULL;
55     }
56     if(!init){
57     L = gl_create(10);
58     init = 1;
59     }
60     return L;
61     }
62    
63     /**
64     Return gl_list of SlvFunctionsT. C++ will use this to produce a
65     nice little list of integrator names that can be used in Python :-/
66     */
67     const struct gl_list_t *solver_get_engines(){
68     return solver_get_list(0);
69     }
70    
71     const SlvFunctionsT *solver_engine(const int number){
72     const struct gl_list_t *L = solver_get_engines();
73     int i;
74     const SlvFunctionsT *S, *Sfound=NULL;
75 jpye 1576 /* CONSOLE_DEBUG("Searching for solver #%d in list of %d solvers",number,gl_length(L)); */
76 jpye 1487 for(i=1; i <= gl_length(L); ++i){
77     S = gl_fetch(L,i);
78 jpye 1576 /* CONSOLE_DEBUG("Looking at %s (%d)",S->name,S->number); */
79 jpye 1487 if(S->number==number){
80 jpye 1576 /* CONSOLE_DEBUG("Match!"); */
81 jpye 1487 Sfound = S;
82     break;
83     }
84     }
85 jpye 1576 /* CONSOLE_DEBUG("Returning %d",Sfound); */
86 jpye 1487 return Sfound;
87     }
88    
89     const SlvFunctionsT *solver_engine_named(const char *name){
90     const struct gl_list_t *L = solver_get_engines();
91     int i;
92     const SlvFunctionsT *S, *Sfound=NULL;
93     for(i=1; i <= gl_length(L); ++i){
94     S = gl_fetch(L,i);
95     if(strcmp(S->name,name)==0){
96     Sfound = S;
97     break;
98     }
99     }
100     return Sfound;
101     }
102    
103     int slv_lookup_client(const char *name){
104     #if 0
105     int i;
106    
107     if(name == NULL)return -1;
108    
109     for(i = 0; i < NORC; i++) {
110     if(strcmp( SCD(i).name, name)==0) {
111     return i;
112     }
113     }
114     return -1;
115     #endif
116     const struct gl_list_t *L = solver_get_engines();
117     int i;
118     const SlvFunctionsT *S, *Sfound=NULL;
119     for(i=1; i <= gl_length(L); ++i){
120     S = gl_fetch(L,i);
121     if(strcmp(S->name,name)==0){
122     Sfound = S;
123     break;
124     }
125     }
126     if(Sfound){
127     return S->number;
128     }else{
129     ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid engine name '%s'",name);
130 jpye 1576 return -1;
131 jpye 1487 }
132     }
133    
134    
135     /**
136     Register a new solver.
137    
138     @TODO This needs work still, particularly of the dynamic loading
139     sort. it would be good if here we farmed out the dynamic loading
140     to another file so we don't have to crap this one all up.
141    
142     old args:
143     (SlvRegistration registerfunc, CONST char *func
144     ,CONST char *file, int *new_client_id
145     */
146 jpye 1491 int solver_register(const SlvFunctionsT *solver){
147 jpye 1487 #if 0
148     int status;
149    
150     status = registerfunc(&( SlvClientsData[NORC]));
151     if (!status) { /* ok */
152     SlvClientsData[NORC].number = NORC;
153     *new_client_id = NORC;
154     NORC++;
155     } else {
156     *new_client_id = -2;
157     ERROR_REPORTER_NOLINE(ASC_PROG_ERR,"Client %d registration failure (%d)!",NORC,status);
158     }
159     return status;
160     #endif
161    
162     /* get the current list of registered engines */
163     const struct gl_list_t *L;
164     L = solver_get_engines();
165    
166     #if 0
167     CONSOLE_DEBUG("REGISTERING SOLVER");
168     CONSOLE_DEBUG("There were %lu registered solvers", gl_length(solver_get_list(0)));
169     #endif
170    
171     int i;
172     const SlvFunctionsT *S;
173     for(i=1; i < gl_length(L); ++i){
174     S = (const SlvFunctionsT *)gl_fetch(L,i);
175     if(strcmp(solver->name,S->name)==0){
176     ERROR_REPORTER_HERE(ASC_USER_WARNING,"Solver with name '%s' is already registered",solver->name);
177     return 0;
178     }
179     if(solver->number == S->number){
180     ERROR_REPORTER_HERE(ASC_USER_WARNING,"Solver with ID '%d' is already registered",solver->number);
181     return 0;
182     }
183     }
184    
185 jpye 1749 #if 0
186 jpye 1487 CONSOLE_DEBUG("Adding engine '%s'",solver->name);
187     #endif
188    
189     gl_append_ptr(L,(SlvFunctionsT *)solver);
190    
191 jpye 1749 #if 0
192 jpye 1487 CONSOLE_DEBUG("There are now %lu registered solvers", gl_length(solver_get_list(0)));
193     #endif
194     return 0;
195     }
196    
197    
198     /*------------------------------------------------------------------------------
199     SOLVER REGISTRATION
200     */
201    
202     /* rewrote this stuff to get rid of all the #ifdefs -- JP */
203    
204     struct StaticSolverRegistration{
205 jpye 1491 const char *importname;
206 jpye 1487 };
207    
208     /*
209     The names here are only used to provide information in the case where
210     solver registration fails. The definitive solver names are in the slv*.c
211     files.
212     */
213     static const struct StaticSolverRegistration slv_reg[]={
214 jpye 1504 {"qrslv"}
215     ,{"conopt"}
216     ,{"lrslv"}
217     ,{"cmslv"}
218 jpye 1646 /* ,{"ipopt"} */
219 jpye 1491 ,{NULL}
220     #if 0
221 jpye 1487 /* {0,"SLV",&slv0_register} */
222     /* ,{0,"MINOS",&slv1_register} */
223     /* ,{0,"CSLV",&slv4_register} */
224     /* ,{0,"LSSLV",&slv5_register} */
225     /* ,{0,"MPS",&slv6_register} */
226     /* ,{0,"NGSLV",&slv7_register} */
227     /* ,{0,"OPTSQP",&slv2_register} */
228 jpye 1491 #endif
229 jpye 1487 };
230    
231     int SlvRegisterStandardClients(void){
232     int nclients = 0;
233     //int newclient=0;
234     int error;
235     int i;
236    
237     /* CONSOLE_DEBUG("REGISTERING STANDARD SOLVER ENGINES"); */
238 jpye 1491 for(i=0; slv_reg[i].importname!=NULL;++i){
239 jpye 1519 error = package_load(slv_reg[i].importname,NULL);
240 jpye 1491 if(error){
241     ERROR_REPORTER_HERE(ASC_PROG_ERR
242     ,"Unable to register solver '%s' (error %d)."
243     ,slv_reg[i].importname,error
244     );
245 jpye 1487 }else{
246 jpye 1749 /* CONSOLE_DEBUG("Solver '%s' registered OK",slv_reg[i].importname); */
247 jpye 1491 nclients++;
248 jpye 1487 }
249     }
250     return nclients;
251     }
252    
253     /*------------------------------------------------------*/
254    
255     static void printwarning(const char * fname, slv_system_t sys)
256     {
257     ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,
258     "%s called with bad registered client (%s).",fname,
259     slv_solver_name(slv_get_selected_solver(sys)));
260     }
261    
262     static void printinfo(slv_system_t sys, const char *rname){
263     asc_assert(sys->internals);
264     if(sys->internals->name) {
265     ERROR_REPORTER_NOLINE(ASC_PROG_NOTE,
266     "Client %s does not support function '%s'.",
267     sys->internals->name,rname);
268     }
269     }
270    
271     /*-----------------------------------------------------------
272     These macros do some more elimination of repetition. Here we're
273     trying to replace some more complex 'method-like' calls on
274     slv_system_t:
275    
276     These macros use macro-argument-concatenation and macro stringification.
277     Verified that the former works with Visual C++.
278     http://www.codeproject.com/macro/metamacros.asp
279     */
280    
281     /** Define a method like 'void slv_METHODNAME(sys)' */
282     #define DEFINE_SLV_PROXY_METHOD_VOID(METHOD) \
283     void slv_ ## METHOD (slv_system_t sys){ \
284     if(CF(sys,METHOD)==NULL){ \
285     printwarning(#METHOD,sys); \
286     return; \
287     } \
288     SF(sys,METHOD)(sys,sys->ct); \
289     }
290    
291     /** Define a method like 'RETURNTYPE slv_METHOD(sys)'; */
292     #define DEFINE_SLV_PROXY_METHOD(METHOD,PROP,RETTYPE,ERRVAL) \
293     RETTYPE slv_ ## METHOD (slv_system_t sys){ \
294     /* CONSOLE_DEBUG("slv_" #METHOD);*/ \
295     asc_assert(sys->internals); \
296     /*CONSOLE_DEBUG("internals OK");*/ \
297     if(sys->internals->PROP==NULL){ \
298     /*CONSOLE_DEBUG("method is NULL");*/ \
299     printinfo(sys, #METHOD); \
300     return ERRVAL; \
301     } \
302     /*CONSOLE_DEBUG("running method " #PROP " in solver %d",sys->internals->number);*/ \
303     return (sys->internals->PROP)(sys,sys->ct); \
304     }
305    
306     /** Define a method like 'void slv_METHOD(sys,TYPE PARAMNAME)'; */
307     #define DEFINE_SLV_PROXY_METHOD_PARAM(METHOD,PROP,PARAMTYPE,PARAMNAME) \
308     void slv_ ## METHOD (slv_system_t sys, PARAMTYPE PARAMNAME){ \
309     if(!sys->internals || !sys->internals->PROP){ \
310     printwarning(#METHOD,sys); \
311     return; \
312     } \
313     (sys->internals->PROP)(sys,sys->ct, PARAMNAME); \
314     }
315    
316     DEFINE_SLV_PROXY_METHOD_PARAM(get_parameters,get_parameters,slv_parameters_t*,parameters) /*;*/
317    
318     void slv_set_parameters(slv_system_t sys,slv_parameters_t *parameters)
319     {
320     asc_assert(sys->internals);
321     if(sys->internals->setparam == NULL ) {
322     printwarning("slv_set_parameters",sys);
323     return;
324     }
325     if (parameters->whose != sys->solver) {
326     ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,
327     "slv_set_parameters cannot pass parameters from one solver to a"
328     " another.");
329     return;
330     }
331     (sys->internals->setparam)(sys,sys->ct,parameters);
332     }
333    
334     int slv_get_status(slv_system_t sys, slv_status_t *status){
335 jpye 1549 static const SlvFunctionsT *lastsolver = 0;
336 jpye 1490 if(!sys->internals)return -1;
337 jpye 1549 if(sys->internals->getstatus==NULL){
338     if(lastsolver==0 || lastsolver != sys->internals){
339     printinfo(sys,"get_status");
340     lastsolver = sys->internals;
341     }
342     return -1;
343     }
344 jpye 1487 return (sys->internals->getstatus)(sys,sys->ct,status);
345     }
346    
347     DEFINE_SLV_PROXY_METHOD_PARAM(dump_internals,dumpinternals,int,level) /*;*/
348    
349     DEFINE_SLV_PROXY_METHOD(get_linsolqr_sys, getlinsys, linsolqr_system_t, NULL) /*;*/
350    
351     DEFINE_SLV_PROXY_METHOD(get_sys_mtx, get_sys_mtx, mtx_matrix_t, NULL) /*;*/
352     DEFINE_SLV_PROXY_METHOD(presolve,presolve,int,-1) /*;*/
353     DEFINE_SLV_PROXY_METHOD(resolve,resolve,int,-1) /*;*/
354     DEFINE_SLV_PROXY_METHOD(iterate,iterate,int,-1) /*;*/
355     DEFINE_SLV_PROXY_METHOD(solve,solve,int,-1) /*;*/
356    
357     int slv_eligible_solver(slv_system_t sys)
358     {
359     asc_assert(sys->internals);
360     if(sys->internals->celigible == NULL ) {
361     printwarning("slv_eligible_solver",sys);
362     return 0;
363     }
364     return (sys->internals->celigible)(sys);
365     }
366    
367     //-------------
368    
369    
370     int slv_select_solver(slv_system_t sys,int solver){
371    
372     int status_index;
373     SlvClientDestroyF *destroy;
374     const SlvFunctionsT *S;
375    
376     if(sys ==NULL) {
377     ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_select_solver called with NULL system.");
378     return -1;
379     }
380    
381 jpye 1576 /* CONSOLE_DEBUG("CHECKING FOR SOLVER %d", solver); */
382 jpye 1487
383     if(solver_engine(solver)){
384 jpye 1576 /* CONSOLE_DEBUG("SOLVER FOUND"); */
385 jpye 1487 if(sys->ct != NULL && solver != sys->solver){
386 jpye 1576 /* CONSOLE_DEBUG("DIFFERENT SOLVER"); */
387 jpye 1487 //CONSOLE_DEBUG("g_SlvNumberOfRegisteredClients = %d, sys->solver = %d", g_SlvNumberOfRegisteredClients, sys->solver);
388     asc_assert(sys->solver >= -1);
389     //asc_assert(g_SlvNumberOfRegisteredClients > 0);
390     //asc_assert(sys->solver < g_SlvNumberOfRegisteredClients);
391     S = solver_engine(sys->solver);
392     destroy = S->cdestroy;
393     if(destroy!=NULL) {
394     (destroy)(sys,sys->ct);
395     sys->ct = NULL;
396     }else{
397     ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"slv_select_solver: 'cdestroy' is undefined on solver '%s' (index %d).",
398     S->name, sys->solver);
399     }
400     }
401    
402 jpye 1576 /* CONSOLE_DEBUG("PREVIOUS SOLVER IS CLEAR"); */
403 jpye 1487
404     if(sys->ct != NULL) {
405 jpye 1775 #if SOLVER_DEBUG
406 jpye 1487 CONSOLE_DEBUG("CURRENT SOLVER UNCHANGED");
407 jpye 1775 #endif
408 jpye 1487 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 jpye 1576 /* CONSOLE_DEBUG("CHECKING FOR SOLVER %d", solver); */
453 jpye 1487
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