/[ascend]/trunk/base/generic/packages/sensitivity.c
ViewVC logotype

Contents of /trunk/base/generic/packages/sensitivity.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 975 - (show annotations) (download) (as text)
Tue Dec 19 11:49:59 2006 UTC (18 years, 10 months ago) by johnpye
File MIME type: text/x-csrc
File size: 37311 byte(s)
Fixed bug with Compute_J (using NumberRels instead of slv_get_number_solvers_rels).
Added GPL header to sensitivity.c.
Renamed IDA test cases.
1 /* ASCEND modelling environment
2 Copyright (C) 1990-2006 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 *//** @file
19 Sensititvity analysis code
20
21 @see documentation in sensitivity.h.
22 *//*
23 by Kirk Abbott
24 */
25
26 #include "sensitivity.h"
27
28 #include <string.h>
29 #include <math.h>
30 #include <utilities/error.h>
31 #include <utilities/ascMalloc.h>
32 #include <utilities/ascPanic.h>
33 #include <compiler/packages.h>
34 #include <compiler/fractions.h>
35 #include <compiler/dimen.h>
36 #include <compiler/atomvalue.h>
37 #include <compiler/instquery.h>
38 #include <general/mathmacros.h>
39
40 #define DEBUG 1
41
42 #if 0
43 static real64 *zero_vector(real64 *vec, int size)
44 {
45 int c;
46 for (c=0;c<size;c++) {
47 vec[c] = 0.0;
48 }
49 return vec;
50 }
51 #endif
52
53 /*----------------------------------------------------
54 UTILITY ROUTINES
55 */
56
57 /**
58 Allocate memory for a matrix
59 @param nrows Number of rows
60 @param ncols Number of colums
61 @return Pointer to the allocated matrix memory location
62 */
63 static real64 **make_matrix(int nrows, int ncols)
64 {
65 real64 **result;
66 int i;
67 result = (real64 **)calloc(nrows,sizeof(real64*));
68 for (i=0;i<nrows;i++) {
69 result[i] = (real64 *)calloc(ncols,sizeof(real64));
70 }
71 return result;
72 }
73
74 /**
75 Free a matrix from memory
76 @param matrix Memory location for the matrix
77 @param nrows Number of rows in the matrix
78 */
79 static void free_matrix(real64 **matrix, int nrows)
80 {
81 int i;
82 if (!matrix)
83 return;
84 for (i=0;i<nrows;i++) {
85 if (matrix[i]) {
86 free(matrix[i]);
87 matrix[i] = NULL;
88 }
89 }
90 free(matrix);
91 }
92
93 /**
94 Fetch an element from a branch of an arglist...?
95 */
96 static struct Instance *FetchElement(struct gl_list_t *arglist,
97 unsigned long whichbranch,
98 unsigned long whichelement)
99 {
100 struct gl_list_t *branch;
101 struct Instance *element;
102
103 if (!arglist) return NULL;
104 branch = (struct gl_list_t *)gl_fetch(arglist,whichbranch);
105 element = (struct Instance *)gl_fetch(branch,whichelement);
106 return element;
107 }
108
109 /**
110 Build then presolve an instance
111 */
112 static slv_system_t PreSolve(struct Instance *inst)
113 {
114 slv_system_t sys;
115 slv_parameters_t parameters;
116 struct var_variable **vp;
117 struct rel_relation **rp;
118 int ind,len;
119 char *tmp=NULL;
120
121 sys = system_build(inst);
122 if (sys==NULL) {
123 ERROR_REPORTER_HERE(ASC_PROG_ERR,
124 "Something radically wrong in creating solver.");
125 return NULL;
126 }
127 if (g_SlvNumberOfRegisteredClients == 0) {
128 return NULL;
129 }
130 ind = 0;
131 while (strcmp(slv_solver_name(ind),"QRSlv")) {
132 if (ind >= g_SlvNumberOfRegisteredClients) {
133 ERROR_REPORTER_HERE(ASC_PROG_ERR,
134 "QRSlv must be registered client.");
135 return NULL;
136 }
137 ++ind;
138 }
139 slv_select_solver(sys,ind);
140
141 slv_get_parameters(sys,&parameters);
142 parameters.partition = 0;
143 slv_set_parameters(sys,&parameters);
144 slv_presolve(sys);
145
146 #if DEBUG
147 vp = slv_get_solvers_var_list(sys);
148 len = slv_get_num_solvers_vars(sys);
149 for (ind=0 ; ind<len; ind++) {
150 tmp = var_make_name(sys,vp[ind]);
151 FPRINTF(stderr,"%s %d\n",tmp,var_sindex(vp[ind]));
152 if (tmp!=NULL) ascfree(tmp);
153 }
154 rp = slv_get_solvers_rel_list(sys);
155 len = slv_get_num_solvers_rels(sys);
156 for (ind=0 ; ind<len ; ind++) {
157 tmp = rel_make_name(sys,rp[ind]);
158 FPRINTF(stderr,"%s %d\n",tmp,rel_sindex(rp[ind]));
159 if (tmp) ascfree(tmp);
160 }
161 #endif
162 return sys;
163 }
164
165 #if 0
166 static int ReSolve(slv_system_t sys)
167 {
168 if (!sys)
169 return 1;
170 slv_solve(sys);
171 return 0;
172 }
173 #endif
174
175 /**
176 Build then presolve the solve an instance...
177 */
178 static int DoSolve(struct Instance *inst)
179 {
180 slv_system_t sys;
181
182 sys = system_build(inst);
183 if (!sys) {
184 ERROR_REPORTER_HERE(ASC_PROG_ERR,
185 "Something radically wrong in creating solver.");
186 return 1;
187 }
188 (void)slv_select_solver(sys,0);
189 slv_presolve(sys);
190 slv_solve(sys);
191 system_destroy(sys);
192 return 0;
193 }
194
195 /**
196 Calls 'DoSolve'
197
198 @see DoSolve
199 */
200 int do_solve_eval( struct Instance *i,
201 struct gl_list_t *arglist)
202 {
203 unsigned long len;
204 int result;
205 struct Instance *inst;
206 len = gl_length(arglist);
207
208 /* Ignore unused params */
209 (void)i;
210
211 if (len!=2) {
212 ERROR_REPORTER_HERE(ASC_USER_ERROR,
213 "Wrong number of args to do_solve_eval.");
214 return 1;
215 }
216 inst = FetchElement(arglist,1,1);
217 if (!inst)
218 return 1;
219 result = DoSolve(inst);
220 return result;
221 }
222
223 /**
224 Finite-difference Check Arguments...?
225
226 @param arglist list of arguments
227
228 Argument list contains
229 . arg1 - model inst to be solved
230 . arg2 - array of input instances
231 . arg3 - array of output instances
232 . arg4 - matrix of partials to be written to
233 */
234 static int FiniteDiffCheckArgs(struct gl_list_t *arglist)
235 {
236 struct Instance *inst;
237 unsigned long len;
238 unsigned long ninputs, noutputs;
239
240 len = gl_length(arglist);
241 if (len != 4) {
242 FPRINTF(stderr,"wrong number of args -- 4 expected\n");
243 return 1;
244 }
245 inst = FetchElement(arglist,1,1);
246 if (InstanceKind(inst)!=MODEL_INST) {
247 FPRINTF(stderr,"Arg #1 is not a model instance\n");
248 return 1;
249 }
250 ninputs = gl_length((struct gl_list_t *)gl_fetch(arglist,2));
251 /* input args */
252 noutputs = gl_length((struct gl_list_t *)gl_fetch(arglist,3));
253 /* output args */
254 len = gl_length((struct gl_list_t *)gl_fetch(arglist,4));
255 /* partials matrix */
256 if (len != (ninputs*noutputs)) {
257 FPRINTF(stderr,
258 "The array of partials is inconsistent with the args given\n");
259 return 1;
260 }
261 return 0;
262 }
263
264 /**
265 Finite difference...
266 */
267 static int finite_difference(struct gl_list_t *arglist)
268 {
269 struct Instance *model_inst, *xinst, *inst;
270 slv_system_t sys;
271 int ninputs,noutputs;
272 int i,j,offset;
273 real64 **partials;
274 real64 *y_old, *y_new;
275 real64 x;
276 real64 interval = 1e-6;
277 int result=0;
278
279 model_inst = FetchElement(arglist,1,1);
280 sys = system_build(model_inst);
281 if (!sys) {
282 FPRINTF(stdout,"Something radically wrong in creating solver\n");
283 return 1;
284 }
285 (void)slv_select_solver(sys,0);
286 slv_presolve(sys);
287 slv_solve(sys);
288
289 /* Make the necessary vectors */
290
291 ninputs = (int)gl_length((struct gl_list_t *)gl_fetch(arglist,2));
292 noutputs = (int)gl_length((struct gl_list_t *)gl_fetch(arglist,3));
293 y_old = (real64 *)calloc(noutputs,sizeof(real64));
294 y_new = (real64 *)calloc(noutputs,sizeof(real64));
295 partials = make_matrix(noutputs,ninputs);
296 for (i=0;i<noutputs;i++) { /* get old yvalues */
297 inst = FetchElement(arglist,3,i+1);
298 y_old[i] = RealAtomValue(inst);
299 }
300 for (j=0;j<ninputs;j++) {
301 xinst = FetchElement(arglist,2,j+1);
302 x = RealAtomValue(xinst);
303 SetRealAtomValue(xinst,x+interval,(unsigned)0); /* perturb system */
304 slv_presolve(sys);
305 slv_solve(sys);
306
307 for (i=0;i<noutputs;i++) { /* get new yvalues */
308 inst = FetchElement(arglist,3,i+1);
309 y_new[i] = RealAtomValue(inst);
310 partials[i][j] = -1.0 * (y_old[i] - y_new[i])/interval;
311 PRINTF("y_old = %20.12g y_new = %20.12g\n", y_old[i],y_new[i]);
312 }
313 SetRealAtomValue(xinst,x,(unsigned)0); /* unperturb system */
314 }
315 offset = 0;
316 for (i=0;i<noutputs;i++) {
317 for (j=0;j<ninputs;j++) {
318 inst = FetchElement(arglist,4,offset+j+1);
319 SetRealAtomValue(inst,partials[i][j],(unsigned)0);
320 PRINTF("%12.6f %s",partials[i][j], (j==(ninputs-1)) ? "\n" : " ");
321 }
322 offset += ninputs;
323 }
324 /* error: */
325 free(y_old);
326 free(y_new);
327 free_matrix(partials,noutputs);
328 system_destroy(sys);
329 return result;
330 }
331
332 /**
333 Finite different evaluate...
334 */
335 int do_finite_diff_eval( struct Instance *i,
336 struct gl_list_t *arglist)
337 {
338 int result;
339
340 /* Ignore unused params */
341 (void)i;
342
343 if (FiniteDiffCheckArgs(arglist))
344 return 1;
345 result = finite_difference(arglist);
346 return result;
347 }
348
349 /*-------------------------------------------------
350 SENSITIVITY ANALYSIS CODE
351 */
352
353 /**
354 Sensitivity Analysis
355
356 @param arglist List of arguments
357
358 Argument list contains the following:
359 . arg1 - model inst for which the sensitivity analysis is to be done.
360 . arg2 - array of input instances.
361 . arg3 - array of output instances.
362 . arg4 matrix of partials to be written to.
363 */
364 int SensitivityCheckArgs(struct gl_list_t *arglist)
365 {
366 struct Instance *inst;
367 unsigned long len;
368 unsigned long ninputs, noutputs;
369
370 len = gl_length(arglist);
371 if (len != 4) {
372 FPRINTF(stderr,"wrong number of args -- 4 expected\n");
373 return 1;
374 }
375 inst = FetchElement(arglist,1,1);
376 if (InstanceKind(inst)!=MODEL_INST) {
377 FPRINTF(stderr,"Arg #1 is not a model instance\n");
378 return 1;
379 }
380 ninputs = gl_length((struct gl_list_t *)gl_fetch(arglist,2));
381 /* input args */
382 noutputs = gl_length((struct gl_list_t *)gl_fetch(arglist,3));
383 /* output args */
384 len = gl_length((struct gl_list_t *)gl_fetch(arglist,4));
385 /* partials matrix */
386 if (len != (ninputs*noutputs)) {
387 FPRINTF(stderr,
388 "The array of partials is inconsistent with the args given\n");
389 return 1;
390 }
391 return 0;
392 }
393
394 /**
395 @param arglist List of arguments
396 @param step_length ...?
397
398 The list of arguments should chontain
399
400 . arg1 - model inst for which the sensitivity analysis is to be done.
401 . arg2 - array of input instances.
402 . arg3 - new_input instances, for variable projection.
403 . arg4 - instance representing the step_length for projection.
404
405 The result will be written to standard out.
406 */
407 int SensitivityAllCheckArgs(struct gl_list_t *arglist, double *step_length)
408 {
409 struct Instance *inst;
410 unsigned long len;
411
412 /*
413
414 */
415
416 len = gl_length(arglist);
417 if (len != 4) {
418 FPRINTF(stderr,"wrong number of args -- 4 expected\n");
419 return 1;
420 }
421 inst = FetchElement(arglist,1,1);
422 if (InstanceKind(inst)!=MODEL_INST) {
423 FPRINTF(stderr,"Arg #1 is not a model instance\n");
424 return 1;
425 }
426 /*
427 * we should be checking that arg2 list contains solver vars
428 * and that they are fixed. The same applies to arglist 3... later.
429 * We will check and return the steplength though. 0 means dont do
430 * the variable projection.
431 */
432 inst = FetchElement(arglist,4,1);
433 *step_length = RealAtomValue(inst);
434 if (fabs(*step_length) < 1e-08)
435 *step_length = 0.0;
436 return 0;
437 }
438
439 /**
440 Looks like it returns the number of relations in a solver's system
441
442 @param sys The system in question
443 @return int Number of relations in the system
444 @see slv_count_solvers_rels
445 */
446 int NumberRels(slv_system_t sys)
447 {
448 static int nrels = -1;
449 rel_filter_t rfilter;
450 int tmp;
451
452 if (!sys) { /* a NULL system may be used to reinitialise the count */
453 nrels = -1;
454 return -1;
455 }
456 if (nrels < 0) {
457 /*get used (included) relation count -- equalities only !*/
458 rfilter.matchbits = (REL_INCLUDED | REL_EQUALITY | REL_ACTIVE);
459 rfilter.matchvalue = (REL_INCLUDED | REL_EQUALITY | REL_ACTIVE);
460 tmp = slv_count_solvers_rels(sys,&rfilter);
461 nrels = tmp;
462 return tmp;
463 }
464 else return nrels;
465 }
466
467 int NumberIncludedRels(slv_system_t sys){
468 static int nrels = -1;
469 rel_filter_t rfilter;
470 int tmp;
471
472 if (!sys) { /* a NULL system may be used to reinitialise the count */
473 nrels = -1;
474 return -1;
475 }
476 if (nrels < 0) {
477 /*get used (included) relation count -- equalities only !*/
478 rfilter.matchbits = (REL_INCLUDED | REL_EQUALITY | REL_ACTIVE);
479 rfilter.matchvalue = (REL_INCLUDED | REL_EQUALITY | REL_ACTIVE);
480 tmp = slv_count_solvers_rels(sys,&rfilter);
481 nrels = tmp;
482 return tmp;
483 } else {
484 return nrels;
485 }
486 }
487
488 /**
489 Looks like it returns the number of free variables in a solver's system
490
491 @param sys The system in question
492 @return the number of free variables
493
494 @see slv_count_solvers_vars
495 */
496 int NumberFreeVars(slv_system_t sys){
497
498 static int nvars = -1;
499 var_filter_t vfilter;
500 int tmp;
501
502 if (!sys) {
503 /* no system: return error */
504 nvars = -1;
505 return -1;
506 }
507
508 if (nvars >=0){
509 /* return stored value */
510 return nvars;
511 }
512
513 /* get used (free and incident) variable count */
514 vfilter.matchbits = (VAR_FIXED | VAR_INCIDENT | VAR_SVAR | VAR_ACTIVE);
515 vfilter.matchvalue = (VAR_INCIDENT | VAR_SVAR | VAR_ACTIVE);
516 tmp = slv_count_solvers_vars(sys,&vfilter);
517 nvars = tmp;
518 return tmp;
519 }
520
521 /*
522 * The following bit of code does the computation of the matrix
523 * dz/dx. It accepts a slv_system_t and a list of 'input' variables.
524 * The matrix df/dx now exists and sits to the right of the main
525 * square region of the Jacobian. We will do the following in turn
526 * for each variable in this list:
527 *
528 * 1) Get the variable index, and use it to extract the required column
529 * from the main gradient matrix, this will be stored in a temporary
530 * vector.
531 * 2) We will then clear the column in the original matrix, as we want to
532 * store the caluclated results back in place.
533 * 3) Add the vector extracted in 1) as rhs to the main matrix.
534 * 4) Call linsol solve on this rhs to yield a solution which represents
535 * a column of dx/dx.
536 * 6) Store this vector back in the location cleared out in 2).
537 * 7) Goto 1.
538 *
539 * At the end of this procedure we would have calculated all the columns of
540 * dz/dx, and left them back in the main matrix.
541 */
542
543
544 /*
545 * At this point we should have an empty jacobian. We now
546 * need to call relman_diff over the *entire* matrix.
547 * fixed and free.
548 *
549 * Calculates the entire jacobian.
550 * It is initially unscaled.
551 */
552 int Compute_J(slv_system_t sys)
553 {
554 int32 row;
555 var_filter_t vfilter;
556 linsolqr_system_t lqr_sys;
557 mtx_matrix_t mtx;
558 struct rel_relation **rlist;
559 int nrows;
560 real64 resid;
561
562 /*
563 * Get the linear system from the solve system.
564 * Get the matrix from the linear system.
565 * Get the relations list from the solve system.
566 */
567 lqr_sys = slv_get_linsolqr_sys(sys);
568 mtx = linsolqr_get_matrix(lqr_sys);
569 rlist = slv_get_solvers_rel_list(sys);
570 nrows = slv_get_num_solvers_rels(sys);
571
572 calc_ok = TRUE;
573 vfilter.matchbits = (VAR_SVAR | VAR_ACTIVE) ;
574 vfilter.matchvalue = vfilter.matchbits ;
575
576 /*
577 * Clear the entire matrix and then compute
578 * the gradients at the current point.
579 */
580 mtx_clear_region(mtx,mtx_ENTIRE_MATRIX);
581 for(row=0; row<nrows; row++) {
582 struct rel_relation *rel;
583 rel = rlist[mtx_row_to_org(mtx,row)];
584 asc_assert(rel!=NULL);
585 (void)relman_diffs(rel,&vfilter,mtx,&resid,1);
586 }
587 linsolqr_matrix_was_changed(lqr_sys);
588
589 return(!calc_ok);
590 }
591
592
593
594 /**
595 * At this point we should have an empty jacobian. We now
596 * need to call relman_diff over the *entire* matrix.
597 * Calculates the entire jacobian. It is initially unscaled.
598 *
599 * Note: this assumes the sys given is using one of the ascend solvers
600 * with either linsol or linsolqr. Both are now allowed. baa 7/95
601 */
602 #define SAFE_FIX_ME 0
603 static int Compute_J_OLD(slv_system_t sys)
604 {
605 int32 row;
606 var_filter_t vfilter;
607 linsol_system_t lin_sys = NULL;
608 linsolqr_system_t lqr_sys = NULL;
609 mtx_matrix_t mtx;
610 struct rel_relation **rlist;
611 int nrows;
612 int qr=0;
613 #if DOTIME
614 double time1;
615 #endif
616
617 #if DOTIME
618 time1 = tm_cpu_time();
619 #endif
620 /*
621 * Get the linear system from the solve system.
622 * Get the matrix from the linear system.
623 * Get the relations list from the solve system.
624 */
625 lin_sys = slv_get_linsol_sys(sys);
626 if (lin_sys==NULL) {
627 qr=1;
628 lqr_sys=slv_get_linsolqr_sys(sys);
629 }
630 mtx = slv_get_sys_mtx(sys);
631 if (mtx==NULL || (lin_sys==NULL && lqr_sys==NULL)) {
632 FPRINTF(stderr,"Compute_dy_dx: error, found NULL.\n");
633 return 1;
634 }
635 rlist = slv_get_solvers_rel_list(sys);
636 nrows = NumberIncludedRels(sys);
637
638 calc_ok = TRUE;
639 vfilter.matchbits = VAR_SVAR;
640 vfilter.matchvalue = VAR_SVAR;
641 /*
642 * Clear the entire matrix and then compute
643 * the gradients at the current point.
644 */
645 mtx_clear_region(mtx,mtx_ENTIRE_MATRIX);
646 for(row=0; row<nrows; row++) {
647 struct rel_relation *rel;
648 /* added */
649 double resid;
650
651 rel = rlist[mtx_row_to_org(mtx,row)];
652 (void)relman_diffs(rel,&vfilter,mtx,&resid,SAFE_FIX_ME);
653
654 /* added */
655 rel_set_residual(rel,resid);
656
657 }
658 if (qr) {
659 linsolqr_matrix_was_changed(lqr_sys);
660 } else {
661 linsol_matrix_was_changed(lin_sys);
662 }
663 #if DOTIME
664 time1 = tm_cpu_time() - time1;
665 FPRINTF(stderr,"Time taken for Compute_J = %g\n",time1);
666 #endif
667 return(!calc_ok);
668 }
669
670
671 /*
672 LU Factor Jacobian
673
674 @note The RHS will have been will already have been added before
675 calling this function.
676
677 THERE SEEM TO BE TWO VERSIONS OF THIS... WHAT'S THE STORY?
678 */
679 int LUFactorJacobian1(slv_system_t sys,int rank){
680 linsolqr_system_t lqr_sys;
681 mtx_region_t region;
682 enum factor_method fm;
683
684 mtx_region(&region,0,rank-1,0,rank-1); /* set the region */
685 lqr_sys = slv_get_linsolqr_sys(sys); /* get the linear system */
686
687 linsolqr_matrix_was_changed(lqr_sys);
688 linsolqr_reorder(lqr_sys,&region,natural);
689
690 fm = linsolqr_fmethod(lqr_sys);
691 if (fm == unknown_f) fm = ranki_kw2; /* make sure somebody set it */
692 linsolqr_factor(lqr_sys,fm);
693
694 return 0;
695 }
696
697 /**
698 This is the other version of this, pulled in from Tcl/Tk files.
699
700 * Note a rhs would have been previously added
701 * to keep the system happy.
702 * Now handles both linsol and linsolqr as needed.
703 * Assumes region to be factored is in upper left corner.
704 * Region is reordered using the last reorder method used in
705 * the case of linsolqr, so all linsolqr methods are available
706 * to this routine.
707 */
708 int LUFactorJacobian(slv_system_t sys){
709 linsol_system_t lin_sys=NULL;
710 linsolqr_system_t lqr_sys=NULL;
711 mtx_region_t region;
712 int size,nvars,nrels;
713 #if DOTIME
714 double time1;
715 #endif
716
717 #if DOTIME
718 time1 = tm_cpu_time();
719 #endif
720
721 nvars = NumberFreeVars(sys);
722 nrels = NumberIncludedRels(sys);
723 size = MAX(nvars,nrels); /* get size of matrix */
724 mtx_region(&region,0,size-1,0,size-1); /* set the region */
725 lin_sys = slv_get_linsol_sys(sys); /* get the linear system */
726 if (lin_sys!=NULL) {
727 linsol_matrix_was_changed(lin_sys);
728 linsol_reorder(lin_sys,&region); /* This was entire_MATRIX */
729 linsol_invert(lin_sys,&region); /* invert the given matrix over
730 * the given region */
731 } else {
732 enum factor_method fm;
733 int oldtiming;
734 lqr_sys = slv_get_linsolqr_sys(sys);
735 /* WE are ASSUMING that the system has been qr_preped before now! */
736 linsolqr_matrix_was_changed(lqr_sys);
737 linsolqr_reorder(lqr_sys,&region,natural); /* should retain original */
738 fm = linsolqr_fmethod(lqr_sys);
739 if (fm == unknown_f) {
740 fm = ranki_kw2; /* make sure somebody set it */
741 }
742 oldtiming = g_linsolqr_timing;
743 g_linsolqr_timing = 0;
744 linsolqr_factor(lqr_sys,fm);
745 g_linsolqr_timing = oldtiming;
746 }
747
748 #if DOTIME
749 time1 = tm_cpu_time() - time1;
750 FPRINTF(stderr,"Time taken for LUFactorJacobian = %g\n",time1);
751 #endif
752 return 0;
753 }
754
755
756 /*
757 * At this point the rhs would have already been added.
758 *
759 * Extended to handle either factorization code:
760 * linsol or linsolqr.
761 */
762 int Compute_dy_dx_smart(slv_system_t sys,
763 real64 *rhs,
764 real64 **dy_dx,
765 int *inputs, int ninputs,
766 int *outputs, int noutputs)
767 {
768 linsol_system_t lin_sys=NULL;
769 linsolqr_system_t lqr_sys=NULL;
770 mtx_matrix_t mtx;
771 int col,current_col;
772 int row;
773 int capacity;
774 real64 *solution = NULL;
775 int i,j;
776 #if DOTIME
777 double time1;
778 #endif
779
780 #if DOTIME
781 time1 = tm_cpu_time();
782 #endif
783
784 lin_sys = slv_get_linsol_sys(sys); /* get the linear system */
785 lqr_sys = slv_get_linsolqr_sys(sys); /* get the linear system */
786 mtx = slv_get_sys_mtx(sys); /* get the matrix */
787
788 capacity = mtx_capacity(mtx);
789 solution = ASC_NEW_ARRAY_CLEAR(real64,capacity);
790
791 /*
792 * The array inputs is a list of original indexes, of the variables
793 * that we are trying to obtain the sensitivity to. We have to
794 * get the *current* column from the matrix based on those indices.
795 * Hence we use mtx_org_to_col. This little manipulation is not
796 * necessary for the computed solution as the solve routine returns
797 * the results in the *original* order rather than the *current* order.
798 */
799 if (lin_sys!=NULL) {
800 for (j=0;j<ninputs;j++) {
801 col = inputs[j];
802 current_col = mtx_org_to_col(mtx,col);
803 mtx_org_col_vec(mtx,current_col,rhs,mtx_ALL_ROWS); /* get the column */
804
805 linsol_rhs_was_changed(lin_sys,rhs);
806 linsol_solve(lin_sys,rhs); /* solve */
807 linsol_copy_solution(lin_sys,rhs,solution); /* get the solution */
808
809 for (i=0;i<noutputs;i++) { /* copy the solution to dy_dx */
810 row = outputs[i];
811 dy_dx[i][j] = -1.0*solution[row]; /* the -1 comes from the lin alg */
812 }
813 /*
814 * zero the vector using the incidence pattern of our column.
815 * This is faster than the naiive approach of zeroing
816 * everything especially if the vector is large.
817 */
818 mtx_zr_org_vec_using_col(mtx,current_col,rhs,mtx_ALL_ROWS);
819 }
820 } else {
821 for (j=0;j<ninputs;j++) {
822 col = inputs[j];
823 current_col = mtx_org_to_col(mtx,col);
824 mtx_org_col_vec(mtx,current_col,rhs,mtx_ALL_ROWS);
825
826 linsolqr_rhs_was_changed(lqr_sys,rhs);
827 linsolqr_solve(lqr_sys,rhs);
828 linsolqr_copy_solution(lqr_sys,rhs,solution);
829
830 for (i=0;i<noutputs;i++) {
831 row = outputs[i];
832 dy_dx[i][j] = -1.0*solution[row];
833 }
834 mtx_zr_org_vec_using_col(mtx,current_col,rhs,mtx_ALL_ROWS);
835 }
836 }
837 if (solution) {
838 ascfree((char *)solution);
839 }
840
841 #if DOTIME
842 time1 = tm_cpu_time() - time1;
843 FPRINTF(stderr,"Time for Compute_dy_dx_smart = %g\n",time1);
844 #endif
845 return 0;
846 }
847
848 #if 0
849 static int ComputeInverse(slv_system_t sys,
850 real64 *rhs)
851 {
852 linsolqr_system_t lqr_sys;
853 mtx_matrix_t mtx;
854 int capacity,order;
855 real64 *solution = NULL;
856 int j,k;
857
858 lqr_sys = slv_get_linsolqr_sys(sys); /* get the linear system */
859 mtx = linsolqr_get_matrix(lqr_sys); /* get the matrix */
860
861 capacity = mtx_capacity(mtx);
862 zero_vector(rhs,capacity); /* zero the rhs */
863 solution = ASC_NEW_ARRAY_CLEAR(real64,capacity);
864
865 order = mtx_order(mtx);
866 for (j=0;j<order;j++) {
867 rhs[j] = 1.0;
868 linsolqr_rhs_was_changed(lqr_sys,rhs);
869 linsolqr_solve(lqr_sys,rhs); /* solve */
870 linsolqr_copy_solution(lqr_sys,rhs,solution); /* get the solution */
871
872 FPRINTF(stderr,"This is the rhs and solution for rhs #%d\n",j);
873 for (k=0;k<order;k++) {
874 FPRINTF(stderr,"%12.8g %12.8g\n",rhs[k],solution[k]);
875 }
876 rhs[j] = 0.0;
877 }
878 if (solution) ascfree((char *)solution);
879 return 0;
880 }
881 #endif
882
883 int sensitivity_anal(
884 struct Instance *inst, /* not used but will be */
885 struct gl_list_t *arglist)
886 {
887 struct Instance *which_instance,*tmp_inst, *atominst;
888 struct gl_list_t *branch;
889 struct var_variable **vlist = NULL;
890 int *inputs_ndx_list = NULL, *outputs_ndx_list = NULL;
891 real64 **dy_dx = NULL;
892 slv_system_t sys = NULL;
893 int c;
894 int noutputs = 0;
895 int ninputs;
896 int i,j;
897 int offset;
898 dof_t *dof;
899 int num_vars,ind,found;
900
901 linsolqr_system_t lqr_sys; /* stuff for the linear system & matrix */
902 mtx_matrix_t mtx;
903 int32 capacity;
904 real64 *scratch_vector = NULL;
905 int result=0;
906
907 /* Ignore unused params */
908 (void) inst;
909
910 (void)NumberFreeVars(NULL); /* used to re-init the system */
911 (void)NumberRels(NULL); /* used to re-init the system */
912 which_instance = FetchElement(arglist,1,1);
913 sys = PreSolve(which_instance);
914 if (!sys) {
915 FPRINTF(stderr,"Early termination due to failure in Presolve\n");
916 result = 1;
917 goto error;
918 }
919
920 dof = slv_get_dofdata(sys);
921 if (!(dof->n_rows == dof->n_cols &&
922 dof->n_rows == dof->structural_rank)) {
923 FPRINTF(stderr,"Early termination: non square system\n");
924 result = 1;
925 goto error;
926 }
927 /*
928 * prepare the inputs list
929 */
930 vlist = slv_get_solvers_var_list(sys);
931
932 branch = gl_fetch(arglist,2); /* input args */
933 ninputs = gl_length(branch);
934 inputs_ndx_list = ASC_NEW_ARRAY(int,ninputs);
935
936 num_vars = slv_get_num_solvers_vars(sys);
937 for (c=0;c<ninputs;c++) {
938 atominst = (struct Instance *)gl_fetch(branch,c+1);
939 found = 0;
940 ind = num_vars - 1;
941 /* search backwards because fixed vars are at the end of the
942 var list */
943 while (!found && ind >= 0){
944 if (var_instance(vlist[ind]) == atominst) {
945 inputs_ndx_list[c] = var_sindex(vlist[ind]);
946 found = 1;
947 }
948 --ind;
949 }
950 if (!found) {
951 FPRINTF(stderr,"Sensitivity input variable not found\n");
952 result = 1;
953 goto error;
954 }
955 }
956
957 /*
958 * prepare the outputs list
959 */
960 branch = gl_fetch(arglist,3); /* output args */
961 noutputs = gl_length(branch);
962 outputs_ndx_list = ASC_NEW_ARRAY(int,noutputs);
963 for (c=0;c<noutputs;c++) {
964 atominst = (struct Instance *)gl_fetch(branch,c+1);
965 found = 0;
966 ind = 0;
967 while (!found && ind < num_vars){
968 if (var_instance(vlist[ind]) == atominst) {
969 outputs_ndx_list[c] = var_sindex(vlist[ind]);
970 found = 1;
971 }
972 ++ind;
973 }
974 if (!found) {
975 FPRINTF(stderr,"Sensitivity ouput variable not found\n");
976 result = 1;
977 goto error;
978 }
979 }
980
981 /*
982 * prepare the results dy_dx.
983 */
984 dy_dx = make_matrix(noutputs,ninputs);
985
986
987 result = Compute_J(sys);
988 if (result) {
989 FPRINTF(stderr,"Early termination due to failure in calc Jacobian\n");
990 goto error;
991 }
992
993 /*
994 * Note: the RHS *has* to added here. We will construct the vector
995 * of size matrix capacity and add it. It will be removed after
996 * we finish computing dy_dx.
997 */
998 lqr_sys = slv_get_linsolqr_sys(sys); /* get the linear system */
999 mtx = linsolqr_get_matrix(lqr_sys); /* get the matrix */
1000 capacity = mtx_capacity(mtx);
1001 scratch_vector = ASC_NEW_ARRAY_CLEAR(real64,capacity);
1002 linsolqr_add_rhs(lqr_sys,scratch_vector,FALSE);
1003 result = LUFactorJacobian1(sys,dof->structural_rank);
1004 if (result) {
1005 FPRINTF(stderr,"Early termination due to failure in LUFactorJacobian\n");
1006 goto error;
1007 }
1008 result = Compute_dy_dx_smart(sys, scratch_vector, dy_dx,
1009 inputs_ndx_list, ninputs,
1010 outputs_ndx_list, noutputs);
1011
1012 linsolqr_remove_rhs(lqr_sys,scratch_vector);
1013 if (result) {
1014 FPRINTF(stderr,"Early termination due to failure in Compute_dy_dx\n");
1015 goto error;
1016 }
1017
1018 /*
1019 * Write the results back to the partials array in the
1020 * instance tree
1021 */
1022 offset = 0;
1023 for (i=0;i<noutputs;i++) {
1024 for (j=0;j<ninputs;j++) {
1025 tmp_inst = FetchElement(arglist,4,offset+j+1);
1026 SetRealAtomValue(tmp_inst,dy_dx[i][j],(unsigned)0);
1027 #if DEBUG
1028 FPRINTF(stderr,"%12.8f i%d j%d",dy_dx[i][j],i,j);
1029 #endif
1030 }
1031 #if DEBUG
1032 FPRINTF(stderr,"\n");
1033 #endif
1034 offset += ninputs;
1035 }
1036 #if DEBUG
1037 FPRINTF(stderr,"\n");
1038 #endif
1039
1040 error:
1041 if (inputs_ndx_list) ascfree((char *)inputs_ndx_list);
1042 if (outputs_ndx_list) ascfree((char *)outputs_ndx_list);
1043 if (dy_dx) free_matrix(dy_dx,noutputs);
1044 if (scratch_vector) ascfree((char *)scratch_vector);
1045 if (sys) system_destroy(sys);
1046 return result;
1047 }
1048
1049 /**
1050 Do Data Analysis
1051 */
1052 static int DoDataAnalysis(struct var_variable **inputs,
1053 struct var_variable **outputs,
1054 int ninputs, int noutputs,
1055 real64 **dy_dx)
1056 {
1057 FILE *fp;
1058 double *norm_2, *norm_1;
1059 double input_nominal,maxvalue,sum;
1060 int i,j;
1061
1062 norm_1 = ASC_NEW_ARRAY_CLEAR(double,ninputs);
1063 norm_2 = ASC_NEW_ARRAY_CLEAR(double,ninputs);
1064
1065 fp = fopen("sensitivity.out","w+");
1066 if (!fp) return 0;
1067
1068 /*
1069 * calculate the 1 and 2 norms; cache them away so that we
1070 * can pretty print them. Style is everything !.
1071 *
1072 */
1073 for (j=0;j<ninputs;j++) {
1074 input_nominal = var_nominal(inputs[j]);
1075 maxvalue = sum = 0;
1076 for (i=0;i<noutputs;i++) {
1077 dy_dx[i][j] *= input_nominal/var_nominal(outputs[i]);
1078 maxvalue = MAX(fabs(dy_dx[i][j]),maxvalue);
1079 sum += dy_dx[i][j]*dy_dx[i][j];
1080 }
1081 norm_1[j] = maxvalue;
1082 norm_2[j] = sum;
1083 }
1084
1085 for (j=0;j<ninputs;j++) { /* print the var_index */
1086 FPRINTF(fp,"%8d ",var_mindex(inputs[j]));
1087 }
1088 FPRINTF(fp,"\n");
1089
1090 for (j=0;j<ninputs;j++) { /* print the 1 norms */
1091 FPRINTF(fp,"%-#18.8f ",norm_1[j]);
1092 }
1093 FPRINTF(fp,"\n");
1094
1095 for (j=0;j<ninputs;j++) { /* print the 2 norms */
1096 FPRINTF(fp,"%-#18.8f ",norm_2[j]);
1097 }
1098 FPRINTF(fp,"\n\n");
1099 ascfree((char *)norm_1);
1100 ascfree((char *)norm_2);
1101
1102 for (i=0;i<noutputs;i++) { /* print the scaled data */
1103 for (j=0;j<ninputs;j++) {
1104 FPRINTF(fp,"%-#18.8f %-4d",dy_dx[i][j],i);
1105 }
1106 if (var_fixed(outputs[i]))
1107 FPRINTF(fp," **fixed*** \n");
1108 else
1109 PUTC('\n',fp);
1110 }
1111 FPRINTF(fp,"\n");
1112 fclose(fp);
1113 return 0;
1114 }
1115
1116 #if 0
1117 static int DoProject_X(struct var_variable **old_inputs,
1118 struct var_variable **new_inputs, /* new values of u */
1119 double step_length,
1120 struct var_variable **outputs,
1121 int ninputs, int noutputs,
1122 real64 **dy_dx)
1123 {
1124 struct var_variable *var;
1125 real64 old_y, new_y, tmp;
1126 real64 *delta_x;
1127 int i,j;
1128
1129 delta_x = ASC_NEW_ARRAY_CLEAR(real64,ninputs);
1130
1131 for (j=0;j<ninputs;j++) {
1132 delta_x[j] = var_value(new_inputs[j]) - var_value(old_inputs[j]);
1133 /* delta_x[j] = RealAtomValue(new_inputs[j]) - RealAtomValue(old_inputs[j]); */
1134 }
1135
1136 for (i=0;i<noutputs;i++) {
1137 var = outputs[i];
1138 if (var_fixed(var) || !var_active(var)) /* project only the free vars */
1139 continue;
1140 tmp = 0.0;
1141 for (j=0;j<ninputs;j++) {
1142 tmp += (dy_dx[i][j] * delta_x[j]);
1143 }
1144 /* old_y = RealAtomValue(var); */
1145 old_y = var_value(var);
1146 new_y = old_y + step_length*tmp;
1147 /* SetRealAtomValue(var,new_y,(unsigned)0); */
1148 var_set_value(var,new_y);
1149 # if DEBUG
1150 FPRINTF(stderr,"Old_y = %12.8g; Nex_y = %12.8g\n",old_y,new_y);
1151 # endif
1152 }
1153 ascfree((char *)delta_x);
1154 return 0;
1155 }
1156 #endif
1157
1158
1159
1160 /**
1161 This function is very similar to sensitivity_anal, execept,
1162 that it perform the analysis on the entire system, based on
1163 the given inputs. Nothing is returned. As such the call from
1164 ASCEND is:
1165
1166 <pre>
1167 EXTERN sensitivity_anal_all(
1168 this_instance,
1169 u_old[1..n_inputs],
1170 u_new[1..n_inputs],
1171 step_length
1172 );</pre>
1173
1174 The results will be witten to standard out.
1175 This function is more expensive from a a memory point of view,
1176 as we keep aroung a dense matrix n_outputs * n_inputs, but here
1177 n_outputs may be *much* larger depending on problem size.
1178 */
1179 int sensitivity_anal_all( struct Instance *inst, /* not used but will be */
1180 struct gl_list_t *arglist,
1181 real64 step_length)
1182 {
1183 struct Instance *which_instance;
1184 struct gl_list_t *branch2, *branch3;
1185 dof_t *dof;
1186 struct var_variable **inputs = NULL, **outputs = NULL;
1187 int *inputs_ndx_list = NULL, *outputs_ndx_list = NULL;
1188 real64 **dy_dx = NULL;
1189 struct var_variable **vp,**ptr;
1190 slv_system_t sys = NULL;
1191 long c;
1192 int noutputs=0, ninputs;
1193 var_filter_t vfilter;
1194
1195 struct var_variable **new_inputs = NULL; /* optional stuff for variable
1196 * projection */
1197
1198 linsolqr_system_t lqr_sys; /* stuff for the linear system & matrix */
1199 mtx_matrix_t mtx;
1200 int32 capacity;
1201 real64 *scratch_vector = NULL;
1202 int result=0;
1203
1204 /* Ignore unused params */
1205 (void)inst; (void)step_length;
1206
1207 /*
1208 * Call the presolve for the system. This should number variables
1209 * and relations as well create and order the main Jacobian. The
1210 * only potential problem that I see here is that presolve using
1211 * the slv0 solver *only* recognizes solver vars. So that if one
1212 * wanted to see the sensitivity of a *parameter*, it would not
1213 * be possible. We will have to trap this in CheckArgs.
1214 *
1215 * Also the current version of ascend is fucked in how the var module
1216 * handles variables and their numbering through the interface ptr
1217 * crap.
1218 */
1219
1220 (void)NumberFreeVars(NULL); /* used to re-init the system */
1221 (void)NumberRels(NULL); /* used to re-init the system */
1222 which_instance = FetchElement(arglist,1,1);
1223 sys = PreSolve(which_instance);
1224 if (!sys) {
1225 FPRINTF(stderr,"Early termination due to failure in Presolve\n");
1226 result = 1;
1227 goto error;
1228 }
1229 dof = slv_get_dofdata(sys);
1230
1231 /*
1232 * prepare the inputs list. We dont need the index of the new_inputs
1233 * list. We will grab them later if necessary.
1234 */
1235 branch2 = gl_fetch(arglist,2); /* input args -- old u_values */
1236 branch3 = gl_fetch(arglist,3); /* input args -- new u_values */
1237 ninputs = gl_length(branch2);
1238 inputs = ASC_NEW_ARRAY(struct var_variable *,ninputs+1);
1239 new_inputs = ASC_NEW_ARRAY(struct var_variable *,ninputs+1);
1240
1241 inputs_ndx_list = ASC_NEW_ARRAY(int,ninputs);
1242 for (c=0;c<ninputs;c++) {
1243 inputs[c] = (struct var_variable *)gl_fetch(branch2,c+1);
1244 inputs_ndx_list[c] = var_mindex(inputs[c]);
1245 new_inputs[c] = (struct var_variable *)gl_fetch(branch3,c+1);
1246 }
1247 inputs[ninputs] = NULL; /* null terminate the list */
1248 new_inputs[ninputs] = NULL; /* null terminate the list */
1249
1250 /*
1251 * prepare the outputs list. This is where we differ from
1252 * the other function. The noutputs, and the indexes of these
1253 * outputs is obtained from the entire solve system.
1254 */
1255 vfilter.matchbits = 0;
1256 noutputs = slv_count_solvers_vars(sys,&vfilter);
1257 outputs = ASC_NEW_ARRAY(struct var_variable *,noutputs+1);
1258 outputs_ndx_list = ASC_NEW_ARRAY(int,noutputs);
1259 ptr = vp = slv_get_solvers_var_list(sys);
1260 for (c=0;c<noutputs;c++) {
1261 outputs[c] = *ptr;
1262 outputs_ndx_list[c] = var_sindex(*ptr);
1263 ptr++;
1264 }
1265 outputs[noutputs] = NULL; /* null terminate the list */
1266
1267 /*
1268 * prepare the results dy_dx. This is the expensive part from a
1269 * memory point of view. However I would like to have the entire
1270 * noutputs * ninputs matrix even for a short while so that I
1271 * can compute a number of different types of norms.
1272 */
1273 dy_dx = make_matrix(noutputs,ninputs);
1274
1275 result = Compute_J(sys);
1276 if (result) {
1277 FPRINTF(stderr,"Early termination due to failure in calc Jacobian\n");
1278 goto error;
1279 }
1280
1281 /*
1282 * Note: the RHS *has* to added here. We will construct the vector
1283 * of size matrix capacity and add it. It will be removed after
1284 * we finish computing dy_dx.
1285 */
1286 lqr_sys = slv_get_linsolqr_sys(sys); /* get the linear system */
1287 mtx = linsolqr_get_matrix(lqr_sys); /* get the matrix */
1288 capacity = mtx_capacity(mtx);
1289 scratch_vector = ASC_NEW_ARRAY_CLEAR(real64,capacity);
1290 linsolqr_add_rhs(lqr_sys,scratch_vector,FALSE);
1291 result = LUFactorJacobian1(sys,dof->structural_rank);
1292 if (result) {
1293 FPRINTF(stderr,"Early termination due to failure in LUFactorJacobian\n");
1294 goto error;
1295 }
1296 result = Compute_dy_dx_smart(sys, scratch_vector, dy_dx,
1297 inputs_ndx_list, ninputs,
1298 outputs_ndx_list, noutputs);
1299
1300 linsolqr_remove_rhs(lqr_sys,scratch_vector);
1301 if (result) {
1302 FPRINTF(stderr,"Early termination due to failure in Compute_dy_dx\n");
1303 goto error;
1304 }
1305
1306 /*
1307 * Do some analysis on the results, inclusive of
1308 * writing them to someplace useful.
1309 */
1310 if (DoDataAnalysis(inputs, outputs, ninputs, noutputs, dy_dx))
1311 result = 1;
1312
1313 /*
1314 * Some experimental projection stuff -- not used now.
1315 * if (DoProject_X(inputs, new_inputs, step_length,
1316 * outputs, ninputs, noutputs, dy_dx))
1317 * result = 1;
1318 */
1319
1320 error:
1321 if (inputs) ascfree((char *)inputs);
1322 if (new_inputs) ascfree((char *)new_inputs);
1323 if (inputs_ndx_list) ascfree((char *)inputs_ndx_list);
1324 if (outputs) ascfree((char *)outputs);
1325 if (outputs_ndx_list) ascfree((char *)outputs_ndx_list);
1326 if (dy_dx) free_matrix(dy_dx,noutputs);
1327 if (scratch_vector) ascfree((char *)scratch_vector);
1328 if (sys) system_destroy(sys);
1329 return result;
1330 }
1331
1332
1333 int do_sensitivity_eval( struct Instance *i,
1334 struct gl_list_t *arglist)
1335 {
1336 int result;
1337 if (SensitivityCheckArgs(arglist)) {
1338 return 1;
1339 }
1340 result = sensitivity_anal(i,arglist);
1341 return result;
1342 }
1343
1344 int do_sensitivity_eval_all( struct Instance *i,
1345 struct gl_list_t *arglist)
1346 {
1347 int result;
1348 double step_length = 0.0;
1349 if (SensitivityAllCheckArgs(arglist,&step_length)) {
1350 return 1;
1351 }
1352 result = sensitivity_anal_all(i,arglist,step_length);
1353 return result;
1354 }
1355
1356
1357 char sensitivity_help[] =
1358 "This function does sensitivity analysis dy/dx. It requires 4 args:\n"
1359 " 1. name: name of a reference instance or SELF.\n"
1360 " 2. x: x, where x is an array of > solver_var.\n"
1361 " 3. y: where y is an array of > solver_var.\n"
1362 " 4. dy/dx: which dy_dx[1..n_y][1..n_x].";
1363
1364 int sensitivity_register(void){
1365
1366 int result=0;
1367
1368
1369 result = CreateUserFunctionMethod("do_solve",
1370 do_solve_eval,
1371 2,NULL,NULL,NULL); /* was 2,0,null */
1372 result += CreateUserFunctionMethod("do_finite_difference",
1373 do_finite_diff_eval,
1374 4,NULL,NULL,NULL); /* 4,0,null */
1375 result += CreateUserFunctionMethod("do_sensitivity",
1376 do_sensitivity_eval,
1377 4,sensitivity_help,NULL,NULL);
1378 result += CreateUserFunctionMethod("do_sensitivity_all",
1379 do_sensitivity_eval_all,
1380 4,"See do_sensitivity_eval for details",NULL,NULL);
1381
1382 return result;
1383 }
1384
1385
1386
1387 #undef DEBUG

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