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



commit 5406d4b777bb0c1f36c04b5992b07a8c76731725
Author: Morten Welinder <terra gnome org>
Date:   Fri Apr 24 20:56:15 2015 -0400

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

 plugins/nlsolve/ChangeLog     |    4 +
 plugins/nlsolve/gnm-nlsolve.c |   91 +++++----------------
 src/gnm-marshalers.list       |    1 +
 src/tools/ChangeLog           |    1 +
 src/tools/gnm-solver.c        |  172 ++++++++++++++++++++++++++++++++++++++---
 src/tools/gnm-solver.h        |   26 ++++++-
 6 files changed, 213 insertions(+), 82 deletions(-)
---
diff --git a/plugins/nlsolve/ChangeLog b/plugins/nlsolve/ChangeLog
index 7e87130..4dc2acc 100644
--- a/plugins/nlsolve/ChangeLog
+++ b/plugins/nlsolve/ChangeLog
@@ -1,3 +1,7 @@
+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
diff --git a/plugins/nlsolve/gnm-nlsolve.c b/plugins/nlsolve/gnm-nlsolve.c
index ece76c7..ff7401d 100644
--- a/plugins/nlsolve/gnm-nlsolve.c
+++ b/plugins/nlsolve/gnm-nlsolve.c
@@ -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);
 }
diff --git a/src/gnm-marshalers.list b/src/gnm-marshalers.list
index 52c2879..8961862 100644
--- a/src/gnm-marshalers.list
+++ b/src/gnm-marshalers.list
@@ -23,3 +23,4 @@
 BOOLEAN:POINTER
 BOOLEAN:OBJECT,POINTER
 VOID:BOOLEAN,INT
+VOID:VOID
diff --git a/src/tools/ChangeLog b/src/tools/ChangeLog
index 3b48b1f..385d9ad 100644
--- a/src/tools/ChangeLog
+++ b/src/tools/ChangeLog
@@ -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>
 
diff --git a/src/tools/gnm-solver.c b/src/tools/gnm-solver.c
index 5bdabc1..d0279f1 100644
--- a/src/tools/gnm-solver.c
+++ b/src/tools/gnm-solver.c
@@ -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
diff --git a/src/tools/gnm-solver.h b/src/tools/gnm-solver.h
index c8c7551..ca9a9b2 100644
--- a/src/tools/gnm-solver.h
+++ b/src/tools/gnm-solver.h
@@ -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);
@@ -302,6 +303,29 @@ char *gnm_sub_solver_locate_binary (const char *binary, const char *solver,
                                    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 ())
 #define GNM_SOLVER_FACTORY(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_SOLVER_FACTORY_TYPE, 
GnmSolverFactory))


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]