[gnumeric] Solver: store result linearly.



commit 8ca2b54ef1c6363e256b1df423512edfb52fa798
Author: Morten Welinder <terra gnome org>
Date:   Fri May 1 18:42:47 2015 -0400

    Solver: store result linearly.
    
    Just store results as arrays of gnm_float.  This simplifies life
    all over the place.  (And, who knows, we might one day accept
    input variables that are not simply a range.)

 plugins/glpk/gnm-glpk.c       |   16 +---
 plugins/lpsolve/gnm-lpsolve.c |   27 ++----
 plugins/nlsolve/gnm-nlsolve.c |    2 +-
 src/tools/gnm-solver.c        |  188 +++++++++++++++++------------------------
 src/tools/gnm-solver.h        |    7 +-
 5 files changed, 97 insertions(+), 143 deletions(-)
---
diff --git a/plugins/glpk/gnm-glpk.c b/plugins/glpk/gnm-glpk.c
index 0fa13d1..ff5bd53 100644
--- a/plugins/glpk/gnm-glpk.c
+++ b/plugins/glpk/gnm-glpk.c
@@ -22,7 +22,6 @@
 typedef struct {
        GnmSubSolver *parent;
        char *result_filename;
-       GnmSheetRange srinput;
 } GnmGlpk;
 
 static void
@@ -74,7 +73,6 @@ gnm_glpk_read_solution (GnmGlpk *lp)
        int pstat, dstat;
        gnm_float val;
        GnmSolverResult *result;
-       int width, height;
        gboolean has_integer;
        GSList *l;
 
@@ -96,10 +94,8 @@ gnm_glpk_read_solution (GnmGlpk *lp)
        tl = GSF_INPUT_TEXTLINE (gsf_input_textline_new (input));
        g_object_unref (input);
 
-       width = range_width (&lp->srinput.range);
-       height = range_height (&lp->srinput.range);
        result = g_object_new (GNM_SOLVER_RESULT_TYPE, NULL);
-       result->solution = value_new_array_empty (width, height);
+       result->solution = g_new0 (gnm_float, sol->input_cells->len);
 
        if ((line = gsf_input_textline_utf8_gets (tl)) == NULL)
                goto fail;
@@ -142,7 +138,7 @@ gnm_glpk_read_solution (GnmGlpk *lp)
        for (c = 0; c < cols; c++) {
                gnm_float pval, dval;
                unsigned cstat;
-               int x, y;
+               unsigned idx = c;
 
                if ((line = gsf_input_textline_utf8_gets (tl)) == NULL)
                        goto fail;
@@ -153,10 +149,7 @@ gnm_glpk_read_solution (GnmGlpk *lp)
                              &cstat, &pval, &dval) != 3)
                        goto fail;
 
-               x = c % width;
-               y = c / width;
-               value_array_set (result->solution, x, y,
-                                value_new_float (pval));
+               result->solution[idx] = pval;
        }
 
        gnm_solver_set_status (sol, GNM_SOLVER_STATUS_DONE);
@@ -360,9 +353,6 @@ glpk_solver_factory (GnmSolverFactory *factory, GnmSolverParameters *params)
        GnmGlpk *lp = g_new0 (GnmGlpk, 1);
 
        lp->parent = GNM_SUB_SOLVER (res);
-       gnm_sheet_range_from_value (&lp->srinput,
-                                   gnm_solver_param_get_input (params));
-       if (!lp->srinput.sheet) lp->srinput.sheet = params->sheet;
 
        g_signal_connect (res, "prepare", G_CALLBACK (gnm_glpk_prepare), lp);
        g_signal_connect (res, "start", G_CALLBACK (gnm_glpk_start), lp);
