Skip to content

Commit

Permalink
linsolve: rework testing
Browse files Browse the repository at this point in the history
  • Loading branch information
vladimir-ch committed Jun 25, 2018
1 parent 20f5fb6 commit 44ed8cf
Show file tree
Hide file tree
Showing 2 changed files with 806 additions and 110 deletions.
159 changes: 117 additions & 42 deletions linsolve/iterative_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,71 +6,146 @@ package linsolve

import (
"math"
"math/rand"
"testing"

"golang.org/x/exp/rand"

"gonum.org/v1/gonum/floats"
)

func TestIterativeWithDefault(t *testing.T) {
func TestDefaultMethodDefaultSettings(t *testing.T) {
rnd := rand.New(rand.NewSource(1))

testCases := spdTestCases(rnd)
testCases = append(testCases, unsymTestCases()...)
testIterative(t, nil, testCases)
testCases = append(testCases, unsymTestCases(rnd)...)
for _, tc := range testCases {
testMethodWithSettings(t, nil, nil, tc)
}
}

func TestIterativeWithCG(t *testing.T) {
func TestCG(t *testing.T) {
rnd := rand.New(rand.NewSource(1))

testCases := spdTestCases(rnd)
testIterative(t, &CG{}, testCases)
for _, tc := range testCases {
s := newTestSettings(rnd, tc)
testMethodWithSettings(t, &CG{}, s, tc)
}
}

func TestIterativeWithGMRES(t *testing.T) {
func TestCGDefaultSettings(t *testing.T) {
rnd := rand.New(rand.NewSource(1))

testCases := spdTestCases(rnd)
testCases = append(testCases, unsymTestCases()...)
testIterative(t, &GMRES{}, testCases)
for _, tc := range testCases {
testMethodWithSettings(t, &CG{}, nil, tc)
}
}

func testIterative(t *testing.T, m Method, testCases []testCase) {
const convTol = 1e-14
const defaultWantTol = 1e-10
func TestGMRES(t *testing.T) {
rnd := rand.New(rand.NewSource(1))

testCases := spdTestCases(rnd)
testCases = append(testCases, unsymTestCases(rnd)...)
for _, tc := range testCases {
n := tc.n
// Compute the right-hand side b so that a predetermined vector
// is the solution.
want := make([]float64, n)
for i := range want {
want[i] = 2 + 0.1*float64(i%10)
}
b := make([]float64, n)
tc.MulVecTo(b, false, want)
s := newTestSettings(rnd, tc)
testMethodWithSettings(t, &GMRES{}, s, tc)
}
}

// Initial guess is a random vector.
x := make([]float64, n)
for i := range x {
x[i] = rnd.NormFloat64()
}
func TestGMRESDefaultSettings(t *testing.T) {
rnd := rand.New(rand.NewSource(1))

_, err := Iterative(tc, b, m, &Settings{
InitX: x,
Dst: x,
Tolerance: convTol,
MaxIterations: 40 * n,
})
if err != nil {
t.Errorf("Case %v: unexpected error %v", tc.name, err)
continue
}
testCases := spdTestCases(rnd)
testCases = append(testCases, unsymTestCases(rnd)...)
for _, tc := range testCases {
testMethodWithSettings(t, &GMRES{}, nil, tc)
}
}

func newTestSettings(rnd *rand.Rand, tc testCase) *Settings {
n := len(tc.b)

// Initial guess is a random vector.
initX := make([]float64, n)
for i := range initX {
initX[i] = rnd.NormFloat64()
}

wantTol := tc.tol
if wantTol == 0 {
wantTol = defaultWantTol
// Preallocate a destination slice and fill it with NaN.
dst := make([]float64, n)
for i := range dst {
dst[i] = math.NaN()
}

// Preallocate a work context and fill it with NaN.
work := NewContext(n)
for i := range work.X {
work.X[i] = math.NaN()
work.Residual[i] = math.NaN()
work.Src[i] = math.NaN()
work.Dst[i] = math.NaN()
}
work.ResidualNorm = math.NaN()

return &Settings{
InitX: initX,
Dst: dst,
Tolerance: tc.tol,
MaxIterations: 3 * n,
PreconSolve: tc.PreconSolve,
Work: work,
}
}

func testMethodWithSettings(t *testing.T, m Method, s *Settings, tc testCase) {
wantTol := 1e-11
if s == nil {
wantTol = 1e-7
}

bCopy := make([]float64, len(tc.b))
copy(bCopy, tc.b)

result, err := Iterative(&tc, bCopy, m, s)
if err != nil {
t.Errorf("%v: unexpected error %v", tc.name, err)
return
}

if !floats.Equal(tc.b, bCopy) {
t.Errorf("%v: unexpected modification of b", tc.name)
}

dist := floats.Distance(result.X, tc.want, 2) / floats.Norm(result.X, 2)
if dist > wantTol {
t.Errorf("%v: unexpected solution, |want-got|/|want|=%v", tc.name, dist)
}

if s == nil {
return
}

if result.Stats.Iterations > s.MaxIterations {
t.Errorf("%v: Result.Stats.Iterations greater than Settings.MaxIterations", tc.name)
}

if s.Dst != nil {
if floats.HasNaN(s.Dst) {
t.Errorf("%v: Settings.Dst was not used", tc.name)
}
if !floats.Equal(s.Dst, result.X) {
t.Errorf("%v: Settings.Dst and Result.X not equal", tc.name)
}
dist := floats.Distance(x, want, math.Inf(1))
if dist > wantTol {
t.Errorf("Case %v: unexpected solution, |want-got|=%v", tc.name, dist)
result.X[0] = 123456.7
if s.Dst[0] != result.X[0] {
t.Errorf("%v: Settings.Dst and Result.X are not the same slice", tc.name)
}
}

if s.Work != nil {
if floats.HasNaN(s.Work.X) || floats.HasNaN(s.Work.Residual) || floats.HasNaN(s.Work.Src) || floats.HasNaN(s.Work.Dst) {
t.Errorf("%v: Settings.Work was not used", tc.name)
}
}
}

0 comments on commit 44ed8cf

Please sign in to comment.