Skip to content

Commit c7ac66e

Browse files
kwesiRutledgeKwesi Rutledge
and
Kwesi Rutledge
authored
Added support for multiplication on several important types of variables (#14)
* Added support for multiplication on several important types of variables * Added a few more tests for coverage of the ConcretizeExpression function --------- Co-authored-by: Kwesi Rutledge <[email protected]>
1 parent 6dbb4af commit c7ac66e

File tree

6 files changed

+269
-6
lines changed

6 files changed

+269
-6
lines changed

symbolic/expression.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ func HStack(eIn ...Expression) Expression {
192192
// TODO: Panic if there are 0 expressions in the input
193193
if len(eIn) == 0 {
194194
panic(
195-
fmt.Errorf("HStack: There must be at least one expression in the input; received 0."),
195+
fmt.Errorf("HStack: There must be at least one expression in the input; received 0"),
196196
)
197197
}
198198

@@ -293,9 +293,9 @@ func ConcretizeExpression(e interface{}) Expression {
293293
var (
294294
concrete Expression
295295
)
296-
switch e.(type) {
296+
switch concreteVal := e.(type) {
297297
case []ScalarExpression:
298-
concreteVectorE := ConcretizeVectorExpression(e.([]ScalarExpression))
298+
concreteVectorE := ConcretizeVectorExpression(concreteVal)
299299
// If vector expression is a scalar (i.e., has 1 row), return the scalar expression
300300
if concreteVectorE.Dims()[0] == 1 {
301301
concrete = concreteVectorE.At(0, 0)
@@ -304,7 +304,7 @@ func ConcretizeExpression(e interface{}) Expression {
304304
}
305305

306306
case [][]ScalarExpression:
307-
concreteMatrixE := ConcretizeMatrixExpression(e.([][]ScalarExpression))
307+
concreteMatrixE := ConcretizeMatrixExpression(concreteVal)
308308
// If matrix expression is a scalar (i.e., has 1 row and 1 column), return the scalar expression
309309
switch {
310310
case concreteMatrixE.Dims()[0] == 1 && concreteMatrixE.Dims()[1] == 1: // If the matrix is a scalar

symbolic/variable_matrix.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,21 @@ func (vm VariableMatrix) Multiply(e interface{}) Expression {
310310
case mat.VecDense:
311311
// Use the KVector case
312312
return vm.Multiply(VecDenseToKVector(right))
313+
case VariableVector:
314+
// Output will be another vector
315+
nVMCols := vm.Dims()[1]
316+
var result Expression = K(0.0)
317+
for ii := 0; ii < nVMCols; ii++ {
318+
// Create a new vector from the ii-th column of the matrix
319+
var vmCol VariableVector
320+
for jj := 0; jj < vm.Dims()[0]; jj++ {
321+
vmCol = append(vmCol, vm[jj][ii])
322+
}
323+
result = result.Plus(
324+
vmCol.Multiply(right[ii]),
325+
)
326+
}
327+
return result
313328
case *mat.Dense:
314329
// Use the mat.Dense case
315330
return vm.Multiply(DenseToKMatrix(*right))

symbolic/variable_vector.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,17 @@ func (vv VariableVector) Multiply(rightIn interface{}) Expression {
288288
mvOut = append(mvOut, v.Multiply(right).(Monomial))
289289
}
290290
return mvOut
291+
case Variable:
292+
// Is output a scalar?
293+
if nResultRows == 1 {
294+
return vv[0].Multiply(right)
295+
}
296+
// Create a new vector of monomials.
297+
var mvOut MonomialVector
298+
for _, v := range vv {
299+
mvOut = append(mvOut, v.Multiply(right).(Monomial))
300+
}
301+
return mvOut
291302
case Monomial:
292303
// Is output a scalar?
293304
if nResultRows == 1 {

testing/symbolic/expression_test.go

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package symbolic_test
22

33
import (
4+
"fmt"
45
"testing"
56

67
"github.com/MatProGo-dev/SymbolicMath.go/smErrors"
@@ -250,6 +251,37 @@ func TestExpression_HStack5(t *testing.T) {
250251
}
251252
}
252253

254+
/*
255+
TestExpression_HStack6
256+
Description:
257+
258+
Tests that the HStack function panics when called with no arguments.
259+
*/
260+
func TestExpression_HStack6(t *testing.T) {
261+
// Test
262+
defer func() {
263+
err := recover().(error)
264+
if err == nil {
265+
t.Errorf("The HStack function should panic when called with no arguments")
266+
}
267+
268+
// Collect the expected error which should be a dimension error and
269+
// compare it with the recovered error
270+
expectedError := fmt.Errorf(
271+
"HStack: There must be at least one expression in the input; received 0",
272+
)
273+
if err.Error() != expectedError.Error() {
274+
t.Errorf(
275+
"Expected the error to be %v; received %v",
276+
expectedError,
277+
err,
278+
)
279+
}
280+
281+
}()
282+
symbolic.HStack()
283+
}
284+
253285
/*
254286
TestExpression_VStack1
255287
Description:
@@ -464,3 +496,139 @@ func TestExpression_VStack6(t *testing.T) {
464496
)
465497
}
466498
}
499+
500+
/*
501+
TestExpression_VStack7
502+
Description:
503+
504+
Tests that the VStack function panics when called with no arguments.
505+
*/
506+
func TestExpression_VStack7(t *testing.T) {
507+
// Test
508+
defer func() {
509+
err := recover().(error)
510+
if err == nil {
511+
t.Errorf("The VStack function should panic when called with no arguments")
512+
}
513+
514+
// Collect the expected error which should be a dimension error and
515+
// compare it with the recovered error
516+
expectedError := fmt.Errorf(
517+
"VStack: There must be at least one expression in the input; received 0",
518+
)
519+
if err.Error() != expectedError.Error() {
520+
t.Errorf(
521+
"Expected the error to be %v; received %v",
522+
expectedError,
523+
err,
524+
)
525+
}
526+
527+
}()
528+
symbolic.VStack()
529+
}
530+
531+
/*
532+
TestExpression_ConcretizeExpression1
533+
Description:
534+
535+
Tests the ConcretizeExpression function for a slice of scalar expressions.
536+
The result should be a monomial matrix with three rows and 1 column1.
537+
*/
538+
func TestExpression_ConcretizeExpression1(t *testing.T) {
539+
// Constants
540+
expressions := []symbolic.ScalarExpression{
541+
symbolic.K(1.0),
542+
symbolic.K(2.0),
543+
symbolic.NewVariable(),
544+
}
545+
546+
// Test
547+
result := symbolic.ConcretizeExpression(expressions)
548+
if result.Dims()[0] != 3 || result.Dims()[1] != 1 {
549+
t.Errorf(
550+
"Expected the result to be a 3x1 matrix; received %v",
551+
result.Dims(),
552+
)
553+
}
554+
555+
// Verify that the result is a monomial matrix
556+
if _, ok := result.(symbolic.MonomialVector); !ok {
557+
t.Errorf(
558+
"Expected the result to be a MonomialVector; received %T",
559+
result,
560+
)
561+
}
562+
}
563+
564+
/*
565+
TestExpression_ConcretizeExpression2
566+
Description:
567+
568+
Tests the ConcretizeExpression function for a slice of scalar expressions.
569+
The slice contains only one scalar expression. This should return a scalar expression.
570+
*/
571+
func TestExpression_ConcretizeExpression2(t *testing.T) {
572+
// Constants
573+
expressions := []symbolic.ScalarExpression{
574+
symbolic.K(1.0),
575+
}
576+
577+
// Test
578+
result := symbolic.ConcretizeExpression(expressions)
579+
if result.Dims()[0] != 1 || result.Dims()[1] != 1 {
580+
t.Errorf(
581+
"Expected the result to be a 1x1 matrix; received %v",
582+
result.Dims(),
583+
)
584+
}
585+
586+
// Verify that the result is a scalar expression
587+
if _, ok := result.(symbolic.K); !ok {
588+
t.Errorf(
589+
"Expected the result to be a K; received %T",
590+
result,
591+
)
592+
}
593+
}
594+
595+
/*
596+
TestExpression_ConcretizeExpression3
597+
Description:
598+
599+
Tests the ConcretizeExpression function for a slice of slices of scalar expressions.
600+
The result should be a monomial matrix with three rows and 2 columns.
601+
*/
602+
func TestExpression_ConcretizeExpression3(t *testing.T) {
603+
// Constants
604+
expressions := [][]symbolic.ScalarExpression{
605+
{
606+
symbolic.K(1.0),
607+
symbolic.K(2.0),
608+
}, {
609+
symbolic.K(3.0),
610+
symbolic.NewVariable(),
611+
},
612+
{
613+
symbolic.K(4.0),
614+
symbolic.NewVariable(),
615+
},
616+
}
617+
618+
// Test
619+
result := symbolic.ConcretizeExpression(expressions)
620+
if result.Dims()[0] != 3 || result.Dims()[1] != 2 {
621+
t.Errorf(
622+
"Expected the result to be a 3x2 matrix; received %v",
623+
result.Dims(),
624+
)
625+
}
626+
627+
// Verify that the result is a monomial matrix
628+
if _, ok := result.(symbolic.MonomialMatrix); !ok {
629+
t.Errorf(
630+
"Expected the result to be a MonomialMatrix; received %T",
631+
result,
632+
)
633+
}
634+
}

testing/symbolic/variable_matrix_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,36 @@ func TestVariableMatrix_Multiply14(t *testing.T) {
10571057
}
10581058
}
10591059

1060+
/*
1061+
TestVariableMatrix_Multiply15
1062+
Description:
1063+
1064+
Tests the Multiply method for a VariableMatrix object that is well-defined
1065+
of a dimension (3, 2) being multiplied by a VariableVector of dimension (2, 1).
1066+
The product should be a PolynomialVector of dimension (3, 1) with two monomials in each polynomial.
1067+
*/
1068+
func TestVariableMatrix_Multiply15(t *testing.T) {
1069+
// Constants
1070+
vm2 := symbolic.NewVariableMatrix(3, 2)
1071+
vv1 := symbolic.NewVariableVector(2)
1072+
1073+
// Compute Product
1074+
result := vm2.Multiply(vv1)
1075+
1076+
// Check that object is a PolynomialVector
1077+
if _, ok := result.(symbolic.PolynomialVector); !ok {
1078+
t.Errorf("Expected Multiply to return a PolynomialVector; received %T", result)
1079+
}
1080+
1081+
// Check that each polynomial in the result contains two monomials.
1082+
pv := result.(symbolic.PolynomialVector)
1083+
for i := 0; i < pv.Dims()[0]; i++ {
1084+
if len(pv[i].Monomials) != 2 {
1085+
t.Errorf("Expected each polynomial to contain 2 monomials; received %v", len(pv[i].Monomials))
1086+
}
1087+
}
1088+
}
1089+
10601090
/*
10611091
TestVariableMatrix_Transpose1
10621092
Description:

testing/symbolic/variable_vector_test.go

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ Description:
77
*/
88

99
import (
10+
"strings"
11+
"testing"
12+
1013
getKMatrix "github.com/MatProGo-dev/SymbolicMath.go/get/KMatrix"
1114
getKVector "github.com/MatProGo-dev/SymbolicMath.go/get/KVector"
1215
"github.com/MatProGo-dev/SymbolicMath.go/smErrors"
1316
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
14-
"strings"
15-
"testing"
1617
)
1718

1819
/*
@@ -1007,8 +1008,46 @@ func TestVariableVector_Multiply11(t *testing.T) {
10071008

10081009
/*
10091010
TestVariableVector_Multiply12
1011+
Description:
10101012
1013+
This test verifies that the multiplication of a well-formed variable vector
1014+
of length 11 with a well-formed variable (scalar) returns a monomial vector.
10111015
*/
1016+
func TestVariableVector_Multiply12(t *testing.T) {
1017+
// Constants
1018+
N := 11
1019+
vv1 := symbolic.NewVariableVector(N)
1020+
v2 := symbolic.NewVariable()
1021+
1022+
// Test
1023+
r := vv1.Multiply(v2)
1024+
if _, ok := r.(symbolic.MonomialVector); !ok {
1025+
t.Errorf(
1026+
"Expected vv1.Multiply(v2) to return a Monomial object; received %T",
1027+
r,
1028+
)
1029+
}
1030+
1031+
// Each element of the monomial vector should contain the variable v2
1032+
rAsMV, _ := r.(symbolic.MonomialVector)
1033+
for ii := 0; ii < N; ii++ {
1034+
monomialII := rAsMV.AtVec(ii).(symbolic.Monomial)
1035+
v2Found := false
1036+
for _, variableJJInII := range monomialII.Variables() {
1037+
if variableJJInII.ID == v2.ID {
1038+
v2Found = true
1039+
}
1040+
}
1041+
if !v2Found {
1042+
t.Errorf(
1043+
"Expected r.AtVec(%v) to contain v2; received %v",
1044+
ii,
1045+
monomialII,
1046+
)
1047+
}
1048+
}
1049+
1050+
}
10121051

10131052
/*
10141053
TestVariableVector_Comparison1

0 commit comments

Comments
 (0)