Commit 77b2e049 authored by Jukka-Pekka Iivonen's avatar Jukka-Pekka Iivonen Committed by jpekka

Implemented Simplex algorithm. It can currently solve LP maximization

1999-12-30  Jukka-Pekka Iivonen  <iivonen@iki.fi>

	* src/solver-lp.c: Implemented Simplex algorithm.  It can
 	currently solve LP maximization problems of positive variables
 	having `<=' constraints.

	* src/workbook.c: Added `Solver' tool.

	* src/utils.c (parse_cell_name_list): Bug fix.  The last item on
 	the list was not included on the list.
parent 87152856
1999-12-30 Jukka-Pekka Iivonen <iivonen@iki.fi>
* src/solver-lp.c: Implemented Simplex algorithm. It can
currently solve LP maximization problems of positive variables
having `<=' constraints.
* src/workbook.c: Added `Solver' tool.
* src/utils.c (parse_cell_name_list): Bug fix. The last item on
the list was not included on the list.
1999-12-30 Miguel de Icaza <miguel@gnu.org>
* src/gnumeric-type-util.h (GNUMERIC_MAKE_TYPE): New macro, copied
......
......@@ -33,6 +33,11 @@ Cort:
Andrew:
* Many improvement for multi-dimensional linear regression.
Jukka:
* Implemented Simplex algorithm for Solver
* Solver can now solve some simple LP problems (more interesting
stuff is coming later).
--------------------------------------------------------------------------
Gnumeric 0.45
......
1999-12-30 Jukka-Pekka Iivonen <iivonen@iki.fi>
* src/solver-lp.c: Implemented Simplex algorithm. It can
currently solve LP maximization problems of positive variables
having `<=' constraints.
* src/workbook.c: Added `Solver' tool.
* src/utils.c (parse_cell_name_list): Bug fix. The last item on
the list was not included on the list.
1999-12-30 Miguel de Icaza <miguel@gnu.org>
* src/gnumeric-type-util.h (GNUMERIC_MAKE_TYPE): New macro, copied
......
......@@ -53,7 +53,7 @@ Gnumeric Spread Sheet task list
* Fix the Following Functions
* GROWTH, INDEX, LINEST, LOGEST, MATCH, TREND, and VLOOKUP
* INDEX, MATCH, and VLOOKUP
* Printing
* Preview
......
......@@ -13,7 +13,7 @@
#include "ranges.h"
#include "eval.h"
static void
void
cell_eval_content (Cell *cell)
{
Value *v;
......
......@@ -20,9 +20,9 @@
/* Different constraint types */
static const char *constraint_strs[] = {
N_("<="),
/* N_("="), */
/* N_("="),
N_(">="),
/* N_("int"),
N_("int"),
N_("bool"), */
NULL
};
......@@ -117,6 +117,7 @@ typedef struct {
GSList *constraints;
GtkCList *clist;
Sheet *sheet;
Workbook *wb;
} constraint_dialog_t;
......@@ -169,7 +170,7 @@ dialog_solver_options (Workbook *wb, Sheet *sheet)
gnome_dialog_run (GNOME_DIALOG (dialog));
sheet->solver_parameters.options.assume_linear_model = 1;
sheet->solver_parameters.options.assume_non_negative = 0;
sheet->solver_parameters.options.assume_non_negative = 1;
sheet->solver_parameters.options.automatic_scaling = 0;
sheet->solver_parameters.options.show_iteration_results = 0;
......@@ -236,6 +237,9 @@ constr_add_click (gpointer data)
NULL);
gnome_dialog_close_hides (GNOME_DIALOG (dialog), TRUE);
gnome_dialog_set_parent (GNOME_DIALOG (dialog),
GTK_WINDOW
(constraint_dialog->wb->toplevel));
box = gtk_hbox_new (FALSE, 0);
lhs_entry = hbox_pack_label_and_entry
......@@ -264,10 +268,6 @@ add_dialog:
selection = gnome_dialog_run (GNOME_DIALOG (dialog));
if (selection == 1) {
add_constraint(constraint_dialog, 0, 8, 2, 8, "<=");
add_constraint(constraint_dialog, 0, 9, 2, 9, ">=");
add_constraint(constraint_dialog, 0, 10, 2, 10, ">=");
add_constraint(constraint_dialog, 0, 11, 2, 11, ">=");
gnome_dialog_close (GNOME_DIALOG (dialog));
return;
}
......@@ -335,10 +335,10 @@ dialog_solver (Workbook *wb, Sheet *sheet)
static GtkWidget *constr_button_box;
static GSList *group_equal;
constraint_dialog_t constraint_dialog;
static constraint_dialog_t constraint_dialog;
const char *text, *target_entry_str;
int selection, sel_equal_to;
int selection, sel_equal_to, res;
Cell *target_cell;
CellList *input_cells;
int target_cell_col, target_cell_row;
......@@ -355,6 +355,8 @@ dialog_solver (Workbook *wb, Sheet *sheet)
_("Options..."),
NULL);
gnome_dialog_close_hides (GNOME_DIALOG (dialog), TRUE);
gnome_dialog_set_parent (GNOME_DIALOG (dialog),
GTK_WINDOW (wb->toplevel));
box = gtk_vbox_new (FALSE, 0);
......@@ -411,6 +413,7 @@ dialog_solver (Workbook *wb, Sheet *sheet)
constraint_dialog.constraints = NULL;
constraint_dialog.clist = GTK_CLIST (constraint_list);
constraint_dialog.sheet = sheet;
constraint_dialog.wb = wb;
gtk_signal_connect_object (GTK_OBJECT (constr_add_button),
"clicked",
......@@ -515,9 +518,15 @@ main_dialog:
switch (selection) {
case 0: /* Solve */
if (1 || sheet->solver_parameters.options.assume_linear_model)
solver_simplex(wb, sheet);
else
if (1 ||sheet->solver_parameters.options.assume_linear_model) {
res = solver_simplex(wb, sheet);
if (res == SIMPLEX_UNBOUNDED) {
gnumeric_notice (wb, GNOME_MESSAGE_BOX_ERROR,
_("The problem is unbounded "
"and cannot be solved"));
break;
}
} else
; /* NLP not implemented yet */
break;
case 2: /* Options */
......
......@@ -13,7 +13,7 @@
#include "ranges.h"
#include "eval.h"
static void
void
cell_eval_content (Cell *cell)
{
Value *v;
......
......@@ -275,7 +275,7 @@ parse_cell_name_list (Sheet *sheet,
g_return_val_if_fail (error_flag != NULL, NULL);
buf = g_malloc (strlen (cell_name_str) + 1);
for (i = n = 0; cell_name_str [i]; i++){
for (i = n = 0; ; i++){
if ((cell_name_str [i] == ',') || (cell_name_str [i] == '\0')){
buf [n] = '\0';
......@@ -297,6 +297,8 @@ parse_cell_name_list (Sheet *sheet,
n = 0;
} else
buf [n++] = cell_name_str [i];
if (! cell_name_str [i])
break;
}
*error_flag = 0;
......
/*
* solver-lp: Linear programming methods.
*
* Authors:
*/
* Author:
* Jukka-Pekka Iivonen <iivonen@iki.fi>
*/
#include <config.h>
#include <gnome.h>
#include <math.h>
#include <stdlib.h>
#include "numbers.h"
#include "gnumeric.h"
#include "gnumeric-sheet.h"
#include "utils.h"
#include "solver.h"
#include "func.h"
#include "cell.h"
#include "eval.h"
int solver_simplex (Workbook *wb, Sheet *sheet)
/************************************************************************
*
* S I M P L E X
*
*
*/
/* STEP 1: Construct an initial solution table.
*/
static float_t *
simplex_step_one(Sheet *sheet, int target_col, int target_row,
CellList *inputs, int *n_vars,
GSList *constraints, int *n_constraints, gboolean max_flag)
{
int i;
CellList *cell_list = sheet->solver_parameters.input_cells;
GSList *constraints = sheet->solver_parameters.constraints;
printf ("Target cell: col=%d, row=%d\n",
sheet->solver_parameters.target_cell->col->pos,
sheet->solver_parameters.target_cell->row->pos);
printf ("Input cells:\n");
for (i = 0; cell_list != NULL; cell_list = cell_list->next) {
Cell *cell = (Cell *) cell_list->data;
printf (" %2d: col=%d, row=%d\n", i,
cell->col->pos, cell->row->pos);
SolverConstraint *c;
CellList *input_list = inputs;
GSList *current;
Cell *cell, *target, *lhs, *rhs;
float_t init_value, value, *table;
int n, i, step;
for (n = 0; inputs != NULL; inputs = inputs->next)
n++;
*n_vars = n;
*n_constraints = g_slist_length(constraints);
if (n < 1 || *n_constraints < 1)
return NULL;
step = n + *n_constraints + 2;
table = g_new(float_t, (*n_constraints + 1) * step);
for (i=0; i<(*n_constraints + 1)*step; i++)
table[i] = 0;
inputs = input_list;
target = sheet_cell_fetch(sheet, target_col, target_row);
for (i=2; inputs != NULL; inputs = inputs->next) {
cell = (Cell *) inputs->data;
cell_set_value (cell, value_new_float (0.0));
cell_eval_content(target);
init_value = value_get_as_float(target->value);
current = constraints;
n = 1;
while (current != NULL) {
c = (SolverConstraint *) current->data;
lhs = sheet_cell_fetch(sheet, c->lhs->col->pos,
c->lhs->row->pos);
cell_eval_content(lhs);
table[i + n*step] = -value_get_as_float(lhs->value);
current = current->next;
++n;
}
cell_set_value (cell, value_new_float (1.0));
cell_eval_content(target);
value = value_get_as_float(target->value);
current = constraints;
n = 1;
while (current != NULL) {
c = (SolverConstraint *) current->data;
lhs = sheet_cell_fetch(sheet, c->lhs->col->pos,
c->lhs->row->pos);
cell_eval_content(lhs);
table[i + n*step] += value_get_as_float(lhs->value);
current = current->next;
++n;
}
cell_set_value (cell, value_new_float (0.0));
if (max_flag)
table[i] = value - init_value;
else
table[i] = init_value - value;
++i;
}
printf ("Constraints:\n");
while (constraints != NULL) {
SolverConstraint *c = (SolverConstraint *) constraints->data;
n = 1;
while (constraints != NULL) {
c = (SolverConstraint *) constraints->data;
table[n*step] = n-1 + *n_vars;
rhs = sheet_cell_fetch(sheet, c->rhs->col->pos,
c->rhs->row->pos);
table[1 + n*step] = value_get_as_float(rhs->value);
table[1 + *n_vars + n + n*step] = 1;
printf ("%-30s (col=%d, row=%d %s col=%d, row=%d\n",
c->str, c->lhs->col->pos, c->lhs->row->pos,
c->type, c->rhs->col->pos, c->rhs->row->pos);
constraints = constraints->next;
++n;
}
return 0;
return table;
}
/* STEP 2: Find the pivot column. Most positive rule is applied.
*/
static int
simplex_step_two(float_t *table, int n_vars, int *status)
{
float_t max = 0;
int i, ind = -1;
table += 2;
for (i=0; i<n_vars; i++)
if (table[i] > max) {
max = table[i];
ind = i;
}
if (ind == -1)
*status = SIMPLEX_DONE;
return ind;
}
/* STEP 3: Find the pivot row.
*/
static int
simplex_step_three(float_t *table, int col, int n_vars, int n_rows,
int *status)
{
float_t min, test;
int i, step, min_i;
step = n_rows + n_vars + 2;
table += step;
min = table[1] / table[2+col];
min_i = 0;
for (i=1; i<n_rows; i++) {
test = table[1 + i*step] / table[2 + col + i*step];
if (test < min) {
min = test;
min_i = i;
}
}
if (min < 0)
*status = SIMPLEX_UNBOUNDED;
return min_i;
}
/* STEP 4: Perform pivot operations.
*/
static void
simplex_step_four(float_t *table, int col, int row, int n_vars, int n_rows)
{
float_t pivot, *pivot_row, c;
int i, j, step;
step = n_rows + n_vars + 2;
pivot = table[2 + col + (row+1)*step];
pivot_row = &table[1 + (row+1)*step];
table[(row+1)*step] = col;
for (i=0; i<n_vars+n_rows+1; i++)
pivot_row[i] /= pivot;
for (i=0; i<n_rows+1; i++)
if (i != row+1) {
c = table[2 + col + i*step];
for (j=0; j<n_vars+n_rows+1; j++)
table[j + 1 + i*step] -= pivot_row[j] * c;
}
}
int solver_simplex (Workbook *wb, Sheet *sheet)
{
int i, j, n;
SolverParameters *param = &sheet->solver_parameters;
CellList *cell_list = param->input_cells;
GSList *constraints = param->constraints;
Cell *cell;
float_t *table;
int col, row, status, step;
int n_vars;
int n_constraints;
gboolean max_flag;
max_flag = param->problem_type == SolverMaximize;
status = SIMPLEX_OK;
table = simplex_step_one(sheet,
param->target_cell->col->pos,
param->target_cell->row->pos,
cell_list, &n_vars,
constraints, &n_constraints, max_flag);
if (table == NULL)
return SIMPLEX_UNBOUNDED;
step = n_vars + n_constraints + 2;
for (;;) {
col = simplex_step_two(table, n_vars, &status);
if (status == SIMPLEX_DONE)
break;
row = simplex_step_three(table, col, n_vars,
n_constraints, &status);
if (status == SIMPLEX_UNBOUNDED)
break;
simplex_step_four(table, col, row, n_vars, n_constraints);
}
if (status != SIMPLEX_DONE)
return status;
for (i=1; i<n_constraints+1; i++) {
Cell *c;
cell_list = param->input_cells;
c = (Cell *) cell_list->data;
for (n=0; n < (int) table[i*step]; n++) {
cell_list = cell_list->next;
c = (Cell *) cell_list->data;
}
cell = sheet_cell_fetch(sheet, c->col->pos, c->row->pos);
cell_set_value (cell, value_new_float (table[1+i*step]));
}
cell = sheet_cell_fetch(sheet, param->target_cell->col->pos,
param->target_cell->row->pos);
cell_eval_content(cell);
/* FIXME: Do not do the following loop. Instead recalculate
* everything that depends on the input variables (the list of
* cells in params->input_cells).
*/
while (constraints != NULL) {
SolverConstraint *c = (SolverConstraint *) constraints->data;
cell = sheet_cell_fetch(sheet, c->lhs->col->pos,
c->lhs->row->pos);
cell_eval_content(cell);
constraints = constraints->next;
}
return SIMPLEX_DONE;
}
#ifndef GNUMERIC_SOLVER_H
#define GNUMERIC_SOLVER_H
#define GNUMERIC_SOLVER_H 1
#define SIMPLEX_OK 0
#define SIMPLEX_DONE 1
#define SIMPLEX_UNBOUNDED 2
/* Forward references for structures. */
typedef struct _SolverOptions SolverOptions;
......
......@@ -275,7 +275,7 @@ parse_cell_name_list (Sheet *sheet,
g_return_val_if_fail (error_flag != NULL, NULL);
buf = g_malloc (strlen (cell_name_str) + 1);
for (i = n = 0; cell_name_str [i]; i++){
for (i = n = 0; ; i++){
if ((cell_name_str [i] == ',') || (cell_name_str [i] == '\0')){
buf [n] = '\0';
......@@ -297,6 +297,8 @@ parse_cell_name_list (Sheet *sheet,
n = 0;
} else
buf [n++] = cell_name_str [i];
if (! cell_name_str [i])
break;
}
*error_flag = 0;
......
......@@ -940,7 +940,6 @@ goal_seek_cmd (GtkWidget *widget, Workbook *wb)
dialog_goal_seek (wb, sheet);
}
#if 0
static void
solver_cmd (GtkWidget *widget, Workbook *wb)
{
......@@ -949,7 +948,6 @@ solver_cmd (GtkWidget *widget, Workbook *wb)
sheet = workbook_get_current_sheet (wb);
dialog_solver (wb, sheet);
}
#endif
static void
data_analysis_cmd (GtkWidget *widget, Workbook *wb)
......@@ -1182,9 +1180,7 @@ static GnomeUIInfo workbook_menu_tools [] = {
{ GNOME_APP_UI_ITEM, N_("_Sort..."),
N_("Sort the selected cells"), sort_cells_cmd },
{ GNOME_APP_UI_ITEM, N_("_Goal Seek..."), NULL, goal_seek_cmd },
#if 0
{ GNOME_APP_UI_ITEM, N_("_Solver..."), NULL, solver_cmd },
#endif
{ GNOME_APP_UI_ITEM, N_("_Data Analysis..."), NULL, data_analysis_cmd },
GNOMEUIINFO_END
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment