55
66 "github.com/MatProGo-dev/MatProInterface.go/causeOfProblemNonlinearity"
77 "github.com/MatProGo-dev/MatProInterface.go/mpiErrors"
8+ ope "github.com/MatProGo-dev/MatProInterface.go/mpiErrors/optimization_problem"
89 "github.com/MatProGo-dev/MatProInterface.go/optim"
910 getKVector "github.com/MatProGo-dev/SymbolicMath.go/get/KVector"
1011 "github.com/MatProGo-dev/SymbolicMath.go/symbolic"
@@ -660,21 +661,27 @@ Description:
660661 Where A is a matrix of coefficients, b is a vector of constants, and c is the vector of coefficients
661662 for the objective function. This method also returns the slack variables (i.e., the variables that
662663 are added to the problem to convert the inequalities into equalities).
664+
665+ Note:
666+
667+ This method will transform the vector or matrix constraints in the input problem
668+ into a set of scalar constraints. Thus, the number of constraints in your problem may
669+ "seem" to change.
663670*/
664671func (problemIn * OptimizationProblem ) ToLPStandardForm1 () (* OptimizationProblem , []symbolic.Variable , error ) {
665672 // Input Processing
666673 err := problemIn .Check ()
667674 if err != nil {
668- return nil , nil , fmt . Errorf ( "the optimization problem is not well-formed: %v" , err )
675+ return nil , nil , problemIn . MakeNotWellDefinedError ( )
669676 }
670677
671678 // Check if the problem is linear
672679 if ! problemIn .IsLinear () {
673680 return nil , nil , problemIn .CheckIfLinear ()
674681 }
675682
676- // Setup
677- problemWithAllPositiveVariables , err := problemIn .ToProblemWithAllPositiveVariables ()
683+ // Change the problem so that it is written in terms of strictly positive variables
684+ problemWithAllPositiveVariables , err := problemIn .ToProblemWithAllPositiveVariables () // Note: This method may change the number of variables and constraints in the problem.
678685 if err != nil {
679686 return nil , nil , err
680687 }
@@ -708,94 +715,32 @@ func (problemIn *OptimizationProblem) ToLPStandardForm1() (*OptimizationProblem,
708715 case symbolic .SenseEqual :
709716 // No need to do anything
710717 case symbolic .SenseGreaterThanEqual :
711- switch concreteConstraint := constraint .(type ) {
712- case symbolic.ScalarConstraint :
713- // Add a new SCALAR slack variable to the right hand side
714- problemInStandardForm .AddVariableClassic (0.0 , symbolic .Infinity .Constant (), symbolic .Continuous )
715- nVariables := len (problemInStandardForm .Variables )
716- problemInStandardForm .Variables [nVariables - 1 ].Name = problemInStandardForm .Variables [nVariables - 1 ].Name + " (slack)"
717- slackVariables = append (
718- slackVariables ,
719- problemInStandardForm .Variables [nVariables - 1 ],
720- )
721-
722- newRHS = newRHS .Plus (problemInStandardForm .Variables [nVariables - 1 ])
723- case symbolic.VectorConstraint :
724- // Add a new VECTOR slack variable to the right hand side
725- // TODO(Kwesi): Revisit this when we have a proper Len() method for constraints.
726- dims := concreteConstraint .Dims ()
727- nRows := dims [0 ]
728- problemInStandardForm .AddVariableVectorClassic (
729- nRows ,
730- 0.0 ,
731- symbolic .Infinity .Constant (),
732- symbolic .Continuous ,
733- )
734- nVariables := len (problemInStandardForm .Variables )
735- for jj := nRows - 1 ; jj >= 0 ; jj -- {
736- problemInStandardForm .Variables [nVariables - 1 - jj ].Name = problemInStandardForm .Variables [nVariables - 1 - jj ].Name + " (slack)"
737- slackVariables = append (
738- slackVariables ,
739- problemInStandardForm .Variables [nVariables - 1 - jj ],
740- )
741- }
742-
743- // Add the slack variable to the right hand side
744- newRHS = newRHS .Plus (
745- symbolic .VariableVector (problemInStandardForm .Variables [nVariables - nRows : nVariables ]),
746- )
747- default :
748- return nil , nil , fmt .Errorf (
749- "Unexpected constraint type: %T for \" ToStandardFormWithSlackVariables\" with %v sense" ,
750- constraint ,
751- constraint .ConstrSense (),
752- )
718+ // Constraints MUST be scalar at this point
719+
720+ // Add a new SCALAR slack variable to the right hand side
721+ problemInStandardForm .AddVariableClassic (0.0 , symbolic .Infinity .Constant (), symbolic .Continuous )
722+ nVariables := len (problemInStandardForm .Variables )
723+ problemInStandardForm .Variables [nVariables - 1 ].Name = problemInStandardForm .Variables [nVariables - 1 ].Name + " (slack)"
724+ slackVariables = append (
725+ slackVariables ,
726+ problemInStandardForm .Variables [nVariables - 1 ],
727+ )
728+
729+ newRHS = newRHS .Plus (problemInStandardForm .Variables [nVariables - 1 ])
753730
754- }
755731 case symbolic .SenseLessThanEqual :
756- // Use a switch statement to handle different dimensions of the constraint
757- switch concreteConstraint := constraint .(type ) {
758- case symbolic.ScalarConstraint :
759- // Add a new SCALAR slack variable to the left hand side
760- problemInStandardForm .AddVariableClassic (0.0 , symbolic .Infinity .Constant (), symbolic .Continuous )
761- nVariables := len (problemInStandardForm .Variables )
762- problemInStandardForm .Variables [nVariables - 1 ].Name = problemInStandardForm .Variables [nVariables - 1 ].Name + " (slack)"
763- slackVariables = append (
764- slackVariables ,
765- problemInStandardForm .Variables [nVariables - 1 ],
766- )
767- newLHS = newLHS .Plus (problemInStandardForm .Variables [nVariables - 1 ])
768- case symbolic.VectorConstraint :
769- // Add a new VECTOR slack variable to the left hand side
770- // TODO(Kwesi): Revisit this when we have a proper Len() method for constraints.
771- dims := concreteConstraint .Dims ()
772- nRows := dims [0 ]
773- problemInStandardForm .AddVariableVectorClassic (
774- nRows ,
775- 0.0 ,
776- symbolic .Infinity .Constant (),
777- symbolic .Continuous ,
778- )
779- nVariables := len (problemInStandardForm .Variables )
780- for jj := nRows - 1 ; jj >= 0 ; jj -- {
781- problemInStandardForm .Variables [nVariables - 1 - jj ].Name = problemInStandardForm .Variables [nVariables - 1 - jj ].Name + " (slack)"
782- slackVariables = append (
783- slackVariables ,
784- problemInStandardForm .Variables [nVariables - 1 - jj ],
785- )
786- // fmt.Printf("Slack variable %d: %v\n", jj, problemInStandardForm.Variables[nVariables-1-jj])
787- }
788- // Add the slack variable to the left hand side
789- newLHS = newLHS .Plus (
790- symbolic .VariableVector (problemInStandardForm .Variables [nVariables - nRows : nVariables ]),
791- )
792- default :
793- return nil , nil , fmt .Errorf (
794- "Unexpected constraint type %T for \" ToStandardFormWithSlackVariables\" with %v sense" ,
795- constraint ,
796- constraint .ConstrSense (),
797- )
798- }
732+ // Constraints MUST be scalar at this point
733+
734+ // Add a new SCALAR slack variable to the left hand side
735+ problemInStandardForm .AddVariableClassic (0.0 , symbolic .Infinity .Constant (), symbolic .Continuous )
736+ nVariables := len (problemInStandardForm .Variables )
737+ problemInStandardForm .Variables [nVariables - 1 ].Name = problemInStandardForm .Variables [nVariables - 1 ].Name + " (slack)"
738+ slackVariables = append (
739+ slackVariables ,
740+ problemInStandardForm .Variables [nVariables - 1 ],
741+ )
742+ newLHS = newLHS .Plus (problemInStandardForm .Variables [nVariables - 1 ])
743+
799744 default :
800745 return nil , nil , fmt .Errorf (
801746 "Unknown constraint sense: " + constraint .ConstrSense ().String (),
@@ -831,6 +776,54 @@ func (problemIn *OptimizationProblem) ToLPStandardForm1() (*OptimizationProblem,
831776 return problemInStandardForm , slackVariables , nil
832777}
833778
779+ /*
780+ ToLPStandardForm2
781+ Description:
782+
783+ Transforms the given linear program (represented in an OptimizationProblem object)
784+ into a standard form (i.e., only linear equality constraints and a linear objective function).
785+
786+ max c^T * x
787+ subject to
788+ A * x = b
789+ x >= 0
790+
791+ Where:
792+ - A is a matrix of coefficients,
793+ - b is a vector of constants, and
794+ - c is the vector of coefficients for the objective function.
795+ This method also returns the slack variables (i.e., the variables that
796+ are added to the problem to convert the inequalities into equalities).
797+ */
798+ func (problemIn * OptimizationProblem ) ToLPStandardForm2 () (* OptimizationProblem , []symbolic.Variable , error ) {
799+ // Input Processing
800+ err := problemIn .Check ()
801+ if err != nil {
802+ return nil , nil , problemIn .MakeNotWellDefinedError ()
803+ }
804+
805+ // Use the existing method to convert to standard form 1
806+ problemInStandardForm , slackVariables , err := problemIn .ToLPStandardForm1 ()
807+ if err != nil {
808+ return nil , nil , err
809+ }
810+
811+ // Modify the objective function to be a maximization problem,
812+ // if it is not already.
813+ if problemInStandardForm .Objective .Sense == SenseMinimize {
814+ // If the problem is a minimization problem,
815+ // then we can convert it to a maximization problem by negating the objective function.
816+ newObjectiveExpression := problemInStandardForm .Objective .Expression .Multiply (- 1.0 )
817+ err = problemInStandardForm .SetObjective (newObjectiveExpression , SenseMaximize )
818+ if err != nil {
819+ return nil , nil , fmt .Errorf ("there was a problem setting the new objective function: %v" , err )
820+ }
821+ }
822+
823+ // Return the new problem and the slack variables
824+ return problemInStandardForm , slackVariables , nil
825+ }
826+
834827/*
835828WithAllPositiveVariableConstraintsRemoved
836829Description:
@@ -851,30 +844,12 @@ func (op *OptimizationProblem) WithAllPositiveVariableConstraintsRemoved() *Opti
851844 newProblem .Variables = append (newProblem .Variables , variable )
852845 }
853846
854- // Copy the constraints
855- for _ , constraintII := range op .Constraints {
856- // Check if the constraint is a x >= 0 constraint
857- if symbolic .SenseGreaterThanEqual == constraintII .ConstrSense () {
858- lhsContains1Variable := len (constraintII .Left ().Variables ()) == 1
859- rhs , rhsIsConstant := constraintII .Right ().(symbolic.K )
860- if lhsContains1Variable && rhsIsConstant {
861- if float64 (rhs ) == 0.0 {
862- // If the constraint is of the form x >= 0, we can remove it
863- continue
864- }
865- }
866- }
867-
868- // Check if the constraint is a 0 <= x constraint
869- if symbolic .SenseLessThanEqual == constraintII .ConstrSense () {
870- rhsContains1Variable := len (constraintII .Left ().Variables ()) == 1
871- lhs , lhsIsConstant := constraintII .Right ().(symbolic.K )
872- if rhsContains1Variable && lhsIsConstant {
873- if float64 (lhs ) == 0.0 {
874- // If the constraint is of the form 0 <= x, we can remove it
875- continue
876- }
877- }
847+ // Reduce the constraints to scalar constraints
848+ scalarConstraints := symbolic .CompileConstraintsIntoScalarConstraints (op .Constraints )
849+ for _ , constraintII := range scalarConstraints {
850+ // Check if the constraint is a (Non-negativity) x >= 0 or 0 <= x constraint
851+ if constraintII .IsNonnegativityConstraint () {
852+ continue
878853 }
879854
880855 // Otherwise, we can keep the constraint
@@ -1012,3 +987,10 @@ func (op *OptimizationProblem) SimplifyConstraints() {
1012987 // Set the new constraints
1013988 op .Constraints = newConstraints
1014989}
990+
991+ func (op * OptimizationProblem ) MakeNotWellDefinedError () ope.NotWellDefinedError {
992+ return ope.NotWellDefinedError {
993+ ProblemName : op .Name ,
994+ ErrorSource : op .Check (),
995+ }
996+ }
0 commit comments