diff --git a/examples/derivative1/derivative1.go b/examples/derivative1/derivative1.go new file mode 100644 index 0000000..66e0f62 --- /dev/null +++ b/examples/derivative1/derivative1.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + + "github.com/MatProGo-dev/SymbolicMath.go/symbolic" +) + +/* +derivative1.go +Description: + + This script is meant to construct a simple polynomial + representing an object in free fall within standard + earth gravity, `g`, with an initial upward velocity of `v0`. + + By differentiating the position equation, you should get the + velocity equation. +*/ + +func main() { + // Setup + t := symbolic.NewVariable() + t.Name = "t" + + g := 9.81 // m/s^2 + v0 := 3.0 // m/2^2 + + // Create quadratic function + yPosition := symbolic.K(-0.5 * g).Multiply( + t.Power(2), + ).Plus( + t.Multiply(v0), + ) + + // Create the derivative + yVelocity := yPosition.DerivativeWrt(t) + + // Print the polynomial + fmt.Println(yVelocity.String()) +} diff --git a/examples/substitute1/substitute1.go b/examples/substitute1/substitute1.go new file mode 100644 index 0000000..e60243f --- /dev/null +++ b/examples/substitute1/substitute1.go @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + + getKMatrix "github.com/MatProGo-dev/SymbolicMath.go/get/KMatrix" + "github.com/MatProGo-dev/SymbolicMath.go/symbolic" +) + +/* +substitute1.go +Description: + + This script is meant to show how the substitution + method can be used to evaluate the value of your + expressions. In this case, we will use it to evaluate + a quadratic expression at the point [-3.0, 1.0]. + + The result should be a value of 10.0. +*/ + +func main() { + // Construct quadratic expression + N := 2 + x := symbolic.NewVariableVector(N) + Q := getKMatrix.From( + symbolic.Identity(N)) + + // Create the quadratic polynomial + quadPoly := x.Transpose().Multiply(Q).Multiply(x) + fmt.Println("Polynomial is:", quadPoly) + + // Create the "map" used for substitution + x0 := x[0] + x1 := x[1] + + subMap := map[symbolic.Variable]symbolic.Expression{ + x0: symbolic.K(-3.0), + x1: symbolic.K(1.0), + } + + quadEvaluated := quadPoly.SubstituteAccordingTo(subMap) + + // Print the polynomial + fmt.Println( + "Polynomial at the point x = [", + subMap[x0], + ",", + subMap[x1], + "] is", + quadEvaluated.String()) +} diff --git a/symbolic/constant_matrix.go b/symbolic/constant_matrix.go index 4cfa060..75e5ba1 100644 --- a/symbolic/constant_matrix.go +++ b/symbolic/constant_matrix.go @@ -111,6 +111,8 @@ func (km KMatrix) Plus(e interface{}) Expression { dims := km.Dims() nR, nC := dims[0], dims[1] + // Algorithm + var out Expression switch right := e.(type) { case float64: // Create a matrix of all elements with value right @@ -125,14 +127,13 @@ func (km KMatrix) Plus(e interface{}) Expression { var sumAsDense mat.Dense sumAsDense.Add(&rightAsDense, &kmAsDense) - return DenseToKMatrix(sumAsDense) + out = DenseToKMatrix(sumAsDense) case K: - return km.Plus(float64(right)) // Reuse float64 case + out = km.Plus(float64(right)) // Reuse float64 case case Variable: - // Create a matrix of variables where each element has - // the value of the variable + // Create a matrix of scalar polynomials var rightAsVM VariableMatrix = make([][]Variable, nR) for rIndex := 0; rIndex < nR; rIndex++ { rightAsVM[rIndex] = make([]Variable, nC) @@ -141,47 +142,28 @@ func (km KMatrix) Plus(e interface{}) Expression { } } - return km.Plus(rightAsVM) // Reuse VariableMatrix case + out = km.Plus(rightAsVM) // Reuse VariableMatrix case case mat.Dense: - return km.Plus(DenseToKMatrix(right)) // Reuse KMatrix case + out = km.Plus(DenseToKMatrix(right)) // Reuse KMatrix case case *mat.Dense: - return km.Plus(*right) // Reuse mat.Dense case - - case KMatrix: - // Create the result matrix - var result KMatrix = make([][]K, nR) - for rIndex := 0; rIndex < nR; rIndex++ { - result[rIndex] = make([]K, nC) - for cIndex := 0; cIndex < nC; cIndex++ { - result[rIndex][cIndex] = km[rIndex][cIndex] + right[rIndex][cIndex] - } - } - return result + out = km.Plus(*right) // Reuse mat.Dense case - case VariableMatrix: - // Create the result matrix - var result PolynomialMatrix = make([][]Polynomial, nR) - for rIndex := 0; rIndex < nR; rIndex++ { - result[rIndex] = make([]Polynomial, nC) - for cIndex := 0; cIndex < nC; cIndex++ { - result[rIndex][cIndex] = km[rIndex][cIndex].Plus(right[rIndex][cIndex]).(Polynomial) - // Each addition should create a polynomial - } - } - return result - case PolynomialMatrix: - return right.Plus(km) // Reuse PolynomialMatrix case + case MatrixExpression: + out = MatrixPlusTemplate(km, right) + default: + // If we reach this point, the input is not recognized + panic( + smErrors.UnsupportedInputError{ + FunctionName: "KMatrix.Plus", + Input: e, + }, + ) } - // If we reach this point, the input is not recognized - panic( - smErrors.UnsupportedInputError{ - FunctionName: "KMatrix.Plus", - Input: e, - }, - ) + // Simplify and return + return out.AsSimplifiedExpression() } /* @@ -266,6 +248,8 @@ func (km KMatrix) Multiply(e interface{}) Expression { } } + // Algorithm + var out Expression switch right := e.(type) { case float64: // Use gonum's built-in scale function @@ -273,35 +257,22 @@ func (km KMatrix) Multiply(e interface{}) Expression { var product mat.Dense product.Scale(right, &kmAsDense) - return DenseToKMatrix(product) - + out = DenseToKMatrix(product) case K: - return km.Multiply(float64(right)) // Reuse float64 case + out = km.Multiply(float64(right)) // Reuse float64 case case Polynomial: // Choose the correct output type based on the size of km nR, nC := km.Dims()[0], km.Dims()[1] - switch { - case (nR == 1) && (nC == 1): - // If the output is a scalar, return a scalar - return km[0][0].Multiply(right) - case nC == 1: - // If the output is a vector, return a vector - var outputVec PolynomialVector = make([]Polynomial, nR) - for rIndex := 0; rIndex < nR; rIndex++ { - outputVec[rIndex] = km[rIndex][0].Multiply(right.Copy()).(Polynomial) + var product [][]ScalarExpression + for ii := 0; ii < nR; ii++ { + var productRow []ScalarExpression + for jj := 0; jj < nC; jj++ { + kmAsPoly := km[ii][jj].ToPolynomial() + productRow = append(productRow, kmAsPoly.Multiply(right).(ScalarExpression)) } - return outputVec - default: - // If the output is a matrix, return a matrix - var outputMat PolynomialMatrix = make([][]Polynomial, nR) - for rIndex := 0; rIndex < nR; rIndex++ { - outputMat[rIndex] = make([]Polynomial, nC) - for cIndex := 0; cIndex < nC; cIndex++ { - outputMat[rIndex][cIndex] = km[rIndex][cIndex].Multiply(right.Copy()).(Polynomial) - } - } - return outputMat + product = append(product, productRow) } + out = ConcretizeExpression(product) case *mat.VecDense: // Use gonum's built-in multiplication function @@ -313,10 +284,10 @@ func (km KMatrix) Multiply(e interface{}) Expression { nOutput := product.Len() if nOutput == 1 { // If the output is a scalar, return a scalar - return K(product.AtVec(0)) + out = K(product.AtVec(0)) } else { // Otherwsie return a KVector - return VecDenseToKVector(product) + out = VecDenseToKVector(product) } case VariableVector: @@ -324,27 +295,21 @@ func (km KMatrix) Multiply(e interface{}) Expression { nR := km.Dims()[0] if nR == 1 { // If the output is a scalar, return a scalar - var out Polynomial = K(0).ToPolynomial() + var prod Expression = K(0) for cIndex := 0; cIndex < len(right); cIndex++ { - out = out.Plus( - right[cIndex].Multiply(km[0][cIndex]), - ).(Polynomial) + prod = prod.Plus(right[cIndex].Multiply(km[0][cIndex])) } - return out + out = prod } else { nC := km.Dims()[1] // If the output is a vector, return a vector - var outputVec PolynomialVector = VecDenseToKVector( - ZerosVector(nR), - ).ToPolynomialVector() + var outputVec Expression = VecDenseToKVector(ZerosVector(nR)) for colIndex := 0; colIndex < nC; colIndex++ { kmAsDense := km.ToDense() tempCol := (&kmAsDense).ColView(colIndex) - outputVec = outputVec.Plus( - right[colIndex].Multiply(tempCol), - ).(PolynomialVector) + outputVec = outputVec.Plus(right[colIndex].Multiply(tempCol)) } - return outputVec + out = outputVec } case *mat.Dense: // Check output dimensions @@ -352,38 +317,40 @@ func (km KMatrix) Multiply(e interface{}) Expression { _, nOutputCols := right.Dims() kmAsDense := km.ToDense() - var out mat.Dense - out.Mul(&kmAsDense, right) + var prod mat.Dense + prod.Mul(&kmAsDense, right) switch { case nOutputR == 1 && nOutputCols == 1: // If the constant matrix is a scalar, return the scalar - return K(out.At(0, 0)) + out = K(prod.At(0, 0)) case nOutputCols == 1: // If the output is a vector, return a vector var outputVec KVector = make([]K, nOutputR) for rIndex := 0; rIndex < nOutputR; rIndex++ { - outputVec[rIndex] = K(out.At(rIndex, 0)) + outputVec[rIndex] = K(prod.At(rIndex, 0)) } - return outputVec + out = outputVec default: // If the output is a matrix, return a matrix - return DenseToKMatrix(out) + out = DenseToKMatrix(prod) } case mat.Dense: // Use *mat.Dense method - return km.Multiply(&right) // Reuse *mat.Dense case + out = km.Multiply(&right) // Reuse *mat.Dense case case MatrixExpression: - return MatrixMultiplyTemplate(km, right) + out = MatrixMultiplyTemplate(km, right) + default: + // If we reach this point, the input is not recognized + panic( + smErrors.UnsupportedInputError{ + FunctionName: "KMatrix.Multiply", + Input: e, + }, + ) } - // If we reach this point, the input is not recognized - panic( - smErrors.UnsupportedInputError{ - FunctionName: "KMatrix.Multiply", - Input: e, - }, - ) + return out.AsSimplifiedExpression() } /* diff --git a/symbolic/constant_vector.go b/symbolic/constant_vector.go index 1a50405..ef9d1da 100644 --- a/symbolic/constant_vector.go +++ b/symbolic/constant_vector.go @@ -379,85 +379,71 @@ func (kv KVector) Multiply(rightIn interface{}) Expression { nResultRows := kv.Len() // Compute Multiplication + var out Expression switch right := rightIn.(type) { case float64: - // Is output a vector or a scalar? - if nResultRows == 1 { - return K(float64(kv[0]) * right) - } - - // Use mat.Vector's multiplication method - var result mat.VecDense - kvAsVec := kv.ToVecDense() - result.ScaleVec(right, &kvAsVec) - - return VecDenseToKVector(result) + // If the input is a float64, then use the K method + out = kv.Multiply(K(right)) case K: - // Convert to float64 - eAsFloat := float64(right) + // Iterate through each element + var prod []ScalarExpression + for i := 0; i < nResultRows; i++ { + prod = append(prod, kv[i].Multiply(right).(ScalarExpression)) + } - return kv.Multiply(eAsFloat) + out = ConcretizeExpression(prod) case Variable: - // Is the output a vector or a scalar? - if nResultRows == 1 { - return right.Multiply(kv[0]) + // Iterate through each element + var prod []ScalarExpression + for i := 0; i < nResultRows; i++ { + prod = append(prod, kv[i].Multiply(right).(ScalarExpression)) } - // Create a new monomial vector - var mvOut MonomialVector - for _, element := range kv { - mvOut = append(mvOut, element.Multiply(right).(Monomial)) - } - return mvOut + out = ConcretizeExpression(prod) case Monomial: - // Is the output a vector or a scalar? - if nResultRows == 1 { - return right.Multiply(kv[0]) + // Iterate through each element + var prod []ScalarExpression + for i := 0; i < nResultRows; i++ { + prod = append(prod, kv[i].Multiply(right).(ScalarExpression)) } - // Create a new monomial vector - var mvOut MonomialVector - for _, element := range kv { - mvOut = append(mvOut, element.Multiply(right).(Monomial)) - } - return mvOut + out = ConcretizeExpression(prod) case Polynomial: - // Is the output a vector or a scalar? - if nResultRows == 1 { - return right.Multiply(kv[0]) - } - - // Create a new monomial vector - var pvOut PolynomialVector - for _, element := range kv { - pvOut = append(pvOut, element.Multiply(right).(Polynomial)) + // Iterate through each element + var prod []ScalarExpression + for i := 0; i < nResultRows; i++ { + prod = append(prod, kv[i].Multiply(right).(ScalarExpression)) } - return pvOut + out = ConcretizeExpression(prod) case *mat.VecDense: - return kv.Multiply(*right) + out = kv.Multiply(*right) case mat.VecDense: // If the input is a vector, then use KVector method - return kv.Multiply(VecDenseToKVector(right)) + out = kv.Multiply(VecDenseToKVector(right)) case KVector: // If the input is a KVector and the dimensions match // then right is a (1x1) vector and can use the scalar method. - return kv.Multiply(right[0]) + out = kv.Multiply(right[0]) case VariableVector: // If the input is a KVector and the dimensions match // then right is a (1x1) vector and can use the scalar method. - return kv.Multiply(right[0]) + out = kv.Multiply(right[0]) + + default: + // Panic if the input type is not recognized + panic( + smErrors.UnsupportedInputError{ + FunctionName: "KVector.Multiply", + Input: rightIn, + }, + ) } - // If none of the above input types match, then panic - panic( - smErrors.UnsupportedInputError{ - FunctionName: "KVector.Multiply", - Input: rightIn, - }, - ) + // return + return out.AsSimplifiedExpression() } /* @@ -675,3 +661,17 @@ Description: func (kv KVector) AsSimplifiedExpression() Expression { return kv } + +/* +ToScalarExpressions +Description: + + Converts the KVector into a slice of ScalarExpression type objects. +*/ +func (kv KVector) ToScalarExpressions() []ScalarExpression { + var out []ScalarExpression + for _, k := range kv { + out = append(out, k) + } + return out +} diff --git a/symbolic/matrix_expression.go b/symbolic/matrix_expression.go index 2edd2c2..7ea7f4c 100644 --- a/symbolic/matrix_expression.go +++ b/symbolic/matrix_expression.go @@ -235,7 +235,7 @@ func MatrixMultiplyTemplate(left MatrixExpression, right MatrixExpression) Expre for kk := 0; kk < leftDims[1]; kk++ { sum = sum.Plus(left.At(ii, kk).Multiply(right.At(kk, jj))) } - sumAsSE, tf := sum.(ScalarExpression) + sumAsSE, tf := sum.AsSimplifiedExpression().(ScalarExpression) if !tf { panic( fmt.Errorf( @@ -255,6 +255,51 @@ func MatrixMultiplyTemplate(left MatrixExpression, right MatrixExpression) Expre return ConcretizeExpression(out) } +/* +MatrixPlusTemplate +Description: + + Template for the matrix plus function. +*/ +func MatrixPlusTemplate(left MatrixExpression, right MatrixExpression) MatrixExpression { + // 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[0] != rightDims[0] || leftDims[1] != rightDims[1] { + panic( + smErrors.MatrixDimensionError{ + Arg1: left, + Arg2: right, + Operation: "MatrixPlusTemplate", + }, + ) + } + + // Algorithm + var out [][]ScalarExpression + for ii := 0; ii < leftDims[0]; ii++ { + var tempRow []ScalarExpression + for jj := 0; jj < leftDims[1]; jj++ { + newElt := left.At(ii, jj).Plus(right.At(ii, jj)) + tempRow = append(tempRow, newElt.(ScalarExpression)) + } + out = append(out, tempRow) + } + return ConcretizeMatrixExpression(out) +} + /* MatrixSubstituteTemplate Description: diff --git a/symbolic/monomial.go b/symbolic/monomial.go index 6ab3047..ae7a026 100644 --- a/symbolic/monomial.go +++ b/symbolic/monomial.go @@ -89,45 +89,50 @@ func (m Monomial) Plus(e interface{}) Expression { } // Algorithm + var out Expression switch right := e.(type) { case float64: - return m.Plus(K(right)) + out = m.Plus(K(right)) case K: if m.IsConstant() { - return K(m.Coefficient + float64(right)) + out = K(m.Coefficient + float64(right)) } else { - return Polynomial{ - Monomials: []Monomial{m, right.ToMonomial()}, + out = Polynomial{ + Monomials: []Monomial{m.Copy(), right.ToMonomial()}, } } case Variable: - if m.IsVariable(right) { - mCopy := m + if m.IsDegreeOneContainingVariable(right) { + mCopy := m.Copy() mCopy.Coefficient += 1.0 - return mCopy + out = mCopy } else { - return Polynomial{ - Monomials: []Monomial{m, right.ToMonomial()}, + out = Polynomial{ + Monomials: []Monomial{m.Copy(), right.ToMonomial()}, } } case Monomial: if m.MatchesFormOf(right) { monomialOut := m.Copy() monomialOut.Coefficient += right.Coefficient - return monomialOut + out = monomialOut } else { - return Polynomial{ - Monomials: []Monomial{m, right}, + out = Polynomial{ + Monomials: []Monomial{m.Copy(), right}, } } case Polynomial: - return right.Plus(m) + out = right.Plus(m.Copy()) + default: + panic( + smErrors.UnsupportedInputError{ + FunctionName: "Monomial.Plus", + Input: e, + }, + ) } - // Unrecornized response is a panic - panic( - fmt.Errorf("Unexpected type of right in the Plus() method: %T (%v)", e, e), - ) + return out.AsSimplifiedExpression() } /* @@ -205,7 +210,7 @@ func (m Monomial) Multiply(e interface{}) Expression { return m.Multiply(K(float64(right))) case K: rightAsFloat64 := float64(right) - monomialOut := m + monomialOut := m.Copy() monomialOut.Coefficient *= rightAsFloat64 return monomialOut case Variable: @@ -439,13 +444,30 @@ func (m Monomial) IsConstant() bool { } /* -IsVariable +IsZero +Description: + + Returns true if the monomial defines the constant zero. +*/ +func (m Monomial) IsZero() bool { + // Input Checking + err := m.Check() + if err != nil { + panic(err) + } + + // Algorithm + return m.Coefficient == 0.0 +} + +/* +IsDegreeOneContainingVariable Description: Returns true if the monomial defines an expression containing only the variable v. */ -func (m Monomial) IsVariable(v Variable) bool { +func (m Monomial) IsDegreeOneContainingVariable(v Variable) bool { // Input Checking err := m.Check() if err != nil { @@ -474,6 +496,29 @@ func (m Monomial) IsVariable(v Variable) bool { } } +/* +IsVariable +Description: + + Returns true if the monomial defines the variable v. +*/ +func (m Monomial) IsVariable(v Variable) bool { + // Input Checking + err := m.Check() + if err != nil { + panic(err) + } + + err = v.Check() + if err != nil { + panic(err) + } + + // True, if the monomial contains only the variable v + // and has coefficient 1.0 and degree 1. + return m.IsDegreeOneContainingVariable(v) && (m.Coefficient == 1.0) +} + /* MatchesFormOf Description: @@ -611,6 +656,34 @@ func (m Monomial) ToPolynomial() Polynomial { } } +/* +ToVariable +Description: + + Converts the monomial to a variable if it is a variable. + If the monomial is not a variable, then this function panics. +*/ +func (m Monomial) ToVariable() Variable { + // Input Processing + err := m.Check() + if err != nil { + panic(err) + } + + // Check that VariableFactors is not empty + if len(m.VariableFactors) == 0 { + panic(fmt.Errorf("Cannot convert monomial to variable: VariableFactors is empty")) + } + // Algorithm + if m.IsDegreeOneContainingVariable(m.VariableFactors[0]) { + return m.VariableFactors[0] + } else { + panic( + fmt.Errorf("Can not convert monomial to variable. The monomial is not a variable."), + ) + } +} + /* String Description: diff --git a/symbolic/monomial_vector.go b/symbolic/monomial_vector.go index c263ff2..1563f2a 100644 --- a/symbolic/monomial_vector.go +++ b/symbolic/monomial_vector.go @@ -146,15 +146,16 @@ func (mv MonomialVector) Plus(term1 interface{}) Expression { } // Algorithm + var out Expression switch right := term1.(type) { case float64: - return mv.Plus(K(right)) + out = mv.Plus(K(right)) case K: // Convert the scalar to a scalar vector tempVD := OnesVector(mv.Len()) tempVD.ScaleVec(float64(right), &tempVD) - return mv.Plus(VecDenseToKVector(tempVD)) + out = mv.Plus(VecDenseToKVector(tempVD)) case Monomial: // Check to see if all elements of the monomial vector, // are all monomials like the input monomial. @@ -172,28 +173,28 @@ func (mv MonomialVector) Plus(term1 interface{}) Expression { for _, monomial := range mv { mvOut = append(mvOut, monomial.Plus(right).(Monomial)) } - return mvOut + out = mvOut } else { // Otherwise, create a polynomial vector var pv PolynomialVector for _, monomial := range mv { pv = append(pv, monomial.Plus(right).(Polynomial)) } - return pv.Simplify() + out = pv.Simplify() } case KVector: if mv.IsConstant() { // If monomial vector is really a constant vector, // then don't convert down but simply update the coefficients. var kvOut KVector = VecDenseToKVector(mv.Constant()) - return kvOut.Plus(right) + out = kvOut.Plus(right) } else { // Create a polynomial vector - var pv PolynomialVector + var ve []ScalarExpression for ii, monomial := range mv { - pv = append(pv, monomial.Plus(right[ii]).(Polynomial)) + ve = append(ve, monomial.Plus(right[ii]).(ScalarExpression)) } - return pv.Simplify() + out = ConcretizeVectorExpression(ve) } case MonomialVector: // Check to see if all elements of the monomial vector, @@ -212,7 +213,7 @@ func (mv MonomialVector) Plus(term1 interface{}) Expression { for ii, monomial := range mv { mvOut = append(mvOut, monomial.Plus(right[ii]).(Monomial)) } - return mvOut + out = mvOut } else { // Otherwise, create a polynomial vector var pv PolynomialVector @@ -233,17 +234,20 @@ func (mv MonomialVector) Plus(term1 interface{}) Expression { } } - return pv.Simplify() + out = pv.Simplify() } + default: + // If the right hand side is an unsupported type, then panic + panic( + smErrors.UnsupportedInputError{ + FunctionName: "MonomialVector.Plus", + Input: term1, + }, + ) } - // Unrecognized response is a panic - panic( - smErrors.UnsupportedInputError{ - FunctionName: "MonomialVector.Plus", - Input: term1, - }, - ) + // Return + return out.AsSimplifiedExpression() } /* @@ -325,22 +329,24 @@ func (mv MonomialVector) Multiply(term1 interface{}) Expression { } // Algorithm + var out Expression switch right := term1.(type) { case float64: - return mv.Multiply(K(right)) - case K: - // Create a polynomial vector - var mv MonomialVector - for _, monomial := range mv { - mv = append(mv, monomial.Multiply(right).(Monomial)) - } - return mv + out = mv.Multiply(K(right)) + case Expression: + out = VectorMultiplyTemplate(mv, right) + default: + // Unrecognized response is a panic + panic( + smErrors.UnsupportedInputError{ + FunctionName: "MonomialVector.Multiply", + Input: right, + }, + ) } - // Unrecognized response is a panic - panic( - fmt.Errorf("Unexpected type of term1 in the Multiply() method: %T (%v)", term1, term1), - ) + return out.AsSimplifiedExpression() + } /* @@ -759,3 +765,17 @@ func (mv MonomialVector) AsSimplifiedExpression() Expression { return ConcretizeVectorExpression(out) } + +/* +ToScalarExpressions +Description: + + Converts the MonomialVector into a slice of ScalarExpression type objects. +*/ +func (mv MonomialVector) ToScalarExpressions() []ScalarExpression { + var out []ScalarExpression + for _, monomial := range mv { + out = append(out, monomial) + } + return out +} diff --git a/symbolic/polynomial.go b/symbolic/polynomial.go index 1fbc184..3afc27f 100644 --- a/symbolic/polynomial.go +++ b/symbolic/polynomial.go @@ -2,6 +2,7 @@ package symbolic import ( "fmt" + "math" "github.com/MatProGo-dev/SymbolicMath.go/smErrors" "gonum.org/v1/gonum/mat" @@ -111,9 +112,10 @@ func (p Polynomial) Plus(e interface{}) Expression { } // Constants + var out Expression switch right := e.(type) { case float64: - return p.Plus(K(right)) + out = p.Plus(K(right)) case K: pCopy := p.Copy() @@ -131,7 +133,7 @@ func (p Polynomial) Plus(e interface{}) Expression { newMonomial.Coefficient += float64(right) pCopy.Monomials[constantIndex] = newMonomial } - return pCopy + out = pCopy case Variable: pCopy := p.Copy() @@ -151,30 +153,28 @@ func (p Polynomial) Plus(e interface{}) Expression { pCopy.Monomials[variableIndex] = newMonomial } - return pCopy + out = pCopy case Monomial: - return p.Plus(right.ToPolynomial()) - + out = p.Copy().Plus(right.ToPolynomial()) case Polynomial: pCopy := p.Copy() // Combine the list of monomials. pCopy.Monomials = append(pCopy.Monomials, right.Monomials...) - // Simplify? - return pCopy.Simplify() + out = pCopy.AsSimplifiedExpression() case KVector, VariableVector, MonomialVector, PolynomialVector: ve, _ := ToVectorExpression(right) if ve.Len() == 1 { - return p.Plus(ve.AtVec(0)) // Reuse scalar case + out = p.Plus(ve.AtVec(0)) // Reuse scalar case } else { // Return a polynomial vector - var polVecOut PolynomialVector + var vecExpression []ScalarExpression for ii := 0; ii < ve.Len(); ii++ { - polVecOut = append(polVecOut, p.Plus(ve.AtVec(ii)).(Polynomial)) + vecExpression = append(vecExpression, p.Plus(ve.AtVec(ii)).(ScalarExpression)) } - return polVecOut + out = ConcretizeExpression(vecExpression) } case KMatrix, VariableMatrix, MonomialMatrix, PolynomialMatrix: @@ -186,28 +186,31 @@ func (p Polynomial) Plus(e interface{}) Expression { switch { case nResultRows == 1 && nResultCols == 1: - return p.Plus(rightAsME.At(0, 0)) // Reuse scalar case + out = p.Plus(rightAsME.At(0, 0)) // Reuse scalar case default: - // Return a polynomial matrix - var polMatOut PolynomialMatrix + // Return a matrix expression using a template + var polMatOut [][]ScalarExpression for ii := 0; ii < nResultRows; ii++ { - var polRowOut []Polynomial + var polRowOut []ScalarExpression for jj := 0; jj < nResultCols; jj++ { - polRowOut = append(polRowOut, p.Plus(rightAsME.At(ii, jj)).(Polynomial)) + polRowOut = append(polRowOut, p.Plus(rightAsME.At(ii, jj)).(ScalarExpression)) } polMatOut = append(polMatOut, polRowOut) } - return polMatOut + out = ConcretizeExpression(polMatOut) } + default: + // Unrecognized response is a panic + panic( + smErrors.UnsupportedInputError{ + FunctionName: "Polynomial.Plus", + Input: e, + }, + ) } - // Unrecognized response is a panic - panic( - smErrors.UnsupportedInputError{ - FunctionName: "Polynomial.Plus", - Input: e, - }, - ) + // Return + return out.AsSimplifiedExpression() } /* @@ -295,7 +298,7 @@ func (p Polynomial) VariableMonomialIndex(vIn Variable) int { // Algorithm for ii, monomial := range p.Monomials { - if monomial.IsVariable(vIn) { + if monomial.IsDegreeOneContainingVariable(vIn) { return ii } } @@ -361,33 +364,34 @@ func (p Polynomial) Multiply(e interface{}) Expression { } // Algorithm + var out Expression switch right := e.(type) { case float64: - return p.Multiply(K(right)) + out = p.Multiply(K(right)) case K: pCopy := p.Copy() for ii, _ := range pCopy.Monomials { product_ii := pCopy.Monomials[ii].Multiply(right) pCopy.Monomials[ii] = product_ii.(Monomial) // Convert to Monomial } - return pCopy + out = pCopy case Variable: pCopy := p.Copy() for ii, _ := range pCopy.Monomials { product_ii := pCopy.Monomials[ii].Multiply(right) pCopy.Monomials[ii] = product_ii.(Monomial) // Convert to Monomial } - return pCopy + out = pCopy case Monomial: pCopy := p.Copy() - var out Polynomial + var prod Polynomial for _, m := range pCopy.Monomials { - out.Monomials = append( - out.Monomials, + prod.Monomials = append( + prod.Monomials, m.Multiply(right).(Monomial), ) } - return out + out = prod case Polynomial: pCopy := p.Copy() @@ -400,21 +404,27 @@ func (p Polynomial) Multiply(e interface{}) Expression { ) } - return productOut.(Polynomial).Simplify() + out = productOut case KVector, VariableVector, MonomialVector, PolynomialVector: // Right must be a vector of length 1 ve, _ := ToVectorExpression(right) - return ve.Multiply(p) // Reuse scalar case + out = ve.Multiply(p) // Reuse scalar case case KMatrix, VariableMatrix, MonomialMatrix, PolynomialMatrix: // Right must be a matrix of size [1,1] me, _ := ToMatrixExpression(right) - return me.Multiply(p) // Reuse scalar case + out = me.Multiply(p) // Reuse scalar case + default: + // Unrecognized response is a panic + panic( + smErrors.UnsupportedInputError{ + FunctionName: "Polynomial.Multiply", + Input: e, + }, + ) } - // Unrecognized response is a panic - panic( - fmt.Errorf("Unexpected type of right in the Multiply() method: %T (%v)", e, e), - ) + // Return + return out.AsSimplifiedExpression() } /* @@ -553,46 +563,52 @@ func (p Polynomial) Simplify() Polynomial { panic(err) } - // Find first element that has nonzero coefficient - firstNonZeroIndex := -1 - for ii, monomial := range p.Monomials { - if monomial.Coefficient != 0.0 { - firstNonZeroIndex = ii - break - } + // Create containers for constant monomials and non-constant monomials + // and then combine them. + constantMonomials := Monomial{ + Coefficient: 0.0, } - - if len(p.Monomials) == 0 || firstNonZeroIndex == -1 { - // If there is only a single, zero monomial, then return the zero polynomial - return p + nonConstantMonomials := []Monomial{} + for _, monomial := range p.Monomials { + if monomial.IsConstant() { + constantMonomials.Coefficient += monomial.Coefficient + } else { + // Check to see if the monomial is already in the slice of non-constant monomials + monomialIndex := -1 + for ii, existingMonomial := range nonConstantMonomials { + if existingMonomial.MatchesFormOf(monomial) { + monomialIndex = ii + break + } + } + // If the monomial is already in the slice, then add to its coefficient + if monomialIndex != -1 { + existingMonomial := nonConstantMonomials[monomialIndex] + existingMonomial.Coefficient += monomial.Coefficient + nonConstantMonomials[monomialIndex] = existingMonomial + continue + } + // Otherwise, add the monomial to the slice of non-constant monomials + nonConstantMonomials = append(nonConstantMonomials, monomial) + } } + // If the constant monomial is not zero and there are no other monomials, + // then return just the constant monomial as a constant - // Copy the first element of the polynomial into the new polynomial - pCopy := Polynomial{ - Monomials: []Monomial{p.Monomials[firstNonZeroIndex]}, + // Create the output polynomial + var pCopy Polynomial + if constantMonomials.Coefficient != 0.0 { + pCopy.Monomials = append(pCopy.Monomials, constantMonomials) } - - // Loop through the rest of the monomials - for ii := firstNonZeroIndex + 1; ii < len(p.Monomials); ii++ { - // Check to see if the monomials coefficient is zero - if p.Monomials[ii].Coefficient == 0.0 { - // Don't add it. - continue + for _, monomial := range nonConstantMonomials { + if monomial.Coefficient != 0.0 { + pCopy.Monomials = append(pCopy.Monomials, monomial) } + } - // Check to see if the monomial is already in the polynomial - monomialIndex := pCopy.MonomialIndex(p.Monomials[ii]) - if monomialIndex == -1 { - // Polynomial does not contain the monomial, - // so add a new monomial. - pCopy.Monomials = append(pCopy.Monomials, p.Monomials[ii]) - } else { - // Monomial does contain the variable, so - // modify the monomial which represents that variable. - newMonomial := pCopy.Monomials[monomialIndex] - newMonomial.Coefficient += p.Monomials[ii].Coefficient - pCopy.Monomials[monomialIndex] = newMonomial - } + // If the polynomial is empty, then return a zero polynomial + if len(pCopy.Monomials) == 0 { + pCopy.Monomials = append(pCopy.Monomials, K(0.0).ToMonomial()) } // Return the simplified polynomial @@ -601,7 +617,28 @@ func (p Polynomial) Simplify() Polynomial { } func (p Polynomial) AsSimplifiedExpression() Expression { - return p.Simplify() + // Simplify the polynomial + pReduced := p.Simplify() + + // Create switch based on degree of the polynomial + switch pReduced.Degree() { + case 0: + // If the polynomial is a constant, then return the constant + return K(pReduced.Constant()) + case 1: + // If the polynomial is linear, then check to see if it is just a variable + if len(pReduced.Monomials) == 1 && pReduced.Monomials[0].IsVariable(pReduced.Variables()[0]) { + return pReduced.Monomials[0].ToVariable() + } + } + + // Otherwise, check the number of monomials + // if there is only one, then return that monomial + if len(pReduced.Monomials) == 1 { + return pReduced.Monomials[0] + } + + return pReduced } /* @@ -761,10 +798,26 @@ func (p Polynomial) String() string { // Add monomials for ii, monomial := range p.Monomials { - if ii != 0 { - polynomialString += " + " + if ii == 0 { + polynomialString += monomial.String() + } else { + var nextOperator string + if monomial.Coefficient >= 0 { + nextOperator = "+" + } else { + nextOperator = "-" + } + + // Add next operator to polynomial string + polynomialString += fmt.Sprintf(" %v ", nextOperator) + + // Include the monomial string with the "absolute value coefficient" + absValMonom := monomial.Copy() + absValMonom.Coefficient = math.Abs(absValMonom.Coefficient) + + polynomialString += absValMonom.String() } - polynomialString += monomial.String() + } // Return diff --git a/symbolic/polynomial_matrix.go b/symbolic/polynomial_matrix.go index 379be61..0d7dfd7 100644 --- a/symbolic/polynomial_matrix.go +++ b/symbolic/polynomial_matrix.go @@ -135,71 +135,74 @@ func (pm PolynomialMatrix) Plus(e interface{}) Expression { } // Perform the addition + var out Expression switch right := e.(type) { case float64: - return pm.Plus(K(right)) + out = pm.Plus(K(right)) case K: // Create containers - var sum PolynomialMatrix + var sum [][]ScalarExpression for _, row := range pm { - var sumRow []Polynomial + var sumRow []ScalarExpression for _, polynomial := range row { - sumRow = append(sumRow, polynomial.Plus(right).(Polynomial)) + sumRow = append(sumRow, polynomial.Plus(right).(ScalarExpression)) } sum = append(sum, sumRow) } - return sum + out = ConcretizeExpression(sum) case Monomial: - return pm.Plus(right.ToPolynomial()) + out = pm.Plus(right.ToPolynomial()) case Polynomial: // Create containers - var sum PolynomialMatrix + var sum [][]ScalarExpression for _, row := range pm { - var sumRow []Polynomial + var sumRow []ScalarExpression for _, polynomial := range row { - sumRow = append(sumRow, polynomial.Plus(right).(Polynomial)) + sumRow = append(sumRow, polynomial.Plus(right).(ScalarExpression)) } sum = append(sum, sumRow) } - return sum + out = ConcretizeExpression(sum) case KMatrix: // Create containers - var sum PolynomialMatrix + var sum [][]ScalarExpression for ii, row := range pm { - var sumRow []Polynomial + var sumRow []ScalarExpression for jj, polynomial := range row { - sumRow = append(sumRow, polynomial.Plus(right.At(ii, jj).(K)).(Polynomial)) + sumRow = append(sumRow, polynomial.Plus(right.At(ii, jj).(K)).(ScalarExpression)) } sum = append(sum, sumRow) } - - return sum + out = ConcretizeExpression(sum) case PolynomialMatrix: // Create containers - var sum PolynomialMatrix + var sum [][]ScalarExpression for ii, row := range pm { - var sumRow []Polynomial + var sumRow []ScalarExpression for jj, polynomial := range row { - sumRow = append(sumRow, polynomial.Plus(right[ii][jj]).(Polynomial)) + sumRow = append(sumRow, polynomial.Plus(right[ii][jj]).(ScalarExpression)) } sum = append(sum, sumRow) } - return sum.Simplify() + out = ConcretizeExpression(sum) + default: + // If the right hand side is not supported, then panic + panic( + smErrors.UnsupportedInputError{ + FunctionName: "PolynomialMatrix.Plus", + Input: e, + }, + ) } - // If type isn't recognized, then panic - panic( - smErrors.UnsupportedInputError{ - FunctionName: "PolynomialMatrix.Plus", - Input: e, - }, - ) + // Return + return out.AsSimplifiedExpression() } /* @@ -278,23 +281,24 @@ func (pm PolynomialMatrix) Multiply(e interface{}) Expression { } // Perform the multiplication + var out Expression switch right := e.(type) { case float64: - return pm.Multiply(K(right)) + out = pm.Multiply(K(right)) case K: // Create containers - var product PolynomialMatrix + var product [][]ScalarExpression for _, row := range pm { - var productRow []Polynomial + var productRow []ScalarExpression for _, polynomial := range row { polynomialCopy := Polynomial{Monomials: make([]Monomial, len(polynomial.Monomials))} copy(polynomialCopy.Monomials, polynomial.Monomials) - productRow = append(productRow, polynomialCopy.Multiply(right).(Polynomial)) + productRow = append(productRow, polynomialCopy.Multiply(right).(ScalarExpression)) } product = append(product, productRow) } - return product + out = ConcretizeExpression(product) case VariableVector: // Identify output dimensions nResultRows := pm.Dims()[0] @@ -302,41 +306,39 @@ func (pm PolynomialMatrix) Multiply(e interface{}) Expression { // Create product based on the number of Resulting rows if nResultRows == 1 { // Create container - var product Polynomial = K(0).ToPolynomial() + var product Expression = K(0) for ii, tempPolynomial := range pm[0] { - product = product.Plus( - tempPolynomial.Multiply(right[ii]).(Polynomial), - ).(Polynomial) + product = product.Plus(tempPolynomial.Multiply(right[ii])) } - return product + out = product } else { // Create container - var product PolynomialVector = VecDenseToKVector( - ZerosVector(nResultRows), - ).ToPolynomialVector() + var product []ScalarExpression // Fill container for ii := 0; ii < nResultRows; ii++ { // Construct the ii-th element of the product + product = append(product, K(0)) for jj, polynomial := range pm[ii] { - product[ii] = product[ii].Plus( - polynomial.Multiply(right[jj]).(Polynomial), - ).(Polynomial) + product[ii] = product[ii].Plus(polynomial.Multiply(right[jj])).(ScalarExpression) } } - return product + return ConcretizeExpression(product) } case MatrixExpression: return MatrixMultiplyTemplate(pm, right) + default: + // If the right hand side is not supported, then panic + panic( + smErrors.UnsupportedInputError{ + FunctionName: "PolynomialMatrix.Multiply", + Input: e, + }, + ) } - // If type isn't recognized, then panic - panic( - smErrors.UnsupportedInputError{ - FunctionName: "PolynomialMatrix.Multiply", - Input: e, - }, - ) + // Return + return out.AsSimplifiedExpression() } /* diff --git a/symbolic/polynomial_vector.go b/symbolic/polynomial_vector.go index 00ae6ad..df2cb85 100644 --- a/symbolic/polynomial_vector.go +++ b/symbolic/polynomial_vector.go @@ -192,24 +192,27 @@ func (pv PolynomialVector) Plus(e interface{}) Expression { } // Constants + var out Expression switch right := e.(type) { case float64: - return pv.Plus(K(right)) + out = pv.Plus(K(right)) case K: pvCopy := pv - for ii, polynomial := range pv { - sum := polynomial.Plus(right) - pvCopy[ii] = sum.(Polynomial) + // Algorithm + var sum []ScalarExpression + for _, polynomial := range pvCopy { + tempSum := polynomial.Plus(right) + sum = append(sum, tempSum.(ScalarExpression)) } - return pvCopy + out = ConcretizeExpression(sum) case Variable: pvCopy := pv for ii, polynomial := range pv { sum := polynomial.Plus(right) pvCopy[ii] = sum.(Polynomial) } - return pvCopy + out = pvCopy case Polynomial: pvCopy := pv @@ -219,28 +222,33 @@ func (pv PolynomialVector) Plus(e interface{}) Expression { sum := polynomial.Plus(right) pvCopy[ii] = sum.(Polynomial) } - return pvCopy - case KVector, VariableVector, MonomialVector, PolynomialVector: + out = pvCopy + case VectorExpression: pvCopy := pv // Cast right rightAsVector, _ := ToVectorExpression(right) // Algorithm - for ii, polynomial := range pv { - sum := polynomial.Plus(rightAsVector.AtVec(ii)) - pvCopy[ii] = sum.(Polynomial) + var sum []ScalarExpression + for ii, polynomial := range pvCopy { + tempSum := polynomial.Plus(rightAsVector.AtVec(ii)) + sum = append(sum, tempSum.(ScalarExpression)) } - return pvCopy.Simplify() + out = ConcretizeExpression(sum) + default: + // Default response is a panic + panic( + smErrors.UnsupportedInputError{ + FunctionName: "PolynomialVector.Plus", + Input: e, + }, + ) } - // Default response is a panic - panic( - smErrors.UnsupportedInputError{ - FunctionName: "PolynomialVector.Plus", - Input: e, - }, - ) + // Simplify and return + return out.AsSimplifiedExpression() + } /* @@ -312,29 +320,30 @@ func (pv PolynomialVector) Multiply(rightIn interface{}) Expression { } // Constants + var out Expression switch right := rightIn.(type) { case float64: - return pv.Multiply(K(right)) + out = pv.Multiply(K(right)) case K: pvCopy := pv - for ii, polynomial := range pv { - product := polynomial.Multiply(right) - pvCopy[ii] = product.(Polynomial) + var product []ScalarExpression + for _, polynomial := range pvCopy { + product = append(product, polynomial.Multiply(right).(ScalarExpression)) } - return pvCopy + out = ConcretizeExpression(product) case Polynomial: pvCopy := pv - for ii, polynomial := range pv { - product := polynomial.Multiply(right) - pvCopy[ii] = product.(Polynomial) + var product []ScalarExpression + for _, polynomial := range pvCopy { + product = append(product, polynomial.Multiply(right).(ScalarExpression)) } - return pvCopy + out = ConcretizeExpression(product) case PolynomialVector: // This should only be true if the polynomial vector is actually a polynomial. // Convert it to a polynomial and do the multiplication as if it was with just the scalar. - return pv.Multiply(right[0]) + out = pv.Multiply(right[0]) default: panic( @@ -344,6 +353,8 @@ func (pv PolynomialVector) Multiply(rightIn interface{}) Expression { }, ) } + + return out.AsSimplifiedExpression() } /* @@ -667,3 +678,17 @@ Description: func (pv PolynomialVector) Power(exponent int) Expression { return VectorPowerTemplate(pv, exponent) } + +/* +ToScalarExpressions +Description: + + Converts the PolynomialVector into a slice of ScalarExpression type objects. +*/ +func (pv PolynomialVector) ToScalarExpressions() []ScalarExpression { + var out []ScalarExpression + for _, polynomial := range pv { + out = append(out, polynomial) + } + return out +} diff --git a/symbolic/variable.go b/symbolic/variable.go index f918511..adcc3c7 100644 --- a/symbolic/variable.go +++ b/symbolic/variable.go @@ -86,13 +86,14 @@ func (v Variable) Plus(rightIn interface{}) Expression { } // Algorithm + var out Expression switch right := rightIn.(type) { case float64: - return v.Plus(K(right)) + out = v.Plus(K(right)) case int: - return v.Plus(K(float64(right))) + out = v.Plus(K(float64(right))) case K: - return Polynomial{ + out = Polynomial{ Monomials: []Monomial{ v.ToMonomial(), right.ToMonomial(), @@ -100,17 +101,13 @@ func (v Variable) Plus(rightIn interface{}) Expression { } case Variable: if v.ID == right.ID { - return Polynomial{ - Monomials: []Monomial{ - Monomial{ - Coefficient: 2.0, - VariableFactors: []Variable{v}, - Exponents: []int{1}, - }, - }, + out = Monomial{ + Coefficient: 2.0, + VariableFactors: []Variable{v}, + Exponents: []int{1}, } } else { - return Polynomial{ + out = Polynomial{ Monomials: []Monomial{ v.ToMonomial(), right.ToMonomial(), @@ -118,22 +115,28 @@ func (v Variable) Plus(rightIn interface{}) Expression { } } case Monomial: - return right.Plus(v) + out = right.Plus(v) case Polynomial: - return right.Plus(v) + out = right.Plus(v) case *mat.VecDense: - return v.Plus(VecDenseToKVector(*right)) + out = v.Plus(VecDenseToKVector(*right)) case mat.VecDense: // Convert to KVector - return v.Plus(VecDenseToKVector(right)) - case KVector, VariableVector, MonomialVector, PolynomialVector: + out = v.Plus(VecDenseToKVector(right)) + case VectorExpression: ve, _ := ToVectorExpression(rightIn) - return ve.Plus(v) + out = ve.Plus(v) + default: + panic( + smErrors.UnsupportedInputError{ + FunctionName: "Variable.Plus", + Input: rightIn, + }, + ) } - panic( - fmt.Errorf("there input %v has unexpected type %T given to Variable.Plus()!", rightIn, rightIn), - ) + // Return + return out.AsSimplifiedExpression() } /* @@ -317,12 +320,13 @@ func (v Variable) Multiply(rightIn interface{}) Expression { } } - // Constants + // Algorithm + var out Expression switch right := rightIn.(type) { case float64: - return v.Multiply(K(right)) + out = v.Multiply(K(right)) case int: - return v.Multiply(K(float64(right))) + out = v.Multiply(K(float64(right))) case K: // Create a new monomial monomialOut := Monomial{ @@ -330,7 +334,7 @@ func (v Variable) Multiply(rightIn interface{}) Expression { VariableFactors: []Variable{v}, Exponents: []int{1}, } - return monomialOut + out = monomialOut case Variable: var monomialOut Monomial if right.ID == v.ID { @@ -346,35 +350,33 @@ func (v Variable) Multiply(rightIn interface{}) Expression { Exponents: []int{1, 1}, } } - return monomialOut + out = monomialOut case Monomial: // Use Monomial method - return right.Multiply(v) + out = right.Multiply(v) case Polynomial: // Create a new vector of polynomials. - return right.Multiply(v) + out = right.Multiply(v) case *mat.VecDense: - return v.Multiply(*right) + out = v.Multiply(*right) case mat.VecDense: // Convert to KVector - return v.Multiply(VecDenseToKVector(right)) - case KVector: - // Create a monomial vector and store result in it - var monomialsOut MonomialVector = make([]Monomial, right.Len()) - for i := 0; i < right.Len(); i++ { - monomialsOut[i] = Monomial{ - Coefficient: float64(right[i]), - VariableFactors: []Variable{v}, - Exponents: []int{1}, - } - } - return monomialsOut + out = v.Multiply(VecDenseToKVector(right)) + case VectorExpression: + out = VectorMultiplyTemplate(right, v) + default: + // Unrecornized response is a panic + panic( + smErrors.UnsupportedInputError{ + FunctionName: "Variable.Multiply", + Input: right, + }, + ) } - // Unrecornized response is a panic - panic( - fmt.Errorf("Unexpected input to Variable.Multiply(): %T", rightIn), - ) + // Simplify and Return + return out.AsSimplifiedExpression() + } /* diff --git a/symbolic/variable_matrix.go b/symbolic/variable_matrix.go index a83b3eb..444f032 100644 --- a/symbolic/variable_matrix.go +++ b/symbolic/variable_matrix.go @@ -265,10 +265,11 @@ func (vm VariableMatrix) Multiply(e interface{}) Expression { } // Computation + var out Expression switch right := e.(type) { case float64: // Use K case - return vm.Multiply(K(right)) + out = vm.Multiply(K(right)) case K: // Create a new matrix of polynomials. var mmOut MonomialMatrix @@ -279,37 +280,28 @@ func (vm VariableMatrix) Multiply(e interface{}) Expression { } mmOut = append(mmOut, mmRow) } - return mmOut - + out = mmOut case KVector: // Constants - nResultRows := vm.Dims()[0] - - // Switch on the dimensions of the result - switch nResultRows { - case 1: - // Scalar result - var result Polynomial = K(0).ToMonomial().ToPolynomial() - for ii, k := range right { - result = result.Plus(vm[0][ii].Multiply(k)).(Polynomial) - } - return result - default: - // Create vector result - var result PolynomialVector = VecDenseToKVector(ZerosVector(nResultRows)).ToPolynomialVector() - for ii, vmRow := range vm { - for jj, v := range vmRow { - result[ii] = result[ii].Plus(v.Multiply(right[jj])).(Polynomial) - } + vmRows, vmCols := vm.Dims()[0], vm.Dims()[1] + + // Compute product based on each element + var product []ScalarExpression + for ii := 0; ii < vmRows; ii++ { + var productII ScalarExpression = K(0) + for kk := 0; kk < vmCols; kk++ { + productII = productII.Plus(vm[ii][kk].Multiply(right[kk])).(ScalarExpression) } - return result + product = append(product, productII) } + + out = ConcretizeExpression(product) case *mat.VecDense: // Use the KVector case - return vm.Multiply(VecDenseToKVector(*right)) + out = vm.Multiply(VecDenseToKVector(*right)) case mat.VecDense: // Use the KVector case - return vm.Multiply(VecDenseToKVector(right)) + out = vm.Multiply(VecDenseToKVector(right)) case VariableVector: // Output will be another vector nVMCols := vm.Dims()[1] @@ -324,24 +316,27 @@ func (vm VariableMatrix) Multiply(e interface{}) Expression { vmCol.Multiply(right[ii]), ) } - return result + out = result case *mat.Dense: // Use the mat.Dense case - return vm.Multiply(DenseToKMatrix(*right)) + out = vm.Multiply(DenseToKMatrix(*right)) case mat.Dense: // Use the KMatrix case - return vm.Multiply(DenseToKMatrix(right)) + out = vm.Multiply(DenseToKMatrix(right)) case MatrixExpression: - return MatrixMultiplyTemplate(vm, right) + out = MatrixMultiplyTemplate(vm, right) + default: + // panic if the type is not recognized + panic( + smErrors.UnsupportedInputError{ + FunctionName: "VariableMatrix.Multiply", + Input: e, + }, + ) } - // panic if the type is not recognized - panic( - smErrors.UnsupportedInputError{ - FunctionName: "VariableMatrix.Multiply", - Input: e, - }, - ) + // Return + return out.AsSimplifiedExpression() } /* diff --git a/symbolic/variable_vector.go b/symbolic/variable_vector.go index d843da1..c67c71b 100644 --- a/symbolic/variable_vector.go +++ b/symbolic/variable_vector.go @@ -154,44 +154,20 @@ func (vv VariableVector) Plus(rightIn interface{}) Expression { } // Algorithm + var out Expression switch right := rightIn.(type) { - case K, Variable, Monomial, Polynomial: - rightAsScalar := right.(ScalarExpression) - return rightAsScalar.Plus(vv) + case float64: + out = vv.Plus(K(right)) + case int: + out = vv.Plus(K(right)) + case Expression: + out = VectorPlusTemplate(vv, right) case *mat.VecDense: // Use KVector's method - return vv.Plus(VecDenseToKVector(*right)) + out = vv.Plus(VecDenseToKVector(*right)) case mat.VecDense: // Use KVector's method - return vv.Plus(VecDenseToKVector(right)) - case KVector: - // Create a polynomial vector - var pv PolynomialVector - for ii := 0; ii < vv.Len(); ii++ { - var tempPolynomial Polynomial - if right.AtVec(ii).(K) != 0 { - rightAsK := right.AtVec(ii).(K) - tempPolynomial.Monomials = append(tempPolynomial.Monomials, rightAsK.ToMonomial()) - } - tempPolynomial.Monomials = append( - tempPolynomial.Monomials, - vv[ii].ToMonomial(), - ) - // Create next polynomial. - pv = append(pv, tempPolynomial) - } - return pv - case VariableVector, MonomialVector, PolynomialVector: - // Setup - rightAsVE, _ := right.(VectorExpression) - - // Create a slice of scalarexpressions - var out []ScalarExpression - for ii := 0; ii < vv.Len(); ii++ { - seII, _ := vv[ii].Plus(rightAsVE.AtVec(ii)).(ScalarExpression) - out = append(out, seII) - } - return ConcretizeVectorExpression(out) + out = vv.Plus(VecDenseToKVector(right)) default: panic( smErrors.UnsupportedInputError{ @@ -200,6 +176,9 @@ func (vv VariableVector) Plus(rightIn interface{}) Expression { }, ) } + + // Simplify and return + return out.AsSimplifiedExpression() } /* @@ -272,71 +251,31 @@ func (vv VariableVector) Multiply(rightIn interface{}) Expression { } } - // Constants - nResultRows := vv.Dims()[0] - // Algorithm + var out Expression switch right := rightIn.(type) { case float64: // Use K method return vv.Multiply(K(right)) - case K: - // Is output a scalar - if nResultRows == 1 { - return vv[0].Multiply(right) - } - // Create a new vector of polynomials. - var mvOut MonomialVector - for _, v := range vv { - mvOut = append(mvOut, v.Multiply(right).(Monomial)) - } - return mvOut - case Variable: - // Is output a scalar? - if nResultRows == 1 { - return vv[0].Multiply(right) - } - // Create a new vector of monomials. - var mvOut MonomialVector - for _, v := range vv { - mvOut = append(mvOut, v.Multiply(right).(Monomial)) - } - return mvOut - case Monomial: - // Is output a scalar? - if nResultRows == 1 { - return vv[0].Multiply(right) - } - // Otherwise, create a new vector of monomials. - var mvOut MonomialVector - for _, v := range vv { - mvOut = append(mvOut, v.Multiply(right).(Monomial)) - } - return mvOut - case Polynomial: - // Is output a scalar? - if nResultRows == 1 { - return vv[0].Multiply(right) - } - // Create a new vector of polynomials. - var pvOut PolynomialVector - for _, v := range vv { - pvOut = append(pvOut, v.Multiply(right).(Polynomial)) - } - return pvOut - case KVector, VariableVector, MonomialVector, PolynomialVector: + case ScalarExpression: + out = VectorMultiplyTemplate(vv, right) + case VectorExpression: // Vector of polynomials must be (1x1) rightAsVE, _ := ToVectorExpression(right) - return vv.Multiply(rightAsVE.AtVec(0)) + out = vv.Multiply(rightAsVE.AtVec(0)) + default: + // Otherwise, panic + panic( + smErrors.UnsupportedInputError{ + FunctionName: "VariableVector.Multiply", + Input: rightIn, + }, + ) } - // Otherwise, panic - panic( - smErrors.UnsupportedInputError{ - FunctionName: "VariableVector.Multiply", - Input: rightIn, - }, - ) + // Simplify and return + return out.AsSimplifiedExpression() + } /* @@ -696,3 +635,17 @@ Description: func (vv VariableVector) AsSimplifiedExpression() Expression { return vv } + +/* +ToScalarExpressions +Description: + + Converts the VariableVector into a slice of ScalarExpression type objects. +*/ +func (vv VariableVector) ToScalarExpressions() []ScalarExpression { + var out []ScalarExpression + for _, v := range vv { + out = append(out, v) + } + return out +} diff --git a/symbolic/vector_expression.go b/symbolic/vector_expression.go index bb74bcc..3c42ab2 100644 --- a/symbolic/vector_expression.go +++ b/symbolic/vector_expression.go @@ -97,36 +97,11 @@ type VectorExpression interface { // Simplify simplifies the expression and returns the simplified version AsSimplifiedExpression() Expression -} -///* -//NewVectorExpression -//Description: -// -// NewExpr returns a new expression with a single additive constant value, c, -// and no variables. Creating an expression like sum := NewVectorExpr(0) is useful -// for creating new empty expressions that you can perform operatotions on later -//*/ -//func NewVectorExpression(c mat.VecDense) VectorLinearExpr { -// return VectorLinearExpr{C: c} -//} - -//func (e VectorExpression) getVarsPtr() *uint64 { -// -// if e.NumVars() > 0 { -// return &e.IDs()[0] -// } -// -// return nil -//} -// -//func (e VectorExpression) getCoeffsPtr() *float64 { -// if e.NumVars() > 0 { -// return &e.Coeffs()[0] -// } -// -// return nil -//} + // ToScalarExpressions + // Converts the given VectorExpression into a slice of ScalarExpression interface objects + ToScalarExpressions() []ScalarExpression +} /* IsVectorExpression @@ -414,3 +389,54 @@ func VectorPowerTemplate(base VectorExpression, exponent int) Expression { return result } + +func VectorMultiplyTemplate(ve VectorExpression, right Expression) Expression { + // Setup + veAsSlice := ve.ToScalarExpressions() + + // Algorithms + var prod []ScalarExpression + switch right.(type) { + case ScalarExpression: + for _, se := range veAsSlice { + prod = append(prod, se.Multiply(right).(ScalarExpression)) + } + default: + panic( + smErrors.UnsupportedInputError{ + FunctionName: fmt.Sprintf("%T.Multiply", ve), + Input: right, + }, + ) + } + + return ConcretizeExpression(prod) +} + +func VectorPlusTemplate(ve VectorExpression, right Expression) Expression { + // Setup + veAsSlice := ve.ToScalarExpressions() + + // Algorithms + var sum []ScalarExpression + switch rightExpr := right.(type) { + case ScalarExpression: + for _, se := range veAsSlice { + sum = append(sum, se.Plus(rightExpr).(ScalarExpression)) + } + case VectorExpression: + for ii, se := range veAsSlice { + rightII := rightExpr.AtVec(ii) + sum = append(sum, se.Plus(rightII).(ScalarExpression)) + } + default: + panic( + smErrors.UnsupportedInputError{ + FunctionName: fmt.Sprintf("%T.Plus", ve), + Input: right, + }, + ) + } + + return ConcretizeExpression(sum) +} diff --git a/testing/symbolic/constant_matrix_test.go b/testing/symbolic/constant_matrix_test.go index 45add5c..0c2c559 100644 --- a/testing/symbolic/constant_matrix_test.go +++ b/testing/symbolic/constant_matrix_test.go @@ -218,31 +218,16 @@ func TestConstantMatrix_Plus5(t *testing.T) { pm2 := getKMatrix.From(ones2).ToPolynomialMatrix() // Test - pm3 := km1.Plus(pm2) + sum := km1.Plus(pm2) - // Verify that the result is a polynomial matrix - if _, ok := pm3.(symbolic.PolynomialMatrix); !ok { + // Verify that the result is a constant matrix + _, tf := sum.(symbolic.KMatrix) + if !tf { t.Errorf( - "Expected pm3 to be a symbolic.PolynomialMatrix; received %T", - pm3, + "Expected sum to be a symbolic.KMatrix; received %T", + sum, ) } - - // Verify that the elements of the result is the correct value - nR, nC := eye1.Dims() - for rowIndex := 0; rowIndex < nR; rowIndex++ { - for colIndex := 0; colIndex < nC; colIndex++ { - pm3_ii_jj := pm3.(symbolic.PolynomialMatrix).At(rowIndex, colIndex) - elt := pm3_ii_jj.(symbolic.Polynomial) - if len(elt.Monomials) != 1 { - t.Errorf( - "Expected pm3.At(0,0) to be a degree 0 polynomial; received %v", - pm3.(symbolic.PolynomialMatrix).At(0, 0), - ) - } - } - - } } /* @@ -260,10 +245,11 @@ func TestConstantMatrix_Plus6(t *testing.T) { x2 := symbolic.NewVariable() // Test - pm3 := km1.Plus(x2) + prod := km1.Plus(x2) // Verify that the result is a polynomial matrix - if _, ok := pm3.(symbolic.PolynomialMatrix); !ok { + pm3, ok := prod.(symbolic.PolynomialMatrix) + if !ok { t.Errorf( "Expected pm3 to be a symbolic.PolynomialMatrix; received %T", pm3, @@ -274,13 +260,22 @@ func TestConstantMatrix_Plus6(t *testing.T) { nR, nC := eye1.Dims() for rowIndex := 0; rowIndex < nR; rowIndex++ { for colIndex := 0; colIndex < nC; colIndex++ { - pm3_ii_jj := pm3.(symbolic.PolynomialMatrix).At(rowIndex, colIndex) + pm3_ii_jj := pm3.At(rowIndex, colIndex) elt := pm3_ii_jj.(symbolic.Polynomial) - if len(elt.Monomials) != 2 { - t.Errorf( - "Expected pm3.At(0,0) to be a polynomial with 2 monomials; received %v", - pm3.(symbolic.PolynomialMatrix).At(0, 0), - ) + if rowIndex == colIndex { + if len(elt.Monomials) != 2 { + t.Errorf( + "Expected pm3.At(%v,%v) to be a polynomial with 2 monomials; received %v", + rowIndex, colIndex, pm3_ii_jj, + ) + } + } else { + if len(elt.Monomials) != 1 { + t.Errorf( + "Expected pm3.At(%v,%v) to be a polynomial with 1 monomials; received %v", + rowIndex, colIndex, pm3_ii_jj, + ) + } } } @@ -670,13 +665,14 @@ func TestKMatrix_Multiply9(t *testing.T) { vv2 := symbolic.NewVariableVector(N) // Test - pv3 := km1.Multiply(vv2) + prod := km1.Multiply(vv2) // Verify that the result is a polynomial vector - if _, ok := pv3.(symbolic.PolynomialVector); !ok { + pv3, ok := prod.(symbolic.PolynomialVector) + if !ok { t.Errorf( "Expected pv3 to be a symbolic.PolynomialVector; received %T", - pv3, + prod, ) } @@ -685,14 +681,25 @@ func TestKMatrix_Multiply9(t *testing.T) { // contain 2 monomials nR, _ := ones1.Dims() for rowIndex := 0; rowIndex < nR; rowIndex++ { - pv3_ii := pv3.(symbolic.PolynomialVector).AtVec(rowIndex) - elt := pv3_ii.(symbolic.Polynomial) + pv3_ii := pv3.AtVec(rowIndex) + elt, ok := pv3_ii.(symbolic.Polynomial) + if !ok { + t.Errorf( + "Expected pv3.At(%v) to be a symbolic.Polynomial; received %T", + rowIndex, + pv3.AtVec(rowIndex), + ) + } + // Each element of the result should be a polynomial with N monomials + // (where N is the number of elements in the variable vector) + // - Each monomial should have coefficient 1 + // - Each monomial should have degree 1 if len(elt.Monomials) != N { t.Errorf( "Expected pv3.At(%v) to be a degree %v polynomial; received %v", rowIndex, N, - pv3.(symbolic.PolynomialVector).AtVec(rowIndex), + pv3.AtVec(rowIndex), ) } } diff --git a/testing/symbolic/constant_vector_test.go b/testing/symbolic/constant_vector_test.go index 28ae176..8d986b3 100644 --- a/testing/symbolic/constant_vector_test.go +++ b/testing/symbolic/constant_vector_test.go @@ -1069,41 +1069,51 @@ Description: Verifies that the Multiply method correctly computes the product of a KVector and a VariableVector by checking all elements. + The result should be a monomial vector. Each element should be a monomial containing a variable from the variable vector and a coefficient from the KVector. */ func TestConstantVector_Multiply7(t *testing.T) { // Constants N := 3 - kv1 := symbolic.VecDenseToKVector(symbolic.OnesVector(N)) + kv1 := symbolic.VecDenseToKVector(symbolic.OnesVector(N)).Multiply(3.14).(symbolic.KVector) vv2 := symbolic.NewVariableVector(1) // Test - kv3 := kv1.Multiply(vv2) + prod := kv1.Multiply(vv2) + mv3, tf := prod.(symbolic.MonomialVector) + if !tf { + t.Errorf( + "Expected kv3 to be of type MonomialVector; received %v", + prod, + ) + } + + // Check the elements of the monomial vector for ii := 0; ii < N; ii++ { - if kv3.(symbolic.MonomialVector)[ii].Coefficient != float64(kv1.AtVec(ii).(symbolic.K)) { + if mv3[ii].Coefficient != float64(kv1.AtVec(ii).(symbolic.K)) { t.Errorf( "Expected kv3[%v].Coefficient to be %v; received %v", ii, float64(kv1.AtVec(ii).(symbolic.K)), - kv3.(symbolic.MonomialVector)[ii].Coefficient, + mv3[ii].Coefficient, ) } - if len(kv3.(symbolic.MonomialVector)[ii].VariableFactors) != 1 { + if len(mv3[ii].VariableFactors) != 1 { t.Errorf( "Expected len(kv3[%v].VariableFactors) to be 1; received %v", ii, - len(kv3.(symbolic.MonomialVector)[ii].VariableFactors), + len(mv3[ii].VariableFactors), ) } - if kv3.(symbolic.MonomialVector)[ii].VariableFactors[0] != vv2[0] { + if mv3[ii].VariableFactors[0] != vv2[0] { t.Errorf( "Expected kv3[%v].VariableFactors[0] to be %v; received %v", ii, vv2[ii], - kv3.(symbolic.MonomialVector)[ii].VariableFactors[0], + mv3[ii].VariableFactors[0], ) } } @@ -1148,15 +1158,17 @@ func TestConstantVector_Multiply9(t *testing.T) { ID: 1001, Lower: 0.0, Upper: 1.0, + Name: "dummy (ConstantVector_Multiply9)", } // Test product := kv1.Multiply(v2) - // Check that product is a K object - if _, tf := product.(symbolic.Monomial); !tf { + // Check that product is a Variable object + _, tf := product.(symbolic.Variable) + if !tf { t.Errorf( - "Expected product to be of type Monomial; received %v", + "Expected product to be of type Variable; received %T", product, ) } @@ -1196,8 +1208,8 @@ TestConstantVector_Multiply11 Description: Verifies that the Multiply method correctly computes the product - of a KVector (length 1) and a Polynomial. - The result should be a Polynomial. + of a KVector (length 1) and a Polynomial (really a monomial). + The result should be a Monomial. */ func TestConstantVector_Multiply11(t *testing.T) { // Constants @@ -1216,9 +1228,9 @@ func TestConstantVector_Multiply11(t *testing.T) { product := kv1.Multiply(p2) // Check that product is a K object - if _, tf := product.(symbolic.Polynomial); !tf { + if _, tf := product.(symbolic.Monomial); !tf { t.Errorf( - "Expected product to be of type Polynomial; received %v", + "Expected product to be of type Monomial; received %T", product, ) } @@ -1259,7 +1271,7 @@ Description: Verifies that the Multiply method correctly computes the product of a KVector (length 10) and a Polynomial. - The result should be a PolynomialVector. + The result should be a MonomialVector. */ func TestConstantVector_Multiply13(t *testing.T) { // Constants @@ -1277,13 +1289,26 @@ func TestConstantVector_Multiply13(t *testing.T) { // Test product := kv1.Multiply(p2) - // Check that product is a K object - if _, tf := product.(symbolic.PolynomialVector); !tf { + // Check that product is a MonomialVector object + mv3, tf := product.(symbolic.MonomialVector) + if !tf { t.Errorf( - "Expected product to be of type PolynomialVector; received %v", + "Expected product to be of type MonomialVector; received %v", product, ) } + + // Check that each monomial has only one term + for i, monomial := range mv3 { + if len(monomial.Variables()) != 1 { + t.Errorf( + "Expected monomial #%v to have 1 monomial; found %v", + i, + len(monomial.Variables()), + ) + } + } + } /* diff --git a/testing/symbolic/matrix_constraint_test.go b/testing/symbolic/matrix_constraint_test.go index 687a65c..b30ba08 100644 --- a/testing/symbolic/matrix_constraint_test.go +++ b/testing/symbolic/matrix_constraint_test.go @@ -494,7 +494,7 @@ Description: are well-formed and the sense is well-formed. The left hand side is a monomial matrix and the right hand side is a constant matrix. - After substitution, the left hand side should be a polynomial matrix + After substitution, the left hand side should be a monomial matrix and the right hand side should be a constant matrix. */ func TestMatrixConstraint_Substitute2(t *testing.T) { @@ -520,15 +520,17 @@ func TestMatrixConstraint_Substitute2(t *testing.T) { } // Verify that the left hand side is a polynomial matrix - if _, ok := mcSubstitutedAsMC.LeftHandSide.(symbolic.PolynomialMatrix); !ok { + _, ok = mcSubstitutedAsMC.LeftHandSide.(symbolic.MonomialMatrix) + if !ok { t.Errorf( - "Expected mcSubstituted.LeftHandSide to be of type PolynomialMatrix; received %T", + "Expected mcSubstituted.LeftHandSide to be of type MonomialMatrix; received %T", mcSubstitutedAsMC.LeftHandSide, ) } // Verify that the right hand side is a constant matrix - if _, ok := mcSubstitutedAsMC.RightHandSide.(symbolic.KMatrix); !ok { + _, ok = mcSubstitutedAsMC.RightHandSide.(symbolic.KMatrix) + if !ok { t.Errorf( "Expected mcSubstituted.RightHandSide to be of type KMatrix; received %T", mcSubstitutedAsMC.RightHandSide, @@ -596,7 +598,7 @@ Description: are well-formed and the sense is well-formed. The left hand side is a monomial matrix and the right hand side is a constant matrix. - After substitution, the left hand side should be a polynomial matrix + After substitution, the left hand side should be a monomial matrix and the right hand side should be a constant matrix. */ func TestMatrixConstraint_SubstituteAccordingTo2(t *testing.T) { @@ -626,7 +628,8 @@ func TestMatrixConstraint_SubstituteAccordingTo2(t *testing.T) { } // Verify that the left hand side is a polynomial matrix - if _, ok := mcSubstitutedAsMC.LeftHandSide.(symbolic.PolynomialMatrix); !ok { + _, ok = mcSubstitutedAsMC.LeftHandSide.(symbolic.MonomialMatrix) + if !ok { t.Errorf( "Expected mcSubstituted.LeftHandSide to be of type PolynomialMatrix; received %T", mcSubstitutedAsMC.LeftHandSide, @@ -634,7 +637,8 @@ func TestMatrixConstraint_SubstituteAccordingTo2(t *testing.T) { } // Verify that the right hand side is a constant matrix - if _, ok := mcSubstitutedAsMC.RightHandSide.(symbolic.KMatrix); !ok { + _, ok = mcSubstitutedAsMC.RightHandSide.(symbolic.KMatrix) + if !ok { t.Errorf( "Expected mcSubstituted.RightHandSide to be of type KMatrix; received %T", mcSubstitutedAsMC.RightHandSide, diff --git a/testing/symbolic/monomial_matrix_test.go b/testing/symbolic/monomial_matrix_test.go index 2a50486..5303c6e 100644 --- a/testing/symbolic/monomial_matrix_test.go +++ b/testing/symbolic/monomial_matrix_test.go @@ -1328,10 +1328,10 @@ func TestMonomialMatrix_Multiply8(t *testing.T) { product := mm.Multiply(km2) // Check that the product is of the MonomialMatrix type - productMat, ok := product.(symbolic.PolynomialMatrix) + productMat, ok := product.(symbolic.MonomialMatrix) if !ok { t.Errorf( - "expected Multiply() to return a PolynomialMatrix; received %v", + "expected Multiply() to return a MonomialMatrix; received %v", product, ) } diff --git a/testing/symbolic/monomial_test.go b/testing/symbolic/monomial_test.go index 91e257c..8d9fbd2 100644 --- a/testing/symbolic/monomial_test.go +++ b/testing/symbolic/monomial_test.go @@ -1329,14 +1329,14 @@ func TestMonomial_IsConstant2(t *testing.T) { } /* -TestMonomial_IsVariable1 +TestMonomial_IsDegreeOneContainingVariable1 Description: - Verifies that the Monomial.IsVariable function returns - false when the monomial is not the same as a separate variable - v2. + Verifies that the Monomial.IsDegreeOneContainingVariable function returns + false when the monomial we are checking + does not contain the variable v2. */ -func TestMonomial_IsVariable1(t *testing.T) { +func TestMonomial_IsDegreeOneContainingVariable1(t *testing.T) { // Constants v1 := symbolic.NewVariable() v2 := symbolic.NewVariable() @@ -1347,22 +1347,22 @@ func TestMonomial_IsVariable1(t *testing.T) { } // Test - if m1.IsVariable(v1) { + if m1.IsDegreeOneContainingVariable(v1) { t.Errorf( - "expected m1 to be a variable; received %v", - m1.IsVariable(v1), + "expected m1 to be a monomial; received %v", + m1.IsDegreeOneContainingVariable(v1), ) } } /* -TestMonomial_IsVariable2 +TestMonomial_IsDegreeOneContainingVariable2 Description: - Verifies that the Monomial.IsVariable function + Verifies that the Monomial.IsDegreeOneContainingVariable function panics if the monomial is not well-defined. */ -func TestMonomial_IsVariable2(t *testing.T) { +func TestMonomial_IsDegreeOneContainingVariable2(t *testing.T) { // Constants v1 := symbolic.NewVariable() v2 := symbolic.NewVariable() @@ -1381,17 +1381,17 @@ func TestMonomial_IsVariable2(t *testing.T) { } }() - m1.IsVariable(v1) + m1.IsDegreeOneContainingVariable(v1) } /* -TestMonomial_IsVariable3 +TestMonomial_IsDegreeOneContainingVariable3 Description: - Verifies that the Monomial.IsVariable function + Verifies that the Monomial.IsDegreeOneContainingVariable function panics if the input variable is not well-defined. */ -func TestMonomial_IsVariable3(t *testing.T) { +func TestMonomial_IsDegreeOneContainingVariable3(t *testing.T) { // Constants v2 := symbolic.NewVariable() m1 := symbolic.Monomial{ @@ -1409,7 +1409,7 @@ func TestMonomial_IsVariable3(t *testing.T) { } }() - m1.IsVariable(symbolic.Variable{}) + m1.IsDegreeOneContainingVariable(symbolic.Variable{}) } /* diff --git a/testing/symbolic/monomial_vector_test.go b/testing/symbolic/monomial_vector_test.go index 77c080a..03ad5f6 100644 --- a/testing/symbolic/monomial_vector_test.go +++ b/testing/symbolic/monomial_vector_test.go @@ -1004,7 +1004,7 @@ func TestMonomialVector_Minus4(t *testing.T) { } for _, monomial := range polynomial.Monomials { - if (!monomial.IsConstant()) && (!monomial.IsVariable(v1)) { + if (!monomial.IsConstant()) && (!monomial.IsDegreeOneContainingVariable(v1)) { t.Errorf("expected monomial to be a variable or a constant; received %v", monomial) } } diff --git a/testing/symbolic/polynomial_matrix_test.go b/testing/symbolic/polynomial_matrix_test.go index a3e0e1d..c41561c 100644 --- a/testing/symbolic/polynomial_matrix_test.go +++ b/testing/symbolic/polynomial_matrix_test.go @@ -396,9 +396,9 @@ func TestPolynomialMatrix_Plus3(t *testing.T) { TestPolynomialMatrix_Plus4 Description: - Tests that the Plus() method properly adds a polynomial matrix - with THE SAME polynomial matrix. The result should be a polynomial - matrix with each polynomial containing one monomial. + Tests that the Plus() method properly adds a polynomial matrix (really a variable matrix) + with THE SAME polynomial matrix. The result should be a monomial + matrix. */ func TestPolynomialMatrix_Plus4(t *testing.T) { // Constants @@ -413,24 +413,13 @@ func TestPolynomialMatrix_Plus4(t *testing.T) { // Test pm2 := pm1.Plus(pm1) - pm2AsPM, tf := pm2.(symbolic.PolynomialMatrix) + _, tf := pm2.(symbolic.MonomialMatrix) if !tf { t.Errorf( - "expected pm2 to be a PolynomialMatrix; received %v", + "expected pm2 to be a MonomialMatrix; received %v", pm2, ) } - - for _, pmRow := range pm2AsPM { - for _, p := range pmRow { - if len(p.Monomials) != 1 { - t.Errorf( - "expected len(p.Monomials) to be 1; received %v", - len(p.Monomials), - ) - } - } - } } /* @@ -775,7 +764,7 @@ TestPolynomialMatrix_Minus4 Description: Tests that the Minus() method properly subtracts a polynomial matrix (Each with 1 monomial) - with a float64. The result should be a polynomial matrix with + with a float64. The result should be a polynomial vector with each polynomial containing two monomials. */ func TestPolynomialMatrix_Minus4(t *testing.T) { @@ -791,22 +780,21 @@ func TestPolynomialMatrix_Minus4(t *testing.T) { // Test pm2 := pm1.Minus(1.0) - pm2AsPM, tf := pm2.(symbolic.PolynomialMatrix) + // Check that the result is a polynomial vector + pv3, tf := pm2.(symbolic.PolynomialVector) if !tf { t.Errorf( - "expected pm2 to be a PolynomialMatrix; received %v", - pm2, + "expected pv3 to be a PolynomialVector; received %T", + pv3, ) } - for _, pmRow := range pm2AsPM { - for _, p := range pmRow { - if len(p.Monomials) != 2 { - t.Errorf( - "expected len(p.Monomials) to be 2; received %v", - len(p.Monomials), - ) - } + for _, p := range pv3 { + if len(p.Monomials) != 2 { + t.Errorf( + "expected len(p.Monomials) to be 2; received %v", + len(p.Monomials), + ) } } } @@ -980,7 +968,7 @@ TestPolynomialMatrix_Multiply4 Description: Tests that the Multiply() method properly multiplies a polynomial matrix - with a single constant. The result should be a polynomial matrix with + with a single constant. The result should be a monomial vector with the same number of monomials as the original. But the coefficient of each monomial should be multiplied by the constant. */ @@ -995,31 +983,24 @@ func TestPolynomialMatrix_Multiply4(t *testing.T) { } // Test - pm2 := pm1.Multiply(3.14) + prod := pm1.Multiply(3.14) - pm2AsPM, tf := pm2.(symbolic.PolynomialMatrix) + // Verify that this is a monomial Vector + mv3, tf := prod.(symbolic.MonomialVector) if !tf { t.Errorf( - "expected pm2 to be a PolynomialMatrix; received %v", - pm2, + "expected mv3 to be a MonomialVector; received %T", + mv3, ) } - for _, pmRow := range pm2AsPM { - for _, p := range pmRow { - if len(p.Monomials) != 1 { - t.Errorf( - "expected len(p.Monomials) to be 1; received %v", - len(p.Monomials), - ) - } - - if p.Monomials[0].Coefficient != 3.14 { - t.Errorf( - "expected p.Monomials[0].Coefficient to be 3.14; received %v", - p.Monomials[0].Coefficient, - ) - } + // Check the elements of each monomial + for _, monomial := range mv3 { + if monomial.Coefficient != 3.14 { + t.Errorf( + "expected monomial.Coefficient to be 3.14; received %v", + monomial.Coefficient, + ) } } } diff --git a/testing/symbolic/polynomial_test.go b/testing/symbolic/polynomial_test.go index cfc3c8b..04ad865 100644 --- a/testing/symbolic/polynomial_test.go +++ b/testing/symbolic/polynomial_test.go @@ -15,6 +15,7 @@ import ( getKVector "github.com/MatProGo-dev/SymbolicMath.go/get/KVector" "github.com/MatProGo-dev/SymbolicMath.go/smErrors" "github.com/MatProGo-dev/SymbolicMath.go/symbolic" + "gonum.org/v1/gonum/mat" ) /* @@ -424,32 +425,25 @@ func TestPolynomial_Plus1(t *testing.T) { // Test sum := p1.Plus(k1) - if len(sum.(symbolic.Polynomial).Monomials) != 1 { - t.Errorf( - "expected %v + %v to have 1 monomial; received %v", - p1, - k1, - len(sum.(symbolic.Polynomial).Monomials), - ) - } - // Verify that the sum is a polynomial - if _, tf := sum.(symbolic.Polynomial); !tf { + // Verify that the sum is a constant + k3, tf := sum.(symbolic.K) + if !tf { t.Errorf( - "expected %v + %v to be a polynomial; received %T", + "expected %v + %v to be a constant; received %T", p1, k1, sum, ) } - //Verify that the sum's value matches what we expect - if sum.(symbolic.Polynomial).Monomials[0].Coefficient != 5.85 { + // Verify that the value of the sum is correct + if !reflect.DeepEqual(float64(k3), 5.85) { t.Errorf( - "expected %v + %v to have coefficient 5.85; received %v", + "expected %v + %v to have value 5.85; received %v", p1, k1, - sum.(symbolic.Polynomial).Monomials[0].Coefficient, + float64(k3), ) } } @@ -503,23 +497,25 @@ func TestPolynomial_Plus3(t *testing.T) { // Test sum := p1.Plus(v1) - if len(sum.(symbolic.Polynomial).Monomials) != 1 { + + // Verify that the sum is a monomial + m3, tf := sum.(symbolic.Monomial) + if !tf { t.Errorf( - "expected %v + %v to have 1 monomial; received %v", + "expected %v + %v to be a Monomial; received %T", p1, v1, - len(sum.(symbolic.Polynomial).Monomials), + sum, ) } - // Verify that the coefficient of the sum's monomial - // is 1.0 more than the original - if sum.(symbolic.Polynomial).Monomials[0].Coefficient != 3.0 { + // Verify that the coefficient of the monomial is 3.0 + if m3.Coefficient != 3.0 { t.Errorf( "expected %v + %v to have coefficient 3.0; received %v", p1, v1, - sum.(symbolic.Polynomial).Monomials[0].Coefficient, + m3.Coefficient, ) } } @@ -1396,12 +1392,19 @@ func TestPolynomial_Multiply5(t *testing.T) { // Test prod := p1.Multiply(3.14) - if len(prod.(symbolic.Polynomial).Monomials) != 1 { + m3, tf := prod.(symbolic.Monomial) + if !tf { t.Errorf( - "expected %v * %v to have 1 monomial; received %v", + "expected product to be a Monomial; received %T", + prod, + ) + } + if len(m3.Variables()) != 1 { + t.Errorf( + "expected %v * %v to have 1 variable; received %v", p1, 3.14, - len(prod.(symbolic.Polynomial).Monomials), + len(m3.Variables()), ) } } @@ -1496,7 +1499,7 @@ Description: Verifies that the Polynomial.Multiply method returns the correct output when called with a well-defined polynomial and a well-defined constant matrix. - The resulting polynomial should be a polynomial matrix. + The resulting matrix should be a monomial matrix. */ func TestPolynomial_Multiply8(t *testing.T) { // Setup @@ -1509,11 +1512,11 @@ func TestPolynomial_Multiply8(t *testing.T) { // Test prod := p1.Multiply(km1) - // Verify that the product is a polynomial matrix - prodAsPM, tf := prod.(symbolic.PolynomialMatrix) + // Verify that the product is a monomial matrix + prodAsMM, tf := prod.(symbolic.MonomialMatrix) if !tf { t.Errorf( - "expected %v * %v to return a polynomial matrix; received %T", + "expected %v * %v to return a monomial matrix; received %T", p1, km1, prod, @@ -1521,24 +1524,16 @@ func TestPolynomial_Multiply8(t *testing.T) { } // Verify that the coefficients of the product are correct - for ii, pRow := range prodAsPM { - for jj, p := range pRow { - if len(p.Monomials) != 1 { - t.Errorf( - "expected %v * %v to have 1 monomial; received %v", - p1, - km1, - len(p.Monomials), - ) - } + for ii, monomialRow := range prodAsMM { + for jj, monomial := range monomialRow { - if prodAsPM[ii][jj].Monomials[0].Coefficient != float64(km1.At(ii, jj).(symbolic.K)) { + if monomial.Coefficient != float64(km1.At(ii, jj).(symbolic.K)) { t.Errorf( "expected %v * %v to have coefficient %v; received %v", p1, km1, km1.At(ii, jj), - prodAsPM[ii][jj].Monomials[0].Coefficient, + monomial.Coefficient, ) } @@ -2115,6 +2110,58 @@ func TestPolynomial_String1(t *testing.T) { p1.String() } +func TestPolynomial_String2(t *testing.T) { + // Create polynomial with three terms + N := 3 + vv1 := symbolic.NewVariableVector(N) + vec2 := mat.NewVecDense(N, []float64{-2.0, -4.0, 5.0}) + + prod := vv1.Transpose().Multiply(vec2) + poly3, tf := prod.(symbolic.Polynomial) + if !tf { + t.Errorf( + "Expected product to be a Polynomial; received %T", + prod, + ) + } + + // Create string + out := poly3.String() + + // Check that the first character in the string is the negative symbol. + if string(out[0]) != "-" { + t.Errorf( + "Expected the first character of the string to be the \"-\" character; received %v", + out[0], + ) + } + + // Check that there are two negative signs in string + firstMinusIndex := strings.Index(out, "-") + secondMinusIndex := strings.Index(out[firstMinusIndex+1:], "-") + + if secondMinusIndex == -1 { + t.Errorf( + "Expected there to be two minus indices in the string out; found only 1.", + ) + } + + // Check that there are no "+" signs between the first and second "-" signs + if strings.Index(out[firstMinusIndex:secondMinusIndex], "+") != -1 { + t.Errorf( + "the cleaner String() method should not contain \"+\" and \"-\" signs next to each other, but we observed: \"%v\"", + out[firstMinusIndex:secondMinusIndex], + ) + } + + // Check that last term is preceded by a "+" sign. + if strings.Index(out[secondMinusIndex:], "+") == -1 { + t.Errorf( + "there should be at least one \"+\" sign after the two minus signs; found none!", + ) + } +} + /* TestPolynomial_Substitute1 Description: @@ -2159,13 +2206,25 @@ func TestPolynomial_Substitute2(t *testing.T) { // Test sub := p1.Substitute(v1, v2.Multiply(3.0).(symbolic.ScalarExpression)) - if sub.(symbolic.Polynomial).Monomials[0].Coefficient != 3.0 { + + // Verify that the output is a monomial with coefficient 3.0 + subAsMonomial, tf := sub.(symbolic.Monomial) + if !tf { + t.Errorf( + "expected %v.substitute(%v, %v) to be a monomial; received %T", + p1, + v1, + v2.Multiply(3.0), + sub, + ) + } + if subAsMonomial.Coefficient != 3.0 { t.Errorf( "expected %v.substitute(%v, %v) to have coefficient 3.0; received %v", p1, v1, v2.Multiply(3.0), - sub.(symbolic.Polynomial).Monomials[0].Coefficient, + subAsMonomial.Coefficient, ) } } @@ -2238,8 +2297,20 @@ func TestPolynomial_SubstituteWith4(t *testing.T) { // Test substitution := p1.Substitute(x[0], symbolic.K(2.0)) + // Verify that the output is a polynomial + p3, tf := substitution.(symbolic.Polynomial) + if !tf { + t.Errorf( + "expected %v.substitute(%v, %v) to be a polynomial; received %T", + p1, + x[0], + symbolic.K(2.0), + substitution, + ) + } + // Search for a constant element in the monomials - for _, m := range substitution.(symbolic.Polynomial).Monomials { + for _, m := range p3.Monomials { if m.IsConstant() { if m.Coefficient != 3.0 { t.Errorf( diff --git a/testing/symbolic/polynomial_vector_test.go b/testing/symbolic/polynomial_vector_test.go index 3eff78e..3eeb14c 100644 --- a/testing/symbolic/polynomial_vector_test.go +++ b/testing/symbolic/polynomial_vector_test.go @@ -615,9 +615,11 @@ Description: func TestPolynomialVector_LinearCoeff4(t *testing.T) { // Constants var pv symbolic.PolynomialVector = make([]symbolic.Polynomial, 20) + N := 20 + vv := symbolic.NewVariableVector(N) - for ii := 0; ii < 20; ii++ { - vII := symbolic.NewVariable() + for ii := 0; ii < N; ii++ { + vII := vv[ii] pv[ii] = symbolic.Monomial{ Coefficient: float64(ii), VariableFactors: []symbolic.Variable{vII}, @@ -627,20 +629,22 @@ func TestPolynomialVector_LinearCoeff4(t *testing.T) { // Test linearCoeff := pv.LinearCoeff() + nr, nc := linearCoeff.Dims() for ii := 0; ii < nr; ii++ { for jj := 0; jj < nc; jj++ { if ii == jj { if linearCoeff.At(ii, jj) != float64(ii) { t.Errorf( - "Expected linearCoeff.At(%v, %v) to be 1; received %v", + "Expected linearCoeff.At(%v, %v) to be %v; received %v", ii, jj, + float64(ii), linearCoeff.At(ii, jj), ) } } else { - if linearCoeff.At(ii, jj) != 0 { + if linearCoeff.At(ii, jj) != 0.0 { t.Errorf( "Expected linearCoeff.At(%v, %v) to be 0; received %v", ii, @@ -880,8 +884,8 @@ func TestPolynomialVector_Plus4(t *testing.T) { TestPolynomialVector_Plus5 Description: - Tests that a polynomial vector added to a polynomial vector - produces a polynomial vector. + Tests that a polynomial vector (really a vector of variables) added to + a polynomial vector (really a vector of variables) produces a monomial vector. */ func TestPolynomialVector_Plus5(t *testing.T) { // Constants @@ -891,12 +895,22 @@ func TestPolynomialVector_Plus5(t *testing.T) { } // Test - pv2 := pv1.Plus(pv1).(symbolic.PolynomialVector) - for _, polynomial := range pv2 { - if len(polynomial.Monomials) != 1 { + sum := pv1.Plus(pv1) + + sumAsMV, ok := sum.(symbolic.MonomialVector) + if !ok { + t.Errorf( + "Expected Plus to return a MonomialVector; received %T", + sum, + ) + } + + // Check that each monomial contains a coefficient of 2.0 + for _, monomial := range sumAsMV { + if monomial.Coefficient != 2.0 { t.Errorf( - "Expected polynomial.Monomials to have length 2; received %v", - len(polynomial.Monomials), + "Expected each monomial.Coefficient to be 2.0; received %v", + monomial.Coefficient, ) } } @@ -1393,15 +1407,22 @@ func TestPolynomialVector_Multiply3(t *testing.T) { k2 := symbolic.K(3.14) // Test - pv3 := pv.Multiply(k2).(symbolic.PolynomialVector) - for _, polynomial := range pv3 { - for _, monomial := range polynomial.Monomials { - if monomial.Coefficient != 3.14 { - t.Errorf( - "Expected monomial.Coefficient to be 3.14; received %v", - monomial.Coefficient, - ) - } + product := pv.Multiply(k2) + mv3, ok := product.(symbolic.MonomialVector) + if !ok { + t.Errorf( + "Expected Multiply to return a MonomialVector; received %T", + product, + ) + } + + // Check that each monomial contains a coefficient of 3.14 + for _, monomial := range mv3 { + if monomial.Coefficient != 3.14 { + t.Errorf( + "Expected monomial.Coefficient to be 3.14; received %v", + monomial.Coefficient, + ) } } } @@ -1410,8 +1431,9 @@ func TestPolynomialVector_Multiply3(t *testing.T) { TestPolynomialVector_Multiply4 Description: - This test verifies that the Multiply() method returns a polynomial - with the correct coefficients when the second input is a float64. + This test verifies that the Multiply() method returns a monomial vector + with the correct coefficients when the second input is a float64 + and the input polynomial is *really* a vector of variables. */ func TestPolynomialVector_Multiply4(t *testing.T) { // Constants @@ -1422,15 +1444,23 @@ func TestPolynomialVector_Multiply4(t *testing.T) { f2 := 3.14 // Test - pv3 := pv.Multiply(f2).(symbolic.PolynomialVector) - for _, polynomial := range pv3 { - for _, monomial := range polynomial.Monomials { - if monomial.Coefficient != 3.14 { - t.Errorf( - "Expected monomial.Coefficient to be 3.14; received %v", - monomial.Coefficient, - ) - } + prod := pv.Multiply(f2) + + // Check that the product is a vector of monomials + mv3, ok := prod.(symbolic.MonomialVector) + if !ok { + t.Errorf( + "expected the product to be of type MonomialVector; received %T", + prod, + ) + } + + for _, monomial := range mv3 { + if monomial.Coefficient != 3.14 { + t.Errorf( + "Expected monomial.Coefficient to be 3.14; received %v", + monomial.Coefficient, + ) } } } @@ -1444,19 +1474,30 @@ Description: */ func TestPolynomialVector_Multiply5(t *testing.T) { // Constants - pv := symbolic.PolynomialVector{} - for ii := 0; ii < 20; ii++ { - pv = append(pv, symbolic.NewVariable().ToPolynomial()) - } + N := 10 + vv1 := symbolic.NewVariableVector(N) + pv := vv1.ToPolynomialVector() p2 := symbolic.NewVariable().ToPolynomial() // Test - pv3 := pv.Multiply(p2).(symbolic.PolynomialVector) - for _, polynomial := range pv3 { - if len(polynomial.Monomials) != 1 { + prod := pv.Multiply(p2) + + // Check that the product is actually a monomial vector + mv3, ok := prod.(symbolic.MonomialVector) + if !ok { + t.Errorf( + "Expected product to be of type MonomialVector; received %T", + prod, + ) + } + + // Check that the product contains monomials all of degree 2 + for i, monomial := range mv3 { + if monomial.Degree() != 2 { t.Errorf( - "Expected polynomial.Monomials to have length 2; received %v", - len(polynomial.Monomials), + "Expected each monomial in product to be of degree 2; monomial #%v has degree %v", + i, + monomial.Degree(), ) } } @@ -1522,22 +1563,33 @@ Description: */ func TestPolynomialVector_Multiply7(t *testing.T) { // Constants - pv1 := symbolic.PolynomialVector{} - for ii := 0; ii < 20; ii++ { - pv1 = append(pv1, symbolic.NewVariable().ToPolynomial()) - } + N := 20 + vv1 := symbolic.NewVariableVector(N) + pv1 := vv1.ToPolynomialVector() + pv2 := symbolic.PolynomialVector{} pv2 = append(pv2, symbolic.NewVariable().ToPolynomial()) // Test - pv3 := pv1.Multiply(pv2).(symbolic.PolynomialVector) - for _, polynomial := range pv3 { - if len(polynomial.Monomials) != 1 { - t.Errorf( - "Expected polynomial.Monomials to have length 2; received %v", - len(polynomial.Monomials), - ) - } + prod := pv1.Multiply(pv2) + + // Check that the product is actually a monomial + mv3, ok := prod.(symbolic.MonomialVector) + if !ok { + t.Errorf( + "Expected product to be of type MonomialVector; received %T", + prod, + ) + } + + // Check that the dimension of the new monomial vector + // matches that of the first polynomial vector + if (mv3.Dims()[0] != pv1.Dims()[0]) || (mv3.Dims()[1] != pv1.Dims()[1]) { + t.Errorf( + "dimensions of the product (%v,%v) did not match the dimensions of the input vector (%v,%v)", + mv3.Dims()[0], mv3.Dims()[1], + pv1.Dims()[0], pv1.Dims()[1], + ) } } diff --git a/testing/symbolic/scalar_constraint_test.go b/testing/symbolic/scalar_constraint_test.go index 155670f..977af25 100644 --- a/testing/symbolic/scalar_constraint_test.go +++ b/testing/symbolic/scalar_constraint_test.go @@ -533,7 +533,7 @@ func TestScalarConstraint_LinearInequalityConstraintRepresentation3(t *testing.T } // Get linear representation - A, b := sc.(symbolic.ScalarConstraint).LinearInequalityConstraintRepresentation() + A, b := sc.(symbolic.ScalarConstraint).LinearInequalityConstraintRepresentation([]symbolic.Variable(x)) // Verify that the vector is all ones diff --git a/testing/symbolic/variable_test.go b/testing/symbolic/variable_test.go index 7d9aaa0..1f25da1 100644 --- a/testing/symbolic/variable_test.go +++ b/testing/symbolic/variable_test.go @@ -1,10 +1,11 @@ package symbolic_test import ( - "github.com/MatProGo-dev/SymbolicMath.go/symbolic" - "gonum.org/v1/gonum/mat" "strings" "testing" + + "github.com/MatProGo-dev/SymbolicMath.go/symbolic" + "gonum.org/v1/gonum/mat" ) /* @@ -304,6 +305,7 @@ Description: Tests that the Plus() method works properly when adding a variable to the same variable. + Specifically, we want to make sure that the addition is 2 * x (a Monomial). */ func TestVariable_Plus4(t *testing.T) { // Constants @@ -320,23 +322,22 @@ func TestVariable_Plus4(t *testing.T) { ) } - // Test that sum is a polynomial with 2 terms - sumAsPoly, tf := sum.(symbolic.Polynomial) + // Test that sum is a monomial with 2 terms + sumAsMonom, tf := sum.(symbolic.Monomial) if !tf { t.Errorf( - "expected %v + %v to be a polynomial; received %T", + "expected %v + %v to be a monomial; received %T", x, x, sum, ) } - if len(sumAsPoly.Monomials) != 1 { + if sumAsMonom.Coefficient != 2.0 { t.Errorf( - "expected %v + %v to have 1 term; received %v", - x, - x, - len(sumAsPoly.Monomials), + "Expected coefficient of monomial to be %v; received %v", + 2.0, + sumAsMonom.Coefficient, ) } } diff --git a/testing/symbolic/variable_vector_test.go b/testing/symbolic/variable_vector_test.go index 89a5fb5..b56979c 100644 --- a/testing/symbolic/variable_vector_test.go +++ b/testing/symbolic/variable_vector_test.go @@ -907,26 +907,23 @@ func TestVariableVector_Multiply6(t *testing.T) { // Test r := vv1.Multiply(p2) - if _, ok := r.(symbolic.PolynomialVector); !ok { + mv3, ok := r.(symbolic.MonomialVector) + if !ok { t.Errorf( - "Expected vv1.Multiply(pv2) to return a PolynomialVector object; received %T", + "Expected vv1.Multiply(pv2) to return a MonomialVector object; received %T", r, ) } // Check that all of the coefficients are the same as they were in p2 - rAsPV, _ := r.(symbolic.PolynomialVector) - for ii := 0; ii < N; ii++ { - for jj, monomial := range rAsPV.AtVec(ii).(symbolic.Polynomial).Monomials { - if monomial.Coefficient != p2.Monomials[jj].Coefficient { - t.Errorf( - "Expected %v^th monomial in product's %v^th element to have coefficient %v; received %v", - jj, - ii, - p2.Monomials[jj].Coefficient, - monomial.Coefficient, - ) - } + for jj, monomial := range mv3 { + if monomial.Coefficient != 1.0 { + t.Errorf( + "Expected %v^th monomial in product to have coefficient %v; received %v", + jj, + 1.0, + monomial.Coefficient, + ) } } } @@ -947,26 +944,23 @@ func TestVariableVector_Multiply7(t *testing.T) { // Test r := vv1.Multiply(pv2) - if _, ok := r.(symbolic.PolynomialVector); !ok { + mv3, ok := r.(symbolic.MonomialVector) + if !ok { t.Errorf( - "Expected vv1.Multiply(pv2) to return a PolynomialVector object; received %T", + "Expected vv1.Multiply(pv2) to return a MonomialVector object; received %T", r, ) } // Check that all of the coefficients are the same as they were in pv2 - rAsPV, _ := r.(symbolic.PolynomialVector) - for ii := 0; ii < N; ii++ { - for jj, monomial := range rAsPV.AtVec(ii).(symbolic.Polynomial).Monomials { - if monomial.Coefficient != pv2.AtVec(0).(symbolic.Polynomial).Monomials[jj].Coefficient { - t.Errorf( - "Expected %v^th monomial in product's %v^th element to have coefficient %v; received %v", - jj, - ii, - pv2.AtVec(0).(symbolic.Polynomial).Monomials[jj].Coefficient, - monomial.Coefficient, - ) - } + for jj, monomial := range mv3 { + if monomial.Coefficient != 1.0 { + t.Errorf( + "Expected %v^th monomial to have coefficient %v; received %v", + jj, + 1.0, + monomial.Coefficient, + ) } } } diff --git a/testing/symbolic/vector_constraint_test.go b/testing/symbolic/vector_constraint_test.go index d86e9d5..d2a3e23 100644 --- a/testing/symbolic/vector_constraint_test.go +++ b/testing/symbolic/vector_constraint_test.go @@ -1109,7 +1109,14 @@ func TestVectorConstraint_AsSimplifiedConstraint2(t *testing.T) { N := 7 x := symbolic.NewVariableVector(N) left := x.ToMonomialVector() - right := x.Multiply(2.0).Plus(symbolic.VecDenseToKVector(symbolic.OnesVector(N))).(symbolic.PolynomialVector) + tempRight := x.Multiply(2.0).Plus(symbolic.VecDenseToKVector(symbolic.OnesVector(N))) + right, ok := tempRight.(symbolic.VectorExpression) + if !ok { + t.Errorf( + "Expected right to be a VectorExpression; received %T", + tempRight, + ) + } // Create the vector constraint vc := symbolic.VectorConstraint{left, right, symbolic.SenseLessThanEqual} @@ -1118,14 +1125,26 @@ func TestVectorConstraint_AsSimplifiedConstraint2(t *testing.T) { simplifiedVC := vc.AsSimplifiedConstraint().(symbolic.VectorConstraint) // Check the left hand side - // it should now be a vector of polynomials - if _, ok := simplifiedVC.LeftHandSide.(symbolic.PolynomialVector); !ok { + // - it should now be a vector of polynomials + lhs, ok := simplifiedVC.LeftHandSide.(symbolic.MonomialVector) + if !ok { t.Errorf( - "Expected vc.AsSimplifiedConstraint() to return a vector constraint with a polynomial vector on the left hand side; received %v", + "Expected vc.AsSimplifiedConstraint() to return a vector constraint with a monomial vector on the left hand side; received %v", simplifiedVC.LeftHandSide, ) } + // - With all negative coefficients + for i, monomial := range lhs { + if monomial.Coefficient != -1.0 { + t.Errorf( + "Expected monomial #%v's coefficient to be -1; received %v", + i, + monomial.Coefficient, + ) + } + } + // Check the right hand side if _, ok := simplifiedVC.RightHandSide.(symbolic.KVector); !ok { t.Errorf(