Skip to content

Commit 0214315

Browse files
Created OptimizationProblem's new LinearInequalityConstraintMatrices and LinearEqualityConstraintMatrices (#8)
* Upgraded SymbolicMath * Bumped the version of SymbolicMath.go * Introduced method for assembling linear inequality constraints into single matrices * Updated main readme to take advantage of new problem structure * wip: First attempt at creating the LinearEqualityConstraintMatrices function * Added tests for the new Equality constraint matrices function * Added tests for coverage of the case when there are mixtures of scalar and vector constraints
1 parent dece87d commit 0214315

File tree

5 files changed

+816
-42
lines changed

5 files changed

+816
-42
lines changed

README.md

Lines changed: 22 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -26,59 +26,40 @@ by your model.
2626
## Modeling the Mathematical Program Above
2727
For example, to model the program above one would write the following code:
2828
```
29-
// Constants
30-
modelName := "mpg-qp1"
31-
m := optim.NewModel(modelName)
32-
x := m.AddVariableVector(2)
3329
34-
// Create Vector Constants
35-
c1 := optim.KVector(
36-
*mat.NewVecDense(2, []float64{0.0, 1.0}),
30+
import (
31+
...
32+
"github.com/MatProGo-dev/MatProInterface.go/problem"
33+
getKVector "github.com/MatProGo-dev/SymbolicMath.go/get/KVector"
34+
...
3735
)
3836
39-
c2 := optim.KVector(
40-
*mat.NewVecDense(2, []float64{2.0, 3.0}),
41-
)
37+
// Constants
38+
problemName := "mpg-qp1"
39+
p1 := problem.NewProblem(problemName)
40+
x := p1.AddVariableVector(2)
4241
43-
// Use these to create constraints.
42+
// Create Vector Constants
43+
c1 := getKVector.From([]float64{0.0, 1.0})
44+
c2 := getKVector.From([]float64{2.0, 3.0})
4445
45-
vc1, err := x.LessEq(c2)
46-
if err != nil {
47-
t.Errorf("There was an issue creating the proper vector constraint: %v", err)
48-
}
46+
// Use these to create constraints.
47+
vc1 := x.LessEq(c2)
48+
vc2 := x.GreaterEq(c1)
4949
50-
vc2, err := x.GreaterEq(c1)
51-
if err != nil {
52-
t.Errorf("There was an issue creating the proper vector constraint: %v", err)
53-
}
50+
p1.Constraints = append(p1.Constraints, vc1)
51+
p1.Constraints = append(p1.Constraints, vc2)
5452
5553
// Create objective
56-
Q1 := optim.Identity(x.Len())
54+
Q1 := symbolic.Identity(x.Len())
5755
Q1.Set(0, 1, 0.25)
5856
Q1.Set(1, 0, 0.25)
5957
Q1.Set(1, 1, 0.25)
6058
61-
obj := optim.ScalarQuadraticExpression{
62-
Q: Q1,
63-
X: x,
64-
L: *mat.NewVecDense(x.Len(), []float64{0, -0.97}),
65-
C: 2.0,
66-
}
67-
68-
// Add Constraints
69-
constraints := []optim.Constraint{vc1, vc2}
70-
for _, constr := range constraints {
71-
err = m.AddConstraint(constr)
72-
if err != nil {
73-
t.Errorf("There was an issue adding the vector constraint to the model: %v", err)
74-
}
75-
}
76-
77-
// Add objective
78-
err = m.SetObjective(optim.Objective{obj, optim.SenseMinimize})
79-
if err != nil {
80-
t.Errorf("There was an issue setting the objective of the Gurobi solver model: %v", err)
81-
}
59+
p1.Objective = *problem.NewObjective(
60+
x.Transpose().Multiply(Q).Multiply(x),
61+
problem.SenseMinimize,
62+
)
8263
8364
// Solve using the solver of your choice!
8465
```

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ go 1.21
44

55
require gonum.org/v1/gonum v0.14.0
66

7-
require github.com/MatProGo-dev/SymbolicMath.go v0.1.8
7+
require github.com/MatProGo-dev/SymbolicMath.go v0.2.1

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
github.com/MatProGo-dev/SymbolicMath.go v0.1.8 h1:lpe+6cK/2fg29WwxOykm4hKvfJeqvFUBGturC9qh5ug=
22
github.com/MatProGo-dev/SymbolicMath.go v0.1.8/go.mod h1:gKbGR/6sYWi2koMUEDIPWBPi6jQPELKle0ijIM+eaHU=
3+
github.com/MatProGo-dev/SymbolicMath.go v0.1.9 h1:NMqvS9Bt2DWWLGxd+j3Qta4Ckq/x74gpMM7bt32om5g=
4+
github.com/MatProGo-dev/SymbolicMath.go v0.1.9/go.mod h1:gKbGR/6sYWi2koMUEDIPWBPi6jQPELKle0ijIM+eaHU=
5+
github.com/MatProGo-dev/SymbolicMath.go v0.2.0 h1:W8IREsZGeIuPAKHgJDyeCr3vLJjMr6H/O9RNFAjOVp8=
6+
github.com/MatProGo-dev/SymbolicMath.go v0.2.0/go.mod h1:gKbGR/6sYWi2koMUEDIPWBPi6jQPELKle0ijIM+eaHU=
7+
github.com/MatProGo-dev/SymbolicMath.go v0.2.1 h1:3qcLYNx3+9Ud/saS4QWxJzmDUbVvYZIWt9aX2bQ9iOk=
8+
github.com/MatProGo-dev/SymbolicMath.go v0.2.1/go.mod h1:gKbGR/6sYWi2koMUEDIPWBPi6jQPELKle0ijIM+eaHU=
39
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
410
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
511
gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0=

