[gnumeric] Solver: add beginnings of solver reports.



commit 56d5724e3036cf89dad2d63728a5e2bf214b3d48
Author: Morten Welinder <terra gnome org>
Date:   Sun Jul 8 23:42:20 2012 -0400

    Solver: add beginnings of solver reports.
    
    This work is incomplete.  Notably it's missing undo support.
    But people seem to like solver reports so let's get going.

 src/dialogs/ChangeLog       |    5 +
 src/dialogs/dialog-solver.c |   13 +++
 src/tools/ChangeLog         |    7 ++
 src/tools/dao.c             |   14 ++-
 src/tools/gnm-solver.c      |  248 +++++++++++++++++++++++++++++++++++++++++++
 src/tools/gnm-solver.h      |    2 +
 6 files changed, 284 insertions(+), 5 deletions(-)
---
diff --git a/src/dialogs/ChangeLog b/src/dialogs/ChangeLog
index 91b1497..d36545c 100644
--- a/src/dialogs/ChangeLog
+++ b/src/dialogs/ChangeLog
@@ -1,3 +1,8 @@
+2012-07-08  Morten Welinder  <terra gnome org>
+
+	* dialog-solver.c (run_solver): Create a report when requested.
+	No undo support yet.
+
 2012-06-25  Morten Welinder <terra gnome org>
 
 	* Release 1.11.4
diff --git a/src/dialogs/dialog-solver.c b/src/dialogs/dialog-solver.c
index 090d8e6..9f261a8 100644
--- a/src/dialogs/dialog-solver.c
+++ b/src/dialogs/dialog-solver.c
@@ -630,6 +630,16 @@ cb_timer_tick (SolverState *state)
 	return TRUE;
 }
 
