[gnumeric] solver: add (external) glpk solver.
- From: Morten Welinder <mortenw src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gnumeric] solver: add (external) glpk solver.
- Date: Sat, 14 Nov 2009 17:59:46 +0000 (UTC)
commit 6100df1abebc439e6e679611176c2a80a9af0f34
Author: Morten Welinder <terra gnome org>
Date: Sat Nov 14 12:59:22 2009 -0500
solver: add (external) glpk solver.
configure.in | 1 +
plugins/Makefile.am | 2 +-
plugins/glpk/.gitignore | 9 +
plugins/glpk/ChangeLog | 3 +
plugins/glpk/Makefile.am | 21 +++
plugins/glpk/boot.c | 24 +++
plugins/glpk/boot.h | 12 ++
plugins/glpk/glpk-write.c | 371 +++++++++++++++++++++++++++++++++++++++
plugins/glpk/gnm-glpk.c | 293 ++++++++++++++++++++++++++++++
plugins/glpk/plugin.xml.in | 29 +++
plugins/lpsolve/gnm-lpsolve.c | 12 ++-
plugins/lpsolve/lpsolve-write.c | 6 +-
plugins/lpsolve/plugin.xml.in | 2 +-
po-functions/POTFILES.in | 4 +
po-functions/POTFILES.skip | 1 +
po/POTFILES.in | 5 +
src/dialogs/dialog-solver.c | 2 +
src/tools/gnm-solver.c | 73 +++++++-
src/tools/gnm-solver.h | 11 ++
19 files changed, 872 insertions(+), 9 deletions(-)
---
diff --git a/configure.in b/configure.in
index 66ff91e..c06b34f 100644
--- a/configure.in
+++ b/configure.in
@@ -1065,6 +1065,7 @@ plugins/gnome-glossary/Makefile
plugins/html/Makefile
plugins/lotus-123/Makefile
plugins/lpsolve/Makefile
+plugins/glpk/Makefile
plugins/mps/Makefile
plugins/oleo/Makefile
plugins/openoffice/Makefile
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 062da59..8f0dea1 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -1,5 +1,5 @@
SUBDIRS_FILE_FORMATS = excel lotus-123 oleo sc sylk xbase html dif qpro \
- plan-perfect applix openoffice lpsolve
+ plan-perfect applix openoffice lpsolve glpk
if ENABLE_SOLVER
SUBDIRS_FILE_FORMATS += mps
diff --git a/plugins/glpk/.gitignore b/plugins/glpk/.gitignore
new file mode 100644
index 0000000..6429675
--- /dev/null
+++ b/plugins/glpk/.gitignore
@@ -0,0 +1,9 @@
+Makefile
+Makefile.in
+.deps
+.libs
+*.lo
+*.la
+plugin.xml
+*.loT
+*.sw*
diff --git a/plugins/glpk/ChangeLog b/plugins/glpk/ChangeLog
new file mode 100644
index 0000000..33314bf
--- /dev/null
+++ b/plugins/glpk/ChangeLog
@@ -0,0 +1,3 @@
+2009-11-13 Morten Welinder <terra gnome org>
+
+ * Initial Implementation
diff --git a/plugins/glpk/Makefile.am b/plugins/glpk/Makefile.am
new file mode 100644
index 0000000..8c66c24
--- /dev/null
+++ b/plugins/glpk/Makefile.am
@@ -0,0 +1,21 @@
+AM_CPPFLAGS = \
+ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+ -I$(top_srcdir)/src -I$(top_builddir)/src \
+ $(GNUMERIC_CFLAGS)
+
+gnumeric_plugin_glpkdir = $(gnumeric_plugindir)/glpk
+xmldir = $(gnumeric_plugin_glpkdir)
+gnumeric_plugin_glpk_LTLIBRARIES = glpk.la
+glpk_la_LDFLAGS = -module $(GNUMERIC_PLUGIN_LDFLAGS)
+glpk_la_SOURCES = \
+ boot.h boot.c \
+ gnm-glpk.c \
+ glpk-write.c
+
+xml_in_files = plugin.xml.in
+xml_DATA = $(xml_in_files:.xml.in=.xml)
+
+ INTLTOOL_XML_RULE@
+
+EXTRA_DIST = ChangeLog $(xml_in_files)
+DISTCLEANFILES = $(xml_DATA)
diff --git a/plugins/glpk/boot.c b/plugins/glpk/boot.c
new file mode 100644
index 0000000..fef583f
--- /dev/null
+++ b/plugins/glpk/boot.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2009 Morten Welinder (terra gnome org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gnumeric-config.h>
+#include <gnumeric.h>
+#include "boot.h"
+#include <gnm-plugin.h>
+
+GNM_PLUGIN_MODULE_HEADER;
diff --git a/plugins/glpk/boot.h b/plugins/glpk/boot.h
new file mode 100644
index 0000000..a40468a
--- /dev/null
+++ b/plugins/glpk/boot.h
@@ -0,0 +1,12 @@
+#ifndef GNUMERIC_GLPK_BOOT_H
+#define GNUMERIC_GLPK_BOOT_H
+
+#include "gnumeric.h"
+#include <goffice/goffice.h>
+#include <gsf/gsf-output.h>
+
+void
+glpk_file_save (GOFileSaver const *fs, GOIOContext *io_context,
+ WorkbookView const *wb_view, GsfOutput *output);
+
+#endif
diff --git a/plugins/glpk/glpk-write.c b/plugins/glpk/glpk-write.c
new file mode 100644
index 0000000..d4d81a3
--- /dev/null
+++ b/plugins/glpk/glpk-write.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2009 Morten Welinder (terra gnome org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gnumeric-config.h>
+#include <boot.h>
+#include <numbers.h>
+#include <workbook-view.h>
+#include <sheet.h>
+#include <workbook.h>
+#include <value.h>
+#include <cell.h>
+#include <expr.h>
+#include <tools/gnm-solver.h>
+#include <ranges.h>
+#include <parse-util.h>
+#include <gutils.h>
+#include <goffice/goffice.h>
+#include <glib/gi18n-lib.h>
+
+#include <string.h>
+
+
+static gboolean
+gnm_solver_get_lp_coeff (GnmCell *target, GnmCell *cell,
+ gnm_float *x, GError **err)
+{
+ gnm_float x0, x1;
+ gboolean res = FALSE;
+
+ if (!VALUE_IS_NUMBER (target->value))
+ goto fail;
+ x0 = value_get_as_float (target->value);
+
+ gnm_cell_set_value (cell, value_new_float (1));
+ cell_queue_recalc (cell);
+ gnm_cell_eval (target);
+ if (!VALUE_IS_NUMBER (target->value))
+ goto fail;
+ x1 = value_get_as_float (target->value);
+
+ *x = x1 - x0;
+ res = TRUE;
+ goto out;
+
+fail:
+ g_set_error (err,
+ go_error_invalid (),
+ 0,
+ _("Target cell did not evaluate to a number."));
+ *x = 0;
+
+out:
+ gnm_cell_set_value (cell, value_new_int (0));
+ cell_queue_recalc (cell);
+ gnm_cell_eval (target);
+
+ return res;
+}
+
+/*
+ * FIXME: we need to handle the situation where cells from more than one
+ * sheet are involved.
+ */
+static const char *
+glpk_var_name (GnmSubSolver *ssol, GnmCell const *cell)
+{
+ if (ssol)
+ return gnm_sub_solver_get_cell_name (ssol, cell);
+ return cell_name (cell);
+}
+
+static gboolean
+glpk_affine_func (GString *dst, GnmCell *target, GnmSubSolver *ssol,
+ gboolean zero_too,
+ gnm_float cst, GSList *input_cells, GError **err)
+{
+ GSList *l, *ol;
+ gboolean any = FALSE;
+ gnm_float y;
+ GSList *old_values = NULL;
+ gboolean ok = TRUE;
+
+ if (!target) {
+ gnm_string_add_number (dst, cst);
+ return TRUE;
+ }
+
+ for (l = input_cells; l; l = l->next) {
+ GnmCell *cell = l->data;
+ old_values = g_slist_prepend (old_values,
+ value_dup (cell->value));
+ gnm_cell_set_value (cell, value_new_int (0));
+ cell_queue_recalc (cell);
+ }
+ old_values = g_slist_reverse (old_values);
+
+ gnm_cell_eval (target);
+ y = cst + value_get_as_float (target->value);
+
+ for (l = input_cells; l; l = l->next) {
+ GnmCell *cell = l->data;
+ gnm_float x;
+ ok = gnm_solver_get_lp_coeff (target, cell, &x, err);
+ if (!ok)
+ goto fail;
+ if (x == 0 && !zero_too)
+ continue;
+
+ if (any) {
+ if (x < 0)
+ g_string_append (dst, " - ");
+ else
+ g_string_append (dst, " + ");
+ } else {
+ if (x < 0)
+ g_string_append_c (dst, '-');
+ }
+ x = gnm_abs (x);
+
+ if (x != 1) {
+ gnm_string_add_number (dst, x);
+ g_string_append_c (dst, ' ');
+ }
+
+ g_string_append (dst, glpk_var_name (ssol, cell));
+
+ any = TRUE;
+ }
+
+ if (!any || y)
+ gnm_string_add_number (dst, y);
+
+fail:
+ for (l = input_cells, ol = old_values;
+ l;
+ l = l->next, ol = ol->next) {
+ GnmCell *cell = l->data;
+ GnmValue *old = ol->data;
+ gnm_cell_set_value (cell, old);
+ cell_queue_recalc (cell);
+ }
+ g_slist_free (old_values);
+
+ return ok;
+}
+
+static GString *
+glpk_create_program (Sheet *sheet, GOIOContext *io_context,
+ GnmSubSolver *ssol, GError **err)
+{
+ GnmSolverParameters *sp = sheet->solver_parameters;
+ GString *prg = NULL;
+ GString *constraints = g_string_new (NULL);
+ GString *binaries = g_string_new (NULL);
+ GString *integers = g_string_new (NULL);
+ GString *objfunc = g_string_new (NULL);
+ GSList *l;
+ GnmCell *target_cell = gnm_solver_param_get_target_cell (sp);
+ GSList *input_cells = gnm_solver_param_get_input_cells (sp);
+ gsize progress;
+
+ /* ---------------------------------------- */
+
+ if (ssol) {
+ unsigned ui;
+ GSList *l;
+
+ for (ui = 1, l = input_cells; l; ui++, l = l->next) {
+ GnmCell *cell = l->data;
+ char *name = g_strdup_printf ("X_%u", ui);
+ gnm_sub_solver_name_cell (ssol, cell, name);
+ g_free (name);
+ }
+ }
+
+ /* ---------------------------------------- */
+
+ progress = 2;
+ if (sp->options.assume_non_negative) progress++;
+ if (sp->options.assume_discrete) progress++;
+ progress += g_slist_length (sp->constraints);
+
+ go_io_count_progress_set (io_context, progress, 1);
+
+ /* ---------------------------------------- */
+
+ switch (sp->problem_type) {
+ case GNM_SOLVER_MINIMIZE:
+ g_string_append (objfunc, "Minimize\n");
+ break;
+ case GNM_SOLVER_MAXIMIZE:
+ g_string_append (objfunc, "Maximize\n");
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ go_io_count_progress_update (io_context, 1);
+
+ g_string_append (objfunc, " obj: ");
+ if (!glpk_affine_func (objfunc, target_cell, ssol,
+ TRUE, 0, input_cells, err))
+ goto fail;
+ g_string_append (objfunc, "\n");
+ go_io_count_progress_update (io_context, 1);
+
+ /* ---------------------------------------- */
+
+ if (sp->options.assume_non_negative) {
+ GSList *l;
+ for (l = input_cells; l; l = l->next) {
+ GnmCell *cell = l->data;
+ g_string_append_printf (constraints, " %s >= 0\n",
+ glpk_var_name (ssol, cell));
+ }
+ go_io_count_progress_update (io_context, 1);
+ }
+
+ if (sp->options.assume_discrete) {
+ GSList *l;
+ for (l = input_cells; l; l = l->next) {
+ GnmCell *cell = l->data;
+ g_string_append_printf (integers, " %s\n",
+ glpk_var_name (ssol, cell));
+ }
+ go_io_count_progress_update (io_context, 1);
+ }
+
+ for (l = sp->constraints; l; l = l->next) {
+ GnmSolverConstraint *c = l->data;
+ const char *op = NULL;
+ gboolean right_small = TRUE;
+ int i;
+ gnm_float cl, cr;
+ GnmCell *lhs, *rhs;
+ GString *type = NULL;
+
+ switch (c->type) {
+ case GNM_SOLVER_LE:
+ op = "<=";
+ right_small = FALSE;
+ break;
+ case GNM_SOLVER_GE:
+ op = ">=";
+ break;
+ case GNM_SOLVER_EQ:
+ op = "=";
+ break;
+ case GNM_SOLVER_INTEGER:
+ type = integers;
+ break;
+ case GNM_SOLVER_BOOLEAN:
+ type = binaries;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ for (i = 0;
+ gnm_solver_constraint_get_part (c, sp, i,
+ &lhs, &cl,
+ &rhs, &cr);
+ i++) {
+ if (type) {
+ g_string_append_printf
+ (type, " %s\n",
+ glpk_var_name (ssol, lhs));
+ } else {
+ gboolean ok;
+
+ g_string_append_c (constraints, ' ');
+
+ ok = glpk_affine_func
+ (constraints, lhs, ssol,
+ FALSE, cl, input_cells, err);
+ if (!ok)
+ goto fail;
+
+ g_string_append_c (constraints, ' ');
+ g_string_append (constraints, op);
+ g_string_append_c (constraints, ' ');
+
+ ok = glpk_affine_func
+ (constraints, rhs, ssol,
+ FALSE, cr, input_cells, err);
+ if (!ok)
+ goto fail;
+
+ g_string_append (constraints, "\n");
+ }
+ }
+
+ go_io_count_progress_update (io_context, 1);
+ }
+
+ /* ---------------------------------------- */
+
+ prg = g_string_new (NULL);
+ g_string_append_printf (prg,
+ "\\ Created by Gnumeric %s\n\n",
+ GNM_VERSION_FULL);
+ go_string_append_gstring (prg, objfunc);
+ g_string_append (prg, "\nSubject to\n");
+ go_string_append_gstring (prg, constraints);
+ if (integers->len > 0) {
+ g_string_append (prg, "\nGeneral\n");
+ go_string_append_gstring (prg, integers);
+ }
+ if (binaries->len > 0) {
+ g_string_append (prg, "\nBinary\n");
+ go_string_append_gstring (prg, binaries);
+ }
+ g_string_append (prg, "\nEnd\n");
+
+fail:
+ g_string_free (objfunc, TRUE);
+ g_string_free (constraints, TRUE);
+ g_string_free (integers, TRUE);
+ g_string_free (binaries, TRUE);
+ g_slist_free (input_cells);
+
+ return prg;
+}
+
+void
+glpk_file_save (GOFileSaver const *fs, GOIOContext *io_context,
+ WorkbookView const *wb_view, GsfOutput *output)
+{
+ Sheet *sheet = wb_view_cur_sheet (wb_view);
+ GError *err = NULL;
+ GString *prg;
+ GnmLocale *locale;
+ GnmSubSolver *ssol = g_object_get_data (G_OBJECT (fs), "solver");
+
+ go_io_progress_message (io_context,
+ _("Writing glpk file..."));
+
+ locale = gnm_push_C_locale ();
+ prg = glpk_create_program (sheet, io_context, ssol, &err);
+ gnm_pop_C_locale (locale);
+
+ workbook_recalc (sheet->workbook);
+
+ if (!prg) {
+ go_cmd_context_error_import (GO_CMD_CONTEXT (io_context),
+ err ? err->message : "?");
+ goto fail;
+ }
+
+ gsf_output_write (output, prg->len, prg->str);
+ g_string_free (prg, TRUE);
+
+fail:
+ go_io_progress_unset (io_context);
+ if (err)
+ g_error_free (err);
+}
diff --git a/plugins/glpk/gnm-glpk.c b/plugins/glpk/gnm-glpk.c
new file mode 100644
index 0000000..1bd16f6
--- /dev/null
+++ b/plugins/glpk/gnm-glpk.c
@@ -0,0 +1,293 @@
+#include <gnumeric-config.h>
+#include "gnumeric.h"
+#include <tools/gnm-solver.h>
+#include "cell.h"
+#include "sheet.h"
+#include "value.h"
+#include "ranges.h"
+#include "gutils.h"
+#include <gsf/gsf-impl-utils.h>
+#include <gsf/gsf-input-textline.h>
+#include <gsf/gsf-input-stdio.h>
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#define PRIVATE_KEY "::glpk::"
+
+typedef struct {
+ GnmSubSolver *parent;
+ char *result_filename;
+ GnmSheetRange srinput;
+} GnmGlpk;
+
+static void
+gnm_glpk_cleanup (GnmGlpk *lp)
+{
+ gnm_sub_solver_clear (lp->parent);
+ if (lp->result_filename) {
+ g_unlink (lp->result_filename);
+ g_free (lp->result_filename);
+ lp->result_filename = NULL;
+ }
+}
+
+static void
+gnm_glpk_final (GnmGlpk *lp)
+{
+ gnm_glpk_cleanup (lp);
+ g_free (lp);
+}
+
+static gboolean
+write_program (GnmSolver *sol, WorkbookControl *wbc, GError **err)
+{
+ GnmSubSolver *subsol = GNM_SUB_SOLVER (sol);
+ GOFileSaver *fs;
+
+ fs = go_file_saver_for_mime_type ("application/glpk");
+ if (!fs) {
+ g_set_error (err, G_FILE_ERROR, 0,
+ _("The GLPK exporter is not available."));
+ return FALSE;
+ }
+
+ return gnm_solver_saveas (sol, wbc, fs,
+ "program-XXXXXX.cplex",
+ &subsol->program_filename,
+ err);
+}
+
+static void
+gnm_glpk_read_solution (GnmGlpk *lp)
+{
+ GnmSubSolver *subsol = lp->parent;
+ GnmSolver *sol = GNM_SOLVER (subsol);
+ GsfInput *input;
+ GsfInputTextline *tl;
+ const char *line;
+ unsigned rows, cols, c, r;
+ int pstat, dstat;
+ double val;
+ GnmSolverResult *result;
+ int width, height;
+
+ input = gsf_input_stdio_new (lp->result_filename, NULL);
+ if (!input)
+ return;
+
+ 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);
+
+ line = gsf_input_textline_utf8_gets (tl);
+ if (line == NULL ||
+ sscanf (line, "%u %u", &rows, &cols) != 2 ||
+ cols != g_hash_table_size (subsol->cell_from_name))
+ goto fail;
+
+ line = gsf_input_textline_utf8_gets (tl);
+ if (line == NULL ||
+ sscanf (line, "%d %d %lg", &pstat, &dstat, &val) != 3)
+ goto fail;
+ result->value = val;
+ switch (pstat) {
+ case 2:
+ case 5:
+ result->quality = GNM_SOLVER_RESULT_OPTIMAL;
+ break;
+ case 3:
+ case 4:
+ result->quality = GNM_SOLVER_RESULT_INFEASIBLE;
+ break;
+ case 6:
+ result->quality = GNM_SOLVER_RESULT_UNBOUNDED;
+ break;
+ default:
+ goto fail;
+ }
+
+ for (r = 1; r <= rows; r++) {
+ line = gsf_input_textline_utf8_gets (tl);
+ if (!line)
+ goto fail;
+ }
+
+ for (c = 1; c <= cols; c++) {
+ double pval, dval;
+ unsigned cstat;
+ int x, y;
+
+ line = gsf_input_textline_utf8_gets (tl);
+ if (line == NULL ||
+ sscanf (line, "%u %lg %lg", &cstat, &pval, &dval) != 3)
+ goto fail;
+
+ x = (c - 1) % width;
+ y = (c - 1) / width;
+ value_array_set (result->solution, x, y,
+ value_new_float (pval));
+ }
+
+ gnm_solver_set_status (sol, GNM_SOLVER_STATUS_DONE);
+ g_object_set (subsol, "result", result, NULL);
+ g_object_unref (result);
+
+ g_object_unref (tl);
+ return;
+
+fail:
+ g_object_unref (tl);
+ g_object_unref (result);
+ gnm_solver_set_status (sol, GNM_SOLVER_STATUS_ERROR);
+}
+
+
+static void
+cb_child_exit (GPid pid, gint status, GnmGlpk *lp)
+{
+ GnmSubSolver *subsol = lp->parent;
+ GnmSolver *sol = GNM_SOLVER (subsol);
+
+ if (sol->status != GNM_SOLVER_STATUS_RUNNING)
+ return;
+
+ if (WIFEXITED (status)) {
+ switch (WEXITSTATUS (status)) {
+ case 0: {
+ GnmLocale *locale = gnm_push_C_locale ();
+ gnm_glpk_read_solution (lp);
+ gnm_pop_C_locale (locale);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (sol->status == GNM_SOLVER_STATUS_RUNNING)
+ gnm_solver_set_status (sol, GNM_SOLVER_STATUS_ERROR);
+}
+
+static void
+cb_child_setup (gpointer user)
+{
+ const char *lcvars[] = {
+ "LC_ALL",
+ "LC_MESSAGES",
+ "LC_CTYPE",
+ "LC_NUMERIC"
+ };
+ unsigned ui;
+
+ g_unsetenv ("LANG");
+ for (ui = 0; ui < G_N_ELEMENTS (lcvars); ui++) {
+ const char *v = lcvars[ui];
+ if (g_getenv (v))
+ g_setenv (v, "C", TRUE);
+ }
+}
+
+static gboolean
+gnm_glpk_prepare (GnmSolver *sol, WorkbookControl *wbc, GError **err,
+ GnmGlpk *lp)
+{
+ gboolean ok;
+ int fd;
+
+ g_return_val_if_fail (sol->status == GNM_SOLVER_STATUS_READY, FALSE);
+
+ gnm_solver_set_status (sol, GNM_SOLVER_STATUS_PREPARING);
+ ok = write_program (sol, wbc, err);
+ if (!ok)
+ goto fail;
+
+ fd = g_file_open_tmp ("program-XXXXXX.out", &lp->result_filename, err);
+ if (fd == -1) {
+ g_set_error (err, G_FILE_ERROR, 0,
+ _("Failed to create file for solution"));
+ goto fail;
+ }
+
+ close (fd);
+
+ gnm_solver_set_status (sol, GNM_SOLVER_STATUS_PREPARED);
+
+ return TRUE;
+
+fail:
+ gnm_glpk_cleanup (lp);
+ gnm_solver_set_status (sol, GNM_SOLVER_STATUS_ERROR);
+ return FALSE;
+}
+
+static gboolean
+gnm_glpk_start (GnmSolver *sol, WorkbookControl *wbc, GError **err,
+ GnmGlpk *lp)
+{
+ GnmSubSolver *subsol = GNM_SUB_SOLVER (sol);
+ gboolean ok;
+ gchar *argv[6];
+
+ g_return_val_if_fail (sol->status == GNM_SOLVER_STATUS_PREPARED, FALSE);
+
+ argv[0] = (gchar *)"glpsol";
+ argv[1] = (gchar *)"--write";
+ argv[2] = lp->result_filename;
+ argv[3] = (gchar *)"--cpxlp";
+ argv[4] = subsol->program_filename;
+ argv[5] = NULL;
+
+ ok = gnm_sub_solver_spawn (subsol, argv,
+ cb_child_setup, NULL,
+ (GChildWatchFunc)cb_child_exit, lp,
+ NULL, NULL,
+ NULL, NULL,
+ err);
+
+ return ok;
+}
+
+static gboolean
+gnm_glpk_stop (GnmSolver *sol, GError *err, GnmGlpk *lp)
+{
+ g_return_val_if_fail (sol->status != GNM_SOLVER_STATUS_RUNNING, FALSE);
+
+ gnm_glpk_cleanup (lp);
+
+ gnm_solver_set_status (sol, GNM_SOLVER_STATUS_CANCELLED);
+
+ return TRUE;
+}
+
+GnmSolver *
+glpk_solver_factory (GnmSolverFactory *factory, GnmSolverParameters *params);
+
+GnmSolver *
+glpk_solver_factory (GnmSolverFactory *factory, GnmSolverParameters *params)
+{
+ GnmSolver *res = g_object_new (GNM_SUB_SOLVER_TYPE,
+ "params", params,
+ NULL);
+ 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);
+ g_signal_connect (res, "stop", G_CALLBACK (gnm_glpk_stop), lp);
+
+ g_object_set_data_full (G_OBJECT (res), PRIVATE_KEY, lp,
+ (GDestroyNotify)gnm_glpk_final);
+
+ return res;
+}
diff --git a/plugins/glpk/plugin.xml.in b/plugins/glpk/plugin.xml.in
new file mode 100644
index 0000000..f9db0da
--- /dev/null
+++ b/plugins/glpk/plugin.xml.in
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin id="Gnumeric_glpk">
+ <information>
+ <_name>GLPK Linear Program Solver Interface</_name>
+ <_description>Solver Interface to GLPK</_description>
+ </information>
+ <loader type="Gnumeric_Builtin:module">
+ <attribute name="module_file" value="glpk"/>
+ </loader>
+ <services>
+ <service type="file_saver"
+ id="glpk"
+ save_scope="sheet"
+ file_extension="cplex"
+ mime_type="application/glpk"
+ format_level="write_only">
+ <information>
+ <_description>GLPK Linear Program Solver</_description>
+ </information>
+ </service>
+ <service type="solver"
+ id="glpk"
+ problem_type="mip">
+ <information>
+ <_description>GLPK</_description>
+ </information>
+ </service>
+ </services>
+</plugin>
diff --git a/plugins/lpsolve/gnm-lpsolve.c b/plugins/lpsolve/gnm-lpsolve.c
index 85b56b9..27605a4 100644
--- a/plugins/lpsolve/gnm-lpsolve.c
+++ b/plugins/lpsolve/gnm-lpsolve.c
@@ -30,6 +30,13 @@ gnm_lpsolve_cleanup (GnmLPSolve *lp)
}
}
+static void
+gnm_lpsolve_final (GnmLPSolve *lp)
+{
+ gnm_lpsolve_cleanup (lp);
+ g_free (lp);
+}
+
static gboolean
write_program (GnmSolver *sol, WorkbookControl *wbc, GError **err)
{
@@ -184,6 +191,9 @@ cb_child_exit (GPid pid, gint status, GnmLPSolve *lp)
new_status = GNM_SOLVER_STATUS_ERROR;
break;
}
+ } else {
+ /* Something bad. */
+ new_status = GNM_SOLVER_STATUS_ERROR;
}
gnm_solver_set_status (sol, new_status);
@@ -286,7 +296,7 @@ lpsolve_solver_factory (GnmSolverFactory *factory, GnmSolverParameters *params)
g_signal_connect (res, "stop", G_CALLBACK (gnm_lpsolve_stop), lp);
g_object_set_data_full (G_OBJECT (res), PRIVATE_KEY, lp,
- (GDestroyNotify)g_free);
+ (GDestroyNotify)gnm_lpsolve_final);
return res;
}
diff --git a/plugins/lpsolve/lpsolve-write.c b/plugins/lpsolve/lpsolve-write.c
index 29cf6f0..8f19e1f 100644
--- a/plugins/lpsolve/lpsolve-write.c
+++ b/plugins/lpsolve/lpsolve-write.c
@@ -157,7 +157,8 @@ fail:
}
static GString *
-lpsolve_create_program (Sheet *sheet, GOIOContext *io_context, GError **err)
+lpsolve_create_program (Sheet *sheet, GOIOContext *io_context,
+ GnmSubSolver *ssol, GError **err)
{
GnmSolverParameters *sp = sheet->solver_parameters;
GString *prg = NULL;
@@ -319,12 +320,13 @@ lpsolve_file_save (GOFileSaver const *fs, GOIOContext *io_context,
GError *err = NULL;
GString *prg;
GnmLocale *locale;
+ GnmSubSolver *ssol = g_object_get_data (G_OBJECT (fs), "solver");
go_io_progress_message (io_context,
_("Writing lpsolve file..."));
locale = gnm_push_C_locale ();
- prg = lpsolve_create_program (sheet, io_context, &err);
+ prg = lpsolve_create_program (sheet, io_context, ssol, &err);
gnm_pop_C_locale (locale);
workbook_recalc (sheet->workbook);
diff --git a/plugins/lpsolve/plugin.xml.in b/plugins/lpsolve/plugin.xml.in
index 51bd7ee..deedb12 100644
--- a/plugins/lpsolve/plugin.xml.in
+++ b/plugins/lpsolve/plugin.xml.in
@@ -2,7 +2,7 @@
<plugin id="Gnumeric_lpsolve">
<information>
<_name>LPSolve Linear Program Solver Interface</_name>
- <_description>Specialized Export of Solver Data to LPSolve</_description>
+ <_description>Solver Interface to LPSolve</_description>
</information>
<loader type="Gnumeric_Builtin:module">
<attribute name="module_file" value="lpsolve"/>
diff --git a/po-functions/POTFILES.in b/po-functions/POTFILES.in
index 9dd8303..b1daa3c 100644
--- a/po-functions/POTFILES.in
+++ b/po-functions/POTFILES.in
@@ -31,11 +31,14 @@ plugins/fn-stat/functions.c
plugins/fn-string/functions.c
plugins/fn-tsa/functions.c
plugins/gda/plugin-gda.c
+plugins/glpk/glpk-write.c
+plugins/glpk/gnm-glpk.c
plugins/gnome-db/plugin-gnomedb.c
plugins/html/html_read.c
plugins/html/roff.c
plugins/lotus-123/boot.c
plugins/lotus-123/lotus.c
+plugins/lpsolve/gnm-lpsolve.c
plugins/lpsolve/lpsolve-write.c
plugins/mps/mps.c
plugins/mps/parser.c
@@ -197,6 +200,7 @@ src/tools/dao.c
src/tools/data-shuffling.c
src/tools/fill-series.c
src/tools/filter.c
+src/tools/gnm-solver.c
src/tools/random-generator.c
src/tools/random-generator-cor.c
src/tools/scenarios.c
diff --git a/po-functions/POTFILES.skip b/po-functions/POTFILES.skip
index c5d0ff2..000ac16 100644
--- a/po-functions/POTFILES.skip
+++ b/po-functions/POTFILES.skip
@@ -37,6 +37,7 @@ plugins/fn-string/plugin.xml.in
plugins/fn-tsa/plugin.xml.in
plugins/gda/plugin.xml.in
plugins/gda/ui.xml.in
+plugins/glpk/plugin.xml.in
plugins/gnome-db/plugin.xml.in
plugins/gnome-glossary/plugin.xml.in
plugins/html/plugin.xml.in
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a02eb50..7eba53c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -60,6 +60,9 @@ plugins/fn-tsa/plugin.xml.in
plugins/gda/plugin-gda.c
plugins/gda/plugin.xml.in
plugins/gda/ui.xml.in
+plugins/glpk/glpk-write.c
+plugins/glpk/gnm-glpk.c
+plugins/glpk/plugin.xml.in
plugins/gnome-db/plugin-gnomedb.c
plugins/gnome-db/plugin.xml.in
plugins/gnome-glossary/plugin.xml.in
@@ -69,6 +72,7 @@ plugins/html/roff.c
plugins/lotus-123/boot.c
plugins/lotus-123/lotus.c
plugins/lotus-123/plugin.xml.in
+plugins/lpsolve/gnm-lpsolve.c
plugins/lpsolve/lpsolve-write.c
plugins/lpsolve/plugin.xml.in
plugins/mps/mps.c
@@ -326,6 +330,7 @@ src/tools/dao.c
src/tools/data-shuffling.c
src/tools/fill-series.c
src/tools/filter.c
+src/tools/gnm-solver.c
src/tools/random-generator.c
src/tools/random-generator-cor.c
src/tools/scenarios.c
diff --git a/src/dialogs/dialog-solver.c b/src/dialogs/dialog-solver.c
index 644ab48..42c7990 100644
--- a/src/dialogs/dialog-solver.c
+++ b/src/dialogs/dialog-solver.c
@@ -590,6 +590,8 @@ run_solver (SolverState *state, GnmSolverParameters *param)
&err);
if (ok) {
dialog_res = go_gtk_dialog_run (dialog, top);
+ if (dialog_res == GTK_RESPONSE_YES && !sol->result)
+ dialog_res = GTK_RESPONSE_DELETE_EVENT;
} else {
gtk_widget_destroy (GTK_WIDGET (dialog));
go_gtk_notice_dialog (top, GTK_MESSAGE_ERROR,
diff --git a/src/tools/gnm-solver.c b/src/tools/gnm-solver.c
index 96deb89..131c018 100644
--- a/src/tools/gnm-solver.c
+++ b/src/tools/gnm-solver.c
@@ -21,6 +21,17 @@
/* ------------------------------------------------------------------------- */
+static gboolean
+debug_solver (void)
+{
+ static int debug = -1;
+ if (debug == -1)
+ debug = gnm_debug_flag ("solver");
+ return debug;
+}
+
+/* ------------------------------------------------------------------------- */
+
GType
gnm_solver_status_get_type (void)
{
@@ -896,6 +907,11 @@ gnm_solver_saveas (GnmSolver *solver, WorkbookControl *wbc,
return FALSE;
}
+ /* Give the saver a way to talk to the solver. */
+ g_object_set_data_full (G_OBJECT (fs),
+ "solver", g_object_ref (solver),
+ (GDestroyNotify)g_object_unref);
+
output = gsf_output_stdio_new_FILE (*filename, file, TRUE);
io_context = go_io_context_new (GO_CMD_CONTEXT (wbc));
wbv_save_to_output (wbv, fs, output, io_context);
@@ -903,6 +919,8 @@ gnm_solver_saveas (GnmSolver *solver, WorkbookControl *wbc,
g_object_unref (io_context);
g_object_unref (output);
+ g_object_set_data (G_OBJECT (fs), "solver", NULL);
+
if (!ok) {
g_set_error (err, G_FILE_ERROR, 0,
_("Failed to save linear program"));
@@ -1046,6 +1064,9 @@ gnm_sub_solver_clear (GnmSubSolver *subsol)
g_free (subsol->program_filename);
subsol->program_filename = NULL;
}
+
+ g_hash_table_remove_all (subsol->cell_from_name);
+ g_hash_table_remove_all (subsol->name_from_cell);
}
static void
@@ -1065,11 +1086,20 @@ gnm_sub_solver_init (GnmSubSolver *subsol)
for (i = 0; i <= 2; i++)
subsol->fd[i] = -1;
+
+ subsol->cell_from_name =
+ g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, NULL);
+ subsol->name_from_cell =
+ g_hash_table_new (g_direct_hash, g_direct_equal);
}
static void
cb_child_exit (GPid pid, gint status, GnmSubSolver *subsol)
{
+ if (debug_solver ())
+ g_printerr ("Solver process exited.\n");
+
subsol->child_watch = 0;
if (subsol->child_exit)
subsol->child_exit (pid, status, subsol->exit_data);
@@ -1095,6 +1125,12 @@ gnm_sub_solver_spawn (GnmSubSolver *subsol,
if (!g_path_is_absolute (argv[0]))
spflags |= G_SPAWN_SEARCH_PATH;
+ if (io_stdout == NULL)
+ spflags |= G_SPAWN_STDOUT_TO_DEV_NULL;
+
+ if (debug_solver ())
+ g_printerr ("Spawning %s\n", argv[0]);
+
ok = g_spawn_async_with_pipes
(g_get_home_dir (), /* PWD */
argv,
@@ -1103,8 +1139,8 @@ gnm_sub_solver_spawn (GnmSubSolver *subsol,
child_setup, setup_data,
&subsol->child_pid,
NULL, /* stdin */
- &subsol->fd[1], /* stdout */
- &subsol->fd[2], /* stderr */
+ io_stdout ? &subsol->fd[1] : NULL, /* stdout */
+ io_stdout ? &subsol->fd[2] : NULL, /* stderr */
err);
if (!ok)
goto fail;
@@ -1151,6 +1187,35 @@ fail:
return FALSE;
}
+const char *
+gnm_sub_solver_name_cell (GnmSubSolver *subsol, GnmCell const *cell,
+ const char *name)
+{
+ char *name_copy = g_strdup (name);
+
+ g_hash_table_insert (subsol->cell_from_name,
+ name_copy,
+ (gpointer)cell);
+ g_hash_table_insert (subsol->name_from_cell,
+ (gpointer)cell,
+ name_copy);
+
+ return name_copy;
+}
+
+GnmCell *
+gnm_sub_solver_find_cell (GnmSubSolver *subsol, const char *name)
+{
+ return g_hash_table_lookup (subsol->cell_from_name, name);
+}
+
+const char *
+gnm_sub_solver_get_cell_name (GnmSubSolver *subsol,
+ GnmCell const *cell)
+{
+ return g_hash_table_lookup (subsol->name_from_cell, (gpointer)cell);
+}
+
void
gnm_sub_solver_flush (GnmSubSolver *subsol)
{
@@ -1244,7 +1309,7 @@ gnm_solver_factory_create (GnmSolverFactory *factory,
void
gnm_solver_db_register (GnmSolverFactory *factory)
{
- if (gnm_debug_flag ("solver"))
+ if (debug_solver ())
g_printerr ("Registering %s\n", factory->id);
g_object_ref (factory);
solvers = g_slist_prepend (solvers, factory);
@@ -1253,7 +1318,7 @@ gnm_solver_db_register (GnmSolverFactory *factory)
void
gnm_solver_db_unregister (GnmSolverFactory *factory)
{
- if (gnm_debug_flag ("solver"))
+ if (debug_solver ())
g_printerr ("Unregistering %s\n", factory->id);
solvers = g_slist_remove (solvers, factory);
g_object_unref (factory);
diff --git a/src/tools/gnm-solver.h b/src/tools/gnm-solver.h
index 37de650..d93da60 100644
--- a/src/tools/gnm-solver.h
+++ b/src/tools/gnm-solver.h
@@ -245,6 +245,10 @@ typedef struct {
char *program_filename;
+ /* Hashes between char* and cell*. */
+ GHashTable *cell_from_name;
+ GHashTable *name_from_cell;
+
GPid child_pid;
guint child_watch;
@@ -277,6 +281,13 @@ gboolean gnm_sub_solver_spawn
void gnm_sub_solver_flush (GnmSubSolver *subsol);
+const char *gnm_sub_solver_name_cell (GnmSubSolver *subsol,
+ GnmCell const *cell,
+ const char *name);
+GnmCell *gnm_sub_solver_find_cell (GnmSubSolver *subsol, const char *name);
+const char *gnm_sub_solver_get_cell_name (GnmSubSolver *subsol,
+ GnmCell const *cell);
+
/* ------------------------------------------------------------------------- */
#define GNM_SOLVER_FACTORY_TYPE (gnm_solver_factory_get_type ())
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]