Goal seeking solver
There is a single-variable goal-seeking solver -- similar to the 'goal seek' functionality in spreadsheets -- implemented as an EXTERNAL method call. The solver used is a Brent solver, and it can easily wrap around any other solver that can be invoked from a METHOD declaration (currently this includes QRSlv only). Below is a working example from models/test/brent.a4c, also reproduced below.
can be interpreted as saying 'solve the variable err for a value of 0, by varying the value of x between x.lower_bound and x.upper_bound'. For brent to work, you must provide a method named solve; the recommended contents of that method is as shown below, using the senstivity/solve module. This method is called each err(x) needs to be evaluated.
The variable err will be considered 'solved' when the absolute value of err is less than 1.0e-7 times err.nominal. If you want to alter the solving tolerance, the only easy way to do so is to modify err.nominal: the value 1.0e-7 is hard-wired into the brent routine.
The most common problem when using this routine is that you won't have properly 'bracketed' your solution. At the bounds of x, the sign of the variable err must be opposite; ie the variable must certainly have a root, if the relationship err(x) is continuous.
Note that one could in principle nest several of these brent routines, but only by using submodels, and by carefully choosing what to put in your solve method.
REQUIRE "atoms.a4l"; IMPORT "johnpye/brent/brent"; IMPORT "sensitivity/solve"; MODEL brent1; x IS_A solver_var; y IS_A solver_var; z IS_A solver_var; y = -x^2 - 4*x + 4; z = sin(x); err IS_A solver_var; err = y - z; METHODS METHOD on_load; FIX x; x.lower_bound := 0; x.upper_bound := 10; err.nominal := 1; RUN intersect; END on_load; METHOD solve; EXTERNAL do_solve(SELF); END solve; METHOD intersect; EXTERNAL brent(SELF,err,x); END intersect; END brent1;