1 #include <gnumeric-config.h>
6 #include "expr-deriv.h"
13 #include "gnm-solver.h"
14 #include "workbook-view.h"
15 #include "workbook-control.h"
16 #include "application.h"
17 #include "gnm-marshalers.h"
18 #include <tools/dao.h>
21 #include <gsf/gsf-impl-utils.h>
22 #include <gsf/gsf-output-stdio.h>
23 #include <glib/gi18n-lib.h>
24 #include <glib/gstdio.h>
28 #ifdef HAVE_SYS_WAIT_H
35 #define WIFEXITED(x) ((x) != STILL_ACTIVE)
38 #define WEXITSTATUS(x) (x)
42 /* ------------------------------------------------------------------------- */
45 gnm_solver_debug (void)
47 static int debug
= -1;
49 debug
= gnm_debug_flag ("solver");
54 gnm_solver_cell_name (GnmCell
const *cell
, Sheet
*origin
)
56 GnmConventionsOut out
;
60 gnm_cellref_init (&cr
, cell
->base
.sheet
,
61 cell
->pos
.col
, cell
->pos
.row
,
63 out
.accum
= g_string_new (NULL
);
64 out
.pp
= parse_pos_init_sheet (&pp
, origin
);
65 out
.convs
= origin
->convs
;
66 cellref_as_string (&out
, &cr
, cell
->base
.sheet
== origin
);
67 return g_string_free (out
.accum
, FALSE
);
70 /* ------------------------------------------------------------------------- */
73 gnm_solver_status_get_type (void)
75 static GType etype
= 0;
77 static GEnumValue
const values
[] = {
78 { GNM_SOLVER_STATUS_READY
,
79 "GNM_SOLVER_STATUS_READY",
82 { GNM_SOLVER_STATUS_PREPARING
,
83 "GNM_SOLVER_STATUS_PREPARING",
86 { GNM_SOLVER_STATUS_PREPARED
,
87 "GNM_SOLVER_STATUS_PREPARED",
90 { GNM_SOLVER_STATUS_RUNNING
,
91 "GNM_SOLVER_STATUS_RUNNING",
94 { GNM_SOLVER_STATUS_DONE
,
95 "GNM_SOLVER_STATUS_DONE",
98 { GNM_SOLVER_STATUS_ERROR
,
99 "GNM_SOLVER_STATUS_ERROR",
102 { GNM_SOLVER_STATUS_CANCELLED
,
103 "GNM_SOLVER_STATUS_CANCELLED",
108 etype
= g_enum_register_static ("GnmSolverStatus", values
);
114 gnm_solver_problem_type_get_type (void)
116 static GType etype
= 0;
118 static GEnumValue
const values
[] = {
119 { GNM_SOLVER_MINIMIZE
,
120 "GNM_SOLVER_MINIMIZE",
123 { GNM_SOLVER_MAXIMIZE
,
124 "GNM_SOLVER_MAXIMIZE",
129 etype
= g_enum_register_static ("GnmSolverProblemType", values
);
134 /* ------------------------------------------------------------------------- */
136 static GObjectClass
*gnm_solver_parent_class
;
138 GnmSolverConstraint
*
139 gnm_solver_constraint_new (Sheet
*sheet
)
141 GnmSolverConstraint
*res
= g_new0 (GnmSolverConstraint
, 1);
142 dependent_managed_init (&res
->lhs
, sheet
);
143 dependent_managed_init (&res
->rhs
, sheet
);
148 gnm_solver_constraint_free (GnmSolverConstraint
*c
)
150 gnm_solver_constraint_set_lhs (c
, NULL
);
151 gnm_solver_constraint_set_rhs (c
, NULL
);
155 GnmSolverConstraint
*
156 gnm_solver_constraint_dup (GnmSolverConstraint
*c
, Sheet
*sheet
)
158 GnmSolverConstraint
*res
= gnm_solver_constraint_new (sheet
);
160 dependent_managed_set_expr (&res
->lhs
, c
->lhs
.texpr
);
161 dependent_managed_set_expr (&res
->rhs
, c
->rhs
.texpr
);
165 static GnmSolverConstraint
*
166 gnm_solver_constraint_dup1 (GnmSolverConstraint
*c
)
168 return gnm_solver_constraint_dup (c
, c
->lhs
.sheet
);
172 gnm_solver_constraint_get_type (void)
177 t
= g_boxed_type_register_static ("GnmSolverConstraint",
178 (GBoxedCopyFunc
)gnm_solver_constraint_dup1
,
179 (GBoxedFreeFunc
)gnm_solver_constraint_free
);
184 gnm_solver_constraint_equal (GnmSolverConstraint
const *a
,
185 GnmSolverConstraint
const *b
)
187 return (a
->type
== b
->type
&&
188 gnm_expr_top_equal (a
->lhs
.texpr
, b
->lhs
.texpr
) &&
189 (!gnm_solver_constraint_has_rhs (a
) ||
190 gnm_expr_top_equal (a
->rhs
.texpr
, b
->rhs
.texpr
)));
194 gnm_solver_constraint_has_rhs (GnmSolverConstraint
const *c
)
196 g_return_val_if_fail (c
!= NULL
, FALSE
);
203 case GNM_SOLVER_INTEGER
:
204 case GNM_SOLVER_BOOLEAN
:
211 gnm_solver_constraint_valid (GnmSolverConstraint
const *c
,
212 GnmSolverParameters
const *sp
)
216 g_return_val_if_fail (c
!= NULL
, FALSE
);
218 lhs
= gnm_solver_constraint_get_lhs (c
);
219 if (lhs
== NULL
|| !VALUE_IS_CELLRANGE (lhs
))
222 if (gnm_solver_constraint_has_rhs (c
)) {
223 GnmValue
const *rhs
= gnm_solver_constraint_get_rhs (c
);
226 if (VALUE_IS_CELLRANGE (rhs
)) {
227 GnmSheetRange srl
, srr
;
229 gnm_sheet_range_from_value (&srl
, lhs
);
230 gnm_sheet_range_from_value (&srr
, rhs
);
232 if (range_width (&srl
.range
) != range_width (&srr
.range
) ||
233 range_height (&srl
.range
) != range_height (&srr
.range
))
235 } else if (VALUE_IS_FLOAT (rhs
)) {
242 case GNM_SOLVER_INTEGER
:
243 case GNM_SOLVER_BOOLEAN
: {
244 GnmValue
const *vinput
= gnm_solver_param_get_input (sp
);
245 GnmSheetRange sr_input
, sr_c
;
248 break; /* No need to blame constraint. */
250 gnm_sheet_range_from_value (&sr_input
, vinput
);
251 gnm_sheet_range_from_value (&sr_c
, lhs
);
253 if (eval_sheet (sr_input
.sheet
, sp
->sheet
) !=
254 eval_sheet (sr_c
.sheet
, sp
->sheet
) ||
255 !range_contained (&sr_c
.range
, &sr_input
.range
))
268 * gnm_solver_constraint_get_lhs:
269 * @c: GnmSolverConstraint
271 * Returns: (transfer none) (nullable): Get left-hand side of constraint @c.
274 gnm_solver_constraint_get_lhs (GnmSolverConstraint
const *c
)
276 GnmExprTop
const *texpr
= c
->lhs
.texpr
;
277 return texpr
? gnm_expr_top_get_constant (texpr
) : NULL
;
281 * gnm_solver_constraint_set_lhs:
282 * @c: GnmSolverConstraint
283 * @v: (transfer full) (nullable): new left-hand side
286 gnm_solver_constraint_set_lhs (GnmSolverConstraint
*c
, GnmValue
*v
)
288 GnmExprTop
const *texpr
= v
? gnm_expr_top_new_constant (v
) : NULL
;
289 dependent_managed_set_expr (&c
->lhs
, texpr
);
290 if (texpr
) gnm_expr_top_unref (texpr
);
294 * gnm_solver_constraint_get_rhs:
295 * @c: GnmSolverConstraint
297 * Returns: (transfer none) (nullable): Get right-hand side of constraint @c.
300 gnm_solver_constraint_get_rhs (GnmSolverConstraint
const *c
)
302 GnmExprTop
const *texpr
= c
->rhs
.texpr
;
303 return texpr
? gnm_expr_top_get_constant (texpr
) : NULL
;
307 * gnm_solver_constraint_set_rhs:
308 * @c: GnmSolverConstraint
309 * @v: (transfer full) (nullable): new right-hand side
312 gnm_solver_constraint_set_rhs (GnmSolverConstraint
*c
, GnmValue
*v
)
314 GnmExprTop
const *texpr
= v
? gnm_expr_top_new_constant (v
) : NULL
;
315 dependent_managed_set_expr (&c
->rhs
, texpr
);
316 if (texpr
) gnm_expr_top_unref (texpr
);
320 * gnm_solver_constraint_get_part:
321 * @c: GnmSolverConstraint
322 * @sp: GnmSolverParameters
324 * @lhs: (optional) (out): #GnmCell of left-hand side
325 * @cl: (optional) (out): constant value of left-hand side
326 * @rhs: (optional) (out): #GnmCell of right-hand side
327 * @cr: (optional) (out): constant value of left-hand side
329 * This splits @c into parts and returns information about the @i'th part.
330 * There will be multiple parts when the left-hand side is a cell range.
332 * Returns: %TRUE if the @i'th part exists.
335 gnm_solver_constraint_get_part (GnmSolverConstraint
const *c
,
336 GnmSolverParameters
const *sp
, int i
,
337 GnmCell
**lhs
, gnm_float
*cl
,
338 GnmCell
**rhs
, gnm_float
*cr
)
342 GnmValue
const *vl
, *vr
;
346 if (lhs
) *lhs
= NULL
;
347 if (rhs
) *rhs
= NULL
;
349 if (!gnm_solver_constraint_valid (c
, sp
))
352 vl
= gnm_solver_constraint_get_lhs (c
);
353 vr
= gnm_solver_constraint_get_rhs (c
);
355 gnm_sheet_range_from_value (&sr
, vl
);
356 w
= range_width (&sr
.range
);
357 h
= range_height (&sr
.range
);
365 *lhs
= sheet_cell_get (eval_sheet (sr
.sheet
, sp
->sheet
),
366 sr
.range
.start
.col
+ dx
,
367 sr
.range
.start
.row
+ dy
);
369 if (!gnm_solver_constraint_has_rhs (c
)) {
371 } else if (VALUE_IS_FLOAT (vr
)) {
373 *cr
= value_get_as_float (vr
);
375 gnm_sheet_range_from_value (&sr
, vr
);
377 *rhs
= sheet_cell_get (eval_sheet (sr
.sheet
, sp
->sheet
),
378 sr
.range
.start
.col
+ dx
,
379 sr
.range
.start
.row
+ dy
);
386 gnm_solver_constraint_set_old (GnmSolverConstraint
*c
,
387 GnmSolverConstraintType type
,
388 int lhs_col
, int lhs_row
,
389 int rhs_col
, int rhs_row
,
398 lhs_col
+ (cols
- 1), lhs_row
+ (rows
- 1));
399 gnm_solver_constraint_set_lhs
400 (c
, value_new_cellrange_r (NULL
, &r
));
402 if (gnm_solver_constraint_has_rhs (c
)) {
405 rhs_col
+ (cols
- 1), rhs_row
+ (rows
- 1));
406 gnm_solver_constraint_set_rhs
407 (c
, value_new_cellrange_r (NULL
, &r
));
409 gnm_solver_constraint_set_rhs (c
, NULL
);
413 gnm_solver_constraint_side_as_str (GnmSolverConstraint
const *c
,
415 GString
*buf
, gboolean lhs
)
417 GnmExprTop
const *texpr
;
419 texpr
= lhs
? c
->lhs
.texpr
: c
->rhs
.texpr
;
421 GnmConventionsOut out
;
425 out
.pp
= parse_pos_init_sheet (&pp
, sheet
);
426 out
.convs
= sheet
->convs
;
427 gnm_expr_top_as_gstring (texpr
, &out
);
429 g_string_append (buf
,
430 value_error_name (GNM_ERROR_REF
,
431 sheet
->convs
->output
.translated
));
435 gnm_solver_constraint_as_str (GnmSolverConstraint
const *c
, Sheet
*sheet
)
437 const char * const type_str
[] = {
438 "\xe2\x89\xa4" /* "<=" */,
439 "\xe2\x89\xa5" /* ">=" */,
444 const char *type
= type_str
[c
->type
];
445 gboolean translate
= (c
->type
>= GNM_SOLVER_INTEGER
);
446 GString
*buf
= g_string_new (NULL
);
448 gnm_solver_constraint_side_as_str (c
, sheet
, buf
, TRUE
);
449 g_string_append_c (buf
, ' ');
450 g_string_append (buf
, translate
? _(type
) : type
);
451 if (gnm_solver_constraint_has_rhs (c
)) {
452 g_string_append_c (buf
, ' ');
453 gnm_solver_constraint_side_as_str (c
, sheet
, buf
, FALSE
);
456 return g_string_free (buf
, FALSE
);
460 gnm_solver_constraint_part_as_str (GnmSolverConstraint
const *c
, int i
,
461 GnmSolverParameters
*sp
)
463 const char * const type_str
[] = {
464 "\xe2\x89\xa4" /* "<=" */,
465 "\xe2\x89\xa5" /* ">=" */,
470 const char *type
= type_str
[c
->type
];
471 gboolean translate
= (c
->type
>= GNM_SOLVER_INTEGER
);
476 if (!gnm_solver_constraint_get_part (c
, sp
, i
, &lhs
, &cl
, &rhs
, &cr
))
479 buf
= g_string_new (NULL
);
481 g_string_append (buf
, cell_name (lhs
));
482 g_string_append_c (buf
, ' ');
483 g_string_append (buf
, translate
? _(type
) : type
);
484 if (gnm_solver_constraint_has_rhs (c
)) {
485 g_string_append_c (buf
, ' ');
486 g_string_append (buf
, cell_name (rhs
));
489 return g_string_free (buf
, FALSE
);
492 /* ------------------------------------------------------------------------- */
497 SOLP_PROP_PROBLEM_TYPE
500 static GObjectClass
*gnm_solver_param_parent_class
;
502 GnmSolverParameters
*
503 gnm_solver_param_new (Sheet
*sheet
)
505 return g_object_new (GNM_SOLVER_PARAMETERS_TYPE
,
511 * gnm_solver_param_dup:
512 * @src: #GnmSolverParameters
515 * Returns: (transfer full): duplicate @src, but for @new_sheet.
517 GnmSolverParameters
*
518 gnm_solver_param_dup (GnmSolverParameters
*src
, Sheet
*new_sheet
)
520 GnmSolverParameters
*dst
= gnm_solver_param_new (new_sheet
);
523 dst
->problem_type
= src
->problem_type
;
524 dependent_managed_set_expr (&dst
->target
, src
->target
.texpr
);
525 dependent_managed_set_expr (&dst
->input
, src
->input
.texpr
);
527 g_free (dst
->options
.scenario_name
);
528 dst
->options
= src
->options
;
529 dst
->options
.algorithm
= NULL
;
530 dst
->options
.scenario_name
= g_strdup (src
->options
.scenario_name
);
531 gnm_solver_param_set_algorithm (dst
, src
->options
.algorithm
);
533 /* Copy the constraints */
534 for (l
= src
->constraints
; l
; l
= l
->next
) {
535 GnmSolverConstraint
*old
= l
->data
;
536 GnmSolverConstraint
*new =
537 gnm_solver_constraint_dup (old
, new_sheet
);
539 dst
->constraints
= g_slist_prepend (dst
->constraints
, new);
541 dst
->constraints
= g_slist_reverse (dst
->constraints
);
547 gnm_solver_param_equal (GnmSolverParameters
const *a
,
548 GnmSolverParameters
const *b
)
552 if (a
->sheet
!= b
->sheet
||
553 a
->problem_type
!= b
->problem_type
||
554 !gnm_expr_top_equal (a
->target
.texpr
, b
->target
.texpr
) ||
555 !gnm_expr_top_equal (a
->input
.texpr
, b
->input
.texpr
) ||
556 a
->options
.max_time_sec
!= b
->options
.max_time_sec
||
557 a
->options
.max_iter
!= b
->options
.max_iter
||
558 a
->options
.algorithm
!= b
->options
.algorithm
||
559 a
->options
.model_type
!= b
->options
.model_type
||
560 a
->options
.assume_non_negative
!= b
->options
.assume_non_negative
||
561 a
->options
.assume_discrete
!= b
->options
.assume_discrete
||
562 a
->options
.automatic_scaling
!= b
->options
.automatic_scaling
||
563 a
->options
.program_report
!= b
->options
.program_report
||
564 a
->options
.sensitivity_report
!= b
->options
.sensitivity_report
||
565 a
->options
.add_scenario
!= b
->options
.add_scenario
||
566 strcmp (a
->options
.scenario_name
, b
->options
.scenario_name
) ||
567 a
->options
.gradient_order
!= b
->options
.gradient_order
)
570 for (la
= a
->constraints
, lb
= b
->constraints
;
572 la
= la
->next
, lb
= lb
->next
) {
573 GnmSolverConstraint
*ca
= la
->data
;
574 GnmSolverConstraint
*cb
= lb
->data
;
575 if (!gnm_solver_constraint_equal (ca
, cb
))
582 * gnm_solver_param_get_input:
583 * @sp: #GnmSolverParameters
585 * Returns: (transfer none) (nullable): the input cell area.
588 gnm_solver_param_get_input (GnmSolverParameters
const *sp
)
590 return sp
->input
.texpr
591 ? gnm_expr_top_get_constant (sp
->input
.texpr
)
596 * gnm_solver_param_set_input:
597 * @sp: #GnmSolverParameters
598 * @v: (transfer full) (nullable): new input area
601 gnm_solver_param_set_input (GnmSolverParameters
*sp
, GnmValue
*v
)
603 GnmExprTop
const *texpr
= v
? gnm_expr_top_new_constant (v
) : NULL
;
604 dependent_managed_set_expr (&sp
->input
, texpr
);
605 if (texpr
) gnm_expr_top_unref (texpr
);
609 cb_grab_cells (GnmCellIter
const *iter
, gpointer user
)
611 GPtrArray
*input_cells
= user
;
614 if (NULL
== (cell
= iter
->cell
))
615 cell
= sheet_cell_create (iter
->pp
.sheet
,
616 iter
->pp
.eval
.col
, iter
->pp
.eval
.row
);
617 g_ptr_array_add (input_cells
, cell
);
622 * gnm_solver_param_get_input_cells:
623 * @sp: #GnmSolverParameters
625 * Returns: (element-type GnmCell) (transfer container):
628 gnm_solver_param_get_input_cells (GnmSolverParameters
const *sp
)
630 GnmValue
const *vr
= gnm_solver_param_get_input (sp
);
631 GPtrArray
*input_cells
= g_ptr_array_new ();
635 eval_pos_init_sheet (&ep
, sp
->sheet
);
636 workbook_foreach_cell_in_range (&ep
, vr
, CELL_ITER_ALL
,
645 gnm_solver_param_set_target (GnmSolverParameters
*sp
, GnmCellRef
const *cr
)
648 GnmExprTop
const *texpr
;
649 GnmCellRef cr2
= *cr
;
650 /* Make reference absolute to avoid tracking problems on row/col
652 cr2
.row_relative
= FALSE
;
653 cr2
.col_relative
= FALSE
;
655 texpr
= gnm_expr_top_new (gnm_expr_new_cellref (&cr2
));
656 dependent_managed_set_expr (&sp
->target
, texpr
);
657 gnm_expr_top_unref (texpr
);
659 dependent_managed_set_expr (&sp
->target
, NULL
);
663 gnm_solver_param_get_target (GnmSolverParameters
const *sp
)
665 return sp
->target
.texpr
666 ? gnm_expr_top_get_cellref (sp
->target
.texpr
)
671 gnm_solver_param_get_target_cell (GnmSolverParameters
const *sp
)
673 const GnmCellRef
*cr
= gnm_solver_param_get_target (sp
);
677 return sheet_cell_get (eval_sheet (cr
->sheet
, sp
->sheet
),
682 gnm_solver_param_set_algorithm (GnmSolverParameters
*sp
,
683 GnmSolverFactory
*algo
)
685 sp
->options
.algorithm
= algo
;
689 gnm_solver_param_valid (GnmSolverParameters
const *sp
, GError
**err
)
693 GnmCell
*target_cell
;
694 GPtrArray
*input_cells
;
697 target_cell
= gnm_solver_param_get_target_cell (sp
);
702 _("Invalid solver target"));
705 gnm_cell_eval (target_cell
);
707 if (!gnm_cell_has_expr (target_cell
) ||
708 target_cell
->value
== NULL
||
709 !VALUE_IS_FLOAT (target_cell
->value
)) {
710 char *tcname
= gnm_solver_cell_name (target_cell
, sp
->sheet
);
714 _("Target cell, %s, must contain a formula that evaluates to a number"),
720 if (!gnm_solver_param_get_input (sp
)) {
724 _("Invalid solver input range"));
727 input_cells
= gnm_solver_param_get_input_cells (sp
);
728 for (ui
= 0; ui
< input_cells
->len
; ui
++) {
729 GnmCell
*cell
= g_ptr_array_index (input_cells
, ui
);
730 if (gnm_cell_has_expr (cell
)) {
731 char *cname
= gnm_solver_cell_name (cell
, sp
->sheet
);
735 _("Input cell %s contains a formula"),
738 g_ptr_array_free (input_cells
, TRUE
);
742 g_ptr_array_free (input_cells
, TRUE
);
744 for (i
= 1, l
= sp
->constraints
; l
; i
++, l
= l
->next
) {
745 GnmSolverConstraint
*c
= l
->data
;
746 if (!gnm_solver_constraint_valid (c
, sp
)) {
750 _("Solver constraint #%d is invalid"),
760 gnm_solver_param_constructor (GType type
,
761 guint n_construct_properties
,
762 GObjectConstructParam
*construct_params
)
765 GnmSolverParameters
*sp
;
767 obj
= gnm_solver_param_parent_class
->constructor
768 (type
, n_construct_properties
, construct_params
);
769 sp
= GNM_SOLVER_PARAMETERS (obj
);
771 dependent_managed_init (&sp
->target
, sp
->sheet
);
772 dependent_managed_init (&sp
->input
, sp
->sheet
);
774 sp
->options
.model_type
= GNM_SOLVER_LP
;
775 sp
->options
.max_iter
= 1000;
776 sp
->options
.max_time_sec
= 60;
777 sp
->options
.assume_non_negative
= TRUE
;
778 sp
->options
.scenario_name
= g_strdup ("Optimal");
779 sp
->options
.gradient_order
= 10;
785 gnm_solver_param_finalize (GObject
*obj
)
787 GnmSolverParameters
*sp
= GNM_SOLVER_PARAMETERS (obj
);
789 dependent_managed_set_expr (&sp
->target
, NULL
);
790 dependent_managed_set_expr (&sp
->input
, NULL
);
791 g_slist_free_full (sp
->constraints
,
792 (GFreeFunc
)gnm_solver_constraint_free
);
793 g_free (sp
->options
.scenario_name
);
795 gnm_solver_param_parent_class
->finalize (obj
);
799 gnm_solver_param_get_property (GObject
*object
, guint property_id
,
800 GValue
*value
, GParamSpec
*pspec
)
802 GnmSolverParameters
*sp
= (GnmSolverParameters
*)object
;
804 switch (property_id
) {
805 case SOLP_PROP_SHEET
:
806 g_value_set_object (value
, sp
->sheet
);
809 case SOLP_PROP_PROBLEM_TYPE
:
810 g_value_set_enum (value
, sp
->problem_type
);
814 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
820 gnm_solver_param_set_property (GObject
*object
, guint property_id
,
821 GValue
const *value
, GParamSpec
*pspec
)
823 GnmSolverParameters
*sp
= (GnmSolverParameters
*)object
;
825 switch (property_id
) {
826 case SOLP_PROP_SHEET
:
827 /* We hold no ref. */
828 sp
->sheet
= g_value_get_object (value
);
831 case SOLP_PROP_PROBLEM_TYPE
:
832 sp
->problem_type
= g_value_get_enum (value
);
836 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
842 gnm_solver_param_class_init (GObjectClass
*object_class
)
844 gnm_solver_param_parent_class
= g_type_class_peek_parent (object_class
);
846 object_class
->constructor
= gnm_solver_param_constructor
;
847 object_class
->finalize
= gnm_solver_param_finalize
;
848 object_class
->set_property
= gnm_solver_param_set_property
;
849 object_class
->get_property
= gnm_solver_param_get_property
;
851 g_object_class_install_property (object_class
, SOLP_PROP_SHEET
,
852 g_param_spec_object ("sheet",
857 G_PARAM_CONSTRUCT_ONLY
|
860 g_object_class_install_property (object_class
, SOLP_PROP_PROBLEM_TYPE
,
861 g_param_spec_enum ("problem-type",
864 GNM_SOLVER_PROBLEM_TYPE_TYPE
,
870 GSF_CLASS (GnmSolverParameters
, gnm_solver_param
,
871 gnm_solver_param_class_init
, NULL
, G_TYPE_OBJECT
)
873 /* ------------------------------------------------------------------------- */
882 static guint solver_signals
[SOL_SIG_LAST
] = { 0 };
890 SOL_PROP_SENSITIVITY
,
896 static GObjectClass
*gnm_solver_parent_class
;
898 static void gnm_solver_update_derived (GnmSolver
*sol
);
901 gnm_solver_dispose (GObject
*obj
)
903 GnmSolver
*sol
= GNM_SOLVER (obj
);
905 if (sol
->status
== GNM_SOLVER_STATUS_RUNNING
) {
906 gboolean ok
= gnm_solver_stop (sol
, NULL
);
908 g_warning ("Failed to stop solver -- now what?");
912 g_free (sol
->reason
);
916 g_object_unref (sol
->result
);
920 if (sol
->sensitivity
) {
921 g_object_unref (sol
->sensitivity
);
922 sol
->sensitivity
= NULL
;
926 g_object_unref (sol
->params
);
928 gnm_solver_update_derived (sol
);
932 sol
->gradient_status
= 0;
933 g_ptr_array_unref (sol
->gradient
);
934 sol
->gradient
= NULL
;
938 sol
->hessian_status
= 0;
939 g_ptr_array_unref (sol
->hessian
);
943 gnm_solver_parent_class
->dispose (obj
);
947 gnm_solver_get_property (GObject
*object
, guint property_id
,
948 GValue
*value
, GParamSpec
*pspec
)
950 GnmSolver
*sol
= (GnmSolver
*)object
;
952 switch (property_id
) {
953 case SOL_PROP_STATUS
:
954 g_value_set_enum (value
, sol
->status
);
957 case SOL_PROP_REASON
:
958 g_value_set_string (value
, sol
->reason
);
961 case SOL_PROP_PARAMS
:
962 g_value_set_object (value
, sol
->params
);
965 case SOL_PROP_RESULT
:
966 g_value_set_object (value
, sol
->result
);
969 case SOL_PROP_SENSITIVITY
:
970 g_value_set_object (value
, sol
->sensitivity
);
973 case SOL_PROP_STARTTIME
:
974 g_value_set_double (value
, sol
->starttime
);
977 case SOL_PROP_ENDTIME
:
978 g_value_set_double (value
, sol
->endtime
);
981 case SOL_PROP_FLIP_SIGN
:
982 g_value_set_boolean (value
, sol
->flip_sign
);
986 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
992 gnm_solver_set_property (GObject
*object
, guint property_id
,
993 GValue
const *value
, GParamSpec
*pspec
)
995 GnmSolver
*sol
= (GnmSolver
*)object
;
997 switch (property_id
) {
998 case SOL_PROP_STATUS
:
999 gnm_solver_set_status (sol
, g_value_get_enum (value
));
1002 case SOL_PROP_REASON
:
1003 gnm_solver_set_reason (sol
, g_value_get_string (value
));
1006 case SOL_PROP_PARAMS
: {
1007 GnmSolverParameters
*p
= g_value_dup_object (value
);
1008 if (sol
->params
) g_object_unref (sol
->params
);
1010 gnm_solver_update_derived (sol
);
1014 case SOL_PROP_RESULT
: {
1015 GnmSolverResult
*r
= g_value_dup_object (value
);
1016 if (sol
->result
) g_object_unref (sol
->result
);
1021 case SOL_PROP_SENSITIVITY
: {
1022 GnmSolverSensitivity
*s
= g_value_dup_object (value
);
1023 if (sol
->sensitivity
) g_object_unref (sol
->sensitivity
);
1024 sol
->sensitivity
= s
;
1028 case SOL_PROP_STARTTIME
:
1029 sol
->starttime
= g_value_get_double (value
);
1032 case SOL_PROP_ENDTIME
:
1033 sol
->endtime
= g_value_get_double (value
);
1036 case SOL_PROP_FLIP_SIGN
:
1037 sol
->flip_sign
= g_value_get_boolean (value
);
1041 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
1047 * gnm_solver_prepare: (virtual prepare)
1049 * @wbc: control for user interaction
1050 * @err: location to store error
1052 * Prepare for solving. Preparation need not do anything, but may include
1053 * such tasks as checking that the model is valid for the solver and
1054 * locating necessary external programs.
1056 * Returns: %TRUE ok success, %FALSE on error.
1059 gnm_solver_prepare (GnmSolver
*sol
, WorkbookControl
*wbc
, GError
**err
)
1063 g_return_val_if_fail (GNM_IS_SOLVER (sol
), FALSE
);
1064 g_return_val_if_fail (sol
->status
== GNM_SOLVER_STATUS_READY
, FALSE
);
1066 g_signal_emit (sol
, solver_signals
[SOL_SIG_PREPARE
], 0, wbc
, err
, &res
);
1071 * gnm_solver_start: (virtual start)
1073 * @wbc: control for user interaction
1074 * @err: location to store error
1076 * Start the solving process. If needed, the solver will be prepared first.
1078 * Returns: %TRUE ok success, %FALSE on error.
1081 gnm_solver_start (GnmSolver
*sol
, WorkbookControl
*wbc
, GError
**err
)
1085 g_return_val_if_fail (sol
->status
== GNM_SOLVER_STATUS_READY
||
1086 sol
->status
== GNM_SOLVER_STATUS_PREPARED
,
1089 if (sol
->status
== GNM_SOLVER_STATUS_READY
) {
1090 res
= gnm_solver_prepare (sol
, wbc
, err
);
1095 g_return_val_if_fail (sol
->status
== GNM_SOLVER_STATUS_PREPARED
, FALSE
);
1097 g_signal_emit (sol
, solver_signals
[SOL_SIG_START
], 0, wbc
, err
, &res
);
1102 * gnm_solver_stop: (virtual stop)
1104 * @err: location to store error
1106 * Terminate the currently-running solver.
1108 * Returns: %TRUE ok success, %FALSE on error.
1111 gnm_solver_stop (GnmSolver
*sol
, GError
**err
)
1115 g_return_val_if_fail (GNM_IS_SOLVER (sol
), FALSE
);
1117 g_signal_emit (sol
, solver_signals
[SOL_SIG_STOP
], 0, err
, &res
);
1125 g_get_current_time (&now
);
1126 return now
.tv_sec
+ (now
.tv_usec
/ 1e6
);
1131 gnm_solver_elapsed (GnmSolver
*solver
)
1135 g_return_val_if_fail (GNM_IS_SOLVER (solver
), 0);
1137 if (solver
->starttime
< 0)
1140 endtime
= (solver
->endtime
< 0)
1144 return endtime
- solver
->starttime
;
1148 gnm_solver_check_timeout (GnmSolver
*solver
)
1150 GnmSolverParameters
*sp
;
1152 g_return_val_if_fail (GNM_IS_SOLVER (solver
), FALSE
);
1154 sp
= solver
->params
;
1156 if (solver
->status
!= GNM_SOLVER_STATUS_RUNNING
)
1159 if (gnm_solver_elapsed (solver
) <= sp
->options
.max_time_sec
)
1162 gnm_solver_stop (solver
, NULL
);
1163 gnm_solver_set_reason (solver
, _("Timeout"));
1169 gnm_solver_store_result (GnmSolver
*sol
)
1171 gnm_float
const *solution
;
1172 unsigned ui
, n
= sol
->input_cells
->len
;
1174 g_return_if_fail (GNM_IS_SOLVER (sol
));
1175 g_return_if_fail (sol
->result
!= NULL
);
1176 g_return_if_fail (sol
->result
->solution
);
1178 solution
= gnm_solver_has_solution (sol
)
1179 ? sol
->result
->solution
1182 for (ui
= 0; ui
< n
; ui
++) {
1183 GnmCell
*cell
= g_ptr_array_index (sol
->input_cells
, ui
);
1184 GnmValue
*v
= solution
? value_new_float (solution
[ui
]) : value_new_error_NA (NULL
);
1185 gnm_cell_set_value (cell
, v
);
1186 cell_queue_recalc (cell
);
1191 gnm_solver_finished (GnmSolver
*sol
)
1193 g_return_val_if_fail (GNM_IS_SOLVER (sol
), TRUE
);
1195 switch (sol
->status
) {
1197 case GNM_SOLVER_STATUS_READY
:
1198 case GNM_SOLVER_STATUS_PREPARING
:
1199 case GNM_SOLVER_STATUS_PREPARED
:
1200 case GNM_SOLVER_STATUS_RUNNING
:
1202 case GNM_SOLVER_STATUS_DONE
:
1204 case GNM_SOLVER_STATUS_ERROR
:
1205 case GNM_SOLVER_STATUS_CANCELLED
:
1211 gnm_solver_set_status (GnmSolver
*solver
, GnmSolverStatus status
)
1213 GnmSolverStatus old_status
;
1215 g_return_if_fail (GNM_IS_SOLVER (solver
));
1217 if (status
== solver
->status
)
1220 gnm_solver_set_reason (solver
, NULL
);
1222 old_status
= solver
->status
;
1223 solver
->status
= status
;
1224 g_object_notify (G_OBJECT (solver
), "status");
1226 if (status
== GNM_SOLVER_STATUS_RUNNING
)
1227 g_object_set (G_OBJECT (solver
),
1228 "starttime", current_time (),
1229 "endtime", (double)-1,
1231 else if (old_status
== GNM_SOLVER_STATUS_RUNNING
)
1232 g_object_set (G_OBJECT (solver
),
1233 "endtime", current_time (),
1238 gnm_solver_set_reason (GnmSolver
*solver
, const char *reason
)
1240 g_return_if_fail (GNM_IS_SOLVER (solver
));
1242 if (g_strcmp0 (reason
, solver
->reason
) == 0)
1245 g_free (solver
->reason
);
1246 solver
->reason
= g_strdup (reason
);
1248 g_object_notify (G_OBJECT (solver
), "reason");
1253 gnm_solver_has_solution (GnmSolver
*solver
)
1255 if (solver
->result
== NULL
)
1258 switch (solver
->result
->quality
) {
1259 case GNM_SOLVER_RESULT_NONE
:
1260 case GNM_SOLVER_RESULT_INFEASIBLE
:
1261 case GNM_SOLVER_RESULT_UNBOUNDED
:
1264 case GNM_SOLVER_RESULT_FEASIBLE
:
1265 case GNM_SOLVER_RESULT_OPTIMAL
:
1271 gnm_solver_check_constraints (GnmSolver
*solver
)
1274 GnmSolverParameters
*sp
= solver
->params
;
1275 GnmCell
*target_cell
;
1277 if (sp
->options
.assume_non_negative
||
1278 sp
->options
.assume_discrete
) {
1282 for (ui
= 0; ui
< solver
->input_cells
->len
; ui
++) {
1283 GnmCell
*cell
= g_ptr_array_index (solver
->input_cells
, ui
);
1286 gnm_cell_eval (cell
);
1287 val
= value_get_as_float (cell
->value
);
1288 if (sp
->options
.assume_non_negative
&& val
< 0)
1290 if (sp
->options
.assume_discrete
&&
1291 val
!= gnm_floor (val
))
1294 bad
= (ui
< solver
->input_cells
->len
);
1300 for (l
= sp
->constraints
; l
; l
= l
->next
) {
1301 GnmSolverConstraint
*c
= l
->data
;
1307 gnm_solver_constraint_get_part (c
, sp
, i
,
1312 gnm_cell_eval (lhs
);
1313 cl
= value_get_as_float (lhs
->value
);
1316 gnm_cell_eval (rhs
);
1317 cr
= value_get_as_float (rhs
->value
);
1321 case GNM_SOLVER_INTEGER
:
1322 if (cl
== gnm_floor (cl
))
1325 case GNM_SOLVER_BOOLEAN
:
1326 if (cl
== 0 || cl
== 1)
1342 g_assert_not_reached ();
1348 target_cell
= gnm_solver_param_get_target_cell (sp
);
1349 gnm_cell_eval (target_cell
);
1350 if (!target_cell
|| !VALUE_IS_NUMBER (target_cell
->value
))
1357 gnm_solver_saveas (GnmSolver
*solver
, WorkbookControl
*wbc
,
1359 const char *templ
, char **filename
,
1365 GOIOContext
*io_context
;
1367 WorkbookView
*wbv
= wb_control_view (wbc
);
1369 fd
= g_file_open_tmp (templ
, filename
, err
);
1371 g_set_error (err
, G_FILE_ERROR
, 0,
1372 _("Failed to create file for linear program"));
1376 file
= fdopen (fd
, "wb");
1378 /* This shouldn't really happen. */
1380 g_set_error (err
, G_FILE_ERROR
, 0,
1381 _("Failed to create linear program file"));
1385 /* Give the saver a way to talk to the solver. */
1386 g_object_set_data_full (G_OBJECT (fs
),
1387 "solver", g_object_ref (solver
),
1388 (GDestroyNotify
)g_object_unref
);
1390 output
= gsf_output_stdio_new_FILE (*filename
, file
, TRUE
);
1391 io_context
= go_io_context_new (GO_CMD_CONTEXT (wbc
));
1392 workbook_view_save_to_output (wbv
, fs
, output
, io_context
);
1393 ok
= !go_io_error_occurred (io_context
);
1394 g_object_unref (io_context
);
1395 g_object_unref (output
);
1397 g_object_set_data (G_OBJECT (fs
), "solver", NULL
);
1400 g_set_error (err
, G_FILE_ERROR
, 0,
1401 _("Failed to save linear program"));
1409 gnm_solver_cell_index (GnmSolver
*solver
, GnmCell
const *cell
)
1413 if (g_hash_table_lookup_extended (solver
->index_from_cell
,
1416 return GPOINTER_TO_INT (idx
);
1422 cell_in_cr (GnmSolver
*sol
, GnmCell
const *cell
, gboolean follow
)
1429 idx
= gnm_solver_cell_index (sol
, cell
);
1430 if (idx
< 0 && follow
) {
1431 /* If the expression is just =X42 then look at X42 instead.
1432 This is because the mps loader uses such a level of
1433 indirection. Note: we follow only one such step. */
1434 GnmCellRef
const *cr
= gnm_expr_top_get_cellref (cell
->base
.texpr
);
1436 GnmCell
const *new_cell
;
1442 eval_pos_init_cell (&ep
, cell
);
1443 gnm_cellref_make_abs (&cr2
, cr
, &ep
);
1444 new_cell
= sheet_cell_get (eval_sheet (cr2
.sheet
, cell
->base
.sheet
),
1446 return cell_in_cr (sol
, new_cell
, FALSE
);
1453 cell_is_constant (GnmCell
*cell
, gnm_float
*pc
)
1458 if (cell
->base
.texpr
)
1461 gnm_cell_eval (cell
);
1462 *pc
= value_get_as_float (cell
->value
);
1466 #define SET_LOWER(l_) \
1468 sol->min[idx] = MAX (sol->min[idx], (l_)); \
1471 #define SET_UPPER(l_) \
1473 sol->max[idx] = MIN (sol->max[idx], (l_)); \
1479 gnm_solver_update_derived (GnmSolver
*sol
)
1481 GnmSolverParameters
*params
= sol
->params
;
1483 if (sol
->input_cells
) {
1484 g_ptr_array_free (sol
->input_cells
, TRUE
);
1485 sol
->input_cells
= NULL
;
1488 if (sol
->index_from_cell
) {
1489 g_hash_table_destroy (sol
->index_from_cell
);
1490 sol
->index_from_cell
= NULL
;
1500 g_free (sol
->discrete
);
1501 sol
->discrete
= NULL
;
1507 sol
->target
= gnm_solver_param_get_target_cell (params
);
1509 sol
->input_cells
= gnm_solver_param_get_input_cells (params
);
1511 n
= sol
->input_cells
->len
;
1512 sol
->index_from_cell
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1513 for (ui
= 0; ui
< n
; ui
++) {
1514 GnmCell
*cell
= g_ptr_array_index (sol
->input_cells
, ui
);
1515 g_hash_table_insert (sol
->index_from_cell
, cell
, GUINT_TO_POINTER (ui
));
1518 sol
->min
= g_new (gnm_float
, n
);
1519 sol
->max
= g_new (gnm_float
, n
);
1520 sol
->discrete
= g_new (guint8
, n
);
1521 for (ui
= 0; ui
< n
; ui
++) {
1522 sol
->min
[ui
] = params
->options
.assume_non_negative
? 0 : gnm_ninf
;
1523 sol
->max
[ui
] = gnm_pinf
;
1524 sol
->discrete
[ui
] = params
->options
.assume_discrete
;
1527 for (l
= params
->constraints
; l
; l
= l
->next
) {
1528 GnmSolverConstraint
*c
= l
->data
;
1534 gnm_solver_constraint_get_part (c
, params
, i
,
1538 int idx
= cell_in_cr (sol
, lhs
, TRUE
);
1542 if (!cell_is_constant (rhs
, &cr
))
1546 case GNM_SOLVER_INTEGER
:
1547 sol
->discrete
[idx
] = TRUE
;
1549 case GNM_SOLVER_BOOLEAN
:
1550 sol
->discrete
[idx
] = TRUE
;
1566 g_assert_not_reached ();
1573 * If parameters are discrete, narrow the range by eliminating
1574 * the fractional part of the limits.
1576 for (ui
= 0; ui
< n
; ui
++) {
1577 if (sol
->discrete
[ui
]) {
1578 sol
->min
[ui
] = gnm_ceil (sol
->min
[ui
]);
1579 sol
->max
[ui
] = gnm_floor (sol
->max
[ui
]);
1589 #define ADD_HEADER(txt_) do { \
1590 dao_set_bold (dao, 0, R, 0, R); \
1591 dao_set_cell (dao, 0, R, (txt_)); \
1595 #define AT_LIMIT(s_,l_) \
1596 (gnm_finite (l_) ? gnm_abs ((s_) - (l_)) <= (gnm_abs ((s_)) + gnm_abs ((l_))) / 1e10 : (s_) == (l_))
1598 #define MARK_BAD(col_) \
1601 dao_set_colors (dao, c, R, c, R, \
1602 gnm_color_new_rgb8 (255, 0, 0), \
1608 add_value_or_special (data_analysis_output_t
*dao
, int col
, int row
,
1612 dao_set_cell_float (dao
, col
, row
, x
);
1614 dao_set_cell (dao
, col
, row
, "-");
1615 dao_set_align (dao
, col
, row
, col
, row
,
1616 GNM_HALIGN_CENTER
, GNM_VALIGN_TOP
);
1621 print_vector (const char *name
, const gnm_float
*v
, int n
)
1626 g_printerr ("%s:\n", name
);
1627 for (i
= 0; i
< n
; i
++)
1628 g_printerr ("%15.8" GNM_FORMAT_f
" ", v
[i
]);
1633 gnm_solver_create_program_report (GnmSolver
*solver
, const char *name
)
1635 GnmSolverParameters
*params
= solver
->params
;
1637 data_analysis_output_t
*dao
;
1640 dao
= dao_init_new_sheet (NULL
);
1641 dao
->sheet
= params
->sheet
;
1642 dao_prepare_output (NULL
, dao
, name
);
1644 /* ---------------------------------------- */
1649 ADD_HEADER (_("Target"));
1650 dao_set_cell (dao
, 1, R
, _("Cell"));
1651 dao_set_cell (dao
, 2, R
, _("Value"));
1652 dao_set_cell (dao
, 3, R
, _("Type"));
1653 dao_set_cell (dao
, 4, R
, _("Status"));
1656 tmp
= gnm_solver_cell_name
1657 (gnm_solver_param_get_target_cell (params
),
1659 dao_set_cell (dao
, 1, R
, tmp
);
1662 dao_set_cell_float (dao
, 2, R
, solver
->result
->value
);
1664 switch (params
->problem_type
) {
1665 case GNM_SOLVER_MINIMIZE
:
1666 dao_set_cell (dao
, 3, R
, _("Minimize"));
1668 case GNM_SOLVER_MAXIMIZE
:
1669 dao_set_cell (dao
, 3, R
, _("Maximize"));
1673 switch (solver
->result
->quality
) {
1676 case GNM_SOLVER_RESULT_FEASIBLE
:
1677 dao_set_cell (dao
, 4, R
, _("Feasible"));
1679 case GNM_SOLVER_RESULT_OPTIMAL
:
1680 dao_set_cell (dao
, 4, R
, _("Optimal"));
1688 /* ---------------------------------------- */
1690 if (solver
->input_cells
->len
> 0) {
1693 ADD_HEADER (_("Variables"));
1695 dao_set_cell (dao
, 1, R
, _("Cell"));
1696 dao_set_cell (dao
, 2, R
, _("Value"));
1697 dao_set_cell (dao
, 3, R
, _("Lower"));
1698 dao_set_cell (dao
, 4, R
, _("Upper"));
1699 dao_set_cell (dao
, 5, R
, _("Slack"));
1702 for (ui
= 0; ui
< solver
->input_cells
->len
; ui
++) {
1703 GnmCell
*cell
= g_ptr_array_index (solver
->input_cells
, ui
);
1704 gnm_float L
= solver
->min
[ui
];
1705 gnm_float H
= solver
->max
[ui
];
1706 gnm_float s
= solver
->result
->solution
[ui
];
1707 gnm_float slack
= MIN (s
- L
, H
- s
);
1709 char *cname
= gnm_solver_cell_name (cell
, params
->sheet
);
1710 dao_set_cell (dao
, 1, R
, cname
);
1712 dao_set_cell_value (dao
, 2, R
, value_new_float (s
));
1713 add_value_or_special (dao
, 3, R
, L
);
1714 add_value_or_special (dao
, 4, R
, H
);
1716 add_value_or_special (dao
, 5, R
, slack
);
1720 if (AT_LIMIT (s
, L
) || AT_LIMIT (s
, H
))
1721 dao_set_cell (dao
, 6, R
, _("At limit"));
1723 if (s
< L
|| s
> H
) {
1724 dao_set_cell (dao
, 7, R
, _("Outside bounds"));
1734 /* ---------------------------------------- */
1736 ADD_HEADER (_("Constraints"));
1738 if (params
->constraints
) {
1739 dao_set_cell (dao
, 1, R
, _("Condition"));
1740 dao_set_cell (dao
, 2, R
, _("Value"));
1741 dao_set_cell (dao
, 3, R
, _("Limit"));
1742 dao_set_cell (dao
, 4, R
, _("Slack"));
1744 dao_set_cell (dao
, 1, R
, _("No constraints"));
1748 for (l
= params
->constraints
; l
; l
= l
->next
) {
1749 GnmSolverConstraint
*c
= l
->data
;
1755 gnm_solver_constraint_get_part (c
, params
, i
,
1759 gnm_float slack
= 0;
1760 char *ctxt
= gnm_solver_constraint_part_as_str (c
, i
, params
);
1761 dao_set_cell (dao
, 1, R
, ctxt
);
1765 gnm_cell_eval (lhs
);
1766 cl
= value_get_as_float (lhs
->value
);
1769 gnm_cell_eval (rhs
);
1770 cr
= value_get_as_float (rhs
->value
);
1774 case GNM_SOLVER_INTEGER
: {
1775 gnm_float c
= gnm_fake_round (cl
);
1776 slack
= 0 - gnm_abs (c
- cl
);
1779 case GNM_SOLVER_BOOLEAN
: {
1780 gnm_float c
= (cl
> 0.5 ? 1 : 0);
1781 slack
= 0 - gnm_abs (c
- cl
);
1791 slack
= 0 - gnm_abs (cl
- cr
);
1794 g_assert_not_reached ();
1797 add_value_or_special (dao
, 2, R
, cl
);
1799 add_value_or_special (dao
, 3, R
, cr
);
1801 add_value_or_special (dao
, 4, R
, slack
);
1809 /* ---------------------------------------- */
1811 dao_autofit_columns (dao
);
1812 dao_redraw_respan (dao
);
1818 gnm_solver_create_sensitivity_report (GnmSolver
*solver
, const char *name
)
1820 GnmSolverParameters
*params
= solver
->params
;
1821 GnmSolverSensitivity
*sols
= solver
->sensitivity
;
1823 data_analysis_output_t
*dao
;
1829 dao
= dao_init_new_sheet (NULL
);
1830 dao
->sheet
= params
->sheet
;
1831 dao_prepare_output (NULL
, dao
, name
);
1833 /* ---------------------------------------- */
1835 if (solver
->input_cells
->len
> 0) {
1838 ADD_HEADER (_("Variables"));
1840 dao_set_cell (dao
, 1, R
, _("Cell"));
1841 dao_set_cell (dao
, 2, R
, _("Final\nValue"));
1842 dao_set_cell (dao
, 3, R
, _("Reduced\nCost"));
1843 dao_set_cell (dao
, 4, R
, _("Lower\nLimit"));
1844 dao_set_cell (dao
, 5, R
, _("Upper\nLimit"));
1845 dao_set_align (dao
, 1, R
, 5, R
, GNM_HALIGN_CENTER
, GNM_VALIGN_BOTTOM
);
1846 dao_autofit_these_rows (dao
, R
, R
);
1849 for (ui
= 0; ui
< solver
->input_cells
->len
; ui
++) {
1850 GnmCell
*cell
= g_ptr_array_index (solver
->input_cells
, ui
);
1851 gnm_float L
= sols
->vars
[ui
].low
;
1852 gnm_float H
= sols
->vars
[ui
].high
;
1853 gnm_float red
= sols
->vars
[ui
].reduced_cost
;
1854 gnm_float s
= solver
->result
->solution
[ui
];
1856 char *cname
= gnm_solver_cell_name (cell
, params
->sheet
);
1857 dao_set_cell (dao
, 1, R
, cname
);
1859 dao_set_cell_value (dao
, 2, R
, value_new_float (s
));
1860 add_value_or_special (dao
, 3, R
, red
);
1861 add_value_or_special (dao
, 4, R
, L
);
1862 add_value_or_special (dao
, 5, R
, H
);
1870 /* ---------------------------------------- */
1872 ADD_HEADER (_("Constraints"));
1874 if (params
->constraints
) {
1875 dao_set_cell (dao
, 1, R
, _("Constraint"));
1876 dao_set_cell (dao
, 2, R
, _("Shadow\nPrice"));
1877 dao_set_cell (dao
, 3, R
, _("Constraint\nLHS"));
1878 dao_set_cell (dao
, 4, R
, _("Constraint\nRHS"));
1879 dao_set_cell (dao
, 5, R
, _("Lower\nLimit"));
1880 dao_set_cell (dao
, 6, R
, _("Upper\nLimit"));
1881 dao_set_align (dao
, 1, R
, 6, R
, GNM_HALIGN_CENTER
, GNM_VALIGN_BOTTOM
);
1882 dao_autofit_these_rows (dao
, R
, R
);
1884 dao_set_cell (dao
, 1, R
, _("No constraints"));
1888 for (l
= params
->constraints
; l
; l
= l
->next
) {
1889 GnmSolverConstraint
*c
= l
->data
;
1895 gnm_solver_constraint_get_part (c
, params
, i
,
1902 case GNM_SOLVER_INTEGER
:
1903 case GNM_SOLVER_BOOLEAN
:
1909 ctxt
= gnm_solver_constraint_part_as_str (c
, i
, params
);
1910 dao_set_cell (dao
, 1, R
, ctxt
);
1914 gnm_cell_eval (lhs
);
1915 cl
= value_get_as_float (lhs
->value
);
1918 gnm_cell_eval (rhs
);
1919 cr
= value_get_as_float (rhs
->value
);
1922 add_value_or_special (dao
, 2, R
, sols
->constraints
[cidx
].shadow_price
);
1923 add_value_or_special (dao
, 3, R
, cl
);
1924 add_value_or_special (dao
, 4, R
, cr
);
1925 add_value_or_special (dao
, 5, R
, sols
->constraints
[cidx
].low
);
1926 add_value_or_special (dao
, 6, R
, sols
->constraints
[cidx
].high
);
1932 /* ---------------------------------------- */
1934 dao_autofit_columns (dao
);
1935 dao_redraw_respan (dao
);
1941 gnm_solver_create_report (GnmSolver
*solver
, const char *base
)
1943 GnmSolverParameters
*params
= solver
->params
;
1945 if (params
->options
.program_report
) {
1946 char *name
= g_strdup_printf (base
, _("Program"));
1947 gnm_solver_create_program_report (solver
, name
);
1951 if (params
->options
.sensitivity_report
) {
1952 char *name
= g_strdup_printf (base
, _("Sensitivity"));
1953 gnm_solver_create_sensitivity_report (solver
, name
);
1963 * gnm_solver_get_target_value:
1966 * Returns: the current value of the target cell, possibly with the sign
1970 gnm_solver_get_target_value (GnmSolver
*solver
)
1974 gnm_cell_eval (solver
->target
);
1975 v
= solver
->target
->value
;
1977 if (VALUE_IS_NUMBER (v
) || VALUE_IS_EMPTY (v
)) {
1978 gnm_float y
= value_get_as_float (v
);
1979 return solver
->flip_sign
? 0 - y
: y
;
1985 gnm_solver_set_var (GnmSolver
*sol
, int i
, gnm_float x
)
1987 GnmCell
*cell
= g_ptr_array_index (sol
->input_cells
, i
);
1990 VALUE_IS_FLOAT (cell
->value
) &&
1991 value_get_as_float (cell
->value
) == x
)
1994 gnm_cell_set_value (cell
, value_new_float (x
));
1995 cell_queue_recalc (cell
);
1999 gnm_solver_set_vars (GnmSolver
*sol
, gnm_float
const *xs
)
2001 const int n
= sol
->input_cells
->len
;
2004 for (i
= 0; i
< n
; i
++)
2005 gnm_solver_set_var (sol
, i
, xs
[i
]);
2009 * gnm_solver_save_vars:
2012 * Returns: (transfer full) (element-type GnmValue):
2015 gnm_solver_save_vars (GnmSolver
*sol
)
2017 GPtrArray
*vals
= g_ptr_array_new ();
2020 for (ui
= 0; ui
< sol
->input_cells
->len
; ui
++) {
2021 GnmCell
*cell
= g_ptr_array_index (sol
->input_cells
, ui
);
2022 g_ptr_array_add (vals
, value_dup (cell
->value
));
2029 * gnm_solver_restore_vars:
2031 * @vals: (transfer full) (element-type GnmValue): values to restore
2034 gnm_solver_restore_vars (GnmSolver
*sol
, GPtrArray
*vals
)
2038 for (ui
= 0; ui
< sol
->input_cells
->len
; ui
++) {
2039 GnmCell
*cell
= g_ptr_array_index (sol
->input_cells
, ui
);
2040 gnm_cell_set_value (cell
, g_ptr_array_index (vals
, ui
));
2041 cell_queue_recalc (cell
);
2044 g_ptr_array_free (vals
, TRUE
);
2048 * gnm_solver_has_analytic_gradient:
2051 * Returns: %TRUE if the gradient can be computed analytically.
2054 gnm_solver_has_analytic_gradient (GnmSolver
*sol
)
2056 const int n
= sol
->input_cells
->len
;
2058 if (sol
->gradient_status
== 0) {
2061 sol
->gradient_status
++;
2063 sol
->gradient
= g_ptr_array_new_with_free_func ((GDestroyNotify
)gnm_expr_top_unref
);
2064 for (i
= 0; i
< n
; i
++) {
2065 GnmCell
*cell
= g_ptr_array_index (sol
->input_cells
, i
);
2066 GnmExprTop
const *te
=
2067 gnm_expr_cell_deriv (sol
->target
, cell
);
2069 g_ptr_array_add (sol
->gradient
, (gpointer
)te
);
2071 if (gnm_solver_debug ())
2072 g_printerr ("Unable to compute analytic gradient\n");
2073 sol
->gradient_status
++;
2079 return sol
->gradient_status
== 1;
2083 gnm_solver_compute_gradient_analytically (GnmSolver
*sol
, gnm_float
const *xs
)
2085 const int n
= sol
->input_cells
->len
;
2087 gnm_float
*g
= g_new (gnm_float
, n
);
2090 eval_pos_init_cell (&ep
, sol
->target
);
2091 for (i
= 0; i
< n
; i
++) {
2092 GnmExprTop
const *te
= g_ptr_array_index (sol
->gradient
, i
);
2093 GnmValue
*v
= gnm_expr_top_eval
2094 (te
, &ep
, GNM_EXPR_EVAL_SCALAR_NON_EMPTY
);
2095 g
[i
] = VALUE_IS_NUMBER (v
) ? value_get_as_float (v
) : gnm_nan
;
2101 if (gnm_solver_debug ())
2102 print_vector ("Analytic gradient", g
, n
);
2108 * gnm_solver_compute_gradient: (skip)
2110 * @xs: Point to compute gradient at
2112 * Returns: xxx(transfer full): A vector containing the gradient. This
2113 * function takes the flip-sign property into account. This will use
2114 * analytic gradient, if possible, and a numerical approximation otherwise.
2117 gnm_solver_compute_gradient (GnmSolver
*sol
, gnm_float
const *xs
)
2121 const int n
= sol
->input_cells
->len
;
2123 const int order
= sol
->params
->options
.gradient_order
;
2125 gnm_solver_set_vars (sol
, xs
);
2126 y0
= gnm_solver_get_target_value (sol
);
2128 if (gnm_solver_has_analytic_gradient (sol
))
2129 return gnm_solver_compute_gradient_analytically (sol
, xs
);
2131 g
= g_new (gnm_float
, n
);
2132 for (i
= 0; i
< n
; i
++) {
2133 gnm_float x0
= xs
[i
];
2138 * This computes the least-squares fit of an affine function
2139 * based on 2*order equidistant points symmetrically around
2140 * the value we compute the derivative for.
2142 * We use an even number of ULPs for the step size. This
2143 * ensures that the x values are computed without rounding
2144 * error except, potentially, a single step that crosses an
2145 * integer power of 2.
2147 dx
= 16 * (go_add_epsilon (x0
) - x0
);
2149 for (j
= -order
; j
<= order
; j
++) {
2155 gnm_solver_set_var (sol
, i
, x0
+ j
* dx
);
2156 y
= gnm_solver_get_target_value (sol
);
2159 dy
/= 2 * (order
* (2 * order
* order
+ 3 * order
+ 1) / 6);
2162 gnm_solver_set_var (sol
, i
, x0
);
2165 if (gnm_solver_debug ())
2166 print_vector ("Numerical gradient", g
, n
);
2172 * gnm_solver_has_analytic_hessian:
2175 * Returns: %TRUE if the Hessian can be computed analytically.
2178 gnm_solver_has_analytic_hessian (GnmSolver
*sol
)
2180 const int n
= sol
->input_cells
->len
;
2185 if (!gnm_solver_has_analytic_gradient (sol
))
2186 sol
->hessian_status
= sol
->gradient_status
;
2188 if (sol
->hessian_status
)
2189 return sol
->hessian_status
== 1;
2191 sol
->hessian_status
++;
2192 sol
->hessian
= g_ptr_array_new_with_free_func ((GDestroyNotify
)gnm_expr_top_unref
);
2194 eval_pos_init_cell (&ep
, sol
->target
);
2195 info
= gnm_expr_deriv_info_new ();
2196 for (i
= 0; i
< n
&& sol
->hessian_status
== 1; i
++) {
2197 GnmExprTop
const *gi
= g_ptr_array_index (sol
->gradient
, i
);
2198 for (j
= i
; j
< n
; j
++) {
2200 GnmExprTop
const *te
;
2203 cell
= g_ptr_array_index (sol
->input_cells
, j
);
2204 eval_pos_init_cell (&var
, cell
);
2205 gnm_expr_deriv_info_set_var (info
, &var
);
2206 te
= gnm_expr_top_deriv (gi
, &ep
, info
);
2209 g_ptr_array_add (sol
->hessian
, (gpointer
)te
);
2211 if (gnm_solver_debug ())
2212 g_printerr ("Unable to compute analytic hessian\n");
2213 sol
->hessian_status
++;
2219 gnm_expr_deriv_info_free (info
);
2221 return sol
->hessian_status
== 1;
2225 * gnm_solver_compute_hessian: (skip)
2227 * @xs: Point to compute Hessian at
2229 * Returns: (transfer full): A vector containing the Hessian. This
2230 * function takes the flip-sign property into account. The result vector
2231 * will be n+(n-1)+...+2+1 elements long containing the triangular
2232 * Hessian. Use symmetry to obtain the full Hessian.
2235 gnm_solver_compute_hessian (GnmSolver
*sol
, gnm_float
const *xs
)
2240 int const n
= sol
->input_cells
->len
;
2242 if (!gnm_solver_has_analytic_hessian (sol
))
2245 gnm_solver_set_vars (sol
, xs
);
2247 H
= gnm_matrix_new (n
, n
);
2248 eval_pos_init_cell (&ep
, sol
->target
);
2249 for (i
= k
= 0; i
< n
; i
++) {
2250 for (j
= i
; j
< n
; j
++, k
++) {
2251 GnmExprTop
const *te
=
2252 g_ptr_array_index (sol
->hessian
, k
);
2253 GnmValue
*v
= gnm_expr_top_eval
2254 (te
, &ep
, GNM_EXPR_EVAL_SCALAR_NON_EMPTY
);
2255 gnm_float x
= VALUE_IS_NUMBER (v
)
2256 ? value_get_as_float (v
)
2272 try_step (GnmSolver
*sol
, gnm_float
const *x0
, gnm_float
const *dir
, gnm_float step
)
2274 int const n
= sol
->input_cells
->len
;
2275 gnm_float
*x
= g_new (gnm_float
, n
);
2279 for (i
= 0; i
< n
; i
++)
2280 x
[i
] = x0
[i
] + step
* dir
[i
];
2281 gnm_solver_set_vars (sol
, x
);
2282 y
= gnm_solver_get_target_value (sol
);
2288 * gnm_solver_line_search:
2290 * @x0: Starting point
2292 * @try_reverse: whether to try reverse direction at all
2293 * @step: initial step size
2294 * @max_step: largest allowed step
2295 * @eps: tolerance for optimal step
2296 * @py: (out): location to store resulting objective function value
2298 * Returns: optimal step size.
2301 gnm_solver_line_search (GnmSolver
*sol
,
2302 gnm_float
const *x0
, gnm_float
const *dir
,
2303 gboolean try_reverse
,
2304 gnm_float step
, gnm_float max_step
, gnm_float eps
,
2309 * 1: Found improvement, but not far side of it
2310 * 2: Have (s0,s1,s2) with s1 lowest
2313 gnm_float s
, s0
, s1
, s2
;
2314 gnm_float y
, y0
, y1
, y2
;
2315 gnm_float
const phi
= (gnm_sqrt (5) + 1) / 2;
2317 gboolean debug
= gnm_solver_debug ();
2319 g_return_val_if_fail (eps
>= 0, gnm_nan
);
2320 g_return_val_if_fail (step
> 0, gnm_nan
);
2321 g_return_val_if_fail (max_step
>= step
, gnm_nan
);
2324 g_printerr ("LS: step=%" GNM_FORMAT_g
", max=%" GNM_FORMAT_g
", eps=%" GNM_FORMAT_g
"\n", step
, max_step
, eps
);
2325 print_vector (NULL
, dir
, sol
->input_cells
->len
);
2328 gnm_solver_set_vars (sol
, x0
);
2329 y0
= gnm_solver_get_target_value (sol
);
2333 while (phase
== 0) {
2334 gboolean flat
= TRUE
;
2336 y
= try_step (sol
, x0
, dir
, s
);
2338 g_printerr ("LS0: s:%.6" GNM_FORMAT_g
" y:%.6" GNM_FORMAT_g
"\n",
2340 if (y
< y0
&& gnm_solver_check_constraints (sol
)) {
2349 y
= try_step (sol
, x0
, dir
, -s
);
2351 g_printerr ("LS0: s:%.6" GNM_FORMAT_g
" y:%.6" GNM_FORMAT_g
"\n",
2353 if (y
< y0
&& gnm_solver_check_constraints (sol
)) {
2368 while (phase
== 1) {
2371 if (gnm_abs (s
) >= max_step
)
2374 y
= try_step (sol
, x0
, dir
, s
);
2375 if (!gnm_finite (y
) || !gnm_solver_check_constraints (sol
))
2390 * Phase 2: we have three steps, s0/s1/s2, in order (descending or ascending) such
2392 * 1. y1<=y0 (equality unlikely)
2393 * 2. y1<=y2 (equality unlikely)
2394 * 3a. (s2-s1)=phi*(s1-s0) or
2395 * 3b. (s2-s1)*phi=(s1-s0)
2397 rbig
= TRUE
; /* Initially 3a holds. */
2398 while (phase
== 2) {
2400 gnm_float s01
= s1
- s0
;
2401 gnm_float s12
= s2
- s1
;
2402 g_printerr ("LS2: s0:%.6" GNM_FORMAT_g
" s01=%.6" GNM_FORMAT_g
" s12=%.6" GNM_FORMAT_g
2404 " y:%.10" GNM_FORMAT_g
"/%.10" GNM_FORMAT_g
"/%.10" GNM_FORMAT_g
"\n",
2405 s0
, s01
, s12
, s12
/ s01
, y0
, y1
, y2
);
2408 s
= rbig
? s1
+ (s1
- s0
) * (phi
- 1) : s1
- (s2
- s1
) * (phi
- 1);
2409 if (s
<= s0
|| s
>= s2
|| gnm_abs (s
- s1
) <= eps
)
2412 y
= try_step (sol
, x0
, dir
, s
);
2413 if (!gnm_finite (y
) || !gnm_solver_check_constraints (sol
))
2436 if (y0
== y1
&& y1
== y2
)
2443 g_printerr ("LS: step %.6" GNM_FORMAT_g
"\n", s1
);
2450 * gnm_solver_pick_lp_coords:
2452 * @px1: (out): first coordinate value
2453 * @px2: (out): second coordinate value
2455 * Pick two good values for each coordinate. We prefer 0 and 1
2456 * when they are valid.
2459 gnm_solver_pick_lp_coords (GnmSolver
*sol
,
2460 gnm_float
**px1
, gnm_float
**px2
)
2462 const unsigned n
= sol
->input_cells
->len
;
2463 gnm_float
*x1
= *px1
= g_new (gnm_float
, n
);
2464 gnm_float
*x2
= *px2
= g_new (gnm_float
, n
);
2467 for (ui
= 0; ui
< n
; ui
++) {
2468 const gnm_float L
= sol
->min
[ui
], H
= sol
->max
[ui
];
2471 x1
[ui
] = x2
[ui
] = L
;
2472 } else if (sol
->discrete
[ui
] && H
- L
== 1) {
2476 if (L
<= 0 && H
>= 0)
2478 else if (gnm_finite (L
))
2483 if (x1
[ui
] + 1 <= H
)
2484 x2
[ui
] = x1
[ui
] + 1;
2485 else if (x1
[ui
] - 1 >= H
)
2486 x2
[ui
] = x1
[ui
] - 1;
2487 else if (x1
[ui
] != H
)
2488 x2
[ui
] = (x1
[ui
] + H
) / 2;
2490 x2
[ui
] = (x1
[ui
] + L
) / 2;
2496 * gnm_solver_get_lp_coeffs: (skip)
2498 * @ycell: Cell for which to compute coefficients
2499 * @x1: first coordinate value
2500 * @x2: second coordinate value
2501 * @err: error location
2503 * Returns: xxx(transfer full) (nullable): coordinates, or %NULL in case of error.
2504 * Note: this function is not affected by the flip-sign property, even
2505 * if @ycell happens to coindice with the solver target cell.
2508 gnm_solver_get_lp_coeffs (GnmSolver
*sol
, GnmCell
*ycell
,
2509 gnm_float
const *x1
, gnm_float
const *x2
,
2512 const unsigned n
= sol
->input_cells
->len
;
2514 gnm_float
*res
= g_new (gnm_float
, n
);
2517 gnm_solver_set_vars (sol
, x1
);
2518 gnm_cell_eval (ycell
);
2519 y0
= VALUE_IS_NUMBER (ycell
->value
) ? value_get_as_float (ycell
->value
) : gnm_nan
;
2520 if (!gnm_finite (y0
))
2523 for (ui
= 0; ui
< n
; ui
++) {
2524 gnm_float dx
= x2
[ui
] - x1
[ui
], dy
, y1
;
2531 gnm_solver_set_var (sol
, ui
, x2
[ui
]);
2532 gnm_cell_eval (ycell
);
2533 y1
= VALUE_IS_NUMBER (ycell
->value
) ? value_get_as_float (ycell
->value
) : gnm_nan
;
2537 if (!gnm_finite (res
[ui
]))
2540 if (!sol
->discrete
[ui
] || dx
!= 1) {
2541 gnm_float x01
, y01
, e
, emax
;
2543 x01
= (x1
[ui
] + x2
[ui
]) / 2;
2544 if (sol
->discrete
[ui
]) x01
= gnm_floor (x01
);
2545 gnm_solver_set_var (sol
, ui
, x01
);
2546 gnm_cell_eval (ycell
);
2547 y01
= VALUE_IS_NUMBER (ycell
->value
) ? value_get_as_float (ycell
->value
) : gnm_nan
;
2548 if (!gnm_finite (y01
))
2551 emax
= dy
== 0 ? 1e-10 : gnm_abs (dy
) / 1e-10;
2552 e
= dy
- 2 * (y01
- y0
);
2553 if (gnm_abs (e
) > emax
)
2557 gnm_solver_set_var (sol
, ui
, x1
[ui
]);
2564 go_error_invalid (),
2566 _("Target cell did not evaluate to a number."));
2572 go_error_invalid (),
2574 _("Target cell does not appear to depend linearly on input cells."));
2581 gnm_solver_class_init (GObjectClass
*object_class
)
2583 gnm_solver_parent_class
= g_type_class_peek_parent (object_class
);
2585 object_class
->dispose
= gnm_solver_dispose
;
2586 object_class
->set_property
= gnm_solver_set_property
;
2587 object_class
->get_property
= gnm_solver_get_property
;
2589 g_object_class_install_property (object_class
, SOL_PROP_STATUS
,
2590 g_param_spec_enum ("status",
2592 P_("The solver's current status"),
2593 GNM_SOLVER_STATUS_TYPE
,
2594 GNM_SOLVER_STATUS_READY
,
2596 G_PARAM_READWRITE
));
2598 g_object_class_install_property (object_class
, SOL_PROP_REASON
,
2599 g_param_spec_string ("reason",
2601 P_("The reason behind the solver's status"),
2604 G_PARAM_READWRITE
));
2606 g_object_class_install_property (object_class
, SOL_PROP_PARAMS
,
2607 g_param_spec_object ("params",
2609 P_("Solver parameters"),
2610 GNM_SOLVER_PARAMETERS_TYPE
,
2612 G_PARAM_CONSTRUCT_ONLY
|
2613 G_PARAM_READWRITE
));
2615 g_object_class_install_property (object_class
, SOL_PROP_RESULT
,
2616 g_param_spec_object ("result",
2618 P_("Current best feasible result"),
2619 GNM_SOLVER_RESULT_TYPE
,
2621 G_PARAM_READWRITE
));
2623 g_object_class_install_property (object_class
, SOL_PROP_SENSITIVITY
,
2624 g_param_spec_object ("sensitivity",
2626 P_("Sensitivity results"),
2627 GNM_SOLVER_SENSITIVITY_TYPE
,
2629 G_PARAM_READWRITE
));
2631 g_object_class_install_property (object_class
, SOL_PROP_STARTTIME
,
2632 g_param_spec_double ("starttime",
2634 P_("Time the solver was started"),
2637 G_PARAM_READWRITE
));
2639 g_object_class_install_property (object_class
, SOL_PROP_ENDTIME
,
2640 g_param_spec_double ("endtime",
2642 P_("Time the solver finished"),
2645 G_PARAM_READWRITE
));
2647 g_object_class_install_property (object_class
, SOL_PROP_FLIP_SIGN
,
2648 g_param_spec_boolean ("flip-sign",
2650 P_("Flip sign of target value"),
2652 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
2654 solver_signals
[SOL_SIG_PREPARE
] =
2655 g_signal_new ("prepare",
2656 G_OBJECT_CLASS_TYPE (object_class
),
2658 G_STRUCT_OFFSET (GnmSolverClass
, prepare
),
2660 gnm__BOOLEAN__OBJECT_POINTER
,
2665 solver_signals
[SOL_SIG_START
] =
2666 g_signal_new ("start",
2667 G_OBJECT_CLASS_TYPE (object_class
),
2669 G_STRUCT_OFFSET (GnmSolverClass
, start
),
2671 gnm__BOOLEAN__OBJECT_POINTER
,
2676 solver_signals
[SOL_SIG_STOP
] =
2677 g_signal_new ("stop",
2678 G_OBJECT_CLASS_TYPE (object_class
),
2680 G_STRUCT_OFFSET (GnmSolverClass
, stop
),
2682 gnm__BOOLEAN__POINTER
,
2687 GSF_CLASS (GnmSolver
, gnm_solver
,
2688 &gnm_solver_class_init
, NULL
, G_TYPE_OBJECT
)
2690 /* ------------------------------------------------------------------------- */
2692 static GObjectClass
*gnm_solver_result_parent_class
;
2695 gnm_solver_result_finalize (GObject
*obj
)
2697 GnmSolverResult
*r
= GNM_SOLVER_RESULT (obj
);
2698 g_free (r
->solution
);
2699 gnm_solver_result_parent_class
->finalize (obj
);
2703 gnm_solver_result_class_init (GObjectClass
*object_class
)
2705 gnm_solver_result_parent_class
=
2706 g_type_class_peek_parent (object_class
);
2708 object_class
->finalize
= gnm_solver_result_finalize
;
2711 GSF_CLASS (GnmSolverResult
, gnm_solver_result
,
2712 &gnm_solver_result_class_init
, NULL
, G_TYPE_OBJECT
)
2714 /* ------------------------------------------------------------------------- */
2716 static GObjectClass
*gnm_solver_sensitivity_parent_class
;
2724 gnm_solver_sensitivity_constructed (GObject
*obj
)
2726 GnmSolverSensitivity
*sols
= GNM_SOLVER_SENSITIVITY (obj
);
2727 GnmSolver
*sol
= sols
->solver
;
2728 GnmSolverParameters
*sp
= sol
->params
;
2729 const int n
= sol
->input_cells
->len
;
2733 /* Chain to parent first */
2734 gnm_solver_sensitivity_parent_class
->constructed (obj
);
2736 sols
->vars
= g_new (struct GnmSolverSensitivityVars_
, n
);
2737 for (i
= 0; i
< n
; i
++) {
2738 sols
->vars
[i
].low
= gnm_nan
;
2739 sols
->vars
[i
].high
= gnm_nan
;
2740 sols
->vars
[i
].reduced_cost
= gnm_nan
;
2744 for (l
= sp
->constraints
; l
; l
= l
->next
) {
2745 GnmSolverConstraint
*c
= l
->data
;
2751 gnm_solver_constraint_get_part (c
, sp
, i
,
2758 sols
->constraints
= g_new (struct GnmSolverSensitivityConstraints_
, cn
);
2759 for (i
= 0; i
< cn
; i
++) {
2760 sols
->constraints
[i
].low
= gnm_nan
;
2761 sols
->constraints
[i
].high
= gnm_nan
;
2762 sols
->constraints
[i
].shadow_price
= gnm_nan
;
2767 gnm_solver_sensitivity_finalize (GObject
*obj
)
2769 GnmSolverSensitivity
*r
= GNM_SOLVER_SENSITIVITY (obj
);
2771 g_free (r
->constraints
);
2772 gnm_solver_sensitivity_parent_class
->finalize (obj
);
2776 gnm_solver_sensitivity_get_property (GObject
*object
, guint property_id
,
2777 GValue
*value
, GParamSpec
*pspec
)
2779 GnmSolverSensitivity
*sols
= (GnmSolverSensitivity
*)object
;
2781 switch (property_id
) {
2782 case SOLS_PROP_SOLVER
:
2783 g_value_set_object (value
, sols
->solver
);
2787 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
2793 gnm_solver_sensitivity_set_property (GObject
*object
, guint property_id
,
2794 GValue
const *value
, GParamSpec
*pspec
)
2796 GnmSolverSensitivity
*sols
= (GnmSolverSensitivity
*)object
;
2798 switch (property_id
) {
2799 case SOLS_PROP_SOLVER
:
2800 /* We hold no ref. */
2801 sols
->solver
= g_value_get_object (value
);
2805 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
2811 gnm_solver_sensitivity_class_init (GObjectClass
*object_class
)
2813 gnm_solver_sensitivity_parent_class
=
2814 g_type_class_peek_parent (object_class
);
2816 object_class
->finalize
= gnm_solver_sensitivity_finalize
;
2817 object_class
->constructed
= gnm_solver_sensitivity_constructed
;
2818 object_class
->set_property
= gnm_solver_sensitivity_set_property
;
2819 object_class
->get_property
= gnm_solver_sensitivity_get_property
;
2821 g_object_class_install_property
2822 (object_class
, SOLS_PROP_SOLVER
,
2823 g_param_spec_object ("solver",
2828 G_PARAM_CONSTRUCT_ONLY
|
2829 G_PARAM_READWRITE
));
2832 GSF_CLASS (GnmSolverSensitivity
, gnm_solver_sensitivity
,
2833 gnm_solver_sensitivity_class_init
, NULL
, G_TYPE_OBJECT
)
2835 GnmSolverSensitivity
*
2836 gnm_solver_sensitivity_new (GnmSolver
*sol
)
2838 return g_object_new (GNM_SOLVER_SENSITIVITY_TYPE
, "solver", sol
, NULL
);
2841 /* ------------------------------------------------------------------------- */
2843 static GObjectClass
*gnm_sub_solver_parent_class
;
2846 SUB_SOL_SIG_CHILD_EXIT
,
2850 static guint sub_solver_signals
[SUB_SOL_SIG_LAST
] = { 0 };
2853 gnm_sub_solver_clear (GnmSubSolver
*subsol
)
2857 if (subsol
->child_watch
) {
2858 g_source_remove (subsol
->child_watch
);
2859 subsol
->child_watch
= 0;
2862 if (subsol
->child_pid
) {
2864 TerminateProcess (subsol
->child_pid
, 127);
2866 kill (subsol
->child_pid
, SIGKILL
);
2868 g_spawn_close_pid (subsol
->child_pid
);
2869 subsol
->child_pid
= (GPid
)0;
2872 for (i
= 0; i
<= 2; i
++) {
2873 if (subsol
->channel_watches
[i
]) {
2874 g_source_remove (subsol
->channel_watches
[i
]);
2875 subsol
->channel_watches
[i
] = 0;
2877 if (subsol
->channels
[i
]) {
2878 g_io_channel_unref (subsol
->channels
[i
]);
2879 subsol
->channels
[i
] = NULL
;
2881 if (subsol
->fd
[i
] != -1) {
2882 close (subsol
->fd
[i
]);
2887 if (subsol
->program_filename
) {
2888 g_unlink (subsol
->program_filename
);
2889 g_free (subsol
->program_filename
);
2890 subsol
->program_filename
= NULL
;
2893 if (subsol
->cell_from_name
)
2894 g_hash_table_remove_all (subsol
->cell_from_name
);
2896 if (subsol
->name_from_cell
)
2897 g_hash_table_remove_all (subsol
->name_from_cell
);
2899 if (subsol
->constraint_from_name
)
2900 g_hash_table_remove_all (subsol
->constraint_from_name
);
2904 gnm_sub_solver_dispose (GObject
*obj
)
2906 GnmSubSolver
*subsol
= GNM_SUB_SOLVER (obj
);
2908 gnm_sub_solver_clear (subsol
);
2910 gnm_sub_solver_parent_class
->dispose (obj
);
2914 gnm_sub_solver_finalize (GObject
*obj
)
2916 GnmSubSolver
*subsol
= GNM_SUB_SOLVER (obj
);
2919 * The weird finalization in gnm_lpsolve_final makes it important that
2920 * we leave the object in a state that gnm_sub_solver_clear is happy
2924 g_hash_table_destroy (subsol
->cell_from_name
);
2925 subsol
->cell_from_name
= NULL
;
2927 g_hash_table_destroy (subsol
->name_from_cell
);
2928 subsol
->name_from_cell
= NULL
;
2930 g_hash_table_destroy (subsol
->constraint_from_name
);
2931 subsol
->constraint_from_name
= NULL
;
2933 gnm_sub_solver_parent_class
->finalize (obj
);
2937 gnm_sub_solver_init (GnmSubSolver
*subsol
)
2941 for (i
= 0; i
<= 2; i
++)
2944 subsol
->cell_from_name
=
2945 g_hash_table_new_full (g_str_hash
, g_str_equal
,
2947 subsol
->name_from_cell
=
2948 g_hash_table_new (g_direct_hash
, g_direct_equal
);
2950 subsol
->constraint_from_name
=
2951 g_hash_table_new_full (g_str_hash
, g_str_equal
,
2956 cb_child_exit (G_GNUC_UNUSED GPid pid
, gint status
, GnmSubSolver
*subsol
)
2958 gboolean normal
= WIFEXITED (status
);
2961 subsol
->child_watch
= 0;
2964 code
= WEXITSTATUS (status
);
2965 if (gnm_solver_debug ())
2966 g_printerr ("Solver process exited with code %d\n",
2969 } else if (WIFSIGNALED (status
)) {
2970 code
= WTERMSIG (status
);
2971 if (gnm_solver_debug ())
2972 g_printerr ("Solver process received signal %d\n",
2977 g_printerr ("Solver process exited with status 0x%x\n",
2981 g_signal_emit (subsol
, sub_solver_signals
[SUB_SOL_SIG_CHILD_EXIT
], 0,
2984 if (subsol
->child_pid
) {
2985 g_spawn_close_pid (subsol
->child_pid
);
2986 subsol
->child_pid
= (GPid
)0;
2991 * gnm_sub_solver_spawn: (skip)
2994 gnm_sub_solver_spawn (GnmSubSolver
*subsol
,
2996 GSpawnChildSetupFunc child_setup
, gpointer setup_data
,
2997 GIOFunc io_stdout
, gpointer stdout_data
,
2998 GIOFunc io_stderr
, gpointer stderr_data
,
3001 GnmSolver
*sol
= GNM_SOLVER (subsol
);
3003 GSpawnFlags spflags
= G_SPAWN_DO_NOT_REAP_CHILD
;
3006 g_return_val_if_fail (subsol
->child_watch
== 0, FALSE
);
3007 g_return_val_if_fail (sol
->status
== GNM_SOLVER_STATUS_PREPARED
, FALSE
);
3009 if (!g_path_is_absolute (argv
[0]))
3010 spflags
|= G_SPAWN_SEARCH_PATH
;
3012 if (io_stdout
== NULL
&& !gnm_solver_debug ())
3013 spflags
|= G_SPAWN_STDOUT_TO_DEV_NULL
;
3015 if (gnm_solver_debug ()) {
3016 GString
*msg
= g_string_new ("Spawning");
3018 for (i
= 0; argv
[i
]; i
++) {
3019 g_string_append_c (msg
, ' ');
3020 g_string_append (msg
, argv
[i
]);
3022 g_printerr ("%s\n", msg
->str
);
3023 g_string_free (msg
, TRUE
);
3027 /* Hope for the best... */
3032 ok
= g_spawn_async_with_pipes
3033 (g_get_home_dir (), /* PWD */
3035 NULL
, /* environment */
3037 child_setup
, setup_data
,
3040 io_stdout
? &subsol
->fd
[1] : NULL
, /* stdout */
3041 io_stdout
? &subsol
->fd
[2] : NULL
, /* stderr */
3046 subsol
->child_watch
=
3047 g_child_watch_add (subsol
->child_pid
,
3048 (GChildWatchFunc
)cb_child_exit
, subsol
);
3050 subsol
->io_funcs
[1] = io_stdout
;
3051 subsol
->io_funcs_data
[1] = stdout_data
;
3052 subsol
->io_funcs
[2] = io_stderr
;
3053 subsol
->io_funcs_data
[2] = stderr_data
;
3055 for (fd
= 1; fd
<= 2; fd
++) {
3058 if (subsol
->io_funcs
[fd
] == NULL
)
3062 * Despite the name these are documented to work on Win32.
3063 * Let us hope that is actually true.
3065 subsol
->channels
[fd
] = g_io_channel_unix_new (subsol
->fd
[fd
]);
3066 ioflags
= g_io_channel_get_flags (subsol
->channels
[fd
]);
3067 g_io_channel_set_flags (subsol
->channels
[fd
],
3068 ioflags
| G_IO_FLAG_NONBLOCK
,
3070 subsol
->channel_watches
[fd
] =
3071 g_io_add_watch (subsol
->channels
[fd
],
3073 subsol
->io_funcs
[fd
],
3074 subsol
->io_funcs_data
[fd
]);
3077 gnm_solver_set_status (sol
, GNM_SOLVER_STATUS_RUNNING
);
3081 gnm_sub_solver_clear (subsol
);
3082 gnm_solver_set_status (sol
, GNM_SOLVER_STATUS_ERROR
);
3087 gnm_sub_solver_name_cell (GnmSubSolver
*subsol
, GnmCell
const *cell
,
3090 char *name_copy
= g_strdup (name
);
3092 g_hash_table_insert (subsol
->cell_from_name
,
3095 g_hash_table_insert (subsol
->name_from_cell
,
3103 gnm_sub_solver_find_cell (GnmSubSolver
*subsol
, const char *name
)
3105 return g_hash_table_lookup (subsol
->cell_from_name
, name
);
3109 gnm_sub_solver_get_cell_name (GnmSubSolver
*subsol
,
3110 GnmCell
const *cell
)
3112 return g_hash_table_lookup (subsol
->name_from_cell
, (gpointer
)cell
);
3116 gnm_sub_solver_name_constraint (GnmSubSolver
*subsol
,
3120 char *name_copy
= g_strdup (name
);
3122 g_hash_table_insert (subsol
->constraint_from_name
,
3124 GINT_TO_POINTER (cidx
));
3130 gnm_sub_solver_find_constraint (GnmSubSolver
*subsol
, const char *name
)
3134 if (g_hash_table_lookup_extended (subsol
->constraint_from_name
,
3137 return GPOINTER_TO_INT (idx
);
3144 gnm_sub_solver_locate_binary (const char *binary
, const char *solver
,
3152 GtkFileChooser
*fsel
;
3155 parent
= wbcg
? wbcg_toplevel (wbcg
) : NULL
;
3156 dialog
= gtk_message_dialog_new_with_markup
3158 GTK_DIALOG_DESTROY_WITH_PARENT
,
3159 GTK_MESSAGE_QUESTION
,
3161 _("Gnumeric is unable to locate the program <i>%s</i> needed "
3162 "for the <i>%s</i> solver. For more information see %s.\n\n"
3163 "Would you like to locate it yourself?"),
3164 binary
, solver
, url
);
3165 title
= g_strdup_printf (_("Unable to locate %s"), binary
);
3166 g_object_set (G_OBJECT (dialog
),
3171 res
= go_gtk_dialog_run (GTK_DIALOG (dialog
), parent
);
3173 case GTK_RESPONSE_NO
:
3174 case GTK_RESPONSE_DELETE_EVENT
:
3177 case GTK_RESPONSE_YES
:
3181 title
= g_strdup_printf (_("Locate the %s program"), binary
);
3182 fsel
= GTK_FILE_CHOOSER
3183 (g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG
,
3184 "action", GTK_FILE_CHOOSER_ACTION_OPEN
,
3189 go_gtk_dialog_add_button (GTK_DIALOG (fsel
), GNM_STOCK_CANCEL
,
3190 "gtk-cancel", GTK_RESPONSE_CANCEL
);
3191 go_gtk_dialog_add_button (GTK_DIALOG (fsel
), GNM_STOCK_OK
,
3192 "system-run", GTK_RESPONSE_OK
);
3193 g_object_ref (fsel
);
3194 if (go_gtk_file_sel_dialog (parent
, GTK_WIDGET (fsel
))) {
3195 path
= gtk_file_chooser_get_filename (fsel
);
3196 if (!g_file_test (path
, G_FILE_TEST_IS_EXECUTABLE
)) {
3202 gtk_widget_destroy (GTK_WIDGET (fsel
));
3203 g_object_unref (fsel
);
3210 gnm_sub_solver_flush (GnmSubSolver
*subsol
)
3214 for (fd
= 1; fd
<= 2; fd
++) {
3215 if (subsol
->io_funcs
[fd
] == NULL
)
3218 subsol
->io_funcs
[fd
] (subsol
->channels
[fd
],
3220 subsol
->io_funcs_data
[fd
]);
3225 gnm_sub_solver_class_init (GObjectClass
*object_class
)
3227 gnm_sub_solver_parent_class
= g_type_class_peek_parent (object_class
);
3229 object_class
->dispose
= gnm_sub_solver_dispose
;
3230 object_class
->finalize
= gnm_sub_solver_finalize
;
3232 sub_solver_signals
[SUB_SOL_SIG_CHILD_EXIT
] =
3233 g_signal_new ("child-exit",
3234 G_OBJECT_CLASS_TYPE (object_class
),
3236 G_STRUCT_OFFSET (GnmSubSolverClass
, child_exit
),
3238 gnm__VOID__BOOLEAN_INT
,
3240 G_TYPE_BOOLEAN
, G_TYPE_INT
);
3243 GSF_CLASS (GnmSubSolver
, gnm_sub_solver
,
3244 gnm_sub_solver_class_init
, gnm_sub_solver_init
, GNM_SOLVER_TYPE
)
3246 /* ------------------------------------------------------------------------- */
3249 SOL_ITER_SIG_ITERATE
,
3253 static guint solver_iterator_signals
[SOL_ITER_SIG_LAST
] = { 0 };
3256 gnm_solver_iterator_class_init (GObjectClass
*object_class
)
3258 solver_iterator_signals
[SOL_ITER_SIG_ITERATE
] =
3259 g_signal_new ("iterate",
3260 G_OBJECT_CLASS_TYPE (object_class
),
3262 G_STRUCT_OFFSET (GnmSolverIteratorClass
, iterate
),
3269 gnm_solver_iterator_iterate (GnmSolverIterator
*iter
)
3271 gboolean progress
= FALSE
;
3272 g_signal_emit (iter
, solver_iterator_signals
[SOL_ITER_SIG_ITERATE
], 0, &progress
);
3277 * gnm_solver_iterator_new_func: (skip)
3280 gnm_solver_iterator_new_func (GCallback iterate
, gpointer user
)
3282 GnmSolverIterator
*iter
;
3284 iter
= g_object_new (GNM_SOLVER_ITERATOR_TYPE
, NULL
);
3285 g_signal_connect (iter
, "iterate", G_CALLBACK (iterate
), user
);
3290 cb_polish_iter (GnmSolverIterator
*iter
, GnmIterSolver
*isol
)
3292 GnmSolver
*sol
= GNM_SOLVER (isol
);
3293 const int n
= sol
->input_cells
->len
;
3295 gboolean progress
= FALSE
;
3298 dir
= g_new0 (gnm_float
, n
);
3299 for (c
= 0; c
< n
; c
++) {
3300 gnm_float s
, y
, s0
, sm
, xc
= isol
->xk
[c
];
3307 (void)gnm_frexp (xc
, &e
);
3308 s0
= gnm_ldexp (1, e
- 10);
3309 if (s0
== 0) s0
= GNM_MIN
;
3314 s
= gnm_solver_line_search (sol
, isol
->xk
, dir
, TRUE
,
3318 if (gnm_finite (s
) && s
!= 0) {
3327 gnm_iter_solver_set_solution (isol
);
3333 * gnm_solver_iterator_new_polish:
3334 * @isol: the solver to operate on
3336 * Returns: (transfer full): an iterator object that can be used to polish
3337 * a solution by simple axis-parallel movement.
3340 gnm_solver_iterator_new_polish (GnmIterSolver
*isol
)
3342 return gnm_solver_iterator_new_func (G_CALLBACK (cb_polish_iter
), isol
);
3347 cb_gradient_iter (GnmSolverIterator
*iter
, GnmIterSolver
*isol
)
3349 GnmSolver
*sol
= GNM_SOLVER (isol
);
3350 const int n
= sol
->input_cells
->len
;
3351 gboolean progress
= FALSE
;
3356 /* Search in opposite direction of gradient. */
3357 g
= gnm_solver_compute_gradient (sol
, isol
->xk
);
3358 for (i
= 0; i
< n
; i
++)
3361 s
= gnm_solver_line_search (sol
, isol
->xk
, g
, FALSE
,
3362 1, gnm_pinf
, 0.0, &y
);
3364 for (i
= 0; i
< n
; i
++)
3365 isol
->xk
[i
] += s
* g
[i
];
3373 gnm_iter_solver_set_solution (isol
);
3379 * gnm_solver_iterator_new_gradient:
3380 * @isol: the solver to operate on
3382 * Returns: (transfer full): an iterator object that can be used to perform
3383 * a gradient descent step.
3386 gnm_solver_iterator_new_gradient (GnmIterSolver
*isol
)
3388 return gnm_solver_iterator_new_func (G_CALLBACK (cb_gradient_iter
), isol
);
3391 GSF_CLASS (GnmSolverIterator
, gnm_solver_iterator
,
3392 gnm_solver_iterator_class_init
, NULL
, G_TYPE_OBJECT
)
3394 /* ------------------------------------------------------------------------- */
3401 static GObjectClass
*gnm_solver_iterator_compound_parent_class
;
3404 * gnm_solver_iterator_compound_add:
3405 * @ic: Compound iterator
3406 * @iter: (transfer full): sub-iterator
3407 * @count: repeat count
3409 * Add an iterator to a compound iterator with a given repeat count. As a
3410 * special case, a repeat count of zero means to try the iterator once
3411 * in a cycle, but only if no other sub-iterator has shown any progress so far.
3414 gnm_solver_iterator_compound_add (GnmSolverIteratorCompound
*ic
,
3415 GnmSolverIterator
*iter
,
3418 g_ptr_array_add (ic
->iterators
, iter
);
3419 ic
->counts
= g_renew (unsigned, ic
->counts
, ic
->iterators
->len
);
3420 ic
->counts
[ic
->iterators
->len
- 1] = count
;
3424 gnm_solver_iterator_compound_iterate (GnmSolverIterator
*iter
)
3426 GnmSolverIteratorCompound
*ic
= (GnmSolverIteratorCompound
*)iter
;
3430 if (ic
->cycle
>= ic
->cycles
)
3433 if (ic
->next
>= ic
->iterators
->len
) {
3434 /* We've been through all iterators. */
3435 if (!ic
->cycle_progress
)
3437 ic
->cycle_progress
= FALSE
;
3439 ic
->next_counter
= 0;
3444 if (ic
->next_counter
< ic
->counts
[ic
->next
])
3447 /* Special case: when count==0, use only if no progress. */
3448 if (!ic
->cycle_progress
&& ic
->next_counter
== 0)
3452 ic
->next_counter
= 0;
3455 progress
= gnm_solver_iterator_iterate (g_ptr_array_index (ic
->iterators
, ic
->next
));
3457 ic
->cycle_progress
= TRUE
;
3460 /* No progress, so don't retry. */
3462 ic
->next_counter
= 0;
3465 /* Report progress as long as we have stuff to try. */
3470 gnm_solver_iterator_compound_init (GnmSolverIteratorCompound
*ic
)
3472 ic
->iterators
= g_ptr_array_new ();
3473 ic
->cycles
= G_MAXUINT
;
3477 gnm_solver_iterator_compound_finalize (GObject
*obj
)
3479 GnmSolverIteratorCompound
*ic
= (GnmSolverIteratorCompound
*)obj
;
3480 g_ptr_array_foreach (ic
->iterators
, (GFunc
)g_object_unref
, NULL
);
3481 g_ptr_array_free (ic
->iterators
, TRUE
);
3482 g_free (ic
->counts
);
3483 gnm_solver_iterator_compound_parent_class
->finalize (obj
);
3487 gnm_solver_iterator_compound_get_property (GObject
*object
, guint property_id
,
3488 GValue
*value
, GParamSpec
*pspec
)
3490 GnmSolverIteratorCompound
*it
= (GnmSolverIteratorCompound
*)object
;
3492 switch (property_id
) {
3493 case SOLIC_PROP_CYCLES
:
3494 g_value_set_uint (value
, it
->cycles
);
3498 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
3504 gnm_solver_iterator_compound_set_property (GObject
*object
, guint property_id
,
3505 GValue
const *value
, GParamSpec
*pspec
)
3507 GnmSolverIteratorCompound
*it
= (GnmSolverIteratorCompound
*)object
;
3509 switch (property_id
) {
3510 case SOLIC_PROP_CYCLES
:
3511 it
->cycles
= g_value_get_uint (value
);
3515 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
3521 gnm_solver_iterator_compound_class_init (GObjectClass
*object_class
)
3523 GnmSolverIteratorClass
*iclass
= (GnmSolverIteratorClass
*)object_class
;
3525 gnm_solver_iterator_compound_parent_class
= g_type_class_peek_parent (object_class
);
3527 object_class
->finalize
= gnm_solver_iterator_compound_finalize
;
3528 object_class
->set_property
= gnm_solver_iterator_compound_set_property
;
3529 object_class
->get_property
= gnm_solver_iterator_compound_get_property
;
3530 iclass
->iterate
= gnm_solver_iterator_compound_iterate
;
3532 g_object_class_install_property (object_class
, SOLIC_PROP_CYCLES
,
3533 g_param_spec_uint ("cycles",
3535 P_("Maximum number of cycles"),
3536 0, G_MAXUINT
, G_MAXUINT
,
3537 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
3540 GSF_CLASS (GnmSolverIteratorCompound
, gnm_solver_iterator_compound
,
3541 gnm_solver_iterator_compound_class_init
, gnm_solver_iterator_compound_init
, GNM_SOLVER_ITERATOR_TYPE
)
3543 /* ------------------------------------------------------------------------- */
3545 static GObjectClass
*gnm_iter_solver_parent_class
;
3548 gnm_iter_solver_set_iterator (GnmIterSolver
*isol
, GnmSolverIterator
*iterator
)
3550 GnmSolverIterator
*old_iterator
;
3552 g_return_if_fail (GNM_IS_ITER_SOLVER (isol
));
3554 old_iterator
= isol
->iterator
;
3555 isol
->iterator
= iterator
? g_object_ref (iterator
) : NULL
;
3557 g_object_unref (old_iterator
);
3561 gnm_iter_solver_get_initial_solution (GnmIterSolver
*isol
, GError
**err
)
3563 GnmSolver
*sol
= GNM_SOLVER (isol
);
3564 const int n
= sol
->input_cells
->len
;
3567 if (gnm_solver_check_constraints (sol
))
3573 go_error_invalid (),
3575 _("The initial values do not satisfy the constraints."));
3579 for (i
= 0; i
< n
; i
++) {
3580 GnmCell
*cell
= g_ptr_array_index (sol
->input_cells
, i
);
3581 isol
->xk
[i
] = value_get_as_float (cell
->value
);
3583 isol
->yk
= gnm_solver_get_target_value (sol
);
3585 gnm_iter_solver_set_solution (isol
);
3591 gnm_iter_solver_set_solution (GnmIterSolver
*isol
)
3593 GnmSolver
*sol
= GNM_SOLVER (isol
);
3594 GnmSolverResult
*result
= g_object_new (GNM_SOLVER_RESULT_TYPE
, NULL
);
3595 const int n
= sol
->input_cells
->len
;
3597 result
->quality
= GNM_SOLVER_RESULT_FEASIBLE
;
3598 result
->value
= sol
->flip_sign
? 0 - isol
->yk
: isol
->yk
;
3599 result
->solution
= g_memdup (isol
->xk
, n
* sizeof (gnm_float
));
3600 g_object_set (sol
, "result", result
, NULL
);
3601 g_object_unref (result
);
3603 if (!gnm_solver_check_constraints (sol
)) {
3604 g_printerr ("Infeasible solution set\n");
3609 gnm_iter_solver_clear (GnmIterSolver
*isol
)
3611 if (isol
->idle_tag
) {
3612 g_source_remove (isol
->idle_tag
);
3618 gnm_iter_solver_dispose (GObject
*obj
)
3620 GnmIterSolver
*isol
= GNM_ITER_SOLVER (obj
);
3621 gnm_iter_solver_clear (isol
);
3622 gnm_iter_solver_parent_class
->dispose (obj
);
3626 gnm_iter_solver_finalize (GObject
*obj
)
3628 GnmIterSolver
*isol
= GNM_ITER_SOLVER (obj
);
3630 gnm_iter_solver_parent_class
->finalize (obj
);
3634 gnm_iter_solver_constructed (GObject
*obj
)
3636 GnmIterSolver
*isol
= GNM_ITER_SOLVER (obj
);
3637 GnmSolver
*sol
= GNM_SOLVER (obj
);
3639 /* Chain to parent first */
3640 gnm_iter_solver_parent_class
->constructed (obj
);
3642 isol
->xk
= g_new0 (gnm_float
, sol
->input_cells
->len
);
3646 gnm_iter_solver_init (GnmIterSolver
*isol
)
3651 gnm_iter_solver_idle (gpointer data
)
3653 GnmIterSolver
*isol
= data
;
3654 GnmSolver
*sol
= &isol
->parent
;
3655 GnmSolverParameters
*params
= sol
->params
;
3658 progress
= isol
->iterator
&& gnm_solver_iterator_iterate (isol
->iterator
);
3661 if (!gnm_solver_finished (sol
)) {
3663 gnm_solver_set_status (sol
, GNM_SOLVER_STATUS_DONE
);
3664 } else if (isol
->iterations
>= params
->options
.max_iter
) {
3665 gnm_solver_stop (sol
, NULL
);
3666 gnm_solver_set_reason (sol
, _("Iteration limit exceeded"));
3670 if (gnm_solver_finished (sol
)) {
3683 gnm_iter_solver_start (GnmSolver
*solver
, WorkbookControl
*wbc
, GError
**err
)
3685 GnmIterSolver
*isol
= GNM_ITER_SOLVER (solver
);
3687 g_return_val_if_fail (isol
->idle_tag
== 0, FALSE
);
3689 isol
->idle_tag
= g_idle_add (gnm_iter_solver_idle
, solver
);
3690 gnm_solver_set_status (solver
, GNM_SOLVER_STATUS_RUNNING
);
3696 gnm_iter_solver_stop (GnmSolver
*solver
, GError
**err
)
3698 GnmIterSolver
*isol
= GNM_ITER_SOLVER (solver
);
3699 GnmSolver
*sol
= &isol
->parent
;
3701 gnm_iter_solver_clear (isol
);
3703 g_clear_object (&isol
->iterator
);
3705 gnm_solver_set_status (sol
, GNM_SOLVER_STATUS_CANCELLED
);
3711 gnm_iter_solver_class_init (GObjectClass
*object_class
)
3713 GnmSolverClass
*sclass
= (GnmSolverClass
*)object_class
;
3715 gnm_iter_solver_parent_class
= g_type_class_peek_parent (object_class
);
3717 object_class
->dispose
= gnm_iter_solver_dispose
;
3718 object_class
->finalize
= gnm_iter_solver_finalize
;
3719 object_class
->constructed
= gnm_iter_solver_constructed
;
3720 sclass
->start
= gnm_iter_solver_start
;
3721 sclass
->stop
= gnm_iter_solver_stop
;
3724 GSF_CLASS (GnmIterSolver
, gnm_iter_solver
,
3725 gnm_iter_solver_class_init
, gnm_iter_solver_init
, GNM_SOLVER_TYPE
)
3727 /* ------------------------------------------------------------------------- */
3729 static GObjectClass
*gnm_solver_factory_parent_class
;
3732 gnm_solver_factory_finalize (GObject
*obj
)
3734 GnmSolverFactory
*factory
= GNM_SOLVER_FACTORY (obj
);
3736 if (factory
->notify
)
3737 factory
->notify (factory
->data
);
3739 g_free (factory
->id
);
3740 g_free (factory
->name
);
3742 gnm_solver_factory_parent_class
->finalize (obj
);
3746 gnm_solver_factory_class_init (GObjectClass
*object_class
)
3748 gnm_solver_factory_parent_class
=
3749 g_type_class_peek_parent (object_class
);
3751 object_class
->finalize
= gnm_solver_factory_finalize
;
3754 GSF_CLASS (GnmSolverFactory
, gnm_solver_factory
,
3755 gnm_solver_factory_class_init
, NULL
, G_TYPE_OBJECT
)
3758 static GSList
*solvers
;
3761 * gnm_solver_db_get:
3763 * Returns: (transfer none) (element-type GnmSolverFactory): list of
3764 * registered solver factories.
3767 gnm_solver_db_get (void)
3773 * gnm_solver_factory_new:
3774 * @id: Unique identifier
3775 * @name: Translated name for UI purposes
3776 * @type: Model type created by factory
3777 * @creator: (scope notified): callback for creating a solver
3778 * @functional: (scope notified): callback for checking if factory is functional
3779 * @data: User pointer for @creator and @functional
3780 * @notify: Destroy notification for @data.
3782 * Returns: (transfer full): a new #GnmSolverFactory
3785 gnm_solver_factory_new (const char *id
,
3787 GnmSolverModelType type
,
3788 GnmSolverCreator creator
,
3789 GnmSolverFactoryFunctional functional
,
3791 GDestroyNotify notify
)
3793 GnmSolverFactory
*res
;
3795 g_return_val_if_fail (id
!= NULL
, NULL
);
3796 g_return_val_if_fail (name
!= NULL
, NULL
);
3797 g_return_val_if_fail (creator
!= NULL
, NULL
);
3799 res
= g_object_new (GNM_SOLVER_FACTORY_TYPE
, NULL
);
3800 res
->id
= g_strdup (id
);
3801 res
->name
= g_strdup (name
);
3803 res
->creator
= creator
;
3804 res
->functional
= functional
;
3806 res
->notify
= notify
;
3811 * gnm_solver_factory_create:
3812 * @factory: #GnmSolverFactory
3813 * @param: #GnmSolverParameters
3815 * Returns: (transfer full): a new #GnmSolver
3818 gnm_solver_factory_create (GnmSolverFactory
*factory
,
3819 GnmSolverParameters
*param
)
3821 g_return_val_if_fail (GNM_IS_SOLVER_FACTORY (factory
), NULL
);
3822 return factory
->creator (factory
, param
, factory
->data
);
3826 gnm_solver_factory_functional (GnmSolverFactory
*factory
,
3829 if (factory
== NULL
)
3832 return (factory
->functional
== NULL
||
3833 factory
->functional (factory
, wbcg
, factory
->data
));
3837 cb_compare_factories (GnmSolverFactory
*a
, GnmSolverFactory
*b
)
3839 return go_utf8_collate_casefold (a
->name
, b
->name
);
3843 gnm_solver_db_register (GnmSolverFactory
*factory
)
3845 if (gnm_solver_debug ())
3846 g_printerr ("Registering %s\n", factory
->id
);
3847 g_object_ref (factory
);
3848 solvers
= g_slist_insert_sorted (solvers
, factory
,
3849 (GCompareFunc
)cb_compare_factories
);
3853 gnm_solver_db_unregister (GnmSolverFactory
*factory
)
3855 if (gnm_solver_debug ())
3856 g_printerr ("Unregistering %s\n", factory
->id
);
3857 solvers
= g_slist_remove (solvers
, factory
);
3858 g_object_unref (factory
);
3861 /* ------------------------------------------------------------------------- */