diff --git a/symbolic/constant.go b/symbolic/constant.go index 6d08e01..ee2485c 100644 --- a/symbolic/constant.go +++ b/symbolic/constant.go @@ -437,3 +437,13 @@ func (c K) At(ii, jj int) ScalarExpression { return c } + +/* +AsSimplifiedExpression +Description: + + Returns the simplest form of the expression. +*/ +func (c K) AsSimplifiedExpression() Expression { + return c +} diff --git a/symbolic/constant_matrix.go b/symbolic/constant_matrix.go index b1da688..4cfa060 100644 --- a/symbolic/constant_matrix.go +++ b/symbolic/constant_matrix.go @@ -224,6 +224,8 @@ func (km KMatrix) Minus(e interface{}) Expression { return km.Minus(DenseToKMatrix(right)) // Reuse KMatrix case case *mat.Dense: return km.Minus(*right) // Reuse mat.Dense case + case Expression: + return km.Plus(right.Multiply(-1.0)) } // If we reach this point, the input is not recognized @@ -371,8 +373,8 @@ func (km KMatrix) Multiply(e interface{}) Expression { case mat.Dense: // Use *mat.Dense method return km.Multiply(&right) // Reuse *mat.Dense case - case KMatrix: - return km.Multiply(right.ToDense()) // Reuse *mat.Dense case + case MatrixExpression: + return MatrixMultiplyTemplate(km, right) } // If we reach this point, the input is not recognized @@ -747,3 +749,14 @@ Description: func (km KMatrix) Power(exponent int) Expression { return MatrixPowerTemplate(km, exponent) } + +/* +AsSimplifiedExpression +Description: + + Simplifies the constant matrix. Since the constant matrix is always in simplest form, + this function simply returns the original constant matrix. +*/ +func (km KMatrix) AsSimplifiedExpression() Expression { + return km +} diff --git a/symbolic/constant_vector.go b/symbolic/constant_vector.go index 63414f2..1a50405 100644 --- a/symbolic/constant_vector.go +++ b/symbolic/constant_vector.go @@ -665,3 +665,13 @@ Description: func (kv KVector) Power(exponent int) Expression { return VectorPowerTemplate(kv, exponent) } + +/* +AsSimplifiedExpression +Description: + + Returns the simplest form of the expression. +*/ +func (kv KVector) AsSimplifiedExpression() Expression { + return kv +} diff --git a/symbolic/expression.go b/symbolic/expression.go index b47b9fd..ec298cb 100644 --- a/symbolic/expression.go +++ b/symbolic/expression.go @@ -70,6 +70,9 @@ type Expression interface { // At returns the value at the given row and column index At(ii, jj int) ScalarExpression + + // Simplify simplifies the expression and returns the simplified version + AsSimplifiedExpression() Expression } /* @@ -294,6 +297,8 @@ func ConcretizeExpression(e interface{}) Expression { concrete Expression ) switch concreteVal := e.(type) { + case ScalarExpression: + concrete = concreteVal.AsSimplifiedExpression() case []ScalarExpression: concreteVectorE := ConcretizeVectorExpression(concreteVal) // If vector expression is a scalar (i.e., has 1 row), return the scalar expression diff --git a/symbolic/matrix_expression.go b/symbolic/matrix_expression.go index ef74937..2edd2c2 100644 --- a/symbolic/matrix_expression.go +++ b/symbolic/matrix_expression.go @@ -79,6 +79,9 @@ type MatrixExpression interface { // Power // Raises the scalar expression to the power of the input integer Power(exponent int) Expression + + // Simplify simplifies the expression and returns the simplified version + AsSimplifiedExpression() Expression } /* @@ -190,6 +193,68 @@ func MatrixPowerTemplate(me MatrixExpression, exponent int) MatrixExpression { return out } +/* +MatrixMultiplyTemplate +Description: + + Template for the matrix multiply function. +*/ +func MatrixMultiplyTemplate(left MatrixExpression, right MatrixExpression) Expression { + // Input Processing + err := left.Check() + if err != nil { + panic(err) + } + + err = right.Check() + if err != nil { + panic(err) + } + + // Check dimensions + leftDims := left.Dims() + rightDims := right.Dims() + + if leftDims[1] != rightDims[0] { + panic( + smErrors.MatrixDimensionError{ + Arg1: left, + Arg2: right, + Operation: "MatrixMultiplyTemplate", + }, + ) + } + + // Algorithm + var out [][]ScalarExpression + for ii := 0; ii < leftDims[0]; ii++ { + var tempRow []ScalarExpression + for jj := 0; jj < rightDims[1]; jj++ { + // Compute the (ii,jj) element of the product + var sum Expression = K(0.0) + for kk := 0; kk < leftDims[1]; kk++ { + sum = sum.Plus(left.At(ii, kk).Multiply(right.At(kk, jj))) + } + sumAsSE, tf := sum.(ScalarExpression) + if !tf { + panic( + fmt.Errorf( + "unexpected expression type in MatrixMultiplyTemplate at entry [%v,%v]: %T", + ii, jj, + sum, + ), + ) + } + tempRow = append(tempRow, sumAsSE) + } + out = append(out, tempRow) + } + + // Use the general concretization function (not the matrix-specific one) + // because it will also convert matrices to scalars or vectors if needed. + return ConcretizeExpression(out) +} + /* MatrixSubstituteTemplate Description: @@ -234,6 +299,7 @@ Description: */ func ConcretizeMatrixExpression(sliceIn [][]ScalarExpression) MatrixExpression { // Input Processing + // - Check that the input slice is not empty if len(sliceIn) == 0 { panic( fmt.Errorf( @@ -242,7 +308,7 @@ func ConcretizeMatrixExpression(sliceIn [][]ScalarExpression) MatrixExpression { ) } - // Check the number of columns in each row + // - Check the number of columns in each row is the same numCols := len(sliceIn[0]) for ii, row := range sliceIn { if len(row) != numCols { diff --git a/symbolic/monomial.go b/symbolic/monomial.go index 07b7eb1..6ab3047 100644 --- a/symbolic/monomial.go +++ b/symbolic/monomial.go @@ -775,3 +775,56 @@ func (m Monomial) At(ii, jj int) ScalarExpression { // Algorithm return m } + +/* +AsSimplifiedExpression +Description: + + Returns the simplest form of the expression. + - If the monomial contains no variables, + then it is simply the constant coefficient. (return K(m.Coefficient)) + - If the monomial coefficient is zero, + then it is simply the constant zero. (return K(0)) + - If the monomial contains variables BUT all exponents are zero, + then it is simply the constant zero. (return K(0)) + - If the monomial's coefficient is 1.0 and it contains one variable with degree 1, + then it is simply that variable. (return that variable) + - Otherwise, return the monomial itself. +*/ +func (m Monomial) AsSimplifiedExpression() Expression { + // Input Processing + err := m.Check() + if err != nil { + panic(err) + } + + // Algorithm + // - If the monomial is a constant, return the constant + if m.IsConstant() { + return K(m.Coefficient) + } + // - If the monomial's coefficient is zero, return zero + if m.Coefficient == 0.0 { + return K(0.0) + } + // - If the monomial's coefficient is 1.0 and it contains one variable with degree 1, + // then return that variable + if (m.Coefficient == 1.0) && (len(m.VariableFactors) == 1) && (m.Exponents[0] == 1) { + return m.VariableFactors[0] + } + + // - If the monomial contains variables BUT all exponents are zero, + // then return zero + allExponentsZero := true + for _, exp := range m.Exponents { + if exp != 0 { + allExponentsZero = false + break + } + } + if allExponentsZero { + return K(m.Coefficient) + } + + return m +} diff --git a/symbolic/monomial_matrix.go b/symbolic/monomial_matrix.go index dbe8f78..f68981e 100644 --- a/symbolic/monomial_matrix.go +++ b/symbolic/monomial_matrix.go @@ -310,30 +310,42 @@ func (mm MonomialMatrix) Multiply(e interface{}) Expression { return product case VariableVector: if nRows == 1 { - // Output will be a polynomial - var product Polynomial + // Output will be a scalar expression + var product Expression = K(0.0) for ii, monomial := range mm[0] { - product.Monomials = append(product.Monomials, monomial.Multiply(right[ii]).(Monomial)) + product = product.Plus(monomial.Multiply(right[ii])) } - return product.Simplify() + return ConcretizeExpression(product) } else { // Output will be a polynomial matrix - var product PolynomialVector + //TODO: Add thorough tests of this. + var product []ScalarExpression for _, row := range mm { - product_ii := row[0].ToPolynomial().Multiply(right[0]).(Polynomial) + product_ii := row[0].ToPolynomial().Multiply(right[0]) for jj := 1; jj < len(row); jj++ { product_ii = product_ii.Plus( row[jj].ToPolynomial().Multiply(right[jj]), ).(Polynomial) } - product = append(product, product_ii) - product = product.Simplify() + // Enforce that product_ii is a ScalarExpression + product_iiAsSE, tf := product_ii.(ScalarExpression) + if !tf { + panic( + fmt.Errorf( + "error converting product row to ScalarExpression; got type %T", + product_ii, + ), + ) + } + product = append(product, product_iiAsSE) } - return product + return ConcretizeVectorExpression(product) } + case MatrixExpression: + return MatrixMultiplyTemplate(mm, right) } // Unrecognized response is a panic @@ -676,3 +688,38 @@ Description: func (mm MonomialMatrix) Power(exponent int) Expression { return MatrixPowerTemplate(mm, exponent) } + +/* +AsSimplifiedExpression +Description: + + Returns the simplest form of the expression. +*/ +func (mm MonomialMatrix) AsSimplifiedExpression() Expression { + // Input Processing + err := mm.Check() + if err != nil { + panic(err) + } + + // Create container for simplified matrix + dims := mm.Dims() + nRows, nCols := dims[0], dims[1] + var simplifiedMM [][]ScalarExpression + for ii := 0; ii < nRows; ii++ { + simplifiedRow := make([]ScalarExpression, nCols) + for jj := 0; jj < nCols; jj++ { + simplified := mm[ii][jj].AsSimplifiedExpression() + simplifiedAsSE, tf := simplified.(ScalarExpression) + if !tf { + panic(fmt.Errorf("error simplifying monomial matrix entry %v,%v", ii, jj)) + } + // Save the converted simplified expression + simplifiedRow[jj] = simplifiedAsSE + } + simplifiedMM = append(simplifiedMM, simplifiedRow) + } + + // Return the simplified matrix + return ConcretizeMatrixExpression(simplifiedMM) +} diff --git a/symbolic/monomial_vector.go b/symbolic/monomial_vector.go index 8022070..c263ff2 100644 --- a/symbolic/monomial_vector.go +++ b/symbolic/monomial_vector.go @@ -731,3 +731,31 @@ Description: func (mv MonomialVector) LinearCoeff(wrt ...[]Variable) mat.Dense { return PolynomialLikeVector_SharedLinearCoeffCalc(mv, wrt...) } + +/* +AsSimplifiedExpression +Description: + + Returns the simplest form of the expression. +*/ +func (mv MonomialVector) AsSimplifiedExpression() Expression { + // Input Processing + err := mv.Check() + if err != nil { + panic(err) + } + + // Simplify each monomial in the vector + var out []ScalarExpression + for ii, monomial := range mv { + simplified := monomial.AsSimplifiedExpression() + simplifiedAsSE, tf := simplified.(ScalarExpression) + if !tf { + panic(fmt.Errorf("error simplifying monomial vector entry %v", ii)) + } + // Add the simplified version of the monomial to the output + out = append(out, simplifiedAsSE) + } + + return ConcretizeVectorExpression(out) +} diff --git a/symbolic/polynomial.go b/symbolic/polynomial.go index 9e2b3a7..1fbc184 100644 --- a/symbolic/polynomial.go +++ b/symbolic/polynomial.go @@ -600,6 +600,10 @@ func (p Polynomial) Simplify() Polynomial { } +func (p Polynomial) AsSimplifiedExpression() Expression { + return p.Simplify() +} + /* DerivativeWrt Description: @@ -794,10 +798,10 @@ func (p Polynomial) Substitute(vIn Variable, eIn ScalarExpression) Expression { var out Expression = K(0.0) for _, monomial := range p.Monomials { newMonomial := monomial.Substitute(vIn, eIn) - out = out.Plus(newMonomial).(Polynomial).Simplify() + out = out.Plus(newMonomial) } - return out + return out.AsSimplifiedExpression() } /* diff --git a/symbolic/polynomial_like.go b/symbolic/polynomial_like.go index 6eb3281..4063579 100644 --- a/symbolic/polynomial_like.go +++ b/symbolic/polynomial_like.go @@ -72,6 +72,9 @@ type PolynomialLike interface { // At returns the value at the given row and column index At(ii, jj int) ScalarExpression + + // Simplify simplifies the expression and returns the simplified version + AsSimplifiedExpression() Expression } /* diff --git a/symbolic/polynomial_like_matrix.go b/symbolic/polynomial_like_matrix.go index 0bb051a..fe21f56 100644 --- a/symbolic/polynomial_like_matrix.go +++ b/symbolic/polynomial_like_matrix.go @@ -80,6 +80,9 @@ type PolynomialLikeMatrix interface { // Power returns the expression raised to the power of the input exponent Power(exponent int) Expression + + // Simplify simplifies the expression and returns the simplified version + AsSimplifiedExpression() Expression } /* diff --git a/symbolic/polynomial_like_scalar.go b/symbolic/polynomial_like_scalar.go index 4fa225f..a6afe45 100644 --- a/symbolic/polynomial_like_scalar.go +++ b/symbolic/polynomial_like_scalar.go @@ -78,6 +78,9 @@ type PolynomialLikeScalar interface { // At returns the value at the given row and column index At(ii, jj int) ScalarExpression + + // Simplify simplifies the expression and returns the simplified version + AsSimplifiedExpression() Expression } /* diff --git a/symbolic/polynomial_like_vector.go b/symbolic/polynomial_like_vector.go index 0cfbe36..f0eb565 100644 --- a/symbolic/polynomial_like_vector.go +++ b/symbolic/polynomial_like_vector.go @@ -97,6 +97,9 @@ type PolynomialLikeVector interface { // Power returns the expression raised to the power of the input exponent Power(exponent int) Expression + + // Simplify simplifies the expression and returns the simplified version + AsSimplifiedExpression() Expression } /* diff --git a/symbolic/polynomial_matrix.go b/symbolic/polynomial_matrix.go index bfa3ea4..379be61 100644 --- a/symbolic/polynomial_matrix.go +++ b/symbolic/polynomial_matrix.go @@ -326,6 +326,8 @@ func (pm PolynomialMatrix) Multiply(e interface{}) Expression { } return product } + case MatrixExpression: + return MatrixMultiplyTemplate(pm, right) } // If type isn't recognized, then panic @@ -557,22 +559,32 @@ Description: Simplifies the polynomial matrix, if possible. */ -func (pm PolynomialMatrix) Simplify() PolynomialMatrix { +func (pm PolynomialMatrix) Simplify() MatrixExpression { // Constants nRows, nCols := pm.Dims()[0], pm.Dims()[1] // Fill container with simplified polynomials - var simplified PolynomialMatrix + var simplified [][]ScalarExpression for rowIndex := 0; rowIndex < nRows; rowIndex++ { - tempRow := make([]Polynomial, nCols) + tempRow := make([]ScalarExpression, nCols) for colIndex := 0; colIndex < nCols; colIndex++ { - tempRow[colIndex] = pm[rowIndex][colIndex].Simplify() + // Simplify the polynomial entry + entry := pm[rowIndex][colIndex] + simplifiedAsSE, tf := entry.AsSimplifiedExpression().(ScalarExpression) + if !tf { + panic(fmt.Errorf("error simplifying polynomial matrix entry %v,%v", rowIndex, colIndex)) + } + tempRow[colIndex] = simplifiedAsSE } simplified = append(simplified, tempRow) } // Return simplified polynomial - return simplified + return ConcretizeMatrixExpression(simplified) +} + +func (pm PolynomialMatrix) AsSimplifiedExpression() Expression { + return pm.Simplify() } /* diff --git a/symbolic/polynomial_vector.go b/symbolic/polynomial_vector.go index 16ccdbf..00ae6ad 100644 --- a/symbolic/polynomial_vector.go +++ b/symbolic/polynomial_vector.go @@ -535,7 +535,7 @@ Description: This method simplifies the polynomial vector. */ -func (pv PolynomialVector) Simplify() PolynomialVector { +func (pv PolynomialVector) Simplify() VectorExpression { // Input Processing err := pv.Check() if err != nil { @@ -545,14 +545,27 @@ func (pv PolynomialVector) Simplify() PolynomialVector { // Constants // Algorithm - var simplified PolynomialVector = make([]Polynomial, pv.Len()) - copy(simplified, pv) + var simplified []ScalarExpression - for ii, polynomial := range simplified { - simplified[ii] = polynomial.Simplify() + for ii, polynomial := range pv { + simplifiedEntryII := polynomial.AsSimplifiedExpression() + entryAsSE, ok := simplifiedEntryII.(ScalarExpression) + if !ok { + panic( + fmt.Errorf( + "error converting polynomial vector entry %v to a scalar expression during simplification", + ii, + ), + ) + } + simplified = append(simplified, entryAsSE) } - return simplified + return ConcretizeVectorExpression(simplified) +} + +func (pv PolynomialVector) AsSimplifiedExpression() Expression { + return pv.Simplify() } /* diff --git a/symbolic/scalar_expression.go b/symbolic/scalar_expression.go index 4a1c447..6a3b7f5 100644 --- a/symbolic/scalar_expression.go +++ b/symbolic/scalar_expression.go @@ -77,6 +77,10 @@ type ScalarExpression interface { // At returns the value at the given row and column index At(ii, jj int) ScalarExpression + + // AsSimplifiedExpression + // Simplifies the expression and returns the simplified version + AsSimplifiedExpression() Expression } // NewExpr returns a new expression with a single additive constant value, c, diff --git a/symbolic/variable.go b/symbolic/variable.go index 5188659..f918511 100644 --- a/symbolic/variable.go +++ b/symbolic/variable.go @@ -665,3 +665,13 @@ func UnionOfVariables(varSlices ...[]Variable) []Variable { } return UniqueVars(allVars) } + +/* +AsSimplifiedExpression +Description: + + Simplifies the expression and returns the simplified version. +*/ +func (v Variable) AsSimplifiedExpression() Expression { + return v +} diff --git a/symbolic/variable_matrix.go b/symbolic/variable_matrix.go index 099dfa7..a83b3eb 100644 --- a/symbolic/variable_matrix.go +++ b/symbolic/variable_matrix.go @@ -331,59 +331,8 @@ func (vm VariableMatrix) Multiply(e interface{}) Expression { case mat.Dense: // Use the KMatrix case return vm.Multiply(DenseToKMatrix(right)) - case KMatrix: - // Collect dimensions - nResultRows, nResultCols := vm.Dims()[0], right.Dims()[1] - - // Switch on the dimensions of the result - switch { - case (nResultRows == 1) && (nResultCols == 1): - // Scalar result - var result Polynomial = K(0).ToMonomial().ToPolynomial() - - for ii, vmRow := range vm { - for jj, vIJ := range vmRow { - result = result.Plus(vIJ.Multiply(right[jj][ii])).(Polynomial) - } - } - return result - case nResultCols == 1: - // Vector result - var result PolynomialVector = VecDenseToKVector(ZerosVector(nResultRows)).ToPolynomialVector() - - for ii, vmRow := range vm { - for jj, vIJ := range vmRow { - result[ii] = result[ii].Plus(vIJ.Multiply(right[jj][0])).(Polynomial) - } - } - - return result - - default: - // Create result - var result PolynomialMatrix - - for ii := 0; ii < nResultRows; ii++ { - var resultRow []Polynomial - for jj := 0; jj < nResultCols; jj++ { - resultRow = append(resultRow, K(0).ToMonomial().ToPolynomial()) - } - result = append(result, resultRow) - } - - // Fill in the elements of the new matrix - for ii := 0; ii < nResultRows; ii++ { - for jj := 0; jj < nResultCols; jj++ { - // Compute Sum - for kk := 0; kk < vm.Dims()[1]; kk++ { - result[ii][jj] = result[ii][jj].Plus( - vm[ii][kk].Multiply(right[kk][jj]), - ).(Polynomial) - } - } - } - return result - } + case MatrixExpression: + return MatrixMultiplyTemplate(vm, right) } // panic if the type is not recognized @@ -765,3 +714,13 @@ Description: func (vm VariableMatrix) Power(exponent int) Expression { return MatrixPowerTemplate(vm, exponent) } + +/* +AsSimplifiedExpression +Description: + + Simplifies the expression and returns the simplified version. +*/ +func (vm VariableMatrix) AsSimplifiedExpression() Expression { + return vm +} diff --git a/symbolic/variable_vector.go b/symbolic/variable_vector.go index 1de2b21..d843da1 100644 --- a/symbolic/variable_vector.go +++ b/symbolic/variable_vector.go @@ -686,3 +686,13 @@ Description: func (vv VariableVector) Power(exponent int) Expression { return VectorPowerTemplate(vv, exponent) } + +/* +AsSimplifiedExpression +Description: + + Simplifies the expression and returns the simplified version. +*/ +func (vv VariableVector) AsSimplifiedExpression() Expression { + return vv +} diff --git a/symbolic/vector_expression.go b/symbolic/vector_expression.go index 4b1309d..bb74bcc 100644 --- a/symbolic/vector_expression.go +++ b/symbolic/vector_expression.go @@ -94,6 +94,9 @@ type VectorExpression interface { // Power returns the expression raised to the power of the input exponent Power(exponent int) Expression + + // Simplify simplifies the expression and returns the simplified version + AsSimplifiedExpression() Expression } ///* diff --git a/testing/symbolic/constant_matrix_test.go b/testing/symbolic/constant_matrix_test.go index eeecf6a..45add5c 100644 --- a/testing/symbolic/constant_matrix_test.go +++ b/testing/symbolic/constant_matrix_test.go @@ -1343,3 +1343,60 @@ func TestKMatrix_Power2(t *testing.T) { } } } + +/* +TestKMatrix_AsSimplifiedExpression1 +Description: + + Tests that the AsSimplifiedExpression() method properly returns the KMatrix itself, + as it is already in simplified form. +*/ +func TestKMatrix_AsSimplifiedExpression1(t *testing.T) { + // Setup + A := getKMatrix.From([][]float64{ + {1, 2, 3}, + {4, 5, 6}, + }) + + // Simplify + simp := A.AsSimplifiedExpression() + + // Verify that the result is the same as the original + if !reflect.DeepEqual(A, simp) { + t.Errorf("Expected simplification to not change anything; got %v", simp) + } +} + +/* +TestKMatrix_Minus1 +Description: + + Tests that the Minus() method properly subtracts zero from a KMatrix. + The result should be the same as the original matrix. +*/ +func TestKMatrix_Minus1(t *testing.T) { + // Setup + A := getKMatrix.From([][]float64{ + {1, -2, 3}, + {-4, 5, -6}, + }) + + Z1 := symbolic.ZerosMatrix(2, 3) + + // Algorithm + diff := A.Minus(Z1) + + diffAsKMatrix, tf := diff.(symbolic.KMatrix) + if !tf { + t.Errorf( + "Expected diff to be KMatrix; received type %T", + diff, + ) + } + + // Check the elements of the two matrices + if !reflect.DeepEqual(A, diffAsKMatrix) { + t.Errorf("The two matrices A and diff are not equal!") + } + +} diff --git a/testing/symbolic/constant_vector_test.go b/testing/symbolic/constant_vector_test.go index 96a5cf8..28ae176 100644 --- a/testing/symbolic/constant_vector_test.go +++ b/testing/symbolic/constant_vector_test.go @@ -8,6 +8,7 @@ Description: import ( "fmt" + "reflect" "strings" "testing" @@ -1284,3 +1285,34 @@ func TestConstantVector_Multiply13(t *testing.T) { ) } } + +/* +TestKVector_AsSimplifiedExpression1 +Description: + + Verifies that the AsSimplifiedExpression method correctly + returns the same KVector when called on a KVector. +*/ +func TestKVector_AsSimplifiedExpression1(t *testing.T) { + // Constants + kv1 := symbolic.VecDenseToKVector(symbolic.OnesVector(3)) + + // Test + simplified := kv1.AsSimplifiedExpression() + + // Check that the simplified expression is a KVector + if _, tf := simplified.(symbolic.KVector); !tf { + t.Errorf( + "Expected simplified to be of type KVector; received %v", + simplified, + ) + } + + // Check that the simplified expression is equal to the original + if !reflect.DeepEqual(simplified, kv1) { + t.Errorf( + "Expected simplified to be equal to kv1; received %v", + simplified, + ) + } +} diff --git a/testing/symbolic/matrix_expression_test.go b/testing/symbolic/matrix_expression_test.go index 34e7577..ca44a5b 100644 --- a/testing/symbolic/matrix_expression_test.go +++ b/testing/symbolic/matrix_expression_test.go @@ -2,10 +2,11 @@ package symbolic_test import ( "fmt" - "github.com/MatProGo-dev/SymbolicMath.go/smErrors" - "github.com/MatProGo-dev/SymbolicMath.go/symbolic" "strings" "testing" + + "github.com/MatProGo-dev/SymbolicMath.go/smErrors" + "github.com/MatProGo-dev/SymbolicMath.go/symbolic" ) /* @@ -413,3 +414,123 @@ func TestMatrixExpression_MatrixSubstituteTemplate3(t *testing.T) { }() symbolic.MatrixSubstituteTemplate(x, v1, m1) } + +/* +TestMatrixExpression_MatrixMultiplyTemplate1 +Description: + + Tests that the matrix multiply template properly panics when called with a MatrixExpression that is not + well-defined (in this case, a MonomialMatrix). +*/ +func TestMatrixExpression_MatrixMultiplyTemplate1(t *testing.T) { + // Setup + m := symbolic.Monomial{ + Coefficient: 1.2, + VariableFactors: []symbolic.Variable{symbolic.NewVariable(), symbolic.NewVariable()}, + Exponents: []int{1}, + } + x := symbolic.MonomialMatrix{ + {m, m}, + {m, m}, + } + y := symbolic.MonomialMatrix{ + {m, m}, + {m, m}, + } + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf("Expected a panic when calling MatrixMultiplyTemplate on a MonomialMatrix; received nil") + } + + rAsE, tf := r.(error) + if !tf { + t.Errorf("Expected the panic to be an error; received %T", r) + } + + if !strings.Contains(rAsE.Error(), m.Check().Error()) { + t.Errorf("Expected the panic to contain the error message %v; received %v", m.Check().Error(), rAsE.Error()) + } + }() + symbolic.MatrixMultiplyTemplate(x, y) +} + +/* +TestMatrixExpression_MatrixMultiplyTemplate2 +Description: + + Tests that the matrix multiply template properly panics when called with a MatrixExpression that is well-defined + but with incompatible dimensions. + In this case, we use two KMatrix objects with incompatible dimensions. + The first KMatrix is 2x3 and the second KMatrix is 2x2. +*/ +func TestMatrixExpression_MatrixMultiplyTemplate2(t *testing.T) { + // Setup + x := symbolic.KMatrix{ + {symbolic.K(1), symbolic.K(2), symbolic.K(3)}, + {symbolic.K(4), symbolic.K(5), symbolic.K(6)}, + } + y := symbolic.KMatrix{ + {symbolic.K(7), symbolic.K(8)}, + {symbolic.K(9), symbolic.K(10)}, + } + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf("Expected a panic when calling MatrixMultiplyTemplate with incompatible dimensions; received nil") + } + + rAsE, tf := r.(error) + if !tf { + t.Errorf("Expected the panic to be an error; received %T", r) + } + + if !strings.Contains(rAsE.Error(), "dimension error") { + t.Errorf("Expected the panic to contain the error message %v; received %v", "incompatible dimensions", rAsE.Error()) + } + }() + symbolic.MatrixMultiplyTemplate(x, y) +} + +/* +TestMatrixExpression_MatrixMultiplyTemplate3 +Description: + + Tests the multiplication of a KMatrix and a VariableMatrix using + the MatrixMultiplyTemplate function. This test should trigger a panic + when the second matrix (the VariableMatrix) is not well-defined. +*/ +func TestMatrixExpression_MatrixMultiplyTemplate3(t *testing.T) { + // Setup + x := symbolic.KMatrix{ + {symbolic.K(1), symbolic.K(2)}, + {symbolic.K(3), symbolic.K(4)}, + } + v := symbolic.Variable{} + y := symbolic.VariableMatrix{ + {v, v}, + {v, v}, + } + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf("Expected a panic when calling MatrixMultiplyTemplate with an invalid VariableMatrix; received nil") + } + + rAsE, tf := r.(error) + if !tf { + t.Errorf("Expected the panic to be an error; received %T", r) + } + + if !strings.Contains(rAsE.Error(), v.Check().Error()) { + t.Errorf("Expected the panic to contain the error message %v; received %v", v.Check().Error(), rAsE.Error()) + } + }() + symbolic.MatrixMultiplyTemplate(x, y) +} diff --git a/testing/symbolic/monomial_matrix_test.go b/testing/symbolic/monomial_matrix_test.go index bfd22d5..2a50486 100644 --- a/testing/symbolic/monomial_matrix_test.go +++ b/testing/symbolic/monomial_matrix_test.go @@ -1305,6 +1305,46 @@ func TestMonomialMatrix_Multiply7(t *testing.T) { mm.Multiply("a") } +/* +TestMonomialMatrix_Multiply8 +Description: + + Tests that the Multiply() method properly multiplies a matrix + of Monomials with a KMatrix. The result should be a matrix of + monomials where each monomial has the scaled coefficients + of the original monomial. +*/ +func TestMonomialMatrix_Multiply8(t *testing.T) { + // Constants + v1 := symbolic.NewVariable() + m1 := v1.ToMonomial() + var mm symbolic.MonomialMatrix = [][]symbolic.Monomial{ + {m1, m1}, + {m1, m1}, + } + km2 := symbolic.DenseToKMatrix(symbolic.OnesMatrix(2, 2)) + + // Test + product := mm.Multiply(km2) + + // Check that the product is of the MonomialMatrix type + productMat, ok := product.(symbolic.PolynomialMatrix) + if !ok { + t.Errorf( + "expected Multiply() to return a PolynomialMatrix; received %v", + product, + ) + } + + // Check that the dimensions of the product are (2,2) + if dims := productMat.Dims(); dims[0] != 2 || dims[1] != 2 { + t.Errorf( + "expected Multiply() to return a PolynomialMatrix with dimensions (2,2); received %v", + dims, + ) + } +} + /* TestMonomialMatrix_Transpose1 Description: @@ -2268,3 +2308,120 @@ func TestMonomialMatrix_SubstituteAccordingTo3(t *testing.T) { mm.SubstituteAccordingTo(testMap) t.Errorf("expected SubstituteAccordingTo() to panic; it did not") } + +/* +TestMonomialMatrix_AsSimplifiedExpression1 +Description: + + Tests that the AsSimplifiedExpression() method properly converts a monomial matrix + of constant monomials to a symbolic.KMatrix. +*/ +func TestMonomialMatrix_AsSimplifiedExpression1(t *testing.T) { + // Constants + var mm symbolic.MonomialMatrix = [][]symbolic.Monomial{ + {symbolic.K(1.0).ToMonomial(), symbolic.K(2.0).ToMonomial()}, + {symbolic.K(3.0).ToMonomial(), symbolic.K(4.0).ToMonomial()}, + } + + // Test + simplified := mm.AsSimplifiedExpression() + + // Check that simplified is a KMatrix + sAsCM, ok := simplified.(symbolic.KMatrix) + if !ok { + t.Errorf( + "expected AsSimplifiedExpression() to return a KMatrix; received %v", + simplified, + ) + } + + // Check that the values in the KMatrix are correct + expectedValues := [][]float64{{1.0, 2.0}, {3.0, 4.0}} + for ii := 0; ii < 2; ii++ { + for jj := 0; jj < 2; jj++ { + if float64(sAsCM.At(ii, jj).(symbolic.K)) != expectedValues[ii][jj] { + t.Errorf( + "expected AsSimplifiedExpression() to return a KMatrix with value %v at (%v,%v); received %v", + expectedValues[ii][jj], + ii, jj, + sAsCM.At(ii, jj), + ) + } + } + } +} + +/* +TestMonomialMatrix_AsSimplifiedExpression2 +Description: + + Tests that the AsSimplifiedExpression() method properly panics when called + with a monomial matrix that is not well-defined. +*/ +func TestMonomialMatrix_AsSimplifiedExpression2(t *testing.T) { + // Constants + var mm symbolic.MonomialMatrix + + // Test + defer func() { + if r := recover(); r == nil { + t.Errorf( + "expected AsSimplifiedExpression() to panic; it did not", + ) + } + }() + + mm.AsSimplifiedExpression() +} + +/* +TestMonomialMatrix_Power1 +Description: + + Tests that the Power() method properly computes the power of a square + monomial matrix that represents all constants. + + In the case the matrix is: + [1 2] + [3 4] + + and the exponent is 2, the result should be: + + [7 10] + [15 22] +*/ +func TestMonomialMatrix_Power1(t *testing.T) { + // Constants + var mm symbolic.MonomialMatrix = [][]symbolic.Monomial{ + {symbolic.K(1.0).ToMonomial(), symbolic.K(2.0).ToMonomial()}, + {symbolic.K(3.0).ToMonomial(), symbolic.K(4.0).ToMonomial()}, + } + exponent := 2 + + // Test + powered := mm.Power(exponent) + + // Check that powered is a KMatrix + pAsCM, ok := powered.(symbolic.KMatrix) + if !ok { + t.Errorf( + "expected Power() to return a KMatrix; received %v", + powered, + ) + } + + // Check that the values in the KMatrix are correct + expectedValues := [][]float64{{7.0, 10.0}, {15.0, 22.0}} + for ii := 0; ii < 2; ii++ { + for jj := 0; jj < 2; jj++ { + if float64(pAsCM.At(ii, jj).(symbolic.K)) != expectedValues[ii][jj] { + t.Errorf( + "expected Power() to return a KMatrix with value %v at (%v,%v); received %v", + expectedValues[ii][jj], + ii, jj, + pAsCM.At(ii, jj), + ) + } + } + } +} diff --git a/testing/symbolic/monomial_test.go b/testing/symbolic/monomial_test.go index 8aebb33..91e257c 100644 --- a/testing/symbolic/monomial_test.go +++ b/testing/symbolic/monomial_test.go @@ -8,10 +8,11 @@ Description: import ( "fmt" - "github.com/MatProGo-dev/SymbolicMath.go/smErrors" - "github.com/MatProGo-dev/SymbolicMath.go/symbolic" "strings" "testing" + + "github.com/MatProGo-dev/SymbolicMath.go/smErrors" + "github.com/MatProGo-dev/SymbolicMath.go/symbolic" ) /* @@ -1668,3 +1669,297 @@ func TestMonomial_String2(t *testing.T) { _ = m1.String() } + +/* +TestMonomial_At1 +Description: + + Verifies that the Monomial.At function returns the proper value + when the monomial is well-defined and the inputs are 0,0. +*/ +func TestMonomial_At1(t *testing.T) { + // Constants + v1 := symbolic.NewVariable() + v2 := symbolic.NewVariable() + m1 := symbolic.Monomial{ + Coefficient: 3.14, + VariableFactors: []symbolic.Variable{v1, v2}, + Exponents: []int{1, 2}, + } + + // Test + val := m1.At(0, 0) + valAsMonomial, tf := val.(symbolic.Monomial) + if !tf { + t.Errorf( + "expected val to be a monomial; received %T", + val, + ) + } + + if valAsMonomial.Coefficient != 3.14 { + t.Errorf( + "expected val coefficient to be %v; received %v", + 3.14, + valAsMonomial.Coefficient, + ) + } +} + +/* +TestMonomial_AsSimplifiedExpression1 +Description: + + Verifies that the Monomial.AsSimplifiedExpression function properly + panics when the monomial is not well-defined. +*/ +func TestMonomial_AsSimplifiedExpression1(t *testing.T) { + // Constants + v1 := symbolic.NewVariable() + m1 := symbolic.Monomial{ + Coefficient: 3.14, + VariableFactors: []symbolic.Variable{v1}, + Exponents: []int{1, 2}, + } + + // Test + defer func() { + if r := recover(); r == nil { + t.Errorf( + "expected AsSimplifiedExpression to panic; received nil", + ) + } + }() + + m1.AsSimplifiedExpression() + t.Errorf("expected panic; received nil") +} + +/* +TestMonomial_AsSimplifiedExpression2 +Description: + + Verifies that the Monomial.AsSimplifiedExpression function properly + returns a monomial expression when the monomial is well-defined + and the variable has a non-zero exponent. +*/ +func TestMonomial_AsSimplifiedExpression2(t *testing.T) { + // Constants + v1 := symbolic.NewVariable() + m1 := symbolic.Monomial{ + Coefficient: 3.14, + VariableFactors: []symbolic.Variable{v1}, + Exponents: []int{2}, + } + + // Compute AsSimplifiedExpression + simplified := m1.AsSimplifiedExpression() + + // Verify that the simplified is a monomial + simplifiedAsM, tf := simplified.(symbolic.Monomial) + if !tf { + t.Errorf( + "expected simplified to be a monomial; received %T", + simplified, + ) + } + + // Verify that the coefficient is the same + if simplifiedAsM.Coefficient != m1.Coefficient { + t.Errorf( + "expected simplified coefficient to be %v; received %v", + m1.Coefficient, + simplifiedAsM.Coefficient, + ) + } + + // Verify that the variable factors are the same + if len(simplifiedAsM.VariableFactors) != len(m1.VariableFactors) { + t.Errorf( + "expected simplified variable factors to be %v; received %v", + m1.VariableFactors, + simplifiedAsM.VariableFactors, + ) + } else { + for i, v := range simplifiedAsM.VariableFactors { + if v != m1.VariableFactors[i] { + t.Errorf( + "expected simplified variable factors to be %v; received %v", + m1.VariableFactors, + simplifiedAsM.VariableFactors, + ) + } + } + } + + // Verify that the exponents are the same + if len(simplifiedAsM.Exponents) != len(m1.Exponents) { + t.Errorf( + "expected simplified exponents to be %v; received %v", + m1.Exponents, + simplifiedAsM.Exponents, + ) + } else { + for i, e := range simplifiedAsM.Exponents { + if e != m1.Exponents[i] { + t.Errorf( + "expected simplified exponents to be %v; received %v", + m1.Exponents, + simplifiedAsM.Exponents, + ) + } + } + } +} + +/* +TestMonomial_AsSimplifiedExpression3 +Description: + + Verifies that the Monomial.AsSimplifiedExpression function properly + returns a constant expression (K) when the monomial is well-defined + and the variable has a zero exponent. +*/ +func TestMonomial_AsSimplifiedExpression3(t *testing.T) { + // Constants + v1 := symbolic.NewVariable() + m1 := symbolic.Monomial{ + Coefficient: 3.14, + VariableFactors: []symbolic.Variable{v1}, + Exponents: []int{0}, + } + + // Compute AsSimplifiedExpression + simplified := m1.AsSimplifiedExpression() + + // Verify that the simplified is a constant (K) + simplifiedAsK, tf := simplified.(symbolic.K) + if !tf { + t.Errorf( + "expected simplified to be a constant; received %T", + simplified, + ) + } + + // Verify that the simplified is a constant + if float64(simplifiedAsK) != 3.14 { + t.Errorf( + "expected simplified to be a constant; received %v", + simplifiedAsK, + ) + } +} + +/* +TestMonomial_AsSimplifiedExpression4 +Description: + + Verifies that the Monomial.AsSimplifiedExpression function properly + returns a constant expression (K) when the monomial is well-defined + and has no variable factors. +*/ +func TestMonomial_AsSimplifiedExpression4(t *testing.T) { + // Constants + m1 := symbolic.Monomial{ + Coefficient: 3.14, + VariableFactors: []symbolic.Variable{}, + Exponents: []int{}, + } + + // Compute AsSimplifiedExpression + simplified := m1.AsSimplifiedExpression() + + // Verify that the simplified is a constant (K) + simplifiedAsK, tf := simplified.(symbolic.K) + if !tf { + t.Errorf( + "expected simplified to be a constant; received %T", + simplified, + ) + } + + // Verify that the simplified is a constant + if float64(simplifiedAsK) != 3.14 { + t.Errorf( + "expected simplified to be a constant; received %v", + simplifiedAsK, + ) + } +} + +/* +TestMonomial_AsSimplifiedExpression5 +Description: + + Verifies that the Monomial.AsSimplifiedExpression function properly + returns a constant expression of zero (K(0)) when the monomial is well-defined + and has a coefficient of zero. +*/ +func TestMonomial_AsSimplifiedExpression5(t *testing.T) { + // Constants + v1 := symbolic.NewVariable() + m1 := symbolic.Monomial{ + Coefficient: 0, + VariableFactors: []symbolic.Variable{v1}, + Exponents: []int{2}, + } + + // Compute AsSimplifiedExpression + simplified := m1.AsSimplifiedExpression() + + // Verify that the simplified is a constant (K) + simplifiedAsK, tf := simplified.(symbolic.K) + if !tf { + t.Errorf( + "expected simplified to be a constant; received %T", + simplified, + ) + } + + // Verify that the simplified is a constant + if float64(simplifiedAsK) != 0 { + t.Errorf( + "expected simplified to be a constant; received %v", + simplifiedAsK, + ) + } +} + +/* +TestMonomial_AsSimplifiedExpression6 +Description: + + Verifies that the Monomial.AsSimplifiedExpression function properly + returns a single variable expression when the monomial is well-defined + and has a coefficient of one and a single variable factor with exponent one. +*/ +func TestMonomial_AsSimplifiedExpression6(t *testing.T) { + // Constants + v1 := symbolic.NewVariable() + m1 := symbolic.Monomial{ + Coefficient: 1, + VariableFactors: []symbolic.Variable{v1}, + Exponents: []int{1}, + } + + // Compute AsSimplifiedExpression + simplified := m1.AsSimplifiedExpression() + + // Verify that the simplified is a variable + simplifiedAsV, tf := simplified.(symbolic.Variable) + if !tf { + t.Errorf( + "expected simplified to be a variable; received %T", + simplified, + ) + } + + // Verify that the simplified is the same variable + if simplifiedAsV != v1 { + t.Errorf( + "expected simplified to be %v; received %v", + v1, + simplifiedAsV, + ) + } +} diff --git a/testing/symbolic/monomial_vector_test.go b/testing/symbolic/monomial_vector_test.go index 63fa3ee..77c080a 100644 --- a/testing/symbolic/monomial_vector_test.go +++ b/testing/symbolic/monomial_vector_test.go @@ -1882,3 +1882,71 @@ func TestMonomialVector_LinearCoeff1(t *testing.T) { t.Errorf("The two matrices are not equal!") } } + +/* +TestMonomialVector_AsSimplifiedExpression1 +Description: + + Verifies that the AsSimplifiedExpression() method properly panics + when called on an improperly initialized MonomialVector. +*/ +func TestMonomialVector_AsSimplifiedExpression1(t *testing.T) { + // Constants + mv := symbolic.MonomialVector{} + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf( + "Expected mv.AsSimplifiedExpression() to panic; received %v", + mv.AsSimplifiedExpression(), + ) + } + }() + + mv.AsSimplifiedExpression() + t.Errorf("Test should panic before this is reached!") +} + +/* +TestMonomialVector_AsSimplifiedExpression2 +Description: + + Verifies that the AsSimplifiedExpression() method properly returns + a constant vector when called on a MonomialVector containing + only constant monomials. +*/ +func TestMonomialVector_AsSimplifiedExpression2(t *testing.T) { + // Constants + mv := symbolic.MonomialVector{ + symbolic.Monomial{Coefficient: 3.14}, + symbolic.Monomial{Coefficient: 2.71}, + symbolic.Monomial{Coefficient: 1.41}, + } + + // Test + se := mv.AsSimplifiedExpression() + + // Verify that the result is a KVector + kv, tf := se.(symbolic.KVector) + if !tf { + t.Errorf( + "expected se to be a KVector; received %T", + se, + ) + } + + // Verify that the elements of the KVector are correct + expectedValues := []float64{3.14, 2.71, 1.41} + for ii, val := range kv { + if float64(val) != expectedValues[ii] { + t.Errorf( + "expected kv[%v] to be %v; received %v", + ii, + expectedValues[ii], + float64(val), + ) + } + } +} diff --git a/testing/symbolic/polynomial_matrix_test.go b/testing/symbolic/polynomial_matrix_test.go index 951b261..a3e0e1d 100644 --- a/testing/symbolic/polynomial_matrix_test.go +++ b/testing/symbolic/polynomial_matrix_test.go @@ -1207,6 +1207,54 @@ func TestPolynomialMatrix_Multiply8(t *testing.T) { pm1.Multiply(vm1) } +/* +TestPolynomialMatrix_Multiply9 +Description: + + Tests that the Multiply() method properly computes the product of a + polynomial matrix (2 x 3) with a constant matrix (3 x 2). + The output should be a polynomial matrix (2 x 2). +*/ +func TestPolynomialMatrix_Multiply9(t *testing.T) { + // Constants + var pm1 symbolic.PolynomialMatrix = [][]symbolic.Polynomial{ + { + symbolic.NewVariable().ToPolynomial(), + symbolic.NewVariable().ToPolynomial(), + symbolic.NewVariable().ToPolynomial(), + }, + { + symbolic.NewVariable().ToPolynomial(), + symbolic.NewVariable().ToPolynomial(), + symbolic.NewVariable().ToPolynomial(), + }, + } + + km1 := getKMatrix.From([][]float64{ + {1.0, 2.0}, + {3.0, 4.0}, + {5.0, 6.0}, + }) + + // Test + pm2 := pm1.Multiply(km1) + + pm2AsPM, tf := pm2.(symbolic.PolynomialMatrix) + if !tf { + t.Errorf( + "expected pm2 to be a PolynomialMatrix; received %v", + pm2, + ) + } + + if pm2AsPM.Dims()[0] != 2 || pm2AsPM.Dims()[1] != 2 { + t.Errorf( + "expected pm2.Dims() to be [2,2]; received %v", + pm2AsPM.Dims(), + ) + } +} + /* TestPolynomialMatrix_Transpose1 Description: @@ -1795,3 +1843,56 @@ func TestPolynomialMatrix_String1(t *testing.T) { } } + +/* +TestPolynomialMatrix_AsSimplifiedExpression1 +Description: + + Tests that the AsSimplifiedExpression() method properly returns + a PolynomialMatrix when a well-defined polynomial matrix + calls it. The result should be a polynomial matrix with + the same dimensions and number of monomials in each polynomial. +*/ +func TestPolynomialMatrix_AsSimplifiedExpression1(t *testing.T) { + // Constants + v1 := symbolic.NewVariable() + p1 := v1.ToPolynomial() + var pm1 symbolic.PolynomialMatrix = [][]symbolic.Polynomial{ + {p1.Minus(3.14).(symbolic.Polynomial), p1.Minus(3.14).(symbolic.Polynomial)}, + {p1.Minus(3.14).(symbolic.Polynomial), p1.Minus(3.14).(symbolic.Polynomial)}, + {p1.Minus(3.14).(symbolic.Polynomial), p1.Minus(3.14).(symbolic.Polynomial)}, + } + + // Test + pm2 := pm1.AsSimplifiedExpression() + + pm2AsPM, tf := pm2.(symbolic.PolynomialMatrix) + if !tf { + t.Errorf( + "expected pm2 to be a PolynomialMatrix; received %v", + pm2, + ) + } + + // Check that the dimensions are the same + if pm1.Dims()[0] != pm2AsPM.Dims()[0] || pm1.Dims()[1] != pm2AsPM.Dims()[1] { + t.Errorf( + "expected pm2.Dims() to be %v; received %v", + pm1.Dims(), + pm2AsPM.Dims(), + ) + } + + // Check that each polynomial in pm2 contains two monomials + for _, pm2Row := range pm2AsPM { + for _, p := range pm2Row { + if len(p.Monomials) != 2 { + t.Errorf( + "expected len(p.Monomials) to be 2; received %v", + len(p.Monomials), + ) + } + } + } + +} diff --git a/testing/symbolic/polynomial_test.go b/testing/symbolic/polynomial_test.go index b87b9a0..cfc3c8b 100644 --- a/testing/symbolic/polynomial_test.go +++ b/testing/symbolic/polynomial_test.go @@ -7,13 +7,14 @@ Description: */ import ( + "reflect" + "strings" + "testing" + getKMatrix "github.com/MatProGo-dev/SymbolicMath.go/get/KMatrix" getKVector "github.com/MatProGo-dev/SymbolicMath.go/get/KVector" "github.com/MatProGo-dev/SymbolicMath.go/smErrors" "github.com/MatProGo-dev/SymbolicMath.go/symbolic" - "reflect" - "strings" - "testing" ) /* @@ -2203,3 +2204,52 @@ func TestPolynomial_Substitute3(t *testing.T) { // Call the Substitute method p1.Substitute(v1, symbolic.NewVariable()) } + +/* +TestPolynomial_SubstituteWith4 +Description: + + Verifies that the Polynomial.Substitute method correctly computes the substitution + when the polynomial is well-defined and the expression used for substitution is well-defined. + We make the polynomial very long and complex to replicate a bug that occurred in one + of the downstream projects. + + p1 = 1 + x1 + x2 + x3 + ... + x20 + substitute x1 with 2. +*/ +func TestPolynomial_SubstituteWith4(t *testing.T) { + // Constants + N := 20 + x := symbolic.NewVariableVector(N) + + // Create a polynomial that is the sum of 1 and all variables in x + sum1 := symbolic.K(1).Plus( + x.Transpose().Multiply(symbolic.OnesVector(N)), + ) + p1, tf := sum1.(symbolic.Polynomial) + if !tf { + t.Errorf( + "expected %v to be a polynomial; received %T", + sum1, + sum1, + ) + } + + // Test + substitution := p1.Substitute(x[0], symbolic.K(2.0)) + + // Search for a constant element in the monomials + for _, m := range substitution.(symbolic.Polynomial).Monomials { + if m.IsConstant() { + if m.Coefficient != 3.0 { + t.Errorf( + "expected (%v).substitute(%v, %v) to have constant 3.0; received %v", + p1, + x[0], + symbolic.K(2.0), + m.Coefficient, + ) + } + } + } +} diff --git a/testing/symbolic/polynomial_vector_test.go b/testing/symbolic/polynomial_vector_test.go index 9ddb461..3eff78e 100644 --- a/testing/symbolic/polynomial_vector_test.go +++ b/testing/symbolic/polynomial_vector_test.go @@ -1953,10 +1953,19 @@ func TestPolynomialVector_Simplify1(t *testing.T) { } // Try to simplify - pvOut := pv.Simplify() + simplified := pv.Simplify() + + // Concretize simplified to polynomial vector + pvOut, ok := simplified.(symbolic.PolynomialVector) + if !ok { + t.Errorf( + "Expected pv.Simplify() to return a PolynomialVector; received %T", + simplified, + ) + } // Check each element of pvOut and verify that it has two monomials. - for _, polynomial := range pvOut { + for _, polynomial := range []symbolic.Polynomial(pvOut) { if len(polynomial.Monomials) != 2 { t.Errorf( "Expected polynomial.Monomials to have length 2; received %v", @@ -1983,10 +1992,59 @@ func TestPolynomialVector_Simplify2(t *testing.T) { } // Try to simplify - pvOut := pv.Simplify() + simplified := pv.Simplify() + + // Concretize simplified to polynomial vector + pvOut, ok := simplified.(symbolic.PolynomialVector) + if !ok { + t.Errorf( + "Expected pv.Simplify() to return a PolynomialVector; received %T", + simplified, + ) + } + + // Check each element of pvOut and verify that it has two monomials. + for _, polynomial := range []symbolic.Polynomial(pvOut) { + if len(polynomial.Monomials) != 2 { + t.Errorf( + "Expected polynomial.Monomials to have length 2; received %v", + len(polynomial.Monomials), + ) + } + } +} + +/* +TestPolynomialVector_AsSimplifiedExpression1 +Description: + + This test verifies that the AsSimplifiedExpression method + returns a well-defined simplified expression when called + on a well-defined polynomial vector. +*/ +func TestPolynomialVector_AsSimplifiedExpression1(t *testing.T) { + // Create a polynomial vector + pv := symbolic.PolynomialVector{} + for ii := 0; ii < 20; ii++ { + pv = append(pv, symbolic.NewVariable().Plus(3.14).Plus(2.17).Plus( + symbolic.Monomial{Coefficient: 1.1}, + ).(symbolic.Polynomial)) + } + + // Test + simplified := pv.AsSimplifiedExpression() + + // Concretize simplified to polynomial vector + pvOut, ok := simplified.(symbolic.PolynomialVector) + if !ok { + t.Errorf( + "Expected pv.AsSimplifiedExpression() to return a PolynomialVector; received %T", + simplified, + ) + } // Check each element of pvOut and verify that it has two monomials. - for _, polynomial := range pvOut { + for _, polynomial := range []symbolic.Polynomial(pvOut) { if len(polynomial.Monomials) != 2 { t.Errorf( "Expected polynomial.Monomials to have length 2; received %v", diff --git a/testing/symbolic/variable_matrix_test.go b/testing/symbolic/variable_matrix_test.go index 3bbd85b..6c1636f 100644 --- a/testing/symbolic/variable_matrix_test.go +++ b/testing/symbolic/variable_matrix_test.go @@ -1087,6 +1087,46 @@ func TestVariableMatrix_Multiply15(t *testing.T) { } } +/* +TestVariableMatrix_Multiply16 +Description: + + Tests that the Multiply method for a VariableMatrix object that is well-defined + with dimension (2,3) properly multiplies a MonomialMatrix with dimension (3,2). + The resulting object should be a PolynomialMatrix with dimension (2,2) + and each polynomial should contain three monomials. +*/ +func TestVariableMatrix_Multiply16(t *testing.T) { + // Constants + vm := symbolic.VariableMatrix{ + {symbolic.NewVariable(), symbolic.NewVariable(), symbolic.NewVariable()}, + {symbolic.NewVariable(), symbolic.NewVariable(), symbolic.NewVariable()}, + } + mm := symbolic.MonomialMatrix{ + {symbolic.NewVariable().ToMonomial(), symbolic.NewVariable().ToMonomial()}, + {symbolic.NewVariable().ToMonomial(), symbolic.NewVariable().ToMonomial()}, + {symbolic.NewVariable().ToMonomial(), symbolic.NewVariable().ToMonomial()}, + } + + // Compute Product + result := vm.Multiply(mm) + + // Check that object is a PolynomialMatrix + if _, ok := result.(symbolic.PolynomialMatrix); !ok { + t.Errorf("Expected Multiply to return a PolynomialMatrix; received %T", result) + } + + // Check that each polynomial in the result contains three monomials. + pv := result.(symbolic.PolynomialMatrix) + for i := 0; i < pv.Dims()[0]; i++ { + for j := 0; j < pv.Dims()[1]; j++ { + if len(pv[i][j].Monomials) != 3 { + t.Errorf("Expected each polynomial to contain 3 monomials; received %v", len(pv[i][j].Monomials)) + } + } + } +} + /* TestVariableMatrix_Transpose1 Description: