[gnumeric] mps: re-implement.
- From: Morten Welinder <mortenw src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gnumeric] mps: re-implement.
- Date: Thu, 19 Nov 2009 20:27:07 +0000 (UTC)
commit c30dfc56693d0371bc1bcb81b11e268e17b95e89
Author: Morten Welinder <terra gnome org>
Date: Thu Nov 19 15:26:51 2009 -0500
mps: re-implement.
NEWS | 1 +
plugins/mps/ChangeLog | 4 +
plugins/mps/Makefile.am | 2 +-
plugins/mps/mps.c | 1025 ++++++++++++++++++++++-------------------------
plugins/mps/mps.h | 171 --------
plugins/mps/parser.c | 573 --------------------------
6 files changed, 485 insertions(+), 1291 deletions(-)
---
diff --git a/NEWS b/NEWS
index 610c85b..586ba18 100644
--- a/NEWS
+++ b/NEWS
@@ -24,6 +24,7 @@ Morten:
* Fix extreme case for R.PGAMMA.
* Fix solver undo/redo.
* Make solver parameter changes always persist. [#440664]
+ * Re-implement MPS importer.
--------------------------------------------------------------------------
Gnumeric 1.9.15
diff --git a/plugins/mps/ChangeLog b/plugins/mps/ChangeLog
index a85b982..9d367eb 100644
--- a/plugins/mps/ChangeLog
+++ b/plugins/mps/ChangeLog
@@ -1,3 +1,7 @@
+2009-11-19 Morten Welinder <terra gnome org>
+
+ * mps.c: Re-implement.
+
2009-11-17 Morten Welinder <terra gnome org>
* parser.c (mps_add_column, mps_add_rhs, mps_add_bound): Use
diff --git a/plugins/mps/Makefile.am b/plugins/mps/Makefile.am
index f730d8e..3456058 100644
--- a/plugins/mps/Makefile.am
+++ b/plugins/mps/Makefile.am
@@ -7,7 +7,7 @@ gnumeric_plugin_mpsdir = $(gnumeric_plugindir)/mps
xmldir = $(gnumeric_plugin_mpsdir)
gnumeric_plugin_mps_LTLIBRARIES = mps.la
mps_la_LDFLAGS = -module $(GNUMERIC_PLUGIN_LDFLAGS)
-mps_la_SOURCES = mps.c mps.h parser.c
+mps_la_SOURCES = mps.c
xml_in_files = plugin.xml.in
xml_DATA = $(xml_in_files:.xml.in=.xml)
diff --git a/plugins/mps/mps.c b/plugins/mps/mps.c
index 155da5a..f2ecc5a 100644
--- a/plugins/mps/mps.c
+++ b/plugins/mps/mps.c
@@ -1,19 +1,6 @@
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
- * mps.c: MPS file importer.
- *
- * Authors:
- * Jukka-Pekka Iivonen <jiivonen hutcs cs hut fi>
- *
- * MPS importer module. MPS format is a de facto standard ASCII format
- * among most of the commercial LP solvers.
- *
- * This implementation does not yet support ranges and all types
- * of bounds but is already quite suitable for testing the
- * solving algorithms. See, for example, the Netlib collection
- * of LP problems in MPS format (ftp://netlib2.cs.utk.edu/lp/data).
- *
- * Supported bound types are: UP and LO.
+ * 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
@@ -29,631 +16,577 @@
* 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 "mps.h"
-#include "cell.h"
-#include "sheet.h"
-#include "value.h"
#include <goffice/goffice.h>
-#include "workbook-view.h"
-#include "workbook.h"
+#include <glib/gi18n-lib.h>
+#include <gsf/gsf-input-textline.h>
+
#include <gnm-plugin.h>
-#include "ranges.h"
-#include "style.h"
-#include "value.h"
+#include <gutils.h>
+#include <workbook-view.h>
+#include <workbook.h>
+#include <sheet.h>
+#include <value.h>
+#include <mstyle.h>
+#include <sheet-style.h>
+#include <cell.h>
+#include <ranges.h>
+#include <expr.h>
#include <tools/gnm-solver.h>
-#include "sheet-style.h"
-#include "parse-util.h"
-#include "func.h"
-#include "expr.h"
-#include <glib/gi18n-lib.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdlib.h>
+
#include <string.h>
GNM_PLUGIN_MODULE_HEADER;
-
-/*************************************************************************
- *
- * Sheet creation.
- */
+typedef struct {
+ char *name;
+ GnmSolverConstraintType type;
+ GnmExpr const *expr;
+ gnm_float rhs;
+} MpsRow;
+
+typedef struct {
+ GOIOContext *io_context;
+
+ GsfInputTextline *input;
+ char *line;
+ GPtrArray *split;
+
+ GPtrArray *rows;
+ GHashTable *row_hash;
+
+ GHashTable *col_hash;
+
+ Workbook *wb;
+ Sheet *sheet;
+ GnmSolverParameters *param;
+} MpsState;
+
+/* ------------------------------------------------------------------------- */
+
+/* Vertical */
+enum { CONSTR_BASE_COL = 3 };
+enum { CONSTR_BASE_ROW = 8 };
+
+/* Vertical */
+enum { VAR_BASE_COL = 0 };
+enum { VAR_BASE_ROW = 8 };
+
+/* Horizontal */
+enum { OBJ_BASE_COL = 0 };
+enum { OBJ_BASE_ROW = 4 };
+
-/* Writes a string into a cell. */
static void
-mps_set_cell (Sheet *sheet, int col, int row, const gchar *str)
+mps_set_cell (MpsState *state, int col, int row, const gchar *str)
{
- GnmCell *cell = sheet_cell_fetch (sheet, col, row);
+ GnmCell *cell = sheet_cell_fetch (state->sheet, col, row);
gnm_cell_set_value (cell, value_new_string (str));
}
-/* Writes a gnm_float into a cell. */
static void
-mps_set_cell_float (Sheet *sheet, int col, int row, const gnm_float f)
+mps_set_expr (MpsState *state, int col, int row, GnmExpr const *expr)
+{
+ GnmCell *cell = sheet_cell_fetch (state->sheet, col, row);
+ GnmExprTop const *texpr = gnm_expr_top_new (expr);
+ gnm_cell_set_expr (cell, texpr);
+ gnm_expr_top_unref (texpr);
+}
+
+static void
+mps_set_cell_float (MpsState *state, int col, int row, const gnm_float f)
{
- GnmCell *cell = sheet_cell_fetch (sheet, col, row);
+ GnmCell *cell = sheet_cell_fetch (state->sheet, col, row);
gnm_cell_set_value (cell, value_new_float (f));
}
static void
-mps_set_style (Sheet *sh, int c1, int r1, int c2, int r2,
+mps_set_style (MpsState *state, int c1, int r1, int c2, int r2,
gboolean italic, gboolean bold, gboolean ulined)
{
- GnmStyle *mstyle;
- GnmRange range;
+ GnmStyle *mstyle = gnm_style_new ();
+ GnmRange range;
- mstyle = gnm_style_new ();
range_init (&range, c1, r1, c2, r2);
gnm_style_set_font_italic (mstyle, italic);
- gnm_style_set_font_bold (mstyle, bold);
- gnm_style_set_font_uline (mstyle, ulined);
- sheet_style_apply_range (sh, &range, mstyle);
+ gnm_style_set_font_bold (mstyle, bold);
+ gnm_style_set_font_uline (mstyle, ulined);
+ sheet_style_apply_range (state->sheet, &range, mstyle);
}
-/* Callback for the hash table mapping. */
-static void
-put_into_index (gpointer key, gpointer value, gpointer user_data)
-{
- MpsInputContext *ctxt = (MpsInputContext *) user_data;
- MpsColInfo *info = (MpsColInfo *) value;
-
- ctxt->col_name_tbl[info->index] = info->name;
-}
+/* ------------------------------------------------------------------------- */
-/* Make the constraint coefficient matrix and other preparations. */
-static void
-mps_prepare (WorkbookView *wbv, MpsInputContext *ctxt)
+static gboolean
+readline (MpsState *state)
{
- gint i, n, col;
- GSList *current, *tmp;
+ do {
+ char *line = state->line =
+ gsf_input_textline_utf8_gets (state->input);
- ctxt->rows = g_slist_reverse (ctxt->rows);
- ctxt->cols = g_slist_reverse (ctxt->cols);
+ if (!line)
+ return FALSE;
+ if (line[0] == '*' || line[0] == 0)
+ continue;
- ctxt->col_name_tbl = g_new (gchar *, ctxt->n_cols);
- g_hash_table_foreach (ctxt->col_hash, put_into_index, (gpointer) ctxt);
+ return g_ascii_isspace (line[0]);
+ } while (1);
+}
- ctxt->matrix = g_new (gnm_float *, ctxt->n_rows + ctxt->n_bounds);
- for (i = 0; i < ctxt->n_rows + ctxt->n_bounds; i++) {
- ctxt->matrix[i] = g_new (gnm_float, ctxt->n_cols);
- for (n = 0; n < ctxt->n_cols; n++)
- ctxt->matrix[i][n] = 0.0;
- }
+static gboolean
+splitline (MpsState *state)
+{
+ char *s;
+
+ if (!readline (state))
+ return FALSE;
+
+ g_ptr_array_set_size (state->split, 0);
+ s = state->line;
+ do {
+ while (g_ascii_isspace (*s))
+ s++;
+ if (!*s)
+ break;
+ g_ptr_array_add (state->split, s);
+ while (*s && !g_ascii_isspace (*s))
+ s++;
+ if (!*s)
+ break;
+ *s++ = 0;
+ } while (1);
+
+ return TRUE;
+}
- current = ctxt->cols;
- while (current != NULL) {
- MpsCol *col = (MpsCol *) current->data;
- MpsColInfo *info;
+static void
+ignore_section (MpsState *state)
+{
+ while (readline (state))
+ ; /* Nothing */
+}
- info = (MpsColInfo *) g_hash_table_lookup (ctxt->col_hash,
- col->name);
- ctxt->matrix[col->row->index][info->index] = col->value;
- current = current->next;
- }
+/* ------------------------------------------------------------------------- */
- if ((ctxt->n_cols + MAX_COL - 1) / MAX_COL == 1)
- col = CONSTRAINT_COL + ctxt->n_cols - 1;
- else
- col = CONSTRAINT_COL + MAX_COL - 1;
+static void
+mps_mark_error (MpsState *state, const char *fmt, ...)
+{
+ GOErrorInfo *error;
+ va_list args;
- current = ctxt->bounds;
- tmp = ctxt->rows;
- ctxt->rows = NULL;
- i = ctxt->n_rows + ctxt->n_bounds - 2;
- while (current != NULL) {
- MpsBound *bound = (MpsBound *) current->data;
- static const MpsRowType type_map[] = {
- LessOrEqualRow, GreaterOrEqualRow, EqualityRow
- };
+ va_start (args, fmt);
+ error = go_error_info_new_vprintf (GO_ERROR, fmt, args);
+ va_end (args);
- ctxt->matrix[ctxt->n_rows][bound->col_index] = 1.0;
+ go_io_error_info_set (state->io_context, error);
+}
- mps_set_cell_float (wbv->current_sheet, col + 3,
- i-- + CONSTRAINT_ROW,
- bound->value);
+static void
+mps_parse_name (MpsState *state)
+{
+ const char *s;
- mps_add_row (ctxt, type_map[(gint) bound->type], bound->name);
+ mps_set_cell (state, 0, 0, _("Program Name"));
+ mps_set_style (state, 0, 0, 0, 0, FALSE, TRUE, FALSE);
- current = current->next;
+ s = state->line + 4;
+ while (g_ascii_isspace (*s))
+ s++;
+ if (*s) {
+ mps_set_cell (state, 0, 1, s);
}
- ctxt->rows = g_slist_concat (tmp, ctxt->rows);
+
+ ignore_section (state);
}
-
static void
-mps_write_sheet_labels (MpsInputContext *ctxt, Sheet *sh)
+mps_parse_rows (MpsState *state)
{
- int i, row, col, inc;
- int n_rows_per_fn;
-
- /*
- * Sheet header titles.
- */
-
- /* Print 'Program Name'. */
- n_rows_per_fn = (ctxt->n_cols + MAX_COL - 1) / MAX_COL;
- mps_set_cell (sh, MAIN_INFO_COL, MAIN_INFO_ROW - 1, _("Program Name"));
- mps_set_style (sh, MAIN_INFO_COL, MAIN_INFO_ROW - 1,
- MAIN_INFO_COL + 5, MAIN_INFO_ROW - 1,
- FALSE, TRUE, FALSE);
-
- /* Print 'Status'. */
- mps_set_cell (sh, MAIN_INFO_COL + 3, MAIN_INFO_ROW - 1, _("Feasible"));
-
- /* Names of the variables. */
- row = VARIABLE_ROW - 1;
- if (n_rows_per_fn == 1) {
- for (i = 0; i < ctxt->n_cols; i++)
- mps_set_cell (sh, VARIABLE_COL + i, row,
- ctxt->col_name_tbl[i]);
- } else {
- GString *buf;
- for (i = 0; i < MAX_COL; i++) {
- buf = g_string_new (NULL);
- g_string_append_printf (buf, "C[%d]", i + 1);
- mps_set_cell (sh, VARIABLE_COL + i, row, buf->str);
- g_string_free (buf, TRUE);
+ gboolean seen_objfunc = FALSE;
+
+ g_ptr_array_add (state->rows, NULL);
+
+ while (splitline (state)) {
+ GPtrArray *split = state->split;
+ const char *type;
+ const char *name;
+ MpsRow *row;
+ gboolean is_objfunc = FALSE;
+
+ if (split->len < 2) {
+ mps_mark_error (state,
+ _("Invalid line in ROWS section"));
+ ignore_section (state);
+ return;
}
-
- for (i = 0; i < n_rows_per_fn; i++) {
- buf = g_string_new (NULL);
- g_string_append_printf (buf, "R[%d]", i + 1);
- mps_set_cell (sh, VARIABLE_COL - 1, row + i + 1,
- buf->str);
- g_string_free (buf, TRUE);
- }
- mps_set_style (sh, VARIABLE_COL - 1, row,
- VARIABLE_COL - 1, row + n_rows_per_fn,
- FALSE, TRUE, FALSE);
- }
- mps_set_style (sh, VARIABLE_COL, row, VARIABLE_COL + MAX_COL,
- row, FALSE, TRUE, FALSE);
-
-
- /* Print 'Objective value'. */
- mps_set_cell (sh, MAIN_INFO_COL + 1, MAIN_INFO_ROW - 1,
- _("Objective Value"));
-
- inc = n_rows_per_fn * 2;
-
- /* Print 'Objective function:' */
- mps_set_cell (sh, VARIABLE_COL, VARIABLE_ROW - 2,
- _("Objective function:"));
- mps_set_style (sh, VARIABLE_COL, VARIABLE_ROW - 2,
- VARIABLE_COL, VARIABLE_ROW - 2,
- FALSE, TRUE, TRUE);
-
- /* Print 'Constraints:'. */
- mps_set_cell (sh, CONSTRAINT_COL, CONSTRAINT_ROW - 2 + inc,
- _("Constraints:"));
- mps_set_style (sh, CONSTRAINT_COL, CONSTRAINT_ROW - 2 + inc,
- CONSTRAINT_COL, CONSTRAINT_ROW - 2 + inc,
- FALSE, TRUE, TRUE);
-
- /*
- * Print constraint titles.
- */
-
- /* Name field. */
- row = CONSTRAINT_ROW - 1 + inc;
- mps_set_cell (sh, CONSTRAINT_COL - 1, row, _("Name"));
-
- /* Names of the variables. */
- if (n_rows_per_fn == 1) {
- for (i = 0; i < ctxt->n_cols; i++)
- mps_set_cell (sh, CONSTRAINT_COL + i, row,
- ctxt->col_name_tbl[i]);
- } else {
- GString *buf;
- for (i = 0; i < MAX_COL; i++) {
- buf = g_string_new (NULL);
- g_string_append_printf (buf, "C[%d]", i + 1);
- mps_set_cell (sh, CONSTRAINT_COL + i, row, buf->str);
- g_string_free (buf, TRUE);
+ type = g_ptr_array_index (split, 0);
+ name = g_ptr_array_index (split, 1);
+
+ if (g_hash_table_lookup (state->row_hash, name)) {
+ mps_mark_error (state,
+ _("Duplicate rows name %s"),
+ name);
+ ignore_section (state);
+ return;
}
- }
- mps_set_style (sh, CONSTRAINT_COL - 1, row,
- CONSTRAINT_COL + MAX_COL + 5,
- row, FALSE, TRUE, FALSE);
+ if (strcmp (type, "E") == 0) {
+ row = g_new0 (MpsRow, 1);
+ row->type = GNM_SOLVER_EQ;
+ } else if (strcmp (type, "L") == 0) {
+ row = g_new0 (MpsRow, 1);
+ row->type = GNM_SOLVER_LE;
+ } else if (strcmp (type, "G") == 0) {
+ row = g_new0 (MpsRow, 1);
+ row->type = GNM_SOLVER_GE;
+ } else if (strcmp (type, "N") == 0) {
+ if (seen_objfunc) {
+ mps_mark_error (state,
+ _("Duplicate objective row"));
+ ignore_section (state);
+ return;
+ }
+ row = g_new0 (MpsRow, 1);
+ is_objfunc = TRUE;
+ seen_objfunc = TRUE;
+ g_ptr_array_index (state->rows, 0) = row;
+ }
- /* Value, Type, RHS, Slack, and Status titles. */
- if (n_rows_per_fn == 1)
- col = CONSTRAINT_COL + ctxt->n_cols - 1;
- else
- col = CONSTRAINT_COL + MAX_COL - 1;
+ row->name = g_strdup (name);
+ g_hash_table_insert (state->row_hash, row->name, row);
+ if (!is_objfunc)
+ g_ptr_array_add (state->rows, row);
+ }
- mps_set_cell (sh, col + 1, row, _("Value"));
- mps_set_cell (sh, col + 2, row, _("Type"));
- mps_set_cell (sh, col + 3, row, _("RHS"));
- mps_set_cell (sh, col + 4, row, _("Slack"));
+ if (!seen_objfunc) {
+ mps_mark_error (state,
+ _("Missing objective row"));
+ return;
+ }
}
-
static void
-mps_write_coefficients (MpsInputContext *ctxt, Sheet *sh,
- GnmSolverParameters *param)
+mps_parse_columns (MpsState *state)
{
- GSList *current;
- int i, n, r, ecol, inc2;
- int n_rows_per_fn;
- GnmRange range, v_range;
- GnmCell *cell;
- const GnmExprTop *texpr;
-
- /*
- * Add objective function stuff into the sheet.
- */
-
- /* Print the column names, initialize the variables to 0, and
- * print the coefficients of the objective function. */
- n_rows_per_fn = (ctxt->n_cols + MAX_COL - 1) / MAX_COL;
- if (n_rows_per_fn == 1)
- ecol = CONSTRAINT_COL + ctxt->n_cols - 1;
- else
- ecol = CONSTRAINT_COL + MAX_COL - 1;
- for (i = 0; i < ctxt->n_cols; i++) {
- int col = VARIABLE_COL + i % MAX_COL;
- int row = VARIABLE_ROW + i / MAX_COL;
- mps_set_cell_float (sh, col, row, 0.0);
- mps_set_cell_float (sh, col, row + (n_rows_per_fn + 1),
- ctxt->objective_row
- ? ctxt->matrix[ctxt->objective_row->index][i]
- : 0);
- }
-
- /*
- * Add constraints into the sheet.
- */
-
- /* Print constraints. */
- inc2 = 2 * n_rows_per_fn;
- param->constraints = NULL;
-
- /* Initialize var_range to contain the range name of the
- * objective function variables. */
- range_init (&v_range,
- VARIABLE_COL, VARIABLE_ROW,
- VARIABLE_COL + MIN (MAX_COL, ctxt->n_cols) - 1,
- VARIABLE_ROW + n_rows_per_fn - 1);
-
- i = 0;
- for (current = ctxt->rows; current != NULL; current = current->next) {
- GnmSolverConstraint *c;
- MpsRow *row = current->data;
- int col, r;
- const GnmExprTop *texpr;
- GnmCellRef ref1, ref2;
-
- static const gchar *const type_str[] = {
- "=", "<=", ">="
- };
- static const GnmSolverConstraintType type_map[] = {
- GNM_SOLVER_EQ, GNM_SOLVER_LE, GNM_SOLVER_GE
- };
-
- if (row->type == ObjectiveRow)
- continue;
- col = CONSTRAINT_COL;
- r = CONSTRAINT_ROW + i * n_rows_per_fn + inc2;
-
- /* Add row name. */
- mps_set_cell (sh, col - 1, r, row->name);
-
- /* Coefficients. */
- for (n = 0; n < ctxt->n_cols; n++)
- /* Write only non-zero coefficents in order to save
- * memory, and, in addition, to speed up the loading.
- */
-#ifndef MPS_WRITE_ZERO_COEFFICIENTS
- if (ctxt->matrix[row->index][n] != 0)
-#endif
- mps_set_cell_float
- (sh, col + n % MAX_COL,
- r + n / MAX_COL,
- ctxt->matrix[row->index][n]);
-
- /* Add Type field. */
- mps_set_cell (sh, ecol + 2, r, type_str[(int) row->type]);
-
-
- /* Add RHS field (zero). */
- mps_set_cell_float (sh, ecol + 3, r, 0);
-
-
- /* Add LHS field using SUMPRODUCT function. */
- range_init (&range,
- col, r,
- col + MIN (MAX_COL, ctxt->n_cols) - 1,
- r + (n_rows_per_fn - 1));
-
- cell = sheet_cell_fetch (sh, ecol + 1, r);
- texpr = gnm_expr_top_new
- (gnm_expr_new_funcall2
- (gnm_func_lookup ("SUMPRODUCT", NULL),
- gnm_expr_new_constant
- (value_new_cellrange_r (NULL, &v_range)),
- gnm_expr_new_constant
- (value_new_cellrange_r (NULL, &range))));
- gnm_cell_set_expr (cell, texpr);
- gnm_expr_top_unref (texpr);
- cell_queue_recalc (cell);
-
- /* Add Slack calculation */
- gnm_cellref_init (&ref1, sh, ecol + 1, r, FALSE);
- gnm_cellref_init (&ref2, sh, ecol + 3, r, FALSE);
- cell = sheet_cell_fetch (sh, ecol + 4, r);
- texpr = gnm_expr_top_new
- (gnm_expr_new_funcall1
- (gnm_func_lookup ("ABS", NULL),
- gnm_expr_new_binary
- (gnm_expr_new_cellref (&ref1),
- GNM_EXPR_OP_SUB,
- gnm_expr_new_cellref (&ref2))));
- gnm_cell_set_expr (cell, texpr);
- gnm_expr_top_unref (texpr);
- cell_queue_recalc (cell);
-
- /* Add Solver constraint */
- c = gnm_solver_constraint_new (sh);
- gnm_solver_constraint_set_old (c, type_map[row->type],
- ecol + 1, r,
- ecol + 3, r,
- 1, 1);
-
- param->constraints = g_slist_append (param->constraints, c);
- i++;
- }
+ while (splitline (state)) {
+ GPtrArray *split = state->split;
+ const char *colname;
+ unsigned ui;
+ GnmCell *cell;
+
+ if (split->len % 2 != 1) {
+ mps_mark_error (state,
+ _("Invalid column line"));
+ continue;
+ }
- /* Write RHSes. */
- current = ctxt->rhs;
- r = CONSTRAINT_ROW + inc2;
- while (current != NULL) {
- MpsRhs *rhs = current->data;
+ colname = g_ptr_array_index (split, 0);
+ cell = g_hash_table_lookup (state->col_hash, colname);
+ if (!cell) {
+ int x = VAR_BASE_COL;
+ int y = VAR_BASE_ROW + 1 + g_hash_table_size (state->col_hash);
+ cell = sheet_cell_fetch (state->sheet, x + 1, y);
+ g_hash_table_insert (state->col_hash,
+ g_strdup (colname),
+ cell);
+ mps_set_cell (state, x, y, colname);
+ }
- mps_set_cell_float (sh, ecol + 3,
- r + rhs->row->index * n_rows_per_fn,
- rhs->value);
- current = current->next;
+ for (ui = 1; ui < split->len; ui += 2) {
+ const char *rowname = g_ptr_array_index (split, ui);
+ const char *valtxt = g_ptr_array_index (split, ui + 1);
+ gnm_float val = gnm_strto (valtxt, NULL);
+ gboolean neg = (val < 0);
+ MpsRow *row = g_hash_table_lookup (state->row_hash,
+ rowname);
+ GnmCellRef cr;
+ GnmExpr const *expr;
+
+ if (!row) {
+ mps_mark_error (state,
+ _("Invalid row name, %s, in columns"),
+ rowname);
+ continue;
+ }
+ if (val == 0)
+ continue;
+
+ if (row->expr) {
+ val = gnm_abs (val);
+ }
+
+ gnm_cellref_init (&cr, NULL,
+ cell->pos.col, cell->pos.row,
+ FALSE);
+ expr = gnm_expr_new_cellref (&cr);
+ if (gnm_abs (val) != 1) {
+ expr = gnm_expr_new_binary
+ (gnm_expr_new_constant (value_new_float (val)),
+ GNM_EXPR_OP_MULT,
+ expr);
+ } else if (neg && row->expr == NULL)
+ expr = gnm_expr_new_unary
+ (GNM_EXPR_OP_UNARY_NEG,
+ expr);
+
+ if (row->expr) {
+ expr = gnm_expr_new_binary
+ (row->expr,
+ neg ? GNM_EXPR_OP_SUB : GNM_EXPR_OP_ADD,
+ expr);
+ }
+
+ row->expr = expr;
+ }
}
-
- /* Write the objective fn. */
- range_init (&range,
- VARIABLE_COL, VARIABLE_ROW + (1 + n_rows_per_fn),
- VARIABLE_COL + MIN (MAX_COL, ctxt->n_cols) - 1,
- VARIABLE_ROW + 2 * n_rows_per_fn);
- texpr = gnm_expr_top_new
- (gnm_expr_new_funcall2
- (gnm_func_lookup ("SUMPRODUCT", NULL),
- gnm_expr_new_constant
- (value_new_cellrange_r (NULL, &v_range)),
- gnm_expr_new_constant
- (value_new_cellrange_r (NULL, &range))));
- cell = sheet_cell_fetch (sh, OBJECTIVE_VALUE_COL, MAIN_INFO_ROW);
- gnm_cell_set_expr (cell, texpr);
- gnm_expr_top_unref (texpr);
- cell_queue_recalc (cell);
-
- /* Store the input cell range for the Solver dialog. */
- gnm_solver_param_set_input (param,
- value_new_cellrange_r (NULL, &v_range));
}
-/* Creates the spreadsheet model. */
static void
-mps_create_sheet (MpsInputContext *ctxt, WorkbookView *wbv)
+mps_parse_rhs (MpsState *state)
{
- Sheet *sh = wbv->current_sheet;
- gint i;
- int n_rows_per_fn;
- GnmSolverParameters *param = sh->solver_parameters;
- const char *row_name =
- ctxt->objective_row
- ? ctxt->objective_row->name
- : "-";
- GnmCellRef cr;
-
- n_rows_per_fn = (ctxt->n_cols + MAX_COL - 1) / MAX_COL;
- mps_prepare (wbv, ctxt);
-
- mps_write_sheet_labels (ctxt, sh);
- mps_write_coefficients (ctxt, sh, param);
-
- /* Print the name of the objective function */
- if (ctxt->n_cols < MAX_COL)
- mps_set_cell (sh, VARIABLE_COL - 1,
- VARIABLE_ROW + 1 + n_rows_per_fn,
- row_name);
- else {
- for (i = 0; i < n_rows_per_fn; i++) {
- GString *buf = g_string_new (NULL);
- g_string_append_printf (buf, "%s (R[%d])",
- row_name, i + 1);
- mps_set_cell (sh, VARIABLE_COL - 1,
- VARIABLE_ROW + 1 + i + n_rows_per_fn,
- buf->str);
- g_string_free (buf, TRUE);
+ while (splitline (state)) {
+ GPtrArray *split = state->split;
+ unsigned ui;
+
+ if (split->len % 2 == 1) {
+ mps_mark_error (state,
+ _("Invalid rhs line"));
+ continue;
}
- }
- gnm_cellref_init (&cr, NULL, OBJECTIVE_VALUE_COL, MAIN_INFO_ROW, TRUE);
- gnm_solver_param_set_target (param, &cr);
- param->problem_type = GNM_SOLVER_MINIMIZE;
-
- /* Write the name of the program. */
- if (ctxt->name != NULL)
- mps_set_cell (sh, MAIN_INFO_COL, MAIN_INFO_ROW, ctxt->name);
-
-
- /* Autofit column A */
- i = sheet_col_size_fit_pixels (sh, 0, 0, gnm_sheet_get_last_row (ctxt->sheet), FALSE);
- if (i == 0)
- return;
- sheet_col_set_size_pixels (sh, 0, i, TRUE);
- sheet_recompute_spans_for_col (sh, 0);
- workbook_recalc (sh->workbook);
+ for (ui = 0; ui < split->len; ui += 2) {
+ const char *rowname = g_ptr_array_index (split, ui);
+ const char *valtxt = g_ptr_array_index (split, ui + 1);
+ MpsRow *row = g_hash_table_lookup (state->row_hash,
+ rowname);
+
+ if (!row) {
+ mps_mark_error (state,
+ _("Invalid row name, %s, in rhs"),
+ rowname);
+ continue;
+ }
+ row->rhs += gnm_strto (valtxt, NULL);
+ }
+ }
}
-
-/************************************************************************
- *
- * Data structure initializations and releasing.
- */
-
-/* Make the initializations. */
-static MpsInputContext *
-mps_input_context_new (GOIOContext *io_context, Workbook *wb, GsfInput *input)
+static void
+mps_parse_file (MpsState *state)
{
- MpsInputContext *ctxt = NULL;
-
- ctxt = g_new (MpsInputContext, 1);
- ctxt->io_context = io_context;
-
- ctxt->input = (GsfInputTextline *)gsf_input_textline_new (input);
- ctxt->line_no = 1;
- ctxt->line = NULL;
- ctxt->sheet = workbook_sheet_add (wb, -1, GNM_DEFAULT_COLS, GNM_DEFAULT_ROWS);
-
- ctxt->name = NULL;
- ctxt->rows = NULL;
- ctxt->cols = NULL;
- ctxt->rhs = NULL;
- ctxt->bounds = NULL;
- ctxt->row_hash = g_hash_table_new (g_str_hash, g_str_equal);
- ctxt->col_hash = g_hash_table_new (g_str_hash, g_str_equal);
- ctxt->col_name_tbl = NULL;
- ctxt->objective_row = NULL;
- ctxt->matrix = NULL;
+ gboolean done = FALSE;
+ readline (state);
- ctxt->n_rows = ctxt->n_cols = ctxt->n_bounds = 0;
+ while (!done) {
+ char *line = state->line;
+ char *section;
+ unsigned ui;
- g_slist_free (ctxt->rows);
-
- go_io_progress_message (io_context, _("Reading file..."));
+ if (!line) {
+ /* Ignore missing end marker. */
+ break;
+ }
- return ctxt;
+ ui = 0;
+ while (g_ascii_isalnum (line[ui]))
+ ui++;
+ section = g_strndup (line, ui);
+
+ if (strcmp (section, "ENDATA") == 0)
+ done = TRUE;
+ else if (strcmp (section, "NAME") == 0)
+ mps_parse_name (state);
+ else if (strcmp (section, "ROWS") == 0)
+ mps_parse_rows (state);
+ else if (strcmp (section, "COLUMNS") == 0)
+ mps_parse_columns (state);
+ else if (strcmp (section, "RHS") == 0)
+ mps_parse_rhs (state);
+ else {
+ g_warning ("Invalid section %s\n", section);
+ ignore_section (state);
+ }
+ g_free (section);
+ }
}
-/* Call-back for mps_input_context_destroy. */
-static gboolean
-rh_rm_cb (gpointer key, gpointer value, gpointer user_data)
-{
- return TRUE;
-}
+/* ------------------------------------------------------------------------- */
-/* Call-back for mps_input_context_destroy. */
-static gboolean
-ch_rm_cb (gpointer key, gpointer value, gpointer user_data)
+static void
+mps_fill_sheet (MpsState *state)
{
- MpsColInfo *info = (MpsColInfo *) value;
+ unsigned ui;
+ GnmSolverParameters *param = state->param;
+
+ /* ---------------------------------------- */
+
+ mps_set_cell (state, CONSTR_BASE_COL, CONSTR_BASE_ROW,
+ _("Constraint"));
+ mps_set_cell (state, CONSTR_BASE_COL + 1, CONSTR_BASE_ROW,
+ _("Value"));
+ mps_set_cell (state, CONSTR_BASE_COL + 2, CONSTR_BASE_ROW,
+ _("Type"));
+ mps_set_cell (state, CONSTR_BASE_COL + 3, CONSTR_BASE_ROW,
+ _("Limit"));
+ mps_set_style (state, CONSTR_BASE_COL, CONSTR_BASE_ROW,
+ CONSTR_BASE_COL + 3, CONSTR_BASE_ROW,
+ FALSE, TRUE, FALSE);
+
+ /* Zeroth row is objective function. */
+ for (ui = 1; ui < state->rows->len; ui++) {
+ int x = CONSTR_BASE_COL;
+ int y = CONSTR_BASE_ROW + ui;
+ MpsRow *row = g_ptr_array_index (state->rows, ui);
+ const char *typetxt;
+ GnmSolverConstraint *c = gnm_solver_constraint_new
+ (state->sheet);
+ GnmRange r;
+
+ mps_set_cell (state, x, y, row->name);
+ if (row->expr) {
+ mps_set_expr (state, x + 1, y, row->expr);
+ row->expr = NULL;
+ } else
+ mps_set_cell_float (state, x + 1, y, 0);
+ range_init (&r, x + 1, y, x + 1, y);
+ gnm_solver_constraint_set_lhs
+ (c,
+ value_new_cellrange_r (NULL, &r));
+
+ c->type = row->type;
+ switch (row->type) {
+ case GNM_SOLVER_LE:
+ typetxt = "\xe2\x89\xa4";
+ break;
+ case GNM_SOLVER_GE:
+ typetxt = "\xe2\x89\xa5";
+ break;
+ case GNM_SOLVER_EQ:
+ typetxt = "=";
+ break;
+ case GNM_SOLVER_INTEGER:
+ typetxt = "Integer";
+ break;
+ case GNM_SOLVER_BOOLEAN:
+ typetxt = "Boolean";
+ break;
+ default:
+ g_assert_not_reached ();
+ }
- g_free (info->name);
- g_free (info);
+ mps_set_cell (state, x + 2, y, typetxt);
- return TRUE;
-}
+ if (gnm_solver_constraint_has_rhs (c)) {
+ mps_set_cell_float (state, x + 3, y, row->rhs);
+ range_init (&r, x + 3, y, x + 3, y);
+ gnm_solver_constraint_set_rhs
+ (c,
+ value_new_cellrange_r (NULL, &r));
+ }
-static void
-free_row (MpsRow *row)
-{
- if (row) {
- g_free (row->name);
- g_free (row);
+ param->constraints = g_slist_append (param->constraints, c);
}
-}
-/* Free the allocated memory. */
-static void
-mps_input_context_destroy (MpsInputContext *ctxt)
-{
- GSList *current;
- int i;
+ /* ---------------------------------------- */
- go_io_progress_unset (ctxt->io_context);
+ {
+ GnmRange r;
+ GnmValue *vinput;
- free_row (ctxt->objective_row);
- ctxt->objective_row = NULL;
+ mps_set_cell (state, VAR_BASE_COL, VAR_BASE_ROW,
+ _("Variable"));
+ mps_set_cell (state, VAR_BASE_COL + 1, VAR_BASE_ROW,
+ _("Value"));
+ mps_set_style (state, VAR_BASE_COL, VAR_BASE_ROW,
+ VAR_BASE_COL + 1, VAR_BASE_ROW,
+ FALSE, TRUE, FALSE);
- /* Free ROWS */
- for (current = ctxt->rows; current != NULL; current = current->next) {
- MpsRow *row = current->data;
- free_row (row);
- }
- g_slist_free (ctxt->rows);
- ctxt->rows = NULL;
-
- /* Free COLUMNS */
- for (current = ctxt->cols; current != NULL; current = current->next) {
- MpsCol *col = current->data;
- g_free (col->name);
- g_free (col);
+ range_init (&r,
+ VAR_BASE_COL + 1, VAR_BASE_ROW + 1,
+ VAR_BASE_COL + 1, VAR_BASE_ROW + g_hash_table_size (state->col_hash));
+ vinput = value_new_cellrange_r (NULL, &r);
+ gnm_solver_param_set_input (param, vinput);
}
- g_slist_free (ctxt->cols);
- ctxt->cols = NULL;
-
- /* Free RHSs */
- for (current = ctxt->rhs; current != NULL; current = current->next) {
- MpsRhs *rhs = current->data;
- g_free (rhs->name);
- g_free (rhs);
- }
- g_slist_free (ctxt->rhs);
- /* Free BOUNDS */
- for (current = ctxt->bounds; current!=NULL; current = current->next) {
- MpsBound *bound = current->data;
- g_free (bound->name);
- g_free (bound);
- }
- g_slist_free (ctxt->bounds);
+ /* ---------------------------------------- */
- g_hash_table_foreach_remove (ctxt->row_hash, (GHRFunc) rh_rm_cb, NULL);
- g_hash_table_foreach_remove (ctxt->col_hash, (GHRFunc) ch_rm_cb, NULL);
- g_hash_table_destroy (ctxt->row_hash);
- g_hash_table_destroy (ctxt->col_hash);
+ {
+ int x = OBJ_BASE_COL;
+ int y = OBJ_BASE_ROW;
+ MpsRow *row = g_ptr_array_index (state->rows, 0);
+ GnmCellRef cr;
- g_free (ctxt->col_name_tbl);
- ctxt->col_name_tbl = NULL;
+ mps_set_cell (state, x, y, _("Objective function"));
+ mps_set_style (state, x, y, x, y, FALSE, TRUE, FALSE);
- if (ctxt->matrix) {
- for (i = 0; i < ctxt->n_rows + ctxt->n_bounds; i++)
- g_free (ctxt->matrix[i]);
- g_free (ctxt->matrix);
- ctxt->matrix = NULL;
- }
+ if (row->expr) {
+ mps_set_expr (state, x + 1, y, row->expr);
+ row->expr = NULL;
+ } else
+ mps_set_cell_float (state, x + 1, y, 0);
- g_free (ctxt->name);
- g_object_unref (G_OBJECT (ctxt->input)); ctxt->input = NULL;
- g_free (ctxt);
-}
+ param->problem_type = GNM_SOLVER_MINIMIZE;
+ gnm_cellref_init (&cr, NULL, x + 1, y, FALSE);
+ gnm_solver_param_set_target (param, &cr);
+ }
+}
-
-/*---------------------------------------------------------------------*/
+/* ------------------------------------------------------------------------- */
-/*
- * The public plug-in API.
- */
+void
+mps_file_open (GOFileOpener const *fo, GOIOContext *io_context,
+ WorkbookView *wbv, GsfInput *input);
void
mps_file_open (GOFileOpener const *fo, GOIOContext *io_context,
WorkbookView *wbv, GsfInput *input)
{
- MpsInputContext *ctxt;
-
- ctxt = mps_input_context_new (io_context, wb_view_get_workbook (wbv),
- input);
- if (ctxt != NULL) {
- mps_parse_file (ctxt);
- if (go_io_error_occurred (io_context)) {
- go_io_error_push (io_context, go_error_info_new_str
- (_("Error while reading MPS "
- "file.")));
- } else
- mps_create_sheet (ctxt, wbv);
- mps_input_context_destroy (ctxt);
- } else if (!go_io_error_occurred (io_context))
- go_io_error_unknown (io_context);
+ MpsState state;
+ GnmLocale *locale;
+ unsigned ui;
+
+ memset (&state, 0, sizeof (state));
+ state.io_context = io_context;
+ state.wb = wb_view_get_workbook (wbv);
+ state.input = GSF_INPUT_TEXTLINE (gsf_input_textline_new (input));
+ state.sheet = workbook_sheet_add (state.wb, -1,
+ GNM_DEFAULT_COLS, GNM_DEFAULT_ROWS);
+ state.param = state.sheet->solver_parameters;
+ state.split = g_ptr_array_new ();
+ state.rows = g_ptr_array_new ();
+ state.row_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ state.col_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, NULL);
+
+ locale = gnm_push_C_locale ();
+ mps_parse_file (&state);
+ gnm_pop_C_locale (locale);
+
+ if (go_io_error_occurred (io_context)) {
+ go_io_error_push (io_context, go_error_info_new_str
+ (_("Error while reading MPS file.")));
+ } else {
+ mps_fill_sheet (&state);
+
+ workbook_queue_all_recalc (state.wb);
+ workbook_recalc (state.wb);
+ }
+
+ g_hash_table_destroy (state.row_hash);
+ for (ui = 0; ui < state.rows->len; ui++) {
+ MpsRow *row = g_ptr_array_index (state.rows, ui);
+ if (!row)
+ continue;
+ g_free (row->name);
+ if (row->expr)
+ gnm_expr_free (row->expr);
+ g_free (row);
+ }
+ g_ptr_array_free (state.rows, TRUE);
+
+ g_hash_table_destroy (state.col_hash);
+
+ g_ptr_array_free (state.split, TRUE);
+ g_object_unref (state.input);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]