2 * Copyright 2007-2008, Christof Lutteroth, lutteroth@cs.auckland.ac.nz
3 * Copyright 2007-2008, James Kim, jkim202@ec.auckland.ac.nz
4 * Copyright 2010, Clemens Zeidler <haiku@clemens-zeidler.de>
5 * Distributed under the terms of the MIT License.
9 #include "LinearSpec.h"
13 #include "ActiveSetSolver.h"
16 using namespace LinearProgramming
;
19 // #define DEBUG_LINEAR_SPECIFICATIONS
21 #ifdef DEBUG_LINEAR_SPECIFICATIONS
23 #define TRACE(x...) printf(x)
25 #define TRACE(x...) /* nothing */
29 SolverInterface::SolverInterface(LinearSpec
* linSpec
)
37 SolverInterface::~SolverInterface()
43 SolverInterface::AddConstraint(Constraint
* constraint
, bool notifyListener
)
45 return fLinearSpec
->AddConstraint(constraint
, notifyListener
);
50 SolverInterface::RemoveConstraint(Constraint
* constraint
, bool deleteConstraint
,
53 return fLinearSpec
->RemoveConstraint(constraint
, deleteConstraint
,
58 SpecificationListener::~SpecificationListener()
64 SpecificationListener::VariableAdded(Variable
* variable
)
70 SpecificationListener::VariableRemoved(Variable
* variable
)
76 SpecificationListener::ConstraintAdded(Constraint
* constraint
)
82 SpecificationListener::ConstraintRemoved(Constraint
* constraint
)
89 * Creates a new specification for a linear programming problem.
91 LinearSpec::LinearSpec()
96 fSolver
= new ActiveSetSolver(this);
102 * Removes the specification and deletes all constraints,
103 * objective function summands and variables.
105 LinearSpec::~LinearSpec()
107 for (int32 i
= 0; i
< fConstraints
.CountItems(); i
++)
108 delete (Constraint
*)fConstraints
.ItemAt(i
);
109 while (fVariables
.CountItems() > 0)
110 RemoveVariable(fVariables
.ItemAt(0));
117 LinearSpec::AddListener(SpecificationListener
* listener
)
119 return fListeners
.AddItem(listener
);
124 LinearSpec::RemoveListener(SpecificationListener
* listener
)
126 return fListeners
.RemoveItem(listener
);
131 * Adds a new variable to the specification.
133 * @return the new variable
136 LinearSpec::AddVariable()
138 Variable
* variable
= new(std::nothrow
) Variable(this);
141 if (!AddVariable(variable
)) {
151 LinearSpec::AddVariable(Variable
* variable
)
153 if (variable
->IsValid())
156 if (!fVariables
.AddItem(variable
))
159 if (variable
->fLS
== NULL
)
160 variable
->fLS
= this;
162 if (!fSolver
->VariableAdded(variable
)) {
163 fVariables
.RemoveItem(variable
);
166 variable
->fIsValid
= true;
168 if (!UpdateRange(variable
)) {
169 RemoveVariable(variable
, false);
173 for (int32 i
= 0; i
< fListeners
.CountItems(); i
++)
174 fListeners
.ItemAt(i
)->VariableAdded(variable
);
181 LinearSpec::RemoveVariable(Variable
* variable
, bool deleteVariable
)
183 // must be called first otherwise the index is invalid
184 if (fSolver
->VariableRemoved(variable
) == false)
187 // do we know the variable?
188 if (fVariables
.RemoveItem(variable
) == false)
190 fUsedVariables
.RemoveItem(variable
);
191 variable
->fIsValid
= false;
193 // invalidate all constraints that use this variable
194 ConstraintList markedForInvalidation
;
195 const ConstraintList
& constraints
= Constraints();
196 for (int i
= 0; i
< constraints
.CountItems(); i
++) {
197 Constraint
* constraint
= constraints
.ItemAt(i
);
199 SummandList
* summands
= constraint
->LeftSide();
200 for (int j
= 0; j
< summands
->CountItems(); j
++) {
201 Summand
* summand
= summands
->ItemAt(j
);
202 if (summand
->Var() == variable
) {
203 markedForInvalidation
.AddItem(constraint
);
208 for (int i
= 0; i
< markedForInvalidation
.CountItems(); i
++)
209 RemoveConstraint(markedForInvalidation
.ItemAt(i
));
214 for (int32 i
= 0; i
< fListeners
.CountItems(); i
++)
215 fListeners
.ItemAt(i
)->VariableRemoved(variable
);
222 LinearSpec::IndexOf(const Variable
* variable
) const
224 return fUsedVariables
.IndexOf(variable
);
229 LinearSpec::GlobalIndexOf(const Variable
* variable
) const
231 return fVariables
.IndexOf(variable
);
236 LinearSpec::UpdateRange(Variable
* variable
)
238 if (!fSolver
->VariableRangeChanged(variable
))
245 LinearSpec::AddConstraint(Constraint
* constraint
)
247 return AddConstraint(constraint
, true);
252 LinearSpec::RemoveConstraint(Constraint
* constraint
, bool deleteConstraint
)
254 return RemoveConstraint(constraint
, deleteConstraint
, true);
259 LinearSpec::AddConstraint(Constraint
* constraint
, bool notifyListener
)
261 if (!fConstraints
.AddItem(constraint
))
264 // ref count the used variables
265 SummandList
* leftSide
= constraint
->LeftSide();
266 for (int i
= 0; i
< leftSide
->CountItems(); i
++) {
267 Variable
* var
= leftSide
->ItemAt(i
)->Var();
268 _AddConstraintRef(var
);
271 if (!fSolver
->ConstraintAdded(constraint
)) {
272 RemoveConstraint(constraint
, false);
276 constraint
->fLS
= this;
278 if (notifyListener
) {
279 for (int32 i
= 0; i
< fListeners
.CountItems(); i
++)
280 fListeners
.ItemAt(i
)->ConstraintAdded(constraint
);
288 LinearSpec::RemoveConstraint(Constraint
* constraint
, bool deleteConstraint
,
291 if (constraint
== NULL
)
293 fSolver
->ConstraintRemoved(constraint
);
294 if (!fConstraints
.RemoveItem(constraint
))
297 SummandList
* leftSide
= constraint
->LeftSide();
298 for (int32 i
= 0; i
< leftSide
->CountItems(); i
++) {
299 Variable
* var
= leftSide
->ItemAt(i
)->Var();
300 _RemoveConstraintRef(var
);
303 if (notifyListener
) {
304 for (int32 i
= 0; i
< fListeners
.CountItems(); i
++)
305 fListeners
.ItemAt(i
)->ConstraintRemoved(constraint
);
308 constraint
->fLS
= NULL
;
309 if (deleteConstraint
)
317 LinearSpec::_AddConstraintRef(Variable
* var
)
319 if (var
->AddReference() == 1)
320 fUsedVariables
.AddItem(var
);
325 LinearSpec::_RemoveConstraintRef(Variable
* var
)
327 if (var
->RemoveReference() == 0)
328 fUsedVariables
.RemoveItem(var
);
333 LinearSpec::UpdateLeftSide(Constraint
* constraint
,
334 const SummandList
* oldSummands
)
336 SummandList
* leftSide
= constraint
->LeftSide();
337 if (leftSide
!= NULL
) {
338 for (int32 i
= 0; i
< leftSide
->CountItems(); i
++) {
339 Variable
* var
= leftSide
->ItemAt(i
)->Var();
340 _AddConstraintRef(var
);
343 if (oldSummands
!= NULL
) {
344 // the summands have changed, update the var ref count
345 for (int32 i
= 0; i
< oldSummands
->CountItems(); i
++) {
346 Variable
* var
= oldSummands
->ItemAt(i
)->Var();
347 _RemoveConstraintRef(var
);
351 if (!fSolver
->LeftSideChanged(constraint
))
358 LinearSpec::UpdateRightSide(Constraint
* constraint
)
360 if (!fSolver
->RightSideChanged(constraint
))
367 LinearSpec::UpdateOperator(Constraint
* constraint
)
369 if (!fSolver
->OperatorChanged(constraint
))
376 LinearSpec::UpdatePenalties(Constraint
* constraint
)
378 if (!fSolver
->PenaltiesChanged(constraint
))
385 * Adds a new soft linear constraint to the specification.
386 * i.e. a constraint that does not always have to be satisfied.
388 * @param coeffs the constraint's coefficients
389 * @param vars the constraint's variables
390 * @param op the constraint's operand
391 * @param rightSide the constant value on the constraint's right side
392 * @param penaltyNeg the coefficient penalizing negative deviations from the exact solution
393 * @param penaltyPos the coefficient penalizing positive deviations from the exact solution
396 LinearSpec::AddConstraint(SummandList
* summands
, OperatorType op
,
397 double rightSide
, double penaltyNeg
, double penaltyPos
)
399 return _AddConstraint(summands
, op
, rightSide
, penaltyNeg
, penaltyPos
);
404 * Adds a new soft linear constraint to the specification with a single summand.
406 * @param coeff1 the constraint's first coefficient
407 * @param var1 the constraint's first variable
408 * @param op the constraint's operand
409 * @param rightSide the constant value on the constraint's right side
410 * @param penaltyNeg the coefficient penalizing negative deviations from the exact solution
411 * @param penaltyPos the coefficient penalizing positive deviations from the exact solution
414 LinearSpec::AddConstraint(double coeff1
, Variable
* var1
,
415 OperatorType op
, double rightSide
, double penaltyNeg
, double penaltyPos
)
417 SummandList
* summands
= new(std::nothrow
) SummandList(1);
420 summands
->AddItem(new(std::nothrow
) Summand(coeff1
, var1
));
421 if (!_CheckSummandList(summands
))
423 return _AddConstraint(summands
, op
, rightSide
, penaltyNeg
, penaltyPos
);
428 * Adds a new soft linear constraint to the specification with two summands.
430 * @param coeff1 the constraint's first coefficient
431 * @param var1 the constraint's first variable
432 * @param coeff2 the constraint's second coefficient
433 * @param var2 the constraint's second variable
434 * @param op the constraint's operand
435 * @param rightSide the constant value on the constraint's right side
436 * @param penaltyNeg the coefficient penalizing negative deviations from the exact solution
437 * @param penaltyPos the coefficient penalizing positive deviations from the exact solution
440 LinearSpec::AddConstraint(double coeff1
, Variable
* var1
,
441 double coeff2
, Variable
* var2
, OperatorType op
, double rightSide
,
442 double penaltyNeg
, double penaltyPos
)
444 SummandList
* summands
= new(std::nothrow
) SummandList(2);
447 summands
->AddItem(new(std::nothrow
) Summand(coeff1
, var1
));
448 summands
->AddItem(new(std::nothrow
) Summand(coeff2
, var2
));
449 if (!_CheckSummandList(summands
))
451 return _AddConstraint(summands
, op
, rightSide
, penaltyNeg
, penaltyPos
);
456 * Adds a new soft linear constraint to the specification with three summands.
458 * @param coeff1 the constraint's first coefficient
459 * @param var1 the constraint's first variable
460 * @param coeff2 the constraint's second coefficient
461 * @param var2 the constraint's second variable
462 * @param coeff3 the constraint's third coefficient
463 * @param var3 the constraint's third variable
464 * @param op the constraint's operand
465 * @param rightSide the constant value on the constraint's right side
466 * @param penaltyNeg the coefficient penalizing negative deviations from the exact solution
467 * @param penaltyPos the coefficient penalizing positive deviations from the exact solution
470 LinearSpec::AddConstraint(double coeff1
, Variable
* var1
,
471 double coeff2
, Variable
* var2
, double coeff3
, Variable
* var3
,
472 OperatorType op
, double rightSide
, double penaltyNeg
, double penaltyPos
)
474 SummandList
* summands
= new(std::nothrow
) SummandList(2);
477 summands
->AddItem(new(std::nothrow
) Summand(coeff1
, var1
));
478 summands
->AddItem(new(std::nothrow
) Summand(coeff2
, var2
));
479 summands
->AddItem(new(std::nothrow
) Summand(coeff3
, var3
));
480 if (!_CheckSummandList(summands
))
482 return _AddConstraint(summands
, op
, rightSide
, penaltyNeg
, penaltyPos
);
487 * Adds a new soft linear constraint to the specification with four summands.
489 * @param coeff1 the constraint's first coefficient
490 * @param var1 the constraint's first variable
491 * @param coeff2 the constraint's second coefficient
492 * @param var2 the constraint's second variable
493 * @param coeff3 the constraint's third coefficient
494 * @param var3 the constraint's third variable
495 * @param coeff4 the constraint's fourth coefficient
496 * @param var4 the constraint's fourth variable
497 * @param op the constraint's operand
498 * @param rightSide the constant value on the constraint's right side
499 * @param penaltyNeg the coefficient penalizing negative deviations from the exact solution
500 * @param penaltyPos the coefficient penalizing positive deviations from the exact solution
503 LinearSpec::AddConstraint(double coeff1
, Variable
* var1
,
504 double coeff2
, Variable
* var2
, double coeff3
, Variable
* var3
,
505 double coeff4
, Variable
* var4
, OperatorType op
, double rightSide
,
506 double penaltyNeg
, double penaltyPos
)
508 SummandList
* summands
= new(std::nothrow
) SummandList(2);
511 summands
->AddItem(new(std::nothrow
) Summand(coeff1
, var1
));
512 summands
->AddItem(new(std::nothrow
) Summand(coeff2
, var2
));
513 summands
->AddItem(new(std::nothrow
) Summand(coeff3
, var3
));
514 summands
->AddItem(new(std::nothrow
) Summand(coeff4
, var4
));
515 if (!_CheckSummandList(summands
))
517 return _AddConstraint(summands
, op
, rightSide
, penaltyNeg
, penaltyPos
);
522 LinearSpec::FindMins(const VariableList
* variables
)
524 fResult
= fSolver
->FindMins(variables
);
530 LinearSpec::FindMaxs(const VariableList
* variables
)
532 fResult
= fSolver
->FindMaxs(variables
);
538 LinearSpec::_CheckSummandList(SummandList
* list
)
541 for (int i
= 0; i
< list
->CountItems(); i
++) {
542 if (list
->ItemAt(i
) == NULL
) {
550 for (int i
= 0; i
< list
->CountItems(); i
++)
551 delete list
->ItemAt(i
);
558 LinearSpec::_AddConstraint(SummandList
* leftSide
, OperatorType op
,
559 double rightSide
, double penaltyNeg
, double penaltyPos
)
561 Constraint
* constraint
= new(std::nothrow
) Constraint(this, leftSide
,
562 op
, rightSide
, penaltyNeg
, penaltyPos
);
563 if (constraint
== NULL
)
565 if (!AddConstraint(constraint
)) {
573 #ifdef DEBUG_LINEAR_SPECIFICATIONS
574 static bigtime_t sAverageSolvingTime
= 0;
575 static int32 sSolvedCount
= 0;
582 bigtime_t startTime
= system_time();
584 fResult
= fSolver
->Solve();
586 fSolvingTime
= system_time() - startTime
;
588 #ifdef DEBUG_LINEAR_SPECIFICATIONS
589 sAverageSolvingTime
+= fSolvingTime
;
591 TRACE("Solving time %i average %i [micro s]\n", (int)fSolvingTime
,
592 int(sAverageSolvingTime
/ sSolvedCount
));
600 * Writes the specification into a text file.
601 * The file will be overwritten if it exists.
603 * @param fname the file name
606 LinearSpec::Save(const char* fileName
)
608 return fSolver
->SaveModel(fileName
) == B_OK
;
613 * Gets the constraints generated by calls to Variable::SetRange()
617 LinearSpec::GetRangeConstraints(const Variable
* var
, const Constraint
** _min
,
618 const Constraint
** _max
) const
620 fSolver
->GetRangeConstraints(var
, _min
, _max
);
625 * Gets the constraints.
627 * @return the constraints
629 const ConstraintList
&
630 LinearSpec::Constraints() const
637 LinearSpec::UsedVariables() const
639 return fUsedVariables
;
644 LinearSpec::AllVariables() const
651 * Gets the result type.
653 * @return the result type
656 LinearSpec::Result() const
663 * Gets the solving time.
665 * @return the solving time
668 LinearSpec::SolvingTime() const
675 LinearSpec::ToString() const
678 string
<< "LinearSpec " << (addr_t
)this << ":\n";
679 for (int i
= 0; i
< fVariables
.CountItems(); i
++) {
680 Variable
* variable
= fVariables
.ItemAt(i
);
681 string
+= variable
->ToString();
682 string
<< "=" << (float)variable
->Value() << " ";
685 for (int i
= 0; i
< fConstraints
.CountItems(); i
++) {
686 Constraint
* c
= fConstraints
.ItemAt(i
);
688 string
+= c
->ToString();
692 if (fResult
== kError
)
694 else if (fResult
== kOptimal
)
695 string
<< "kOptimal";
696 else if (fResult
== kSuboptimal
)
697 string
<< "kSuboptimal";
698 else if (fResult
== kInfeasible
)
699 string
<< "kInfeasible";
700 else if (fResult
== kUnbounded
)
701 string
<< "kUnbounded";
702 else if (fResult
== kDegenerate
)
703 string
<< "kDegenerate";
704 else if (fResult
== kNumFailure
)
705 string
<< "kNumFailure";
708 string
<< " SolvingTime=" << fSolvingTime
<< "micro s";