Skip to content

Introducing the LinearCoeff method as a requirement of the PolynomialLikeVector set of objects #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions symbolic/constant_matrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package symbolic

import (
"fmt"

"github.com/MatProGo-dev/SymbolicMath.go/smErrors"
"gonum.org/v1/gonum/mat"
)
Expand Down
4 changes: 2 additions & 2 deletions symbolic/constant_vector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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...)
}

/*
Expand Down
17 changes: 17 additions & 0 deletions symbolic/monomial_vector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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...)
}
1 change: 1 addition & 0 deletions symbolic/polynomial_like_matrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package symbolic

import (
"fmt"

"gonum.org/v1/gonum/mat"
)

Expand Down
58 changes: 56 additions & 2 deletions symbolic/polynomial_like_vector.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Description:
import (
"fmt"

"github.com/MatProGo-dev/SymbolicMath.go/smErrors"
"gonum.org/v1/gonum/mat"
)

Expand All @@ -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
Expand Down Expand Up @@ -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
}
43 changes: 2 additions & 41 deletions symbolic/polynomial_vector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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...)
}

/*
Expand Down
4 changes: 2 additions & 2 deletions symbolic/variable_vector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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...)
}

/*
Expand Down
10 changes: 6 additions & 4 deletions testing/symbolic/constant_vector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

/*
Expand Down Expand Up @@ -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,
Expand Down
46 changes: 43 additions & 3 deletions testing/symbolic/monomial_vector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

/*
Expand Down Expand Up @@ -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!")
}
}
7 changes: 4 additions & 3 deletions testing/symbolic/polynomial_vector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

/*
Expand Down Expand Up @@ -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) {
Expand Down
71 changes: 71 additions & 0 deletions testing/symbolic/variable_vector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down