diff --git a/plugins/lpsolve/gnm-lpsolve.c b/plugins/lpsolve/gnm-lpsolve.c
index 4e9150e..16f45e8 100644
--- a/plugins/lpsolve/gnm-lpsolve.c
+++ b/plugins/lpsolve/gnm-lpsolve.c
@@ -17,7 +17,6 @@
 typedef struct {
        GnmSubSolver *parent;
        GnmSolverResult *result;
-       GnmSheetRange srinput;
        enum { SEC_UNKNOWN, SEC_VALUES } section;
 } GnmLPSolve;
 
@@ -61,12 +60,14 @@ write_program (GnmSolver *sol, WorkbookControl *wbc, GError **err)
 static GnmSolverResult *
 gnm_lpsolve_start_solution (GnmLPSolve *lp)
 {
+       int n;
+
        g_return_val_if_fail (lp->result == NULL, NULL);
 
+       n = GNM_SOLVER (lp->parent)->input_cells->len;
+
        lp->result = g_object_new (GNM_SOLVER_RESULT_TYPE, NULL);
-       lp->result->solution = value_new_array_empty
-               (range_width (&lp->srinput.range),
-                range_height (&lp->srinput.range));
+       lp->result->solution = g_new0 (gnm_float, n);
 
        return lp->result;
 }
@@ -85,6 +86,7 @@ gnm_lpsolve_flush_solution (GnmLPSolve *lp)
 static gboolean
 cb_read_stdout (GIOChannel *channel, GIOCondition cond, GnmLPSolve *lp)
 {
+       GnmSolver *sol = GNM_SOLVER (lp->parent);
        const char obj_line_prefix[] = "Value of objective function:";
        size_t obj_line_len = sizeof (obj_line_prefix) - 1;
        const char val_header_line[] = "Actual values of the variables:";
@@ -117,10 +119,10 @@ cb_read_stdout (GIOChannel *channel, GIOCondition cond, GnmLPSolve *lp)
                        lp->section = SEC_VALUES;
                } else if (lp->section == SEC_VALUES && lp->result) {
                        GnmSolverResult *r = lp->result;
-                       int x, y;
                        double v;
                        char *space = strchr (line, ' ');
                        GnmCell *cell;
+                       int idx;
 
                        if (!space) {
                                lp->section = SEC_UNKNOWN;
@@ -128,7 +130,8 @@ cb_read_stdout (GIOChannel *channel, GIOCondition cond, GnmLPSolve *lp)
                        }
                        *space = 0;
                        cell = gnm_sub_solver_find_cell (lp->parent, line);
-                       if (!cell) {
+                       idx = gnm_solver_cell_index (sol, cell);
+                       if (idx < 0) {
                                g_printerr ("Strange cell %s in output\n",
                                            line);
                                lp->section = SEC_UNKNOWN;
@@ -136,14 +139,7 @@ cb_read_stdout (GIOChannel *channel, GIOCondition cond, GnmLPSolve *lp)
                        }
 
                        v = g_ascii_strtod (space + 1, NULL);
-                       x = cell->pos.col - lp->srinput.range.start.col;
-                       y = cell->pos.row - lp->srinput.range.start.row;
-                       if (x >= 0 &&
-                           x < value_area_get_width (r->solution, NULL) &&
-                           y >= 0 &&
-                           y < value_area_get_height (r->solution, NULL))
-                               value_array_set (r->solution, x, y,
-                                                value_new_float (v));
+                       r->solution[idx] = v;
                }
                g_free (line);
        } while (1);
@@ -354,9 +350,6 @@ lpsolve_solver_factory (GnmSolverFactory *factory, GnmSolverParameters *params)
        GnmLPSolve *lp = g_new0 (GnmLPSolve, 1);
 
        lp->parent = GNM_SUB_SOLVER (res);
-       gnm_sheet_range_from_value (&lp->srinput,
-                                   gnm_solver_param_get_input (params));
-       if (!lp->srinput.sheet) lp->srinput.sheet = params->sheet;
 
        g_signal_connect (res, "prepare", G_CALLBACK (gnm_lpsolve_prepare), lp);
        g_signal_connect (res, "start", G_CALLBACK (gnm_lpsolve_start), lp);
