|
|
|
|
// 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 mat
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"gonum.org/v1/gonum/blas"
|
|
|
|
|
"gonum.org/v1/gonum/blas/blas64"
|
|
|
|
|
"gonum.org/v1/gonum/internal/asm/f64"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
vector *VecDense
|
|
|
|
|
|
|
|
|
|
_ Matrix = vector
|
|
|
|
|
_ allMatrix = vector
|
|
|
|
|
_ Vector = vector
|
|
|
|
|
_ Reseter = vector
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Vector is a vector.
|
|
|
|
|
type Vector interface {
|
|
|
|
|
Matrix
|
|
|
|
|
AtVec(int) float64
|
|
|
|
|
Len() int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TransposeVec is a type for performing an implicit transpose of a Vector.
|
|
|
|
|
// It implements the Vector interface, returning values from the transpose
|
|
|
|
|
// of the vector within.
|
|
|
|
|
type TransposeVec struct {
|
|
|
|
|
Vector Vector
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// At returns the value of the element at row i and column j of the transposed
|
|
|
|
|
// matrix, that is, row j and column i of the Vector field.
|
|
|
|
|
func (t TransposeVec) At(i, j int) float64 {
|
|
|
|
|
return t.Vector.At(j, i)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AtVec returns the element at position i. It panics if i is out of bounds.
|
|
|
|
|
func (t TransposeVec) AtVec(i int) float64 {
|
|
|
|
|
return t.Vector.AtVec(i)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Dims returns the dimensions of the transposed vector.
|
|
|
|
|
func (t TransposeVec) Dims() (r, c int) {
|
|
|
|
|
c, r = t.Vector.Dims()
|
|
|
|
|
return r, c
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// T performs an implicit transpose by returning the Vector field.
|
|
|
|
|
func (t TransposeVec) T() Matrix {
|
|
|
|
|
return t.Vector
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Len returns the number of columns in the vector.
|
|
|
|
|
func (t TransposeVec) Len() int {
|
|
|
|
|
return t.Vector.Len()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TVec performs an implicit transpose by returning the Vector field.
|
|
|
|
|
func (t TransposeVec) TVec() Vector {
|
|
|
|
|
return t.Vector
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Untranspose returns the Vector field.
|
|
|
|
|
func (t TransposeVec) Untranspose() Matrix {
|
|
|
|
|
return t.Vector
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t TransposeVec) UntransposeVec() Vector {
|
|
|
|
|
return t.Vector
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VecDense represents a column vector.
|
|
|
|
|
type VecDense struct {
|
|
|
|
|
mat blas64.Vector
|
|
|
|
|
// A BLAS vector can have a negative increment, but allowing this
|
|
|
|
|
// in the mat type complicates a lot of code, and doesn't gain anything.
|
|
|
|
|
// VecDense must have positive increment in this package.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewVecDense creates a new VecDense of length n. If data == nil,
|
|
|
|
|
// a new slice is allocated for the backing slice. If len(data) == n, data is
|
|
|
|
|
// used as the backing slice, and changes to the elements of the returned VecDense
|
|
|
|
|
// will be reflected in data. If neither of these is true, NewVecDense will panic.
|
|
|
|
|
// NewVecDense will panic if n is zero.
|
|
|
|
|
func NewVecDense(n int, data []float64) *VecDense {
|
|
|
|
|
if n <= 0 {
|
|
|
|
|
if n == 0 {
|
|
|
|
|
panic(ErrZeroLength)
|
|
|
|
|
}
|
|
|
|
|
panic("mat: negative dimension")
|
|
|
|
|
}
|
|
|
|
|
if len(data) != n && data != nil {
|
|
|
|
|
panic(ErrShape)
|
|
|
|
|
}
|
|
|
|
|
if data == nil {
|
|
|
|
|
data = make([]float64, n)
|
|
|
|
|
}
|
|
|
|
|
return &VecDense{
|
|
|
|
|
mat: blas64.Vector{
|
|
|
|
|
N: n,
|
|
|
|
|
Inc: 1,
|
|
|
|
|
Data: data,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SliceVec returns a new Vector that shares backing data with the receiver.
|
|
|
|
|
// The returned matrix starts at i of the receiver and extends k-i elements.
|
|
|
|
|
// SliceVec panics with ErrIndexOutOfRange if the slice is outside the capacity
|
|
|
|
|
// of the receiver.
|
|
|
|
|
func (v *VecDense) SliceVec(i, k int) Vector {
|
|
|
|
|
if i < 0 || k <= i || v.Cap() < k {
|
|
|
|
|
panic(ErrIndexOutOfRange)
|
|
|
|
|
}
|
|
|
|
|
return &VecDense{
|
|
|
|
|
mat: blas64.Vector{
|
|
|
|
|
N: k - i,
|
|
|
|
|
Inc: v.mat.Inc,
|
|
|
|
|
Data: v.mat.Data[i*v.mat.Inc : (k-1)*v.mat.Inc+1],
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Dims returns the number of rows and columns in the matrix. Columns is always 1
|
|
|
|
|
// for a non-Reset vector.
|
|
|
|
|
func (v *VecDense) Dims() (r, c int) {
|
|
|
|
|
if v.IsEmpty() {
|
|
|
|
|
return 0, 0
|
|
|
|
|
}
|
|
|
|
|
return v.mat.N, 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Caps returns the number of rows and columns in the backing matrix. Columns is always 1
|
|
|
|
|
// for a non-Reset vector.
|
|
|
|
|
func (v *VecDense) Caps() (r, c int) {
|
|
|
|
|
if v.IsEmpty() {
|
|
|
|
|
return 0, 0
|
|
|
|
|
}
|
|
|
|
|
return v.Cap(), 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Len returns the length of the vector.
|
|
|
|
|
func (v *VecDense) Len() int {
|
|
|
|
|
return v.mat.N
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cap returns the capacity of the vector.
|
|
|
|
|
func (v *VecDense) Cap() int {
|
|
|
|
|
if v.IsEmpty() {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
return (cap(v.mat.Data)-1)/v.mat.Inc + 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// T performs an implicit transpose by returning the receiver inside a Transpose.
|
|
|
|
|
func (v *VecDense) T() Matrix {
|
|
|
|
|
return Transpose{v}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TVec performs an implicit transpose by returning the receiver inside a TransposeVec.
|
|
|
|
|
func (v *VecDense) TVec() Vector {
|
|
|
|
|
return TransposeVec{v}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reset empties the matrix so that it can be reused as the
|
|
|
|
|
// receiver of a dimensionally restricted operation.
|
|
|
|
|
//
|
|
|
|
|
// Reset should not be used when the matrix shares backing data.
|
|
|
|
|
// See the Reseter interface for more information.
|
|
|
|
|
func (v *VecDense) Reset() {
|
|
|
|
|
// No change of Inc or N to 0 may be
|
|
|
|
|
// made unless both are set to 0.
|
|
|
|
|
v.mat.Inc = 0
|
|
|
|
|
v.mat.N = 0
|
|
|
|
|
v.mat.Data = v.mat.Data[:0]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Zero sets all of the matrix elements to zero.
|
|
|
|
|
func (v *VecDense) Zero() {
|
|
|
|
|
for i := 0; i < v.mat.N; i++ {
|
|
|
|
|
v.mat.Data[v.mat.Inc*i] = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CloneVec makes a copy of a into the receiver, overwriting the previous value
|
|
|
|
|
// of the receiver.
|
|
|
|
|
func (v *VecDense) CloneVec(a Vector) {
|
|
|
|
|
if v == a {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
n := a.Len()
|
|
|
|
|
v.mat = blas64.Vector{
|
|
|
|
|
N: n,
|
|
|
|
|
Inc: 1,
|
|
|
|
|
Data: use(v.mat.Data, n),
|
|
|
|
|
}
|
|
|
|
|
if r, ok := a.(RawVectorer); ok {
|
|
|
|
|
blas64.Copy(r.RawVector(), v.mat)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
for i := 0; i < a.Len(); i++ {
|
|
|
|
|
v.SetVec(i, a.AtVec(i))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VecDenseCopyOf returns a newly allocated copy of the elements of a.
|
|
|
|
|
func VecDenseCopyOf(a Vector) *VecDense {
|
|
|
|
|
v := &VecDense{}
|
|
|
|
|
v.CloneVec(a)
|
|
|
|
|
return v
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (v *VecDense) 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 vectors and
|
|
|
|
|
// returns the number of elements it copied.
|
|
|
|
|
func (v *VecDense) CopyVec(a Vector) int {
|
|
|
|
|
n := min(v.Len(), a.Len())
|
|
|
|
|
if v == a {
|
|
|
|
|
return n
|
|
|
|
|
}
|
|
|
|
|
if r, ok := a.(RawVectorer); ok {
|
|
|
|
|
src := r.RawVector()
|
|
|
|
|
src.N = n
|
|
|
|
|
dst := v.mat
|
|
|
|
|
dst.N = n
|
|
|
|
|
blas64.Copy(src, dst)
|
|
|
|
|
return n
|
|
|
|
|
}
|
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
|
v.setVec(i, a.AtVec(i))
|
|
|
|
|
}
|
|
|
|
|
return n
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ScaleVec scales the vector a by alpha, placing the result in the receiver.
|
|
|
|
|
func (v *VecDense) ScaleVec(alpha float64, a Vector) {
|
|
|
|
|
n := a.Len()
|
|
|
|
|
|
|
|
|
|
if v == a {
|
|
|
|
|
if v.mat.Inc == 1 {
|
|
|
|
|
f64.ScalUnitary(alpha, v.mat.Data)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
f64.ScalInc(alpha, v.mat.Data, uintptr(n), uintptr(v.mat.Inc))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v.reuseAsNonZeroed(n)
|
|
|
|
|
|
|
|
|
|
if rv, ok := a.(RawVectorer); ok {
|
|
|
|
|
mat := rv.RawVector()
|
|
|
|
|
v.checkOverlap(mat)
|
|
|
|
|
if v.mat.Inc == 1 && mat.Inc == 1 {
|
|
|
|
|
f64.ScalUnitaryTo(v.mat.Data, alpha, mat.Data)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
f64.ScalIncTo(v.mat.Data, uintptr(v.mat.Inc),
|
|
|
|
|
alpha, mat.Data, uintptr(n), uintptr(mat.Inc))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
|
v.setVec(i, alpha*a.AtVec(i))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AddScaledVec adds the vectors a and alpha*b, placing the result in the receiver.
|
|
|
|
|
func (v *VecDense) AddScaledVec(a Vector, alpha float64, b Vector) {
|
|
|
|
|
if alpha == 1 {
|
|
|
|
|
v.AddVec(a, b)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if alpha == -1 {
|
|
|
|
|
v.SubVec(a, b)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ar := a.Len()
|
|
|
|
|
br := b.Len()
|
|
|
|
|
|
|
|
|
|
if ar != br {
|
|
|
|
|
panic(ErrShape)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var amat, bmat blas64.Vector
|
|
|
|
|
fast := true
|
|
|
|
|
aU, _ := untransposeExtract(a)
|
|
|
|
|
if rv, ok := aU.(*VecDense); ok {
|
|
|
|
|
amat = rv.mat
|
|
|
|
|
if v != a {
|
|
|
|
|
v.checkOverlap(amat)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
fast = false
|
|
|
|
|
}
|
|
|
|
|
bU, _ := untransposeExtract(b)
|
|
|
|
|
if rv, ok := bU.(*VecDense); ok {
|
|
|
|
|
bmat = rv.mat
|
|
|
|
|
if v != b {
|
|
|
|
|
v.checkOverlap(bmat)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
fast = false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v.reuseAsNonZeroed(ar)
|
|
|
|
|
|
|
|
|
|
switch {
|
|
|
|
|
case alpha == 0: // v <- a
|
|
|
|
|
if v == a {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
v.CopyVec(a)
|
|
|
|
|
case v == a && v == b: // v <- v + alpha * v = (alpha + 1) * v
|
|
|
|
|
blas64.Scal(alpha+1, v.mat)
|
|
|
|
|
case !fast: // v <- a + alpha * b without blas64 support.
|
|
|
|
|
for i := 0; i < ar; i++ {
|
|
|
|
|
v.setVec(i, a.AtVec(i)+alpha*b.AtVec(i))
|
|
|
|
|
}
|
|
|
|
|
case v == a && v != b: // v <- v + alpha * b
|
|
|
|
|
if v.mat.Inc == 1 && bmat.Inc == 1 {
|
|
|
|
|
// Fast path for a common case.
|
|
|
|
|
f64.AxpyUnitaryTo(v.mat.Data, alpha, bmat.Data, amat.Data)
|
|
|
|
|
} else {
|
|
|
|
|
f64.AxpyInc(alpha, bmat.Data, v.mat.Data,
|
|
|
|
|
uintptr(ar), uintptr(bmat.Inc), uintptr(v.mat.Inc), 0, 0)
|
|
|
|
|
}
|
|
|
|
|
default: // v <- a + alpha * b or v <- a + alpha * v
|
|
|
|
|
if v.mat.Inc == 1 && amat.Inc == 1 && bmat.Inc == 1 {
|
|
|
|
|
// Fast path for a common case.
|
|
|
|
|
f64.AxpyUnitaryTo(v.mat.Data, alpha, bmat.Data, amat.Data)
|
|
|
|
|
} else {
|
|
|
|
|
f64.AxpyIncTo(v.mat.Data, uintptr(v.mat.Inc), 0,
|
|
|
|
|
alpha, bmat.Data, amat.Data,
|
|
|
|
|
uintptr(ar), uintptr(bmat.Inc), uintptr(amat.Inc), 0, 0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AddVec adds the vectors a and b, placing the result in the receiver.
|
|
|
|
|
func (v *VecDense) AddVec(a, b Vector) {
|
|
|
|
|
ar := a.Len()
|
|
|
|
|
br := b.Len()
|
|
|
|
|
|
|
|
|
|
if ar != br {
|
|
|
|
|
panic(ErrShape)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v.reuseAsNonZeroed(ar)
|
|
|
|
|
|
|
|
|
|
aU, _ := untransposeExtract(a)
|
|
|
|
|
bU, _ := untransposeExtract(b)
|
|
|
|
|
|
|
|
|
|
if arv, ok := aU.(*VecDense); ok {
|
|
|
|
|
if brv, ok := bU.(*VecDense); ok {
|
|
|
|
|
amat := arv.mat
|
|
|
|
|
bmat := brv.mat
|
|
|
|
|
|
|
|
|
|
if v != a {
|
|
|
|
|
v.checkOverlap(amat)
|
|
|
|
|
}
|
|
|
|
|
if v != b {
|
|
|
|
|
v.checkOverlap(bmat)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if v.mat.Inc == 1 && amat.Inc == 1 && bmat.Inc == 1 {
|
|
|
|
|
// Fast path for a common case.
|
|
|
|
|
f64.AxpyUnitaryTo(v.mat.Data, 1, bmat.Data, amat.Data)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
f64.AxpyIncTo(v.mat.Data, uintptr(v.mat.Inc), 0,
|
|
|
|
|
1, bmat.Data, amat.Data,
|
|
|
|
|
uintptr(ar), uintptr(bmat.Inc), uintptr(amat.Inc), 0, 0)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := 0; i < ar; i++ {
|
|
|
|
|
v.setVec(i, a.AtVec(i)+b.AtVec(i))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SubVec subtracts the vector b from a, placing the result in the receiver.
|
|
|
|
|
func (v *VecDense) SubVec(a, b Vector) {
|
|
|
|
|
ar := a.Len()
|
|
|
|
|
br := b.Len()
|
|
|
|
|
|
|
|
|
|
if ar != br {
|
|
|
|
|
panic(ErrShape)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v.reuseAsNonZeroed(ar)
|
|
|
|
|
|
|
|
|
|
aU, _ := untransposeExtract(a)
|
|
|
|
|
bU, _ := untransposeExtract(b)
|
|
|
|
|
|
|
|
|
|
if arv, ok := aU.(*VecDense); ok {
|
|
|
|
|
if brv, ok := bU.(*VecDense); ok {
|
|
|
|
|
amat := arv.mat
|
|
|
|
|
bmat := brv.mat
|
|
|
|
|
|
|
|
|
|
if v != a {
|
|
|
|
|
v.checkOverlap(amat)
|
|
|
|
|
}
|
|
|
|
|
if v != b {
|
|
|
|
|
v.checkOverlap(bmat)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if v.mat.Inc == 1 && amat.Inc == 1 && bmat.Inc == 1 {
|
|
|
|
|
// Fast path for a common case.
|
|
|
|
|
f64.AxpyUnitaryTo(v.mat.Data, -1, bmat.Data, amat.Data)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
f64.AxpyIncTo(v.mat.Data, uintptr(v.mat.Inc), 0,
|
|
|
|
|
-1, bmat.Data, amat.Data,
|
|
|
|
|
uintptr(ar), uintptr(bmat.Inc), uintptr(amat.Inc), 0, 0)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := 0; i < ar; i++ {
|
|
|
|
|
v.setVec(i, a.AtVec(i)-b.AtVec(i))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MulElemVec performs element-wise multiplication of a and b, placing the result
|
|
|
|
|
// in the receiver.
|
|
|
|
|
func (v *VecDense) MulElemVec(a, b Vector) {
|
|
|
|
|
ar := a.Len()
|
|
|
|
|
br := b.Len()
|
|
|
|
|
|
|
|
|
|
if ar != br {
|
|
|
|
|
panic(ErrShape)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v.reuseAsNonZeroed(ar)
|
|
|
|
|
|
|
|
|
|
aU, _ := untransposeExtract(a)
|
|
|
|
|
bU, _ := untransposeExtract(b)
|
|
|
|
|
|
|
|
|
|
if arv, ok := aU.(*VecDense); ok {
|
|
|
|
|
if brv, ok := bU.(*VecDense); ok {
|
|
|
|
|
amat := arv.mat
|
|
|
|
|
bmat := brv.mat
|
|
|
|
|
|
|
|
|
|
if v != a {
|
|
|
|
|
v.checkOverlap(amat)
|
|
|
|
|
}
|
|
|
|
|
if v != b {
|
|
|
|
|
v.checkOverlap(bmat)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if v.mat.Inc == 1 && amat.Inc == 1 && bmat.Inc == 1 {
|
|
|
|
|
// Fast path for a common case.
|
|
|
|
|
for i, a := range amat.Data {
|
|
|
|
|
v.mat.Data[i] = a * bmat.Data[i]
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
var ia, ib int
|
|
|
|
|
for i := 0; i < ar; i++ {
|
|
|
|
|
v.setVec(i, amat.Data[ia]*bmat.Data[ib])
|
|
|
|
|
ia += amat.Inc
|
|
|
|
|
ib += bmat.Inc
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := 0; i < ar; i++ {
|
|
|
|
|
v.setVec(i, a.AtVec(i)*b.AtVec(i))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DivElemVec performs element-wise division of a by b, placing the result
|
|
|
|
|
// in the receiver.
|
|
|
|
|
func (v *VecDense) DivElemVec(a, b Vector) {
|
|
|
|
|
ar := a.Len()
|
|
|
|
|
br := b.Len()
|
|
|
|
|
|
|
|
|
|
if ar != br {
|
|
|
|
|
panic(ErrShape)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v.reuseAsNonZeroed(ar)
|
|
|
|
|
|
|
|
|
|
aU, _ := untransposeExtract(a)
|
|
|
|
|
bU, _ := untransposeExtract(b)
|
|
|
|
|
|
|
|
|
|
if arv, ok := aU.(*VecDense); ok {
|
|
|
|
|
if brv, ok := bU.(*VecDense); ok {
|
|
|
|
|
amat := arv.mat
|
|
|
|
|
bmat := brv.mat
|
|
|
|
|
|
|
|
|
|
if v != a {
|
|
|
|
|
v.checkOverlap(amat)
|
|
|
|
|
}
|
|
|
|
|
if v != b {
|
|
|
|
|
v.checkOverlap(bmat)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if v.mat.Inc == 1 && amat.Inc == 1 && bmat.Inc == 1 {
|
|
|
|
|
// Fast path for a common case.
|
|
|
|
|
for i, a := range amat.Data {
|
|
|
|
|
v.setVec(i, a/bmat.Data[i])
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
var ia, ib int
|
|
|
|
|
for i := 0; i < ar; i++ {
|
|
|
|
|
v.setVec(i, amat.Data[ia]/bmat.Data[ib])
|
|
|
|
|
ia += amat.Inc
|
|
|
|
|
ib += bmat.Inc
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := 0; i < ar; i++ {
|
|
|
|
|
v.setVec(i, a.AtVec(i)/b.AtVec(i))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MulVec computes a * b. 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
|
|
|
|
|
// or if the number of columns in b does not equal 1.
|
|
|
|
|
func (v *VecDense) MulVec(a Matrix, b Vector) {
|
|
|
|
|
r, c := a.Dims()
|
|
|
|
|
br, bc := b.Dims()
|
|
|
|
|
if c != br || bc != 1 {
|
|
|
|
|
panic(ErrShape)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
aU, trans := untransposeExtract(a)
|
|
|
|
|
var bmat blas64.Vector
|
|
|
|
|
fast := true
|
|
|
|
|
bU, _ := untransposeExtract(b)
|
|
|
|
|
if rv, ok := bU.(*VecDense); ok {
|
|
|
|
|
bmat = rv.mat
|
|
|
|
|
if v != b {
|
|
|
|
|
v.checkOverlap(bmat)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
fast = false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v.reuseAsNonZeroed(r)
|
|
|
|
|
var restore func()
|
|
|
|
|
if v == aU {
|
|
|
|
|
v, restore = v.isolatedWorkspace(aU.(*VecDense))
|
|
|
|
|
defer restore()
|
|
|
|
|
} else if v == b {
|
|
|
|
|
v, restore = v.isolatedWorkspace(b)
|
|
|
|
|
defer restore()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO(kortschak): Improve the non-fast paths.
|
|
|
|
|
switch aU := aU.(type) {
|
|
|
|
|
case Vector:
|
|
|
|
|
if b.Len() == 1 {
|
|
|
|
|
// {n,1} x {1,1}
|
|
|
|
|
v.ScaleVec(b.AtVec(0), aU)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// {1,n} x {n,1}
|
|
|
|
|
if fast {
|
|
|
|
|
if rv, ok := aU.(*VecDense); ok {
|
|
|
|
|
amat := rv.mat
|
|
|
|
|
if v != aU {
|
|
|
|
|
v.checkOverlap(amat)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if amat.Inc == 1 && bmat.Inc == 1 {
|
|
|
|
|
// Fast path for a common case.
|
|
|
|
|
v.setVec(0, f64.DotUnitary(amat.Data, bmat.Data))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
v.setVec(0, f64.DotInc(amat.Data, bmat.Data,
|
|
|
|
|
uintptr(c), uintptr(amat.Inc), uintptr(bmat.Inc), 0, 0))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var sum float64
|
|
|
|
|
for i := 0; i < c; i++ {
|
|
|
|
|
sum += aU.AtVec(i) * b.AtVec(i)
|
|
|
|
|
}
|
|
|
|
|
v.setVec(0, sum)
|
|
|
|
|
return
|
|
|
|
|
case *SymBandDense:
|
|
|
|
|
if fast {
|
|
|
|
|
aU.checkOverlap(v.asGeneral())
|
|
|
|
|
blas64.Sbmv(1, aU.mat, bmat, 0, v.mat)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
case *SymDense:
|
|
|
|
|
if fast {
|
|
|
|
|
aU.checkOverlap(v.asGeneral())
|
|
|
|
|
blas64.Symv(1, aU.mat, bmat, 0, v.mat)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
case *TriDense:
|
|
|
|
|
v.CopyVec(b)
|
|
|
|
|
aU.checkOverlap(v.asGeneral())
|
|
|
|
|
ta := blas.NoTrans
|
|
|
|
|
if trans {
|
|
|
|
|
ta = blas.Trans
|
|
|
|
|
}
|
|
|
|
|
blas64.Trmv(ta, aU.mat, v.mat)
|
|
|
|
|
case *Dense:
|
|
|
|
|
if fast {
|
|
|
|
|
aU.checkOverlap(v.asGeneral())
|
|
|
|
|
t := blas.NoTrans
|
|
|
|
|
if trans {
|
|
|
|
|
t = blas.Trans
|
|
|
|
|
}
|
|
|
|
|
blas64.Gemv(t, 1, aU.mat, bmat, 0, v.mat)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
if fast {
|
|
|
|
|
for i := 0; i < r; i++ {
|
|
|
|
|
var f float64
|
|
|
|
|
for j := 0; j < c; j++ {
|
|
|
|
|
f += a.At(i, j) * bmat.Data[j*bmat.Inc]
|
|
|
|
|
}
|
|
|
|
|
v.setVec(i, f)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := 0; i < r; i++ {
|
|
|
|
|
var f float64
|
|
|
|
|
for j := 0; j < c; j++ {
|
|
|
|
|
f += a.At(i, j) * b.AtVec(j)
|
|
|
|
|
}
|
|
|
|
|
v.setVec(i, f)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReuseAsVec changes the receiver if it IsEmpty() to be of size n×1.
|
|
|
|
|
//
|
|
|
|
|
// ReuseAsVec re-uses the backing data slice if it has sufficient capacity,
|
|
|
|
|
// otherwise a new slice is allocated. The backing data is zero on return.
|
|
|
|
|
//
|
|
|
|
|
// ReuseAsVec panics if the receiver is not empty, and panics if
|
|
|
|
|
// the input size is less than one. To empty the receiver for re-use,
|
|
|
|
|
// Reset should be used.
|
|
|
|
|
func (v *VecDense) ReuseAsVec(n int) {
|
|
|
|
|
if n <= 0 {
|
|
|
|
|
if n == 0 {
|
|
|
|
|
panic(ErrZeroLength)
|
|
|
|
|
}
|
|
|
|
|
panic(ErrNegativeDimension)
|
|
|
|
|
}
|
|
|
|
|
if !v.IsEmpty() {
|
|
|
|
|
panic(ErrReuseNonEmpty)
|
|
|
|
|
}
|
|
|
|
|
v.reuseAsZeroed(n)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// reuseAsNonZeroed resizes an empty vector to a r×1 vector,
|
|
|
|
|
// or checks that a non-empty matrix is r×1.
|
|
|
|
|
func (v *VecDense) reuseAsNonZeroed(r int) {
|
|
|
|
|
// reuseAsNonZeroed must be kept in sync with reuseAsZeroed.
|
|
|
|
|
if r == 0 {
|
|
|
|
|
panic(ErrZeroLength)
|
|
|
|
|
}
|
|
|
|
|
if v.IsEmpty() {
|
|
|
|
|
v.mat = blas64.Vector{
|
|
|
|
|
N: r,
|
|
|
|
|
Inc: 1,
|
|
|
|
|
Data: use(v.mat.Data, r),
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if r != v.mat.N {
|
|
|
|
|
panic(ErrShape)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// reuseAsZeroed resizes an empty vector to a r×1 vector,
|
|
|
|
|
// or checks that a non-empty matrix is r×1.
|
|
|
|
|
func (v *VecDense) reuseAsZeroed(r int) {
|
|
|
|
|
// reuseAsZeroed must be kept in sync with reuseAsNonZeroed.
|
|
|
|
|
if r == 0 {
|
|
|
|
|
panic(ErrZeroLength)
|
|
|
|
|
}
|
|
|
|
|
if v.IsEmpty() {
|
|
|
|
|
v.mat = blas64.Vector{
|
|
|
|
|
N: r,
|
|
|
|
|
Inc: 1,
|
|
|
|
|
Data: useZeroed(v.mat.Data, r),
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if r != v.mat.N {
|
|
|
|
|
panic(ErrShape)
|
|
|
|
|
}
|
|
|
|
|
v.Zero()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsEmpty returns whether the receiver is empty. Empty matrices can be the
|
|
|
|
|
// receiver for size-restricted operations. The receiver can be emptied using
|
|
|
|
|
// Reset.
|
|
|
|
|
func (v *VecDense) IsEmpty() bool {
|
|
|
|
|
// It must be the case that v.Dims() returns
|
|
|
|
|
// zeros in this case. See comment in Reset().
|
|
|
|
|
return v.mat.Inc == 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (v *VecDense) isolatedWorkspace(a Vector) (n *VecDense, restore func()) {
|
|
|
|
|
l := a.Len()
|
|
|
|
|
if l == 0 {
|
|
|
|
|
panic(ErrZeroLength)
|
|
|
|
|
}
|
|
|
|
|
n = getWorkspaceVec(l, false)
|
|
|
|
|
return n, func() {
|
|
|
|
|
v.CopyVec(n)
|
|
|
|
|
putWorkspaceVec(n)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// asDense returns a Dense representation of the receiver with the same
|
|
|
|
|
// underlying data.
|
|
|
|
|
func (v *VecDense) asDense() *Dense {
|
|
|
|
|
return &Dense{
|
|
|
|
|
mat: v.asGeneral(),
|
|
|
|
|
capRows: v.mat.N,
|
|
|
|
|
capCols: 1,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// asGeneral returns a blas64.General representation of the receiver with the
|
|
|
|
|
// same underlying data.
|
|
|
|
|
func (v *VecDense) asGeneral() blas64.General {
|
|
|
|
|
return blas64.General{
|
|
|
|
|
Rows: v.mat.N,
|
|
|
|
|
Cols: 1,
|
|
|
|
|
Stride: v.mat.Inc,
|
|
|
|
|
Data: v.mat.Data,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ColViewOf reflects the column j of the RawMatrixer m, into the receiver
|
|
|
|
|
// backed by the same underlying data. The receiver must either be empty
|
|
|
|
|
// have length equal to the number of rows of m.
|
|
|
|
|
func (v *VecDense) ColViewOf(m RawMatrixer, j int) {
|
|
|
|
|
rm := m.RawMatrix()
|
|
|
|
|
|
|
|
|
|
if j >= rm.Cols || j < 0 {
|
|
|
|
|
panic(ErrColAccess)
|
|
|
|
|
}
|
|
|
|
|
if !v.IsEmpty() && v.mat.N != rm.Rows {
|
|
|
|
|
panic(ErrShape)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v.mat.Inc = rm.Stride
|
|
|
|
|
v.mat.Data = rm.Data[j : (rm.Rows-1)*rm.Stride+j+1]
|
|
|
|
|
v.mat.N = rm.Rows
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RowViewOf reflects the row i of the RawMatrixer m, into the receiver
|
|
|
|
|
// backed by the same underlying data. The receiver must either be
|
|
|
|
|
// empty or have length equal to the number of columns of m.
|
|
|
|
|
func (v *VecDense) RowViewOf(m RawMatrixer, i int) {
|
|
|
|
|
rm := m.RawMatrix()
|
|
|
|
|
|
|
|
|
|
if i >= rm.Rows || i < 0 {
|
|
|
|
|
panic(ErrRowAccess)
|
|
|
|
|
}
|
|
|
|
|
if !v.IsEmpty() && v.mat.N != rm.Cols {
|
|
|
|
|
panic(ErrShape)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v.mat.Inc = 1
|
|
|
|
|
v.mat.Data = rm.Data[i*rm.Stride : i*rm.Stride+rm.Cols]
|
|
|
|
|
v.mat.N = rm.Cols
|
|
|
|
|
}
|