Files
gonum/mat/shadow.go

281 lines
7.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright ©2015 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"
)
const (
// regionOverlap is the panic string used for the general case
// of a matrix region overlap between a source and destination.
regionOverlap = "mat: bad region: overlap"
// regionIdentity is the panic string used for the specific
// case of complete agreement between a source and a destination.
regionIdentity = "mat: bad region: identical"
// mismatchedStrides is the panic string used for overlapping
// data slices with differing strides.
mismatchedStrides = "mat: bad region: different strides"
)
// checkOverlap returns false if the receiver does not overlap data elements
// referenced by the parameter and panics otherwise.
//
// checkOverlap methods return a boolean to allow the check call to be added to a
// boolean expression, making use of short-circuit operators.
func (m *Dense) checkOverlap(a blas64.General) bool {
mat := m.RawMatrix()
if cap(mat.Data) == 0 || cap(a.Data) == 0 {
return false
}
off := offset(mat.Data[:1], a.Data[:1])
if off == 0 {
// At least one element overlaps.
if mat.Cols == a.Cols && mat.Rows == a.Rows && mat.Stride == a.Stride {
panic(regionIdentity)
}
panic(regionOverlap)
}
if off > 0 && len(mat.Data) <= off {
// We know m is completely before a.
return false
}
if off < 0 && len(a.Data) <= -off {
// We know m is completely after a.
return false
}
if mat.Stride != a.Stride {
// Too hard, so assume the worst.
panic(mismatchedStrides)
}
if off < 0 {
off = -off
mat.Cols, a.Cols = a.Cols, mat.Cols
}
if rectanglesOverlap(off, mat.Cols, a.Cols, mat.Stride) {
panic(regionOverlap)
}
return false
}
func (s *SymDense) checkOverlap(a blas64.Symmetric) bool {
mat := s.RawSymmetric()
if cap(mat.Data) == 0 || cap(a.Data) == 0 {
return false
}
off := offset(mat.Data[:1], a.Data[:1])
if off == 0 {
// At least one element overlaps.
if mat.N == a.N && mat.Stride == a.Stride {
panic(regionIdentity)
}
panic(regionOverlap)
}
if off > 0 && len(mat.Data) <= off {
// We know s is completely before a.
return false
}
if off < 0 && len(a.Data) <= -off {
// We know s is completely after a.
return false
}
if mat.Stride != a.Stride {
// Too hard, so assume the worst.
panic(mismatchedStrides)
}
if off < 0 {
off = -off
mat.N, a.N = a.N, mat.N
// If we created the matrix it will always
// be in the upper triangle, but don't trust
// that this is the case.
mat.Uplo, a.Uplo = a.Uplo, mat.Uplo
}
if trianglesOverlap(off, mat.N, a.N, mat.Stride, mat.Uplo == blas.Upper, a.Uplo == blas.Upper) {
panic(regionOverlap)
}
return false
}
func (t *TriDense) checkOverlap(a blas64.Triangular) bool {
mat := t.RawTriangular()
if cap(mat.Data) == 0 || cap(a.Data) == 0 {
return false
}
off := offset(mat.Data[:1], a.Data[:1])
if off == 0 {
// At least one element overlaps.
if mat.N == a.N && mat.Stride == a.Stride {
panic(regionIdentity)
}
panic(regionOverlap)
}
if off > 0 && len(mat.Data) <= off {
// We know t is completely before a.
return false
}
if off < 0 && len(a.Data) <= -off {
// We know t is completely after a.
return false
}
if mat.Stride != a.Stride {
// Too hard, so assume the worst.
panic(mismatchedStrides)
}
if off < 0 {
off = -off
mat.N, a.N = a.N, mat.N
mat.Uplo, a.Uplo = a.Uplo, mat.Uplo
}
if trianglesOverlap(off, mat.N, a.N, mat.Stride, mat.Uplo == blas.Upper, a.Uplo == blas.Upper) {
panic(regionOverlap)
}
return false
}
func (v *VecDense) checkOverlap(a blas64.Vector) bool {
mat := v.mat
if cap(mat.Data) == 0 || cap(a.Data) == 0 {
return false
}
off := offset(mat.Data[:1], a.Data[:1])
if off == 0 {
// At least one element overlaps.
if mat.Inc == a.Inc && len(mat.Data) == len(a.Data) {
panic(regionIdentity)
}
panic(regionOverlap)
}
if off > 0 && len(mat.Data) <= off {
// We know v is completely before a.
return false
}
if off < 0 && len(a.Data) <= -off {
// We know v is completely after a.
return false
}
if mat.Inc != a.Inc {
// Too hard, so assume the worst.
panic(mismatchedStrides)
}
if mat.Inc == 1 || off&mat.Inc == 0 {
panic(regionOverlap)
}
return false
}
// rectanglesOverlap returns whether the strided rectangles a and b overlap
// when b is offset by off elements after a but has at least one element before
// the end of a. off must be positive. a and b have aCols and bCols respectively.
//
// rectanglesOverlap works by shifting both matrices left such that the left
// column of a is at 0. The column indexes are flattened by obtaining the shifted
// relative left and right column positions modulo the common stride. This allows
// direct comparison of the column offsets when the matrix backing data slices
// are known to overlap.
func rectanglesOverlap(off, aCols, bCols, stride int) bool {
if stride == 1 {
// Unit stride means overlapping data
// slices must overlap as matrices.
return true
}
// Flatten the shifted matrix column positions
// so a starts at 0, modulo the common stride.
aTo := aCols
// The mod stride operations here make the from
// and to indexes comparable between a and b when
// the data slices of a and b overlap.
bFrom := off % stride
bTo := (bFrom + bCols) % stride
if bTo == 0 || bFrom < bTo {
// b matrix is not wrapped: compare for
// simple overlap.
return bFrom < aTo
}
// b strictly wraps and so must overlap with a.
return true
}
// trianglesOverlap returns whether the strided triangles a and b overlap
// when b is offset by off elements after a but has at least one element before
// the end of a. off must be positive. a and b are aSize×aSize and bSize×bSize
// respectively.
func trianglesOverlap(off, aSize, bSize, stride int, aUpper, bUpper bool) bool {
if !rectanglesOverlap(off, aSize, bSize, stride) {
// Fast return if bounding rectangles do not overlap.
return false
}
// Find location of b relative to a.
rowOffset := off / stride
colOffset := off % stride
if (off+bSize)%stride < colOffset {
// We have wrapped, so readjust offsets.
rowOffset++
colOffset -= stride
}
if aUpper {
// Check whether the upper left of b
// is in the triangle of a
if rowOffset >= 0 && rowOffset <= colOffset {
return true
}
// Check whether the upper right of b
// is in the triangle of a.
return bUpper && rowOffset < colOffset+bSize
}
// Check whether the upper left of b
// is in the triangle of a
if colOffset >= 0 && rowOffset >= colOffset {
return true
}
if bUpper {
// Check whether the upper right corner of b
// is in a or the upper row of b spans a row
// of a.
return rowOffset > colOffset+bSize || colOffset < 0
}
if colOffset < 0 {
// Check whether the lower left of a
// is in the triangle of b or below
// the diagonal of a. This requires a
// swap of reference origin.
return -rowOffset+aSize > -colOffset
}
// Check whether the lower left of b
// is in the triangle of a or below
// the diagonal of a.
return rowOffset+bSize > colOffset
}