diff --git a/plugins/nlsolve/gnm-nlsolve.c b/plugins/nlsolve/gnm-nlsolve.c
index ee05b6d..55d55dd 100644
--- a/plugins/nlsolve/gnm-nlsolve.c
+++ b/plugins/nlsolve/gnm-nlsolve.c
@@ -465,7 +465,7 @@ rosenbrock_iter (GnmNlsolve *nl)
                        nl->smallsteps++;
                }
 
-               if (!nl->tentative && nl->smallsteps > 50) {
+               if (0 && !nl->tentative && nl->smallsteps > 50) {
                        gnm_float yk = isol->yk;
 
                        nl->tentative = 10;
diff --git a/src/tools/gnm-solver.c b/src/tools/gnm-solver.c
index 779c70a..26bd3d0 100644
--- a/src/tools/gnm-solver.c
+++ b/src/tools/gnm-solver.c
@@ -818,6 +818,11 @@ gnm_solver_dispose (GObject *obj)
                sol->input_cells = NULL;
        }
 
+       if (sol->index_from_cell) {
+               g_hash_table_destroy (sol->index_from_cell);
+               sol->index_from_cell = NULL;
+       }
+
        gnm_solver_parent_class->dispose (obj);
 }
 
@@ -826,21 +831,15 @@ gnm_solver_constructed (GObject *obj)
 {
        GnmSolver *sol = GNM_SOLVER (obj);
        GnmSolverParameters *params = sol->params;
-       GnmValue const *vinput = gnm_solver_param_get_input (params);
+       unsigned ui;
 
        sol->target = gnm_solver_param_get_target_cell (params);
 
        sol->input_cells = gnm_solver_param_get_input_cells (params);
-       if (vinput) {
-               GnmCellRef origin;
-               GnmEvalPos ep;
-
-               eval_pos_init_sheet (&ep, params->sheet);
-               gnm_cellref_make_abs (&origin, &vinput->v_range.cell.a, &ep);
-               sol->origin.col = origin.col;
-               sol->origin.row = origin.row;
-               sol->input_width = value_area_get_width (vinput, &ep);
-               sol->input_height = value_area_get_height (vinput, &ep);
+       sol->index_from_cell = g_hash_table_new (g_direct_hash, g_direct_equal);
+       for (ui = 0; ui < sol->input_cells->len; ui++) {
+               GnmCell *cell = g_ptr_array_index (sol->input_cells, ui);
+               g_hash_table_insert (sol->index_from_cell, cell, GUINT_TO_POINTER (ui));
        }
 
        gnm_solver_parent_class->constructed (obj);
@@ -1055,30 +1054,20 @@ gnm_solver_check_timeout (GnmSolver *solver)
 void
 gnm_solver_store_result (GnmSolver *sol)
 {
-       GnmValue const *vinput;
-       GnmSheetRange sr;
-       GnmValue const *solution;
+       gnm_float const *solution;
        unsigned ui, n = sol->input_cells->len;
 
        g_return_if_fail (GNM_IS_SOLVER (sol));
        g_return_if_fail (sol->result != NULL);
        g_return_if_fail (sol->result->solution);
 
-       vinput = gnm_solver_param_get_input (sol->params);
-       gnm_sheet_range_from_value (&sr, vinput);
-       if (!sr.sheet) sr.sheet = sol->params->sheet;
-
        solution = gnm_solver_has_solution (sol)
                ? sol->result->solution
                : NULL;
 
        for (ui = 0; ui < n; ui++) {
                GnmCell *cell = g_ptr_array_index (sol->input_cells, ui);
-               int x = cell->pos.col - sol->origin.col;
-               int y = cell->pos.row - sol->origin.row;
-               GnmValue *v = solution
-                       ? value_dup (value_area_fetch_x_y (solution, x, y, NULL))
-                       : value_new_error_NA (NULL);
+               GnmValue *v = solution ? value_new_float (solution[ui]) : value_new_error_NA (NULL);
                gnm_cell_set_value (cell, v);
                cell_queue_recalc (cell);
        }
@@ -1302,15 +1291,29 @@ gnm_solver_saveas (GnmSolver *solver, WorkbookControl *wbc,
        return TRUE;
 }
 
-static gboolean
-cell_in_cr (GnmCell const *cell, GnmSheetRange *sr, gboolean follow,
-           int *px, int *py)
+int
+gnm_solver_cell_index (GnmSolver *solver, GnmCell const *cell)
+{
+       gpointer idx;
+
+       if (g_hash_table_lookup_extended (solver->index_from_cell,
+                                         (gpointer)cell,
+                                         NULL, &idx))
+               return GPOINTER_TO_INT (idx);
+       else
+               return -1;
+}
+
+static int
+cell_in_cr (GnmSolver *sol, GnmCell const *cell, gboolean follow)
 {
+       int idx;
+
        if (!cell)
-               return FALSE;
+               return -1;
 
-       if (sr->sheet != cell->base.sheet ||
-           !range_contains (&sr->range, cell->pos.col, cell->pos.row)) {
+       idx = gnm_solver_cell_index (sol, cell);
+       if (idx < 0 && follow) {
                /* If the expression is just =X42 then look at X42 instead.
                   This is because the mps loader uses such a level of
                   indirection.  Note: we follow only one such step.  */
@@ -1320,19 +1323,16 @@ cell_in_cr (GnmCell const *cell, GnmSheetRange *sr, gboolean follow,
                GnmEvalPos ep;
 
                if (!cr)
-                       return FALSE;
+                       return -1;
 
                eval_pos_init_cell (&ep, cell);
                gnm_cellref_make_abs (&cr2, cr, &ep);
                new_cell = sheet_cell_get (eval_sheet (cr2.sheet, cell->base.sheet),
                                           cr2.col, cr2.row);
-               return cell_in_cr (new_cell, sr, FALSE, px, py);
-
+               return cell_in_cr (sol, new_cell, FALSE);
        }
 
-       *px = cell->pos.col - sr->range.start.col;
-       *py = cell->pos.row - sr->range.start.row;
-       return TRUE;
+       return idx;
 }
 
 static gboolean
@@ -1350,14 +1350,14 @@ cell_is_constant (GnmCell *cell, gnm_float *pc)
 }
 
 #define SET_LOWER(l_)                                          \
-  do {                                                         \
-         (*pmin)[y * w + x] = MAX ((*pmin)[y * w + x], (l_));  \
-  } while (0)
+       do {                                                    \
+               (*pmin)[idx] = MAX ((*pmin)[idx], (l_));        \
+       } while (0)
 
 #define SET_UPPER(l_)                                          \
-  do {                                                         \
-         (*pmax)[y * w + x] = MIN ((*pmax)[y * w + x], (l_));  \
-  } while (0)
+       do {                                                    \
+               (*pmax)[idx] = MIN ((*pmax)[idx], (l_));        \
+       } while (0)
 
 
 
@@ -1366,28 +1366,20 @@ gnm_solver_get_limits (GnmSolver *solver, gnm_float **pmin, gnm_float **pmax)
 {
        GnmValue const *vinput;
        GnmSolverParameters *params = solver->params;
-       int x, y, w, h;
-       GnmSheetRange sr;
        GSList *l;
+       unsigned const n = solver->input_cells->len;
+       unsigned ui;
 
        *pmin = *pmax = NULL;
 
        vinput = gnm_solver_param_get_input (params);
        if (!vinput) return;
 
-       gnm_sheet_range_from_value (&sr, vinput);
-       if (!sr.sheet) sr.sheet = params->sheet;
-       h = range_height (&sr.range);
-       w = range_width (&sr.range);
-
-       *pmin = g_new (gnm_float, h * w);
-       *pmax = g_new (gnm_float, h * w);
-
-       for (x = 0; x < w; x++) {
-               for (y = 0; y < h; y++) {
-                       (*pmin)[y * w + x] = params->options.assume_non_negative ? 0 : gnm_ninf;
-                       (*pmax)[y * w + x] = gnm_pinf;
-               }
+       *pmin = g_new (gnm_float, n);
+       *pmax = g_new (gnm_float, n);
+       for (ui = 0; ui < n; ui++) {
+               (*pmin)[ui] = params->options.assume_non_negative ? 0 : gnm_ninf;
+               (*pmax)[ui] = gnm_pinf;
        }
 
        for (l = params->constraints; l; l = l->next) {
@@ -1401,7 +1393,9 @@ gnm_solver_get_limits (GnmSolver *solver, gnm_float **pmin, gnm_float **pmax)
                                                     &lhs, &cl,
                                                     &rhs, &cr);
                     i++) {
-                       if (!cell_in_cr (lhs, &sr, TRUE, &x, &y))
+                       int idx = cell_in_cr (solver, lhs, TRUE);
+
+                       if (idx < 0)
                                continue;
                        if (!cell_is_constant (rhs, &cr))
                                continue;
@@ -1430,7 +1424,6 @@ gnm_solver_get_limits (GnmSolver *solver, gnm_float **pmin, gnm_float **pmax)
                        }
                }
        }
-
 }
 
 #undef SET_LOWER
@@ -1528,9 +1521,8 @@ gnm_solver_create_report (GnmSolver *solver, const char *name)
 
        vinput = gnm_solver_param_get_input (params);
        if (vinput) {
-               int x, y, w, h;
-               GnmSheetRange sr;
                gnm_float *pmin, *pmax;
+               unsigned ui;
 
                ADD_HEADER (_("Variables"));
 
@@ -1541,47 +1533,35 @@ gnm_solver_create_report (GnmSolver *solver, const char *name)
                dao_set_cell (dao, 5, R, _("Slack"));
                R++;
 
-               gnm_sheet_range_from_value (&sr, vinput);
-               if (!sr.sheet) sr.sheet = params->sheet;
-               h = range_height (&sr.range);
-               w = range_width (&sr.range);
-
                gnm_solver_get_limits (solver, &pmin, &pmax);
 
-               for (x = 0; x < w; x++) {
-                       for (y = 0; y < h; y++) {
-                               GnmCell *cell = sheet_cell_fetch
-                                       (sr.sheet,
-                                        sr.range.start.col + x,
-                                        sr.range.start.row + y);
-                               gnm_float m = pmin[y * w + x];
-                               gnm_float M = pmax[y * w + x];
-                               GnmValue const *vs = value_area_fetch_x_y (solver->result->solution, x, y, 
NULL);
-                               gnm_float s = value_get_as_float (vs);
-                               gnm_float slack = MIN (s - m, M - s);
-
-                               char *cname = gnm_solver_cell_name (cell, params->sheet);
-                               dao_set_cell (dao, 1, R, cname);
-                               g_free (cname);
-                               dao_set_cell_value (dao, 2, R, value_dup (vs));
-                               add_value_or_special (dao, 3, R, m);
-                               add_value_or_special (dao, 4, R, M);
-
-                               add_value_or_special (dao, 5, R, slack);
-                               if (slack < 0)
-                                       MARK_BAD (5);
-
-                               if (AT_LIMIT (s, m) || AT_LIMIT (s, M))
-                                       dao_set_cell (dao, 6, R, _("At limit"));
-
-
-                               if (s < m || s > M) {
-                                       dao_set_cell (dao, 7, R, _("Outside bounds"));
-                                       MARK_BAD (7);
-                               }
+               for (ui = 0; ui < solver->input_cells->len; ui++) {
+                       GnmCell *cell = g_ptr_array_index (solver->input_cells, ui);
+                       gnm_float m = pmin[ui];
+                       gnm_float M = pmax[ui];
+                       gnm_float s = solver->result->solution[ui];
+                       gnm_float slack = MIN (s - m, M - s);
+
+                       char *cname = gnm_solver_cell_name (cell, params->sheet);
+                       dao_set_cell (dao, 1, R, cname);
+                       g_free (cname);
+                       dao_set_cell_value (dao, 2, R, value_new_float (s));
+                       add_value_or_special (dao, 3, R, m);
+                       add_value_or_special (dao, 4, R, M);
 
-                               R++;
+                       add_value_or_special (dao, 5, R, slack);
+                       if (slack < 0)
+                               MARK_BAD (5);
+
+                       if (AT_LIMIT (s, m) || AT_LIMIT (s, M))
+                               dao_set_cell (dao, 6, R, _("At limit"));
+
+                       if (s < m || s > M) {
+                               dao_set_cell (dao, 7, R, _("Outside bounds"));
+                               MARK_BAD (7);
                        }
+
+                       R++;
                }
 
                g_free (pmin);
@@ -1875,7 +1855,7 @@ static void
 gnm_solver_result_finalize (GObject *obj)
 {
        GnmSolverResult *r = GNM_SOLVER_RESULT (obj);
-       value_release (r->solution);
+       g_free (r->solution);
        gnm_solver_result_parent_class->finalize (obj);
 }
 
@@ -2587,20 +2567,10 @@ gnm_iter_solver_set_solution (GnmIterSolver *isol)
        GnmSolver *sol = GNM_SOLVER (isol);
        GnmSolverResult *result = g_object_new (GNM_SOLVER_RESULT_TYPE, NULL);
        const int n = sol->input_cells->len;
-       int i;
 
        result->quality = GNM_SOLVER_RESULT_FEASIBLE;
        result->value = sol->flip_sign ? 0 - isol->yk : isol->yk;
-       result->solution = value_new_array_empty (sol->input_width,
-                                                 sol->input_height);
-       for (i = 0; i < n; i++) {
-               GnmCell *cell = g_ptr_array_index (sol->input_cells, i);
-               value_array_set (result->solution,
-                                cell->pos.col - sol->origin.col,
-                                cell->pos.row - sol->origin.row,
-                                value_new_float (isol->xk[i]));
-       }
-
+       result->solution = g_memdup (isol->xk, n * sizeof (gnm_float));
        g_object_set (sol, "result", result, NULL);
        g_object_unref (result);
 
diff --git a/src/tools/gnm-solver.h b/src/tools/gnm-solver.h
index 564f693..50be250 100644
--- a/src/tools/gnm-solver.h
+++ b/src/tools/gnm-solver.h
@@ -175,7 +175,7 @@ typedef struct {
        gnm_float value;
 
        /* Array value of solution, if any */
-       GnmValue *solution;
+       gnm_float *solution;
 } GnmSolverResult;
 
 typedef struct {
@@ -205,8 +205,7 @@ typedef struct {
        /* Derived information */
        GnmCell *target;
        GPtrArray *input_cells;
-       GnmCellPos origin;
-       int input_width, input_height;
+       GHashTable *index_from_cell;
 } GnmSolver;
 
 typedef struct {
@@ -252,6 +251,8 @@ gboolean gnm_solver_saveas (GnmSolver *solver, WorkbookControl *wbc,
 
 gboolean gnm_solver_debug (void);
 
+int gnm_solver_cell_index (GnmSolver *solver, GnmCell const *cell);
+
 gnm_float gnm_solver_get_target_value (GnmSolver *solver);
 void gnm_solver_set_var (GnmSolver *sol, int i, gnm_float x);
 void gnm_solver_set_vars (GnmSolver *sol, gnm_float const *xs);


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