mat: retain type and dimension attributes in encodings

This commit is contained in:
Dan Kortschak
2018-06-01 21:51:20 +09:30
committed by GitHub
parent 6ee525f5f2
commit 57680b76df
2 changed files with 229 additions and 109 deletions

294
mat/io.go
View File

@@ -5,51 +5,86 @@
package mat package mat
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt"
"io" "io"
"math" "math"
) )
const ( // version is the current on-disk codec version.
// maxLen is the biggest slice/array len one can create on a 32/64b platform. const version uint32 = 0x1
maxLen = int64(int(^uint(0) >> 1))
) // maxLen is the biggest slice/array len one can create on a 32/64b platform.
const maxLen = int64(int(^uint(0) >> 1))
var ( var (
headerSize = binary.Size(storage{})
sizeInt64 = binary.Size(int64(0)) sizeInt64 = binary.Size(int64(0))
sizeFloat64 = binary.Size(float64(0)) sizeFloat64 = binary.Size(float64(0))
errWrongType = errors.New("mat: wrong data type")
errTooBig = errors.New("mat: resulting data slice too big") errTooBig = errors.New("mat: resulting data slice too big")
errTooSmall = errors.New("mat: input slice too small") errTooSmall = errors.New("mat: input slice too small")
errBadBuffer = errors.New("mat: data buffer size mismatch") errBadBuffer = errors.New("mat: data buffer size mismatch")
errBadSize = errors.New("mat: invalid dimension") errBadSize = errors.New("mat: invalid dimension")
) )
// Type encoding scheme:
//
// Type Form Packing Uplo Unit Rows Columns kU kL
// uint8 [GST] uint8 [BPF] uint8 [AUL] bool int64 int64 int64 int64
// General 'G' 'F' 'A' false r c 0 0
// Band 'G' 'B' 'A' false r c kU kL
// Symmetric 'S' 'F' ul false n n 0 0
// SymmetricBand 'S' 'B' ul false n n k k
// SymmetricPacked 'S' 'P' ul false n n 0 0
// Triangular 'T' 'F' ul Diag==Unit n n 0 0
// TriangularBand 'T' 'B' ul Diag==Unit n n k k
// TriangularPacked 'T' 'P' ul Diag==Unit n n 0 0
//
// G - general, S - symmetric, T - triangular
// F - full, B - band, P - packed
// A - all, U - upper, L - lower
// MarshalBinary encodes the receiver into a binary form and returns the result. // MarshalBinary encodes the receiver into a binary form and returns the result.
// //
// Dense is little-endian encoded as follows: // Dense is little-endian encoded as follows:
// 0 - 7 number of rows (int64) // 0 - 3 Version = 1 (uint32)
// 8 - 15 number of columns (int64) // 4 'G' (byte)
// 16 - .. matrix data elements (float64) // 5 'F' (byte)
// 6 'A' (byte)
// 7 0 (byte)
// 8 - 15 number of rows (int64)
// 16 - 23 number of columns (int64)
// 24 - 31 0 (int64)
// 32 - 39 0 (int64)
// 40 - .. matrix data elements (float64)
// [0,0] [0,1] ... [0,ncols-1] // [0,0] [0,1] ... [0,ncols-1]
// [1,0] [1,1] ... [1,ncols-1] // [1,0] [1,1] ... [1,ncols-1]
// ... // ...
// [nrows-1,0] ... [nrows-1,ncols-1] // [nrows-1,0] ... [nrows-1,ncols-1]
func (m Dense) MarshalBinary() ([]byte, error) { func (m Dense) MarshalBinary() ([]byte, error) {
bufLen := int64(m.mat.Rows)*int64(m.mat.Cols)*int64(sizeFloat64) + 2*int64(sizeInt64) bufLen := int64(headerSize) + int64(m.mat.Rows)*int64(m.mat.Cols)*int64(sizeFloat64)
if bufLen <= 0 { if bufLen <= 0 {
// bufLen is too big and has wrapped around. // bufLen is too big and has wrapped around.
return nil, errTooBig return nil, errTooBig
} }
p := 0 header := storage{
Form: 'G', Packing: 'F', Uplo: 'A',
Rows: int64(m.mat.Rows), Cols: int64(m.mat.Cols),
Version: version,
}
buf := make([]byte, bufLen) buf := make([]byte, bufLen)
binary.LittleEndian.PutUint64(buf[p:p+sizeInt64], uint64(m.mat.Rows)) n, err := header.marshalBinaryTo(bytes.NewBuffer(buf[:0]))
p += sizeInt64 if err != nil {
binary.LittleEndian.PutUint64(buf[p:p+sizeInt64], uint64(m.mat.Cols)) return buf[:n], err
p += sizeInt64 }
p := headerSize
r, c := m.Dims() r, c := m.Dims()
for i := 0; i < r; i++ { for i := 0; i < r; i++ {
for j := 0; j < c; j++ { for j := 0; j < c; j++ {
@@ -66,26 +101,22 @@ func (m Dense) MarshalBinary() ([]byte, error) {
// //
// See MarshalBinary for the on-disk layout. // See MarshalBinary for the on-disk layout.
func (m Dense) MarshalBinaryTo(w io.Writer) (int, error) { func (m Dense) MarshalBinaryTo(w io.Writer) (int, error) {
var n int header := storage{
var buf [8]byte Form: 'G', Packing: 'F', Uplo: 'A',
binary.LittleEndian.PutUint64(buf[:], uint64(m.mat.Rows)) Rows: int64(m.mat.Rows), Cols: int64(m.mat.Cols),
nn, err := w.Write(buf[:]) Version: version,
n += nn
if err != nil {
return n, err
} }
binary.LittleEndian.PutUint64(buf[:], uint64(m.mat.Cols)) n, err := header.marshalBinaryTo(w)
nn, err = w.Write(buf[:])
n += nn
if err != nil { if err != nil {
return n, err return n, err
} }
r, c := m.Dims() r, c := m.Dims()
var b [8]byte
for i := 0; i < r; i++ { for i := 0; i < r; i++ {
for j := 0; j < c; j++ { for j := 0; j < c; j++ {
binary.LittleEndian.PutUint64(buf[:], math.Float64bits(m.at(i, j))) binary.LittleEndian.PutUint64(b[:], math.Float64bits(m.at(i, j)))
nn, err = w.Write(buf[:]) nn, err := w.Write(b[:])
n += nn n += nn
if err != nil { if err != nil {
return n, err return n, err
@@ -113,19 +144,26 @@ func (m *Dense) UnmarshalBinary(data []byte) error {
panic("mat: unmarshal into non-zero matrix") panic("mat: unmarshal into non-zero matrix")
} }
if len(data) < 2*sizeInt64 { if len(data) < headerSize {
return errTooSmall return errTooSmall
} }
p := 0 var header storage
rows := int64(binary.LittleEndian.Uint64(data[p : p+sizeInt64])) err := header.unmarshalBinary(data[:headerSize])
p += sizeInt64 if err != nil {
cols := int64(binary.LittleEndian.Uint64(data[p : p+sizeInt64])) return err
p += sizeInt64 }
rows := header.Rows
cols := header.Cols
header.Version = 0
header.Rows = 0
header.Cols = 0
if (header != storage{Form: 'G', Packing: 'F', Uplo: 'A'}) {
return errWrongType
}
if rows < 0 || cols < 0 { if rows < 0 || cols < 0 {
return errBadSize return errBadSize
} }
size := rows * cols size := rows * cols
if size == 0 { if size == 0 {
return ErrZeroLength return ErrZeroLength
@@ -133,11 +171,11 @@ func (m *Dense) UnmarshalBinary(data []byte) error {
if int(size) < 0 || size > maxLen { if int(size) < 0 || size > maxLen {
return errTooBig return errTooBig
} }
if len(data) != headerSize+int(rows*cols)*sizeFloat64 {
if len(data) != int(size)*sizeFloat64+2*sizeInt64 {
return errBadBuffer return errBadBuffer
} }
p := headerSize
m.reuseAs(int(rows), int(cols)) m.reuseAs(int(rows), int(cols))
for i := range m.mat.Data { for i := range m.mat.Data {
m.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(data[p : p+sizeFloat64])) m.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(data[p : p+sizeFloat64]))
@@ -165,27 +203,22 @@ func (m *Dense) UnmarshalBinaryFrom(r io.Reader) (int, error) {
panic("mat: unmarshal into non-zero matrix") panic("mat: unmarshal into non-zero matrix")
} }
var ( var header storage
n int n, err := header.unmarshalBinaryFrom(r)
buf [8]byte
)
nn, err := readFull(r, buf[:])
n += nn
if err != nil { if err != nil {
return n, err return n, err
} }
rows := int64(binary.LittleEndian.Uint64(buf[:])) rows := header.Rows
cols := header.Cols
nn, err = readFull(r, buf[:]) header.Version = 0
n += nn header.Rows = 0
if err != nil { header.Cols = 0
return n, err if (header != storage{Form: 'G', Packing: 'F', Uplo: 'A'}) {
return n, errWrongType
} }
cols := int64(binary.LittleEndian.Uint64(buf[:]))
if rows < 0 || cols < 0 { if rows < 0 || cols < 0 {
return n, errBadSize return n, errBadSize
} }
size := rows * cols size := rows * cols
if size == 0 { if size == 0 {
return n, ErrZeroLength return n, ErrZeroLength
@@ -195,13 +228,17 @@ func (m *Dense) UnmarshalBinaryFrom(r io.Reader) (int, error) {
} }
m.reuseAs(int(rows), int(cols)) m.reuseAs(int(rows), int(cols))
var b [8]byte
for i := range m.mat.Data { for i := range m.mat.Data {
nn, err = readFull(r, buf[:]) nn, err := readFull(r, b[:])
n += nn n += nn
if err != nil { if err != nil {
if err == io.EOF {
return n, io.ErrUnexpectedEOF
}
return n, err return n, err
} }
m.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(buf[:])) m.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(b[:]))
} }
return n, nil return n, nil
@@ -210,20 +247,36 @@ func (m *Dense) UnmarshalBinaryFrom(r io.Reader) (int, error) {
// MarshalBinary encodes the receiver into a binary form and returns the result. // MarshalBinary encodes the receiver into a binary form and returns the result.
// //
// VecDense is little-endian encoded as follows: // VecDense is little-endian encoded as follows:
// 0 - 7 number of elements (int64) //
// 8 - .. vector's data elements (float64) // 0 - 3 Version = 1 (uint32)
// 4 'G' (byte)
// 5 'F' (byte)
// 6 'A' (byte)
// 7 0 (byte)
// 8 - 15 number of elements (int64)
// 16 - 23 1 (int64)
// 24 - 31 0 (int64)
// 32 - 39 0 (int64)
// 40 - .. vector's data elements (float64)
func (v VecDense) MarshalBinary() ([]byte, error) { func (v VecDense) MarshalBinary() ([]byte, error) {
bufLen := int64(sizeInt64) + int64(v.n)*int64(sizeFloat64) bufLen := int64(headerSize) + int64(v.n)*int64(sizeFloat64)
if bufLen <= 0 { if bufLen <= 0 {
// bufLen is too big and has wrapped around. // bufLen is too big and has wrapped around.
return nil, errTooBig return nil, errTooBig
} }
p := 0 header := storage{
Form: 'G', Packing: 'F', Uplo: 'A',
Rows: int64(v.n), Cols: 1,
Version: version,
}
buf := make([]byte, bufLen) buf := make([]byte, bufLen)
binary.LittleEndian.PutUint64(buf[p:p+sizeInt64], uint64(v.n)) n, err := header.marshalBinaryTo(bytes.NewBuffer(buf[:0]))
p += sizeInt64 if err != nil {
return buf[:n], err
}
p := headerSize
for i := 0; i < v.n; i++ { for i := 0; i < v.n; i++ {
binary.LittleEndian.PutUint64(buf[p:p+sizeFloat64], math.Float64bits(v.at(i))) binary.LittleEndian.PutUint64(buf[p:p+sizeFloat64], math.Float64bits(v.at(i)))
p += sizeFloat64 p += sizeFloat64
@@ -237,21 +290,20 @@ func (v VecDense) MarshalBinary() ([]byte, error) {
// //
// See MarshalBainry for the on-disk format. // See MarshalBainry for the on-disk format.
func (v VecDense) MarshalBinaryTo(w io.Writer) (int, error) { func (v VecDense) MarshalBinaryTo(w io.Writer) (int, error) {
var ( header := storage{
n int Form: 'G', Packing: 'F', Uplo: 'A',
buf [8]byte Rows: int64(v.n), Cols: 1,
) Version: version,
}
binary.LittleEndian.PutUint64(buf[:], uint64(v.n)) n, err := header.marshalBinaryTo(w)
nn, err := w.Write(buf[:])
n += nn
if err != nil { if err != nil {
return n, err return n, err
} }
var buf [8]byte
for i := 0; i < v.n; i++ { for i := 0; i < v.n; i++ {
binary.LittleEndian.PutUint64(buf[:], math.Float64bits(v.at(i))) binary.LittleEndian.PutUint64(buf[:], math.Float64bits(v.at(i)))
nn, err = w.Write(buf[:]) nn, err := w.Write(buf[:])
n += nn n += nn
if err != nil { if err != nil {
return n, err return n, err
@@ -278,22 +330,39 @@ func (v *VecDense) UnmarshalBinary(data []byte) error {
panic("mat: unmarshal into non-zero vector") panic("mat: unmarshal into non-zero vector")
} }
p := 0 if len(data) < headerSize {
n := int64(binary.LittleEndian.Uint64(data[p : p+sizeInt64])) return errTooSmall
}
var header storage
err := header.unmarshalBinary(data[:headerSize])
if err != nil {
return err
}
if header.Cols != 1 {
return ErrShape
}
n := header.Rows
header.Version = 0
header.Rows = 0
header.Cols = 0
if (header != storage{Form: 'G', Packing: 'F', Uplo: 'A'}) {
return errWrongType
}
if n == 0 { if n == 0 {
return ErrZeroLength return ErrZeroLength
} }
p += sizeInt64
if n < 0 { if n < 0 {
return errBadSize return errBadSize
} }
if n > maxLen { if int64(maxLen) < n {
return errTooBig return errTooBig
} }
if len(data) != int(n)*sizeFloat64+sizeInt64 { if len(data) != headerSize+int(n)*sizeFloat64 {
return errBadBuffer return errBadBuffer
} }
p := headerSize
v.reuseAs(int(n)) v.reuseAs(int(n))
for i := range v.mat.Data { for i := range v.mat.Data {
v.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(data[p : p+sizeFloat64])) v.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(data[p : p+sizeFloat64]))
@@ -314,43 +383,94 @@ func (v *VecDense) UnmarshalBinaryFrom(r io.Reader) (int, error) {
panic("mat: unmarshal into non-zero vector") panic("mat: unmarshal into non-zero vector")
} }
var ( var header storage
n int n, err := header.unmarshalBinaryFrom(r)
buf [8]byte
)
nn, err := readFull(r, buf[:])
n += nn
if err != nil { if err != nil {
return n, err return n, err
} }
sz := int64(binary.LittleEndian.Uint64(buf[:])) if header.Cols != 1 {
if sz == 0 { return n, ErrShape
}
l := header.Rows
header.Version = 0
header.Rows = 0
header.Cols = 0
if (header != storage{Form: 'G', Packing: 'F', Uplo: 'A'}) {
return n, errWrongType
}
if l == 0 {
return n, ErrZeroLength return n, ErrZeroLength
} }
if sz < 0 { if l < 0 {
return n, errBadSize return n, errBadSize
} }
if sz > maxLen { if int64(maxLen) < l {
return n, errTooBig return n, errTooBig
} }
v.reuseAs(int(sz)) v.reuseAs(int(l))
var b [8]byte
for i := range v.mat.Data { for i := range v.mat.Data {
nn, err = readFull(r, buf[:]) nn, err := readFull(r, b[:])
n += nn n += nn
if err != nil { if err != nil {
if err == io.EOF {
return n, io.ErrUnexpectedEOF
}
return n, err return n, err
} }
v.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(buf[:])) v.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(b[:]))
}
if n != sizeInt64+int(sz)*sizeFloat64 {
return n, io.ErrUnexpectedEOF
} }
return n, nil return n, nil
} }
// storage is the internal representation of the storage format of a
// serialised matrix.
type storage struct {
Version uint32 // Keep this first.
Form byte // [GST]
Packing byte // [BPF]
Uplo byte // [AUL]
Unit bool
Rows int64
Cols int64
KU int64
KL int64
}
// TODO(kortschak): Consider replacing these with calls to direct
// encoding/decoding of fields rather than to binary.Write/binary.Read.
func (s storage) marshalBinaryTo(w io.Writer) (int, error) {
buf := bytes.NewBuffer(make([]byte, 0, headerSize))
err := binary.Write(buf, binary.LittleEndian, s)
if err != nil {
return 0, err
}
return w.Write(buf.Bytes())
}
func (s *storage) unmarshalBinary(buf []byte) error {
err := binary.Read(bytes.NewReader(buf), binary.LittleEndian, s)
if err != nil {
return err
}
if s.Version != version {
return fmt.Errorf("mat: incorrect version: %d", s.Version)
}
return nil
}
func (s *storage) unmarshalBinaryFrom(r io.Reader) (int, error) {
buf := make([]byte, headerSize)
n, err := readFull(r, buf)
if err != nil {
return n, err
}
return n, s.unmarshalBinary(buf[:n])
}
// readFull reads from r into buf until it has read len(buf). // readFull reads from r into buf until it has read len(buf).
// It returns the number of bytes copied and an error if fewer bytes were read. // It returns the number of bytes copied and an error if fewer bytes were read.
// If an EOF happens after reading fewer than len(buf) bytes, io.ErrUnexpectedEOF is returned. // If an EOF happens after reading fewer than len(buf) bytes, io.ErrUnexpectedEOF is returned.

View File

@@ -29,48 +29,48 @@ var denseData = []struct {
eq func(got, want Matrix) bool eq func(got, want Matrix) bool
}{ }{
{ {
raw: []byte("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), raw: []byte("\x01\x00\x00\x00GFA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
want: NewDense(0, 0, []float64{}), want: NewDense(0, 0, []float64{}),
err: ErrZeroLength, err: ErrZeroLength,
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\x02\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@"), raw: []byte("\x01\x00\x00\x00GFA\x00\x02\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@"),
want: NewDense(2, 2, []float64{1, 2, 3, 4}), want: NewDense(2, 2, []float64{1, 2, 3, 4}),
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@"), raw: []byte("\x01\x00\x00\x00GFA\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@"),
want: NewDense(2, 3, []float64{1, 2, 3, 4, 5, 6}), want: NewDense(2, 3, []float64{1, 2, 3, 4, 5, 6}),
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\x03\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@"), raw: []byte("\x01\x00\x00\x00GFA\x00\x03\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@"),
want: NewDense(3, 2, []float64{1, 2, 3, 4, 5, 6}), want: NewDense(3, 2, []float64{1, 2, 3, 4, 5, 6}),
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\x03\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@\x00\x00\x00\x00\x00\x00\x1c@\x00\x00\x00\x00\x00\x00 @\x00\x00\x00\x00\x00\x00\"@"), raw: []byte("\x01\x00\x00\x00GFA\x00\x03\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@\x00\x00\x00\x00\x00\x00\x1c@\x00\x00\x00\x00\x00\x00 @\x00\x00\x00\x00\x00\x00\"@"),
want: NewDense(3, 3, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}), want: NewDense(3, 3, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}),
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\x02\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@"), raw: []byte("\x01\x00\x00\x00GFA\x00\x02\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@"),
want: NewDense(3, 3, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}).Slice(0, 2, 0, 2).(*Dense), want: NewDense(3, 3, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}).Slice(0, 2, 0, 2).(*Dense),
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\x02\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@\x00\x00\x00\x00\x00\x00 @\x00\x00\x00\x00\x00\x00\"@"), raw: []byte("\x01\x00\x00\x00GFA\x00\x02\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@\x00\x00\x00\x00\x00\x00 @\x00\x00\x00\x00\x00\x00\"@"),
want: NewDense(3, 3, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}).Slice(1, 3, 1, 3).(*Dense), want: NewDense(3, 3, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}).Slice(1, 3, 1, 3).(*Dense),
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\x03\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@\x00\x00\x00\x00\x00\x00 @\x00\x00\x00\x00\x00\x00\"@"), raw: []byte("\x01\x00\x00\x00GFA\x00\x03\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@\x00\x00\x00\x00\x00\x00 @\x00\x00\x00\x00\x00\x00\"@"),
want: NewDense(3, 3, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}).Slice(0, 3, 1, 3).(*Dense), want: NewDense(3, 3, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}).Slice(0, 3, 1, 3).(*Dense),
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xff\x00\x00\x00\x00\x00\x00\xf0\u007f\x01\x00\x00\x00\x00\x00\xf8\u007f"), raw: []byte("\x01\x00\x00\x00GFA\x00\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xff\x00\x00\x00\x00\x00\x00\xf0\u007f\x01\x00\x00\x00\x00\x00\xf8\u007f"),
want: NewDense(1, 4, []float64{0, math.Inf(-1), math.Inf(+1), math.NaN()}), want: NewDense(1, 4, []float64{0, math.Inf(-1), math.Inf(+1), math.NaN()}),
eq: func(got, want Matrix) bool { eq: func(got, want Matrix) bool {
for _, v := range []bool{ for _, v := range []bool{
@@ -97,7 +97,7 @@ func TestDenseMarshal(t *testing.T) {
} }
nrows, ncols := test.want.Dims() nrows, ncols := test.want.Dims()
sz := nrows*ncols*sizeFloat64 + 2*sizeInt64 sz := headerSize + nrows*ncols*sizeFloat64
if len(buf) != sz { if len(buf) != sz {
t.Errorf("encoded size test-%d: want=%d got=%d\n", i, sz, len(buf)) t.Errorf("encoded size test-%d: want=%d got=%d\n", i, sz, len(buf))
} }
@@ -123,7 +123,7 @@ func TestDenseMarshalTo(t *testing.T) {
} }
nrows, ncols := test.want.Dims() nrows, ncols := test.want.Dims()
sz := nrows*ncols*sizeFloat64 + 2*sizeInt64 sz := headerSize + nrows*ncols*sizeFloat64
if n != sz { if n != sz {
t.Errorf("encoded size test-%d: want=%d got=%d\n", i, sz, n) t.Errorf("encoded size test-%d: want=%d got=%d\n", i, sz, n)
} }
@@ -304,43 +304,43 @@ var vectorData = []struct {
eq func(got, want Matrix) bool eq func(got, want Matrix) bool
}{ }{
{ {
raw: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), raw: []byte("\x01\x00\x00\x00GFA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
want: NewVecDense(0, []float64{}), want: NewVecDense(0, []float64{}),
err: ErrZeroLength, err: ErrZeroLength,
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@"), raw: []byte("\x01\x00\x00\x00GFA\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@"),
want: NewVecDense(4, []float64{1, 2, 3, 4}), want: NewVecDense(4, []float64{1, 2, 3, 4}),
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@"), raw: []byte("\x01\x00\x00\x00GFA\x00\x06\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@"),
want: NewVecDense(6, []float64{1, 2, 3, 4, 5, 6}), want: NewVecDense(6, []float64{1, 2, 3, 4, 5, 6}),
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@\x00\x00\x00\x00\x00\x00\x1c@\x00\x00\x00\x00\x00\x00 @\x00\x00\x00\x00\x00\x00\"@"), raw: []byte("\x01\x00\x00\x00GFA\x00\t\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@\x00\x00\x00\x00\x00\x00\x1c@\x00\x00\x00\x00\x00\x00 @\x00\x00\x00\x00\x00\x00\"@"),
want: NewVecDense(9, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}), want: NewVecDense(9, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}),
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@"), raw: []byte("\x01\x00\x00\x00GFA\x00\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@"),
want: NewVecDense(9, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}).SliceVec(0, 3).(*VecDense), want: NewVecDense(9, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}).SliceVec(0, 3).(*VecDense),
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@"), raw: []byte("\x01\x00\x00\x00GFA\x00\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@"),
want: NewVecDense(9, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}).SliceVec(1, 4).(*VecDense), want: NewVecDense(9, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}).SliceVec(1, 4).(*VecDense),
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@\x00\x00\x00\x00\x00\x00\x1c@\x00\x00\x00\x00\x00\x00 @"), raw: []byte("\x01\x00\x00\x00GFA\x00\b\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@\x00\x00\x00\x00\x00\x00\x1c@\x00\x00\x00\x00\x00\x00 @"),
want: NewVecDense(9, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}).SliceVec(0, 8).(*VecDense), want: NewVecDense(9, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}).SliceVec(0, 8).(*VecDense),
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x18@"), raw: []byte("\x01\x00\x00\x00GFA\x00\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x18@"),
want: &VecDense{ want: &VecDense{
mat: blas64.Vector{ mat: blas64.Vector{
Data: []float64{0, 1, 2, 3, 4, 5, 6}, Data: []float64{0, 1, 2, 3, 4, 5, 6},
@@ -351,7 +351,7 @@ var vectorData = []struct {
eq: Equal, eq: Equal,
}, },
{ {
raw: []byte("\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xff\x00\x00\x00\x00\x00\x00\xf0\u007f\x01\x00\x00\x00\x00\x00\xf8\u007f"), raw: []byte("\x01\x00\x00\x00GFA\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xff\x00\x00\x00\x00\x00\x00\xf0\u007f\x01\x00\x00\x00\x00\x00\xf8\u007f"),
want: NewVecDense(4, []float64{0, math.Inf(-1), math.Inf(+1), math.NaN()}), want: NewVecDense(4, []float64{0, math.Inf(-1), math.Inf(+1), math.NaN()}),
eq: func(got, want Matrix) bool { eq: func(got, want Matrix) bool {
for _, v := range []bool{ for _, v := range []bool{
@@ -378,7 +378,7 @@ func TestVecDenseMarshal(t *testing.T) {
} }
nrows, ncols := test.want.Dims() nrows, ncols := test.want.Dims()
sz := nrows*ncols*sizeFloat64 + sizeInt64 sz := headerSize + nrows*ncols*sizeFloat64
if len(buf) != sz { if len(buf) != sz {
t.Errorf("encoded size test-%d: want=%d got=%d\n", i, sz, len(buf)) t.Errorf("encoded size test-%d: want=%d got=%d\n", i, sz, len(buf))
} }
@@ -404,7 +404,7 @@ func TestVecDenseMarshalTo(t *testing.T) {
} }
nrows, ncols := test.want.Dims() nrows, ncols := test.want.Dims()
sz := nrows*ncols*sizeFloat64 + sizeInt64 sz := headerSize + nrows*ncols*sizeFloat64
if n != sz { if n != sz {
t.Errorf("encoded size test-%d: want=%d got=%d\n", i, sz, n) t.Errorf("encoded size test-%d: want=%d got=%d\n", i, sz, n)
} }