+static void
+create_report (GnmSolver *sol, SolverState *state)
+{
+	Sheet *sheet = state->sheet;
+	char *base = g_strdup_printf ("%s Report", sheet->name_unquoted);
+	gnm_solver_create_report (sol, base);
+	g_free (base);
+}
+
+
 static GnmSolverResult *
 run_solver (SolverState *state, GnmSolverParameters *param)
 {
@@ -800,6 +810,9 @@ run_solver (SolverState *state, GnmSolverParameters *param)
 		GOUndo *redo;
 
 		gnm_solver_store_result (sol);
+		if (param->options.program_report)
+			create_report (sol, state);
+
 		redo = clipboard_copy_range_undo (sr.sheet, &sr.range);
 		cmd_generic (WORKBOOK_CONTROL (state->wbcg),
 			     _("Running solver"),
diff --git a/src/tools/ChangeLog b/src/tools/ChangeLog
index 866c213..a969be6 100644
--- a/src/tools/ChangeLog
+++ b/src/tools/ChangeLog
@@ -1,3 +1,10 @@
+2012-07-08  Morten Welinder  <terra gnome org>
+
+	* gnm-solver.c (gnm_solver_create_report): New function.
+	Incomplete.
+
+	* dao.c (dao_prepare_output): Improve this for missing ->wbc.
+
 2012-06-30  Morten Welinder  <terra gnome org>
 
 	* gnm-solver.c (gnm_sub_solver_locate_binary): Mark various
diff --git a/src/tools/dao.c b/src/tools/dao.c
index 53f4035..f7d433d 100644
--- a/src/tools/dao.c
+++ b/src/tools/dao.c
@@ -85,6 +85,7 @@ dao_init (data_analysis_output_t *dao,
 	dao->retain_format     = FALSE;
 	dao->retain_comments   = FALSE;
 	dao->put_formulas      = FALSE;
+	dao->wbc               = NULL;
 	dao->sos               = NULL;
         dao->omit_so           = FALSE;
 
@@ -248,8 +249,10 @@ dao_prepare_output (WorkbookControl *wbc, data_analysis_output_t *dao,
 		dao->wbc = wbc;
 
 	if (dao->type == NewSheetOutput) {
-		Sheet *old_sheet = wb_control_cur_sheet (dao->wbc);
-		Workbook *wb = wb_control_get_workbook (dao->wbc);
+		Sheet *old_sheet = dao->wbc
+			? wb_control_cur_sheet (dao->wbc)
+			: dao->sheet;
+		Workbook *wb = old_sheet->workbook;
 		char *name_with_counter = g_strdup_printf ("%s (1)", name);
 		unique_name = workbook_sheet_get_free_name
 			(wb, name_with_counter, FALSE, TRUE);
@@ -270,7 +273,9 @@ dao_prepare_output (WorkbookControl *wbc, data_analysis_output_t *dao,
 		workbook_sheet_attach (wb, dao->sheet);
 		dao->wbc = wb_control_wrapper_new (dao->wbc, NULL, wb, NULL);
 	}
-	wb_view_sheet_focus (wb_control_view (dao->wbc), dao->sheet);
+
+	if (dao->wbc)
+		wb_view_sheet_focus (wb_control_view (dao->wbc), dao->sheet);
 
 	if (dao->rows == 0 || (dao->rows == 1 && dao->cols == 1))
 		dao->rows = gnm_sheet_get_max_rows (dao->sheet) - dao->start_row;
@@ -678,8 +683,7 @@ dao_autofit_columns (data_analysis_output_t *dao)
  * @row2:
  * @style:
  *
- * sets the given cell range to bold
- *
+ * Applies a partial style to the given region.
  *
  **/
 static void
diff --git a/src/tools/gnm-solver.c b/src/tools/gnm-solver.c
index 0a61a55..e27ac9d 100644
--- a/src/tools/gnm-solver.c
+++ b/src/tools/gnm-solver.c
@@ -11,6 +11,7 @@
 #include "workbook-view.h"
 #include "workbook-control.h"
 #include "gnm-marshalers.h"
+#include "dao.h"
 #include <gsf/gsf-impl-utils.h>
 #include <gsf/gsf-output-stdio.h>
 #include <glib/gi18n-lib.h>
@@ -618,6 +619,24 @@ gnm_solver_param_valid (GnmSolverParameters const *sp, GError **err)
 	return TRUE;
 }
 
+static char *
+gnm_solver_param_cell_name (GnmSolverParameters const *sp, GnmCell const *cell)
+{
+	GnmConventionsOut out;
+	GnmCellRef cr;
+	GnmParsePos pp;
+
+	gnm_cellref_init (&cr, cell->base.sheet,
+			  cell->pos.col, cell->pos.row,
+			  TRUE);
+	out.accum = g_string_new (NULL);
+	out.pp = parse_pos_init_sheet (&pp, sp->sheet);
+	out.convs = sheet_get_conventions (sp->sheet);
+	cellref_as_string (&out, &cr, cell->base.sheet == sp->sheet);
+	return g_string_free (out.accum, FALSE);
+}
+
+
 static GObject *
 gnm_solver_param_constructor (GType type,
 			      guint n_construct_properties,
@@ -1229,6 +1248,235 @@ 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)
+{
+	if (!cell)
+		return FALSE;
+
+	if (sr->sheet != cell->base.sheet ||
+	    !range_contains (&sr->range, cell->pos.col, cell->pos.row)) {
+		/* If the expression is just =X42 thenm look at X42 instead.
+		   This is because the mps loader uses such a level of
+		   indirection.  Note: we follow only one such step.  */
+		GnmCellRef const *cr = gnm_expr_top_get_cellref (cell->base.texpr);
+		GnmCellRef cr2;
+		GnmCell const *new_cell;
+		GnmEvalPos ep;
+
+		if (!cr)
+			return FALSE;
+
+		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);
+
+	}
+
+	*px = cell->pos.col - sr->range.start.col;
+	*py = cell->pos.row - sr->range.start.row;
+	return TRUE;
+}
+
+static gboolean
+cell_is_constant (GnmCell const *cell, gnm_float *pc)
+{
+	if (!cell)
+		return TRUE;
+
+	if (cell->base.texpr)
+		return FALSE;
+
+	*pc = value_get_as_float (cell->value);
+	return TRUE;
+}
+
+#define SET_LOWER(l_)						\
+  do {								\
+	  (*pmin)[y * w + x] = MAX ((*pmin)[y * w + x], (l_));	\
+  } while (0)
+
+#define SET_UPPER(l_)						\
+  do {								\
+	  (*pmax)[y * w + x] = MIN ((*pmax)[y * w + x], (l_));	\
+  } while (0)
+
+
+
+static void
+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;
+
+	*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;
+		}
+	}
+
+	for (l = params->constraints; l; l = l->next) {
+		GnmSolverConstraint *c = l->data;
+		int i;
+		gnm_float cl, cr;
+		GnmCell *lhs, *rhs;
+
+		for (i = 0;
+		     gnm_solver_constraint_get_part (c, params, i,
+						     &lhs, &cl,
+						     &rhs, &cr);
+		     i++) {
+			if (!cell_in_cr (lhs, &sr, TRUE, &x, &y))
+				continue;
+			if (!cell_is_constant (rhs, &cr))
+				continue;
+
+			switch (c->type) {
+			case GNM_SOLVER_INTEGER:
+				break;
+			case GNM_SOLVER_BOOLEAN:
+				SET_LOWER (0.0);
+				SET_UPPER (1.0);
+				break;
+			case GNM_SOLVER_LE:
+				SET_UPPER (cr);
+				break;
+			case GNM_SOLVER_GE:
+				SET_LOWER (cr);
+				break;
+			case GNM_SOLVER_EQ:
+				SET_LOWER (cr);
+				SET_UPPER (cr);
+				break;
+
+			default:
+				g_assert_not_reached ();
+				break;
+			}
+		}
+	}
+
+}
+
+#undef SET_LOWER
+#undef SET_UPPER
+
+#define ADD_HEADER(txt_) do {			\
+	dao_set_cell (dao, 0, R, (txt_));	\
+	dao_set_bold (dao, 0, R, 0, R);		\
+	R++;					\
+} while (0)
+
+#define AT_LIMIT(s_,l_) \
+  (gnm_finite (l_) ? gnm_abs ((s_) - (l_)) <= (gnm_abs ((s_)) + gnm_abs ((l_))) / 1e10 : (s_) == (l_))
+
+void
+gnm_solver_create_report (GnmSolver *solver, const char *name)
+{
+	GnmSolverParameters *params = solver->params;
+	int R = 0;
+	GnmValue const *vinput;
+	data_analysis_output_t *dao;
+	char *tmp;
+
+	dao = dao_init_new_sheet (NULL);
+	dao->sheet = params->sheet;
+	dao_prepare_output (NULL, dao, name);
+
+	/* ---------------------------------------- */
+
+	ADD_HEADER (_("Target"));
+	tmp = gnm_solver_param_cell_name (params,
+					  gnm_solver_param_get_target_cell (params));
+	dao_set_cell (dao, 1, R, tmp);
+	g_free (tmp);
+
+	dao_set_cell_float (dao, 2, R, solver->result->value);
+	R++;
+	R++;
+
+	/* ---------------------------------------- */
+
+	vinput = gnm_solver_param_get_input (params);
+	if (vinput) {
+		int x, y, w, h;
+		GnmSheetRange sr;
+		gnm_float *pmin, *pmax;
+
+		ADD_HEADER (_("Variables"));
+
+		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++) {
+				int vcol = sr.range.start.col + x;
+				int vrow = 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);
+				dao_set_cell (dao, 1, R, cell_coord_name (vcol, vrow));
+				dao_set_cell_value (dao, 2, R, value_dup (vs));
+				if (gnm_finite (m))
+					dao_set_cell_float (dao, 3, R, m);
+				else
+					dao_set_cell (dao, 3, R, "-");
+
+				if (gnm_finite (M))
+					dao_set_cell_float (dao, 4, R, M);
+				else
+					dao_set_cell (dao, 4, R, "-");
+
+				if (AT_LIMIT (s, m) || AT_LIMIT (s, M))
+					dao_set_cell (dao, 5, R, _("At limit"));
+
+
+				if (s < m || s > M)
+					dao_set_cell (dao, 6, R, _("Outside bounds"));
+
+				R++;
+			}
+		}
+
+		g_free (pmin);
+		g_free (pmax);
+		R++;
+	}
+
+	/* ---------------------------------------- */
+
+	dao_free (dao);
+}
+
+#undef AT_LIMIT
+#undef ADD_HEADER
+
+
 static void
 gnm_solver_class_init (GObjectClass *object_class)
 {
diff --git a/src/tools/gnm-solver.h b/src/tools/gnm-solver.h
index 162ec16..3fb1281 100644
--- a/src/tools/gnm-solver.h
+++ b/src/tools/gnm-solver.h
@@ -228,6 +228,8 @@ void gnm_solver_set_reason (GnmSolver *solver, const char *reason);
 
 void gnm_solver_store_result (GnmSolver *solver);
 
+void gnm_solver_create_report (GnmSolver *solver, const char *name);
+
 double gnm_solver_elapsed (GnmSolver *solver);
 
 gboolean gnm_solver_check_timeout (GnmSolver *solver);



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