diff --git a/symbolic/constant_matrix.go b/symbolic/constant_matrix.go index 303e9d7..b1da688 100644 --- a/symbolic/constant_matrix.go +++ b/symbolic/constant_matrix.go @@ -2,6 +2,7 @@ package symbolic import ( "fmt" + "github.com/MatProGo-dev/SymbolicMath.go/smErrors" "gonum.org/v1/gonum/mat" ) diff --git a/symbolic/constant_vector.go b/symbolic/constant_vector.go index 638aedf..bb7abfe 100644 --- a/symbolic/constant_vector.go +++ b/symbolic/constant_vector.go @@ -101,8 +101,8 @@ Description: This function returns a slice of the coefficients in the expression. For constants, this is always nil. */ -func (kv KVector) LinearCoeff() mat.Dense { - return ZerosMatrix(kv.Len(), kv.Len()) +func (kv KVector) LinearCoeff(wrt ...[]Variable) mat.Dense { + return PolynomialLikeVector_SharedLinearCoeffCalc(kv, wrt...) } /* diff --git a/symbolic/monomial_vector.go b/symbolic/monomial_vector.go index 868f502..8022070 100644 --- a/symbolic/monomial_vector.go +++ b/symbolic/monomial_vector.go @@ -714,3 +714,20 @@ Description: func (mv MonomialVector) Power(exponent int) Expression { return VectorPowerTemplate(mv, exponent) } + +/* +LinearCoeff +Description: + + This function retrieves the "linear coefficient" of the monomial vector. + In math, this is extracting the matrix A such that: + + mv' = L * v + + where: + - v is the vector of variables for the monomial vector. + - mv' is the monomial vector mv with ONLY the terms that have degree 1 +*/ +func (mv MonomialVector) LinearCoeff(wrt ...[]Variable) mat.Dense { + return PolynomialLikeVector_SharedLinearCoeffCalc(mv, wrt...) +} diff --git a/symbolic/polynomial_like_matrix.go b/symbolic/polynomial_like_matrix.go index 27393ce..0bb051a 100644 --- a/symbolic/polynomial_like_matrix.go +++ b/symbolic/polynomial_like_matrix.go @@ -2,6 +2,7 @@ package symbolic import ( "fmt" + "gonum.org/v1/gonum/mat" ) diff --git a/symbolic/polynomial_like_vector.go b/symbolic/polynomial_like_vector.go index 3f17a55..414f97c 100644 --- a/symbolic/polynomial_like_vector.go +++ b/symbolic/polynomial_like_vector.go @@ -9,6 +9,7 @@ Description: import ( "fmt" + "github.com/MatProGo-dev/SymbolicMath.go/smErrors" "gonum.org/v1/gonum/mat" ) @@ -29,8 +30,8 @@ type PolynomialLikeVector interface { // Variables returns the number of variables in the expression. Variables() []Variable - //// Coeffs returns a slice of the coefficients in the expression - //LinearCoeff() mat.Dense + // Coeffs returns a slice of the coefficients in the expression + LinearCoeff(wrt ...[]Variable) mat.Dense // Constant returns the constant additive value in the expression Constant() mat.VecDense @@ -157,3 +158,56 @@ func ToPolynomialLikeVector(e interface{}) (PolynomialLikeVector, error) { ) } } + +/* +PolynomialLikeVector_SharedLinearCoeffCalc +Description: + + This function retrieves the "linear coefficient" of the monomial vector. + In math, this is extracting the matrix A such that: + + mv = L * v + + where v is the vector of variables for the monomial vector. +*/ +func PolynomialLikeVector_SharedLinearCoeffCalc(plv PolynomialLikeVector, wrt ...[]Variable) mat.Dense { + // Input Processing + err := plv.Check() + if err != nil { + panic(err) + } + + // Check to see if the user provided a slice of variables + var wrtVars []Variable + switch len(wrt) { + case 0: + wrtVars = plv.Variables() + case 1: + wrtVars = wrt[0] + default: + panic(fmt.Errorf("Too many inputs provided to LinearCoeff() method.")) + } + + // Check the wrtVars + if len(wrtVars) == 0 { + panic( + smErrors.CanNotGetLinearCoeffOfConstantError{plv}, + ) + } + + // Iterate through each monomial in the vector and extract the Linear Coefficient vector + // for each + L := mat.NewDense(plv.Len(), len(wrtVars), nil) + + for ii := 0; ii < plv.Len(); ii++ { + mvII := plv.AtVec(ii) + + // Get Coefficients for mvII and populate it + coeffsII := mvII.LinearCoeff(wrtVars) + for jj := 0; jj < len(wrtVars); jj++ { + L.Set(ii, jj, coeffsII.AtVec(jj)) + } + } + + return *L +} diff --git a/symbolic/polynomial_vector.go b/symbolic/polynomial_vector.go index 67b8eab..dc86a39 100644 --- a/symbolic/polynomial_vector.go +++ b/symbolic/polynomial_vector.go @@ -161,47 +161,8 @@ Description: The output is a matrix where element (ii,jj) of the matrix describes the coefficient of variable jj (from pv.Variables()) in the polynomial at index ii. */ -func (pv PolynomialVector) LinearCoeff(vSlices ...[]Variable) mat.Dense { - // Input Processing - err := pv.Check() - if err != nil { - panic(err) - } - - // Check to see if the user provided a slice of variables - var varSlice []Variable - switch len(vSlices) { - case 0: - varSlice = pv.Variables() - case 1: - varSlice = vSlices[0] - default: - panic(fmt.Errorf("Too many inputs provided to LinearCoeff() method.")) - } - - if len(varSlice) == 0 { - panic( - smErrors.CanNotGetLinearCoeffOfConstantError{Expression: pv}, - ) - } - - // Constants - var linearCoeff mat.Dense = ZerosMatrix(pv.Len(), len(varSlice)) - - // Algorithm - for rowIndex := 0; rowIndex < pv.Len(); rowIndex++ { - // Row i of the matrix linearCoeff is the linear coefficients of the polynomial at index i - polynomialII := pv[rowIndex] - linearCoeffsII := polynomialII.LinearCoeff(varSlice) - - // Convert linearCoeffsII to a slice of float64's - linearCoeffsIIAsSlice := make([]float64, linearCoeffsII.Len()) - for jj := 0; jj < linearCoeffsII.Len(); jj++ { - linearCoeffsIIAsSlice[jj] = linearCoeffsII.AtVec(jj) - } - linearCoeff.SetRow(rowIndex, linearCoeffsIIAsSlice) - } - return linearCoeff +func (pv PolynomialVector) LinearCoeff(wrt ...[]Variable) mat.Dense { + return PolynomialLikeVector_SharedLinearCoeffCalc(pv, wrt...) } /* diff --git a/symbolic/variable_vector.go b/symbolic/variable_vector.go index c9a2d95..f06468d 100644 --- a/symbolic/variable_vector.go +++ b/symbolic/variable_vector.go @@ -119,8 +119,8 @@ Description: Returns the matrix which is multiplied by Variables to get the current "expression". For a single vector, this is an identity matrix. */ -func (vv VariableVector) LinearCoeff() mat.Dense { - return Identity(vv.Len()) +func (vv VariableVector) LinearCoeff(wrt ...[]Variable) mat.Dense { + return PolynomialLikeVector_SharedLinearCoeffCalc(vv, wrt...) } /* diff --git a/testing/symbolic/constant_vector_test.go b/testing/symbolic/constant_vector_test.go index ffcdf3e..2bd304e 100644 --- a/testing/symbolic/constant_vector_test.go +++ b/testing/symbolic/constant_vector_test.go @@ -8,12 +8,13 @@ Description: import ( "fmt" + "strings" + "testing" + 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" - "strings" - "testing" ) /* @@ -150,11 +151,12 @@ func TestConstantVector_LinearCoeff1(t *testing.T) { // Constants N := 11 kv := symbolic.VecDenseToKVector(symbolic.OnesVector(N)) + vv := symbolic.NewVariableVector(13) // Test for ii := 0; ii < N; ii++ { - for jj := 0; jj < N; jj++ { - if L := kv.LinearCoeff(); L.At(ii, jj) != 0 { + for jj := 0; jj < vv.Len(); jj++ { + if L := kv.LinearCoeff(vv); L.At(ii, jj) != 0 { t.Errorf( "Expected kv.LinearCoeff().At(%v,%v) to be 0; received %v", ii, jj, diff --git a/testing/symbolic/monomial_vector_test.go b/testing/symbolic/monomial_vector_test.go index c08e475..9bc242f 100644 --- a/testing/symbolic/monomial_vector_test.go +++ b/testing/symbolic/monomial_vector_test.go @@ -2,13 +2,14 @@ package symbolic_test import ( "fmt" + "math" + "strings" + "testing" + 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" - "math" - "strings" - "testing" ) /* @@ -1842,3 +1843,42 @@ func TestMonomialVector_Power2(t *testing.T) { ) } } + +/* +TestMonomialVector_LinearCoeff1 +Description: + + Tests that the LinearCoeff1 method properly returns a matrix of all zeros, + when called on a monomial vector containing ALL monomials with degree > 1. +*/ +func TestMonomialVector_LinearCoeff1(t *testing.T) { + // Setup + n := 11 + x1 := symbolic.NewVariable() + x2 := symbolic.NewVariable() + + var mSlice []symbolic.Monomial + for ii := 0; ii < n; ii++ { + // Assemble Monomial slice + mSlice = append( + mSlice, + symbolic.Monomial{ + Coefficient: 3.14, + VariableFactors: []symbolic.Variable{x1, x2}, + Exponents: []int{1, ii + 1}, + }, + ) + } + + // Create monomial vector + mv0 := symbolic.MonomialVector(mSlice) + + // Find LinearCoeff + L0 := mv0.LinearCoeff() + + // Compare all elements to zeros + ZerosMat1 := symbolic.ZerosMatrix(n, 2) + if !mat.EqualApprox(&L0, &ZerosMat1, 0.001) { + t.Errorf("The two matrices are not equal!") + } +} diff --git a/testing/symbolic/polynomial_vector_test.go b/testing/symbolic/polynomial_vector_test.go index 9d4ceea..43ddf48 100644 --- a/testing/symbolic/polynomial_vector_test.go +++ b/testing/symbolic/polynomial_vector_test.go @@ -7,10 +7,11 @@ Description: */ import ( - "github.com/MatProGo-dev/SymbolicMath.go/smErrors" - "github.com/MatProGo-dev/SymbolicMath.go/symbolic" "strings" "testing" + + "github.com/MatProGo-dev/SymbolicMath.go/smErrors" + "github.com/MatProGo-dev/SymbolicMath.go/symbolic" ) /* @@ -525,7 +526,7 @@ func TestPolynomialVector_LinearCoeff1(t *testing.T) { TestPolynomialVector_LinearCoeff2 Description: - This test verifies that the LinearCoeff method panics when a polynomial of all + This test verifies that the LinearCoeff method DOES NOT PANIC when a polynomial of all constants is provided to the method. */ func TestPolynomialVector_LinearCoeff2(t *testing.T) { diff --git a/testing/symbolic/variable_vector_test.go b/testing/symbolic/variable_vector_test.go index 8e78b99..8bdf2a1 100644 --- a/testing/symbolic/variable_vector_test.go +++ b/testing/symbolic/variable_vector_test.go @@ -157,6 +157,77 @@ func TestVariableVector_LinearCoeff1(t *testing.T) { } } +/* +TestVariableVector_LinearCoeff2 +Description: + + Verifies that the LinearCoeff method returns a non-square matrix when it is a + LONG vector composed of only 2 different variables. +*/ +func TestVariableVector_LinearCoeff2(t *testing.T) { + // Constants + N := 111 + vv := symbolic.NewVariableVector(2) + var vvSlice2 []symbolic.Variable + for ii := 0; ii < N; ii++ { + if ii%2 == 0 { + vvSlice2 = append(vvSlice2, vv[0]) + } else { + vvSlice2 = append(vvSlice2, vv[1]) + } + } + vv2 := symbolic.VariableVector(vvSlice2) + + // Test + L2 := vv2.LinearCoeff() + + // Compare dimensions + nRows, nCols := L2.Dims() + if nRows != N { + t.Errorf("Expeected L2 to contain %v rows; received %v", N, nRows) + } + + if nCols != 2 { + t.Errorf("Expected L2 to contain %v rows; received %v", 2, nCols) + } + + // Check the elements + for ii := 0; ii < nRows; ii++ { + if ii%2 == 0 { + if L2.At(ii, 0) != 1 { + t.Errorf( + "Expected vv.LinearCoeff().At(%v,%v) to be 1; received %v", + ii, 0, + L2.At(ii, 0), + ) + } + if L2.At(ii, 1) != 0 { + t.Errorf( + "Expected vv.LinearCoeff().At(%v,%v) to be 0; received %v", + ii, 1, + L2.At(ii, 1), + ) + } + continue + } else { + if L2.At(ii, 0) != 0 { + t.Errorf( + "Expected vv.LinearCoeff().At(%v,%v) to be 0; received %v", + ii, 0, + L2.At(ii, 0), + ) + } + if L2.At(ii, 1) != 1 { + t.Errorf( + "Expected vv.LinearCoeff().At(%v,%v) to be 1; received %v", + ii, 1, + L2.At(ii, 1), + ) + } + } + } +} + /* TestVariableVector_Plus1 Description: