Skip to content

Commit 34e6b24

Browse files
kwesiRutledgeKwesi Rutledge
and
Kwesi Rutledge
authored
Introducing the LinearCoeff method as a requirement of the PolynomialLikeVector set of objects (#15)
* Reintroduced LinearCoeff function for PolynomialLikeVector interface and got it working! (Basically) * Added sanity test to check a corner case for the variable vector's LinearCoeff function --------- Co-authored-by: Kwesi Rutledge <[email protected]>
1 parent c7ac66e commit 34e6b24

11 files changed

+205
-57
lines changed

symbolic/constant_matrix.go

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package symbolic
22

33
import (
44
"fmt"
5+
56
"github.com/MatProGo-dev/SymbolicMath.go/smErrors"
67
"gonum.org/v1/gonum/mat"
78
)

symbolic/constant_vector.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ Description:
101101
102102
This function returns a slice of the coefficients in the expression. For constants, this is always nil.
103103
*/
104-
func (kv KVector) LinearCoeff() mat.Dense {
105-
return ZerosMatrix(kv.Len(), kv.Len())
104+
func (kv KVector) LinearCoeff(wrt ...[]Variable) mat.Dense {
105+
return PolynomialLikeVector_SharedLinearCoeffCalc(kv, wrt...)
106106
}
107107

108108
/*

symbolic/monomial_vector.go

+17
Original file line numberDiff line numberDiff line change
@@ -714,3 +714,20 @@ Description:
714714
func (mv MonomialVector) Power(exponent int) Expression {
715715
return VectorPowerTemplate(mv, exponent)
716716
}
717+
718+
/*
719+
LinearCoeff
720+
Description:
721+
722+
This function retrieves the "linear coefficient" of the monomial vector.
723+
In math, this is extracting the matrix A such that:
724+
725+
mv' = L * v
726+
727+
where:
728+
- v is the vector of variables for the monomial vector.
729+
- mv' is the monomial vector mv with ONLY the terms that have degree 1
730+
*/
731+
func (mv MonomialVector) LinearCoeff(wrt ...[]Variable) mat.Dense {
732+
return PolynomialLikeVector_SharedLinearCoeffCalc(mv, wrt...)
733+
}

symbolic/polynomial_like_matrix.go

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package symbolic
22

33
import (
44
"fmt"
5+
56
"gonum.org/v1/gonum/mat"
67
)
78

symbolic/polynomial_like_vector.go

+56-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Description:
99
import (
1010
"fmt"
1111

12+
"github.com/MatProGo-dev/SymbolicMath.go/smErrors"
1213
"gonum.org/v1/gonum/mat"
1314
)
1415

@@ -29,8 +30,8 @@ type PolynomialLikeVector interface {
2930
// Variables returns the number of variables in the expression.
3031
Variables() []Variable
3132

32-
//// Coeffs returns a slice of the coefficients in the expression
33-
//LinearCoeff() mat.Dense
33+
// Coeffs returns a slice of the coefficients in the expression
34+
LinearCoeff(wrt ...[]Variable) mat.Dense
3435

3536
// Constant returns the constant additive value in the expression
3637
Constant() mat.VecDense
@@ -157,3 +158,56 @@ func ToPolynomialLikeVector(e interface{}) (PolynomialLikeVector, error) {
157158
)
158159
}
159160
}
161+
162+
/*
163+
PolynomialLikeVector_SharedLinearCoeffCalc
164+
Description:
165+
166+
This function retrieves the "linear coefficient" of the monomial vector.
167+
In math, this is extracting the matrix A such that:
168+
169+
mv = L * v
170+
171+
where v is the vector of variables for the monomial vector.
172+
*/
173+
func PolynomialLikeVector_SharedLinearCoeffCalc(plv PolynomialLikeVector, wrt ...[]Variable) mat.Dense {
174+
// Input Processing
175+
err := plv.Check()
176+
if err != nil {
177+
panic(err)
178+
}
179+
180+
// Check to see if the user provided a slice of variables
181+
var wrtVars []Variable
182+
switch len(wrt) {
183+
case 0:
184+
wrtVars = plv.Variables()
185+
case 1:
186+
wrtVars = wrt[0]
187+
default:
188+
panic(fmt.Errorf("Too many inputs provided to LinearCoeff() method."))
189+
}
190+
191+
// Check the wrtVars
192+
if len(wrtVars) == 0 {
193+
panic(
194+
smErrors.CanNotGetLinearCoeffOfConstantError{plv},
195+
)
196+
}
197+
198+
// Iterate through each monomial in the vector and extract the Linear Coefficient vector
199+
// for each
200+
L := mat.NewDense(plv.Len(), len(wrtVars), nil)
201+
202+
for ii := 0; ii < plv.Len(); ii++ {
203+
mvII := plv.AtVec(ii)
204+
205+
// Get Coefficients for mvII and populate it
206+
coeffsII := mvII.LinearCoeff(wrtVars)
207+
for jj := 0; jj < len(wrtVars); jj++ {
208+
L.Set(ii, jj, coeffsII.AtVec(jj))
209+
}
210+
}
211+
212+
return *L
213+
}

symbolic/polynomial_vector.go

+2-41
Original file line numberDiff line numberDiff line change
@@ -161,47 +161,8 @@ Description:
161161
The output is a matrix where element (ii,jj) of the matrix describes the coefficient
162162
of variable jj (from pv.Variables()) in the polynomial at index ii.
163163
*/
164-
func (pv PolynomialVector) LinearCoeff(vSlices ...[]Variable) mat.Dense {
165-
// Input Processing
166-
err := pv.Check()
167-
if err != nil {
168-
panic(err)
169-
}
170-
171-
// Check to see if the user provided a slice of variables
172-
var varSlice []Variable
173-
switch len(vSlices) {
174-
case 0:
175-
varSlice = pv.Variables()
176-
case 1:
177-
varSlice = vSlices[0]
178-
default:
179-
panic(fmt.Errorf("Too many inputs provided to LinearCoeff() method."))
180-
}
181-
182-
if len(varSlice) == 0 {
183-
panic(
184-
smErrors.CanNotGetLinearCoeffOfConstantError{Expression: pv},
185-
)
186-
}
187-
188-
// Constants
189-
var linearCoeff mat.Dense = ZerosMatrix(pv.Len(), len(varSlice))
190-
191-
// Algorithm
192-
for rowIndex := 0; rowIndex < pv.Len(); rowIndex++ {
193-
// Row i of the matrix linearCoeff is the linear coefficients of the polynomial at index i
194-
polynomialII := pv[rowIndex]
195-
linearCoeffsII := polynomialII.LinearCoeff(varSlice)
196-
197-
// Convert linearCoeffsII to a slice of float64's
198-
linearCoeffsIIAsSlice := make([]float64, linearCoeffsII.Len())
199-
for jj := 0; jj < linearCoeffsII.Len(); jj++ {
200-
linearCoeffsIIAsSlice[jj] = linearCoeffsII.AtVec(jj)
201-
}
202-
linearCoeff.SetRow(rowIndex, linearCoeffsIIAsSlice)
203-
}
204-
return linearCoeff
164+
func (pv PolynomialVector) LinearCoeff(wrt ...[]Variable) mat.Dense {
165+
return PolynomialLikeVector_SharedLinearCoeffCalc(pv, wrt...)
205166
}
206167

207168
/*

symbolic/variable_vector.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,8 @@ Description:
119119
Returns the matrix which is multiplied by Variables to get the current "expression".
120120
For a single vector, this is an identity matrix.
121121
*/
122-
func (vv VariableVector) LinearCoeff() mat.Dense {
123-
return Identity(vv.Len())
122+
func (vv VariableVector) LinearCoeff(wrt ...[]Variable) mat.Dense {
123+
return PolynomialLikeVector_SharedLinearCoeffCalc(vv, wrt...)
124124
}
125125

126126
/*

testing/symbolic/constant_vector_test.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ Description:
88

99
import (
1010
"fmt"
11+
"strings"
12+
"testing"
13+
1114
getKVector "github.com/MatProGo-dev/SymbolicMath.go/get/KVector"
1215
"github.com/MatProGo-dev/SymbolicMath.go/smErrors"
1316
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
1417
"gonum.org/v1/gonum/mat"
15-
"strings"
16-
"testing"
1718
)
1819

1920
/*
@@ -150,11 +151,12 @@ func TestConstantVector_LinearCoeff1(t *testing.T) {
150151
// Constants
151152
N := 11
152153
kv := symbolic.VecDenseToKVector(symbolic.OnesVector(N))
154+
vv := symbolic.NewVariableVector(13)
153155

154156
// Test
155157
for ii := 0; ii < N; ii++ {
156-
for jj := 0; jj < N; jj++ {
157-
if L := kv.LinearCoeff(); L.At(ii, jj) != 0 {
158+
for jj := 0; jj < vv.Len(); jj++ {
159+
if L := kv.LinearCoeff(vv); L.At(ii, jj) != 0 {
158160
t.Errorf(
159161
"Expected kv.LinearCoeff().At(%v,%v) to be 0; received %v",
160162
ii, jj,

testing/symbolic/monomial_vector_test.go

+43-3
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ package symbolic_test
22

33
import (
44
"fmt"
5+
"math"
6+
"strings"
7+
"testing"
8+
59
getKVector "github.com/MatProGo-dev/SymbolicMath.go/get/KVector"
610
"github.com/MatProGo-dev/SymbolicMath.go/smErrors"
711
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
812
"gonum.org/v1/gonum/mat"
9-
"math"
10-
"strings"
11-
"testing"
1213
)
1314

1415
/*
@@ -1842,3 +1843,42 @@ func TestMonomialVector_Power2(t *testing.T) {
18421843
)
18431844
}
18441845
}
1846+
1847+
/*
1848+
TestMonomialVector_LinearCoeff1
1849+
Description:
1850+
1851+
Tests that the LinearCoeff1 method properly returns a matrix of all zeros,
1852+
when called on a monomial vector containing ALL monomials with degree > 1.
1853+
*/
1854+
func TestMonomialVector_LinearCoeff1(t *testing.T) {
1855+
// Setup
1856+
n := 11
1857+
x1 := symbolic.NewVariable()
1858+
x2 := symbolic.NewVariable()
1859+
1860+
var mSlice []symbolic.Monomial
1861+
for ii := 0; ii < n; ii++ {
1862+
// Assemble Monomial slice
1863+
mSlice = append(
1864+
mSlice,
1865+
symbolic.Monomial{
1866+
Coefficient: 3.14,
1867+
VariableFactors: []symbolic.Variable{x1, x2},
1868+
Exponents: []int{1, ii + 1},
1869+
},
1870+
)
1871+
}
1872+
1873+
// Create monomial vector
1874+
mv0 := symbolic.MonomialVector(mSlice)
1875+
1876+
// Find LinearCoeff
1877+
L0 := mv0.LinearCoeff()
1878+
1879+
// Compare all elements to zeros
1880+
ZerosMat1 := symbolic.ZerosMatrix(n, 2)
1881+
if !mat.EqualApprox(&L0, &ZerosMat1, 0.001) {
1882+
t.Errorf("The two matrices are not equal!")
1883+
}
1884+
}

testing/symbolic/polynomial_vector_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ Description:
77
*/
88

99
import (
10-
"github.com/MatProGo-dev/SymbolicMath.go/smErrors"
11-
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
1210
"strings"
1311
"testing"
12+
13+
"github.com/MatProGo-dev/SymbolicMath.go/smErrors"
14+
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
1415
)
1516

1617
/*
@@ -525,7 +526,7 @@ func TestPolynomialVector_LinearCoeff1(t *testing.T) {
525526
TestPolynomialVector_LinearCoeff2
526527
Description:
527528
528-
This test verifies that the LinearCoeff method panics when a polynomial of all
529+
This test verifies that the LinearCoeff method DOES NOT PANIC when a polynomial of all
529530
constants is provided to the method.
530531
*/
531532
func TestPolynomialVector_LinearCoeff2(t *testing.T) {

testing/symbolic/variable_vector_test.go

+71
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,77 @@ func TestVariableVector_LinearCoeff1(t *testing.T) {
157157
}
158158
}
159159

160+
/*
161+
TestVariableVector_LinearCoeff2
162+
Description:
163+
164+
Verifies that the LinearCoeff method returns a non-square matrix when it is a
165+
LONG vector composed of only 2 different variables.
166+
*/
167+
func TestVariableVector_LinearCoeff2(t *testing.T) {
168+
// Constants
169+
N := 111
170+
vv := symbolic.NewVariableVector(2)
171+
var vvSlice2 []symbolic.Variable
172+
for ii := 0; ii < N; ii++ {
173+
if ii%2 == 0 {
174+
vvSlice2 = append(vvSlice2, vv[0])
175+
} else {
176+
vvSlice2 = append(vvSlice2, vv[1])
177+
}
178+
}
179+
vv2 := symbolic.VariableVector(vvSlice2)
180+
181+
// Test
182+
L2 := vv2.LinearCoeff()
183+
184+
// Compare dimensions
185+
nRows, nCols := L2.Dims()
186+
if nRows != N {
187+
t.Errorf("Expeected L2 to contain %v rows; received %v", N, nRows)
188+
}
189+
190+
if nCols != 2 {
191+
t.Errorf("Expected L2 to contain %v rows; received %v", 2, nCols)
192+
}
193+
194+
// Check the elements
195+
for ii := 0; ii < nRows; ii++ {
196+
if ii%2 == 0 {
197+
if L2.At(ii, 0) != 1 {
198+
t.Errorf(
199+
"Expected vv.LinearCoeff().At(%v,%v) to be 1; received %v",
200+
ii, 0,
201+
L2.At(ii, 0),
202+
)
203+
}
204+
if L2.At(ii, 1) != 0 {
205+
t.Errorf(
206+
"Expected vv.LinearCoeff().At(%v,%v) to be 0; received %v",
207+
ii, 1,
208+
L2.At(ii, 1),
209+
)
210+
}
211+
continue
212+
} else {
213+
if L2.At(ii, 0) != 0 {
214+
t.Errorf(
215+
"Expected vv.LinearCoeff().At(%v,%v) to be 0; received %v",
216+
ii, 0,
217+
L2.At(ii, 0),
218+
)
219+
}
220+
if L2.At(ii, 1) != 1 {
221+
t.Errorf(
222+
"Expected vv.LinearCoeff().At(%v,%v) to be 1; received %v",
223+
ii, 1,
224+
L2.At(ii, 1),
225+
)
226+
}
227+
}
228+
}
229+
}
230+
160231
/*
161232
TestVariableVector_Plus1
162233
Description:

0 commit comments

Comments
 (0)