forked from LaconicNetwork/kompose
350 lines
7.2 KiB
Go
350 lines
7.2 KiB
Go
// Copyright ©2013 The gonum Authors. All rights reserved.
|
||
// Use of this source code is governed by a BSD-style
|
||
// license that can be found in the LICENSE file.
|
||
|
||
package mat64
|
||
|
||
import (
|
||
"math"
|
||
|
||
"github.com/gonum/blas"
|
||
"github.com/gonum/blas/blas64"
|
||
)
|
||
|
||
var (
|
||
vector *Vector
|
||
|
||
_ Matrix = vector
|
||
|
||
// _ Cloner = vector
|
||
// _ Viewer = vector
|
||
// _ Subvectorer = vector
|
||
|
||
// _ Adder = vector
|
||
// _ Suber = vector
|
||
// _ Muler = vector
|
||
// _ Dotter = vector
|
||
// _ ElemMuler = vector
|
||
|
||
// _ Scaler = vector
|
||
// _ Applyer = vector
|
||
|
||
// _ Normer = vector
|
||
// _ Sumer = vector
|
||
|
||
// _ Stacker = vector
|
||
// _ Augmenter = vector
|
||
|
||
// _ Equaler = vector
|
||
// _ ApproxEqualer = vector
|
||
|
||
// _ RawMatrixLoader = vector
|
||
// _ RawMatrixer = vector
|
||
)
|
||
|
||
// Vector represents a column vector.
|
||
type Vector struct {
|
||
mat blas64.Vector
|
||
n int
|
||
// A BLAS vector can have a negative increment, but allowing this
|
||
// in the mat64 type complicates a lot of code, and doesn't gain anything.
|
||
// Vector must have positive increment in this package.
|
||
}
|
||
|
||
// NewVector creates a new Vector of length n. If len(data) == n, data is used
|
||
// as the backing data slice. If data == nil, a new slice is allocated. If
|
||
// neither of these is true, NewVector will panic.
|
||
func NewVector(n int, data []float64) *Vector {
|
||
if len(data) != n && data != nil {
|
||
panic(ErrShape)
|
||
}
|
||
if data == nil {
|
||
data = make([]float64, n)
|
||
}
|
||
return &Vector{
|
||
mat: blas64.Vector{
|
||
Inc: 1,
|
||
Data: data,
|
||
},
|
||
n: n,
|
||
}
|
||
}
|
||
|
||
// ViewVec returns a sub-vector view of the receiver starting at element i and
|
||
// extending n columns. If i is out of range, or if n is zero or extend beyond the
|
||
// bounds of the Vector ViewVec will panic with ErrIndexOutOfRange. The returned
|
||
// Vector retains reference to the underlying vector.
|
||
func (v *Vector) ViewVec(i, n int) *Vector {
|
||
if i+n > v.n {
|
||
panic(ErrIndexOutOfRange)
|
||
}
|
||
return &Vector{
|
||
n: n,
|
||
mat: blas64.Vector{
|
||
Inc: v.mat.Inc,
|
||
Data: v.mat.Data[i*v.mat.Inc:],
|
||
},
|
||
}
|
||
}
|
||
|
||
func (v *Vector) Dims() (r, c int) { return v.n, 1 }
|
||
|
||
// Len returns the length of the vector.
|
||
func (v *Vector) Len() int {
|
||
return v.n
|
||
}
|
||
|
||
func (v *Vector) Reset() {
|
||
v.mat.Data = v.mat.Data[:0]
|
||
v.mat.Inc = 0
|
||
v.n = 0
|
||
}
|
||
|
||
func (v *Vector) RawVector() blas64.Vector {
|
||
return v.mat
|
||
}
|
||
|
||
// CopyVec makes a copy of elements of a into the receiver. It is similar to the
|
||
// built-in copy; it copies as much as the overlap between the two matrices and
|
||
// returns the number of rows and columns it copied.
|
||
func (v *Vector) CopyVec(a *Vector) (n int) {
|
||
n = min(v.Len(), a.Len())
|
||
blas64.Copy(n, a.mat, v.mat)
|
||
return n
|
||
}
|
||
|
||
// AddVec adds a and b element-wise, placing the result in the receiver.
|
||
func (v *Vector) AddVec(a, b *Vector) {
|
||
ar := a.Len()
|
||
br := b.Len()
|
||
|
||
if ar != br {
|
||
panic(ErrShape)
|
||
}
|
||
|
||
v.reuseAs(ar)
|
||
|
||
amat, bmat := a.RawVector(), b.RawVector()
|
||
for i := 0; i < v.n; i++ {
|
||
v.mat.Data[i*v.mat.Inc] = amat.Data[i*amat.Inc] + bmat.Data[i*bmat.Inc]
|
||
}
|
||
}
|
||
|
||
// SubVec subtracts the vector b from a, placing the result in the receiver.
|
||
func (v *Vector) SubVec(a, b *Vector) {
|
||
ar := a.Len()
|
||
br := b.Len()
|
||
|
||
if ar != br {
|
||
panic(ErrShape)
|
||
}
|
||
|
||
v.reuseAs(ar)
|
||
|
||
amat, bmat := a.RawVector(), b.RawVector()
|
||
for i := 0; i < v.n; i++ {
|
||
v.mat.Data[i*v.mat.Inc] = amat.Data[i*amat.Inc] - bmat.Data[i*bmat.Inc]
|
||
}
|
||
}
|
||
|
||
// MulElemVec performs element-wise multiplication of a and b, placing the result
|
||
// in the receiver.
|
||
func (v *Vector) MulElemVec(a, b *Vector) {
|
||
ar := a.Len()
|
||
br := b.Len()
|
||
|
||
if ar != br {
|
||
panic(ErrShape)
|
||
}
|
||
|
||
v.reuseAs(ar)
|
||
|
||
amat, bmat := a.RawVector(), b.RawVector()
|
||
for i := 0; i < v.n; i++ {
|
||
v.mat.Data[i*v.mat.Inc] = amat.Data[i*amat.Inc] * bmat.Data[i*bmat.Inc]
|
||
}
|
||
}
|
||
|
||
// DivElemVec performs element-wise division of a by b, placing the result
|
||
// in the receiver.
|
||
func (v *Vector) DivElemVec(a, b *Vector) {
|
||
ar := a.Len()
|
||
br := b.Len()
|
||
|
||
if ar != br {
|
||
panic(ErrShape)
|
||
}
|
||
|
||
v.reuseAs(ar)
|
||
|
||
amat, bmat := a.RawVector(), b.RawVector()
|
||
for i := 0; i < v.n; i++ {
|
||
v.mat.Data[i*v.mat.Inc] = amat.Data[i*amat.Inc] / bmat.Data[i*bmat.Inc]
|
||
}
|
||
}
|
||
|
||
// MulVec computes a * b if trans == false and a^T * b if trans == true. The
|
||
// result is stored into the receiver. MulVec panics if the number of columns in
|
||
// a does not equal the number of rows in b.
|
||
func (v *Vector) MulVec(a Matrix, trans bool, b *Vector) {
|
||
ar, ac := a.Dims()
|
||
br := b.Len()
|
||
if trans {
|
||
if ar != br {
|
||
panic(ErrShape)
|
||
}
|
||
} else {
|
||
if ac != br {
|
||
panic(ErrShape)
|
||
}
|
||
}
|
||
|
||
var w Vector
|
||
if v != a && v != b {
|
||
w = *v
|
||
}
|
||
if w.n == 0 {
|
||
if trans {
|
||
w.mat.Data = use(w.mat.Data, ac)
|
||
} else {
|
||
w.mat.Data = use(w.mat.Data, ar)
|
||
}
|
||
|
||
w.mat.Inc = 1
|
||
w.n = ar
|
||
if trans {
|
||
w.n = ac
|
||
}
|
||
} else {
|
||
if trans {
|
||
if ac != w.n {
|
||
panic(ErrShape)
|
||
}
|
||
} else {
|
||
if ar != w.n {
|
||
panic(ErrShape)
|
||
}
|
||
}
|
||
}
|
||
|
||
switch a := a.(type) {
|
||
case RawSymmetricer:
|
||
amat := a.RawSymmetric()
|
||
blas64.Symv(1, amat, b.mat, 0, w.mat)
|
||
case RawTriangular:
|
||
w.CopyVec(b)
|
||
amat := a.RawTriangular()
|
||
ta := blas.NoTrans
|
||
if trans {
|
||
ta = blas.Trans
|
||
}
|
||
blas64.Trmv(ta, amat, w.mat)
|
||
case RawMatrixer:
|
||
amat := a.RawMatrix()
|
||
t := blas.NoTrans
|
||
if trans {
|
||
t = blas.Trans
|
||
}
|
||
blas64.Gemv(t, 1, amat, b.mat, 0, w.mat)
|
||
case Vectorer:
|
||
if trans {
|
||
col := make([]float64, ar)
|
||
for c := 0; c < ac; c++ {
|
||
w.mat.Data[c*w.mat.Inc] = blas64.Dot(ar,
|
||
blas64.Vector{Inc: 1, Data: a.Col(col, c)},
|
||
b.mat,
|
||
)
|
||
}
|
||
} else {
|
||
row := make([]float64, ac)
|
||
for r := 0; r < ar; r++ {
|
||
w.mat.Data[r*w.mat.Inc] = blas64.Dot(ac,
|
||
blas64.Vector{Inc: 1, Data: a.Row(row, r)},
|
||
b.mat,
|
||
)
|
||
}
|
||
}
|
||
default:
|
||
if trans {
|
||
col := make([]float64, ar)
|
||
for c := 0; c < ac; c++ {
|
||
for i := range col {
|
||
col[i] = a.At(i, c)
|
||
}
|
||
var f float64
|
||
for i, e := range col {
|
||
f += e * b.mat.Data[i*b.mat.Inc]
|
||
}
|
||
w.mat.Data[c*w.mat.Inc] = f
|
||
}
|
||
} else {
|
||
row := make([]float64, ac)
|
||
for r := 0; r < ar; r++ {
|
||
for i := range row {
|
||
row[i] = a.At(r, i)
|
||
}
|
||
var f float64
|
||
for i, e := range row {
|
||
f += e * b.mat.Data[i*b.mat.Inc]
|
||
}
|
||
w.mat.Data[r*w.mat.Inc] = f
|
||
}
|
||
}
|
||
}
|
||
*v = w
|
||
}
|
||
|
||
// Equals compares the vectors represented by b and the receiver and returns true
|
||
// if the vectors are element-wise equal.
|
||
func (v *Vector) EqualsVec(b *Vector) bool {
|
||
n := v.Len()
|
||
nb := b.Len()
|
||
if n != nb {
|
||
return false
|
||
}
|
||
for i := 0; i < n; i++ {
|
||
if v.mat.Data[i*v.mat.Inc] != b.mat.Data[i*b.mat.Inc] {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
// EqualsApproxVec compares the vectors represented by b and the receiver, with
|
||
// tolerance for element-wise equality specified by epsilon.
|
||
func (v *Vector) EqualsApproxVec(b *Vector, epsilon float64) bool {
|
||
n := v.Len()
|
||
nb := b.Len()
|
||
if n != nb {
|
||
return false
|
||
}
|
||
for i := 0; i < n; i++ {
|
||
if math.Abs(v.mat.Data[i*v.mat.Inc]-b.mat.Data[i*b.mat.Inc]) > epsilon {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
// reuseAs resizes an empty vector to a r×1 vector,
|
||
// or checks that a non-empty matrix is r×1.
|
||
func (v *Vector) reuseAs(r int) {
|
||
if v.isZero() {
|
||
v.mat = blas64.Vector{
|
||
Inc: 1,
|
||
Data: use(v.mat.Data, r),
|
||
}
|
||
v.n = r
|
||
return
|
||
}
|
||
if r != v.n {
|
||
panic(ErrShape)
|
||
}
|
||
}
|
||
|
||
func (v *Vector) isZero() bool {
|
||
// It must be the case that v.Dims() returns
|
||
// zeros in this case. See comment in Reset().
|
||
return v.mat.Inc == 0
|
||
}
|