-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathalpha.go
97 lines (86 loc) · 2.83 KB
/
alpha.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package stackmc
import (
"gonum.org/v1/gonum/mat"
"gonum.org/v1/gonum/stat"
"gonum.org/v1/gonum/stat/distmv"
)
// AlphaComputer uses the predictions to compute α for each fold. The returned
// alpha is indexed first by fold, and then by fit within that fold.
//
// The computed alpha should respect the information in Folds, using only the data
// specified by the Assess field of the fold.
type AlphaComputer interface {
ComputeAlpha(xs mat.Matrix, fs, weights []float64, p distmv.RandLogProber, folds []Fold, fps []FoldPrediction, evs [][]float64) [][]float64
}
// SingleAlpha asigns a single alpha to all of the folds of each fit. It concatenates
// the predictions for the Assess indices across all of the folds, and calculates
// alpha as
// α = A^-1 b
// A_ij = cov(g_i, g_j)
// b_i = cov(f,g_i)
// the optimal estimator for a fixed control variate.
type SingleAlpha struct{}
var _ AlphaComputer = SingleAlpha{}
func (sa SingleAlpha) ComputeAlpha(xs mat.Matrix, f, weights []float64, d distmv.RandLogProber, folds []Fold, fps []FoldPrediction, evs [][]float64) [][]float64 {
nFitters := len(evs[0])
var totalPoints int
for i := range folds {
totalPoints += len(folds[i].Assess)
}
assessData := mat.NewDense(totalPoints, nFitters+1, nil)
var weightData []float64
if weights != nil {
weightData = make([]float64, totalPoints)
}
var count int
for i := range folds {
for _, v := range folds[i].Assess {
// First column is the actual function value
assessData.Set(count, 0, f[v])
if weights != nil {
weightData[count] = weights[v]
}
// Rest of the columns are the fitter prections, possibly adjusted
// by the fit expected value.
idx, ok := fps[i].ToUniqueIdx[v]
if !ok {
panic("index missing in unique")
}
for k := 0; k < nFitters; k++ {
pred := fps[i].Predictions[k][idx]
assessData.Set(count, k+1, pred)
}
count++
}
}
if count != totalPoints {
panic("bad count")
}
covmat := stat.CovarianceMatrix(nil, assessData, weightData)
// The optimal alpha is A\b where b is variance of f with the fitter (row/column 0)
// and A are the covariances among the fitters (1:end, 1:end).
covarWithF := mat.NewVecDense(nFitters, nil)
for i := 0; i < nFitters; i++ {
covarWithF.SetVec(i, covmat.At(0, i+1))
}
fitterCovar := covmat.SliceSquare(1, nFitters+1).(mat.Symmetric)
var chol mat.Cholesky
ok := chol.Factorize(fitterCovar)
if !ok {
// TODO(btracey): Handle error.
panic("stackmc: cholesky error")
}
alpha := make([]float64, nFitters)
alphaVec := mat.NewVecDense(len(alpha), alpha)
err := chol.SolveVec(alphaVec, covarWithF)
if err != nil {
panic("stackmc: cholesky error")
}
// Set the same alpha for each fit across all of the folds.
alphas := make([][]float64, len(folds))
for i := range alphas {
alphas[i] = make([]float64, nFitters)
copy(alphas[i], alpha)
}
return alphas
}