5 * Jukka-Pekka Iivonen <iivonen@iki.fi>
7 * (C) Copyright 2000, 2002 by Jukka-Pekka Iivonen <iivonen@iki.fi>
8 * (C) Copyright 2009-2013 Morten Welinder (terra@gnome.org)
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see <https://www.gnu.org/licenses/>.
24 #include <gnumeric-config.h>
25 #include <glib/gi18n-lib.h>
27 #include <dialogs/dialogs.h>
28 #include <dialogs/help.h>
32 #include <dialogs/tool-dialogs.h>
36 #include <sheet-view.h>
40 #include <parse-util.h>
43 #include <clipboard.h>
44 #include <selection.h>
45 #include <application.h>
46 #include <tools/gnm-solver.h>
47 #include <widgets/gnm-expr-entry.h>
49 #include <goffice/goffice.h>
51 #include <tools/scenarios.h>
53 #define SOLVER_KEY "solver-dialog"
60 GnmExprEntry
*target_entry
;
61 GnmExprEntry
*change_cell_entry
;
62 GtkWidget
*max_iter_entry
;
63 GtkWidget
*max_time_entry
;
64 GtkWidget
*gradient_order_entry
;
65 GtkWidget
*solve_button
;
66 GtkWidget
*close_button
;
67 GtkWidget
*stop_button
;
68 GtkWidget
*help_button
;
69 GtkWidget
*add_button
;
70 GtkWidget
*change_button
;
71 GtkWidget
*delete_button
;
72 GtkWidget
*scenario_name_entry
;
77 GtkComboBox
*type_combo
;
78 GtkComboBox
*algorithm_combo
;
79 GtkTreeView
*constraint_list
;
80 GnmSolverConstraint
*constr
;
81 GtkWidget
*warning_dialog
;
85 GtkWidget
*timer_widget
;
87 GtkWidget
*status_widget
;
88 GtkWidget
*problem_status_widget
;
89 GtkWidget
*objective_value_widget
;
98 GnmSolverParameters
*orig_params
;
102 static char const * const problem_type_group
[] = {
109 static char const * const model_type_group
[] = {
117 constraint_fill (GnmSolverConstraint
*c
, SolverState
*state
)
119 Sheet
*sheet
= state
->sheet
;
121 c
->type
= gtk_combo_box_get_active (state
->type_combo
);
123 gnm_solver_constraint_set_lhs
125 gnm_expr_entry_parse_as_value (state
->lhs
.entry
, sheet
));
127 gnm_solver_constraint_set_rhs
129 gnm_solver_constraint_has_rhs (c
)
130 ? gnm_expr_entry_parse_as_value (state
->rhs
.entry
, sheet
)
135 dialog_set_sec_button_sensitivity (G_GNUC_UNUSED GtkWidget
*dummy
,
138 gboolean select_ready
= (state
->constr
!= NULL
);
139 GnmSolverConstraint
*test
= gnm_solver_constraint_new (NULL
);
140 gboolean ready
, has_rhs
;
141 GnmSolverParameters
const *param
= state
->sheet
->solver_parameters
;
143 constraint_fill (test
, state
);
144 ready
= gnm_solver_constraint_valid (test
, param
);
145 has_rhs
= gnm_solver_constraint_has_rhs (test
);
146 gnm_solver_constraint_free (test
);
148 gtk_widget_set_sensitive (state
->add_button
, ready
);
149 gtk_widget_set_sensitive (state
->change_button
, select_ready
&& ready
);
150 gtk_widget_set_sensitive (state
->delete_button
, select_ready
);
151 gtk_widget_set_sensitive (GTK_WIDGET (state
->rhs
.entry
), has_rhs
);
152 gtk_widget_set_sensitive (GTK_WIDGET (state
->rhs
.label
), has_rhs
);
154 /* Return %TRUE iff the current constraint is valid. */
159 constraint_select_click (GtkTreeSelection
*Selection
,
164 GnmSolverConstraint
const *c
;
165 GnmValue
const *lhs
, *rhs
;
167 if (gtk_tree_selection_get_selected (Selection
, &store
, &iter
))
168 gtk_tree_model_get (store
, &iter
, 1, &state
->constr
, -1);
170 state
->constr
= NULL
;
171 dialog_set_sec_button_sensitivity (NULL
, state
);
173 if (state
->constr
== NULL
)
177 lhs
= gnm_solver_constraint_get_lhs (c
);
179 GnmExprTop
const *texpr
=
180 gnm_expr_top_new_constant (value_dup (lhs
));
183 gnm_expr_entry_load_from_expr
186 parse_pos_init_sheet (&pp
, state
->sheet
));
187 gnm_expr_top_unref (texpr
);
189 gnm_expr_entry_load_from_text (state
->lhs
.entry
, "");
191 rhs
= gnm_solver_constraint_get_rhs (c
);
192 if (rhs
&& gnm_solver_constraint_has_rhs (c
)) {
193 GnmExprTop
const *texpr
=
194 gnm_expr_top_new_constant (value_dup (rhs
));
197 gnm_expr_entry_load_from_expr
200 parse_pos_init_sheet (&pp
, state
->sheet
));
201 gnm_expr_top_unref (texpr
);
203 gnm_expr_entry_load_from_text (state
->rhs
.entry
, "");
205 gtk_combo_box_set_active (state
->type_combo
, c
->type
);
209 * cb_dialog_delete_clicked:
216 cb_dialog_delete_clicked (G_GNUC_UNUSED GtkWidget
*button
, SolverState
*state
)
218 if (state
->constr
!= NULL
) {
221 GnmSolverParameters
*param
= state
->sheet
->solver_parameters
;
224 g_slist_remove (param
->constraints
, state
->constr
);
225 gnm_solver_constraint_free (state
->constr
);
226 state
->constr
= NULL
;
228 if (gtk_tree_selection_get_selected (
229 gtk_tree_view_get_selection (state
->constraint_list
),
231 gtk_list_store_remove ((GtkListStore
*)store
, &iter
);
236 constraint_fill_row (SolverState
*state
, GtkListStore
*store
, GtkTreeIter
*iter
)
239 GnmSolverConstraint
*c
= state
->constr
;
241 constraint_fill (c
, state
);
243 text
= gnm_solver_constraint_as_str (c
, state
->sheet
);
244 gtk_list_store_set (store
, iter
, 0, text
, 1, c
, -1);
246 gtk_tree_selection_select_iter (gtk_tree_view_get_selection (state
->constraint_list
), iter
);
250 cb_dialog_add_clicked (SolverState
*state
)
252 if (dialog_set_sec_button_sensitivity (NULL
, state
)) {
254 GtkListStore
*store
= GTK_LIST_STORE (gtk_tree_view_get_model (state
->constraint_list
));
255 GnmSolverParameters
*param
= state
->sheet
->solver_parameters
;
257 gtk_list_store_append (store
, &iter
);
258 state
->constr
= gnm_solver_constraint_new (state
->sheet
);
259 constraint_fill_row (state
, store
, &iter
);
261 g_slist_append (param
->constraints
, state
->constr
);
266 cb_dialog_change_clicked (GtkWidget
*button
, SolverState
*state
)
268 if (state
->constr
!= NULL
) {
272 if (gtk_tree_selection_get_selected (
273 gtk_tree_view_get_selection (state
->constraint_list
),
275 constraint_fill_row (state
, (GtkListStore
*)store
, &iter
);
280 dialog_set_main_button_sensitivity (G_GNUC_UNUSED GtkWidget
*dummy
,
285 ready
= gnm_expr_entry_is_cell_ref (state
->target_entry
, state
->sheet
,
287 && gnm_expr_entry_is_cell_ref (state
->change_cell_entry
,
289 gtk_widget_set_sensitive (state
->solve_button
, ready
);
293 fill_algorithm_combo (SolverState
*state
, GnmSolverModelType type
)
295 GtkListStore
*store
=
296 gtk_list_store_new (2, G_TYPE_STRING
, G_TYPE_POINTER
);
299 GnmSolverParameters
*param
=state
->sheet
->solver_parameters
;
301 gtk_combo_box_set_model (state
->algorithm_combo
, GTK_TREE_MODEL (store
));
304 for (solvers
= gnm_solver_db_get (); solvers
; solvers
= solvers
->next
) {
305 GnmSolverFactory
*entry
= solvers
->data
;
306 if (type
!= entry
->type
)
308 l
= g_slist_prepend (l
, entry
);
310 solvers
= g_slist_reverse (l
);
312 gtk_widget_set_sensitive (GTK_WIDGET (state
->solve_button
),
317 for (l
= solvers
, i
= 0; l
; l
= l
->next
, i
++) {
318 GnmSolverFactory
*factory
= l
->data
;
321 if (param
->options
.algorithm
== factory
)
324 gtk_list_store_append (store
, &iter
);
325 gtk_list_store_set (store
, &iter
,
330 g_slist_free (solvers
);
332 gtk_combo_box_set_active (state
->algorithm_combo
, sel
);
334 g_object_unref (store
);
340 cb_dialog_model_type_clicked (G_GNUC_UNUSED GtkWidget
*button
,
343 GnmSolverModelType type
;
346 type
= gnm_gui_group_value (state
->gui
, model_type_group
);
347 any
= fill_algorithm_combo (state
, type
);
350 go_gtk_notice_nonmodal_dialog
351 (GTK_WINDOW (state
->dialog
),
352 &(state
->warning_dialog
),
354 _("Looking for a subject for your thesis? "
355 "Maybe you would like to write a solver for "
361 unref_state (SolverState
*state
)
364 if (state
->ref_count
> 0)
367 if (state
->orig_params
)
368 g_object_unref (state
->orig_params
);
373 set_params (Sheet
*sheet
, GnmSolverParameters
*params
)
375 return go_undo_binary_new
376 (sheet
, g_object_ref (params
),
377 (GOUndoBinaryFunc
)gnm_sheet_set_solver_params
,
378 NULL
, g_object_unref
);
381 #define GET_BOOL_ENTRY(name_, field_) \
383 GtkWidget *w_ = go_gtk_builder_get_widget (state->gui, (name_)); \
384 param->field_ = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w_)); \
388 extract_settings (SolverState
*state
)
390 GnmSolverParameters
*param
= state
->sheet
->solver_parameters
;
392 GnmValue
*target_range
;
393 GnmValue
*input_range
;
394 GnmSolverFactory
*factory
= NULL
;
396 target_range
= gnm_expr_entry_parse_as_value (state
->target_entry
,
398 input_range
= gnm_expr_entry_parse_as_value (state
->change_cell_entry
,
401 gnm_solver_param_set_input (param
, input_range
);
403 gnm_solver_param_set_target (param
,
405 ? &target_range
->v_range
.cell
.a
408 param
->problem_type
=
409 gnm_gui_group_value (state
->gui
, problem_type_group
);
410 param
->options
.model_type
=
411 gnm_gui_group_value (state
->gui
, model_type_group
);
413 if (gtk_combo_box_get_active_iter (state
->algorithm_combo
, &iter
)) {
414 gtk_tree_model_get (gtk_combo_box_get_model (state
->algorithm_combo
),
415 &iter
, 1, &factory
, -1);
416 gnm_solver_param_set_algorithm (param
, factory
);
418 gnm_solver_param_set_algorithm (param
, NULL
);
420 param
->options
.max_iter
= gtk_spin_button_get_value
421 (GTK_SPIN_BUTTON (state
->max_iter_entry
));
422 param
->options
.max_time_sec
= gtk_spin_button_get_value
423 (GTK_SPIN_BUTTON (state
->max_time_entry
));
424 param
->options
.gradient_order
= gtk_spin_button_get_value
425 (GTK_SPIN_BUTTON (state
->gradient_order_entry
));
427 GET_BOOL_ENTRY ("autoscale_button", options
.automatic_scaling
);
428 GET_BOOL_ENTRY ("non_neg_button", options
.assume_non_negative
);
429 GET_BOOL_ENTRY ("all_int_button", options
.assume_discrete
);
430 GET_BOOL_ENTRY ("program", options
.program_report
);
431 GET_BOOL_ENTRY ("sensitivity", options
.sensitivity_report
);
433 g_free (param
->options
.scenario_name
);
434 param
->options
.scenario_name
= g_strdup
435 (gtk_entry_get_text (GTK_ENTRY (state
->scenario_name_entry
)));
437 GET_BOOL_ENTRY ("optimal_scenario", options
.add_scenario
);
439 value_release (target_range
);
442 #undef GET_BOOL_ENTRY
445 check_for_changed_options (SolverState
*state
)
447 Sheet
*sheet
= state
->sheet
;
449 if (!gnm_solver_param_equal (sheet
->solver_parameters
,
450 state
->orig_params
)) {
451 GOUndo
*undo
= set_params (sheet
, state
->orig_params
);
452 GOUndo
*redo
= set_params (sheet
, sheet
->solver_parameters
);
453 cmd_generic (GNM_WBC (state
->wbcg
),
454 _("Changing solver parameters"),
457 g_object_unref (state
->orig_params
);
459 gnm_solver_param_dup (sheet
->solver_parameters
,
465 cb_dialog_solver_destroy (SolverState
*state
)
467 g_return_if_fail (state
!= NULL
);
469 if (state
->run
.solver
) {
470 gnm_solver_stop (state
->run
.solver
, NULL
);
471 g_object_set (state
->run
.solver
, "result", NULL
, NULL
);
474 extract_settings (state
);
476 check_for_changed_options (state
);
478 if (state
->gui
!= NULL
) {
479 g_object_unref (state
->gui
);
483 wbcg_edit_finish (state
->wbcg
, WBC_EDIT_REJECT
, NULL
);
485 state
->dialog
= NULL
;
489 cb_dialog_close_clicked (G_GNUC_UNUSED GtkWidget
*button
,
492 gtk_widget_destroy (state
->dialog
);
496 cb_stop_solver (SolverState
*state
)
498 GnmSolver
*sol
= state
->run
.solver
;
500 switch (sol
->status
) {
501 case GNM_SOLVER_STATUS_RUNNING
: {
502 gboolean ok
= gnm_solver_stop (sol
, NULL
);
504 g_warning ("Failed to stop solver!");
506 g_object_set (sol
, "result", NULL
, NULL
);
516 remove_timer_source (SolverState
*state
)
518 if (state
->run
.timer_source
) {
519 g_source_remove (state
->run
.timer_source
);
520 state
->run
.timer_source
= 0;
525 remove_objective_value_source (SolverState
*state
)
527 if (state
->run
.obj_val_source
) {
528 g_source_remove (state
->run
.obj_val_source
);
529 state
->run
.obj_val_source
= 0;
534 update_obj_value (SolverState
*state
)
536 GnmSolver
*sol
= state
->run
.solver
;
537 GnmSolverResult
*r
= sol
->result
;
541 switch (r
? r
->quality
: GNM_SOLVER_RESULT_NONE
) {
543 case GNM_SOLVER_RESULT_NONE
:
547 case GNM_SOLVER_RESULT_FEASIBLE
:
551 case GNM_SOLVER_RESULT_OPTIMAL
:
555 case GNM_SOLVER_RESULT_INFEASIBLE
:
556 txt
= _("Infeasible");
559 case GNM_SOLVER_RESULT_UNBOUNDED
:
560 txt
= _("Unbounded");
563 gtk_label_set_text (GTK_LABEL (state
->run
.problem_status_widget
), txt
);
565 if (gnm_solver_has_solution (sol
)) {
566 txt
= valtxt
= gnm_format_value (go_format_general (),
573 gtk_label_set_text (GTK_LABEL (state
->run
.objective_value_widget
),
577 remove_objective_value_source (state
);
581 cb_notify_status (SolverState
*state
)
583 GnmSolver
*sol
= state
->run
.solver
;
585 gboolean finished
= gnm_solver_finished (sol
);
586 gboolean running
= FALSE
;
588 switch (sol
->status
) {
589 case GNM_SOLVER_STATUS_READY
:
592 case GNM_SOLVER_STATUS_PREPARING
:
593 text
= _("Preparing");
595 case GNM_SOLVER_STATUS_PREPARED
:
596 text
= _("Prepared");
598 case GNM_SOLVER_STATUS_RUNNING
:
602 case GNM_SOLVER_STATUS_DONE
:
606 case GNM_SOLVER_STATUS_ERROR
:
609 case GNM_SOLVER_STATUS_CANCELLED
:
610 text
= _("Cancelled");
615 char *text2
= g_strconcat (text
,
616 " (", sol
->reason
, ")",
618 gtk_label_set_text (GTK_LABEL (state
->run
.status_widget
),
622 gtk_label_set_text (GTK_LABEL (state
->run
.status_widget
), text
);
625 gtk_widget_set_visible (state
->run
.spinner
, running
);
626 gtk_widget_set_visible (state
->stop_button
, !finished
);
627 gtk_widget_set_sensitive (state
->solve_button
, finished
);
628 gtk_widget_set_sensitive (state
->close_button
, finished
);
630 if (state
->run
.obj_val_source
)
631 update_obj_value (state
);
634 remove_timer_source (state
);
635 if (state
->run
.in_main
)
641 cb_obj_val_tick (SolverState
*state
)
643 state
->run
.obj_val_source
= 0;
644 update_obj_value (state
);
649 cb_notify_result (SolverState
*state
)
651 if (state
->run
.obj_val_source
== 0)
652 state
->run
.obj_val_source
= g_timeout_add
653 (100, (GSourceFunc
)cb_obj_val_tick
, state
);
657 cb_timer_tick (SolverState
*state
)
659 GnmSolver
*sol
= state
->run
.solver
;
660 double dsecs
= gnm_solver_elapsed (sol
);
661 int secs
= (int)CLAMP (dsecs
, 0, INT_MAX
);
662 int hh
= secs
/ 3600;
663 int mm
= secs
/ 60 % 60;
666 ? g_strdup_printf ("%02d:%02d:%02d", hh
, mm
, ss
)
667 : g_strdup_printf ("%02d:%02d", mm
, ss
);
669 gtk_label_set_text (GTK_LABEL (state
->run
.timer_widget
), txt
);
672 if (gnm_solver_check_timeout (sol
)) {
673 cb_notify_status (state
);
680 create_report (GnmSolver
*sol
, SolverState
*state
)
682 Sheet
*sheet
= state
->sheet
;
683 char *base
= g_strdup_printf (_("%s %%s Report"), sheet
->name_unquoted
);
684 gnm_solver_create_report (sol
, base
);
689 static GnmSolverResult
*
690 run_solver (SolverState
*state
, GnmSolverParameters
*param
)
696 GnmSolver
*sol
= NULL
;
697 GnmValue
const *vinput
;
698 GtkWindow
*top
= GTK_WINDOW (gtk_widget_get_toplevel (state
->dialog
));
699 GnmSolverResult
*res
= NULL
;
703 sol
= gnm_solver_factory_functional (param
->options
.algorithm
,
705 ? gnm_solver_factory_create (param
->options
.algorithm
, param
)
708 go_gtk_notice_dialog (top
, GTK_MESSAGE_ERROR
,
709 _("The chosen solver is not functional."));
713 gtk_notebook_set_current_page (GTK_NOTEBOOK (state
->notebook
), -1);
715 state
->run
.solver
= sol
;
717 vinput
= gnm_solver_param_get_input (param
);
718 gnm_sheet_range_from_value (&sr
, vinput
);
719 if (!sr
.sheet
) sr
.sheet
= param
->sheet
;
720 undo
= clipboard_copy_range_undo (sr
.sheet
, &sr
.range
);
722 g_signal_connect_swapped (G_OBJECT (sol
),
724 G_CALLBACK (cb_notify_status
),
726 g_signal_connect_swapped (G_OBJECT (sol
),
728 G_CALLBACK (cb_notify_status
),
730 cb_notify_status (state
);
732 g_signal_connect_swapped (G_OBJECT (sol
),
734 G_CALLBACK (cb_notify_result
),
736 cb_notify_result (state
);
738 state
->run
.timer_source
= g_timeout_add_seconds
739 (1, (GSourceFunc
)cb_timer_tick
, state
);
740 cb_timer_tick (state
);
742 /* ---------------------------------------- */
744 ok
= gnm_solver_start (sol
,
745 GNM_WBC (state
->wbcg
),
748 state
->run
.in_main
++;
749 go_cmd_context_set_sensitive (GO_CMD_CONTEXT (state
->wbcg
), FALSE
);
751 go_cmd_context_set_sensitive (GO_CMD_CONTEXT (state
->wbcg
), TRUE
);
752 state
->run
.in_main
--;
753 ok
= gnm_solver_has_solution (sol
);
755 gnm_solver_set_reason (sol
, err
->message
);
757 g_clear_error (&err
);
759 remove_objective_value_source (state
);
760 remove_timer_source (state
);
762 /* ---------------------------------------- */
767 gnm_solver_store_result (sol
);
768 redo
= clipboard_copy_range_undo (sr
.sheet
, &sr
.range
);
770 if (param
->options
.program_report
||
771 param
->options
.sensitivity_report
) {
772 Workbook
*wb
= param
->sheet
->workbook
;
773 GOUndo
*undo_report
, *redo_report
;
775 undo_report
= go_undo_binary_new
777 workbook_sheet_state_new (wb
),
778 (GOUndoBinaryFunc
)workbook_sheet_state_restore
,
780 (GFreeFunc
)workbook_sheet_state_free
);
781 undo
= go_undo_combine (undo
, undo_report
);
783 create_report (sol
, state
);
785 redo_report
= go_undo_binary_new
787 workbook_sheet_state_new (wb
),
788 (GOUndoBinaryFunc
)workbook_sheet_state_restore
,
790 (GFreeFunc
)workbook_sheet_state_free
);
791 redo
= go_undo_combine (redo
, redo_report
);
794 cmd_generic (GNM_WBC (state
->wbcg
),
797 res
= g_object_ref (sol
->result
);
803 g_object_unref (undo
);
805 if (state
->run
.solver
) {
806 g_object_unref (state
->run
.solver
);
807 state
->run
.solver
= NULL
;
817 solver_add_scenario (SolverState
*state
, GnmSolverResult
*res
, gchar
const *name
)
819 GnmSolverParameters
*param
= state
->sheet
->solver_parameters
;
820 GnmValue
const *vinput
;
823 WorkbookControl
*wbc
= GNM_WBC (state
->wbcg
);
825 vinput
= gnm_solver_param_get_input (param
);
826 gnm_sheet_range_from_value (&sr
, vinput
);
828 sc
= gnm_sheet_scenario_new (param
->sheet
, name
);
829 switch (res
->quality
) {
830 case GNM_SOLVER_RESULT_OPTIMAL
:
831 gnm_scenario_set_comment
832 (sc
, _("Optimal solution created by solver.\n"));
834 case GNM_SOLVER_RESULT_FEASIBLE
:
835 gnm_scenario_set_comment
836 (sc
, _("Feasible solution created by solver.\n"));
841 gnm_scenario_add_area (sc
, &sr
);
843 cmd_scenario_add (wbc
, sc
, sc
->sheet
);
847 * cb_dialog_solve_clicked:
854 cb_dialog_solve_clicked (G_GNUC_UNUSED GtkWidget
*button
,
857 GnmSolverResult
*res
;
858 GnmSolverParameters
*param
= state
->sheet
->solver_parameters
;
861 if (state
->warning_dialog
!= NULL
) {
862 gtk_widget_destroy (state
->warning_dialog
);
863 state
->warning_dialog
= NULL
;
866 extract_settings (state
);
868 if (!gnm_solver_param_valid (param
, &err
)) {
869 GtkWidget
*top
= gtk_widget_get_toplevel (state
->dialog
);
870 go_gtk_notice_dialog (GTK_WINDOW (top
), GTK_MESSAGE_ERROR
,
875 check_for_changed_options (state
);
877 res
= run_solver (state
, param
);
882 if ((res
->quality
== GNM_SOLVER_RESULT_OPTIMAL
||
883 res
->quality
== GNM_SOLVER_RESULT_FEASIBLE
) &&
884 param
->options
.add_scenario
)
885 solver_add_scenario (state
, res
,
886 param
->options
.scenario_name
);
888 g_object_unref (res
);
890 go_gtk_notice_nonmodal_dialog
891 (GTK_WINDOW (state
->dialog
),
892 &(state
->warning_dialog
),
902 #define INIT_BOOL_ENTRY(name_, field_) \
904 GtkWidget *w_ = go_gtk_builder_get_widget (state->gui, (name_)); \
905 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w_), \
911 * dialog_solver_init:
914 * Create the dialog (guru).
918 dialog_solver_init (SolverState
*state
)
921 GnmSolverParameters
*param
;
922 GtkCellRenderer
*renderer
;
924 GtkTreeViewColumn
*column
;
926 GnmCell
*target_cell
;
927 GnmValue
const *input
;
930 param
= state
->sheet
->solver_parameters
;
932 state
->gui
= gnm_gtk_builder_load ("res:ui/solver.ui", NULL
, GO_CMD_CONTEXT (state
->wbcg
));
933 if (state
->gui
== NULL
)
936 state
->dialog
= go_gtk_builder_get_widget (state
->gui
, "Solver");
937 if (state
->dialog
== NULL
)
940 state
->notebook
= go_gtk_builder_get_widget (state
->gui
, "solver_notebook");
943 state
->solve_button
= go_gtk_builder_get_widget (state
->gui
, "solvebutton");
944 g_signal_connect (G_OBJECT (state
->solve_button
), "clicked",
945 G_CALLBACK (cb_dialog_solve_clicked
), state
);
947 state
->close_button
= go_gtk_builder_get_widget (state
->gui
, "closebutton");
948 g_signal_connect (G_OBJECT (state
->close_button
), "clicked",
949 G_CALLBACK (cb_dialog_close_clicked
), state
);
951 state
->help_button
= go_gtk_builder_get_widget (state
->gui
, "helpbutton");
952 gnm_init_help_button (state
->help_button
, GNUMERIC_HELP_LINK_SOLVER
);
954 state
->add_button
= go_gtk_builder_get_widget (state
->gui
, "addbutton");
955 gtk_button_set_alignment (GTK_BUTTON (state
->add_button
), 0.5, .5);
956 g_signal_connect_swapped (G_OBJECT (state
->add_button
), "clicked",
957 G_CALLBACK (cb_dialog_add_clicked
), state
);
959 state
->change_button
= go_gtk_builder_get_widget (state
->gui
,
961 g_signal_connect (G_OBJECT (state
->change_button
), "clicked",
962 G_CALLBACK (cb_dialog_change_clicked
), state
);
964 state
->delete_button
= go_gtk_builder_get_widget (state
->gui
,
966 gtk_button_set_alignment (GTK_BUTTON (state
->delete_button
), 0.5, .5);
967 g_signal_connect (G_OBJECT (state
->delete_button
), "clicked",
968 G_CALLBACK (cb_dialog_delete_clicked
), state
);
970 state
->stop_button
= go_gtk_builder_get_widget (state
->gui
, "stopbutton");
971 g_signal_connect_swapped (G_OBJECT (state
->stop_button
),
972 "clicked", G_CALLBACK (cb_stop_solver
),
976 grid
= GTK_GRID (go_gtk_builder_get_widget (state
->gui
,
978 state
->target_entry
= gnm_expr_entry_new (state
->wbcg
, TRUE
);
979 gnm_expr_entry_set_flags (state
->target_entry
,
980 GNM_EE_SINGLE_RANGE
|
981 GNM_EE_FORCE_ABS_REF
|
982 GNM_EE_SHEET_OPTIONAL
, GNM_EE_MASK
);
983 gtk_widget_set_hexpand (GTK_WIDGET (state
->target_entry
), TRUE
);
984 gtk_grid_attach (grid
, GTK_WIDGET (state
->target_entry
), 1, 0, 2, 1);
985 gnm_editable_enters (GTK_WINDOW (state
->dialog
),
986 GTK_WIDGET (state
->target_entry
));
987 gtk_widget_show (GTK_WIDGET (state
->target_entry
));
988 g_signal_connect_after (G_OBJECT (state
->target_entry
), "changed",
989 G_CALLBACK (dialog_set_main_button_sensitivity
),
992 /* change_cell_entry */
993 state
->change_cell_entry
= gnm_expr_entry_new (state
->wbcg
, TRUE
);
994 gnm_expr_entry_set_flags (state
->change_cell_entry
,
995 GNM_EE_SINGLE_RANGE
|
996 GNM_EE_FORCE_ABS_REF
|
997 GNM_EE_SHEET_OPTIONAL
, GNM_EE_MASK
);
998 gtk_widget_set_hexpand (GTK_WIDGET (state
->change_cell_entry
), TRUE
);
999 gtk_grid_attach (grid
,
1000 GTK_WIDGET (state
->change_cell_entry
), 1, 2, 2, 1);
1001 gnm_editable_enters (GTK_WINDOW (state
->dialog
),
1002 GTK_WIDGET (state
->change_cell_entry
));
1003 gtk_widget_show (GTK_WIDGET (state
->change_cell_entry
));
1004 g_signal_connect_after (G_OBJECT (state
->change_cell_entry
), "changed",
1005 G_CALLBACK (dialog_set_main_button_sensitivity
), state
);
1008 state
->algorithm_combo
= GTK_COMBO_BOX
1009 (go_gtk_builder_get_widget (state
->gui
, "algorithm_combo"));
1010 renderer
= (GtkCellRenderer
*) gtk_cell_renderer_text_new();
1011 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (state
->algorithm_combo
), renderer
, TRUE
);
1012 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (state
->algorithm_combo
), renderer
,
1015 fill_algorithm_combo (state
, param
->options
.model_type
);
1017 for (i
= 0; model_type_group
[i
]; i
++) {
1018 const char *bname
= model_type_group
[i
];
1019 GtkWidget
*w
= go_gtk_builder_get_widget(state
->gui
, bname
);
1020 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w
),
1021 param
->options
.model_type
==
1022 (GnmSolverModelType
)i
);
1023 g_signal_connect (G_OBJECT (w
), "clicked",
1024 G_CALLBACK (cb_dialog_model_type_clicked
), state
);
1028 state
->max_iter_entry
= go_gtk_builder_get_widget (state
->gui
,
1030 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state
->max_iter_entry
),
1031 param
->options
.max_iter
);
1033 state
->max_time_entry
= go_gtk_builder_get_widget (state
->gui
,
1035 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state
->max_time_entry
),
1036 param
->options
.max_time_sec
);
1038 state
->gradient_order_entry
= go_gtk_builder_get_widget (state
->gui
,
1039 "gradient_order_entry");
1040 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state
->gradient_order_entry
),
1041 param
->options
.gradient_order
);
1044 grid
= GTK_GRID (go_gtk_builder_get_widget (state
->gui
,
1045 "constraints-grid"));
1046 state
->lhs
.entry
= gnm_expr_entry_new (state
->wbcg
, TRUE
);
1047 gnm_expr_entry_set_flags (state
->lhs
.entry
,
1048 GNM_EE_SINGLE_RANGE
|
1049 GNM_EE_FORCE_ABS_REF
|
1050 GNM_EE_SHEET_OPTIONAL
, GNM_EE_MASK
);
1051 gtk_widget_set_hexpand (GTK_WIDGET (state
->lhs
.entry
), TRUE
);
1052 gtk_grid_attach (grid
, GTK_WIDGET (state
->lhs
.entry
), 0, 4, 1, 1);
1053 state
->lhs
.label
= go_gtk_builder_get_widget (state
->gui
, "lhs_label");
1054 gtk_label_set_mnemonic_widget (GTK_LABEL (state
->lhs
.label
),
1055 GTK_WIDGET (state
->lhs
.entry
));
1056 gtk_widget_show (GTK_WIDGET (state
->lhs
.entry
));
1057 g_signal_connect_after (G_OBJECT (state
->lhs
.entry
),
1059 G_CALLBACK (dialog_set_sec_button_sensitivity
), state
);
1060 g_signal_connect_swapped (
1061 gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state
->lhs
.entry
)),
1062 "activate", G_CALLBACK (cb_dialog_add_clicked
), state
);
1065 state
->rhs
.entry
= gnm_expr_entry_new (state
->wbcg
, TRUE
);
1066 gnm_expr_entry_set_flags (state
->rhs
.entry
,
1067 GNM_EE_SINGLE_RANGE
|
1068 GNM_EE_FORCE_ABS_REF
|
1069 GNM_EE_SHEET_OPTIONAL
|
1070 GNM_EE_CONSTANT_ALLOWED
,
1072 gtk_widget_set_hexpand (GTK_WIDGET (state
->rhs
.entry
), TRUE
);
1073 gtk_grid_attach (grid
, GTK_WIDGET (state
->rhs
.entry
), 2, 4, 1, 1);
1074 gtk_widget_show (GTK_WIDGET (state
->rhs
.entry
));
1075 state
->rhs
.label
= go_gtk_builder_get_widget (state
->gui
, "rhs_label");
1076 gtk_label_set_mnemonic_widget (
1077 GTK_LABEL (state
->rhs
.label
), GTK_WIDGET (state
->rhs
.entry
));
1078 g_signal_connect_after (G_OBJECT (state
->rhs
.entry
),
1080 G_CALLBACK (dialog_set_sec_button_sensitivity
), state
);
1081 g_signal_connect_swapped (
1082 gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state
->rhs
.entry
)),
1083 "activate", G_CALLBACK (cb_dialog_add_clicked
), state
);
1086 state
->type_combo
= GTK_COMBO_BOX
1087 (go_gtk_builder_get_widget (state
->gui
, "type_menu"));
1088 gtk_combo_box_set_active (state
->type_combo
, 0);
1089 g_signal_connect (state
->type_combo
, "changed",
1090 G_CALLBACK (dialog_set_sec_button_sensitivity
),
1093 /* constraint_list */
1094 state
->constraint_list
= GTK_TREE_VIEW (go_gtk_builder_get_widget
1095 (state
->gui
, "constraint_list"));
1097 state
->constr
= NULL
;
1098 g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (state
->constraint_list
)), "changed",
1099 G_CALLBACK (constraint_select_click
), state
);
1100 gtk_tree_view_set_reorderable (state
->constraint_list
, TRUE
);
1101 store
= gtk_list_store_new (2, G_TYPE_STRING
, G_TYPE_POINTER
);
1102 gtk_tree_view_set_model (state
->constraint_list
, GTK_TREE_MODEL(store
));
1103 renderer
= gtk_cell_renderer_text_new ();
1104 column
= gtk_tree_view_column_new_with_attributes (
1105 _("Subject to the Constraints:"),
1106 renderer
, "text", 0, NULL
);
1107 gtk_tree_view_column_set_expand (column
, TRUE
);
1108 gtk_tree_view_append_column (state
->constraint_list
, column
);
1111 GtkWidget
*w
= GTK_WIDGET (state
->constraint_list
);
1112 int width
, height
, vsep
;
1113 PangoLayout
*layout
=
1114 gtk_widget_create_pango_layout (w
, "Mg19");
1116 gtk_widget_style_get (w
,
1117 "vertical_separator", &vsep
,
1120 pango_layout_get_pixel_size (layout
, &width
, &height
);
1121 gtk_widget_set_size_request (w
,
1123 (2 * height
+ vsep
) * (4 + 1));
1124 g_object_unref (layout
);
1127 /* Loading the old solver specs... from param */
1129 for (cl
= param
->constraints
; cl
; cl
= cl
->next
) {
1130 GnmSolverConstraint
const *c
= cl
->data
;
1134 gtk_list_store_append (store
, &iter
);
1135 str
= gnm_solver_constraint_as_str (c
, state
->sheet
);
1136 gtk_list_store_set (store
, &iter
, 0, str
, 1, c
, -1);
1139 g_object_unref (store
);
1141 INIT_BOOL_ENTRY ("autoscale_button", options
.automatic_scaling
);
1142 INIT_BOOL_ENTRY ("non_neg_button", options
.assume_non_negative
);
1143 INIT_BOOL_ENTRY ("all_int_button", options
.assume_discrete
);
1144 INIT_BOOL_ENTRY ("program", options
.program_report
);
1145 INIT_BOOL_ENTRY ("sensitivity", options
.sensitivity_report
);
1147 input
= gnm_solver_param_get_input (param
);
1149 gnm_expr_entry_load_from_text (state
->change_cell_entry
,
1150 value_peek_string (input
));
1151 target_cell
= gnm_solver_param_get_target_cell (param
);
1153 gnm_expr_entry_load_from_text (state
->target_entry
,
1154 cell_name (target_cell
));
1156 SheetView
*sv
= wb_control_cur_sheet_view
1157 (GNM_WBC (state
->wbcg
));
1159 GnmRange first
= {sv
->edit_pos
, sv
->edit_pos
};
1160 gnm_expr_entry_load_from_range (state
->target_entry
,
1161 state
->sheet
, &first
);
1165 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
1166 go_gtk_builder_get_widget(state
->gui
, "max_button")),
1167 param
->problem_type
== GNM_SOLVER_MAXIMIZE
);
1168 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
1169 go_gtk_builder_get_widget(state
->gui
, "min_button")),
1170 param
->problem_type
== GNM_SOLVER_MINIMIZE
);
1171 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
1172 go_gtk_builder_get_widget(state
->gui
, "no_scenario")),
1173 ! param
->options
.add_scenario
);
1174 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
1175 go_gtk_builder_get_widget(state
->gui
, "optimal_scenario")),
1176 param
->options
.add_scenario
);
1178 state
->scenario_name_entry
= go_gtk_builder_get_widget
1179 (state
->gui
, "scenario_name_entry");
1180 gtk_entry_set_text (GTK_ENTRY (state
->scenario_name_entry
),
1181 param
->options
.scenario_name
);
1183 state
->run
.status_widget
= go_gtk_builder_get_widget (state
->gui
, "solver_status_label");
1184 state
->run
.problem_status_widget
= go_gtk_builder_get_widget (state
->gui
, "problem_status_label");
1185 state
->run
.objective_value_widget
= go_gtk_builder_get_widget (state
->gui
, "objective_value_label");
1186 state
->run
.timer_widget
= go_gtk_builder_get_widget (state
->gui
, "elapsed_time_label");
1187 state
->run
.spinner
= go_gtk_builder_get_widget (state
->gui
, "run_spinner");
1191 gnm_expr_entry_grab_focus (state
->target_entry
, FALSE
);
1192 wbcg_set_entry (state
->wbcg
, state
->target_entry
);
1194 dialog_set_main_button_sensitivity (NULL
, state
);
1195 dialog_set_sec_button_sensitivity (NULL
, state
);
1198 wbc_gtk_attach_guru (state
->wbcg
, state
->dialog
);
1200 g_signal_connect_swapped (G_OBJECT (state
->dialog
),
1202 G_CALLBACK (cb_dialog_solver_destroy
),
1204 g_object_set_data_full (G_OBJECT (state
->dialog
),
1206 (GDestroyNotify
)unref_state
);
1216 * Create the dialog (guru).
1220 dialog_solver (WBCGtk
*wbcg
, Sheet
*sheet
)
1223 GnmSolverParameters
*old_params
= sheet
->solver_parameters
;
1227 /* Only pop up one copy per workbook */
1228 if (gnm_dialog_raise_if_exists (wbcg
, SOLVER_KEY
))
1232 * First time around, pick a functional algorithm preferably one we
1233 * can determine is functional without asking the user anything.
1235 got_it
= gnm_solver_factory_functional (old_params
->options
.algorithm
,
1237 for (pass
= 1; !got_it
&& pass
<= 2; pass
++) {
1239 WBCGtk
*wbcg2
= pass
== 2 ? wbcg
: NULL
;
1241 for (l
= gnm_solver_db_get (); l
; l
= l
->next
) {
1242 GnmSolverFactory
*factory
= l
->data
;
1243 if (old_params
->options
.model_type
!= factory
->type
)
1245 if (gnm_solver_factory_functional (factory
, wbcg2
)) {
1247 gnm_solver_param_set_algorithm (old_params
,
1254 state
= g_new0 (SolverState
, 1);
1255 state
->ref_count
= 1;
1257 state
->sheet
= sheet
;
1258 state
->warning_dialog
= NULL
;
1259 state
->orig_params
= gnm_solver_param_dup (sheet
->solver_parameters
,
1262 if (dialog_solver_init (state
)) {
1263 go_gtk_notice_dialog (wbcg_toplevel (wbcg
), GTK_MESSAGE_ERROR
,
1264 _("Could not create the Solver dialog."));
1265 unref_state (state
);
1269 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state
->dialog
), wbcg
,
1270 GNM_DIALOG_DESTROY_SHEET_REMOVED
);
1272 gnm_keyed_dialog (state
->wbcg
, GTK_WINDOW (state
->dialog
),
1275 gtk_widget_show (state
->dialog
);