Commit bc61e8b9 authored by Morten Welinder's avatar Morten Welinder

GoalSeek: code cleanup.

Move non-gui part of the code out of dialogs.
parent 9ba3a3d3
......@@ -86,175 +86,21 @@ typedef struct {
} GoalSeekState;
typedef struct {
GoalSeekState *state;
GnmCell *xcell, *ycell;
gnm_float ytarget;
gboolean update_ui;
} GoalEvalData;
static GnmGoalSeekStatus
goal_seek_eval (gnm_float x, gnm_float *y, void *vevaldata)
{
GoalEvalData const *evaldata = vevaldata;
GnmValue *v = value_new_float (x);
if (evaldata->update_ui) {
sheet_cell_set_value (evaldata->xcell, v);
} else {
gnm_cell_set_value (evaldata->xcell, v);
cell_queue_recalc (evaldata->xcell);
}
gnm_cell_eval (evaldata->ycell);
if (evaldata->ycell->value) {
*y = value_get_as_float (evaldata->ycell->value) - evaldata->ytarget;
if (gnm_finite (*y))
return GOAL_SEEK_OK;
}
return GOAL_SEEK_ERROR;
}
static GnmGoalSeekStatus
gnumeric_goal_seek (GoalSeekState *state)
{
GnmGoalSeekData seekdata;
GoalEvalData evaldata;
GnmGoalSeekStatus status;
gboolean hadold;
gnm_float oldx;
GnmGoalSeekCellData celldata;
goal_seek_initialize (&seekdata);
seekdata.xmin = state->xmin;
seekdata.xmax = state->xmax;
evaldata.xcell = state->change_cell;
evaldata.ycell = state->set_cell;
evaldata.ytarget = state->target_value;
evaldata.update_ui = FALSE;
evaldata.state = state;
hadold = !VALUE_IS_EMPTY_OR_ERROR (state->change_cell->value);
oldx = hadold ? value_get_as_float (state->change_cell->value) : 0;
/* PLAN A: Newton's iterative method from initial or midpoint. */
{
gnm_float x0;
if (hadold && oldx >= seekdata.xmin && oldx <= seekdata.xmax)
x0 = oldx;
else
x0 = (seekdata.xmin + seekdata.xmax) / 2;
status = goal_seek_newton (goal_seek_eval, NULL,
&seekdata, &evaldata,
x0);
if (status == GOAL_SEEK_OK)
goto DONE;
}
/* PLAN B: Trawl uniformly. */
if (!seekdata.havexpos || !seekdata.havexneg) {
status = goal_seek_trawl_uniformly (goal_seek_eval,
&seekdata, &evaldata,
seekdata.xmin, seekdata.xmax,
100);
if (status == GOAL_SEEK_OK)
goto DONE;
}
/* PLAN C: Trawl normally from middle. */
if (!seekdata.havexpos || !seekdata.havexneg) {
gnm_float sigma, mu;
int i;
sigma = MIN (seekdata.xmax - seekdata.xmin, 1e6);
mu = (seekdata.xmax + seekdata.xmin) / 2;
for (i = 0; i < 5; i++) {
sigma /= 10;
status = goal_seek_trawl_normally (goal_seek_eval,
&seekdata, &evaldata,
mu, sigma, 30);
if (status == GOAL_SEEK_OK)
goto DONE;
}
}
/* PLAN D: Trawl normally from left. */
if (!seekdata.havexpos || !seekdata.havexneg) {
gnm_float sigma, mu;
int i;
sigma = MIN (seekdata.xmax - seekdata.xmin, 1e6);
mu = seekdata.xmin;
for (i = 0; i < 5; i++) {
sigma /= 10;
status = goal_seek_trawl_normally (goal_seek_eval,
&seekdata, &evaldata,
mu, sigma, 20);
if (status == GOAL_SEEK_OK)
goto DONE;
}
}
/* PLAN E: Trawl normally from right. */
if (!seekdata.havexpos || !seekdata.havexneg) {
gnm_float sigma, mu;
int i;
sigma = MIN (seekdata.xmax - seekdata.xmin, 1e6);
mu = seekdata.xmax;
for (i = 0; i < 5; i++) {
sigma /= 10;
status = goal_seek_trawl_normally (goal_seek_eval,
&seekdata, &evaldata,
mu, sigma, 20);
if (status == GOAL_SEEK_OK)
goto DONE;
}
}
/* PLAN F: Newton iteration with uniform net of starting points. */
if (!seekdata.havexpos || !seekdata.havexneg) {
int i;
const int N = 10;
for (i = 1; i <= N; i++) {
gnm_float x0 = seekdata.xmin +
(seekdata.xmax - seekdata.xmin) / (N + 1) * i;
status = goal_seek_newton (goal_seek_eval, NULL,
&seekdata, &evaldata,
x0);
if (status == GOAL_SEEK_OK)
goto DONE;
}
}
/* PLAN Z: Bisection. */
{
status = goal_seek_bisection (goal_seek_eval,
&seekdata, &evaldata);
if (status == GOAL_SEEK_OK)
goto DONE;
}
DONE:
evaldata.update_ui = TRUE;
if (status == GOAL_SEEK_OK) {
gnm_float yroot;
(void) goal_seek_eval (seekdata.root, &yroot, &evaldata);
} else if (hadold) {
gnm_float ydummy;
(void) goal_seek_eval (oldx, &ydummy, &evaldata);
}
celldata.xcell = state->change_cell;
celldata.ycell = state->set_cell;
celldata.ytarget = state->target_value;
return status;
return gnm_goal_seek_cell (&seekdata, &celldata);
}
static void
......
......@@ -16,6 +16,9 @@
#include <gnumeric.h>
#include <tools/goal-seek.h>
#include <gnm-random.h>
#include <value.h>
#include <cell.h>
#include <sheet.h>
#include <stdlib.h>
#include <math.h>
......@@ -725,35 +728,159 @@ goal_seek_trawl_normally (GnmGoalSeekFunction f,
return GOAL_SEEK_ERROR;
}
#ifdef STANDALONE
static GnmGoalSeekStatus
f (gnm_float x, gnm_float *y, void *user_data)
/**
* gnm_goal_seek_eval_cell:
* @x: x-value for which to evaluate
* @y: (out): location to store result
* @data: user data
*
* Returns: An status indicating whether evaluation went ok.
*/
GnmGoalSeekStatus
gnm_goal_seek_eval_cell (gnm_float x, gnm_float *y, gpointer data_)
{
*y = x * x - 2;
GnmGoalSeekCellData const *data = data_;
GnmValue *v = value_new_float (x);
gnm_cell_set_value (data->xcell, v);
cell_queue_recalc (data->xcell);
gnm_cell_eval (data->ycell);
if (data->ycell->value &&
VALUE_IS_NUMBER (data->ycell->value)) {
*y = value_get_as_float (data->ycell->value) - data->ytarget;
if (gnm_finite (*y))
return GOAL_SEEK_OK;
}
return GOAL_SEEK_ERROR;
}
static GnmGoalSeekStatus
df (gnm_float x, gnm_float *y, void *user_data)
GnmGoalSeekStatus
gnm_goal_seek_cell (GnmGoalSeekData *data,
GnmGoalSeekCellData *celldata)
{
*y = 2 * x;
return GOAL_SEEK_OK;
}
GnmGoalSeekStatus status;
gboolean hadold;
gnm_float oldx;
GnmValue *v;
hadold = !VALUE_IS_EMPTY_OR_ERROR (celldata->xcell->value);
oldx = hadold ? value_get_as_float (celldata->xcell->value) : 0;
int
main ()
{
GnmGoalSeekData data;
/* PLAN A: Newton's iterative method from initial or midpoint. */
{
gnm_float x0;
if (hadold && oldx >= data->xmin && oldx <= data->xmax)
x0 = oldx;
else
x0 = (data->xmin + data->xmax) / 2;
status = goal_seek_newton (gnm_goal_seek_eval_cell, NULL,
data, celldata,
x0);
if (status == GOAL_SEEK_OK)
goto DONE;
}
/* PLAN B: Trawl uniformly. */
if (!data->havexpos || !data->havexneg) {
status = goal_seek_trawl_uniformly (gnm_goal_seek_eval_cell,
data, celldata,
data->xmin, data->xmax,
100);
if (status == GOAL_SEEK_OK)
goto DONE;
}
/* PLAN C: Trawl normally from middle. */
if (!data->havexpos || !data->havexneg) {
gnm_float sigma, mu;
int i;
goal_seek_initialize (&data);
data.xmin = -100;
data.xmax = 100;
sigma = MIN (data->xmax - data->xmin, 1e6);
mu = (data->xmax + data->xmin) / 2;
goal_seek_newton (f, NULL, &data, NULL, 50.0);
for (i = 0; i < 5; i++) {
sigma /= 10;
status = goal_seek_trawl_normally (gnm_goal_seek_eval_cell,
data, celldata,
mu, sigma, 30);
if (status == GOAL_SEEK_OK)
goto DONE;
}
}
/* PLAN D: Trawl normally from left. */
if (!data->havexpos || !data->havexneg) {
gnm_float sigma, mu;
int i;
sigma = MIN (data->xmax - data->xmin, 1e6);
mu = data->xmin;
for (i = 0; i < 5; i++) {
sigma /= 10;
status = goal_seek_trawl_normally (gnm_goal_seek_eval_cell,
data, celldata,
mu, sigma, 20);
if (status == GOAL_SEEK_OK)
goto DONE;
}
}
/* PLAN E: Trawl normally from right. */
if (!data->havexpos || !data->havexneg) {
gnm_float sigma, mu;
int i;
goal_seek_newton (f, df, &data, NULL, 50.0);
sigma = MIN (data->xmax - data->xmin, 1e6);
mu = data->xmax;
return 0;
for (i = 0; i < 5; i++) {
sigma /= 10;
status = goal_seek_trawl_normally (gnm_goal_seek_eval_cell,
data, celldata,
mu, sigma, 20);
if (status == GOAL_SEEK_OK)
goto DONE;
}
}
/* PLAN F: Newton iteration with uniform net of starting points. */
if (!data->havexpos || !data->havexneg) {
int i;
const int N = 10;
for (i = 1; i <= N; i++) {
gnm_float x0 = data->xmin +
(data->xmax - data->xmin) / (N + 1) * i;
status = goal_seek_newton (gnm_goal_seek_eval_cell, NULL,
data, celldata,
x0);
if (status == GOAL_SEEK_OK)
goto DONE;
}
}
/* PLAN Z: Bisection. */
{
status = goal_seek_bisection (gnm_goal_seek_eval_cell,
data, celldata);
if (status == GOAL_SEEK_OK)
goto DONE;
}
DONE:
if (status == GOAL_SEEK_OK)
v = value_new_float (data->root);
else if (hadold)
v = value_new_float (oldx);
else
v = value_new_empty ();
sheet_cell_set_value (celldata->xcell, v);
return status;
}
#endif
......@@ -5,22 +5,28 @@
#include <glib.h>
typedef struct {
gnm_float xmin; /* Minimum allowed value for x. */
gnm_float xmax; /* Maximum allowed value for x. */
gnm_float precision; /* Desired relative precision. */
gnm_float xmin; // Minimum allowed value for x.
gnm_float xmax; // Maximum allowed value for x.
gnm_float precision; // Desired relative precision.
gboolean havexpos; /* Do we have a valid xpos? */
gnm_float xpos; /* Value for which f(xpos) > 0. */
gnm_float ypos; /* f(xpos). */
gboolean havexpos; // Do we have a valid xpos?
gnm_float xpos; // Value for which f(xpos) > 0.
gnm_float ypos; // f(xpos).
gboolean havexneg; /* Do we have a valid xneg? */
gnm_float xneg; /* Value for which f(xneg) < 0. */
gnm_float yneg; /* f(xneg). */
gboolean havexneg; // Do we have a valid xneg?
gnm_float xneg; // Value for which f(xneg) < 0.
gnm_float yneg; // f(xneg).
gboolean have_root; /* Do we have a valid root? */
gnm_float root; /* Value for which f(root) == 0. */
gboolean have_root; // Do we have a valid root?
gnm_float root; // Value for which f(root) == 0.
} GnmGoalSeekData;
typedef struct {
GnmCell *xcell; // Cell to change
GnmCell *ycell; // Result cell
gnm_float ytarget; // Target value
} GnmGoalSeekCellData;
typedef enum { GOAL_SEEK_OK, GOAL_SEEK_ERROR } GnmGoalSeekStatus;
typedef GnmGoalSeekStatus (*GnmGoalSeekFunction) (gnm_float x, gnm_float *y, void *user_data);
......@@ -54,4 +60,11 @@ GnmGoalSeekStatus goal_seek_trawl_normally (GnmGoalSeekFunction f,
gnm_float mu, gnm_float sigma,
int points);
GnmGoalSeekStatus gnm_goal_seek_eval_cell (gnm_float x,
gnm_float *y, gpointer data);
GnmGoalSeekStatus gnm_goal_seek_cell (GnmGoalSeekData *data,
GnmGoalSeekCellData *celldata);
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment