Commit 5406d4b7 authored by Morten Welinder's avatar Morten Welinder

Solver: refactor idle-loop handling into a solver subclass.

parent c5110c7b
2015-04-24 Morten Welinder <terra@gnome.org>
* gnm-nlsolve.c: Recast in terms of GnmIterSolver.
2015-04-16 Morten Welinder <terra@gnome.org>
* Release 1.12.22
......
......@@ -41,7 +41,7 @@
typedef struct {
GnmSolver *parent;
GnmIterSolver *parent;
/* Input/output cells. */
GPtrArray *vars;
......@@ -55,7 +55,6 @@ typedef struct {
/* Current point. */
gnm_float *xk, yk;
int k;
/* Rosenbrock state */
gnm_float **xi;
......@@ -65,29 +64,17 @@ typedef struct {
/* Parameters: */
gboolean debug;
int max_iter;
guint64 max_iter;
gnm_float min_factor;
guint idle_tag;
} GnmNlsolve;
static void free_matrix (gnm_float **m, int n);
static void
gnm_nlsolve_cleanup (GnmNlsolve *nl)
{
if (nl->idle_tag) {
g_source_remove (nl->idle_tag);
nl->idle_tag = 0;
}
}
static void
gnm_nlsolve_final (GnmNlsolve *nl)
{
const int n = nl->vars->len;
gnm_nlsolve_cleanup (nl);
if (nl->vars)
g_ptr_array_free (nl->vars, TRUE);
g_free (nl->xk);
......@@ -187,7 +174,7 @@ free_matrix (gnm_float **m, int n)
static void
gnm_nlsolve_set_solution (GnmNlsolve *nl)
{
GnmSolver *sol = nl->parent;
GnmSolver *sol = GNM_SOLVER (nl->parent);
GnmSolverResult *result = g_object_new (GNM_SOLVER_RESULT_TYPE, NULL);
const int n = nl->vars->len;
int i;
......@@ -215,7 +202,7 @@ gnm_nlsolve_set_solution (GnmNlsolve *nl)
static gboolean
gnm_nlsolve_get_initial_solution (GnmNlsolve *nl, GError **err)
{
GnmSolver *sol = nl->parent;
GnmSolver *sol = GNM_SOLVER (nl->parent);
const int n = nl->vars->len;
int i;
......@@ -258,7 +245,6 @@ gnm_nlsolve_prepare (GnmSolver *sol, WorkbookControl *wbc, GError **err,
if (ok) {
gnm_solver_set_status (sol, GNM_SOLVER_STATUS_PREPARED);
} else {
gnm_nlsolve_cleanup (nl);
gnm_solver_set_status (sol, GNM_SOLVER_STATUS_ERROR);
}
......@@ -344,7 +330,7 @@ compute_hessian (GnmNlsolve *nl, const gnm_float *xs, const gnm_float *g0)
static gboolean
newton_improve (GnmNlsolve *nl, gnm_float *xs, gnm_float *y, gnm_float ymax)
{
GnmSolver *sol = nl->parent;
GnmSolver *sol = GNM_SOLVER (nl->parent);
const int n = nl->vars->len;
gnm_float *g, **H, *d;
gboolean ok;
......@@ -438,7 +424,8 @@ rosenbrock_tentative_end (GnmNlsolve *nl, gboolean accept)
static gboolean
rosenbrock_iter (GnmNlsolve *nl)
{
GnmSolver *sol = nl->parent;
GnmSolver *sol = GNM_SOLVER (nl->parent);
GnmIterSolver *isol = GNM_ITER_SOLVER (sol);
const int n = nl->vars->len;
int i, j;
const gnm_float alpha = 3;
......@@ -460,7 +447,7 @@ rosenbrock_iter (GnmNlsolve *nl)
}
}
if (nl->k % 20 == 0) {
if (isol->iterations % 20 == 0) {
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
nl->xi[i][j] = (i == j);
......@@ -647,7 +634,7 @@ rosenbrock_shutdown (GnmNlsolve *nl)
static gboolean
polish_iter (GnmNlsolve *nl)
{
GnmSolver *sol = nl->parent;
GnmSolver *sol = GNM_SOLVER (nl->parent);
const int n = nl->vars->len;
gnm_float *x;
gnm_float step;
......@@ -692,25 +679,23 @@ polish_iter (GnmNlsolve *nl)
return any_at_all;
}
static gint
gnm_nlsolve_idle (gpointer data)
static void
gnm_nlsolve_iterate (GnmIterSolver *isol, GnmNlsolve *nl)
{
GnmNlsolve *nl = data;
GnmSolver *sol = nl->parent;
GnmSolver *sol = GNM_SOLVER (isol);
const int n = nl->vars->len;
gboolean ok;
gboolean call_again = TRUE;
if (nl->k == 0)
if (isol->iterations == 0)
rosenbrock_init (nl);
if (nl->debug) {
g_printerr ("Iteration %d at %.15" GNM_FORMAT_g "\n",
nl->k, nl->yk);
g_printerr ("Iteration %ld at %.15" GNM_FORMAT_g "\n",
(long)(isol->iterations), nl->yk);
print_vector ("Current point", nl->xk, n);
}
nl->k++;
ok = rosenbrock_iter (nl);
if (!ok && !nl->tentative) {
......@@ -722,7 +707,7 @@ gnm_nlsolve_idle (gpointer data)
call_again = FALSE;
}
if (call_again && nl->k >= nl->max_iter) {
if (call_again && isol->iterations >= nl->max_iter) {
gnm_solver_set_status (sol, GNM_SOLVER_STATUS_DONE);
call_again = FALSE;
}
......@@ -733,37 +718,6 @@ gnm_nlsolve_idle (gpointer data)
rosenbrock_shutdown (nl);
}
if (!call_again)
nl->idle_tag = 0;
return call_again;
}
static gboolean
gnm_nlsolve_start (GnmSolver *sol, WorkbookControl *wbc, GError **err,
GnmNlsolve *nl)
{
gboolean ok = TRUE;
g_return_val_if_fail (sol->status == GNM_SOLVER_STATUS_PREPARED, FALSE);
nl->idle_tag = g_idle_add (gnm_nlsolve_idle, nl);
gnm_solver_set_status (sol, GNM_SOLVER_STATUS_RUNNING);
return ok;
}
static gboolean
gnm_nlsolve_stop (GnmSolver *sol, GError *err, GnmNlsolve *nl)
{
g_return_val_if_fail (sol->status == GNM_SOLVER_STATUS_RUNNING, FALSE);
gnm_nlsolve_cleanup (nl);
gnm_solver_set_status (sol, GNM_SOLVER_STATUS_CANCELLED);
return TRUE;
}
gboolean
......@@ -782,9 +736,9 @@ nlsolve_solver_factory (GnmSolverFactory *factory, GnmSolverParameters *params);
GnmSolver *
nlsolve_solver_factory (GnmSolverFactory *factory, GnmSolverParameters *params)
{
GnmSolver *res = g_object_new (GNM_SOLVER_TYPE,
"params", params,
NULL);
GnmIterSolver *res = g_object_new (GNM_ITER_SOLVER_TYPE,
"params", params,
NULL);
GnmNlsolve *nl = g_new0 (GnmNlsolve, 1);
GSList *input_cells, *l;
int n;
......@@ -792,7 +746,7 @@ nlsolve_solver_factory (GnmSolverFactory *factory, GnmSolverParameters *params)
GnmEvalPos ep;
GnmCellRef origin;
nl->parent = GNM_SOLVER (res);
nl->parent = res;
nl->maximize = (params->problem_type == GNM_SOLVER_MAXIMIZE);
......@@ -822,11 +776,10 @@ nlsolve_solver_factory (GnmSolverFactory *factory, GnmSolverParameters *params)
nl->xk = g_new (gnm_float, n);
g_signal_connect (res, "prepare", G_CALLBACK (gnm_nlsolve_prepare), nl);
g_signal_connect (res, "start", G_CALLBACK (gnm_nlsolve_start), nl);
g_signal_connect (res, "stop", G_CALLBACK (gnm_nlsolve_stop), nl);
g_signal_connect (res, "iterate", G_CALLBACK (gnm_nlsolve_iterate), nl);
g_object_set_data_full (G_OBJECT (res), PRIVATE_KEY, nl,
(GDestroyNotify)gnm_nlsolve_final);
return res;
return GNM_SOLVER (res);
}
......@@ -23,3 +23,4 @@
BOOLEAN:POINTER
BOOLEAN:OBJECT,POINTER
VOID:BOOLEAN,INT
VOID:VOID
......@@ -4,6 +4,7 @@
function.
(gnm_solver_check_constraints): Avoid undefined C behaviour.
(gnm_solver_param_get_input_cells): Avoid O(n^2) list handling.
(gnm_iter_solver_class_init): New class for in-process solvers.
2015-04-16 Morten Welinder <terra@gnome.org>
......
......@@ -767,7 +767,6 @@ enum {
SOL_SIG_PREPARE,
SOL_SIG_START,
SOL_SIG_STOP,
SOL_SIG_CHILD_EXIT,
SOL_SIG_LAST
};
......@@ -889,6 +888,18 @@ gnm_solver_set_property (GObject *object, guint property_id,
}
}
/**
* gnm_solver_prepare:
* @sol: solver
* @wbc: control for user interaction
* @err: location to store error
*
* Prepare for solving. Preparation need not do anything, but may include
* such tasks as checking that the model is valid for the solver and
* locating necessary external programs.
*
* Returns: %TRUE ok success, %FALSE on error.
*/
gboolean
gnm_solver_prepare (GnmSolver *sol, WorkbookControl *wbc, GError **err)
{
......@@ -901,6 +912,16 @@ gnm_solver_prepare (GnmSolver *sol, WorkbookControl *wbc, GError **err)
return res;
}
/**
* gnm_solver_start:
* @sol: solver
* @wbc: control for user interaction
* @err: location to store error
*
* Start the solving process. If needed, the solver will be prepared first.
*
* Returns: %TRUE ok success, %FALSE on error.
*/
gboolean
gnm_solver_start (GnmSolver *sol, WorkbookControl *wbc, GError **err)
{
......@@ -922,6 +943,15 @@ gnm_solver_start (GnmSolver *sol, WorkbookControl *wbc, GError **err)
return res;
}
/**
* gnm_solver_stop:
* @sol: solver
* @err: location to store error
*
* Terminate the currently-running solver.
*
* Returns: %TRUE ok success, %FALSE on error.
*/
gboolean
gnm_solver_stop (GnmSolver *sol, GError **err)
{
......@@ -1700,16 +1730,6 @@ gnm_solver_class_init (GObjectClass *object_class)
gnm__BOOLEAN__POINTER,
G_TYPE_BOOLEAN, 1,
G_TYPE_POINTER);
solver_signals[SOL_SIG_CHILD_EXIT] =
g_signal_new ("child-exit",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GnmSolverClass, child_exit),
NULL, NULL,
gnm__VOID__BOOLEAN_INT,
G_TYPE_NONE, 2,
G_TYPE_BOOLEAN, G_TYPE_INT);
}
GSF_CLASS (GnmSolver, gnm_solver,
......@@ -1743,6 +1763,13 @@ GSF_CLASS (GnmSolverResult, gnm_solver_result,
static GObjectClass *gnm_sub_solver_parent_class;
enum {
SUB_SOL_SIG_CHILD_EXIT,
SUB_SOL_SIG_LAST
};
static guint sub_solver_signals[SUB_SOL_SIG_LAST] = { 0 };
void
gnm_sub_solver_clear (GnmSubSolver *subsol)
{
......@@ -1862,7 +1889,7 @@ cb_child_exit (G_GNUC_UNUSED GPid pid, gint status, GnmSubSolver *subsol)
status);
}
g_signal_emit (subsol, solver_signals[SOL_SIG_CHILD_EXIT], 0,
g_signal_emit (subsol, sub_solver_signals[SUB_SOL_SIG_CHILD_EXIT], 0,
normal, code);
if (subsol->child_pid) {
......@@ -2081,6 +2108,16 @@ gnm_sub_solver_class_init (GObjectClass *object_class)
object_class->dispose = gnm_sub_solver_dispose;
object_class->finalize = gnm_sub_solver_finalize;
sub_solver_signals[SUB_SOL_SIG_CHILD_EXIT] =
g_signal_new ("child-exit",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GnmSubSolverClass, child_exit),
NULL, NULL,
gnm__VOID__BOOLEAN_INT,
G_TYPE_NONE, 2,
G_TYPE_BOOLEAN, G_TYPE_INT);
}
GSF_CLASS (GnmSubSolver, gnm_sub_solver,
......@@ -2088,6 +2125,117 @@ GSF_CLASS (GnmSubSolver, gnm_sub_solver,
/* ------------------------------------------------------------------------- */
static GObjectClass *gnm_iter_solver_parent_class;
enum {
ITER_SOL_SIG_ITERATE,
ITER_SOL_SIG_LAST
};
static guint iter_solver_signals[ITER_SOL_SIG_LAST] = { 0 };
static void
gnm_iter_solver_clear (GnmIterSolver *isol)
{
if (isol->idle_tag) {
g_source_remove (isol->idle_tag);
isol->idle_tag = 0;
}
}
static void
gnm_iter_solver_dispose (GObject *obj)
{
GnmIterSolver *isol = GNM_ITER_SOLVER (obj);
gnm_iter_solver_clear (isol);
gnm_iter_solver_parent_class->dispose (obj);
}
static void
gnm_iter_solver_finalize (GObject *obj)
{
GnmIterSolver *isol = GNM_ITER_SOLVER (obj);
(void)isol;
gnm_iter_solver_parent_class->finalize (obj);
}
static void
gnm_iter_solver_init (GnmIterSolver *isol)
{
}
static gint
gnm_iter_solver_idle (gpointer data)
{
GnmIterSolver *isol = data;
GnmSolver *sol = &isol->parent;
g_signal_emit (isol, iter_solver_signals[ITER_SOL_SIG_ITERATE], 0);
isol->iterations++;
if (gnm_solver_finished (sol)) {
isol->idle_tag = 0;
return FALSE;
} else {
/* Call again. */
return TRUE;
}
}
static gboolean
gnm_iter_solver_start (GnmSolver *solver, WorkbookControl *wbc, GError **err)
{
GnmIterSolver *isol = GNM_ITER_SOLVER (solver);
g_return_val_if_fail (isol->idle_tag == 0, FALSE);
isol->idle_tag = g_idle_add (gnm_iter_solver_idle, solver);
gnm_solver_set_status (solver, GNM_SOLVER_STATUS_RUNNING);
return TRUE;
}
static gboolean
gnm_iter_solver_stop (GnmSolver *solver, GError **err)
{
GnmIterSolver *isol = GNM_ITER_SOLVER (solver);
GnmSolver *sol = &isol->parent;
gnm_iter_solver_clear (isol);
gnm_solver_set_status (sol, GNM_SOLVER_STATUS_CANCELLED);
return TRUE;
}
static void
gnm_iter_solver_class_init (GObjectClass *object_class)
{
GnmSolverClass *sclass = (GnmSolverClass *)object_class;
gnm_iter_solver_parent_class = g_type_class_peek_parent (object_class);
object_class->dispose = gnm_iter_solver_dispose;
object_class->finalize = gnm_iter_solver_finalize;
sclass->start = gnm_iter_solver_start;
sclass->stop = gnm_iter_solver_stop;
iter_solver_signals[ITER_SOL_SIG_ITERATE] =
g_signal_new ("iterate",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GnmIterSolverClass, iterate),
NULL, NULL,
gnm__VOID__VOID,
G_TYPE_NONE, 0);
}
GSF_CLASS (GnmIterSolver, gnm_iter_solver,
gnm_iter_solver_class_init, gnm_iter_solver_init, GNM_SOLVER_TYPE)
/* ------------------------------------------------------------------------- */
static GObjectClass *gnm_solver_factory_parent_class;
static void
......
......@@ -210,7 +210,6 @@ typedef struct {
gboolean (*start) (GnmSolver *solver,
WorkbookControl *wbc, GError **err);
gboolean (*stop) (GnmSolver *solver, GError **err);
void (*child_exit) (GnmSolver *solver, gboolean normal, int code);
} GnmSolverClass;
GType gnm_solver_get_type (void);
......@@ -274,6 +273,8 @@ typedef struct {
typedef struct {
GnmSolverClass parent_class;
void (*child_exit) (GnmSubSolver *subsol, gboolean normal, int code);
} GnmSubSolverClass;
GType gnm_sub_solver_get_type (void);
......@@ -301,6 +302,29 @@ char *gnm_sub_solver_locate_binary (const char *binary, const char *solver,
const char *url,
WBCGtk *wbcg);
/* ------------------------------------------------------------------------- */
/* Solver subclass for iterative in-process solvers. */
#define GNM_ITER_SOLVER_TYPE (gnm_iter_solver_get_type ())
#define GNM_ITER_SOLVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_ITER_SOLVER_TYPE, GnmIterSolver))
#define GNM_IS_ITER_SOLVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNM_ITER_SOLVER_TYPE))
typedef struct {
GnmSolver parent;
guint64 iterations;
guint idle_tag;
} GnmIterSolver;
typedef struct {
GnmSolverClass parent_class;
void (*iterate) (GnmIterSolver *isol);
} GnmIterSolverClass;
GType gnm_iter_solver_get_type (void);
/* ------------------------------------------------------------------------- */
#define GNM_SOLVER_FACTORY_TYPE (gnm_solver_factory_get_type ())
......
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