blob: 110581ad08779a5d845fcf59e788a6dfccd99902 [file] [log] [blame]
// 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
_ Vector = vector
_ Reseter = vector
)
// Vector is a column vector.
type Vector interface {
Matrix
Len() int
}
// VecDense represents a column vector.
type VecDense struct {
mat blas64.Vector
n int
// 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.
func NewVecDense(n int, data []float64) *VecDense {
if len(data) != n && data != nil {
panic(ErrShape)
}
if data == nil {
data = make([]float64, n)
}
return &VecDense{
mat: blas64.Vector{
Inc: 1,
Data: data,
},
n: n,
}
}
// SliceVec returns a new VecDense 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) *VecDense {
if i < 0 || k <= i || v.Cap() < k {
panic(ErrIndexOutOfRange)
}
return &VecDense{
n: k - i,
mat: blas64.Vector{
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.IsZero() {
return 0, 0
}
return v.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.IsZero() {
return 0, 0
}
return v.Cap(), 1
}
// Len returns the length of the vector.
func (v *VecDense) Len() int {
return v.n
}
// Cap returns the capacity of the vector.
func (v *VecDense) Cap() int {
if v.IsZero() {
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}
}
// Reset zeros the length of the vector so that it can be reused as the
// receiver of a dimensionally restricted operation.
//
// 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.n = 0
v.mat.Data = v.mat.Data[:0]
}
// CloneVec makes a copy of a into the receiver, overwriting the previous value
// of the receiver.
func (v *VecDense) CloneVec(a *VecDense) {
if v == a {
return
}
v.n = a.n
v.mat = blas64.Vector{
Inc: 1,
Data: use(v.mat.Data, v.n),
}
blas64.Copy(v.n, a.mat, v.mat)
}
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 *VecDense) int {
n := min(v.Len(), a.Len())
if v != a {
blas64.Copy(n, a.mat, v.mat)
}
return n
}
// ScaleVec scales the vector a by alpha, placing the result in the receiver.
func (v *VecDense) ScaleVec(alpha float64, a *VecDense) {
n := a.Len()
if v != a {
v.reuseAs(n)
if v.mat.Inc == 1 && a.mat.Inc == 1 {
f64.ScalUnitaryTo(v.mat.Data, alpha, a.mat.Data)
return
}
f64.ScalIncTo(v.mat.Data, uintptr(v.mat.Inc),
alpha, a.mat.Data, uintptr(n), uintptr(a.mat.Inc))
return
}
if v.mat.Inc == 1 {
f64.ScalUnitary(alpha, v.mat.Data)
return
}
f64.ScalInc(alpha, v.mat.Data, uintptr(n), uintptr(v.mat.Inc))
}
// AddScaledVec adds the vectors a and alpha*b, placing the result in the receiver.
func (v *VecDense) AddScaledVec(a *VecDense, alpha float64, b *VecDense) {
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)
}
if v != a {
v.checkOverlap(a.mat)
}
if v != b {
v.checkOverlap(b.mat)
}
v.reuseAs(ar)
switch {
case alpha == 0: // v <- a
v.CopyVec(a)
case v == a && v == b: // v <- v + alpha * v = (alpha + 1) * v
blas64.Scal(ar, alpha+1, v.mat)
case v == a && v != b: // v <- v + alpha * b
if v.mat.Inc == 1 && b.mat.Inc == 1 {
// Fast path for a common case.
f64.AxpyUnitaryTo(v.mat.Data, alpha, b.mat.Data, a.mat.Data)
} else {
f64.AxpyInc(alpha, b.mat.Data, v.mat.Data,
uintptr(ar), uintptr(b.mat.Inc), uintptr(v.mat.Inc), 0, 0)
}
default: // v <- a + alpha * b or v <- a + alpha * v
if v.mat.Inc == 1 && a.mat.Inc == 1 && b.mat.Inc == 1 {
// Fast path for a common case.
f64.AxpyUnitaryTo(v.mat.Data, alpha, b.mat.Data, a.mat.Data)
} else {
f64.AxpyIncTo(v.mat.Data, uintptr(v.mat.Inc), 0,
alpha, b.mat.Data, a.mat.Data,
uintptr(ar), uintptr(b.mat.Inc), uintptr(a.mat.Inc), 0, 0)
}
}
}
// AddVec adds the vectors a and b, placing the result in the receiver.
func (v *VecDense) AddVec(a, b *VecDense) {
ar := a.Len()
br := b.Len()
if ar != br {
panic(ErrShape)
}
if v != a {
v.checkOverlap(a.mat)
}
if v != b {
v.checkOverlap(b.mat)
}
v.reuseAs(ar)
if v.mat.Inc == 1 && a.mat.Inc == 1 && b.mat.Inc == 1 {
// Fast path for a common case.
f64.AxpyUnitaryTo(v.mat.Data, 1, b.mat.Data, a.mat.Data)
return
}
f64.AxpyIncTo(v.mat.Data, uintptr(v.mat.Inc), 0,
1, b.mat.Data, a.mat.Data,
uintptr(ar), uintptr(b.mat.Inc), uintptr(a.mat.Inc), 0, 0)
}
// SubVec subtracts the vector b from a, placing the result in the receiver.
func (v *VecDense) SubVec(a, b *VecDense) {
ar := a.Len()
br := b.Len()
if ar != br {
panic(ErrShape)
}
if v != a {
v.checkOverlap(a.mat)
}
if v != b {
v.checkOverlap(b.mat)
}
v.reuseAs(ar)
if v.mat.Inc == 1 && a.mat.Inc == 1 && b.mat.Inc == 1 {
// Fast path for a common case.
f64.AxpyUnitaryTo(v.mat.Data, -1, b.mat.Data, a.mat.Data)
return
}
f64.AxpyIncTo(v.mat.Data, uintptr(v.mat.Inc), 0,
-1, b.mat.Data, a.mat.Data,
uintptr(ar), uintptr(b.mat.Inc), uintptr(a.mat.Inc), 0, 0)
}
// MulElemVec performs element-wise multiplication of a and b, placing the result
// in the receiver.
func (v *VecDense) MulElemVec(a, b *VecDense) {
ar := a.Len()
br := b.Len()
if ar != br {
panic(ErrShape)
}
if v != a {
v.checkOverlap(a.mat)
}
if v != b {
v.checkOverlap(b.mat)
}
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 *VecDense) DivElemVec(a, b *VecDense) {
ar := a.Len()
br := b.Len()
if ar != br {
panic(ErrShape)
}
if v != a {
v.checkOverlap(a.mat)
}
if v != b {
v.checkOverlap(b.mat)
}
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. 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 *VecDense) MulVec(a Matrix, b *VecDense) {
r, c := a.Dims()
br := b.Len()
if c != br {
panic(ErrShape)
}
if v != b {
v.checkOverlap(b.mat)
}
a, trans := untranspose(a)
ar, ac := a.Dims()
v.reuseAs(r)
var restore func()
if v == a {
v, restore = v.isolatedWorkspace(a.(*VecDense))
defer restore()
} else if v == b {
v, restore = v.isolatedWorkspace(b)
defer restore()
}
switch a := a.(type) {
case *VecDense:
if v != a {
v.checkOverlap(a.mat)
}
if a.Len() == 1 {
// {1,1} x {1,n}
av := a.At(0, 0)
for i := 0; i < b.Len(); i++ {
v.mat.Data[i*v.mat.Inc] = av * b.mat.Data[i*b.mat.Inc]
}
return
}
if b.Len() == 1 {
// {1,n} x {1,1}
bv := b.At(0, 0)
for i := 0; i < a.Len(); i++ {
v.mat.Data[i*v.mat.Inc] = bv * a.mat.Data[i*a.mat.Inc]
}
return
}
// {n,1} x {1,n}
var sum float64
for i := 0; i < c; i++ {
sum += a.At(i, 0) * b.At(i, 0)
}
v.SetVec(0, sum)
return
case RawSymmetricer:
amat := a.RawSymmetric()
blas64.Symv(1, amat, b.mat, 0, v.mat)
case RawTriangular:
v.CopyVec(b)
amat := a.RawTriangular()
ta := blas.NoTrans
if trans {
ta = blas.Trans
}
blas64.Trmv(ta, amat, v.mat)
case RawMatrixer:
amat := a.RawMatrix()
// We don't know that a is a *Dense, so make
// a temporary Dense to check overlap.
(&Dense{mat: amat}).checkOverlap(v.asGeneral())
t := blas.NoTrans
if trans {
t = blas.Trans
}
blas64.Gemv(t, 1, amat, b.mat, 0, v.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]
}
v.mat.Data[c*v.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]
}
v.mat.Data[r*v.mat.Inc] = f
}
}
}
}
// reuseAs resizes an empty vector to a r×1 vector,
// or checks that a non-empty matrix is r×1.
func (v *VecDense) 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)
}
}
// IsZero returns whether the receiver is zero-sized. Zero-sized vectors can be the
// receiver for size-restricted operations. VecDenses can be zeroed using Reset.
func (v *VecDense) IsZero() 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 *VecDense) (n *VecDense, restore func()) {
l := a.Len()
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.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.n,
Cols: 1,
Stride: v.mat.Inc,
Data: v.mat.Data,
}
}