[gnumeric] solver: improve undo/redo.
- From: Morten Welinder <mortenw src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gnumeric] solver: improve undo/redo.
- Date: Sun, 15 Nov 2009 05:03:36 +0000 (UTC)
commit 7bd121851585db43ddf1ae92739d6c53022d6546
Author: Morten Welinder <terra gnome org>
Date: Sun Nov 15 00:03:13 2009 -0500
solver: improve undo/redo.
NEWS | 2 +
src/commands.c | 4 +-
src/commands.h | 3 +-
src/dialogs/dialog-solver.c | 303 ++++++++++++++++++++++++++-----------------
src/expr.c | 2 +
src/sheet.c | 10 ++
src/sheet.h | 2 +
src/tools/gnm-solver.c | 103 ++++++++++++---
src/tools/gnm-solver.h | 6 +
9 files changed, 294 insertions(+), 141 deletions(-)
---
diff --git a/NEWS b/NEWS
index 0e234d0..610c85b 100644
--- a/NEWS
+++ b/NEWS
@@ -22,6 +22,8 @@ Morten:
* Use external solver program.
* Allow constants on solver constraints' rhs. [#369728]
* Fix extreme case for R.PGAMMA.
+ * Fix solver undo/redo.
+ * Make solver parameter changes always persist. [#440664]
--------------------------------------------------------------------------
Gnumeric 1.9.15
diff --git a/src/commands.c b/src/commands.c
index 26a0ab4..ba874ea 100644
--- a/src/commands.c
+++ b/src/commands.c
@@ -6321,7 +6321,7 @@ cmd_solver_finalize (GObject *cmd)
}
gboolean
-cmd_solver (WorkbookControl *wbc, GOUndo *undo, GOUndo *redo)
+cmd_solver (WorkbookControl *wbc, const char *txt, GOUndo *undo, GOUndo *redo)
{
CmdSolver *me;
@@ -6332,7 +6332,7 @@ cmd_solver (WorkbookControl *wbc, GOUndo *undo, GOUndo *redo)
me->cmd.sheet = NULL;
me->cmd.size = 1;
- me->cmd.cmd_descriptor = g_strdup_printf (_("Solver"));
+ me->cmd.cmd_descriptor = g_strdup (txt);
me->undo = undo;
me->redo = redo;
diff --git a/src/commands.h b/src/commands.h
index 8649365..c93de89 100644
--- a/src/commands.h
+++ b/src/commands.h
@@ -128,7 +128,8 @@ gboolean cmd_text_to_columns (WorkbookControl *wbc,
GnmRange const *target, Sheet *target_sheet,
GnmCellRegion *content);
-gboolean cmd_solver (WorkbookControl *wbc, GOUndo *undo, GOUndo *redo);
+gboolean cmd_solver (WorkbookControl *wbc, const char *text,
+ GOUndo *undo, GOUndo *redo);
gboolean cmd_goal_seek (WorkbookControl *wbc,
GnmCell *cell, GnmValue *ov, GnmValue *nv);
diff --git a/src/dialogs/dialog-solver.c b/src/dialogs/dialog-solver.c
index b28e9ba..125ef90 100644
--- a/src/dialogs/dialog-solver.c
+++ b/src/dialogs/dialog-solver.c
@@ -75,7 +75,7 @@ typedef struct {
GtkComboBox *type_combo;
GtkComboBox *algorithm_combo;
GtkTreeView *constraint_list;
- GnmSolverConstraint *constr;
+ GnmSolverConstraint *constr;
GtkWidget *warning_dialog;
struct {
@@ -92,6 +92,8 @@ typedef struct {
Sheet *sheet;
WBCGtk *wbcg;
+
+ GnmSolverParameters *orig_params;
} SolverState;
@@ -291,6 +293,8 @@ fill_algorithm_combo (SolverState *state, GnmSolverModelType type)
GtkListStore *store =
gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
GSList *solvers, *l;
+ int sel = 0, i;
+ GnmSolverParameters *param =state->sheet->solver_parameters;
gtk_combo_box_set_model (state->algorithm_combo, GTK_TREE_MODEL (store));
@@ -301,17 +305,20 @@ fill_algorithm_combo (SolverState *state, GnmSolverModelType type)
continue;
l = g_slist_prepend (l, entry);
}
- solvers = l;
+ solvers = g_slist_reverse (l);
gtk_widget_set_sensitive (GTK_WIDGET (state->solve_button),
solvers != NULL);
if (!solvers)
return FALSE;
- for (l = solvers; l; l = l->next) {
+ for (l = solvers, i = 0; l; l = l->next, i++) {
GnmSolverFactory *factory = l->data;
GtkTreeIter iter;
+ if (param->options.algorithm == factory)
+ sel = i;
+
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
0, factory->name,
@@ -320,7 +327,7 @@ fill_algorithm_combo (SolverState *state, GnmSolverModelType type)
}
g_slist_free (solvers);
- gtk_combo_box_set_active (state->algorithm_combo, 0);
+ gtk_combo_box_set_active (state->algorithm_combo, sel);
return TRUE;
}
@@ -347,10 +354,122 @@ cb_dialog_model_type_clicked (G_GNUC_UNUSED GtkWidget *button,
}
static void
+free_state (SolverState *state)
+{
+ if (state->orig_params)
+ g_object_unref (state->orig_params);
+ g_free (state);
+}
+
+static GOUndo *
+set_params (Sheet *sheet, GnmSolverParameters *params)
+{
+ return go_undo_binary_new
+ (sheet, g_object_ref (params),
+ (GOUndoBinaryFunc)gnm_sheet_set_solver_params,
+ NULL, g_object_unref);
+}
+
+#define GET_BOOL_ENTRY(name_, field_) \
+do { \
+ GtkWidget *w_ = glade_xml_get_widget (state->gui, (name_)); \
+ param->field_ = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w_)); \
+} while (0)
+
+static void
+extract_settings (SolverState *state)
+{
+ GnmSolverParameters *param = state->sheet->solver_parameters;
+ GtkTreeIter iter;
+ GnmCell *target_cell;
+ GnmValue *target_range;
+ GnmValue *input_range;
+ GnmSolverFactory *factory = NULL;
+ gboolean dual_program;
+
+ target_range = gnm_expr_entry_parse_as_value (state->target_entry,
+ state->sheet);
+ input_range = gnm_expr_entry_parse_as_value (state->change_cell_entry,
+ state->sheet);
+
+ gnm_solver_param_set_input (param, input_range);
+
+ gnm_solver_param_set_target (param,
+ target_range
+ ? &target_range->v_range.cell.a
+ : NULL);
+ target_cell = gnm_solver_param_get_target_cell (param);
+
+ param->problem_type =
+ gnumeric_glade_group_value (state->gui, problem_type_group);
+ param->options.model_type =
+ gnumeric_glade_group_value (state->gui, model_type_group);
+
+ gtk_combo_box_get_active_iter (state->algorithm_combo, &iter);
+ gtk_tree_model_get (gtk_combo_box_get_model (state->algorithm_combo),
+ &iter, 1, &factory, -1);
+ param->options.algorithm = factory;
+
+ param->options.automatic_scaling = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON (glade_xml_get_widget
+ (state->gui, "autoscale_button")));
+
+ param->options.max_iter = gtk_spin_button_get_value
+ (GTK_SPIN_BUTTON (state->max_iter_entry));
+ param->options.max_time_sec = gtk_spin_button_get_value
+ (GTK_SPIN_BUTTON (state->max_time_entry));
+
+ GET_BOOL_ENTRY ("non_neg_button", options.assume_non_negative);
+ GET_BOOL_ENTRY ("all_int_button", options.assume_discrete);
+ GET_BOOL_ENTRY ("answer", options.answer_report);
+ GET_BOOL_ENTRY ("sensitivity", options.sensitivity_report);
+ GET_BOOL_ENTRY ("limits", options.limits_report);
+ GET_BOOL_ENTRY ("performance", options.performance_report);
+ GET_BOOL_ENTRY ("program", options.program_report);
+
+ g_free (param->options.scenario_name);
+ param->options.scenario_name = g_strdup
+ (gtk_entry_get_text (GTK_ENTRY (state->scenario_name_entry)));
+
+ GET_BOOL_ENTRY ("optimal_scenario", options.add_scenario);
+
+ dual_program = FALSE;
+ param->options.dual_program_report = dual_program;
+
+ value_release (target_range);
+}
+
+#undef GET_BOOL_ENTRY
+
+static void
+check_for_changed_options (SolverState *state)
+{
+ Sheet *sheet = state->sheet;
+
+ if (!gnm_solver_param_equal (sheet->solver_parameters,
+ state->orig_params)) {
+ GOUndo *undo = set_params (sheet, state->orig_params);
+ GOUndo *redo = set_params (sheet, sheet->solver_parameters);
+ cmd_solver (WORKBOOK_CONTROL (state->wbcg),
+ _("Changing solver parameters"),
+ undo, redo);
+
+ g_object_unref (state->orig_params);
+ state->orig_params =
+ gnm_solver_param_dup (sheet->solver_parameters,
+ sheet);
+ }
+}
+
+static void
cb_dialog_solver_destroy (SolverState *state)
{
g_return_if_fail (state != NULL);
+ extract_settings (state);
+
+ check_for_changed_options (state);
+
if (state->gui != NULL) {
g_object_unref (G_OBJECT (state->gui));
state->gui = NULL;
@@ -359,7 +478,6 @@ cb_dialog_solver_destroy (SolverState *state)
wbcg_edit_finish (state->wbcg, WBC_EDIT_REJECT, NULL);
state->dialog = NULL;
- g_free (state);
}
static void
@@ -621,7 +739,9 @@ run_solver (SolverState *state, GnmSolverParameters *param)
gnm_solver_store_result (sol);
redo = clipboard_copy_range_undo (sr.sheet, &sr.range);
- cmd_solver (WORKBOOK_CONTROL (state->wbcg), undo, redo);
+ cmd_solver (WORKBOOK_CONTROL (state->wbcg),
+ _("Running solver"),
+ undo, redo);
res = g_object_ref (sol->result);
undo = redo = NULL;
break;
@@ -670,90 +790,16 @@ static void
cb_dialog_solve_clicked (G_GNUC_UNUSED GtkWidget *button,
SolverState *state)
{
- GnmSolverResult *res;
- GnmValue *target_range;
- GnmValue *input_range;
- gboolean answer, sensitivity, limits, performance;
- gboolean program, dual_program;
+ GnmSolverResult *res;
+ GnmSolverParameters *param = state->sheet->solver_parameters;
GError *err = NULL;
- GnmSolverParameters *param;
- GtkTreeIter iter;
- GnmCell *target_cell;
- GnmSolverFactory *factory = NULL;
-
- param = state->sheet->solver_parameters;
- if (state->warning_dialog != NULL)
+ if (state->warning_dialog != NULL) {
gtk_widget_destroy (state->warning_dialog);
+ state->warning_dialog = NULL;
+ }
- target_range = gnm_expr_entry_parse_as_value (state->target_entry,
- state->sheet);
- input_range = gnm_expr_entry_parse_as_value (state->change_cell_entry,
- state->sheet);
-
- gnm_solver_param_set_input (param, input_range);
-
- gnm_solver_param_set_target (param,
- target_range
- ? &target_range->v_range.cell.a
- : NULL);
- target_cell = gnm_solver_param_get_target_cell (param);
-
- param->problem_type =
- gnumeric_glade_group_value (state->gui, problem_type_group);
- param->options.model_type =
- gnumeric_glade_group_value (state->gui, model_type_group);
-
- gtk_combo_box_get_active_iter (state->algorithm_combo, &iter);
- gtk_tree_model_get (gtk_combo_box_get_model (state->algorithm_combo),
- &iter, 1, &factory, -1);
- param->options.algorithm = factory;
-
- param->options.assume_non_negative = gtk_toggle_button_get_active
- (GTK_TOGGLE_BUTTON (glade_xml_get_widget (state->gui,
- "non_neg_button")));
- param->options.assume_discrete = gtk_toggle_button_get_active
- (GTK_TOGGLE_BUTTON (glade_xml_get_widget (state->gui,
- "all_int_button")));
- param->options.automatic_scaling = gtk_toggle_button_get_active
- (GTK_TOGGLE_BUTTON (glade_xml_get_widget
- (state->gui, "autoscale_button")));
-
- param->options.max_iter = gtk_spin_button_get_value
- (GTK_SPIN_BUTTON (state->max_iter_entry));
- param->options.max_time_sec = gtk_spin_button_get_value
- (GTK_SPIN_BUTTON (state->max_time_entry));
-
- answer = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
- glade_xml_get_widget (state->gui, "answer")));
- param->options.answer_report = answer;
-
- sensitivity = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
- glade_xml_get_widget (state->gui, "sensitivity")));
- param->options.sensitivity_report = sensitivity;
-
- limits = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
- glade_xml_get_widget (state->gui, "limits")));
- param->options.limits_report = limits;
-
- performance = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
- glade_xml_get_widget (state->gui, "performance")));
- param->options.performance_report = performance;
-
- program = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
- glade_xml_get_widget (state->gui, "program")));
- param->options.program_report = program;
-
- g_free (param->options.scenario_name);
- param->options.scenario_name = g_strdup
- (gtk_entry_get_text (GTK_ENTRY (state->scenario_name_entry)));
-
- param->options.add_scenario = gtk_toggle_button_get_active
- (GTK_TOGGLE_BUTTON (glade_xml_get_widget (state->gui,
- "optimal_scenario")));
-
- dual_program = FALSE;
- param->options.dual_program_report = dual_program;
+ extract_settings (state);
if (!gnm_solver_param_valid (param, &err)) {
GtkWidget *top = gtk_widget_get_toplevel (state->dialog);
@@ -762,6 +808,8 @@ cb_dialog_solve_clicked (G_GNUC_UNUSED GtkWidget *button,
goto out;
}
+ check_for_changed_options (state);
+
res = run_solver (state, param);
workbook_recalc (state->sheet->workbook);
@@ -786,11 +834,28 @@ cb_dialog_solve_clicked (G_GNUC_UNUSED GtkWidget *button,
}
out:
- value_release (target_range);
if (err)
g_error_free (err);
}
+static void
+bool_entry_changed (GtkToggleButton *tb, SolverState *state)
+{
+ GnmSolverParameters *param = state->sheet->solver_parameters;
+ gulong offset =
+ GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (tb), "offset"));
+ gboolean *pb =
+ (gboolean *)((char *)param + offset);
+ *pb = gtk_toggle_button_get_active (tb);
+}
+
+#define INIT_BOOL_ENTRY(name_, field_) \
+do { \
+ GtkWidget *w_ = glade_xml_get_widget (state->gui, (name_)); \
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w_), \
+ param->field_); \
+} while (0)
+
/**
* dialog_init:
@@ -802,8 +867,8 @@ cb_dialog_solve_clicked (G_GNUC_UNUSED GtkWidget *button,
static gboolean
dialog_init (SolverState *state)
{
- GtkTable *table;
- GnmSolverParameters *param;
+ GtkTable *table;
+ GnmSolverParameters *param;
GtkCellRenderer *renderer;
GtkListStore *store;
GtkTreeViewColumn *column;
@@ -904,15 +969,19 @@ dialog_init (SolverState *state)
/* Options */
state->max_iter_entry = glade_xml_get_widget (state->gui,
"max_iter_entry");
- if (state->max_iter_entry == NULL)
- return TRUE;
- gtk_entry_set_text (GTK_ENTRY (state->max_iter_entry), "200");
+ {
+ char *txt = g_strdup_printf ("%d", param->options.max_iter);
+ gtk_entry_set_text (GTK_ENTRY (state->max_iter_entry), txt);
+ g_free (txt);
+ }
state->max_time_entry = glade_xml_get_widget (state->gui,
"max_time_entry");
- if (state->max_time_entry == NULL)
- return TRUE;
- gtk_entry_set_text (GTK_ENTRY (state->max_time_entry), "30");
+ {
+ char *txt = g_strdup_printf ("%d", param->options.max_time_sec);
+ gtk_entry_set_text (GTK_ENTRY (state->max_time_entry), txt);
+ g_free (txt);
+ }
/* lhs_entry */
table = GTK_TABLE (glade_xml_get_widget (state->gui, "edit-table"));
@@ -1014,28 +1083,13 @@ dialog_init (SolverState *state)
g_free (str);
}
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
- glade_xml_get_widget(state->gui, "non_neg_button")),
- param->options.assume_non_negative);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
- glade_xml_get_widget(state->gui, "all_int_button")),
- param->options.assume_discrete);
-
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
- glade_xml_get_widget(state->gui, "answer")),
- param->options.answer_report);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
- glade_xml_get_widget(state->gui, "sensitivity")),
- param->options.sensitivity_report);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
- glade_xml_get_widget(state->gui, "limits")),
- param->options.limits_report);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
- glade_xml_get_widget(state->gui, "performance")),
- param->options.performance_report);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
- glade_xml_get_widget(state->gui, "program")),
- param->options.program_report);
+ INIT_BOOL_ENTRY ("non_neg_button", options.assume_non_negative);
+ INIT_BOOL_ENTRY ("all_int_button", options.assume_discrete);
+ INIT_BOOL_ENTRY ("answer", options.answer_report);
+ INIT_BOOL_ENTRY ("sensitivity", options.sensitivity_report);
+ INIT_BOOL_ENTRY ("limits", options.limits_report);
+ INIT_BOOL_ENTRY ("performance", options.performance_report);
+ INIT_BOOL_ENTRY ("program", options.program_report);
input = gnm_solver_param_get_input (param);
if (input != NULL)
@@ -1045,6 +1099,7 @@ dialog_init (SolverState *state)
if (target_cell)
gnm_expr_entry_load_from_text (state->target_entry,
cell_name (target_cell));
+
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
glade_xml_get_widget(state->gui, "max_button")),
param->problem_type == GNM_SOLVER_MAXIMIZE);
@@ -1066,8 +1121,6 @@ dialog_init (SolverState *state)
state->scenario_name_entry = glade_xml_get_widget
(state->gui, "scenario_name_entry");
- if (state->scenario_name_entry == NULL)
- return TRUE;
gtk_entry_set_text (GTK_ENTRY (state->scenario_name_entry),
param->options.scenario_name);
@@ -1079,8 +1132,14 @@ dialog_init (SolverState *state)
/* dialog */
wbc_gtk_attach_guru (state->wbcg, state->dialog);
+
+ g_signal_connect_swapped (G_OBJECT (state->dialog),
+ "destroy",
+ G_CALLBACK (cb_dialog_solver_destroy),
+ state);
g_object_set_data_full (G_OBJECT (state->dialog),
- "state", state, (GDestroyNotify) cb_dialog_solver_destroy);
+ "state", state,
+ (GDestroyNotify)free_state);
return FALSE;
}
@@ -1106,11 +1165,13 @@ dialog_solver (WBCGtk *wbcg, Sheet *sheet)
state->wbcg = wbcg;
state->sheet = sheet;
state->warning_dialog = NULL;
+ state->orig_params = gnm_solver_param_dup (sheet->solver_parameters,
+ sheet);
if (dialog_init (state)) {
go_gtk_notice_dialog (wbcg_toplevel (wbcg), GTK_MESSAGE_ERROR,
_("Could not create the Solver dialog."));
- g_free (state);
+ free_state (state);
return;
}
diff --git a/src/expr.c b/src/expr.c
index 7541795..c9d8bb4 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -2889,6 +2889,8 @@ gnm_expr_top_equal (GnmExprTop const *te1, GnmExprTop const *te2)
{
if (te1 == te2)
return TRUE;
+ if (te1 == NULL || te2 == NULL)
+ return FALSE;
g_return_val_if_fail (IS_GNM_EXPR_TOP (te1), FALSE);
g_return_val_if_fail (IS_GNM_EXPR_TOP (te2), FALSE);
diff --git a/src/sheet.c b/src/sheet.c
index d13dd6e..6b584ba 100644
--- a/src/sheet.c
+++ b/src/sheet.c
@@ -5768,6 +5768,16 @@ gnm_sheet_get_size2 (Sheet const *sheet, Workbook const *wb)
: workbook_get_sheet_size (wb);
}
+void
+gnm_sheet_set_solver_params (Sheet *sheet, GnmSolverParameters *param)
+{
+ g_return_if_fail (IS_SHEET (sheet));
+ g_return_if_fail (GNM_IS_SOLVER_PARAMETERS (param));
+
+ g_object_ref (param);
+ g_object_unref (sheet->solver_parameters);
+ sheet->solver_parameters = param;
+}
GHashTable *
gnm_sheet_get_sort_setups (Sheet *sheet)
diff --git a/src/sheet.h b/src/sheet.h
index 90814d0..a0a098f 100644
--- a/src/sheet.h
+++ b/src/sheet.h
@@ -279,6 +279,8 @@ gboolean sheet_range_has_heading (Sheet const *sheet, GnmRange const *src,
void gnm_sheet_foreach_name (Sheet const *sheet, GHFunc func, gpointer data);
+void gnm_sheet_set_solver_params (Sheet *sheet, GnmSolverParameters *param);
+
GHashTable *gnm_sheet_get_sort_setups (Sheet *sheet);
void gnm_sheet_add_sort_setup (Sheet *sheet, char *key, gpointer setup);
gconstpointer gnm_sheet_find_sort_setup (Sheet *sheet, char const *key);
diff --git a/src/tools/gnm-solver.c b/src/tools/gnm-solver.c
index 131c018..9048a3c 100644
--- a/src/tools/gnm-solver.c
+++ b/src/tools/gnm-solver.c
@@ -120,12 +120,22 @@ gnm_solver_constraint_dup (GnmSolverConstraint *c, Sheet *sheet)
{
GnmSolverConstraint *res = gnm_solver_constraint_new (sheet);
res->type = c->type;
- dependent_managed_set_expr (&res->lhs, res->lhs.texpr);
- dependent_managed_set_expr (&res->rhs, res->lhs.texpr);
+ dependent_managed_set_expr (&res->lhs, c->lhs.texpr);
+ dependent_managed_set_expr (&res->rhs, c->rhs.texpr);
return res;
}
gboolean
+gnm_solver_constraint_equal (GnmSolverConstraint const *a,
+ GnmSolverConstraint const *b)
+{
+ return (a->type == b->type &&
+ gnm_expr_top_equal (a->lhs.texpr, b->lhs.texpr) &&
+ (!gnm_solver_constraint_has_rhs (a) ||
+ gnm_expr_top_equal (a->rhs.texpr, b->rhs.texpr)));
+}
+
+gboolean
gnm_solver_constraint_has_rhs (GnmSolverConstraint const *c)
{
g_return_val_if_fail (c != NULL, FALSE);
@@ -403,6 +413,45 @@ gnm_solver_param_dup (GnmSolverParameters *src, Sheet *new_sheet)
return dst;
}
+gboolean
+gnm_solver_param_equal (GnmSolverParameters const *a,
+ GnmSolverParameters const *b)
+{
+ GSList *la, *lb;
+
+ if (a->sheet != b->sheet ||
+ a->problem_type != b->problem_type ||
+ !gnm_expr_top_equal (a->target.texpr, b->target.texpr) ||
+ !gnm_expr_top_equal (a->input.texpr, b->input.texpr) ||
+ a->options.max_time_sec != b->options.max_time_sec ||
+ a->options.max_iter != b->options.max_iter ||
+ a->options.algorithm != b->options.algorithm ||
+ a->options.model_type != b->options.model_type ||
+ a->options.assume_non_negative != b->options.assume_non_negative ||
+ a->options.assume_discrete != b->options.assume_discrete ||
+ a->options.automatic_scaling != b->options.automatic_scaling ||
+ a->options.show_iter_results != b->options.show_iter_results ||
+ a->options.answer_report != b->options.answer_report ||
+ a->options.sensitivity_report != b->options.sensitivity_report ||
+ a->options.limits_report != b->options.limits_report ||
+ a->options.performance_report != b->options.performance_report ||
+ a->options.program_report != b->options.program_report ||
+ a->options.dual_program_report != b->options.dual_program_report ||
+ a->options.add_scenario != b->options.add_scenario ||
+ strcmp (a->options.scenario_name, b->options.scenario_name))
+ return FALSE;
+
+ for (la = a->constraints, lb = b->constraints;
+ la && lb;
+ la = la->next, lb = lb->next) {
+ GnmSolverConstraint *ca = la->data;
+ GnmSolverConstraint *cb = lb->data;
+ if (!gnm_solver_constraint_equal (ca, cb))
+ return FALSE;
+ }
+ return la == lb;
+}
+
GnmValue const *
gnm_solver_param_get_input (GnmSolverParameters const *sp)
{
@@ -453,17 +502,19 @@ gnm_solver_param_get_input_cells (GnmSolverParameters const *sp)
void
gnm_solver_param_set_target (GnmSolverParameters *sp, GnmCellRef const *cr)
{
- GnmCellRef cr2 = *cr;
- GnmExprTop const *texpr;
-
- /* Make reference absolute to avoid tracking problems on row/col
- insert. */
- cr2.row_relative = FALSE;
- cr2.col_relative = FALSE;
-
- texpr = gnm_expr_top_new (gnm_expr_new_cellref (&cr2));
- dependent_managed_set_expr (&sp->target, texpr);
- gnm_expr_top_unref (texpr);
+ if (cr) {
+ GnmExprTop const *texpr;
+ GnmCellRef cr2 = *cr;
+ /* Make reference absolute to avoid tracking problems on row/col
+ insert. */
+ cr2.row_relative = FALSE;
+ cr2.col_relative = FALSE;
+
+ texpr = gnm_expr_top_new (gnm_expr_new_cellref (&cr2));
+ dependent_managed_set_expr (&sp->target, texpr);
+ gnm_expr_top_unref (texpr);
+ } else
+ dependent_managed_set_expr (&sp->target, NULL);
}
const GnmCellRef *
@@ -565,9 +616,12 @@ gnm_solver_param_constructor (GType type,
dependent_managed_init (&sp->target, sp->sheet);
dependent_managed_init (&sp->input, sp->sheet);
- sp->options.model_type = GNM_SOLVER_LP;
+ sp->options.model_type = GNM_SOLVER_LP;
+ sp->options.max_iter = 100;
+ sp->options.max_time_sec = 30;
sp->options.assume_non_negative = TRUE;
sp->options.scenario_name = g_strdup ("Optimal");
+ sp->options.algorithm = g_slist_nth_data (gnm_solver_db_get (), 0);
return obj;
}
@@ -1128,8 +1182,16 @@ gnm_sub_solver_spawn (GnmSubSolver *subsol,
if (io_stdout == NULL)
spflags |= G_SPAWN_STDOUT_TO_DEV_NULL;
- if (debug_solver ())
- g_printerr ("Spawning %s\n", argv[0]);
+ if (debug_solver ()) {
+ GString *msg = g_string_new ("Spawning");
+ int i;
+ for (i = 0; argv[i]; i++) {
+ g_string_append_c (msg, ' ');
+ g_string_append (msg, argv[i]);
+ }
+ g_printerr ("%s\n", msg->str);
+ g_string_free (msg, TRUE);
+ }
ok = g_spawn_async_with_pipes
(g_get_home_dir (), /* PWD */
@@ -1306,13 +1368,20 @@ gnm_solver_factory_create (GnmSolverFactory *factory,
return factory->creator (factory, param);
}
+static int
+cb_compare_factories (GnmSolverFactory *a, GnmSolverFactory *b)
+{
+ return go_utf8_collate_casefold (a->name, b->name);
+}
+
void
gnm_solver_db_register (GnmSolverFactory *factory)
{
if (debug_solver ())
g_printerr ("Registering %s\n", factory->id);
g_object_ref (factory);
- solvers = g_slist_prepend (solvers, factory);
+ solvers = g_slist_insert_sorted (solvers, factory,
+ (GCompareFunc)cb_compare_factories);
}
void
diff --git a/src/tools/gnm-solver.h b/src/tools/gnm-solver.h
index d93da60..f336d2c 100644
--- a/src/tools/gnm-solver.h
+++ b/src/tools/gnm-solver.h
@@ -70,6 +70,8 @@ GnmSolverConstraint *gnm_solver_constraint_new (Sheet *sheet);
void gnm_solver_constraint_free (GnmSolverConstraint *c);
GnmSolverConstraint *gnm_solver_constraint_dup (GnmSolverConstraint *c,
Sheet *sheet);
+gboolean gnm_solver_constraint_equal (GnmSolverConstraint const *a,
+ GnmSolverConstraint const *b);
void gnm_solver_constraint_set_old (GnmSolverConstraint *c,
GnmSolverConstraintType type,
@@ -119,6 +121,7 @@ typedef struct {
#define GNM_SOLVER_PARAMETERS_TYPE (gnm_solver_param_get_type ())
#define GNM_SOLVER_PARAMETERS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_SOLVER_PARAMETERS_TYPE, GnmSolverParameters))
+#define GNM_IS_SOLVER_PARAMETERS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNM_SOLVER_PARAMETERS_TYPE))
struct GnmSolverParameters_ {
GObject parent;
@@ -153,6 +156,9 @@ GnmSolverParameters *gnm_solver_param_new (Sheet *sheet);
GnmSolverParameters *gnm_solver_param_dup (GnmSolverParameters *src_param,
Sheet *new_sheet);
+gboolean gnm_solver_param_equal (GnmSolverParameters const *a,
+ GnmSolverParameters const *b);
+
GnmValue const *gnm_solver_param_get_input (GnmSolverParameters const *sp);
void gnm_solver_param_set_input (GnmSolverParameters *sp, GnmValue *v);
GSList *gnm_solver_param_get_input_cells (GnmSolverParameters const *sp);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]