static void solver_add_scenario (SolverState *state, GnmSolverResult *res, gchar const *name) { GnmSolverParameters *param = state->sheet->solver_parameters; GnmValue const *vinput; GnmScenario *sc; GnmSheetRange sr; WorkbookControl *wbc = GNM_WBC (state->wbcg); vinput = gnm_solver_param_get_input (param); gnm_sheet_range_from_value (&sr, vinput); sc = gnm_sheet_scenario_new (param->sheet, name); switch (res->quality) { case GNM_SOLVER_RESULT_OPTIMAL: gnm_scenario_set_comment (sc, _("Optimal solution created by solver.\n")); break; case GNM_SOLVER_RESULT_FEASIBLE: gnm_scenario_set_comment (sc, _("Feasible solution created by solver.\n")); break; default: break; } gnm_scenario_add_area (sc, &sr); cmd_scenario_add (wbc, sc, sc->sheet); }
GnmSolver * nlsolve_solver_factory (GnmSolverFactory *factory, GnmSolverParameters *params) { GnmSolver *res = g_object_new (GNM_SOLVER_TYPE, "params", params, NULL); GnmNlsolve *nl = g_new0 (GnmNlsolve, 1); GSList *input_cells, *l; int n; GnmValue const *vinput = gnm_solver_param_get_input (params); GnmEvalPos ep; GnmCellRef origin; nl->parent = GNM_SOLVER (res); nl->maximize = (params->problem_type == GNM_SOLVER_MAXIMIZE); eval_pos_init_sheet (&ep, params->sheet); if (vinput) { gnm_cellref_make_abs (&origin, &vinput->v_range.cell.a, &ep); nl->origin.col = origin.col; nl->origin.row = origin.row; nl->input_width = value_area_get_width (vinput, &ep); nl->input_height = value_area_get_height (vinput, &ep); } nl->debug = gnm_solver_debug (); nl->max_iter = params->options.max_iter; nl->min_factor = 1e-10; nl->target = gnm_solver_param_get_target_cell (params); nl->vars = g_ptr_array_new (); input_cells = gnm_solver_param_get_input_cells (params); for (l = input_cells; l; l = l->next) g_ptr_array_add (nl->vars, l->data); g_slist_free (input_cells); n = nl->vars->len; nl->x0 = g_new (gnm_float, n); 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_object_set_data_full (G_OBJECT (res), PRIVATE_KEY, nl, (GDestroyNotify)gnm_nlsolve_final); return res; }
GnmSolver * lpsolve_solver_factory (GnmSolverFactory *factory, GnmSolverParameters *params) { GnmSolver *res = g_object_new (GNM_SUB_SOLVER_TYPE, "params", params, NULL); 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); g_signal_connect (res, "stop", G_CALLBACK (gnm_lpsolve_stop), lp); g_signal_connect (res, "child-exit", G_CALLBACK (gnm_lpsolve_child_exit), lp); g_object_set_data_full (G_OBJECT (res), PRIVATE_KEY, lp, (GDestroyNotify)gnm_lpsolve_final); return res; }
/** * dialog_init: * @state: * * Create the dialog (guru). * **/ static gboolean dialog_init (SolverState *state) { GtkGrid *grid; GnmSolverParameters *param; GtkCellRenderer *renderer; GtkListStore *store; GtkTreeViewColumn *column; GSList *cl; GnmCell *target_cell; GnmValue const *input; int i; param = state->sheet->solver_parameters; state->gui = gnm_gtk_builder_load ("solver.ui", NULL, GO_CMD_CONTEXT (state->wbcg)); if (state->gui == NULL) return TRUE; state->dialog = go_gtk_builder_get_widget (state->gui, "Solver"); if (state->dialog == NULL) return TRUE; state->notebook = go_gtk_builder_get_widget (state->gui, "solver_notebook"); /* buttons */ state->solve_button = go_gtk_builder_get_widget (state->gui, "solvebutton"); g_signal_connect (G_OBJECT (state->solve_button), "clicked", G_CALLBACK (cb_dialog_solve_clicked), state); state->close_button = go_gtk_builder_get_widget (state->gui, "closebutton"); g_signal_connect (G_OBJECT (state->close_button), "clicked", G_CALLBACK (cb_dialog_close_clicked), state); state->help_button = go_gtk_builder_get_widget (state->gui, "helpbutton"); gnm_init_help_button (state->help_button, GNUMERIC_HELP_LINK_SOLVER); state->add_button = go_gtk_builder_get_widget (state->gui, "addbutton"); gtk_button_set_alignment (GTK_BUTTON (state->add_button), 0.5, .5); g_signal_connect_swapped (G_OBJECT (state->add_button), "clicked", G_CALLBACK (cb_dialog_add_clicked), state); state->change_button = go_gtk_builder_get_widget (state->gui, "changebutton"); g_signal_connect (G_OBJECT (state->change_button), "clicked", G_CALLBACK (cb_dialog_change_clicked), state); state->delete_button = go_gtk_builder_get_widget (state->gui, "deletebutton"); gtk_button_set_alignment (GTK_BUTTON (state->delete_button), 0.5, .5); g_signal_connect (G_OBJECT (state->delete_button), "clicked", G_CALLBACK (cb_dialog_delete_clicked), state); state->stop_button = go_gtk_builder_get_widget (state->gui, "stopbutton"); g_signal_connect_swapped (G_OBJECT (state->stop_button), "clicked", G_CALLBACK (cb_stop_solver), state); /* target_entry */ grid = GTK_GRID (go_gtk_builder_get_widget (state->gui, "parameter-grid")); state->target_entry = gnm_expr_entry_new (state->wbcg, TRUE); gnm_expr_entry_set_flags (state->target_entry, GNM_EE_SINGLE_RANGE | GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL, GNM_EE_MASK); gtk_widget_set_hexpand (GTK_WIDGET (state->target_entry), TRUE); gtk_grid_attach (grid, GTK_WIDGET (state->target_entry), 1, 0, 2, 1); gnm_editable_enters (GTK_WINDOW (state->dialog), GTK_WIDGET (state->target_entry)); gtk_widget_show (GTK_WIDGET (state->target_entry)); g_signal_connect_after (G_OBJECT (state->target_entry), "changed", G_CALLBACK (dialog_set_main_button_sensitivity), state); /* change_cell_entry */ state->change_cell_entry = gnm_expr_entry_new (state->wbcg, TRUE); gnm_expr_entry_set_flags (state->change_cell_entry, GNM_EE_SINGLE_RANGE | GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL, GNM_EE_MASK); gtk_widget_set_hexpand (GTK_WIDGET (state->change_cell_entry), TRUE); gtk_grid_attach (grid, GTK_WIDGET (state->change_cell_entry), 1, 2, 2, 1); gnm_editable_enters (GTK_WINDOW (state->dialog), GTK_WIDGET (state->change_cell_entry)); gtk_widget_show (GTK_WIDGET (state->change_cell_entry)); g_signal_connect_after (G_OBJECT (state->change_cell_entry), "changed", G_CALLBACK (dialog_set_main_button_sensitivity), state); /* Algorithm */ state->algorithm_combo = GTK_COMBO_BOX (go_gtk_builder_get_widget (state->gui, "algorithm_combo")); renderer = (GtkCellRenderer*) gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (state->algorithm_combo), renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (state->algorithm_combo), renderer, "text", 0, NULL); fill_algorithm_combo (state, param->options.model_type); for (i = 0; model_type_group[i]; i++) { const char *bname = model_type_group[i]; GtkWidget *w = go_gtk_builder_get_widget(state->gui, bname); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), param->options.model_type == (GnmSolverModelType)i); g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (cb_dialog_model_type_clicked), state); } /* Options */ state->max_iter_entry = go_gtk_builder_get_widget (state->gui, "max_iter_entry"); gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->max_iter_entry), param->options.max_iter); state->max_time_entry = go_gtk_builder_get_widget (state->gui, "max_time_entry"); gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->max_time_entry), param->options.max_time_sec); state->gradient_order_entry = go_gtk_builder_get_widget (state->gui, "gradient_order_entry"); gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->gradient_order_entry), param->options.gradient_order); /* lhs_entry */ grid = GTK_GRID (go_gtk_builder_get_widget (state->gui, "constraints-grid")); state->lhs.entry = gnm_expr_entry_new (state->wbcg, TRUE); gnm_expr_entry_set_flags (state->lhs.entry, GNM_EE_SINGLE_RANGE | GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL, GNM_EE_MASK); gtk_widget_set_hexpand (GTK_WIDGET (state->lhs.entry), TRUE); gtk_grid_attach (grid, GTK_WIDGET (state->lhs.entry), 0, 4, 1, 1); state->lhs.label = go_gtk_builder_get_widget (state->gui, "lhs_label"); gtk_label_set_mnemonic_widget (GTK_LABEL (state->lhs.label), GTK_WIDGET (state->lhs.entry)); gtk_widget_show (GTK_WIDGET (state->lhs.entry)); g_signal_connect_after (G_OBJECT (state->lhs.entry), "changed", G_CALLBACK (dialog_set_sec_button_sensitivity), state); g_signal_connect_swapped ( gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state->lhs.entry)), "activate", G_CALLBACK (cb_dialog_add_clicked), state); /* rhs_entry */ state->rhs.entry = gnm_expr_entry_new (state->wbcg, TRUE); gnm_expr_entry_set_flags (state->rhs.entry, GNM_EE_SINGLE_RANGE | GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL | GNM_EE_CONSTANT_ALLOWED, GNM_EE_MASK); gtk_widget_set_hexpand (GTK_WIDGET (state->rhs.entry), TRUE); gtk_grid_attach (grid, GTK_WIDGET (state->rhs.entry), 2, 4, 1, 1); gtk_widget_show (GTK_WIDGET (state->rhs.entry)); state->rhs.label = go_gtk_builder_get_widget (state->gui, "rhs_label"); gtk_label_set_mnemonic_widget ( GTK_LABEL (state->rhs.label), GTK_WIDGET (state->rhs.entry)); g_signal_connect_after (G_OBJECT (state->rhs.entry), "changed", G_CALLBACK (dialog_set_sec_button_sensitivity), state); g_signal_connect_swapped ( gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state->rhs.entry)), "activate", G_CALLBACK (cb_dialog_add_clicked), state); /* type_menu */ state->type_combo = GTK_COMBO_BOX (go_gtk_builder_get_widget (state->gui, "type_menu")); gtk_combo_box_set_active (state->type_combo, 0); g_signal_connect (state->type_combo, "changed", G_CALLBACK (dialog_set_sec_button_sensitivity), state); /* constraint_list */ state->constraint_list = GTK_TREE_VIEW (go_gtk_builder_get_widget (state->gui, "constraint_list")); state->constr = NULL; g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (state->constraint_list)), "changed", G_CALLBACK (constraint_select_click), state); gtk_tree_view_set_reorderable (state->constraint_list, TRUE); store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER); gtk_tree_view_set_model (state->constraint_list, GTK_TREE_MODEL(store)); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ( _("Subject to the Constraints:"), renderer, "text", 0, NULL); gtk_tree_view_column_set_expand (column, TRUE); gtk_tree_view_append_column (state->constraint_list, column); { GtkWidget *w = GTK_WIDGET (state->constraint_list); int width, height, vsep; PangoLayout *layout = gtk_widget_create_pango_layout (w, "Mg19"); gtk_widget_style_get (w, "vertical_separator", &vsep, NULL); pango_layout_get_pixel_size (layout, &width, &height); gtk_widget_set_size_request (w, -1, (2 * height + vsep) * (4 + 1)); g_object_unref (layout); } /* Loading the old solver specs... from param */ for (cl = param->constraints; cl; cl = cl->next) { GnmSolverConstraint const *c = cl->data; GtkTreeIter iter; char *str; gtk_list_store_append (store, &iter); str = gnm_solver_constraint_as_str (c, state->sheet); gtk_list_store_set (store, &iter, 0, str, 1, c, -1); g_free (str); } g_object_unref (store); INIT_BOOL_ENTRY ("autoscale_button", options.automatic_scaling); INIT_BOOL_ENTRY ("non_neg_button", options.assume_non_negative); INIT_BOOL_ENTRY ("all_int_button", options.assume_discrete); INIT_BOOL_ENTRY ("program", options.program_report); INIT_BOOL_ENTRY ("sensitivity", options.sensitivity_report); input = gnm_solver_param_get_input (param); if (input != NULL) gnm_expr_entry_load_from_text (state->change_cell_entry, value_peek_string (input)); target_cell = gnm_solver_param_get_target_cell (param); if (target_cell) gnm_expr_entry_load_from_text (state->target_entry, cell_name (target_cell)); else { SheetView *sv = wb_control_cur_sheet_view (GNM_WBC (state->wbcg)); if (sv) { GnmRange first = {sv->edit_pos, sv->edit_pos}; gnm_expr_entry_load_from_range (state->target_entry, state->sheet, &first); } } gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON ( go_gtk_builder_get_widget(state->gui, "max_button")), param->problem_type == GNM_SOLVER_MAXIMIZE); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON ( go_gtk_builder_get_widget(state->gui, "min_button")), param->problem_type == GNM_SOLVER_MINIMIZE); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON ( go_gtk_builder_get_widget(state->gui, "no_scenario")), ! param->options.add_scenario); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON ( go_gtk_builder_get_widget(state->gui, "optimal_scenario")), param->options.add_scenario); state->scenario_name_entry = go_gtk_builder_get_widget (state->gui, "scenario_name_entry"); gtk_entry_set_text (GTK_ENTRY (state->scenario_name_entry), param->options.scenario_name); state->run.status_widget = go_gtk_builder_get_widget (state->gui, "solver_status_label"); state->run.problem_status_widget = go_gtk_builder_get_widget (state->gui, "problem_status_label"); state->run.objective_value_widget = go_gtk_builder_get_widget (state->gui, "objective_value_label"); state->run.timer_widget = go_gtk_builder_get_widget (state->gui, "elapsed_time_label"); state->run.spinner = go_gtk_builder_get_widget (state->gui, "run_spinner"); /* Done */ gnm_expr_entry_grab_focus (state->target_entry, FALSE); wbcg_set_entry (state->wbcg, state->target_entry); dialog_set_main_button_sensitivity (NULL, state); dialog_set_sec_button_sensitivity (NULL, 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)unref_state); return FALSE; }
static GnmSolverResult * run_solver (SolverState *state, GnmSolverParameters *param) { GError *err = NULL; gboolean ok; GnmSheetRange sr; GOUndo *undo = NULL; GnmSolver *sol = NULL; GnmValue const *vinput; GtkWindow *top = GTK_WINDOW (gtk_widget_get_toplevel (state->dialog)); GnmSolverResult *res = NULL; state->ref_count++; sol = gnm_solver_factory_functional (param->options.algorithm, state->wbcg) ? gnm_solver_factory_create (param->options.algorithm, param) : NULL; if (!sol) { go_gtk_notice_dialog (top, GTK_MESSAGE_ERROR, _("The chosen solver is not functional.")); goto fail; } gtk_notebook_set_current_page (GTK_NOTEBOOK (state->notebook), -1); state->run.solver = sol; vinput = gnm_solver_param_get_input (param); gnm_sheet_range_from_value (&sr, vinput); if (!sr.sheet) sr.sheet = param->sheet; undo = clipboard_copy_range_undo (sr.sheet, &sr.range); g_signal_connect_swapped (G_OBJECT (sol), "notify::status", G_CALLBACK (cb_notify_status), state); g_signal_connect_swapped (G_OBJECT (sol), "notify::reason", G_CALLBACK (cb_notify_status), state); cb_notify_status (state); g_signal_connect_swapped (G_OBJECT (sol), "notify::result", G_CALLBACK (cb_notify_result), state); cb_notify_result (state); state->run.timer_source = g_timeout_add_seconds (1, (GSourceFunc)cb_timer_tick, state); cb_timer_tick (state); /* ---------------------------------------- */ ok = gnm_solver_start (sol, GNM_WBC (state->wbcg), &err); if (ok) { state->run.in_main++; go_cmd_context_set_sensitive (GO_CMD_CONTEXT (state->wbcg), FALSE); gtk_main (); go_cmd_context_set_sensitive (GO_CMD_CONTEXT (state->wbcg), TRUE); state->run.in_main--; ok = gnm_solver_has_solution (sol); } else if (err) { gnm_solver_set_reason (sol, err->message); } g_clear_error (&err); remove_objective_value_source (state); remove_timer_source (state); /* ---------------------------------------- */ if (ok) { GOUndo *redo; gnm_solver_store_result (sol); redo = clipboard_copy_range_undo (sr.sheet, &sr.range); if (param->options.program_report || param->options.sensitivity_report) { Workbook *wb = param->sheet->workbook; GOUndo *undo_report, *redo_report; undo_report = go_undo_binary_new (wb, workbook_sheet_state_new (wb), (GOUndoBinaryFunc)workbook_sheet_state_restore, NULL, (GFreeFunc)workbook_sheet_state_free); undo = go_undo_combine (undo, undo_report); create_report (sol, state); redo_report = go_undo_binary_new (wb, workbook_sheet_state_new (wb), (GOUndoBinaryFunc)workbook_sheet_state_restore, NULL, (GFreeFunc)workbook_sheet_state_free); redo = go_undo_combine (redo, redo_report); } cmd_generic (GNM_WBC (state->wbcg), _("Running solver"), undo, redo); res = g_object_ref (sol->result); undo = redo = NULL; } fail: if (undo) g_object_unref (undo); if (state->run.solver) { g_object_unref (state->run.solver); state->run.solver = NULL; } unref_state (state); return res; }