1 #include <gnumeric-config.h>
8 #include "gnumeric-conf.h"
9 #include <gsf/gsf-impl-utils.h>
10 #include <glib/gi18n-lib.h>
13 #define SOLVER_PROGRAM "lp_solve"
14 #define SOLVER_URL "http://sourceforge.net/projects/lpsolve/"
15 #define PRIVATE_KEY "::lpsolve::"
16 #define SOLVER_INF 1e30
20 GnmSolverResult
*result
;
21 GnmSolverSensitivity
*sensitivity
;
22 enum { SEC_UNKNOWN
, SEC_VALUES
,
23 SEC_LIMITS
, SEC_DUAL_LIMITS
} section
;
27 gnm_lpsolve_cleanup (GnmLPSolve
*lp
)
29 gnm_sub_solver_clear (lp
->parent
);
32 g_object_unref (lp
->result
);
36 if (lp
->sensitivity
) {
37 g_object_unref (lp
->sensitivity
);
38 lp
->sensitivity
= NULL
;
43 gnm_lpsolve_final (GnmLPSolve
*lp
)
45 gnm_lpsolve_cleanup (lp
);
50 write_program (GnmSolver
*sol
, WorkbookControl
*wbc
, GError
**err
)
52 GnmSubSolver
*subsol
= GNM_SUB_SOLVER (sol
);
55 fs
= go_file_saver_for_mime_type ("application/lpsolve");
57 g_set_error (err
, G_FILE_ERROR
, 0,
58 _("The LPSolve exporter is not available."));
62 return gnm_solver_saveas (sol
, wbc
, fs
,
64 &subsol
->program_filename
,
68 static GnmSolverResult
*
69 gnm_lpsolve_start_solution (GnmLPSolve
*lp
)
74 g_return_val_if_fail (lp
->result
== NULL
, NULL
);
76 sol
= GNM_SOLVER (lp
->parent
);
77 n
= sol
->input_cells
->len
;
79 lp
->result
= g_object_new (GNM_SOLVER_RESULT_TYPE
, NULL
);
80 lp
->result
->solution
= g_new0 (gnm_float
, n
);
82 lp
->sensitivity
= gnm_solver_sensitivity_new (sol
);
88 gnm_lpsolve_flush_solution (GnmLPSolve
*lp
)
91 g_object_set (lp
->parent
, "result", lp
->result
, NULL
);
92 g_object_unref (lp
->result
);
95 g_clear_object (&lp
->sensitivity
);
100 my_strsplit (const char *line
)
102 GPtrArray
*res
= g_ptr_array_new ();
107 while (g_ascii_isspace (*line
))
114 while (*end
&& !g_ascii_isspace (*end
))
117 g_ptr_array_add (res
, g_strndup (line
, end
- line
));
120 g_ptr_array_add (res
, NULL
);
122 return (char **)g_ptr_array_free (res
, FALSE
);
128 if (v
<= -SOLVER_INF
)
130 if (v
>= +SOLVER_INF
)
137 cb_read_stdout (GIOChannel
*channel
, GIOCondition cond
, GnmLPSolve
*lp
)
139 GnmSolver
*sol
= GNM_SOLVER (lp
->parent
);
140 const char obj_line_prefix
[] = "Value of objective function:";
141 size_t obj_line_len
= sizeof (obj_line_prefix
) - 1;
142 const char val_header_line
[] = "Actual values of the variables:";
143 size_t val_header_len
= sizeof (val_header_line
) - 1;
144 const char limit_header_line
[] = "Objective function limits:";
145 size_t limit_header_len
= sizeof (limit_header_line
) - 1;
146 const char dual_limit_header_line
[] = "Dual values with from - till limits:";
147 size_t dual_limit_header_len
= sizeof (dual_limit_header_line
) - 1;
157 status
= g_io_channel_read_line (channel
,
160 if (status
!= G_IO_STATUS_NORMAL
)
166 lp
->section
= SEC_UNKNOWN
;
167 else if (lp
->section
== SEC_UNKNOWN
&&
168 !strncmp (line
, obj_line_prefix
, obj_line_len
)) {
170 gnm_lpsolve_flush_solution (lp
);
171 r
= gnm_lpsolve_start_solution (lp
);
172 r
->quality
= GNM_SOLVER_RESULT_FEASIBLE
;
173 r
->value
= g_ascii_strtod (line
+ obj_line_len
, NULL
);
174 } else if (lp
->section
== SEC_UNKNOWN
&&
175 !strncmp (line
, val_header_line
, val_header_len
)) {
176 lp
->section
= SEC_VALUES
;
177 } else if (lp
->section
== SEC_UNKNOWN
&&
178 !strncmp (line
, limit_header_line
, limit_header_len
)) {
179 lp
->section
= SEC_LIMITS
;
180 } else if (lp
->section
== SEC_UNKNOWN
&&
181 !strncmp (line
, dual_limit_header_line
, dual_limit_header_len
)) {
182 lp
->section
= SEC_DUAL_LIMITS
;
183 } else if (lp
->section
== SEC_VALUES
&& lp
->result
) {
184 GnmSolverResult
*r
= lp
->result
;
186 char *space
= strchr (line
, ' ');
191 lp
->section
= SEC_UNKNOWN
;
195 cell
= gnm_sub_solver_find_cell (lp
->parent
, line
);
196 idx
= gnm_solver_cell_index (sol
, cell
);
198 g_printerr ("Strange cell %s in output\n",
200 lp
->section
= SEC_UNKNOWN
;
204 v
= g_ascii_strtod (space
+ 1, NULL
);
205 r
->solution
[idx
] = v
;
206 } else if (lp
->section
== SEC_LIMITS
) {
212 if (g_ascii_isspace (line
[0]))
215 items
= my_strsplit (line
);
217 if (g_strv_length (items
) != 4)
220 cell
= gnm_sub_solver_find_cell (lp
->parent
, items
[0]);
221 idx
= gnm_solver_cell_index (sol
, cell
);
225 low
= fixup_inf (g_ascii_strtod (items
[1], NULL
));
226 high
= fixup_inf (g_ascii_strtod (items
[2], NULL
));
228 lp
->sensitivity
->vars
[idx
].low
= low
;
229 lp
->sensitivity
->vars
[idx
].high
= high
;
236 g_printerr ("Strange limit line in output: %s\n",
238 lp
->section
= SEC_UNKNOWN
;
240 } else if (lp
->section
== SEC_DUAL_LIMITS
) {
241 double dual
, low
, high
;
246 if (g_ascii_isspace (line
[0]))
249 items
= my_strsplit (line
);
251 if (g_strv_length (items
) != 4)
254 cell
= gnm_sub_solver_find_cell (lp
->parent
, items
[0]);
255 idx
= gnm_solver_cell_index (sol
, cell
);
258 ? gnm_sub_solver_find_constraint (lp
->parent
, items
[0])
261 dual
= fixup_inf (g_ascii_strtod (items
[1], NULL
));
262 low
= fixup_inf (g_ascii_strtod (items
[2], NULL
));
263 high
= fixup_inf (g_ascii_strtod (items
[3], NULL
));
266 lp
->sensitivity
->vars
[idx
].reduced_cost
= dual
;
267 } else if (cidx
>= 0) {
268 lp
->sensitivity
->constraints
[cidx
].low
= low
;
269 lp
->sensitivity
->constraints
[cidx
].high
= high
;
270 lp
->sensitivity
->constraints
[cidx
].shadow_price
= dual
;
279 g_printerr ("Strange dual limit line in output: %s\n",
281 lp
->section
= SEC_UNKNOWN
;
293 gnm_lpsolve_child_exit (GnmSubSolver
*subsol
, gboolean normal
, int code
,
296 GnmSolver
*sol
= GNM_SOLVER (subsol
);
297 GnmSolverStatus new_status
= GNM_SOLVER_STATUS_DONE
;
299 if (sol
->status
!= GNM_SOLVER_STATUS_RUNNING
)
306 case 0: /* Optimal */
307 gnm_sub_solver_flush (subsol
);
309 lp
->result
->quality
= GNM_SOLVER_RESULT_OPTIMAL
;
310 g_object_set (lp
->parent
,
311 "sensitivity", lp
->sensitivity
,
313 gnm_lpsolve_flush_solution (lp
);
316 case 2: /* Infeasible */
317 r
= gnm_lpsolve_start_solution (lp
);
318 r
->quality
= GNM_SOLVER_RESULT_INFEASIBLE
;
319 gnm_lpsolve_flush_solution (lp
);
322 case 3: /* Unbounded */
323 r
= gnm_lpsolve_start_solution (lp
);
324 r
->quality
= GNM_SOLVER_RESULT_UNBOUNDED
;
325 gnm_lpsolve_flush_solution (lp
);
328 case 1: /* Suboptimal */
329 case 4: /* Degenerate */
330 gnm_sub_solver_flush (subsol
);
331 gnm_lpsolve_flush_solution (lp
);
335 case 5: /* Numfailure */
336 case 6: /* Userabort */
337 case 7: /* Timeout */
338 case 8: /* Running (eh?) */
339 case 9: /* Presolved (eh?) */
340 new_status
= GNM_SOLVER_STATUS_ERROR
;
345 new_status
= GNM_SOLVER_STATUS_ERROR
;
348 gnm_solver_set_status (sol
, new_status
);
352 cb_child_setup (gpointer user
)
354 const char *lcvars
[] = {
363 for (ui
= 0; ui
< G_N_ELEMENTS (lcvars
); ui
++) {
364 const char *v
= lcvars
[ui
];
366 g_setenv (v
, "C", TRUE
);
371 gnm_lpsolve_prepare (GnmSolver
*sol
, WorkbookControl
*wbc
, GError
**err
,
376 g_return_val_if_fail (sol
->status
== GNM_SOLVER_STATUS_READY
, FALSE
);
378 gnm_solver_set_status (sol
, GNM_SOLVER_STATUS_PREPARING
);
379 ok
= write_program (sol
, wbc
, err
);
381 gnm_solver_set_status (sol
, GNM_SOLVER_STATUS_PREPARED
);
383 gnm_lpsolve_cleanup (lp
);
384 gnm_solver_set_status (sol
, GNM_SOLVER_STATUS_ERROR
);
391 gnm_lpsolve_start (GnmSolver
*sol
, WorkbookControl
*wbc
, GError
**err
,
394 GnmSubSolver
*subsol
= GNM_SUB_SOLVER (sol
);
398 GnmSolverParameters
*param
= sol
->params
;
401 g_return_val_if_fail (sol
->status
== GNM_SOLVER_STATUS_PREPARED
, FALSE
);
403 binary
= gnm_conf_get_plugin_lpsolve_lpsolve_path ();
404 if (binary
== NULL
|| *binary
== 0)
405 binary
= SOLVER_PROGRAM
;
407 argv
[argc
++] = (gchar
*)binary
;
408 argv
[argc
++] = (gchar
*)"-i";
409 argv
[argc
++] = (gchar
*)(param
->options
.automatic_scaling
412 argv
[argc
++] = (gchar
*)"-S6";
413 argv
[argc
++] = subsol
->program_filename
;
415 g_assert (argc
< (int)G_N_ELEMENTS (argv
));
417 ok
= gnm_sub_solver_spawn (subsol
, argv
,
418 cb_child_setup
, NULL
,
419 (GIOFunc
)cb_read_stdout
, lp
,
424 g_error_matches (*err
, G_SPAWN_ERROR
, G_SPAWN_ERROR_NOENT
)) {
426 g_set_error (err
, G_SPAWN_ERROR
, G_SPAWN_ERROR_NOENT
,
427 _("The %s program was not found. You can either "
428 "install it or use another solver. "
429 "For more information see %s"),
438 gnm_lpsolve_stop (GnmSolver
*sol
, GError
*err
, GnmLPSolve
*lp
)
440 g_return_val_if_fail (sol
->status
== GNM_SOLVER_STATUS_RUNNING
, FALSE
);
442 gnm_lpsolve_cleanup (lp
);
444 gnm_solver_set_status (sol
, GNM_SOLVER_STATUS_CANCELLED
);
450 lpsolve_solver_create (GnmSolverParameters
*params
)
452 GnmSolver
*res
= g_object_new (GNM_SUB_SOLVER_TYPE
,
455 GnmLPSolve
*lp
= g_new0 (GnmLPSolve
, 1);
457 lp
->parent
= GNM_SUB_SOLVER (res
);
459 g_signal_connect (res
, "prepare", G_CALLBACK (gnm_lpsolve_prepare
), lp
);
460 g_signal_connect (res
, "start", G_CALLBACK (gnm_lpsolve_start
), lp
);
461 g_signal_connect (res
, "stop", G_CALLBACK (gnm_lpsolve_stop
), lp
);
462 g_signal_connect (res
, "child-exit", G_CALLBACK (gnm_lpsolve_child_exit
), lp
);
464 g_object_set_data_full (G_OBJECT (res
), PRIVATE_KEY
, lp
,
465 (GDestroyNotify
)gnm_lpsolve_final
);
472 lpsolve_solver_factory_functional (GnmSolverFactory
*factory
,
475 const char *full_path
= gnm_conf_get_plugin_lpsolve_lpsolve_path ();
478 if (full_path
&& *full_path
)
479 return g_file_test (full_path
, G_FILE_TEST_IS_EXECUTABLE
);
481 path
= g_find_program_in_path (SOLVER_PROGRAM
);
490 path
= gnm_sub_solver_locate_binary (SOLVER_PROGRAM
,
495 gnm_conf_set_plugin_lpsolve_lpsolve_path (path
);
504 lpsolve_solver_factory (GnmSolverFactory
*factory
, GnmSolverParameters
*params
)
506 return lpsolve_solver_create (params
);