problem/optimization_problem.go

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import (
55

66
"github.com/MatProGo-dev/MatProInterface.go/mpiErrors"
77
"github.com/MatProGo-dev/MatProInterface.go/optim"
8+
getKVector "github.com/MatProGo-dev/SymbolicMath.go/get/KVector"
89
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
10+
"gonum.org/v1/gonum/mat"
911
)
1012

1113
// OptimizationProblem represents the overall constrained linear optimization model to be
@@ -340,3 +342,196 @@ func (op *OptimizationProblem) IsLinear() bool {
340342
// All Checks Passed!
341343
return true
342344
}
345+
346+
/*
347+
LinearInequalityConstraintMatrices
348+
Description:
349+
350+
Returns the linear INEQUALITY constraint matrices and vectors.
351+
For all linear inequality constraints, we assemble them into the form:
352+
Ax <= b
353+
Where A is the matrix of coefficients, x is the vector of variables, and b is the vector of constants.
354+
We return A and b.
355+
*/
356+
func (op *OptimizationProblem) LinearInequalityConstraintMatrices() (symbolic.KMatrix, symbolic.KVector) {
357+
// Setup
358+
359+
// Collect the Variables of this Problem
360+
x := op.Variables
361+
362+
// Iterate through all constraints and collect the linear constraints
363+
// into a matrix and vector.
364+
scalar_constraints := make([]symbolic.ScalarConstraint, 0)
365+
vector_constraints := make([]symbolic.VectorConstraint, 0)
366+
for _, constraint := range op.Constraints {
367+
// Skip this constraint if it is not linear
368+
if !constraint.IsLinear() {
369+
continue
370+
}
371+
// Skip this constraint if it is not an inequality
372+
if constraint.ConstrSense() == symbolic.SenseEqual {
373+
continue
374+
}
375+
switch c := constraint.(type) {
376+
case symbolic.ScalarConstraint:
377+
scalar_constraints = append(scalar_constraints, c)
378+
case symbolic.VectorConstraint:
379+
vector_constraints = append(vector_constraints, c)
380+
}
381+
}
382+
383+
// Create the matrix and vector elements from the scalar constraints
384+
A_components_scalar := make([]mat.VecDense, len(scalar_constraints))
385+
b_components_scalar := make([]float64, len(scalar_constraints))
386+
for ii, constraint := range scalar_constraints {
387+
A_components_scalar[ii], b_components_scalar[ii] = constraint.LinearInequalityConstraintRepresentation(x)
388+
}
389+
390+
// Create the matrix and vector elements from the vector constraints
391+
A_components_vector := make([]mat.Dense, len(vector_constraints))
392+
b_components_vector := make([]mat.VecDense, len(vector_constraints))
393+
for ii, constraint := range vector_constraints {
394+
A_components_vector[ii], b_components_vector[ii] = constraint.LinearInequalityConstraintRepresentation(x)
395+
}
396+
397+
// Assemble the matrix and vector components
398+
var AOut symbolic.Expression
399+
var bOut symbolic.Expression
400+
scalar_constraint_matrices_exist := len(A_components_scalar) > 0
401+
if scalar_constraint_matrices_exist {
402+
AOut = symbolic.VecDenseToKVector(A_components_scalar[0]).Transpose()
403+
for ii := 1; ii < len(A_components_scalar); ii++ {
404+
AOut = symbolic.VStack(
405+
AOut,
406+
symbolic.VecDenseToKVector(A_components_scalar[ii]).Transpose(),
407+
)
408+
}
409+
bOut = getKVector.From(b_components_scalar)
410+
}
411+
412+
vector_constraint_matrices_exist := len(A_components_vector) > 0
413+
if vector_constraint_matrices_exist {
414+
// Create the matrix, if it doesn't already exist
415+
if !scalar_constraint_matrices_exist {
416+
AOut = symbolic.DenseToKMatrix(A_components_vector[0])
417+
bOut = symbolic.VecDenseToKVector(b_components_vector[0])
418+
} else {
419+
AOut = symbolic.VStack(
420+
AOut,
421+
symbolic.DenseToKMatrix(A_components_vector[0]),
422+
)
423+
bOut = symbolic.VStack(
424+
bOut,
425+
symbolic.VecDenseToKVector(b_components_vector[0]),
426+
)
427+
}
428+
for ii := 1; ii < len(A_components_vector); ii++ {
429+
AOut = symbolic.VStack(
430+
AOut,
431+
symbolic.DenseToKMatrix(A_components_vector[ii]),
432+
)
433+
bOut = symbolic.VStack(
434+
bOut,
435+
symbolic.VecDenseToKVector(b_components_vector[ii]),
436+
)
437+
}
438+
}
439+
440+
return AOut.(symbolic.KMatrix), bOut.(symbolic.KVector)
441+
}
442+
443+
/*
444+
LinearEqualityConstraintMatrices
445+
Description:
446+
447+
Returns the linear EQUALITY constraint matrices and vectors.
448+
For all linear equality constraints, we assemble them into the form:
449+
Cx = d
450+
Where C is the matrix of coefficients, x is the vector of variables, and d is the vector of constants.
451+
We return C and d.
452+
*/
453+
func (op *OptimizationProblem) LinearEqualityConstraintMatrices() (symbolic.KMatrix, symbolic.KVector) {
454+
// Setup
455+
456+
// Collect the Variables of this Problem
457+
x := op.Variables
458+
459+
// Iterate through all constraints and collect the linear constraints
460+
// into a matrix and vector.
461+
scalar_constraints := make([]symbolic.ScalarConstraint, 0)
462+
vector_constraints := make([]symbolic.VectorConstraint, 0)
463+
for _, constraint := range op.Constraints {
464+
// Skip this constraint if it is not linear
465+
if !constraint.IsLinear() {
466+
continue
467+
}
468+
// Skip this constraint if it is not an equality
469+
if constraint.ConstrSense() != symbolic.SenseEqual {
470+
continue
471+
}
472+
switch c := constraint.(type) {
473+
case symbolic.ScalarConstraint:
474+
scalar_constraints = append(scalar_constraints, c)
475+
case symbolic.VectorConstraint:
476+
vector_constraints = append(vector_constraints, c)
477+
}
478+
}
479+
480+
// Create the matrix and vector elements from the scalar constraints
481+
C_components_scalar := make([]mat.VecDense, len(scalar_constraints))
482+
d_components_scalar := make([]float64, len(scalar_constraints))
483+
for ii, constraint := range scalar_constraints {
484+
C_components_scalar[ii], d_components_scalar[ii] = constraint.LinearEqualityConstraintRepresentation(x)
485+
}
486+
487+
// Create the matrix and vector elements from the vector constraints
488+
C_components_vector := make([]mat.Dense, len(vector_constraints))
489+
d_components_vector := make([]mat.VecDense, len(vector_constraints))
490+
for ii, constraint := range vector_constraints {
491+
C_components_vector[ii], d_components_vector[ii] = constraint.LinearEqualityConstraintRepresentation(x)
492+
}
493+
494+
// Assemble the matrix and vector components
495+
var COut symbolic.Expression
496+
var dOut symbolic.Expression
497+
scalar_constraint_matrices_exist := len(C_components_scalar) > 0
498+
if scalar_constraint_matrices_exist {
499+
COut = symbolic.VecDenseToKVector(C_components_scalar[0]).Transpose()
500+
for ii := 1; ii < len(C_components_scalar); ii++ {
501+
COut = symbolic.VStack(
502+
COut,
503+
symbolic.VecDenseToKVector(C_components_scalar[ii]).Transpose(),
504+
)
505+
}
506+
dOut = getKVector.From(d_components_scalar)
507+
}
508+
vector_constraint_matrices_exist := len(C_components_vector) > 0
509+
510+
if vector_constraint_matrices_exist {
511+
// Create the matrix, if it doesn't already exist
512+
if !scalar_constraint_matrices_exist {
513+
COut = symbolic.DenseToKMatrix(C_components_vector[0])
514+
dOut = symbolic.VecDenseToKVector(d_components_vector[0])
515+
} else {
516+
COut = symbolic.VStack(
517+
COut,
518+
symbolic.DenseToKMatrix(C_components_vector[0]),
519+
)
520+
dOut = symbolic.VStack(
521+
dOut,
522+
symbolic.VecDenseToKVector(d_components_vector[0]),
523+
)
524+
}
525+
for ii := 1; ii < len(C_components_vector); ii++ {
526+
COut = symbolic.VStack(
527+
COut,
528+
symbolic.DenseToKMatrix(C_components_vector[ii]),
529+
)
530+
dOut = symbolic.VStack(
531+
dOut,
532+
symbolic.VecDenseToKVector(d_components_vector[ii]),
533+
)
534+
}
535+
}
536+
return COut.(symbolic.KMatrix), dOut.(symbolic.KVector)
537+
}

0 commit comments

Comments
 (0)