| 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,¶meters); |
| 142 |
parameters.partition = 0; |
| 143 |
slv_set_parameters(sys,¶meters); |
| 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(®ion,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,®ion,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(®ion,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,®ion); /* This was entire_MATRIX */ |
| 729 |
linsol_invert(lin_sys,®ion); /* 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,®ion,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 |