mirror of
https://github.com/gonum/gonum.git
synced 2025-10-16 12:10:37 +08:00
281 lines
7.1 KiB
Go
281 lines
7.1 KiB
Go
// 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
|
||
}
|