[gnumeric] lpsolve: new output format.
- From: Morten Welinder <mortenw src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gnumeric] lpsolve: new output format.
- Date: Tue, 27 Oct 2009 03:11:01 +0000 (UTC)
commit 04a97eeb509230f335d3401852f56af62c986a64
Author: Morten Welinder <terra gnome org>
Date: Mon Oct 26 23:10:10 2009 -0400
lpsolve: new output format.
NEWS | 1 +
configure.in | 29 ++--
plugins/Makefile.am | 2 +-
plugins/lpsolve/.gitignore | 9 +
plugins/lpsolve/ChangeLog | 3 +
plugins/lpsolve/Makefile.am | 20 +++
plugins/lpsolve/boot.c | 24 +++
plugins/lpsolve/boot.h | 7 +
plugins/lpsolve/lpsolve-write.c | 362 +++++++++++++++++++++++++++++++++++++++
plugins/lpsolve/plugin.xml.in | 17 ++
10 files changed, 459 insertions(+), 15 deletions(-)
---
diff --git a/NEWS b/NEWS
index 427f592..204291b 100644
--- a/NEWS
+++ b/NEWS
@@ -28,6 +28,7 @@ Morten:
* Fix Excel-to-Gnumeric paste problem. [#381732, #388531].
* Fix Alt-Enter problems. [#597295]
* Make DATE work for pre-1900 dates. [#599365]
+ * Add lpsolve output format.
Yaacov Zamir:
* Hebrew Calendar functions. [#153738]
diff --git a/configure.in b/configure.in
index d994f63..9eb8be8 100644
--- a/configure.in
+++ b/configure.in
@@ -1059,31 +1059,32 @@ plugins/fn-stat/Makefile
plugins/fn-string/Makefile
plugins/fn-random/Makefile
plugins/fn-tsa/Makefile
-plugins/sc/Makefile
-plugins/sylk/Makefile
+plugins/applix/Makefile
+plugins/corba/Makefile
+plugins/dif/Makefile
plugins/excel/Makefile
plugins/gda/Makefile
plugins/gnome-db/Makefile
+plugins/gnome-glossary/Makefile
+plugins/html/Makefile
plugins/lotus-123/Makefile
+plugins/lpsolve/Makefile
+plugins/mps/Makefile
plugins/oleo/Makefile
-plugins/python-loader/Makefile
-plugins/gnome-glossary/Makefile
-plugins/py-func/Makefile
-plugins/corba/Makefile
+plugins/openoffice/Makefile
+plugins/paradox/Makefile
plugins/perl-func/Makefile
plugins/perl-loader/Makefile
-plugins/xbase/Makefile
-plugins/html/Makefile
-plugins/dif/Makefile
-plugins/qpro/Makefile
plugins/plan-perfect/Makefile
-plugins/applix/Makefile
-plugins/openoffice/Makefile
plugins/psiconv/Makefile
-plugins/mps/Makefile
+plugins/py-func/Makefile
+plugins/python-loader/Makefile
+plugins/qpro/Makefile
plugins/sample_datasource/Makefile
-plugins/paradox/Makefile
+plugins/sc/Makefile
+plugins/sylk/Makefile
plugins/uihello/Makefile
+plugins/xbase/Makefile
po/Makefile.in
po-functions/Makefile.in
schemas/Makefile
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index a7e86dc..062da59 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
+ plan-perfect applix openoffice lpsolve
if ENABLE_SOLVER
SUBDIRS_FILE_FORMATS += mps
diff --git a/plugins/lpsolve/.gitignore b/plugins/lpsolve/.gitignore
new file mode 100644
index 0000000..6429675
--- /dev/null
+++ b/plugins/lpsolve/.gitignore
@@ -0,0 +1,9 @@
+Makefile
+Makefile.in
+.deps
+.libs
+*.lo
+*.la
+plugin.xml
+*.loT
+*.sw*
diff --git a/plugins/lpsolve/ChangeLog b/plugins/lpsolve/ChangeLog
new file mode 100644
index 0000000..50ac830
--- /dev/null
+++ b/plugins/lpsolve/ChangeLog
@@ -0,0 +1,3 @@
+2009-10-26 Morten Welinder <terra gnome org>
+
+ * Initial Implementation
diff --git a/plugins/lpsolve/Makefile.am b/plugins/lpsolve/Makefile.am
new file mode 100644
index 0000000..88bcca0
--- /dev/null
+++ b/plugins/lpsolve/Makefile.am
@@ -0,0 +1,20 @@
+AM_CPPFLAGS = \
+ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+ -I$(top_srcdir)/src -I$(top_builddir)/src \
+ $(GNUMERIC_CFLAGS)
+
+gnumeric_plugin_lpsolvedir = $(gnumeric_plugindir)/lpsolve
+xmldir = $(gnumeric_plugin_lpsolvedir)
+gnumeric_plugin_lpsolve_LTLIBRARIES = lpsolve.la
+lpsolve_la_LDFLAGS = -module $(GNUMERIC_PLUGIN_LDFLAGS)
+lpsolve_la_SOURCES = \
+ boot.h boot.c \
+ lpsolve-write.h lpsolve-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/lpsolve/boot.c b/plugins/lpsolve/boot.c
new file mode 100644
index 0000000..fef583f
--- /dev/null
+++ b/plugins/lpsolve/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/lpsolve/boot.h b/plugins/lpsolve/boot.h
new file mode 100644
index 0000000..e2b1b2c
--- /dev/null
+++ b/plugins/lpsolve/boot.h
@@ -0,0 +1,7 @@
+#ifndef GNUMERIC_LPSOLVE_BOOT_H
+#define GNUMERIC_LPSOLVE_BOOT_H
+
+#include "gnumeric.h"
+#include <goffice/goffice.h>
+
+#endif
diff --git a/plugins/lpsolve/lpsolve-write.c b/plugins/lpsolve/lpsolve-write.c
new file mode 100644
index 0000000..5e36cc6
--- /dev/null
+++ b/plugins/lpsolve/lpsolve-write.c
@@ -0,0 +1,362 @@
+/*
+ * 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 <numbers.h>
+#include <workbook-view.h>
+#include <sheet.h>
+#include <workbook.h>
+#include <value.h>
+#include <cell.h>
+#include <solver.h>
+#include <ranges.h>
+#include <parse-util.h>
+#include <goffice/goffice.h>
+#include <glib/gi18n-lib.h>
+
+#include <gsf/gsf-output.h>
+#include <string.h>
+
+
+static gboolean
+gnm_solver_get_lp_coeff (GnmCell *target, GnmCell *cell,
+ gnm_float *x, GError **err)
+{
+ gnm_float x0, x1;
+ GnmValue *old = value_dup (cell->value);
+ gboolean res = FALSE;
+
+ 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);
+
+ gnm_cell_set_value (cell, value_new_float (0));
+ cell_queue_recalc (cell);
+ gnm_cell_eval (target);
+ if (!VALUE_IS_NUMBER (target->value))
+ goto fail;
+ x0 = 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, old);
+ 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 *
+lpsolve_var_name (GnmCell const *cell)
+{
+ return cell_name (cell);
+}
+
+static double
+force_round (double d)
+{
+ volatile double *pd = &d;
+ return *pd;
+}
+
+static void
+lpsolve_add_number (GString *buf, double d)
+{
+ char tmp[G_ASCII_DTOSTR_BUF_SIZE + 1];
+ double d1 = force_round (d);
+ double d2;
+
+ g_ascii_formatd (tmp, G_ASCII_DTOSTR_BUF_SIZE, "%.15g", d1);
+
+ d2 = force_round (g_ascii_strtod (tmp, NULL));
+ if (d1 != d2) {
+ g_ascii_formatd (tmp, G_ASCII_DTOSTR_BUF_SIZE, "%.16g", d1);
+ }
+
+ g_string_append (buf, tmp);
+}
+
+static gboolean
+lpsolve_affine_func (GString *dst, GnmCell *target,
+ GSList *input_cells, GError **err)
+{
+ GSList *l;
+ gboolean any = FALSE;
+ gnm_float y = value_get_as_float (target->value);
+
+ for (l = input_cells; l; l = l->next) {
+ GnmCell *cell = l->data;
+ gnm_float x;
+ gboolean ok = gnm_solver_get_lp_coeff (target, cell, &x, err);
+ if (!ok)
+ return FALSE;
+ if (x == 0)
+ continue;
+
+ y -= x * value_get_as_float (cell->value);
+
+ 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) {
+ lpsolve_add_number (dst, x);
+ g_string_append_c (dst, ' ');
+ }
+
+ g_string_append (dst, lpsolve_var_name (cell));
+
+ any = TRUE;
+ }
+
+ if (!any || y)
+ lpsolve_add_number (dst, y);
+
+ return TRUE;
+}
+
+static GnmValue *
+cb_grab_cells (GnmCellIter const *iter, gpointer user)
+{
+ GList **the_list = user;
+ GnmCell *cell;
+
+ if (NULL == (cell = iter->cell))
+ cell = sheet_cell_create (iter->pp.sheet,
+ iter->pp.eval.col, iter->pp.eval.row);
+ *the_list = g_list_append (*the_list, cell);
+ return NULL;
+}
+
+static GString *
+lpsolve_create_program (Sheet *sheet, GError **err)
+{
+ SolverParameters const *sp = sheet->solver_parameters;
+ GString *prg = NULL;
+ GString *constraints = g_string_new (NULL);
+ GString *declarations = g_string_new (NULL);
+ GString *objfunc = g_string_new (NULL);
+ GSList *l, *input_cells = NULL;
+
+ /* This is insane -- why do we keep a string? */
+ {
+ GnmEvalPos ep;
+ GnmRange r;
+ GnmValue *vr;
+
+ if (!range_parse (&r, sp->input_entry_str,
+ gnm_sheet_get_size (sheet)))
+ goto fail;
+
+ vr = value_new_cellrange_r (sheet, &r);
+ eval_pos_init_sheet (&ep, sheet);
+ workbook_foreach_cell_in_range (&ep, vr, CELL_ITER_ALL,
+ cb_grab_cells, &input_cells);
+ value_release (vr);
+ }
+
+ /* ---------------------------------------- */
+
+ switch (sp->problem_type) {
+ case SolverEqualTo:
+ if (!lpsolve_affine_func (constraints, sp->target_cell,
+ input_cells, err))
+ goto fail;
+ /* FIXME -- what value goes here? */
+ g_string_append (constraints, " = 42;\n");
+ /* Fall through */
+ case SolverMinimize:
+ g_string_append (objfunc, "min: ");
+ break;
+ case SolverMaximize:
+ g_string_append (objfunc, "max: ");
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (!lpsolve_affine_func (objfunc, sp->target_cell,
+ input_cells, err))
+ goto fail;
+ g_string_append (objfunc, ";\n");
+
+ /* ---------------------------------------- */
+
+ if (sp->options.assume_non_negative) {
+ GSList *l;
+ for (l = input_cells; l; l = l->next) {
+ GnmCell *cell = l->data;
+ g_string_append (constraints,
+ lpsolve_var_name (cell));
+ g_string_append (constraints, " >= 0;\n");
+ }
+ }
+
+ if (sp->options.assume_discrete) {
+ GSList *l;
+ for (l = input_cells; l; l = l->next) {
+ GnmCell *cell = l->data;
+ g_string_append (declarations, "int ");
+ g_string_append (declarations,
+ lpsolve_var_name (cell));
+ g_string_append (declarations, ";\n");
+ }
+ }
+
+ for (l = sp->constraints; l; l = l->next) {
+ SolverConstraint *c = l->data;
+ const char *op = NULL;
+ const char *type = NULL;
+ int dx, dy;
+ gboolean right_small = TRUE;
+
+ switch (c->type) {
+ case SolverLE:
+ op = "<=";
+ right_small = FALSE;
+ break;
+ case SolverGE:
+ op = ">=";
+ break;
+ case SolverEQ:
+ op = "=";
+ break;
+ case SolverINT:
+ type = "int";
+ break;
+ case SolverBOOL:
+ type = "binary";
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ for (dy = 0; dy < c->rows; dy++) {
+ for (dx = 0; dx < c->cols; dx++) {
+ GnmCell *lhs =
+ sheet_cell_get (sheet,
+ c->lhs.col + dx,
+ c->lhs.row + dy);
+ GnmCell *rhs =
+ sheet_cell_get (sheet,
+ c->rhs.col + dx,
+ c->rhs.row + dy);
+
+ if (!lhs || (op && !rhs))
+ continue;
+
+ if (type) {
+ g_string_append (declarations, type);
+ g_string_append_c (declarations, ' ');
+ g_string_append (declarations, lpsolve_var_name (lhs));
+ g_string_append (declarations, ";\n");
+ } else {
+ gboolean ok;
+
+ ok = lpsolve_affine_func
+ (constraints, lhs, input_cells, err);
+ if (!ok)
+ goto fail;
+
+ g_string_append_c (constraints, ' ');
+ g_string_append (constraints, op);
+ g_string_append_c (constraints, ' ');
+
+ ok = lpsolve_affine_func
+ (constraints, rhs, input_cells, err);
+ if (!ok)
+ goto fail;
+
+ g_string_append (constraints, ";\n");
+ }
+ }
+ }
+ }
+
+ /* ---------------------------------------- */
+
+ prg = g_string_new (NULL);
+ g_string_append_printf (prg,
+ "/* Created by Gnumeric %s */\n",
+ GNM_VERSION_FULL);
+ g_string_append (prg, "\n/* Object function */\n");
+ go_string_append_gstring (prg, objfunc);
+ g_string_append (prg, "\n/* Constraints */\n");
+ go_string_append_gstring (prg, constraints);
+ g_string_append (prg, "\n/* Declarations */\n");
+ go_string_append_gstring (prg, declarations);
+ g_string_append (prg, "\n\n/* The End */\n");
+
+fail:
+ g_string_free (objfunc, TRUE);
+ g_string_free (constraints, TRUE);
+ g_string_free (declarations, TRUE);
+
+ return prg;
+}
+
+void
+lpsolve_file_save (GOFileSaver const *fs, GOIOContext *io_context,
+ WorkbookView const *wb_view, GsfOutput *output);
+
+void
+lpsolve_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;
+
+ workbook_recalc (sheet->workbook);
+
+ prg = lpsolve_create_program (sheet, &err);
+ if (!prg) {
+ go_cmd_context_error_import (GO_CMD_CONTEXT (io_context),
+ err->message);
+ g_error_free (err);
+ return;
+ }
+
+ gsf_output_write (output, prg->len, prg->str);
+ g_string_free (prg, TRUE);
+}
diff --git a/plugins/lpsolve/plugin.xml.in b/plugins/lpsolve/plugin.xml.in
new file mode 100644
index 0000000..476c642
--- /dev/null
+++ b/plugins/lpsolve/plugin.xml.in
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin id="Gnumeric_html">
+ <information>
+ <_name>LPSolve</_name>
+ <_description>Export to LPSolve</_description>
+ </information>
+ <loader type="Gnumeric_Builtin:module">
+ <attribute name="module_file" value="lpsolve"/>
+ </loader>
+ <services>
+ <service type="file_saver" id="lpsolve" file_extension="lp" format_level="write_only">
+ <information>
+ <_description>LPSolve</_description>
+ </information>
+ </service>
+ </services>
+</plugin>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]