Upgrade OpenShift and its dependencies.

OpenShift version 1.4.0-alpha.0
This commit is contained in:
Tomas Kral
2016-10-18 12:04:00 +02:00
parent 5e1a5cbb3b
commit 1f8a0e06c9
1786 changed files with 424709 additions and 33395 deletions
+39
View File
@@ -0,0 +1,39 @@
sudo: required
language: go
# Versions of go that are explicitly supported by gonum.
go:
- 1.5beta1
- 1.3.3
- 1.4.2
env:
matrix:
- BLAS_LIB=OpenBLAS
- BLAS_LIB=gonum
# at some point, when travis allows builds on darwin
#- BLAS_LIB=Accellerate
# at some point, when the issue with drotgm is resolved
#- BLAS_LIB=ATLAS
# Required for coverage.
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
# Install the appropriate blas library if we are using cgo.
install:
- source .travis/$TRAVIS_OS_NAME/$BLAS_LIB/install.sh
- go get github.com/gonum/floats
# Get deps, build, test, and ensure the code is gofmt'ed.
# Move into the native directory if we aren't using an external blas lib.
# If we are building as gonum, then we have access to the coveralls api key, so we can run coverage as well.
script:
- if [[ "$BLAS_LIB" == "gonum" ]]; then pushd native; fi
- go get -d -t -v ./...
- go test -a -v ./...
- go test -a -tags noasm -v ./...
- diff <(gofmt -d .) <("")
- if [[ $TRAVIS_SECURE_ENV_VARS = "true" ]]; then bash -c "${TRAVIS_BUILD_DIR}/.travis/test-coverage.sh"; fi
+96
View File
@@ -0,0 +1,96 @@
# Gonum BLAS [![Build Status](https://travis-ci.org/gonum/blas.svg?branch=master)](https://travis-ci.org/gonum/blas) [![Coverage Status](https://img.shields.io/coveralls/gonum/blas.svg)](https://coveralls.io/r/gonum/blas)
A collection of packages to provide BLAS functionality for the [Go programming
language](http://golang.org)
## Installation
```sh
go get github.com/gonum/blas
```
### BLAS C-bindings
If you want to use OpenBLAS, install it in any directory:
```sh
git clone https://github.com/xianyi/OpenBLAS
cd OpenBLAS
make
```
The blas/cgo package provides bindings to C-backed BLAS packages. blas/cgo needs the `CGO_LDFLAGS`
environment variable to point to the blas installation. More information can be found in the
[cgo command documentation](http://golang.org/cmd/cgo/).
Then install the blas/cgo package:
```sh
CGO_LDFLAGS="-L/path/to/OpenBLAS -lopenblas" go install github.com/gonum/blas/cgo
```
For Windows you can download binary packages for OpenBLAS at
[SourceForge](http://sourceforge.net/projects/openblas/files/).
If you want to use a different BLAS package such as the Intel MKL you can
adjust the `CGO_LDFLAGS` variable:
```sh
CGO_LDFLAGS="-lmkl_rt" go install github.com/gonum/blas/cgo
```
On OS X the easiest solution is to use the libraries provided by the system:
```sh
CGO_LDFLAGS="-framework Accelerate" go install github.com/gonum/blas/cgo
```
## Packages
### blas
Defines [BLAS API](http://www.netlib.org/blas/blast-forum/cinterface.pdf) split in several
interfaces.
### blas/native
Go implementation of the BLAS API (incomplete, implements the `float32` and `float64` API)
### blas/cgo
Binding to a C implementation of the cblas interface (e.g. ATLAS, OpenBLAS, Intel MKL)
The recommended (free) option for good performance on both Linux and Darwin is OpenBLAS.
### blas/blas64 and blas/blas32
Wrappers for an implementation of the double (i.e., `float64`) and single (`float32`)
precision real parts of the blas API
```Go
package main
import (
"fmt"
"github.com/gonum/blas/blas64"
)
func main() {
v := blas64.Vector{Inc: 1, Data: []float64{1, 1, 1}}
fmt.Println("v has length:", blas64.Nrm2(len(v.Data), v))
}
```
### blas/cblas128 and blas/cblas64
Wrappers for an implementation of the double (i.e., `complex128`) and single (`complex64`)
precision complex parts of the blas API
Currently blas/cblas64 and blas/cblas128 require blas/cgo.
## Issues
If you find any bugs, feel free to file an issue on the github issue tracker.
Discussions on API changes, added features, code review, or similar requests
are preferred on the [gonum-dev Google Group](https://groups.google.com/forum/#!forum/gonum-dev).
## License
Please see [github.com/gonum/license](https://github.com/gonum/license) for general
license information, contributors, authors, etc on the Gonum suite of packages.
+388
View File
@@ -0,0 +1,388 @@
// 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 blas provides interfaces for the BLAS linear algebra standard.
All methods must perform appropriate parameter checking and panic if
provided parameters that do not conform to the requirements specified
by the BLAS standard.
Quick Reference Guide to the BLAS from http://www.netlib.org/lapack/lug/node145.html
This version is modified to remove the "order" option. All matrix operations are
on row-order matrices.
Level 1 BLAS
dim scalar vector vector scalars 5-element prefixes
struct
_rotg ( a, b ) S, D
_rotmg( d1, d2, a, b ) S, D
_rot ( n, x, incX, y, incY, c, s ) S, D
_rotm ( n, x, incX, y, incY, param ) S, D
_swap ( n, x, incX, y, incY ) S, D, C, Z
_scal ( n, alpha, x, incX ) S, D, C, Z, Cs, Zd
_copy ( n, x, incX, y, incY ) S, D, C, Z
_axpy ( n, alpha, x, incX, y, incY ) S, D, C, Z
_dot ( n, x, incX, y, incY ) S, D, Ds
_dotu ( n, x, incX, y, incY ) C, Z
_dotc ( n, x, incX, y, incY ) C, Z
__dot ( n, alpha, x, incX, y, incY ) Sds
_nrm2 ( n, x, incX ) S, D, Sc, Dz
_asum ( n, x, incX ) S, D, Sc, Dz
I_amax( n, x, incX ) s, d, c, z
Level 2 BLAS
options dim b-width scalar matrix vector scalar vector prefixes
_gemv ( trans, m, n, alpha, a, lda, x, incX, beta, y, incY ) S, D, C, Z
_gbmv ( trans, m, n, kL, kU, alpha, a, lda, x, incX, beta, y, incY ) S, D, C, Z
_hemv ( uplo, n, alpha, a, lda, x, incX, beta, y, incY ) C, Z
_hbmv ( uplo, n, k, alpha, a, lda, x, incX, beta, y, incY ) C, Z
_hpmv ( uplo, n, alpha, ap, x, incX, beta, y, incY ) C, Z
_symv ( uplo, n, alpha, a, lda, x, incX, beta, y, incY ) S, D
_sbmv ( uplo, n, k, alpha, a, lda, x, incX, beta, y, incY ) S, D
_spmv ( uplo, n, alpha, ap, x, incX, beta, y, incY ) S, D
_trmv ( uplo, trans, diag, n, a, lda, x, incX ) S, D, C, Z
_tbmv ( uplo, trans, diag, n, k, a, lda, x, incX ) S, D, C, Z
_tpmv ( uplo, trans, diag, n, ap, x, incX ) S, D, C, Z
_trsv ( uplo, trans, diag, n, a, lda, x, incX ) S, D, C, Z
_tbsv ( uplo, trans, diag, n, k, a, lda, x, incX ) S, D, C, Z
_tpsv ( uplo, trans, diag, n, ap, x, incX ) S, D, C, Z
options dim scalar vector vector matrix prefixes
_ger ( m, n, alpha, x, incX, y, incY, a, lda ) S, D
_geru ( m, n, alpha, x, incX, y, incY, a, lda ) C, Z
_gerc ( m, n, alpha, x, incX, y, incY, a, lda ) C, Z
_her ( uplo, n, alpha, x, incX, a, lda ) C, Z
_hpr ( uplo, n, alpha, x, incX, ap ) C, Z
_her2 ( uplo, n, alpha, x, incX, y, incY, a, lda ) C, Z
_hpr2 ( uplo, n, alpha, x, incX, y, incY, ap ) C, Z
_syr ( uplo, n, alpha, x, incX, a, lda ) S, D
_spr ( uplo, n, alpha, x, incX, ap ) S, D
_syr2 ( uplo, n, alpha, x, incX, y, incY, a, lda ) S, D
_spr2 ( uplo, n, alpha, x, incX, y, incY, ap ) S, D
Level 3 BLAS
options dim scalar matrix matrix scalar matrix prefixes
_gemm ( transA, transB, m, n, k, alpha, a, lda, b, ldb, beta, c, ldc ) S, D, C, Z
_symm ( side, uplo, m, n, alpha, a, lda, b, ldb, beta, c, ldc ) S, D, C, Z
_hemm ( side, uplo, m, n, alpha, a, lda, b, ldb, beta, c, ldc ) C, Z
_syrk ( uplo, trans, n, k, alpha, a, lda, beta, c, ldc ) S, D, C, Z
_herk ( uplo, trans, n, k, alpha, a, lda, beta, c, ldc ) C, Z
_syr2k( uplo, trans, n, k, alpha, a, lda, b, ldb, beta, c, ldc ) S, D, C, Z
_her2k( uplo, trans, n, k, alpha, a, lda, b, ldb, beta, c, ldc ) C, Z
_trmm ( side, uplo, transA, diag, m, n, alpha, a, lda, b, ldb ) S, D, C, Z
_trsm ( side, uplo, transA, diag, m, n, alpha, a, lda, b, ldb ) S, D, C, Z
Meaning of prefixes
S - float32 C - complex64
D - float64 Z - complex128
Matrix types
GE - GEneral GB - General Band
SY - SYmmetric SB - Symmetric Band SP - Symmetric Packed
HE - HErmitian HB - Hermitian Band HP - Hermitian Packed
TR - TRiangular TB - Triangular Band TP - Triangular Packed
Options
trans = NoTrans, Trans, ConjTrans
uplo = Upper, Lower
diag = Nonunit, Unit
side = Left, Right (A or op(A) on the left, or A or op(A) on the right)
For real matrices, Trans and ConjTrans have the same meaning.
For Hermitian matrices, trans = Trans is not allowed.
For complex symmetric matrices, trans = ConjTrans is not allowed.
*/
package blas
// Flag constants indicate Givens transformation H matrix state.
type Flag int
const (
Identity Flag = iota - 2 // H is the identity matrix; no rotation is needed.
Rescaling // H specifies rescaling.
OffDiagonal // Off-diagonal elements of H are units.
Diagonal // Diagonal elements of H are units.
)
// SrotmParams contains Givens transformation parameters returned
// by the Float32 Srotm method.
type SrotmParams struct {
Flag
H [4]float32 // Column-major 2 by 2 matrix.
}
// DrotmParams contains Givens transformation parameters returned
// by the Float64 Drotm method.
type DrotmParams struct {
Flag
H [4]float64 // Column-major 2 by 2 matrix.
}
// Transpose is used to specify the transposition operation for a
// routine.
type Transpose int
const (
NoTrans Transpose = 111 + iota
Trans
ConjTrans
)
// Uplo is used to specify whether the matrix is an upper or lower
// triangular matrix.
type Uplo int
const (
All Uplo = 120 + iota
Upper
Lower
)
// Diag is used to specify whether the matrix is a unit or non-unit
// triangular matrix.
type Diag int
const (
NonUnit Diag = 131 + iota
Unit
)
// Side is used to specify from which side a multiplication operation
// is performed.
type Side int
const (
Left Side = 141 + iota
Right
)
// Float32 implements the single precision real BLAS routines.
type Float32 interface {
Float32Level1
Float32Level2
Float32Level3
}
// Float32Level1 implements the single precision real BLAS Level 1 routines.
type Float32Level1 interface {
Sdsdot(n int, alpha float32, x []float32, incX int, y []float32, incY int) float32
Dsdot(n int, x []float32, incX int, y []float32, incY int) float64
Sdot(n int, x []float32, incX int, y []float32, incY int) float32
Snrm2(n int, x []float32, incX int) float32
Sasum(n int, x []float32, incX int) float32
Isamax(n int, x []float32, incX int) int
Sswap(n int, x []float32, incX int, y []float32, incY int)
Scopy(n int, x []float32, incX int, y []float32, incY int)
Saxpy(n int, alpha float32, x []float32, incX int, y []float32, incY int)
Srotg(a, b float32) (c, s, r, z float32)
Srotmg(d1, d2, b1, b2 float32) (p SrotmParams, rd1, rd2, rb1 float32)
Srot(n int, x []float32, incX int, y []float32, incY int, c, s float32)
Srotm(n int, x []float32, incX int, y []float32, incY int, p SrotmParams)
Sscal(n int, alpha float32, x []float32, incX int)
}
// Float32Level2 implements the single precision real BLAS Level 2 routines.
type Float32Level2 interface {
Sgemv(tA Transpose, m, n int, alpha float32, a []float32, lda int, x []float32, incX int, beta float32, y []float32, incY int)
Sgbmv(tA Transpose, m, n, kL, kU int, alpha float32, a []float32, lda int, x []float32, incX int, beta float32, y []float32, incY int)
Strmv(ul Uplo, tA Transpose, d Diag, n int, a []float32, lda int, x []float32, incX int)
Stbmv(ul Uplo, tA Transpose, d Diag, n, k int, a []float32, lda int, x []float32, incX int)
Stpmv(ul Uplo, tA Transpose, d Diag, n int, ap []float32, x []float32, incX int)
Strsv(ul Uplo, tA Transpose, d Diag, n int, a []float32, lda int, x []float32, incX int)
Stbsv(ul Uplo, tA Transpose, d Diag, n, k int, a []float32, lda int, x []float32, incX int)
Stpsv(ul Uplo, tA Transpose, d Diag, n int, ap []float32, x []float32, incX int)
Ssymv(ul Uplo, n int, alpha float32, a []float32, lda int, x []float32, incX int, beta float32, y []float32, incY int)
Ssbmv(ul Uplo, n, k int, alpha float32, a []float32, lda int, x []float32, incX int, beta float32, y []float32, incY int)
Sspmv(ul Uplo, n int, alpha float32, ap []float32, x []float32, incX int, beta float32, y []float32, incY int)
Sger(m, n int, alpha float32, x []float32, incX int, y []float32, incY int, a []float32, lda int)
Ssyr(ul Uplo, n int, alpha float32, x []float32, incX int, a []float32, lda int)
Sspr(ul Uplo, n int, alpha float32, x []float32, incX int, ap []float32)
Ssyr2(ul Uplo, n int, alpha float32, x []float32, incX int, y []float32, incY int, a []float32, lda int)
Sspr2(ul Uplo, n int, alpha float32, x []float32, incX int, y []float32, incY int, a []float32)
}
// Float32Level3 implements the single precision real BLAS Level 3 routines.
type Float32Level3 interface {
Sgemm(tA, tB Transpose, m, n, k int, alpha float32, a []float32, lda int, b []float32, ldb int, beta float32, c []float32, ldc int)
Ssymm(s Side, ul Uplo, m, n int, alpha float32, a []float32, lda int, b []float32, ldb int, beta float32, c []float32, ldc int)
Ssyrk(ul Uplo, t Transpose, n, k int, alpha float32, a []float32, lda int, beta float32, c []float32, ldc int)
Ssyr2k(ul Uplo, t Transpose, n, k int, alpha float32, a []float32, lda int, b []float32, ldb int, beta float32, c []float32, ldc int)
Strmm(s Side, ul Uplo, tA Transpose, d Diag, m, n int, alpha float32, a []float32, lda int, b []float32, ldb int)
Strsm(s Side, ul Uplo, tA Transpose, d Diag, m, n int, alpha float32, a []float32, lda int, b []float32, ldb int)
}
// Float64 implements the single precision real BLAS routines.
type Float64 interface {
Float64Level1
Float64Level2
Float64Level3
}
// Float64Level1 implements the double precision real BLAS Level 1 routines.
type Float64Level1 interface {
Ddot(n int, x []float64, incX int, y []float64, incY int) float64
Dnrm2(n int, x []float64, incX int) float64
Dasum(n int, x []float64, incX int) float64
Idamax(n int, x []float64, incX int) int
Dswap(n int, x []float64, incX int, y []float64, incY int)
Dcopy(n int, x []float64, incX int, y []float64, incY int)
Daxpy(n int, alpha float64, x []float64, incX int, y []float64, incY int)
Drotg(a, b float64) (c, s, r, z float64)
Drotmg(d1, d2, b1, b2 float64) (p DrotmParams, rd1, rd2, rb1 float64)
Drot(n int, x []float64, incX int, y []float64, incY int, c float64, s float64)
Drotm(n int, x []float64, incX int, y []float64, incY int, p DrotmParams)
Dscal(n int, alpha float64, x []float64, incX int)
}
// Float64Level2 implements the double precision real BLAS Level 2 routines.
type Float64Level2 interface {
Dgemv(tA Transpose, m, n int, alpha float64, a []float64, lda int, x []float64, incX int, beta float64, y []float64, incY int)
Dgbmv(tA Transpose, m, n, kL, kU int, alpha float64, a []float64, lda int, x []float64, incX int, beta float64, y []float64, incY int)
Dtrmv(ul Uplo, tA Transpose, d Diag, n int, a []float64, lda int, x []float64, incX int)
Dtbmv(ul Uplo, tA Transpose, d Diag, n, k int, a []float64, lda int, x []float64, incX int)
Dtpmv(ul Uplo, tA Transpose, d Diag, n int, ap []float64, x []float64, incX int)
Dtrsv(ul Uplo, tA Transpose, d Diag, n int, a []float64, lda int, x []float64, incX int)
Dtbsv(ul Uplo, tA Transpose, d Diag, n, k int, a []float64, lda int, x []float64, incX int)
Dtpsv(ul Uplo, tA Transpose, d Diag, n int, ap []float64, x []float64, incX int)
Dsymv(ul Uplo, n int, alpha float64, a []float64, lda int, x []float64, incX int, beta float64, y []float64, incY int)
Dsbmv(ul Uplo, n, k int, alpha float64, a []float64, lda int, x []float64, incX int, beta float64, y []float64, incY int)
Dspmv(ul Uplo, n int, alpha float64, ap []float64, x []float64, incX int, beta float64, y []float64, incY int)
Dger(m, n int, alpha float64, x []float64, incX int, y []float64, incY int, a []float64, lda int)
Dsyr(ul Uplo, n int, alpha float64, x []float64, incX int, a []float64, lda int)
Dspr(ul Uplo, n int, alpha float64, x []float64, incX int, ap []float64)
Dsyr2(ul Uplo, n int, alpha float64, x []float64, incX int, y []float64, incY int, a []float64, lda int)
Dspr2(ul Uplo, n int, alpha float64, x []float64, incX int, y []float64, incY int, a []float64)
}
// Float64Level3 implements the double precision real BLAS Level 3 routines.
type Float64Level3 interface {
Dgemm(tA, tB Transpose, m, n, k int, alpha float64, a []float64, lda int, b []float64, ldb int, beta float64, c []float64, ldc int)
Dsymm(s Side, ul Uplo, m, n int, alpha float64, a []float64, lda int, b []float64, ldb int, beta float64, c []float64, ldc int)
Dsyrk(ul Uplo, t Transpose, n, k int, alpha float64, a []float64, lda int, beta float64, c []float64, ldc int)
Dsyr2k(ul Uplo, t Transpose, n, k int, alpha float64, a []float64, lda int, b []float64, ldb int, beta float64, c []float64, ldc int)
Dtrmm(s Side, ul Uplo, tA Transpose, d Diag, m, n int, alpha float64, a []float64, lda int, b []float64, ldb int)
Dtrsm(s Side, ul Uplo, tA Transpose, d Diag, m, n int, alpha float64, a []float64, lda int, b []float64, ldb int)
}
// Complex64 implements the single precision complex BLAS routines.
type Complex64 interface {
Complex64Level1
Complex64Level2
Complex64Level3
}
// Complex64Level1 implements the single precision complex BLAS Level 1 routines.
type Complex64Level1 interface {
Cdotu(n int, x []complex64, incX int, y []complex64, incY int) (dotu complex64)
Cdotc(n int, x []complex64, incX int, y []complex64, incY int) (dotc complex64)
Scnrm2(n int, x []complex64, incX int) float32
Scasum(n int, x []complex64, incX int) float32
Icamax(n int, x []complex64, incX int) int
Cswap(n int, x []complex64, incX int, y []complex64, incY int)
Ccopy(n int, x []complex64, incX int, y []complex64, incY int)
Caxpy(n int, alpha complex64, x []complex64, incX int, y []complex64, incY int)
Cscal(n int, alpha complex64, x []complex64, incX int)
Csscal(n int, alpha float32, x []complex64, incX int)
}
// Complex64Level2 implements the single precision complex BLAS routines Level 2 routines.
type Complex64Level2 interface {
Cgemv(tA Transpose, m, n int, alpha complex64, a []complex64, lda int, x []complex64, incX int, beta complex64, y []complex64, incY int)
Cgbmv(tA Transpose, m, n, kL, kU int, alpha complex64, a []complex64, lda int, x []complex64, incX int, beta complex64, y []complex64, incY int)
Ctrmv(ul Uplo, tA Transpose, d Diag, n int, a []complex64, lda int, x []complex64, incX int)
Ctbmv(ul Uplo, tA Transpose, d Diag, n, k int, a []complex64, lda int, x []complex64, incX int)
Ctpmv(ul Uplo, tA Transpose, d Diag, n int, ap []complex64, x []complex64, incX int)
Ctrsv(ul Uplo, tA Transpose, d Diag, n int, a []complex64, lda int, x []complex64, incX int)
Ctbsv(ul Uplo, tA Transpose, d Diag, n, k int, a []complex64, lda int, x []complex64, incX int)
Ctpsv(ul Uplo, tA Transpose, d Diag, n int, ap []complex64, x []complex64, incX int)
Chemv(ul Uplo, n int, alpha complex64, a []complex64, lda int, x []complex64, incX int, beta complex64, y []complex64, incY int)
Chbmv(ul Uplo, n, k int, alpha complex64, a []complex64, lda int, x []complex64, incX int, beta complex64, y []complex64, incY int)
Chpmv(ul Uplo, n int, alpha complex64, ap []complex64, x []complex64, incX int, beta complex64, y []complex64, incY int)
Cgeru(m, n int, alpha complex64, x []complex64, incX int, y []complex64, incY int, a []complex64, lda int)
Cgerc(m, n int, alpha complex64, x []complex64, incX int, y []complex64, incY int, a []complex64, lda int)
Cher(ul Uplo, n int, alpha float32, x []complex64, incX int, a []complex64, lda int)
Chpr(ul Uplo, n int, alpha float32, x []complex64, incX int, a []complex64)
Cher2(ul Uplo, n int, alpha complex64, x []complex64, incX int, y []complex64, incY int, a []complex64, lda int)
Chpr2(ul Uplo, n int, alpha complex64, x []complex64, incX int, y []complex64, incY int, ap []complex64)
}
// Complex64Level3 implements the single precision complex BLAS Level 3 routines.
type Complex64Level3 interface {
Cgemm(tA, tB Transpose, m, n, k int, alpha complex64, a []complex64, lda int, b []complex64, ldb int, beta complex64, c []complex64, ldc int)
Csymm(s Side, ul Uplo, m, n int, alpha complex64, a []complex64, lda int, b []complex64, ldb int, beta complex64, c []complex64, ldc int)
Csyrk(ul Uplo, t Transpose, n, k int, alpha complex64, a []complex64, lda int, beta complex64, c []complex64, ldc int)
Csyr2k(ul Uplo, t Transpose, n, k int, alpha complex64, a []complex64, lda int, b []complex64, ldb int, beta complex64, c []complex64, ldc int)
Ctrmm(s Side, ul Uplo, tA Transpose, d Diag, m, n int, alpha complex64, a []complex64, lda int, b []complex64, ldb int)
Ctrsm(s Side, ul Uplo, tA Transpose, d Diag, m, n int, alpha complex64, a []complex64, lda int, b []complex64, ldb int)
Chemm(s Side, ul Uplo, m, n int, alpha complex64, a []complex64, lda int, b []complex64, ldb int, beta complex64, c []complex64, ldc int)
Cherk(ul Uplo, t Transpose, n, k int, alpha float32, a []complex64, lda int, beta float32, c []complex64, ldc int)
Cher2k(ul Uplo, t Transpose, n, k int, alpha complex64, a []complex64, lda int, b []complex64, ldb int, beta float32, c []complex64, ldc int)
}
// Complex128 implements the double precision complex BLAS routines.
type Complex128 interface {
Complex128Level1
Complex128Level2
Complex128Level3
}
// Complex128Level1 implements the double precision complex BLAS Level 1 routines.
type Complex128Level1 interface {
Zdotu(n int, x []complex128, incX int, y []complex128, incY int) (dotu complex128)
Zdotc(n int, x []complex128, incX int, y []complex128, incY int) (dotc complex128)
Dznrm2(n int, x []complex128, incX int) float64
Dzasum(n int, x []complex128, incX int) float64
Izamax(n int, x []complex128, incX int) int
Zswap(n int, x []complex128, incX int, y []complex128, incY int)
Zcopy(n int, x []complex128, incX int, y []complex128, incY int)
Zaxpy(n int, alpha complex128, x []complex128, incX int, y []complex128, incY int)
Zscal(n int, alpha complex128, x []complex128, incX int)
Zdscal(n int, alpha float64, x []complex128, incX int)
}
// Complex128Level2 implements the double precision complex BLAS Level 2 routines.
type Complex128Level2 interface {
Zgemv(tA Transpose, m, n int, alpha complex128, a []complex128, lda int, x []complex128, incX int, beta complex128, y []complex128, incY int)
Zgbmv(tA Transpose, m, n int, kL int, kU int, alpha complex128, a []complex128, lda int, x []complex128, incX int, beta complex128, y []complex128, incY int)
Ztrmv(ul Uplo, tA Transpose, d Diag, n int, a []complex128, lda int, x []complex128, incX int)
Ztbmv(ul Uplo, tA Transpose, d Diag, n, k int, a []complex128, lda int, x []complex128, incX int)
Ztpmv(ul Uplo, tA Transpose, d Diag, n int, ap []complex128, x []complex128, incX int)
Ztrsv(ul Uplo, tA Transpose, d Diag, n int, a []complex128, lda int, x []complex128, incX int)
Ztbsv(ul Uplo, tA Transpose, d Diag, n, k int, a []complex128, lda int, x []complex128, incX int)
Ztpsv(ul Uplo, tA Transpose, d Diag, n int, ap []complex128, x []complex128, incX int)
Zhemv(ul Uplo, n int, alpha complex128, a []complex128, lda int, x []complex128, incX int, beta complex128, y []complex128, incY int)
Zhbmv(ul Uplo, n, k int, alpha complex128, a []complex128, lda int, x []complex128, incX int, beta complex128, y []complex128, incY int)
Zhpmv(ul Uplo, n int, alpha complex128, ap []complex128, x []complex128, incX int, beta complex128, y []complex128, incY int)
Zgeru(m, n int, alpha complex128, x []complex128, incX int, y []complex128, incY int, a []complex128, lda int)
Zgerc(m, n int, alpha complex128, x []complex128, incX int, y []complex128, incY int, a []complex128, lda int)
Zher(ul Uplo, n int, alpha float64, x []complex128, incX int, a []complex128, lda int)
Zhpr(ul Uplo, n int, alpha float64, x []complex128, incX int, a []complex128)
Zher2(ul Uplo, n int, alpha complex128, x []complex128, incX int, y []complex128, incY int, a []complex128, lda int)
Zhpr2(ul Uplo, n int, alpha complex128, x []complex128, incX int, y []complex128, incY int, ap []complex128)
}
// Complex128Level3 implements the double precision complex BLAS Level 3 routines.
type Complex128Level3 interface {
Zgemm(tA, tB Transpose, m, n, k int, alpha complex128, a []complex128, lda int, b []complex128, ldb int, beta complex128, c []complex128, ldc int)
Zsymm(s Side, ul Uplo, m, n int, alpha complex128, a []complex128, lda int, b []complex128, ldb int, beta complex128, c []complex128, ldc int)
Zsyrk(ul Uplo, t Transpose, n, k int, alpha complex128, a []complex128, lda int, beta complex128, c []complex128, ldc int)
Zsyr2k(ul Uplo, t Transpose, n, k int, alpha complex128, a []complex128, lda int, b []complex128, ldb int, beta complex128, c []complex128, ldc int)
Ztrmm(s Side, ul Uplo, tA Transpose, d Diag, m, n int, alpha complex128, a []complex128, lda int, b []complex128, ldb int)
Ztrsm(s Side, ul Uplo, tA Transpose, d Diag, m, n int, alpha complex128, a []complex128, lda int, b []complex128, ldb int)
Zhemm(s Side, ul Uplo, m, n int, alpha complex128, a []complex128, lda int, b []complex128, ldb int, beta complex128, c []complex128, ldc int)
Zherk(ul Uplo, t Transpose, n, k int, alpha float64, a []complex128, lda int, beta float64, c []complex128, ldc int)
Zher2k(ul Uplo, t Transpose, n, k int, alpha complex128, a []complex128, lda int, b []complex128, ldb int, beta float64, c []complex128, ldc int)
}
+434
View File
@@ -0,0 +1,434 @@
// 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 blas64 provides a simple interface to the float64 BLAS API.
package blas64
import (
"github.com/gonum/blas"
"github.com/gonum/blas/native"
)
var blas64 blas.Float64 = native.Implementation{}
// Use sets the BLAS float64 implementation to be used by subsequent BLAS calls.
// The default implementation is native.Implementation.
func Use(b blas.Float64) {
blas64 = b
}
// Implementation returns the current BLAS float64 implementation.
//
// Implementation allows direct calls to the current the BLAS float64 implementation
// giving finer control of parameters.
func Implementation() blas.Float64 {
return blas64
}
// Vector represents a vector with an associated element increment.
type Vector struct {
Inc int
Data []float64
}
// General represents a matrix using the conventional storage scheme.
type General struct {
Rows, Cols int
Stride int
Data []float64
}
// Band represents a band matrix using the band storage scheme.
type Band struct {
Rows, Cols int
KL, KU int
Stride int
Data []float64
}
// Triangular represents a triangular matrix using the conventional storage scheme.
type Triangular struct {
N int
Stride int
Data []float64
Uplo blas.Uplo
Diag blas.Diag
}
// TriangularBand represents a triangular matrix using the band storage scheme.
type TriangularBand struct {
N, K int
Stride int
Data []float64
Uplo blas.Uplo
Diag blas.Diag
}
// TriangularPacked represents a triangular matrix using the packed storage scheme.
type TriangularPacked struct {
N int
Data []float64
Uplo blas.Uplo
Diag blas.Diag
}
// Symmetric represents a symmetric matrix using the conventional storage scheme.
type Symmetric struct {
N int
Stride int
Data []float64
Uplo blas.Uplo
}
// SymmetricBand represents a symmetric matrix using the band storage scheme.
type SymmetricBand struct {
N, K int
Stride int
Data []float64
Uplo blas.Uplo
}
// SymmetricPacked represents a symmetric matrix using the packed storage scheme.
type SymmetricPacked struct {
N int
Data []float64
Uplo blas.Uplo
}
// Level 1
const negInc = "blas64: negative vector increment"
// Dot computes the dot product of the two vectors
// \sum_i x[i]*y[i]
func Dot(n int, x, y Vector) float64 {
return blas64.Ddot(n, x.Data, x.Inc, y.Data, y.Inc)
}
// Nrm2 computes the Euclidean norm of a vector,
// sqrt(\sum_i x[i] * x[i]).
//
// Nrm2 will panic if the vector increment is negative.
func Nrm2(n int, x Vector) float64 {
if x.Inc < 0 {
panic(negInc)
}
return blas64.Dnrm2(n, x.Data, x.Inc)
}
// Asum computes the sum of the absolute values of the elements of x.
// \sum_i |x[i]|
//
// Asum will panic if the vector increment is negative.
func Asum(n int, x Vector) float64 {
if x.Inc < 0 {
panic(negInc)
}
return blas64.Dasum(n, x.Data, x.Inc)
}
// Iamax returns the index of the largest element of x. If there are multiple
// such indices the earliest is returned. Iamax returns -1 if n == 0.
//
// Iamax will panic if the vector increment is negative.
func Iamax(n int, x Vector) int {
if x.Inc < 0 {
panic(negInc)
}
return blas64.Idamax(n, x.Data, x.Inc)
}
// Swap exchanges the elements of two vectors.
// x[i], y[i] = y[i], x[i] for all i
func Swap(n int, x, y Vector) {
blas64.Dswap(n, x.Data, x.Inc, y.Data, y.Inc)
}
// Copy copies the elements of x into the elements of y.
// y[i] = x[i] for all i
func Copy(n int, x, y Vector) {
blas64.Dcopy(n, x.Data, x.Inc, y.Data, y.Inc)
}
// Axpy adds alpha times x to y
// y[i] += alpha * x[i] for all i
func Axpy(n int, alpha float64, x, y Vector) {
blas64.Daxpy(n, alpha, x.Data, x.Inc, y.Data, y.Inc)
}
// Rotg computes the plane rotation
// _ _ _ _ _ _
// | c s | | a | | r |
// | -s c | * | b | = | 0 |
// ‾ ‾ ‾ ‾ ‾ ‾
// where
// r = ±(a^2 + b^2)
// c = a/r, the cosine of the plane rotation
// s = b/r, the sine of the plane rotation
func Rotg(a, b float64) (c, s, r, z float64) {
return blas64.Drotg(a, b)
}
// Rotmg computes the modified Givens rotation. See
// http://www.netlib.org/lapack/explore-html/df/deb/drotmg_8f.html
// for more details.
func Rotmg(d1, d2, b1, b2 float64) (p blas.DrotmParams, rd1, rd2, rb1 float64) {
return blas64.Drotmg(d1, d2, b1, b2)
}
// Rot applies a plane transformation.
// x[i] = c * x[i] + s * y[i]
// y[i] = c * y[i] - s * x[i]
func Rot(n int, x, y Vector, c, s float64) {
blas64.Drot(n, x.Data, x.Inc, y.Data, y.Inc, c, s)
}
// Rotm applies the modified Givens rotation to the 2×n matrix.
func Rotm(n int, x, y Vector, p blas.DrotmParams) {
blas64.Drotm(n, x.Data, x.Inc, y.Data, y.Inc, p)
}
// Scal scales x by alpha.
// x[i] *= alpha
//
// Scal will panic if the vector increment is negative
func Scal(n int, alpha float64, x Vector) {
if x.Inc < 0 {
panic(negInc)
}
blas64.Dscal(n, alpha, x.Data, x.Inc)
}
// Level 2
// Gemv computes
// y = alpha * a * x + beta * y if tA = blas.NoTrans
// y = alpha * A^T * x + beta * y if tA = blas.Trans or blas.ConjTrans
// where A is an m×n dense matrix, x and y are vectors, and alpha is a scalar.
func Gemv(tA blas.Transpose, alpha float64, a General, x Vector, beta float64, y Vector) {
blas64.Dgemv(tA, a.Rows, a.Cols, alpha, a.Data, a.Stride, x.Data, x.Inc, beta, y.Data, y.Inc)
}
// Gbmv computes
// y = alpha * A * x + beta * y if tA == blas.NoTrans
// y = alpha * A^T * x + beta * y if tA == blas.Trans or blas.ConjTrans
// where a is an m×n band matrix kL subdiagonals and kU super-diagonals, and
// m and n refer to the size of the full dense matrix it represents.
// x and y are vectors, and alpha and beta are scalars.
func Gbmv(tA blas.Transpose, alpha float64, a Band, x Vector, beta float64, y Vector) {
blas64.Dgbmv(tA, a.Rows, a.Cols, a.KL, a.KU, alpha, a.Data, a.Stride, x.Data, x.Inc, beta, y.Data, y.Inc)
}
// Trmv computes
// x = A * x if tA == blas.NoTrans
// x = A^T * x if tA == blas.Trans or blas.ConjTrans
// A is an n×n Triangular matrix and x is a vector.
func Trmv(tA blas.Transpose, a Triangular, x Vector) {
blas64.Dtrmv(a.Uplo, tA, a.Diag, a.N, a.Data, a.Stride, x.Data, x.Inc)
}
// Tbmv computes
// x = A * x if tA == blas.NoTrans
// x = A^T * x if tA == blas.Trans or blas.ConjTrans
// where A is an n×n triangular banded matrix with k diagonals, and x is a vector.
func Tbmv(tA blas.Transpose, a TriangularBand, x Vector) {
blas64.Dtbmv(a.Uplo, tA, a.Diag, a.N, a.K, a.Data, a.Stride, x.Data, x.Inc)
}
// Tpmv computes
// x = A * x if tA == blas.NoTrans
// x = A^T * x if tA == blas.Trans or blas.ConjTrans
// where A is an n×n unit triangular matrix in packed format, and x is a vector.
func Tpmv(tA blas.Transpose, a TriangularPacked, x Vector) {
blas64.Dtpmv(a.Uplo, tA, a.Diag, a.N, a.Data, x.Data, x.Inc)
}
// Trsv solves
// A * x = b if tA == blas.NoTrans
// A^T * x = b if tA == blas.Trans or blas.ConjTrans
// A is an n×n triangular matrix and x is a vector.
// At entry to the function, x contains the values of b, and the result is
// stored in place into x.
//
// No test for singularity or near-singularity is included in this
// routine. Such tests must be performed before calling this routine.
func Trsv(tA blas.Transpose, a Triangular, x Vector) {
blas64.Dtrsv(a.Uplo, tA, a.Diag, a.N, a.Data, a.Stride, x.Data, x.Inc)
}
// Tbsv solves
// A * x = b
// where A is an n×n triangular banded matrix with k diagonals in packed format,
// and x is a vector.
// At entry to the function, x contains the values of b, and the result is
// stored in place into x.
//
// No test for singularity or near-singularity is included in this
// routine. Such tests must be performed before calling this routine.
func Tbsv(tA blas.Transpose, a TriangularBand, x Vector) {
blas64.Dtbsv(a.Uplo, tA, a.Diag, a.N, a.K, a.Data, a.Stride, x.Data, x.Inc)
}
// Tpsv solves
// A * x = b if tA == blas.NoTrans
// A^T * x = b if tA == blas.Trans or blas.ConjTrans
// where A is an n×n triangular matrix in packed format and x is a vector.
// At entry to the function, x contains the values of b, and the result is
// stored in place into x.
//
// No test for singularity or near-singularity is included in this
// routine. Such tests must be performed before calling this routine.
func Tpsv(tA blas.Transpose, a TriangularPacked, x Vector) {
blas64.Dtpsv(a.Uplo, tA, a.Diag, a.N, a.Data, x.Data, x.Inc)
}
// Symv computes
// y = alpha * A * x + beta * y,
// where a is an n×n symmetric matrix, x and y are vectors, and alpha and
// beta are scalars.
func Symv(alpha float64, a Symmetric, x Vector, beta float64, y Vector) {
blas64.Dsymv(a.Uplo, a.N, alpha, a.Data, a.Stride, x.Data, x.Inc, beta, y.Data, y.Inc)
}
// Sbmv performs
// y = alpha * A * x + beta * y
// where A is an n×n symmetric banded matrix, x and y are vectors, and alpha
// and beta are scalars.
func Sbmv(alpha float64, a SymmetricBand, x Vector, beta float64, y Vector) {
blas64.Dsbmv(a.Uplo, a.N, a.K, alpha, a.Data, a.Stride, x.Data, x.Inc, beta, y.Data, y.Inc)
}
// Spmv performs
// y = alpha * A * x + beta * y,
// where A is an n×n symmetric matrix in packed format, x and y are vectors
// and alpha and beta are scalars.
func Spmv(alpha float64, a SymmetricPacked, x Vector, beta float64, y Vector) {
blas64.Dspmv(a.Uplo, a.N, alpha, a.Data, x.Data, x.Inc, beta, y.Data, y.Inc)
}
// Ger performs the rank-one operation
// A += alpha * x * y^T
// where A is an m×n dense matrix, x and y are vectors, and alpha is a scalar.
func Ger(alpha float64, x, y Vector, a General) {
blas64.Dger(a.Rows, a.Cols, alpha, x.Data, x.Inc, y.Data, y.Inc, a.Data, a.Stride)
}
// Syr performs the rank-one update
// a += alpha * x * x^T
// where a is an n×n symmetric matrix, and x is a vector.
func Syr(alpha float64, x Vector, a Symmetric) {
blas64.Dsyr(a.Uplo, a.N, alpha, x.Data, x.Inc, a.Data, a.Stride)
}
// Spr computes the rank-one operation
// a += alpha * x * x^T
// where a is an n×n symmetric matrix in packed format, x is a vector, and
// alpha is a scalar.
func Spr(alpha float64, x Vector, a SymmetricPacked) {
blas64.Dspr(a.Uplo, a.N, alpha, x.Data, x.Inc, a.Data)
}
// Syr2 performs the symmetric rank-two update
// A += alpha * x * y^T + alpha * y * x^T
// where A is a symmetric n×n matrix, x and y are vectors, and alpha is a scalar.
func Syr2(alpha float64, x, y Vector, a Symmetric) {
blas64.Dsyr2(a.Uplo, a.N, alpha, x.Data, x.Inc, y.Data, y.Inc, a.Data, a.Stride)
}
// Spr2 performs the symmetric rank-2 update
// a += alpha * x * y^T + alpha * y * x^T
// where a is an n×n symmetric matirx in packed format and x and y are vectors.
func Spr2(alpha float64, x, y Vector, a SymmetricPacked) {
blas64.Dspr2(a.Uplo, a.N, alpha, x.Data, x.Inc, y.Data, y.Inc, a.Data)
}
// Level 3
// Gemm computes
// C = beta * C + alpha * A * B.
// tA and tB specify whether A or B are transposed. A, B, and C are m×n dense
// matrices.
func Gemm(tA, tB blas.Transpose, alpha float64, a, b General, beta float64, c General) {
var m, n, k int
if tA == blas.NoTrans {
m, k = a.Rows, a.Cols
} else {
m, k = a.Cols, a.Rows
}
if tB == blas.NoTrans {
n = b.Cols
} else {
n = b.Rows
}
blas64.Dgemm(tA, tB, m, n, k, alpha, a.Data, a.Stride, b.Data, b.Stride, beta, c.Data, c.Stride)
}
// Symm performs one of
// C = alpha * A * B + beta * C if side == blas.Left
// C = alpha * B * A + beta * C if side == blas.Right
// where A is an n×n symmetric matrix, B and C are m×n matrices, and alpha
// is a scalar.
func Symm(s blas.Side, alpha float64, a Symmetric, b General, beta float64, c General) {
var m, n int
if s == blas.Left {
m, n = a.N, b.Cols
} else {
m, n = b.Rows, a.N
}
blas64.Dsymm(s, a.Uplo, m, n, alpha, a.Data, a.Stride, b.Data, b.Stride, beta, c.Data, c.Stride)
}
// Syrk performs the symmetric rank-k operation
// C = alpha * A * A^T + beta*C
// C is an n×n symmetric matrix. A is an n×k matrix if tA == blas.NoTrans, and
// a k×n matrix otherwise. alpha and beta are scalars.
func Syrk(t blas.Transpose, alpha float64, a General, beta float64, c Symmetric) {
var n, k int
if t == blas.NoTrans {
n, k = a.Rows, a.Cols
} else {
n, k = a.Cols, a.Rows
}
blas64.Dsyrk(c.Uplo, t, n, k, alpha, a.Data, a.Stride, beta, c.Data, c.Stride)
}
// Syr2k performs the symmetric rank 2k operation
// C = alpha * A * B^T + alpha * B * A^T + beta * C
// where C is an n×n symmetric matrix. A and B are n×k matrices if
// tA == NoTrans and k×n otherwise. alpha and beta are scalars.
func Syr2k(t blas.Transpose, alpha float64, a, b General, beta float64, c Symmetric) {
var n, k int
if t == blas.NoTrans {
n, k = a.Rows, a.Cols
} else {
n, k = a.Cols, a.Rows
}
blas64.Dsyr2k(c.Uplo, t, n, k, alpha, a.Data, a.Stride, b.Data, b.Stride, beta, c.Data, c.Stride)
}
// Trmm performs
// B = alpha * A * B if tA == blas.NoTrans and side == blas.Left
// B = alpha * A^T * B if tA == blas.Trans or blas.ConjTrans, and side == blas.Left
// B = alpha * B * A if tA == blas.NoTrans and side == blas.Right
// B = alpha * B * A^T if tA == blas.Trans or blas.ConjTrans, and side == blas.Right
// where A is an n×n triangular matrix, and B is an m×n matrix.
func Trmm(s blas.Side, tA blas.Transpose, alpha float64, a Triangular, b General) {
blas64.Dtrmm(s, a.Uplo, tA, a.Diag, b.Rows, b.Cols, alpha, a.Data, a.Stride, b.Data, b.Stride)
}
// Trsm solves
// A * X = alpha * B if tA == blas.NoTrans side == blas.Left
// A^T * X = alpha * B if tA == blas.Trans or blas.ConjTrans, and side == blas.Left
// X * A = alpha * B if tA == blas.NoTrans side == blas.Right
// X * A^T = alpha * B if tA == blas.Trans or blas.ConjTrans, and side == blas.Right
// where A is an n×n triangular matrix, x is an m×n matrix, and alpha is a
// scalar.
//
// At entry to the function, X contains the values of B, and the result is
// stored in place into X.
//
// No check is made that A is invertible.
func Trsm(s blas.Side, tA blas.Transpose, alpha float64, a Triangular, b General) {
blas64.Dtrsm(s, a.Uplo, tA, a.Diag, b.Rows, b.Cols, alpha, a.Data, a.Stride, b.Data, b.Stride)
}
+391
View File
@@ -0,0 +1,391 @@
// Copyright ©2014 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 native
import (
"fmt"
"runtime"
"sync"
"github.com/gonum/blas"
"github.com/gonum/internal/asm"
)
// Dgemm computes
// C = beta * C + alpha * A * B.
// tA and tB specify whether A or B are transposed. A, B, and C are m×n dense
// matrices.
func (Implementation) Dgemm(tA, tB blas.Transpose, m, n, k int, alpha float64, a []float64, lda int, b []float64, ldb int, beta float64, c []float64, ldc int) {
if tA != blas.NoTrans && tA != blas.Trans && tA != blas.ConjTrans {
panic(badTranspose)
}
if tB != blas.NoTrans && tB != blas.Trans && tB != blas.ConjTrans {
panic(badTranspose)
}
var amat, bmat, cmat general64
if tA != blas.NoTrans {
amat = general64{
data: a,
rows: k,
cols: m,
stride: lda,
}
} else {
amat = general64{
data: a,
rows: m,
cols: k,
stride: lda,
}
}
err := amat.check('a')
if err != nil {
panic(err.Error())
}
if tB != blas.NoTrans {
bmat = general64{
data: b,
rows: n,
cols: k,
stride: ldb,
}
} else {
bmat = general64{
data: b,
rows: k,
cols: n,
stride: ldb,
}
}
err = bmat.check('b')
if err != nil {
panic(err.Error())
}
cmat = general64{
data: c,
rows: m,
cols: n,
stride: ldc,
}
err = cmat.check('c')
if err != nil {
panic(err.Error())
}
// scale c
if beta != 1 {
if beta == 0 {
for i := 0; i < m; i++ {
ctmp := cmat.data[i*cmat.stride : i*cmat.stride+cmat.cols]
for j := range ctmp {
ctmp[j] = 0
}
}
} else {
for i := 0; i < m; i++ {
ctmp := cmat.data[i*cmat.stride : i*cmat.stride+cmat.cols]
for j := range ctmp {
ctmp[j] *= beta
}
}
}
}
dgemmParallel(tA, tB, amat, bmat, cmat, alpha)
}
func dgemmParallel(tA, tB blas.Transpose, a, b, c general64, alpha float64) {
// dgemmParallel computes a parallel matrix multiplication by partitioning
// a and b into sub-blocks, and updating c with the multiplication of the sub-block
// In all cases,
// A = [ A_11 A_12 ... A_1j
// A_21 A_22 ... A_2j
// ...
// A_i1 A_i2 ... A_ij]
//
// and same for B. All of the submatrix sizes are blockSize*blockSize except
// at the edges.
// In all cases, there is one dimension for each matrix along which
// C must be updated sequentially.
// Cij = \sum_k Aik Bki, (A * B)
// Cij = \sum_k Aki Bkj, (A^T * B)
// Cij = \sum_k Aik Bjk, (A * B^T)
// Cij = \sum_k Aki Bjk, (A^T * B^T)
//
// This code computes one {i, j} block sequentially along the k dimension,
// and computes all of the {i, j} blocks concurrently. This
// partitioning allows Cij to be updated in-place without race-conditions.
// Instead of launching a goroutine for each possible concurrent computation,
// a number of worker goroutines are created and channels are used to pass
// available and completed cases.
//
// http://alexkr.com/docs/matrixmult.pdf is a good reference on matrix-matrix
// multiplies, though this code does not copy matrices to attempt to eliminate
// cache misses.
aTrans := tA == blas.Trans || tA == blas.ConjTrans
bTrans := tB == blas.Trans || tB == blas.ConjTrans
maxKLen, parBlocks := computeNumBlocks64(a, b, aTrans, bTrans)
if parBlocks < minParBlock {
// The matrix multiplication is small in the dimensions where it can be
// computed concurrently. Just do it in serial.
dgemmSerial(tA, tB, a, b, c, alpha)
return
}
nWorkers := runtime.GOMAXPROCS(0)
if parBlocks < nWorkers {
nWorkers = parBlocks
}
// There is a tradeoff between the workers having to wait for work
// and a large buffer making operations slow.
buf := buffMul * nWorkers
if buf > parBlocks {
buf = parBlocks
}
sendChan := make(chan subMul, buf)
// Launch workers. A worker receives an {i, j} submatrix of c, and computes
// A_ik B_ki (or the transposed version) storing the result in c_ij. When the
// channel is finally closed, it signals to the waitgroup that it has finished
// computing.
var wg sync.WaitGroup
for i := 0; i < nWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// Make local copies of otherwise global variables to reduce shared memory.
// This has a noticable effect on benchmarks in some cases.
alpha := alpha
aTrans := aTrans
bTrans := bTrans
crows := c.rows
ccols := c.cols
for sub := range sendChan {
i := sub.i
j := sub.j
leni := blockSize
if i+leni > crows {
leni = crows - i
}
lenj := blockSize
if j+lenj > ccols {
lenj = ccols - j
}
cSub := c.view(i, j, leni, lenj)
// Compute A_ik B_kj for all k
for k := 0; k < maxKLen; k += blockSize {
lenk := blockSize
if k+lenk > maxKLen {
lenk = maxKLen - k
}
var aSub, bSub general64
if aTrans {
aSub = a.view(k, i, lenk, leni)
} else {
aSub = a.view(i, k, leni, lenk)
}
if bTrans {
bSub = b.view(j, k, lenj, lenk)
} else {
bSub = b.view(k, j, lenk, lenj)
}
dgemmSerial(tA, tB, aSub, bSub, cSub, alpha)
}
}
}()
}
// Send out all of the {i, j} subblocks for computation.
for i := 0; i < c.rows; i += blockSize {
for j := 0; j < c.cols; j += blockSize {
sendChan <- subMul{
i: i,
j: j,
}
}
}
close(sendChan)
wg.Wait()
}
// computeNumBlocks says how many blocks there are to compute. maxKLen says the length of the
// k dimension, parBlocks is the number of blocks that could be computed in parallel
// (the submatrices in i and j). expect is the full number of blocks that will be computed.
func computeNumBlocks64(a, b general64, aTrans, bTrans bool) (maxKLen, parBlocks int) {
aRowBlocks := a.rows / blockSize
if a.rows%blockSize != 0 {
aRowBlocks++
}
aColBlocks := a.cols / blockSize
if a.cols%blockSize != 0 {
aColBlocks++
}
bRowBlocks := b.rows / blockSize
if b.rows%blockSize != 0 {
bRowBlocks++
}
bColBlocks := b.cols / blockSize
if b.cols%blockSize != 0 {
bColBlocks++
}
switch {
case !aTrans && !bTrans:
// Cij = \sum_k Aik Bki
maxKLen = a.cols
parBlocks = aRowBlocks * bColBlocks
case aTrans && !bTrans:
// Cij = \sum_k Aki Bkj
maxKLen = a.rows
parBlocks = aColBlocks * bColBlocks
case !aTrans && bTrans:
// Cij = \sum_k Aik Bjk
maxKLen = a.cols
parBlocks = aRowBlocks * bRowBlocks
case aTrans && bTrans:
// Cij = \sum_k Aki Bjk
maxKLen = a.rows
parBlocks = aColBlocks * bRowBlocks
}
return
}
// dgemmSerial is serial matrix multiply
func dgemmSerial(tA, tB blas.Transpose, a, b, c general64, alpha float64) {
switch {
case tA == blas.NoTrans && tB == blas.NoTrans:
dgemmSerialNotNot(a, b, c, alpha)
return
case tA != blas.NoTrans && tB == blas.NoTrans:
dgemmSerialTransNot(a, b, c, alpha)
return
case tA == blas.NoTrans && tB != blas.NoTrans:
dgemmSerialNotTrans(a, b, c, alpha)
return
case tA != blas.NoTrans && tB != blas.NoTrans:
dgemmSerialTransTrans(a, b, c, alpha)
return
default:
panic("unreachable")
}
}
// dgemmSerial where neither a nor b are transposed
func dgemmSerialNotNot(a, b, c general64, alpha float64) {
if debug {
if a.cols != b.rows {
panic("inner dimension mismatch")
}
if a.rows != c.rows {
panic("outer dimension mismatch")
}
if b.cols != c.cols {
panic("outer dimension mismatch")
}
}
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for i := 0; i < a.rows; i++ {
ctmp := c.data[i*c.stride : i*c.stride+c.cols]
for l, v := range a.data[i*a.stride : i*a.stride+a.cols] {
tmp := alpha * v
if tmp != 0 {
asm.DaxpyUnitary(tmp, b.data[l*b.stride:l*b.stride+b.cols], ctmp, ctmp)
}
}
}
}
// dgemmSerial where neither a is transposed and b is not
func dgemmSerialTransNot(a, b, c general64, alpha float64) {
if debug {
if a.rows != b.rows {
fmt.Println(a.rows, b.rows)
panic("inner dimension mismatch")
}
if a.cols != c.rows {
panic("outer dimension mismatch")
}
if b.cols != c.cols {
panic("outer dimension mismatch")
}
}
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for l := 0; l < a.rows; l++ {
btmp := b.data[l*b.stride : l*b.stride+b.cols]
for i, v := range a.data[l*a.stride : l*a.stride+a.cols] {
tmp := alpha * v
ctmp := c.data[i*c.stride : i*c.stride+c.cols]
if tmp != 0 {
asm.DaxpyUnitary(tmp, btmp, ctmp, ctmp)
}
}
}
}
// dgemmSerial where neither a is not transposed and b is
func dgemmSerialNotTrans(a, b, c general64, alpha float64) {
if debug {
if a.cols != b.cols {
panic("inner dimension mismatch")
}
if a.rows != c.rows {
panic("outer dimension mismatch")
}
if b.rows != c.cols {
panic("outer dimension mismatch")
}
}
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for i := 0; i < a.rows; i++ {
atmp := a.data[i*a.stride : i*a.stride+a.cols]
ctmp := c.data[i*c.stride : i*c.stride+c.cols]
for j := 0; j < b.rows; j++ {
ctmp[j] += alpha * asm.DdotUnitary(atmp, b.data[j*b.stride:j*b.stride+b.cols])
}
}
}
// dgemmSerial where both are transposed
func dgemmSerialTransTrans(a, b, c general64, alpha float64) {
if debug {
if a.rows != b.cols {
panic("inner dimension mismatch")
}
if a.cols != c.rows {
panic("outer dimension mismatch")
}
if b.rows != c.cols {
panic("outer dimension mismatch")
}
}
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for l := 0; l < a.rows; l++ {
for i, v := range a.data[l*a.stride : l*a.stride+a.cols] {
ctmp := c.data[i*c.stride : i*c.stride+c.cols]
if v != 0 {
tmp := alpha * v
if tmp != 0 {
asm.DaxpyInc(tmp, b.data[l:], ctmp, uintptr(b.rows), uintptr(b.stride), 1, 0, 0)
}
}
}
}
}
+88
View File
@@ -0,0 +1,88 @@
// 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.
// Ensure changes made to blas/native are reflected in blas/cgo where relevant.
/*
Package native is a Go implementation of the BLAS API. This implementation
panics when the input arguments are invalid as per the standard, for example
if a vector increment is zero. Please note that the treatment of NaN values
is not specified, and differs among the BLAS implementations.
github.com/gonum/blas/blas64 provides helpful wrapper functions to the BLAS
interface. The rest of this text describes the layout of the data for the input types.
Please note that in the function documentation, x[i] refers to the i^th element
of the vector, which will be different from the i^th element of the slice if
incX != 1.
See http://www.netlib.org/lapack/explore-html/d4/de1/_l_i_c_e_n_s_e_source.html
for more license information.
Vector arguments are effectively strided slices. They have two input arguments,
a number of elements, n, and an increment, incX. The increment specifies the
distance between elements of the vector. The actual Go slice may be longer
than necessary.
The increment may be positive or negative, except in functions with only
a single vector argument where the increment may only be positive. If the increment
is negative, s[0] is the last element in the slice. Note that this is not the same
as counting backward from the end of the slice, as len(s) may be longer than
necessary. So, for example, if n = 5 and incX = 3, the elements of s are
[0 * * 1 * * 2 * * 3 * * 4 * * * ...]
where elements are never accessed. If incX = -3, the same elements are
accessed, just in reverse order (4, 3, 2, 1, 0).
Dense matrices are specified by a number of rows, a number of columns, and a stride.
The stride specifies the number of entries in the slice between the first element
of successive rows. The stride must be at least as large as the number of columns
but may be longer.
[a00 ... a0n a0* ... a1stride-1 a21 ... amn am* ... amstride-1]
Thus, dense[i*ld + j] refers to the {i, j}th element of the matrix.
Symmetric and triangular matrices (non-packed) are stored identically to Dense,
except that only elements in one triangle of the matrix are accessed.
Packed symmetric and packed triangular matrices are laid out with the entries
condensed such that all of the unreferenced elements are removed. So, the upper triangular
matrix
[
1 2 3
0 4 5
0 0 6
]
and the lower-triangular matrix
[
1 0 0
2 3 0
4 5 6
]
will both be compacted as [1 2 3 4 5 6]. The (i, j) element of the original
dense matrix can be found at element i*n - (i-1)*i/2 + j for upper triangular,
and at element i * (i+1) /2 + j for lower triangular.
Banded matrices are laid out in a compact format, constructed by removing the
zeros in the rows and aligning the diagonals. For example, the matrix
[
1 2 3 0 0 0
4 5 6 7 0 0
0 8 9 10 11 0
0 0 12 13 14 15
0 0 0 16 17 18
0 0 0 0 19 20
]
implicitly becomes ( entries are never accessed)
[
* 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
16 17 18 *
19 20 * *
]
which is given to the BLAS routine as [ 1 2 3 4 ...].
See http://www.crest.iu.edu/research/mtl/reference/html/banded.html
for more information
*/
package native
+155
View File
@@ -0,0 +1,155 @@
// Copyright ©2014 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 native
import (
"errors"
"fmt"
"math"
)
func newGeneral64(r, c int) general64 {
return general64{
data: make([]float64, r*c),
rows: r,
cols: c,
stride: c,
}
}
type general64 struct {
data []float64
rows, cols int
stride int
}
// adds element-wise into receiver. rows and columns must match
func (g general64) add(h general64) {
if debug {
if g.rows != h.rows {
panic("blas: row size mismatch")
}
if g.cols != h.cols {
panic("blas: col size mismatch")
}
}
for i := 0; i < g.rows; i++ {
gtmp := g.data[i*g.stride : i*g.stride+g.cols]
for j, v := range h.data[i*h.stride : i*h.stride+h.cols] {
gtmp[j] += v
}
}
}
// at returns the value at the ith row and jth column. For speed reasons, the
// rows and columns are not bounds checked.
func (g general64) at(i, j int) float64 {
if debug {
if i < 0 || i >= g.rows {
panic("blas: row out of bounds")
}
if j < 0 || j >= g.cols {
panic("blas: col out of bounds")
}
}
return g.data[i*g.stride+j]
}
func (g general64) check(c byte) error {
if g.rows < 0 {
return errors.New("blas: rows < 0")
}
if g.cols < 0 {
return errors.New("blas: cols < 0")
}
if g.stride < 1 {
return errors.New("blas: stride < 1")
}
if g.stride < g.cols {
return errors.New("blas: illegal stride")
}
if (g.rows-1)*g.stride+g.cols > len(g.data) {
return fmt.Errorf("blas: index of %c out of range", c)
}
return nil
}
func (g general64) clone() general64 {
data := make([]float64, len(g.data))
copy(data, g.data)
return general64{
data: data,
rows: g.rows,
cols: g.cols,
stride: g.stride,
}
}
// assumes they are the same size
func (g general64) copy(h general64) {
if debug {
if g.rows != h.rows {
panic("blas: row mismatch")
}
if g.cols != h.cols {
panic("blas: col mismatch")
}
}
for k := 0; k < g.rows; k++ {
copy(g.data[k*g.stride:(k+1)*g.stride], h.data[k*h.stride:(k+1)*h.stride])
}
}
func (g general64) equal(a general64) bool {
if g.rows != a.rows || g.cols != a.cols || g.stride != a.stride {
return false
}
for i, v := range g.data {
if a.data[i] != v {
return false
}
}
return true
}
/*
// print is to aid debugging. Commented out to avoid fmt import
func (g general64) print() {
fmt.Println("r = ", g.rows, "c = ", g.cols, "stride: ", g.stride)
for i := 0; i < g.rows; i++ {
fmt.Println(g.data[i*g.stride : (i+1)*g.stride])
}
}
*/
func (g general64) view(i, j, r, c int) general64 {
if debug {
if i < 0 || i+r > g.rows {
panic("blas: row out of bounds")
}
if j < 0 || j+c > g.cols {
panic("blas: col out of bounds")
}
}
return general64{
data: g.data[i*g.stride+j : (i+r-1)*g.stride+j+c],
rows: r,
cols: c,
stride: g.stride,
}
}
func (g general64) equalWithinAbs(a general64, tol float64) bool {
if g.rows != a.rows || g.cols != a.cols || g.stride != a.stride {
return false
}
for i, v := range g.data {
if math.Abs(a.data[i]-v) > tol {
return false
}
}
return true
}
+157
View File
@@ -0,0 +1,157 @@
// Generated code do not edit. Run `go generate`.
// Copyright ©2014 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 native
import (
"errors"
"fmt"
math "github.com/gonum/blas/native/internal/math32"
)
func newGeneral32(r, c int) general32 {
return general32{
data: make([]float32, r*c),
rows: r,
cols: c,
stride: c,
}
}
type general32 struct {
data []float32
rows, cols int
stride int
}
// adds element-wise into receiver. rows and columns must match
func (g general32) add(h general32) {
if debug {
if g.rows != h.rows {
panic("blas: row size mismatch")
}
if g.cols != h.cols {
panic("blas: col size mismatch")
}
}
for i := 0; i < g.rows; i++ {
gtmp := g.data[i*g.stride : i*g.stride+g.cols]
for j, v := range h.data[i*h.stride : i*h.stride+h.cols] {
gtmp[j] += v
}
}
}
// at returns the value at the ith row and jth column. For speed reasons, the
// rows and columns are not bounds checked.
func (g general32) at(i, j int) float32 {
if debug {
if i < 0 || i >= g.rows {
panic("blas: row out of bounds")
}
if j < 0 || j >= g.cols {
panic("blas: col out of bounds")
}
}
return g.data[i*g.stride+j]
}
func (g general32) check(c byte) error {
if g.rows < 0 {
return errors.New("blas: rows < 0")
}
if g.cols < 0 {
return errors.New("blas: cols < 0")
}
if g.stride < 1 {
return errors.New("blas: stride < 1")
}
if g.stride < g.cols {
return errors.New("blas: illegal stride")
}
if (g.rows-1)*g.stride+g.cols > len(g.data) {
return fmt.Errorf("blas: index of %c out of range", c)
}
return nil
}
func (g general32) clone() general32 {
data := make([]float32, len(g.data))
copy(data, g.data)
return general32{
data: data,
rows: g.rows,
cols: g.cols,
stride: g.stride,
}
}
// assumes they are the same size
func (g general32) copy(h general32) {
if debug {
if g.rows != h.rows {
panic("blas: row mismatch")
}
if g.cols != h.cols {
panic("blas: col mismatch")
}
}
for k := 0; k < g.rows; k++ {
copy(g.data[k*g.stride:(k+1)*g.stride], h.data[k*h.stride:(k+1)*h.stride])
}
}
func (g general32) equal(a general32) bool {
if g.rows != a.rows || g.cols != a.cols || g.stride != a.stride {
return false
}
for i, v := range g.data {
if a.data[i] != v {
return false
}
}
return true
}
/*
// print is to aid debugging. Commented out to avoid fmt import
func (g general32) print() {
fmt.Println("r = ", g.rows, "c = ", g.cols, "stride: ", g.stride)
for i := 0; i < g.rows; i++ {
fmt.Println(g.data[i*g.stride : (i+1)*g.stride])
}
}
*/
func (g general32) view(i, j, r, c int) general32 {
if debug {
if i < 0 || i+r > g.rows {
panic("blas: row out of bounds")
}
if j < 0 || j+c > g.cols {
panic("blas: col out of bounds")
}
}
return general32{
data: g.data[i*g.stride+j : (i+r-1)*g.stride+j+c],
rows: r,
cols: c,
stride: g.stride,
}
}
func (g general32) equalWithinAbs(a general32, tol float32) bool {
if g.rows != a.rows || g.cols != a.cols || g.stride != a.stride {
return false
}
for i, v := range g.data {
if math.Abs(a.data[i]-v) > tol {
return false
}
}
return true
}
+113
View File
@@ -0,0 +1,113 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// 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 math32 provides float32 versions of standard library math package
// routines used by gonum/blas/native.
package math32
import (
"math"
)
const (
unan = 0x7fc00000
uinf = 0x7f800000
uneginf = 0xff800000
mask = 0x7f8 >> 3
shift = 32 - 8 - 1
bias = 127
)
// Abs returns the absolute value of x.
//
// Special cases are:
// Abs(±Inf) = +Inf
// Abs(NaN) = NaN
func Abs(x float32) float32 {
switch {
case x < 0:
return -x
case x == 0:
return 0 // return correctly abs(-0)
}
return x
}
// Copysign returns a value with the magnitude
// of x and the sign of y.
func Copysign(x, y float32) float32 {
const sign = 1 << 31
return math.Float32frombits(math.Float32bits(x)&^sign | math.Float32bits(y)&sign)
}
// Hypot returns Sqrt(p*p + q*q), taking care to avoid
// unnecessary overflow and underflow.
//
// Special cases are:
// Hypot(±Inf, q) = +Inf
// Hypot(p, ±Inf) = +Inf
// Hypot(NaN, q) = NaN
// Hypot(p, NaN) = NaN
func Hypot(p, q float32) float32 {
// special cases
switch {
case IsInf(p, 0) || IsInf(q, 0):
return Inf(1)
case IsNaN(p) || IsNaN(q):
return NaN()
}
if p < 0 {
p = -p
}
if q < 0 {
q = -q
}
if p < q {
p, q = q, p
}
if p == 0 {
return 0
}
q = q / p
return p * Sqrt(1+q*q)
}
// Inf returns positive infinity if sign >= 0, negative infinity if sign < 0.
func Inf(sign int) float32 {
var v uint32
if sign >= 0 {
v = uinf
} else {
v = uneginf
}
return math.Float32frombits(v)
}
// IsInf reports whether f is an infinity, according to sign.
// If sign > 0, IsInf reports whether f is positive infinity.
// If sign < 0, IsInf reports whether f is negative infinity.
// If sign == 0, IsInf reports whether f is either infinity.
func IsInf(f float32, sign int) bool {
// Test for infinity by comparing against maximum float.
// To avoid the floating-point hardware, could use:
// x := math.Float32bits(f);
// return sign >= 0 && x == uinf || sign <= 0 && x == uneginf;
return sign >= 0 && f > math.MaxFloat32 || sign <= 0 && f < -math.MaxFloat32
}
// IsNaN reports whether f is an IEEE 754 ``not-a-number'' value.
func IsNaN(f float32) (is bool) {
// IEEE 754 says that only NaNs satisfy f != f.
// To avoid the floating-point hardware, could use:
// x := math.Float32bits(f);
// return uint32(x>>shift)&mask == mask && x != uinf && x != uneginf
return f != f
}
// NaN returns an IEEE 754 ``not-a-number'' value.
func NaN() float32 { return math.Float32frombits(unan) }
+25
View File
@@ -0,0 +1,25 @@
// 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.
//+build !amd64 noasm
package math32
import (
"math"
)
// Sqrt returns the square root of x.
//
// Special cases are:
// Sqrt(+Inf) = +Inf
// Sqrt(±0) = ±0
// Sqrt(x < 0) = NaN
// Sqrt(NaN) = NaN
func Sqrt(x float32) float32 {
// FIXME(kortschak): Direct translation of the math package
// asm code for 386 fails to build. No test hardware is available
// for arm, so using conversion instead.
return float32(math.Sqrt(float64(x)))
}
+20
View File
@@ -0,0 +1,20 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// 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.
//+build !noasm
package math32
// Sqrt returns the square root of x.
//
// Special cases are:
// Sqrt(+Inf) = +Inf
// Sqrt(±0) = ±0
// Sqrt(x < 0) = NaN
// Sqrt(NaN) = NaN
func Sqrt(x float32) float32
+20
View File
@@ -0,0 +1,20 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// 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.
//+build !noasm
// TODO(kortschak): use textflag.h after we drop Go 1.3 support
//#include "textflag.h"
// Don't insert stack check preamble.
#define NOSPLIT 4
// func Sqrt(x float32) float32
TEXT ·Sqrt(SB),NOSPLIT,$0
SQRTSS x+0(FP), X0
MOVSS X0, ret+8(FP)
RET
+599
View File
@@ -0,0 +1,599 @@
// 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 native
import (
"math"
"github.com/gonum/blas"
"github.com/gonum/internal/asm"
)
var _ blas.Float64Level1 = Implementation{}
// Dnrm2 computes the Euclidean norm of a vector,
// sqrt(\sum_i x[i] * x[i]).
// This function returns 0 if incX is negative.
func (Implementation) Dnrm2(n int, x []float64, incX int) float64 {
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return 0
}
if incX > 0 && (n-1)*incX >= len(x) {
panic(badX)
}
if n < 2 {
if n == 1 {
return math.Abs(x[0])
}
if n == 0 {
return 0
}
if n < 1 {
panic(negativeN)
}
}
var (
scale float64 = 0
sumSquares float64 = 1
)
if incX == 1 {
x = x[:n]
for _, v := range x {
absxi := math.Abs(v)
if scale < absxi {
sumSquares = 1 + sumSquares*(scale/absxi)*(scale/absxi)
scale = absxi
} else {
sumSquares = sumSquares + (absxi/scale)*(absxi/scale)
}
}
return scale * math.Sqrt(sumSquares)
}
for ix := 0; ix < n*incX; ix += incX {
val := x[ix]
if val == 0 {
continue
}
absxi := math.Abs(val)
if scale < absxi {
sumSquares = 1 + sumSquares*(scale/absxi)*(scale/absxi)
scale = absxi
} else {
sumSquares = sumSquares + (absxi/scale)*(absxi/scale)
}
}
return scale * math.Sqrt(sumSquares)
}
// Dasum computes the sum of the absolute values of the elements of x.
// \sum_i |x[i]|
// Dasum returns 0 if incX is negative.
func (Implementation) Dasum(n int, x []float64, incX int) float64 {
var sum float64
if n < 0 {
panic(negativeN)
}
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return 0
}
if incX > 0 && (n-1)*incX >= len(x) {
panic(badX)
}
if incX == 1 {
x = x[:n]
for _, v := range x {
sum += math.Abs(v)
}
return sum
}
for i := 0; i < n; i++ {
sum += math.Abs(x[i*incX])
}
return sum
}
// Idamax returns the index of the largest element of x. If there are multiple
// such indices the earliest is returned. Idamax returns -1 if incX is negative or if
// n == 0.
func (Implementation) Idamax(n int, x []float64, incX int) int {
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return -1
}
if incX > 0 && (n-1)*incX >= len(x) {
panic(badX)
}
if n < 2 {
if n == 1 {
return 0
}
if n == 0 {
return -1 // Netlib returns invalid index when n == 0
}
if n < 1 {
panic(negativeN)
}
}
idx := 0
max := math.Abs(x[0])
if incX == 1 {
for i, v := range x[:n] {
absV := math.Abs(v)
if absV > max {
max = absV
idx = i
}
}
return idx
}
ix := incX
for i := 1; i < n; i++ {
v := x[ix]
absV := math.Abs(v)
if absV > max {
max = absV
idx = i
}
ix += incX
}
return idx
}
// Dswap exchanges the elements of two vectors.
// x[i], y[i] = y[i], x[i] for all i
func (Implementation) Dswap(n int, x []float64, incX int, y []float64, incY int) {
if n < 1 {
if n == 0 {
return
}
panic(negativeN)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
if incX == 1 && incY == 1 {
x = x[:n]
for i, v := range x {
x[i], y[i] = y[i], v
}
return
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
for i := 0; i < n; i++ {
x[ix], y[iy] = y[iy], x[ix]
ix += incX
iy += incY
}
}
// Dcopy copies the elements of x into the elements of y.
// y[i] = x[i] for all i
func (Implementation) Dcopy(n int, x []float64, incX int, y []float64, incY int) {
if n < 1 {
if n == 0 {
return
}
panic(negativeN)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
if incX == 1 && incY == 1 {
copy(y[:n], x[:n])
return
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
for i := 0; i < n; i++ {
y[iy] = x[ix]
ix += incX
iy += incY
}
}
// Daxpy adds alpha times x to y
// y[i] += alpha * x[i] for all i
func (Implementation) Daxpy(n int, alpha float64, x []float64, incX int, y []float64, incY int) {
if n < 1 {
if n == 0 {
return
}
panic(negativeN)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
if alpha == 0 {
return
}
if incX == 1 && incY == 1 {
if len(x) < n {
panic(badLenX)
}
if len(y) < n {
panic(badLenY)
}
asm.DaxpyUnitary(alpha, x[:n], y, y)
return
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
if ix >= len(x) || ix+(n-1)*incX >= len(x) {
panic(badLenX)
}
if iy >= len(y) || iy+(n-1)*incY >= len(y) {
panic(badLenY)
}
asm.DaxpyInc(alpha, x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy))
}
// Drotg computes the plane rotation
// _ _ _ _ _ _
// | c s | | a | | r |
// | -s c | * | b | = | 0 |
// ‾ ‾ ‾ ‾ ‾ ‾
// where
// r = ±(a^2 + b^2)
// c = a/r, the cosine of the plane rotation
// s = b/r, the sine of the plane rotation
//
// NOTE: There is a discrepancy between the refence implementation and the BLAS
// technical manual regarding the sign for r when a or b are zero.
// Drotg agrees with the definition in the manual and other
// common BLAS implementations.
func (Implementation) Drotg(a, b float64) (c, s, r, z float64) {
if b == 0 && a == 0 {
return 1, 0, a, 0
}
absA := math.Abs(a)
absB := math.Abs(b)
aGTb := absA > absB
r = math.Hypot(a, b)
if aGTb {
r = math.Copysign(r, a)
} else {
r = math.Copysign(r, b)
}
c = a / r
s = b / r
if aGTb {
z = s
} else if c != 0 { // r == 0 case handled above
z = 1 / c
} else {
z = 1
}
return
}
// Drotmg computes the modified Givens rotation. See
// http://www.netlib.org/lapack/explore-html/df/deb/drotmg_8f.html
// for more details.
func (Implementation) Drotmg(d1, d2, x1, y1 float64) (p blas.DrotmParams, rd1, rd2, rx1 float64) {
var p1, p2, q1, q2, u float64
const (
gam = 4096.0
gamsq = 16777216.0
rgamsq = 5.9604645e-8
)
if d1 < 0 {
p.Flag = blas.Rescaling
return
}
p2 = d2 * y1
if p2 == 0 {
p.Flag = blas.Identity
rd1 = d1
rd2 = d2
rx1 = x1
return
}
p1 = d1 * x1
q2 = p2 * y1
q1 = p1 * x1
absQ1 := math.Abs(q1)
absQ2 := math.Abs(q2)
if absQ1 < absQ2 && q2 < 0 {
p.Flag = blas.Rescaling
return
}
if d1 == 0 {
p.Flag = blas.Diagonal
p.H[0] = p1 / p2
p.H[3] = x1 / y1
u = 1 + p.H[0]*p.H[3]
rd1, rd2 = d2/u, d1/u
rx1 = y1 / u
return
}
// Now we know that d1 != 0, and d2 != 0. If d2 == 0, it would be caught
// when p2 == 0, and if d1 == 0, then it is caught above
if absQ1 > absQ2 {
p.H[1] = -y1 / x1
p.H[2] = p2 / p1
u = 1 - p.H[2]*p.H[1]
rd1 = d1
rd2 = d2
rx1 = x1
p.Flag = blas.OffDiagonal
// u must be greater than zero because |q1| > |q2|, so check from netlib
// is unnecessary
// This is left in for ease of comparison with complex routines
//if u > 0 {
rd1 /= u
rd2 /= u
rx1 *= u
//}
} else {
p.Flag = blas.Diagonal
p.H[0] = p1 / p2
p.H[3] = x1 / y1
u = 1 + p.H[0]*p.H[3]
rd1 = d2 / u
rd2 = d1 / u
rx1 = y1 * u
}
for rd1 <= rgamsq || rd1 >= gamsq {
if p.Flag == blas.OffDiagonal {
p.H[0] = 1
p.H[3] = 1
p.Flag = blas.Rescaling
} else if p.Flag == blas.Diagonal {
p.H[1] = -1
p.H[2] = 1
p.Flag = blas.Rescaling
}
if rd1 <= rgamsq {
rd1 *= gam * gam
rx1 /= gam
p.H[0] /= gam
p.H[2] /= gam
} else {
rd1 /= gam * gam
rx1 *= gam
p.H[0] *= gam
p.H[2] *= gam
}
}
for math.Abs(rd2) <= rgamsq || math.Abs(rd2) >= gamsq {
if p.Flag == blas.OffDiagonal {
p.H[0] = 1
p.H[3] = 1
p.Flag = blas.Rescaling
} else if p.Flag == blas.Diagonal {
p.H[1] = -1
p.H[2] = 1
p.Flag = blas.Rescaling
}
if math.Abs(rd2) <= rgamsq {
rd2 *= gam * gam
p.H[1] /= gam
p.H[3] /= gam
} else {
rd2 /= gam * gam
p.H[1] *= gam
p.H[3] *= gam
}
}
return
}
// Drot applies a plane transformation.
// x[i] = c * x[i] + s * y[i]
// y[i] = c * y[i] - s * x[i]
func (Implementation) Drot(n int, x []float64, incX int, y []float64, incY int, c float64, s float64) {
if n < 1 {
if n == 0 {
return
}
panic(negativeN)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
if incX == 1 && incY == 1 {
x = x[:n]
for i, vx := range x {
vy := y[i]
x[i], y[i] = c*vx+s*vy, c*vy-s*vx
}
return
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
for i := 0; i < n; i++ {
vx := x[ix]
vy := y[iy]
x[ix], y[iy] = c*vx+s*vy, c*vy-s*vx
ix += incX
iy += incY
}
}
// Drotm applies the modified Givens rotation to the 2×n matrix.
func (Implementation) Drotm(n int, x []float64, incX int, y []float64, incY int, p blas.DrotmParams) {
if n <= 0 {
if n == 0 {
return
}
panic(negativeN)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
var h11, h12, h21, h22 float64
var ix, iy int
switch p.Flag {
case blas.Identity:
return
case blas.Rescaling:
h11 = p.H[0]
h12 = p.H[2]
h21 = p.H[1]
h22 = p.H[3]
case blas.OffDiagonal:
h11 = 1
h12 = p.H[2]
h21 = p.H[1]
h22 = 1
case blas.Diagonal:
h11 = p.H[0]
h12 = 1
h21 = -1
h22 = p.H[3]
}
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
if incX == 1 && incY == 1 {
x = x[:n]
for i, vx := range x {
vy := y[i]
x[i], y[i] = vx*h11+vy*h12, vx*h21+vy*h22
}
return
}
for i := 0; i < n; i++ {
vx := x[ix]
vy := y[iy]
x[ix], y[iy] = vx*h11+vy*h12, vx*h21+vy*h22
ix += incX
iy += incY
}
return
}
// Dscal scales x by alpha.
// x[i] *= alpha
// Dscal has no effect if incX < 0.
func (Implementation) Dscal(n int, alpha float64, x []float64, incX int) {
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return
}
if incX > 0 && (n-1)*incX >= len(x) {
panic(badX)
}
if n < 1 {
if n == 0 {
return
}
if n < 1 {
panic(negativeN)
}
}
if alpha == 0 {
if incX == 1 {
x = x[:n]
for i := range x {
x[i] = 0
}
}
for ix := 0; ix < n*incX; ix += incX {
x[ix] = 0
}
}
if incX == 1 {
x = x[:n]
for i := range x {
x[i] *= alpha
}
return
}
for ix := 0; ix < n*incX; ix += incX {
x[ix] *= alpha
}
return
}
+46
View File
@@ -0,0 +1,46 @@
// 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 native
import (
"github.com/gonum/internal/asm"
)
// Ddot computes the dot product of the two vectors
// \sum_i x[i]*y[i]
func (Implementation) Ddot(n int, x []float64, incX int, y []float64, incY int) float64 {
if n < 0 {
panic(negativeN)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if incX == 1 && incY == 1 {
if len(x) < n {
panic(badLenX)
}
if len(y) < n {
panic(badLenY)
}
return asm.DdotUnitary(x[:n], y)
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
if ix >= len(x) || ix+(n-1)*incX >= len(x) {
panic(badLenX)
}
if iy >= len(y) || iy+(n-1)*incY >= len(y) {
panic(badLenY)
}
return asm.DdotInc(x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy))
}
+623
View File
@@ -0,0 +1,623 @@
// Generated code do not edit. Run `go generate`.
// 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 native
import (
math "github.com/gonum/blas/native/internal/math32"
"github.com/gonum/blas"
"github.com/gonum/internal/asm"
)
var _ blas.Float32Level1 = Implementation{}
// Snrm2 computes the Euclidean norm of a vector,
// sqrt(\sum_i x[i] * x[i]).
// This function returns 0 if incX is negative.
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Snrm2(n int, x []float32, incX int) float32 {
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return 0
}
if incX > 0 && (n-1)*incX >= len(x) {
panic(badX)
}
if n < 2 {
if n == 1 {
return math.Abs(x[0])
}
if n == 0 {
return 0
}
if n < 1 {
panic(negativeN)
}
}
var (
scale float32 = 0
sumSquares float32 = 1
)
if incX == 1 {
x = x[:n]
for _, v := range x {
absxi := math.Abs(v)
if scale < absxi {
sumSquares = 1 + sumSquares*(scale/absxi)*(scale/absxi)
scale = absxi
} else {
sumSquares = sumSquares + (absxi/scale)*(absxi/scale)
}
}
return scale * math.Sqrt(sumSquares)
}
for ix := 0; ix < n*incX; ix += incX {
val := x[ix]
if val == 0 {
continue
}
absxi := math.Abs(val)
if scale < absxi {
sumSquares = 1 + sumSquares*(scale/absxi)*(scale/absxi)
scale = absxi
} else {
sumSquares = sumSquares + (absxi/scale)*(absxi/scale)
}
}
return scale * math.Sqrt(sumSquares)
}
// Sasum computes the sum of the absolute values of the elements of x.
// \sum_i |x[i]|
// Sasum returns 0 if incX is negative.
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Sasum(n int, x []float32, incX int) float32 {
var sum float32
if n < 0 {
panic(negativeN)
}
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return 0
}
if incX > 0 && (n-1)*incX >= len(x) {
panic(badX)
}
if incX == 1 {
x = x[:n]
for _, v := range x {
sum += math.Abs(v)
}
return sum
}
for i := 0; i < n; i++ {
sum += math.Abs(x[i*incX])
}
return sum
}
// Isamax returns the index of the largest element of x. If there are multiple
// such indices the earliest is returned. Idamax returns -1 if incX is negative or if
// n == 0.
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Isamax(n int, x []float32, incX int) int {
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return -1
}
if incX > 0 && (n-1)*incX >= len(x) {
panic(badX)
}
if n < 2 {
if n == 1 {
return 0
}
if n == 0 {
return -1 // Netlib returns invalid index when n == 0
}
if n < 1 {
panic(negativeN)
}
}
idx := 0
max := math.Abs(x[0])
if incX == 1 {
for i, v := range x[:n] {
absV := math.Abs(v)
if absV > max {
max = absV
idx = i
}
}
return idx
}
ix := incX
for i := 1; i < n; i++ {
v := x[ix]
absV := math.Abs(v)
if absV > max {
max = absV
idx = i
}
ix += incX
}
return idx
}
// Sswap exchanges the elements of two vectors.
// x[i], y[i] = y[i], x[i] for all i
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Sswap(n int, x []float32, incX int, y []float32, incY int) {
if n < 1 {
if n == 0 {
return
}
panic(negativeN)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
if incX == 1 && incY == 1 {
x = x[:n]
for i, v := range x {
x[i], y[i] = y[i], v
}
return
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
for i := 0; i < n; i++ {
x[ix], y[iy] = y[iy], x[ix]
ix += incX
iy += incY
}
}
// Scopy copies the elements of x into the elements of y.
// y[i] = x[i] for all i
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Scopy(n int, x []float32, incX int, y []float32, incY int) {
if n < 1 {
if n == 0 {
return
}
panic(negativeN)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
if incX == 1 && incY == 1 {
copy(y[:n], x[:n])
return
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
for i := 0; i < n; i++ {
y[iy] = x[ix]
ix += incX
iy += incY
}
}
// Saxpy adds alpha times x to y
// y[i] += alpha * x[i] for all i
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Saxpy(n int, alpha float32, x []float32, incX int, y []float32, incY int) {
if n < 1 {
if n == 0 {
return
}
panic(negativeN)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
if alpha == 0 {
return
}
if incX == 1 && incY == 1 {
if len(x) < n {
panic(badLenX)
}
if len(y) < n {
panic(badLenY)
}
asm.SaxpyUnitary(alpha, x[:n], y, y)
return
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
if ix >= len(x) || ix+(n-1)*incX >= len(x) {
panic(badLenX)
}
if iy >= len(y) || iy+(n-1)*incY >= len(y) {
panic(badLenY)
}
asm.SaxpyInc(alpha, x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy))
}
// Srotg computes the plane rotation
// _ _ _ _ _ _
// | c s | | a | | r |
// | -s c | * | b | = | 0 |
// ‾ ‾ ‾ ‾ ‾ ‾
// where
// r = ±(a^2 + b^2)
// c = a/r, the cosine of the plane rotation
// s = b/r, the sine of the plane rotation
//
// NOTE: There is a discrepancy between the refence implementation and the BLAS
// technical manual regarding the sign for r when a or b are zero.
// Srotg agrees with the definition in the manual and other
// common BLAS implementations.
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Srotg(a, b float32) (c, s, r, z float32) {
if b == 0 && a == 0 {
return 1, 0, a, 0
}
absA := math.Abs(a)
absB := math.Abs(b)
aGTb := absA > absB
r = math.Hypot(a, b)
if aGTb {
r = math.Copysign(r, a)
} else {
r = math.Copysign(r, b)
}
c = a / r
s = b / r
if aGTb {
z = s
} else if c != 0 { // r == 0 case handled above
z = 1 / c
} else {
z = 1
}
return
}
// Srotmg computes the modified Givens rotation. See
// http://www.netlib.org/lapack/explore-html/df/deb/drotmg_8f.html
// for more details.
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Srotmg(d1, d2, x1, y1 float32) (p blas.SrotmParams, rd1, rd2, rx1 float32) {
var p1, p2, q1, q2, u float32
const (
gam = 4096.0
gamsq = 16777216.0
rgamsq = 5.9604645e-8
)
if d1 < 0 {
p.Flag = blas.Rescaling
return
}
p2 = d2 * y1
if p2 == 0 {
p.Flag = blas.Identity
rd1 = d1
rd2 = d2
rx1 = x1
return
}
p1 = d1 * x1
q2 = p2 * y1
q1 = p1 * x1
absQ1 := math.Abs(q1)
absQ2 := math.Abs(q2)
if absQ1 < absQ2 && q2 < 0 {
p.Flag = blas.Rescaling
return
}
if d1 == 0 {
p.Flag = blas.Diagonal
p.H[0] = p1 / p2
p.H[3] = x1 / y1
u = 1 + p.H[0]*p.H[3]
rd1, rd2 = d2/u, d1/u
rx1 = y1 / u
return
}
// Now we know that d1 != 0, and d2 != 0. If d2 == 0, it would be caught
// when p2 == 0, and if d1 == 0, then it is caught above
if absQ1 > absQ2 {
p.H[1] = -y1 / x1
p.H[2] = p2 / p1
u = 1 - p.H[2]*p.H[1]
rd1 = d1
rd2 = d2
rx1 = x1
p.Flag = blas.OffDiagonal
// u must be greater than zero because |q1| > |q2|, so check from netlib
// is unnecessary
// This is left in for ease of comparison with complex routines
//if u > 0 {
rd1 /= u
rd2 /= u
rx1 *= u
//}
} else {
p.Flag = blas.Diagonal
p.H[0] = p1 / p2
p.H[3] = x1 / y1
u = 1 + p.H[0]*p.H[3]
rd1 = d2 / u
rd2 = d1 / u
rx1 = y1 * u
}
for rd1 <= rgamsq || rd1 >= gamsq {
if p.Flag == blas.OffDiagonal {
p.H[0] = 1
p.H[3] = 1
p.Flag = blas.Rescaling
} else if p.Flag == blas.Diagonal {
p.H[1] = -1
p.H[2] = 1
p.Flag = blas.Rescaling
}
if rd1 <= rgamsq {
rd1 *= gam * gam
rx1 /= gam
p.H[0] /= gam
p.H[2] /= gam
} else {
rd1 /= gam * gam
rx1 *= gam
p.H[0] *= gam
p.H[2] *= gam
}
}
for math.Abs(rd2) <= rgamsq || math.Abs(rd2) >= gamsq {
if p.Flag == blas.OffDiagonal {
p.H[0] = 1
p.H[3] = 1
p.Flag = blas.Rescaling
} else if p.Flag == blas.Diagonal {
p.H[1] = -1
p.H[2] = 1
p.Flag = blas.Rescaling
}
if math.Abs(rd2) <= rgamsq {
rd2 *= gam * gam
p.H[1] /= gam
p.H[3] /= gam
} else {
rd2 /= gam * gam
p.H[1] *= gam
p.H[3] *= gam
}
}
return
}
// Srot applies a plane transformation.
// x[i] = c * x[i] + s * y[i]
// y[i] = c * y[i] - s * x[i]
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Srot(n int, x []float32, incX int, y []float32, incY int, c float32, s float32) {
if n < 1 {
if n == 0 {
return
}
panic(negativeN)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
if incX == 1 && incY == 1 {
x = x[:n]
for i, vx := range x {
vy := y[i]
x[i], y[i] = c*vx+s*vy, c*vy-s*vx
}
return
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
for i := 0; i < n; i++ {
vx := x[ix]
vy := y[iy]
x[ix], y[iy] = c*vx+s*vy, c*vy-s*vx
ix += incX
iy += incY
}
}
// Srotm applies the modified Givens rotation to the 2×n matrix.
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Srotm(n int, x []float32, incX int, y []float32, incY int, p blas.SrotmParams) {
if n <= 0 {
if n == 0 {
return
}
panic(negativeN)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
var h11, h12, h21, h22 float32
var ix, iy int
switch p.Flag {
case blas.Identity:
return
case blas.Rescaling:
h11 = p.H[0]
h12 = p.H[2]
h21 = p.H[1]
h22 = p.H[3]
case blas.OffDiagonal:
h11 = 1
h12 = p.H[2]
h21 = p.H[1]
h22 = 1
case blas.Diagonal:
h11 = p.H[0]
h12 = 1
h21 = -1
h22 = p.H[3]
}
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
if incX == 1 && incY == 1 {
x = x[:n]
for i, vx := range x {
vy := y[i]
x[i], y[i] = vx*h11+vy*h12, vx*h21+vy*h22
}
return
}
for i := 0; i < n; i++ {
vx := x[ix]
vy := y[iy]
x[ix], y[iy] = vx*h11+vy*h12, vx*h21+vy*h22
ix += incX
iy += incY
}
return
}
// Sscal scales x by alpha.
// x[i] *= alpha
// Sscal has no effect if incX < 0.
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Sscal(n int, alpha float32, x []float32, incX int) {
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return
}
if incX > 0 && (n-1)*incX >= len(x) {
panic(badX)
}
if n < 1 {
if n == 0 {
return
}
if n < 1 {
panic(negativeN)
}
}
if alpha == 0 {
if incX == 1 {
x = x[:n]
for i := range x {
x[i] = 0
}
}
for ix := 0; ix < n*incX; ix += incX {
x[ix] = 0
}
}
if incX == 1 {
x = x[:n]
for i := range x {
x[i] *= alpha
}
return
}
for ix := 0; ix < n*incX; ix += incX {
x[ix] *= alpha
}
return
}
+50
View File
@@ -0,0 +1,50 @@
// Generated code do not edit. Run `go generate`.
// 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 native
import (
"github.com/gonum/internal/asm"
)
// Dsdot computes the dot product of the two vectors
// \sum_i x[i]*y[i]
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Dsdot(n int, x []float32, incX int, y []float32, incY int) float64 {
if n < 0 {
panic(negativeN)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if incX == 1 && incY == 1 {
if len(x) < n {
panic(badLenX)
}
if len(y) < n {
panic(badLenY)
}
return asm.DsdotUnitary(x[:n], y)
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
if ix >= len(x) || ix+(n-1)*incX >= len(x) {
panic(badLenX)
}
if iy >= len(y) || iy+(n-1)*incY >= len(y) {
panic(badLenY)
}
return asm.DsdotInc(x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy))
}
+50
View File
@@ -0,0 +1,50 @@
// Generated code do not edit. Run `go generate`.
// 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 native
import (
"github.com/gonum/internal/asm"
)
// Sdot computes the dot product of the two vectors
// \sum_i x[i]*y[i]
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Sdot(n int, x []float32, incX int, y []float32, incY int) float32 {
if n < 0 {
panic(negativeN)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if incX == 1 && incY == 1 {
if len(x) < n {
panic(badLenX)
}
if len(y) < n {
panic(badLenY)
}
return asm.SdotUnitary(x[:n], y)
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
if ix >= len(x) || ix+(n-1)*incX >= len(x) {
panic(badLenX)
}
if iy >= len(y) || iy+(n-1)*incY >= len(y) {
panic(badLenY)
}
return asm.SdotInc(x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy))
}
+50
View File
@@ -0,0 +1,50 @@
// Generated code do not edit. Run `go generate`.
// 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 native
import (
"github.com/gonum/internal/asm"
)
// Sdsdot computes the dot product of the two vectors plus a constant
// alpha + \sum_i x[i]*y[i]
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Sdsdot(n int, alpha float32, x []float32, incX int, y []float32, incY int) float32 {
if n < 0 {
panic(negativeN)
}
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if incX == 1 && incY == 1 {
if len(x) < n {
panic(badLenX)
}
if len(y) < n {
panic(badLenY)
}
return alpha + float32(asm.DsdotUnitary(x[:n], y))
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
if ix >= len(x) || ix+(n-1)*incX >= len(x) {
panic(badLenX)
}
if iy >= len(y) || iy+(n-1)*incY >= len(y) {
panic(badLenY)
}
return alpha + float32(asm.DsdotInc(x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy)))
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+831
View File
@@ -0,0 +1,831 @@
// Copyright ©2014 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 native
import (
"github.com/gonum/blas"
"github.com/gonum/internal/asm"
)
var _ blas.Float64Level3 = Implementation{}
// Dtrsm solves
// A * X = alpha * B if tA == blas.NoTrans and side == blas.Left
// A^T * X = alpha * B if tA == blas.Trans or blas.ConjTrans, and side == blas.Left
// X * A = alpha * B if tA == blas.NoTrans and side == blas.Right
// X * A^T = alpha * B if tA == blas.Trans or blas.ConjTrans, and side == blas.Right
// where A is an n×n triangular matrix, x is an m×n matrix, and alpha is a
// scalar.
//
// At entry to the function, X contains the values of B, and the result is
// stored in place into X.
//
// No check is made that A is invertible.
func (Implementation) Dtrsm(s blas.Side, ul blas.Uplo, tA blas.Transpose, d blas.Diag, m, n int, alpha float64, a []float64, lda int, b []float64, ldb int) {
if s != blas.Left && s != blas.Right {
panic(badSide)
}
if ul != blas.Lower && ul != blas.Upper {
panic(badUplo)
}
if tA != blas.NoTrans && tA != blas.Trans && tA != blas.ConjTrans {
panic(badTranspose)
}
if d != blas.NonUnit && d != blas.Unit {
panic(badDiag)
}
if m < 0 {
panic(mLT0)
}
if n < 0 {
panic(nLT0)
}
if ldb < n {
panic(badLdB)
}
var k int
if s == blas.Left {
k = m
} else {
k = n
}
if lda*(k-1)+k > len(a) || lda < max(1, k) {
panic(badLdA)
}
if ldb*(m-1)+n > len(b) || ldb < max(1, n) {
panic(badLdB)
}
if m == 0 || n == 0 {
return
}
if alpha == 0 {
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
for j := range btmp {
btmp[j] = 0
}
}
return
}
nonUnit := d == blas.NonUnit
if s == blas.Left {
if tA == blas.NoTrans {
if ul == blas.Upper {
for i := m - 1; i >= 0; i-- {
btmp := b[i*ldb : i*ldb+n]
if alpha != 1 {
for j := range btmp {
btmp[j] *= alpha
}
}
for ka, va := range a[i*lda+i+1 : i*lda+m] {
k := ka + i + 1
if va != 0 {
asm.DaxpyUnitary(-va, b[k*ldb:k*ldb+n], btmp, btmp)
}
}
if nonUnit {
tmp := 1 / a[i*lda+i]
for j := 0; j < n; j++ {
btmp[j] *= tmp
}
}
}
return
}
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
if alpha != 1 {
for j := 0; j < n; j++ {
btmp[j] *= alpha
}
}
for k, va := range a[i*lda : i*lda+i] {
if va != 0 {
asm.DaxpyUnitary(-va, b[k*ldb:k*ldb+n], btmp, btmp)
}
}
if nonUnit {
tmp := 1 / a[i*lda+i]
for j := 0; j < n; j++ {
btmp[j] *= tmp
}
}
}
return
}
// Cases where a is transposed
if ul == blas.Upper {
for k := 0; k < m; k++ {
btmpk := b[k*ldb : k*ldb+n]
if nonUnit {
tmp := 1 / a[k*lda+k]
for j := 0; j < n; j++ {
btmpk[j] *= tmp
}
}
for ia, va := range a[k*lda+k+1 : k*lda+m] {
i := ia + k + 1
if va != 0 {
btmp := b[i*ldb : i*ldb+n]
asm.DaxpyUnitary(-va, btmpk, btmp, btmp)
}
}
if alpha != 1 {
for j := 0; j < n; j++ {
btmpk[j] *= alpha
}
}
}
return
}
for k := m - 1; k >= 0; k-- {
btmpk := b[k*ldb : k*ldb+n]
if nonUnit {
tmp := 1 / a[k*lda+k]
for j := 0; j < n; j++ {
btmpk[j] *= tmp
}
}
for i, va := range a[k*lda : k*lda+k] {
if va != 0 {
btmp := b[i*ldb : i*ldb+n]
asm.DaxpyUnitary(-va, btmpk, btmp, btmp)
}
}
if alpha != 1 {
for j := 0; j < n; j++ {
btmpk[j] *= alpha
}
}
}
return
}
// Cases where a is to the right of X.
if tA == blas.NoTrans {
if ul == blas.Upper {
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
if alpha != 1 {
for j := 0; j < n; j++ {
btmp[j] *= alpha
}
}
for k, vb := range btmp {
if vb != 0 {
if btmp[k] != 0 {
if nonUnit {
btmp[k] /= a[k*lda+k]
}
btmpk := btmp[k+1 : n]
asm.DaxpyUnitary(-btmp[k], a[k*lda+k+1:k*lda+n], btmpk, btmpk)
}
}
}
}
return
}
for i := 0; i < m; i++ {
btmp := b[i*lda : i*lda+n]
if alpha != 1 {
for j := 0; j < n; j++ {
btmp[j] *= alpha
}
}
for k := n - 1; k >= 0; k-- {
if btmp[k] != 0 {
if nonUnit {
btmp[k] /= a[k*lda+k]
}
asm.DaxpyUnitary(-btmp[k], a[k*lda:k*lda+k], btmp, btmp)
}
}
}
return
}
// Cases where a is transposed.
if ul == blas.Upper {
for i := 0; i < m; i++ {
btmp := b[i*lda : i*lda+n]
for j := n - 1; j >= 0; j-- {
tmp := alpha*btmp[j] - asm.DdotUnitary(a[j*lda+j+1:j*lda+n], btmp[j+1:])
if nonUnit {
tmp /= a[j*lda+j]
}
btmp[j] = tmp
}
}
return
}
for i := 0; i < m; i++ {
btmp := b[i*lda : i*lda+n]
for j := 0; j < n; j++ {
tmp := alpha*btmp[j] - asm.DdotUnitary(a[j*lda:j*lda+j], btmp)
if nonUnit {
tmp /= a[j*lda+j]
}
btmp[j] = tmp
}
}
}
// Dsymm performs one of
// C = alpha * A * B + beta * C if side == blas.Left
// C = alpha * B * A + beta * C if side == blas.Right
// where A is an n×n symmetric matrix, B and C are m×n matrices, and alpha
// is a scalar.
func (Implementation) Dsymm(s blas.Side, ul blas.Uplo, m, n int, alpha float64, a []float64, lda int, b []float64, ldb int, beta float64, c []float64, ldc int) {
if s != blas.Right && s != blas.Left {
panic("goblas: bad side")
}
if ul != blas.Lower && ul != blas.Upper {
panic(badUplo)
}
if m < 0 {
panic(mLT0)
}
if n < 0 {
panic(nLT0)
}
var k int
if s == blas.Left {
k = m
} else {
k = n
}
if lda*(k-1)+k > len(a) || lda < max(1, k) {
panic(badLdA)
}
if ldb*(m-1)+n > len(b) || ldb < max(1, n) {
panic(badLdB)
}
if ldc*(m-1)+n > len(c) || ldc < max(1, n) {
panic(badLdC)
}
if m == 0 || n == 0 {
return
}
if alpha == 0 && beta == 1 {
return
}
if alpha == 0 {
if beta == 0 {
for i := 0; i < m; i++ {
ctmp := c[i*ldc : i*ldc+n]
for j := range ctmp {
ctmp[j] = 0
}
}
return
}
for i := 0; i < m; i++ {
ctmp := c[i*ldc : i*ldc+n]
for j := 0; j < n; j++ {
ctmp[j] *= beta
}
}
return
}
isUpper := ul == blas.Upper
if s == blas.Left {
for i := 0; i < m; i++ {
atmp := alpha * a[i*lda+i]
btmp := b[i*ldb : i*ldb+n]
ctmp := c[i*ldc : i*ldc+n]
for j, v := range btmp {
ctmp[j] *= beta
ctmp[j] += atmp * v
}
for k := 0; k < i; k++ {
var atmp float64
if isUpper {
atmp = a[k*lda+i]
} else {
atmp = a[i*lda+k]
}
atmp *= alpha
ctmp := c[i*ldc : i*ldc+n]
asm.DaxpyUnitary(atmp, b[k*ldb:k*ldb+n], ctmp, ctmp)
}
for k := i + 1; k < m; k++ {
var atmp float64
if isUpper {
atmp = a[i*lda+k]
} else {
atmp = a[k*lda+i]
}
atmp *= alpha
ctmp := c[i*ldc : i*ldc+n]
asm.DaxpyUnitary(atmp, b[k*ldb:k*ldb+n], ctmp, ctmp)
}
}
return
}
if isUpper {
for i := 0; i < m; i++ {
for j := n - 1; j >= 0; j-- {
tmp := alpha * b[i*ldb+j]
var tmp2 float64
atmp := a[j*lda+j+1 : j*lda+n]
btmp := b[i*ldb+j+1 : i*ldb+n]
ctmp := c[i*ldc+j+1 : i*ldc+n]
for k, v := range atmp {
ctmp[k] += tmp * v
tmp2 += btmp[k] * v
}
c[i*ldc+j] *= beta
c[i*ldc+j] += tmp*a[j*lda+j] + alpha*tmp2
}
}
return
}
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
tmp := alpha * b[i*ldb+j]
var tmp2 float64
atmp := a[j*lda : j*lda+j]
btmp := b[i*ldb : i*ldb+j]
ctmp := c[i*ldc : i*ldc+j]
for k, v := range atmp {
ctmp[k] += tmp * v
tmp2 += btmp[k] * v
}
c[i*ldc+j] *= beta
c[i*ldc+j] += tmp*a[j*lda+j] + alpha*tmp2
}
}
}
// Dsyrk performs the symmetric rank-k operation
// C = alpha * A * A^T + beta*C
// C is an n×n symmetric matrix. A is an n×k matrix if tA == blas.NoTrans, and
// a k×n matrix otherwise. alpha and beta are scalars.
func (Implementation) Dsyrk(ul blas.Uplo, tA blas.Transpose, n, k int, alpha float64, a []float64, lda int, beta float64, c []float64, ldc int) {
if ul != blas.Lower && ul != blas.Upper {
panic(badUplo)
}
if tA != blas.Trans && tA != blas.NoTrans && tA != blas.ConjTrans {
panic(badTranspose)
}
if n < 0 {
panic(nLT0)
}
if k < 0 {
panic(kLT0)
}
if ldc < n {
panic(badLdC)
}
var row, col int
if tA == blas.NoTrans {
row, col = n, k
} else {
row, col = k, n
}
if lda*(row-1)+col > len(a) || lda < max(1, col) {
panic(badLdA)
}
if ldc*(n-1)+n > len(c) || ldc < max(1, n) {
panic(badLdC)
}
if alpha == 0 {
if beta == 0 {
if ul == blas.Upper {
for i := 0; i < n; i++ {
ctmp := c[i*ldc+i : i*ldc+n]
for j := range ctmp {
ctmp[j] = 0
}
}
return
}
for i := 0; i < n; i++ {
ctmp := c[i*ldc : i*ldc+i+1]
for j := range ctmp {
ctmp[j] = 0
}
}
return
}
if ul == blas.Upper {
for i := 0; i < n; i++ {
ctmp := c[i*ldc+i : i*ldc+n]
for j := range ctmp {
ctmp[j] *= beta
}
}
return
}
for i := 0; i < n; i++ {
ctmp := c[i*ldc : i*ldc+i+1]
for j := range ctmp {
ctmp[j] *= beta
}
}
return
}
if tA == blas.NoTrans {
if ul == blas.Upper {
for i := 0; i < n; i++ {
ctmp := c[i*ldc+i : i*ldc+n]
atmp := a[i*lda : i*lda+k]
for jc, vc := range ctmp {
j := jc + i
ctmp[jc] = vc*beta + alpha*asm.DdotUnitary(atmp, a[j*lda:j*lda+k])
}
}
return
}
for i := 0; i < n; i++ {
atmp := a[i*lda : i*lda+k]
for j, vc := range c[i*ldc : i*ldc+i+1] {
c[i*ldc+j] = vc*beta + alpha*asm.DdotUnitary(a[j*lda:j*lda+k], atmp)
}
}
return
}
// Cases where a is transposed.
if ul == blas.Upper {
for i := 0; i < n; i++ {
ctmp := c[i*ldc+i : i*ldc+n]
if beta != 1 {
for j := range ctmp {
ctmp[j] *= beta
}
}
for l := 0; l < k; l++ {
tmp := alpha * a[l*lda+i]
if tmp != 0 {
asm.DaxpyUnitary(tmp, a[l*lda+i:l*lda+n], ctmp, ctmp)
}
}
}
return
}
for i := 0; i < n; i++ {
ctmp := c[i*ldc : i*ldc+i+1]
if beta != 0 {
for j := range ctmp {
ctmp[j] *= beta
}
}
for l := 0; l < k; l++ {
tmp := alpha * a[l*lda+i]
if tmp != 0 {
asm.DaxpyUnitary(tmp, a[l*lda:l*lda+i+1], ctmp, ctmp)
}
}
}
}
// Dsyr2k performs the symmetric rank 2k operation
// C = alpha * A * B^T + alpha * B * A^T + beta * C
// where C is an n×n symmetric matrix. A and B are n×k matrices if
// tA == NoTrans and k×n otherwise. alpha and beta are scalars.
func (Implementation) Dsyr2k(ul blas.Uplo, tA blas.Transpose, n, k int, alpha float64, a []float64, lda int, b []float64, ldb int, beta float64, c []float64, ldc int) {
if ul != blas.Lower && ul != blas.Upper {
panic(badUplo)
}
if tA != blas.Trans && tA != blas.NoTrans && tA != blas.ConjTrans {
panic(badTranspose)
}
if n < 0 {
panic(nLT0)
}
if k < 0 {
panic(kLT0)
}
if ldc < n {
panic(badLdC)
}
var row, col int
if tA == blas.NoTrans {
row, col = n, k
} else {
row, col = k, n
}
if lda*(row-1)+col > len(a) || lda < max(1, col) {
panic(badLdA)
}
if ldb*(row-1)+col > len(b) || ldb < max(1, col) {
panic(badLdB)
}
if ldc*(n-1)+n > len(c) || ldc < max(1, n) {
panic(badLdC)
}
if alpha == 0 {
if beta == 0 {
if ul == blas.Upper {
for i := 0; i < n; i++ {
ctmp := c[i*ldc+i : i*ldc+n]
for j := range ctmp {
ctmp[j] = 0
}
}
return
}
for i := 0; i < n; i++ {
ctmp := c[i*ldc : i*ldc+i+1]
for j := range ctmp {
ctmp[j] = 0
}
}
return
}
if ul == blas.Upper {
for i := 0; i < n; i++ {
ctmp := c[i*ldc+i : i*ldc+n]
for j := range ctmp {
ctmp[j] *= beta
}
}
return
}
for i := 0; i < n; i++ {
ctmp := c[i*ldc : i*ldc+i+1]
for j := range ctmp {
ctmp[j] *= beta
}
}
return
}
if tA == blas.NoTrans {
if ul == blas.Upper {
for i := 0; i < n; i++ {
atmp := a[i*lda : i*lda+k]
btmp := b[i*lda : i*lda+k]
ctmp := c[i*ldc+i : i*ldc+n]
for jc := range ctmp {
j := i + jc
var tmp1, tmp2 float64
binner := b[j*ldb : j*ldb+k]
for l, v := range a[j*lda : j*lda+k] {
tmp1 += v * btmp[l]
tmp2 += atmp[l] * binner[l]
}
ctmp[jc] *= beta
ctmp[jc] += alpha * (tmp1 + tmp2)
}
}
return
}
for i := 0; i < n; i++ {
atmp := a[i*lda : i*lda+k]
btmp := b[i*lda : i*lda+k]
ctmp := c[i*ldc : i*ldc+i+1]
for j := 0; j <= i; j++ {
var tmp1, tmp2 float64
binner := b[j*ldb : j*ldb+k]
for l, v := range a[j*lda : j*lda+k] {
tmp1 += v * btmp[l]
tmp2 += atmp[l] * binner[l]
}
ctmp[j] *= beta
ctmp[j] += alpha * (tmp1 + tmp2)
}
}
return
}
if ul == blas.Upper {
for i := 0; i < n; i++ {
ctmp := c[i*ldc+i : i*ldc+n]
if beta != 1 {
for j := range ctmp {
ctmp[j] *= beta
}
}
for l := 0; l < k; l++ {
tmp1 := alpha * b[l*lda+i]
tmp2 := alpha * a[l*lda+i]
btmp := b[l*ldb+i : l*ldb+n]
if tmp1 != 0 || tmp2 != 0 {
for j, v := range a[l*lda+i : l*lda+n] {
ctmp[j] += v*tmp1 + btmp[j]*tmp2
}
}
}
}
return
}
for i := 0; i < n; i++ {
ctmp := c[i*ldc : i*ldc+i+1]
if beta != 1 {
for j := range ctmp {
ctmp[j] *= beta
}
}
for l := 0; l < k; l++ {
tmp1 := alpha * b[l*lda+i]
tmp2 := alpha * a[l*lda+i]
btmp := b[l*ldb : l*ldb+i+1]
if tmp1 != 0 || tmp2 != 0 {
for j, v := range a[l*lda : l*lda+i+1] {
ctmp[j] += v*tmp1 + btmp[j]*tmp2
}
}
}
}
}
// Dtrmm performs
// B = alpha * A * B if tA == blas.NoTrans and side == blas.Left
// B = alpha * A^T * B if tA == blas.Trans or blas.ConjTrans, and side == blas.Left
// B = alpha * B * A if tA == blas.NoTrans and side == blas.Right
// B = alpha * B * A^T if tA == blas.Trans or blas.ConjTrans, and side == blas.Right
// where A is an n×n triangular matrix, and B is an m×n matrix.
func (Implementation) Dtrmm(s blas.Side, ul blas.Uplo, tA blas.Transpose, d blas.Diag, m, n int, alpha float64, a []float64, lda int, b []float64, ldb int) {
if s != blas.Left && s != blas.Right {
panic(badSide)
}
if ul != blas.Lower && ul != blas.Upper {
panic(badUplo)
}
if tA != blas.NoTrans && tA != blas.Trans && tA != blas.ConjTrans {
panic(badTranspose)
}
if d != blas.NonUnit && d != blas.Unit {
panic(badDiag)
}
if m < 0 {
panic(mLT0)
}
if n < 0 {
panic(nLT0)
}
var k int
if s == blas.Left {
k = m
} else {
k = n
}
if lda*(k-1)+k > len(a) || lda < max(1, k) {
panic(badLdA)
}
if ldb*(m-1)+n > len(b) || ldb < max(1, n) {
panic(badLdB)
}
if alpha == 0 {
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
for j := range btmp {
btmp[j] = 0
}
}
return
}
nonUnit := d == blas.NonUnit
if s == blas.Left {
if tA == blas.NoTrans {
if ul == blas.Upper {
for i := 0; i < m; i++ {
tmp := alpha
if nonUnit {
tmp *= a[i*lda+i]
}
btmp := b[i*ldb : i*ldb+n]
for j := range btmp {
btmp[j] *= tmp
}
for ka, va := range a[i*lda+i+1 : i*lda+m] {
k := ka + i + 1
tmp := alpha * va
if tmp != 0 {
asm.DaxpyUnitary(tmp, b[k*ldb:k*ldb+n], btmp, btmp)
}
}
}
return
}
for i := m - 1; i >= 0; i-- {
tmp := alpha
if nonUnit {
tmp *= a[i*lda+i]
}
btmp := b[i*ldb : i*ldb+n]
for j := range btmp {
btmp[j] *= tmp
}
for k, va := range a[i*lda : i*lda+i] {
tmp := alpha * va
if tmp != 0 {
asm.DaxpyUnitary(tmp, b[k*ldb:k*ldb+n], btmp, btmp)
}
}
}
return
}
// Cases where a is transposed.
if ul == blas.Upper {
for k := m - 1; k >= 0; k-- {
btmpk := b[k*ldb : k*ldb+n]
for ia, va := range a[k*lda+k+1 : k*lda+m] {
i := ia + k + 1
btmp := b[i*ldb : i*ldb+n]
tmp := alpha * va
if tmp != 0 {
asm.DaxpyUnitary(tmp, btmpk, btmp, btmp)
}
}
tmp := alpha
if nonUnit {
tmp *= a[k*lda+k]
}
if tmp != 1 {
for j := 0; j < n; j++ {
btmpk[j] *= tmp
}
}
}
return
}
for k := 0; k < m; k++ {
btmpk := b[k*ldb : k*ldb+n]
for i, va := range a[k*lda : k*lda+k] {
btmp := b[i*ldb : i*ldb+n]
tmp := alpha * va
if tmp != 0 {
asm.DaxpyUnitary(tmp, btmpk, btmp, btmp)
}
}
tmp := alpha
if nonUnit {
tmp *= a[k*lda+k]
}
if tmp != 1 {
for j := 0; j < n; j++ {
btmpk[j] *= tmp
}
}
}
return
}
// Cases where a is on the right
if tA == blas.NoTrans {
if ul == blas.Upper {
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
for k := n - 1; k >= 0; k-- {
tmp := alpha * btmp[k]
if tmp != 0 {
btmp[k] = tmp
if nonUnit {
btmp[k] *= a[k*lda+k]
}
for ja, v := range a[k*lda+k+1 : k*lda+n] {
j := ja + k + 1
btmp[j] += tmp * v
}
}
}
}
return
}
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
for k := 0; k < n; k++ {
tmp := alpha * btmp[k]
if tmp != 0 {
btmp[k] = tmp
if nonUnit {
btmp[k] *= a[k*lda+k]
}
asm.DaxpyUnitary(tmp, a[k*lda:k*lda+k], btmp, btmp)
}
}
}
return
}
// Cases where a is transposed.
if ul == blas.Upper {
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
for j, vb := range btmp {
tmp := vb
if nonUnit {
tmp *= a[j*lda+j]
}
tmp += asm.DdotUnitary(a[j*lda+j+1:j*lda+n], btmp[j+1:n])
btmp[j] = alpha * tmp
}
}
return
}
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
for j := n - 1; j >= 0; j-- {
tmp := btmp[j]
if nonUnit {
tmp *= a[j*lda+j]
}
tmp += asm.DdotUnitary(a[j*lda:j*lda+j], btmp[:j])
btmp[j] = alpha * tmp
}
}
}
+843
View File
@@ -0,0 +1,843 @@
// Generated code do not edit. Run `go generate`.
// Copyright ©2014 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 native
import (
"github.com/gonum/blas"
"github.com/gonum/internal/asm"
)
var _ blas.Float32Level3 = Implementation{}
// Strsm solves
// A * X = alpha * B if tA == blas.NoTrans and side == blas.Left
// A^T * X = alpha * B if tA == blas.Trans or blas.ConjTrans, and side == blas.Left
// X * A = alpha * B if tA == blas.NoTrans and side == blas.Right
// X * A^T = alpha * B if tA == blas.Trans or blas.ConjTrans, and side == blas.Right
// where A is an n×n triangular matrix, x is an m×n matrix, and alpha is a
// scalar.
//
// At entry to the function, X contains the values of B, and the result is
// stored in place into X.
//
// No check is made that A is invertible.
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Strsm(s blas.Side, ul blas.Uplo, tA blas.Transpose, d blas.Diag, m, n int, alpha float32, a []float32, lda int, b []float32, ldb int) {
if s != blas.Left && s != blas.Right {
panic(badSide)
}
if ul != blas.Lower && ul != blas.Upper {
panic(badUplo)
}
if tA != blas.NoTrans && tA != blas.Trans && tA != blas.ConjTrans {
panic(badTranspose)
}
if d != blas.NonUnit && d != blas.Unit {
panic(badDiag)
}
if m < 0 {
panic(mLT0)
}
if n < 0 {
panic(nLT0)
}
if ldb < n {
panic(badLdB)
}
var k int
if s == blas.Left {
k = m
} else {
k = n
}
if lda*(k-1)+k > len(a) || lda < max(1, k) {
panic(badLdA)
}
if ldb*(m-1)+n > len(b) || ldb < max(1, n) {
panic(badLdB)
}
if m == 0 || n == 0 {
return
}
if alpha == 0 {
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
for j := range btmp {
btmp[j] = 0
}
}
return
}
nonUnit := d == blas.NonUnit
if s == blas.Left {
if tA == blas.NoTrans {
if ul == blas.Upper {
for i := m - 1; i >= 0; i-- {
btmp := b[i*ldb : i*ldb+n]
if alpha != 1 {
for j := range btmp {
btmp[j] *= alpha
}
}
for ka, va := range a[i*lda+i+1 : i*lda+m] {
k := ka + i + 1
if va != 0 {
asm.SaxpyUnitary(-va, b[k*ldb:k*ldb+n], btmp, btmp)
}
}
if nonUnit {
tmp := 1 / a[i*lda+i]
for j := 0; j < n; j++ {
btmp[j] *= tmp
}
}
}
return
}
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
if alpha != 1 {
for j := 0; j < n; j++ {
btmp[j] *= alpha
}
}
for k, va := range a[i*lda : i*lda+i] {
if va != 0 {
asm.SaxpyUnitary(-va, b[k*ldb:k*ldb+n], btmp, btmp)
}
}
if nonUnit {
tmp := 1 / a[i*lda+i]
for j := 0; j < n; j++ {
btmp[j] *= tmp
}
}
}
return
}
// Cases where a is transposed
if ul == blas.Upper {
for k := 0; k < m; k++ {
btmpk := b[k*ldb : k*ldb+n]
if nonUnit {
tmp := 1 / a[k*lda+k]
for j := 0; j < n; j++ {
btmpk[j] *= tmp
}
}
for ia, va := range a[k*lda+k+1 : k*lda+m] {
i := ia + k + 1
if va != 0 {
btmp := b[i*ldb : i*ldb+n]
asm.SaxpyUnitary(-va, btmpk, btmp, btmp)
}
}
if alpha != 1 {
for j := 0; j < n; j++ {
btmpk[j] *= alpha
}
}
}
return
}
for k := m - 1; k >= 0; k-- {
btmpk := b[k*ldb : k*ldb+n]
if nonUnit {
tmp := 1 / a[k*lda+k]
for j := 0; j < n; j++ {
btmpk[j] *= tmp
}
}
for i, va := range a[k*lda : k*lda+k] {
if va != 0 {
btmp := b[i*ldb : i*ldb+n]
asm.SaxpyUnitary(-va, btmpk, btmp, btmp)
}
}
if alpha != 1 {
for j := 0; j < n; j++ {
btmpk[j] *= alpha
}
}
}
return
}
// Cases where a is to the right of X.
if tA == blas.NoTrans {
if ul == blas.Upper {
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
if alpha != 1 {
for j := 0; j < n; j++ {
btmp[j] *= alpha
}
}
for k, vb := range btmp {
if vb != 0 {
if btmp[k] != 0 {
if nonUnit {
btmp[k] /= a[k*lda+k]
}
btmpk := btmp[k+1 : n]
asm.SaxpyUnitary(-btmp[k], a[k*lda+k+1:k*lda+n], btmpk, btmpk)
}
}
}
}
return
}
for i := 0; i < m; i++ {
btmp := b[i*lda : i*lda+n]
if alpha != 1 {
for j := 0; j < n; j++ {
btmp[j] *= alpha
}
}
for k := n - 1; k >= 0; k-- {
if btmp[k] != 0 {
if nonUnit {
btmp[k] /= a[k*lda+k]
}
asm.SaxpyUnitary(-btmp[k], a[k*lda:k*lda+k], btmp, btmp)
}
}
}
return
}
// Cases where a is transposed.
if ul == blas.Upper {
for i := 0; i < m; i++ {
btmp := b[i*lda : i*lda+n]
for j := n - 1; j >= 0; j-- {
tmp := alpha*btmp[j] - asm.SdotUnitary(a[j*lda+j+1:j*lda+n], btmp[j+1:])
if nonUnit {
tmp /= a[j*lda+j]
}
btmp[j] = tmp
}
}
return
}
for i := 0; i < m; i++ {
btmp := b[i*lda : i*lda+n]
for j := 0; j < n; j++ {
tmp := alpha*btmp[j] - asm.SdotUnitary(a[j*lda:j*lda+j], btmp)
if nonUnit {
tmp /= a[j*lda+j]
}
btmp[j] = tmp
}
}
}
// Ssymm performs one of
// C = alpha * A * B + beta * C if side == blas.Left
// C = alpha * B * A + beta * C if side == blas.Right
// where A is an n×n symmetric matrix, B and C are m×n matrices, and alpha
// is a scalar.
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Ssymm(s blas.Side, ul blas.Uplo, m, n int, alpha float32, a []float32, lda int, b []float32, ldb int, beta float32, c []float32, ldc int) {
if s != blas.Right && s != blas.Left {
panic("goblas: bad side")
}
if ul != blas.Lower && ul != blas.Upper {
panic(badUplo)
}
if m < 0 {
panic(mLT0)
}
if n < 0 {
panic(nLT0)
}
var k int
if s == blas.Left {
k = m
} else {
k = n
}
if lda*(k-1)+k > len(a) || lda < max(1, k) {
panic(badLdA)
}
if ldb*(m-1)+n > len(b) || ldb < max(1, n) {
panic(badLdB)
}
if ldc*(m-1)+n > len(c) || ldc < max(1, n) {
panic(badLdC)
}
if m == 0 || n == 0 {
return
}
if alpha == 0 && beta == 1 {
return
}
if alpha == 0 {
if beta == 0 {
for i := 0; i < m; i++ {
ctmp := c[i*ldc : i*ldc+n]
for j := range ctmp {
ctmp[j] = 0
}
}
return
}
for i := 0; i < m; i++ {
ctmp := c[i*ldc : i*ldc+n]
for j := 0; j < n; j++ {
ctmp[j] *= beta
}
}
return
}
isUpper := ul == blas.Upper
if s == blas.Left {
for i := 0; i < m; i++ {
atmp := alpha * a[i*lda+i]
btmp := b[i*ldb : i*ldb+n]
ctmp := c[i*ldc : i*ldc+n]
for j, v := range btmp {
ctmp[j] *= beta
ctmp[j] += atmp * v
}
for k := 0; k < i; k++ {
var atmp float32
if isUpper {
atmp = a[k*lda+i]
} else {
atmp = a[i*lda+k]
}
atmp *= alpha
ctmp := c[i*ldc : i*ldc+n]
asm.SaxpyUnitary(atmp, b[k*ldb:k*ldb+n], ctmp, ctmp)
}
for k := i + 1; k < m; k++ {
var atmp float32
if isUpper {
atmp = a[i*lda+k]
} else {
atmp = a[k*lda+i]
}
atmp *= alpha
ctmp := c[i*ldc : i*ldc+n]
asm.SaxpyUnitary(atmp, b[k*ldb:k*ldb+n], ctmp, ctmp)
}
}
return
}
if isUpper {
for i := 0; i < m; i++ {
for j := n - 1; j >= 0; j-- {
tmp := alpha * b[i*ldb+j]
var tmp2 float32
atmp := a[j*lda+j+1 : j*lda+n]
btmp := b[i*ldb+j+1 : i*ldb+n]
ctmp := c[i*ldc+j+1 : i*ldc+n]
for k, v := range atmp {
ctmp[k] += tmp * v
tmp2 += btmp[k] * v
}
c[i*ldc+j] *= beta
c[i*ldc+j] += tmp*a[j*lda+j] + alpha*tmp2
}
}
return
}
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
tmp := alpha * b[i*ldb+j]
var tmp2 float32
atmp := a[j*lda : j*lda+j]
btmp := b[i*ldb : i*ldb+j]
ctmp := c[i*ldc : i*ldc+j]
for k, v := range atmp {
ctmp[k] += tmp * v
tmp2 += btmp[k] * v
}
c[i*ldc+j] *= beta
c[i*ldc+j] += tmp*a[j*lda+j] + alpha*tmp2
}
}
}
// Ssyrk performs the symmetric rank-k operation
// C = alpha * A * A^T + beta*C
// C is an n×n symmetric matrix. A is an n×k matrix if tA == blas.NoTrans, and
// a k×n matrix otherwise. alpha and beta are scalars.
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Ssyrk(ul blas.Uplo, tA blas.Transpose, n, k int, alpha float32, a []float32, lda int, beta float32, c []float32, ldc int) {
if ul != blas.Lower && ul != blas.Upper {
panic(badUplo)
}
if tA != blas.Trans && tA != blas.NoTrans && tA != blas.ConjTrans {
panic(badTranspose)
}
if n < 0 {
panic(nLT0)
}
if k < 0 {
panic(kLT0)
}
if ldc < n {
panic(badLdC)
}
var row, col int
if tA == blas.NoTrans {
row, col = n, k
} else {
row, col = k, n
}
if lda*(row-1)+col > len(a) || lda < max(1, col) {
panic(badLdA)
}
if ldc*(n-1)+n > len(c) || ldc < max(1, n) {
panic(badLdC)
}
if alpha == 0 {
if beta == 0 {
if ul == blas.Upper {
for i := 0; i < n; i++ {
ctmp := c[i*ldc+i : i*ldc+n]
for j := range ctmp {
ctmp[j] = 0
}
}
return
}
for i := 0; i < n; i++ {
ctmp := c[i*ldc : i*ldc+i+1]
for j := range ctmp {
ctmp[j] = 0
}
}
return
}
if ul == blas.Upper {
for i := 0; i < n; i++ {
ctmp := c[i*ldc+i : i*ldc+n]
for j := range ctmp {
ctmp[j] *= beta
}
}
return
}
for i := 0; i < n; i++ {
ctmp := c[i*ldc : i*ldc+i+1]
for j := range ctmp {
ctmp[j] *= beta
}
}
return
}
if tA == blas.NoTrans {
if ul == blas.Upper {
for i := 0; i < n; i++ {
ctmp := c[i*ldc+i : i*ldc+n]
atmp := a[i*lda : i*lda+k]
for jc, vc := range ctmp {
j := jc + i
ctmp[jc] = vc*beta + alpha*asm.SdotUnitary(atmp, a[j*lda:j*lda+k])
}
}
return
}
for i := 0; i < n; i++ {
atmp := a[i*lda : i*lda+k]
for j, vc := range c[i*ldc : i*ldc+i+1] {
c[i*ldc+j] = vc*beta + alpha*asm.SdotUnitary(a[j*lda:j*lda+k], atmp)
}
}
return
}
// Cases where a is transposed.
if ul == blas.Upper {
for i := 0; i < n; i++ {
ctmp := c[i*ldc+i : i*ldc+n]
if beta != 1 {
for j := range ctmp {
ctmp[j] *= beta
}
}
for l := 0; l < k; l++ {
tmp := alpha * a[l*lda+i]
if tmp != 0 {
asm.SaxpyUnitary(tmp, a[l*lda+i:l*lda+n], ctmp, ctmp)
}
}
}
return
}
for i := 0; i < n; i++ {
ctmp := c[i*ldc : i*ldc+i+1]
if beta != 0 {
for j := range ctmp {
ctmp[j] *= beta
}
}
for l := 0; l < k; l++ {
tmp := alpha * a[l*lda+i]
if tmp != 0 {
asm.SaxpyUnitary(tmp, a[l*lda:l*lda+i+1], ctmp, ctmp)
}
}
}
}
// Ssyr2k performs the symmetric rank 2k operation
// C = alpha * A * B^T + alpha * B * A^T + beta * C
// where C is an n×n symmetric matrix. A and B are n×k matrices if
// tA == NoTrans and k×n otherwise. alpha and beta are scalars.
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Ssyr2k(ul blas.Uplo, tA blas.Transpose, n, k int, alpha float32, a []float32, lda int, b []float32, ldb int, beta float32, c []float32, ldc int) {
if ul != blas.Lower && ul != blas.Upper {
panic(badUplo)
}
if tA != blas.Trans && tA != blas.NoTrans && tA != blas.ConjTrans {
panic(badTranspose)
}
if n < 0 {
panic(nLT0)
}
if k < 0 {
panic(kLT0)
}
if ldc < n {
panic(badLdC)
}
var row, col int
if tA == blas.NoTrans {
row, col = n, k
} else {
row, col = k, n
}
if lda*(row-1)+col > len(a) || lda < max(1, col) {
panic(badLdA)
}
if ldb*(row-1)+col > len(b) || ldb < max(1, col) {
panic(badLdB)
}
if ldc*(n-1)+n > len(c) || ldc < max(1, n) {
panic(badLdC)
}
if alpha == 0 {
if beta == 0 {
if ul == blas.Upper {
for i := 0; i < n; i++ {
ctmp := c[i*ldc+i : i*ldc+n]
for j := range ctmp {
ctmp[j] = 0
}
}
return
}
for i := 0; i < n; i++ {
ctmp := c[i*ldc : i*ldc+i+1]
for j := range ctmp {
ctmp[j] = 0
}
}
return
}
if ul == blas.Upper {
for i := 0; i < n; i++ {
ctmp := c[i*ldc+i : i*ldc+n]
for j := range ctmp {
ctmp[j] *= beta
}
}
return
}
for i := 0; i < n; i++ {
ctmp := c[i*ldc : i*ldc+i+1]
for j := range ctmp {
ctmp[j] *= beta
}
}
return
}
if tA == blas.NoTrans {
if ul == blas.Upper {
for i := 0; i < n; i++ {
atmp := a[i*lda : i*lda+k]
btmp := b[i*lda : i*lda+k]
ctmp := c[i*ldc+i : i*ldc+n]
for jc := range ctmp {
j := i + jc
var tmp1, tmp2 float32
binner := b[j*ldb : j*ldb+k]
for l, v := range a[j*lda : j*lda+k] {
tmp1 += v * btmp[l]
tmp2 += atmp[l] * binner[l]
}
ctmp[jc] *= beta
ctmp[jc] += alpha * (tmp1 + tmp2)
}
}
return
}
for i := 0; i < n; i++ {
atmp := a[i*lda : i*lda+k]
btmp := b[i*lda : i*lda+k]
ctmp := c[i*ldc : i*ldc+i+1]
for j := 0; j <= i; j++ {
var tmp1, tmp2 float32
binner := b[j*ldb : j*ldb+k]
for l, v := range a[j*lda : j*lda+k] {
tmp1 += v * btmp[l]
tmp2 += atmp[l] * binner[l]
}
ctmp[j] *= beta
ctmp[j] += alpha * (tmp1 + tmp2)
}
}
return
}
if ul == blas.Upper {
for i := 0; i < n; i++ {
ctmp := c[i*ldc+i : i*ldc+n]
if beta != 1 {
for j := range ctmp {
ctmp[j] *= beta
}
}
for l := 0; l < k; l++ {
tmp1 := alpha * b[l*lda+i]
tmp2 := alpha * a[l*lda+i]
btmp := b[l*ldb+i : l*ldb+n]
if tmp1 != 0 || tmp2 != 0 {
for j, v := range a[l*lda+i : l*lda+n] {
ctmp[j] += v*tmp1 + btmp[j]*tmp2
}
}
}
}
return
}
for i := 0; i < n; i++ {
ctmp := c[i*ldc : i*ldc+i+1]
if beta != 1 {
for j := range ctmp {
ctmp[j] *= beta
}
}
for l := 0; l < k; l++ {
tmp1 := alpha * b[l*lda+i]
tmp2 := alpha * a[l*lda+i]
btmp := b[l*ldb : l*ldb+i+1]
if tmp1 != 0 || tmp2 != 0 {
for j, v := range a[l*lda : l*lda+i+1] {
ctmp[j] += v*tmp1 + btmp[j]*tmp2
}
}
}
}
}
// Strmm performs
// B = alpha * A * B if tA == blas.NoTrans and side == blas.Left
// B = alpha * A^T * B if tA == blas.Trans or blas.ConjTrans, and side == blas.Left
// B = alpha * B * A if tA == blas.NoTrans and side == blas.Right
// B = alpha * B * A^T if tA == blas.Trans or blas.ConjTrans, and side == blas.Right
// where A is an n×n triangular matrix, and B is an m×n matrix.
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Strmm(s blas.Side, ul blas.Uplo, tA blas.Transpose, d blas.Diag, m, n int, alpha float32, a []float32, lda int, b []float32, ldb int) {
if s != blas.Left && s != blas.Right {
panic(badSide)
}
if ul != blas.Lower && ul != blas.Upper {
panic(badUplo)
}
if tA != blas.NoTrans && tA != blas.Trans && tA != blas.ConjTrans {
panic(badTranspose)
}
if d != blas.NonUnit && d != blas.Unit {
panic(badDiag)
}
if m < 0 {
panic(mLT0)
}
if n < 0 {
panic(nLT0)
}
var k int
if s == blas.Left {
k = m
} else {
k = n
}
if lda*(k-1)+k > len(a) || lda < max(1, k) {
panic(badLdA)
}
if ldb*(m-1)+n > len(b) || ldb < max(1, n) {
panic(badLdB)
}
if alpha == 0 {
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
for j := range btmp {
btmp[j] = 0
}
}
return
}
nonUnit := d == blas.NonUnit
if s == blas.Left {
if tA == blas.NoTrans {
if ul == blas.Upper {
for i := 0; i < m; i++ {
tmp := alpha
if nonUnit {
tmp *= a[i*lda+i]
}
btmp := b[i*ldb : i*ldb+n]
for j := range btmp {
btmp[j] *= tmp
}
for ka, va := range a[i*lda+i+1 : i*lda+m] {
k := ka + i + 1
tmp := alpha * va
if tmp != 0 {
asm.SaxpyUnitary(tmp, b[k*ldb:k*ldb+n], btmp, btmp)
}
}
}
return
}
for i := m - 1; i >= 0; i-- {
tmp := alpha
if nonUnit {
tmp *= a[i*lda+i]
}
btmp := b[i*ldb : i*ldb+n]
for j := range btmp {
btmp[j] *= tmp
}
for k, va := range a[i*lda : i*lda+i] {
tmp := alpha * va
if tmp != 0 {
asm.SaxpyUnitary(tmp, b[k*ldb:k*ldb+n], btmp, btmp)
}
}
}
return
}
// Cases where a is transposed.
if ul == blas.Upper {
for k := m - 1; k >= 0; k-- {
btmpk := b[k*ldb : k*ldb+n]
for ia, va := range a[k*lda+k+1 : k*lda+m] {
i := ia + k + 1
btmp := b[i*ldb : i*ldb+n]
tmp := alpha * va
if tmp != 0 {
asm.SaxpyUnitary(tmp, btmpk, btmp, btmp)
}
}
tmp := alpha
if nonUnit {
tmp *= a[k*lda+k]
}
if tmp != 1 {
for j := 0; j < n; j++ {
btmpk[j] *= tmp
}
}
}
return
}
for k := 0; k < m; k++ {
btmpk := b[k*ldb : k*ldb+n]
for i, va := range a[k*lda : k*lda+k] {
btmp := b[i*ldb : i*ldb+n]
tmp := alpha * va
if tmp != 0 {
asm.SaxpyUnitary(tmp, btmpk, btmp, btmp)
}
}
tmp := alpha
if nonUnit {
tmp *= a[k*lda+k]
}
if tmp != 1 {
for j := 0; j < n; j++ {
btmpk[j] *= tmp
}
}
}
return
}
// Cases where a is on the right
if tA == blas.NoTrans {
if ul == blas.Upper {
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
for k := n - 1; k >= 0; k-- {
tmp := alpha * btmp[k]
if tmp != 0 {
btmp[k] = tmp
if nonUnit {
btmp[k] *= a[k*lda+k]
}
for ja, v := range a[k*lda+k+1 : k*lda+n] {
j := ja + k + 1
btmp[j] += tmp * v
}
}
}
}
return
}
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
for k := 0; k < n; k++ {
tmp := alpha * btmp[k]
if tmp != 0 {
btmp[k] = tmp
if nonUnit {
btmp[k] *= a[k*lda+k]
}
asm.SaxpyUnitary(tmp, a[k*lda:k*lda+k], btmp, btmp)
}
}
}
return
}
// Cases where a is transposed.
if ul == blas.Upper {
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
for j, vb := range btmp {
tmp := vb
if nonUnit {
tmp *= a[j*lda+j]
}
tmp += asm.SdotUnitary(a[j*lda+j+1:j*lda+n], btmp[j+1:n])
btmp[j] = alpha * tmp
}
}
return
}
for i := 0; i < m; i++ {
btmp := b[i*ldb : i*ldb+n]
for j := n - 1; j >= 0; j-- {
tmp := btmp[j]
if nonUnit {
tmp *= a[j*lda+j]
}
tmp += asm.SdotUnitary(a[j*lda:j*lda+j], btmp[:j])
btmp[j] = alpha * tmp
}
}
}
+66
View File
@@ -0,0 +1,66 @@
// 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.
//go:generate ./single_precision
package native
type Implementation struct{}
// The following are panic strings used during parameter checks.
const (
negativeN = "blas: n < 0"
zeroIncX = "blas: zero x index increment"
zeroIncY = "blas: zero y index increment"
badLenX = "blas: x index out of range"
badLenY = "blas: y index out of range"
mLT0 = "blas: m < 0"
nLT0 = "blas: n < 0"
kLT0 = "blas: k < 0"
kLLT0 = "blas: kL < 0"
kULT0 = "blas: kU < 0"
badUplo = "blas: illegal triangle"
badTranspose = "blas: illegal transpose"
badDiag = "blas: illegal diagonal"
badSide = "blas: illegal side"
badLdA = "blas: index of a out of range"
badLdB = "blas: index of b out of range"
badLdC = "blas: index of c out of range"
badX = "blas: x index out of range"
badY = "blas: y index out of range"
)
// [SD]gemm behavior constants. These are kept here to keep them out of the
// way during single precision code genration.
const (
blockSize = 64 // b x b matrix
minParBlock = 4 // minimum number of blocks needed to go parallel
buffMul = 4 // how big is the buffer relative to the number of workers
)
// [SD]gemm debugging constant.
const debug = false
// subMul is a common type shared by [SD]gemm.
type subMul struct {
i, j int // index of block
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
+395
View File
@@ -0,0 +1,395 @@
// Generated code do not edit. Run `go generate`.
// Copyright ©2014 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 native
import (
"fmt"
"runtime"
"sync"
"github.com/gonum/blas"
"github.com/gonum/internal/asm"
)
// Sgemm computes
// C = beta * C + alpha * A * B.
// tA and tB specify whether A or B are transposed. A, B, and C are m×n dense
// matrices.
//
// Float32 implementations are autogenerated and not directly tested.
func (Implementation) Sgemm(tA, tB blas.Transpose, m, n, k int, alpha float32, a []float32, lda int, b []float32, ldb int, beta float32, c []float32, ldc int) {
if tA != blas.NoTrans && tA != blas.Trans && tA != blas.ConjTrans {
panic(badTranspose)
}
if tB != blas.NoTrans && tB != blas.Trans && tB != blas.ConjTrans {
panic(badTranspose)
}
var amat, bmat, cmat general32
if tA != blas.NoTrans {
amat = general32{
data: a,
rows: k,
cols: m,
stride: lda,
}
} else {
amat = general32{
data: a,
rows: m,
cols: k,
stride: lda,
}
}
err := amat.check('a')
if err != nil {
panic(err.Error())
}
if tB != blas.NoTrans {
bmat = general32{
data: b,
rows: n,
cols: k,
stride: ldb,
}
} else {
bmat = general32{
data: b,
rows: k,
cols: n,
stride: ldb,
}
}
err = bmat.check('b')
if err != nil {
panic(err.Error())
}
cmat = general32{
data: c,
rows: m,
cols: n,
stride: ldc,
}
err = cmat.check('c')
if err != nil {
panic(err.Error())
}
// scale c
if beta != 1 {
if beta == 0 {
for i := 0; i < m; i++ {
ctmp := cmat.data[i*cmat.stride : i*cmat.stride+cmat.cols]
for j := range ctmp {
ctmp[j] = 0
}
}
} else {
for i := 0; i < m; i++ {
ctmp := cmat.data[i*cmat.stride : i*cmat.stride+cmat.cols]
for j := range ctmp {
ctmp[j] *= beta
}
}
}
}
sgemmParallel(tA, tB, amat, bmat, cmat, alpha)
}
func sgemmParallel(tA, tB blas.Transpose, a, b, c general32, alpha float32) {
// dgemmParallel computes a parallel matrix multiplication by partitioning
// a and b into sub-blocks, and updating c with the multiplication of the sub-block
// In all cases,
// A = [ A_11 A_12 ... A_1j
// A_21 A_22 ... A_2j
// ...
// A_i1 A_i2 ... A_ij]
//
// and same for B. All of the submatrix sizes are blockSize*blockSize except
// at the edges.
// In all cases, there is one dimension for each matrix along which
// C must be updated sequentially.
// Cij = \sum_k Aik Bki, (A * B)
// Cij = \sum_k Aki Bkj, (A^T * B)
// Cij = \sum_k Aik Bjk, (A * B^T)
// Cij = \sum_k Aki Bjk, (A^T * B^T)
//
// This code computes one {i, j} block sequentially along the k dimension,
// and computes all of the {i, j} blocks concurrently. This
// partitioning allows Cij to be updated in-place without race-conditions.
// Instead of launching a goroutine for each possible concurrent computation,
// a number of worker goroutines are created and channels are used to pass
// available and completed cases.
//
// http://alexkr.com/docs/matrixmult.pdf is a good reference on matrix-matrix
// multiplies, though this code does not copy matrices to attempt to eliminate
// cache misses.
aTrans := tA == blas.Trans || tA == blas.ConjTrans
bTrans := tB == blas.Trans || tB == blas.ConjTrans
maxKLen, parBlocks := computeNumBlocks32(a, b, aTrans, bTrans)
if parBlocks < minParBlock {
// The matrix multiplication is small in the dimensions where it can be
// computed concurrently. Just do it in serial.
sgemmSerial(tA, tB, a, b, c, alpha)
return
}
nWorkers := runtime.GOMAXPROCS(0)
if parBlocks < nWorkers {
nWorkers = parBlocks
}
// There is a tradeoff between the workers having to wait for work
// and a large buffer making operations slow.
buf := buffMul * nWorkers
if buf > parBlocks {
buf = parBlocks
}
sendChan := make(chan subMul, buf)
// Launch workers. A worker receives an {i, j} submatrix of c, and computes
// A_ik B_ki (or the transposed version) storing the result in c_ij. When the
// channel is finally closed, it signals to the waitgroup that it has finished
// computing.
var wg sync.WaitGroup
for i := 0; i < nWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// Make local copies of otherwise global variables to reduce shared memory.
// This has a noticable effect on benchmarks in some cases.
alpha := alpha
aTrans := aTrans
bTrans := bTrans
crows := c.rows
ccols := c.cols
for sub := range sendChan {
i := sub.i
j := sub.j
leni := blockSize
if i+leni > crows {
leni = crows - i
}
lenj := blockSize
if j+lenj > ccols {
lenj = ccols - j
}
cSub := c.view(i, j, leni, lenj)
// Compute A_ik B_kj for all k
for k := 0; k < maxKLen; k += blockSize {
lenk := blockSize
if k+lenk > maxKLen {
lenk = maxKLen - k
}
var aSub, bSub general32
if aTrans {
aSub = a.view(k, i, lenk, leni)
} else {
aSub = a.view(i, k, leni, lenk)
}
if bTrans {
bSub = b.view(j, k, lenj, lenk)
} else {
bSub = b.view(k, j, lenk, lenj)
}
sgemmSerial(tA, tB, aSub, bSub, cSub, alpha)
}
}
}()
}
// Send out all of the {i, j} subblocks for computation.
for i := 0; i < c.rows; i += blockSize {
for j := 0; j < c.cols; j += blockSize {
sendChan <- subMul{
i: i,
j: j,
}
}
}
close(sendChan)
wg.Wait()
}
// computeNumBlocks says how many blocks there are to compute. maxKLen says the length of the
// k dimension, parBlocks is the number of blocks that could be computed in parallel
// (the submatrices in i and j). expect is the full number of blocks that will be computed.
func computeNumBlocks32(a, b general32, aTrans, bTrans bool) (maxKLen, parBlocks int) {
aRowBlocks := a.rows / blockSize
if a.rows%blockSize != 0 {
aRowBlocks++
}
aColBlocks := a.cols / blockSize
if a.cols%blockSize != 0 {
aColBlocks++
}
bRowBlocks := b.rows / blockSize
if b.rows%blockSize != 0 {
bRowBlocks++
}
bColBlocks := b.cols / blockSize
if b.cols%blockSize != 0 {
bColBlocks++
}
switch {
case !aTrans && !bTrans:
// Cij = \sum_k Aik Bki
maxKLen = a.cols
parBlocks = aRowBlocks * bColBlocks
case aTrans && !bTrans:
// Cij = \sum_k Aki Bkj
maxKLen = a.rows
parBlocks = aColBlocks * bColBlocks
case !aTrans && bTrans:
// Cij = \sum_k Aik Bjk
maxKLen = a.cols
parBlocks = aRowBlocks * bRowBlocks
case aTrans && bTrans:
// Cij = \sum_k Aki Bjk
maxKLen = a.rows
parBlocks = aColBlocks * bRowBlocks
}
return
}
// sgemmSerial is serial matrix multiply
func sgemmSerial(tA, tB blas.Transpose, a, b, c general32, alpha float32) {
switch {
case tA == blas.NoTrans && tB == blas.NoTrans:
sgemmSerialNotNot(a, b, c, alpha)
return
case tA != blas.NoTrans && tB == blas.NoTrans:
sgemmSerialTransNot(a, b, c, alpha)
return
case tA == blas.NoTrans && tB != blas.NoTrans:
sgemmSerialNotTrans(a, b, c, alpha)
return
case tA != blas.NoTrans && tB != blas.NoTrans:
sgemmSerialTransTrans(a, b, c, alpha)
return
default:
panic("unreachable")
}
}
// sgemmSerial where neither a nor b are transposed
func sgemmSerialNotNot(a, b, c general32, alpha float32) {
if debug {
if a.cols != b.rows {
panic("inner dimension mismatch")
}
if a.rows != c.rows {
panic("outer dimension mismatch")
}
if b.cols != c.cols {
panic("outer dimension mismatch")
}
}
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for i := 0; i < a.rows; i++ {
ctmp := c.data[i*c.stride : i*c.stride+c.cols]
for l, v := range a.data[i*a.stride : i*a.stride+a.cols] {
tmp := alpha * v
if tmp != 0 {
asm.SaxpyUnitary(tmp, b.data[l*b.stride:l*b.stride+b.cols], ctmp, ctmp)
}
}
}
}
// sgemmSerial where neither a is transposed and b is not
func sgemmSerialTransNot(a, b, c general32, alpha float32) {
if debug {
if a.rows != b.rows {
fmt.Println(a.rows, b.rows)
panic("inner dimension mismatch")
}
if a.cols != c.rows {
panic("outer dimension mismatch")
}
if b.cols != c.cols {
panic("outer dimension mismatch")
}
}
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for l := 0; l < a.rows; l++ {
btmp := b.data[l*b.stride : l*b.stride+b.cols]
for i, v := range a.data[l*a.stride : l*a.stride+a.cols] {
tmp := alpha * v
ctmp := c.data[i*c.stride : i*c.stride+c.cols]
if tmp != 0 {
asm.SaxpyUnitary(tmp, btmp, ctmp, ctmp)
}
}
}
}
// sgemmSerial where neither a is not transposed and b is
func sgemmSerialNotTrans(a, b, c general32, alpha float32) {
if debug {
if a.cols != b.cols {
panic("inner dimension mismatch")
}
if a.rows != c.rows {
panic("outer dimension mismatch")
}
if b.rows != c.cols {
panic("outer dimension mismatch")
}
}
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for i := 0; i < a.rows; i++ {
atmp := a.data[i*a.stride : i*a.stride+a.cols]
ctmp := c.data[i*c.stride : i*c.stride+c.cols]
for j := 0; j < b.rows; j++ {
ctmp[j] += alpha * asm.SdotUnitary(atmp, b.data[j*b.stride:j*b.stride+b.cols])
}
}
}
// sgemmSerial where both are transposed
func sgemmSerialTransTrans(a, b, c general32, alpha float32) {
if debug {
if a.rows != b.cols {
panic("inner dimension mismatch")
}
if a.cols != c.rows {
panic("outer dimension mismatch")
}
if b.rows != c.cols {
panic("outer dimension mismatch")
}
}
// This style is used instead of the literal [i*stride +j]) is used because
// approximately 5 times faster as of go 1.3.
for l := 0; l < a.rows; l++ {
for i, v := range a.data[l*a.stride : l*a.stride+a.cols] {
ctmp := c.data[i*c.stride : i*c.stride+c.cols]
if v != 0 {
tmp := alpha * v
if tmp != 0 {
asm.SaxpyInc(tmp, b.data[l:], ctmp, uintptr(b.rows), uintptr(b.stride), 1, 0, 0)
}
}
}
}
}
+143
View File
@@ -0,0 +1,143 @@
#!/usr/bin/env bash
# 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.
WARNING='//\
// Float32 implementations are autogenerated and not directly tested.\
'
# Level1 routines.
echo Generating level1single.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > level1single.go
cat level1double.go \
| gofmt -r 'blas.Float64Level1 -> blas.Float32Level1' \
\
| gofmt -r 'float64 -> float32' \
| gofmt -r 'blas.DrotmParams -> blas.SrotmParams' \
\
| gofmt -r 'asm.DaxpyInc -> asm.SaxpyInc' \
| gofmt -r 'asm.DaxpyUnitary -> asm.SaxpyUnitary' \
| gofmt -r 'asm.DdotInc -> asm.SdotInc' \
| gofmt -r 'asm.DdotUnitary -> asm.SdotUnitary' \
\
| sed -e "s_^\(func (Implementation) \)D\(.*\)\$_$WARNING\1S\2_" \
-e 's_^// D_// S_' \
-e "s_^\(func (Implementation) \)Id\(.*\)\$_$WARNING\1Is\2_" \
-e 's_^// Id_// Is_' \
-e 's_"math"_math "github.com/gonum/blas/native/internal/math32"_' \
>> level1single.go
echo Generating level1single_sdot.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > level1single_sdot.go
cat level1double_ddot.go \
| gofmt -r 'float64 -> float32' \
\
| gofmt -r 'asm.DdotInc -> asm.SdotInc' \
| gofmt -r 'asm.DdotUnitary -> asm.SdotUnitary' \
\
| sed -e "s_^\(func (Implementation) \)D\(.*\)\$_$WARNING\1S\2_" \
-e 's_^// D_// S_' \
>> level1single_sdot.go
echo Generating level1single_dsdot.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > level1single_dsdot.go
cat level1double_ddot.go \
| gofmt -r '[]float64 -> []float32' \
\
| gofmt -r 'asm.DdotInc -> asm.DsdotInc' \
| gofmt -r 'asm.DdotUnitary -> asm.DsdotUnitary' \
\
| sed -e "s_^\(func (Implementation) \)D\(.*\)\$_$WARNING\1Ds\2_" \
-e 's_^// D_// Ds_' \
>> level1single_dsdot.go
echo Generating level1single_sdsdot.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > level1single_sdsdot.go
cat level1double_ddot.go \
| gofmt -r 'float64 -> float32' \
\
| gofmt -r 'asm.DdotInc(x, y, f(n), f(incX), f(incY), f(ix), f(iy)) -> alpha + float32(asm.DsdotInc(x, y, f(n), f(incX), f(incY), f(ix), f(iy)))' \
| gofmt -r 'asm.DdotUnitary(a, b) -> alpha + float32(asm.DsdotUnitary(a, b))' \
\
| sed -e "s_^\(func (Implementation) \)D\(.*\)\$_$WARNING\1Sds\2_" \
-e 's_^// D\(.*\)$_// Sds\1 plus a constant_' \
-e 's_\\sum_alpha + \\sum_' \
-e 's/n int/n int, alpha float32/' \
>> level1single_sdsdot.go
# Level2 routines.
echo Generating level2single.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > level2single.go
cat level2double.go \
| gofmt -r 'blas.Float64Level2 -> blas.Float32Level2' \
\
| gofmt -r 'float64 -> float32' \
\
| gofmt -r 'Dscal -> Sscal' \
\
| gofmt -r 'asm.DaxpyInc -> asm.SaxpyInc' \
| gofmt -r 'asm.DaxpyUnitary -> asm.SaxpyUnitary' \
| gofmt -r 'asm.DdotInc -> asm.SdotInc' \
| gofmt -r 'asm.DdotUnitary -> asm.SdotUnitary' \
\
| sed -e "s_^\(func (Implementation) \)D\(.*\)\$_$WARNING\1S\2_" \
-e 's_^// D_// S_' \
>> level2single.go
# Level3 routines.
echo Generating level3single.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > level3single.go
cat level3double.go \
| gofmt -r 'blas.Float64Level3 -> blas.Float32Level3' \
\
| gofmt -r 'float64 -> float32' \
\
| gofmt -r 'asm.DaxpyUnitary -> asm.SaxpyUnitary' \
| gofmt -r 'asm.DdotUnitary -> asm.SdotUnitary' \
\
| sed -e "s_^\(func (Implementation) \)D\(.*\)\$_$WARNING\1S\2_" \
-e 's_^// D_// S_' \
>> level3single.go
echo Generating general_single.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > general_single.go
cat general_double.go \
| gofmt -r 'float64 -> float32' \
\
| gofmt -r 'general64 -> general32' \
| gofmt -r 'newGeneral64 -> newGeneral32' \
\
| sed -e 's/(g general64) print()/(g general32) print()/' \
-e 's_"math"_math "github.com/gonum/blas/native/internal/math32"_' \
>> general_single.go
echo Generating sgemm.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > sgemm.go
cat dgemm.go \
| gofmt -r 'float64 -> float32' \
| gofmt -r 'general64 -> general32' \
\
| gofmt -r 'dgemmParallel -> sgemmParallel' \
| gofmt -r 'computeNumBlocks64 -> computeNumBlocks32' \
| gofmt -r 'dgemmSerial -> sgemmSerial' \
| gofmt -r 'dgemmSerialNotNot -> sgemmSerialNotNot' \
| gofmt -r 'dgemmSerialTransNot -> sgemmSerialTransNot' \
| gofmt -r 'dgemmSerialNotTrans -> sgemmSerialNotTrans' \
| gofmt -r 'dgemmSerialTransTrans -> sgemmSerialTransTrans' \
\
| gofmt -r 'asm.DaxpyInc -> asm.SaxpyInc' \
| gofmt -r 'asm.DaxpyUnitary -> asm.SaxpyUnitary' \
| gofmt -r 'asm.DdotInc -> asm.SdotInc' \
| gofmt -r 'asm.DdotUnitary -> asm.SdotUnitary' \
\
| sed -e "s_^\(func (Implementation) \)D\(.*\)\$_$WARNING\1S\2_" \
-e 's_^// D_// S_' \
-e 's_^// d_// s_' \
>> sgemm.go
+1
View File
@@ -0,0 +1 @@
test.out
+29
View File
@@ -0,0 +1,29 @@
language: go
# Versions of go that are explicitly supported by gonum.
go:
- 1.5beta1
- 1.3.3
- 1.4.2
# Required for coverage.
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
# Get deps, build, test, and ensure the code is gofmt'ed.
# If we are building as gonum, then we have access to the coveralls api key, so we can run coverage as well.
script:
- go get -d -t -v ./...
- go build -v ./...
- go test -v ./...
- diff <(gofmt -d .) <("")
- if [[ $TRAVIS_SECURE_ENV_VARS = "true" ]]; then bash ./.travis/test-coverage.sh; fi
notifications:
email:
recipients:
- jragonmiris@gmail.com
on_success: change
on_failure: always
+15
View File
@@ -0,0 +1,15 @@
# Gonum Graph [![Build Status](https://travis-ci.org/gonum/graph.svg?branch=master)](https://travis-ci.org/gonum/graph) [![Coverage Status](https://img.shields.io/coveralls/gonum/graph.svg)](https://coveralls.io/r/gonum/graph?branch=master)
This is a generalized graph package for the Go language. It aims to provide a clean, transparent API for common algorithms on arbitrary graphs such as finding the graph's strongly connected components, dominators, or searces.
The package is currently in testing, and the API is "semi-stable". The signatures of any functions like AStar are unlikely to change much, but the Graph, Node, and Edge interfaces may change a bit.
## Issues
If you find any bugs, feel free to file an issue on the github issue tracker. Discussions on API changes, added features, code review, or similar requests are preferred on the Gonum-dev Google Group.
https://groups.google.com/forum/#!forum/gonum-dev
## License
Please see github.com/gonum/license for general license information, contributors, authors, etc on the Gonum suite of packages.
+8
View File
@@ -0,0 +1,8 @@
// Copyright ©2014 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 concrete
// TODO(anyone) Package level documentation for this describing the overall
// reason for the package and a summary for the provided types.
+136
View File
@@ -0,0 +1,136 @@
// Copyright ©2014 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 concrete
import (
"github.com/gonum/graph"
"github.com/gonum/matrix/mat64"
)
// DirectedDenseGraph represents a graph such that all IDs are in a contiguous
// block from 0 to n-1.
type DirectedDenseGraph struct {
absent float64
mat *mat64.Dense
}
// NewDirectedDenseGraph creates a directed dense graph with n nodes.
// If passable is true all pairs of nodes will be connected by an edge
// with unit cost, otherwise every node will start unconnected with
// the cost specified by absent.
func NewDirectedDenseGraph(n int, passable bool, absent float64) *DirectedDenseGraph {
mat := make([]float64, n*n)
v := 1.
if !passable {
v = absent
}
for i := range mat {
mat[i] = v
}
return &DirectedDenseGraph{mat: mat64.NewDense(n, n, mat), absent: absent}
}
func (g *DirectedDenseGraph) Has(n graph.Node) bool {
id := n.ID()
r, _ := g.mat.Dims()
return 0 <= id && id < r
}
func (g *DirectedDenseGraph) Nodes() []graph.Node {
r, _ := g.mat.Dims()
nodes := make([]graph.Node, r)
for i := 0; i < r; i++ {
nodes[i] = Node(i)
}
return nodes
}
func (g *DirectedDenseGraph) Edges() []graph.Edge {
var edges []graph.Edge
r, _ := g.mat.Dims()
for i := 0; i < r; i++ {
for j := 0; j < r; j++ {
if i == j {
continue
}
if !isSame(g.mat.At(i, j), g.absent) {
edges = append(edges, Edge{Node(i), Node(j)})
}
}
}
return edges
}
func (g *DirectedDenseGraph) From(n graph.Node) []graph.Node {
var neighbors []graph.Node
id := n.ID()
_, c := g.mat.Dims()
for j := 0; j < c; j++ {
if j == id {
continue
}
if !isSame(g.mat.At(id, j), g.absent) {
neighbors = append(neighbors, Node(j))
}
}
return neighbors
}
func (g *DirectedDenseGraph) To(n graph.Node) []graph.Node {
var neighbors []graph.Node
id := n.ID()
r, _ := g.mat.Dims()
for i := 0; i < r; i++ {
if i == id {
continue
}
if !isSame(g.mat.At(i, id), g.absent) {
neighbors = append(neighbors, Node(i))
}
}
return neighbors
}
func (g *DirectedDenseGraph) HasEdge(x, y graph.Node) bool {
xid := x.ID()
yid := y.ID()
return xid != yid && (!isSame(g.mat.At(xid, yid), g.absent) || !isSame(g.mat.At(yid, xid), g.absent))
}
func (g *DirectedDenseGraph) Edge(u, v graph.Node) graph.Edge {
if g.HasEdge(u, v) {
return Edge{u, v}
}
return nil
}
func (g *DirectedDenseGraph) HasEdgeFromTo(u, v graph.Node) bool {
uid := u.ID()
vid := v.ID()
return uid != vid && !isSame(g.mat.At(uid, vid), g.absent)
}
func (g *DirectedDenseGraph) Weight(e graph.Edge) float64 {
return g.mat.At(e.From().ID(), e.To().ID())
}
func (g *DirectedDenseGraph) SetEdgeWeight(e graph.Edge, weight float64) {
fid := e.From().ID()
tid := e.To().ID()
if fid == tid {
panic("concrete: set edge cost of illegal edge")
}
g.mat.Set(fid, tid, weight)
}
func (g *DirectedDenseGraph) RemoveEdge(e graph.Edge) {
g.mat.Set(e.From().ID(), e.To().ID(), g.absent)
}
func (g *DirectedDenseGraph) Matrix() mat64.Matrix {
// Prevent alteration of dimensions of the returned matrix.
m := *g.mat
return &m
}
+131
View File
@@ -0,0 +1,131 @@
// Copyright ©2014 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 concrete
import (
"github.com/gonum/graph"
"github.com/gonum/matrix/mat64"
)
// UndirectedDenseGraph represents a graph such that all IDs are in a contiguous
// block from 0 to n-1.
type UndirectedDenseGraph struct {
absent float64
mat *mat64.SymDense
}
// NewUndirectedDenseGraph creates an undirected dense graph with n nodes.
// If passable is true all pairs of nodes will be connected by an edge
// with unit cost, otherwise every node will start unconnected with
// the cost specified by absent.
func NewUndirectedDenseGraph(n int, passable bool, absent float64) *UndirectedDenseGraph {
mat := make([]float64, n*n)
v := 1.
if !passable {
v = absent
}
for i := range mat {
mat[i] = v
}
return &UndirectedDenseGraph{mat: mat64.NewSymDense(n, mat), absent: absent}
}
func (g *UndirectedDenseGraph) Has(n graph.Node) bool {
id := n.ID()
r := g.mat.Symmetric()
return 0 <= id && id < r
}
func (g *UndirectedDenseGraph) Nodes() []graph.Node {
r := g.mat.Symmetric()
nodes := make([]graph.Node, r)
for i := 0; i < r; i++ {
nodes[i] = Node(i)
}
return nodes
}
func (g *UndirectedDenseGraph) Edges() []graph.Edge {
var edges []graph.Edge
r, _ := g.mat.Dims()
for i := 0; i < r; i++ {
for j := i + 1; j < r; j++ {
if !isSame(g.mat.At(i, j), g.absent) {
edges = append(edges, Edge{Node(i), Node(j)})
}
}
}
return edges
}
func (g *UndirectedDenseGraph) Degree(n graph.Node) int {
id := n.ID()
var deg int
r := g.mat.Symmetric()
for i := 0; i < r; i++ {
if i == id {
continue
}
if !isSame(g.mat.At(id, i), g.absent) {
deg++
}
}
return deg
}
func (g *UndirectedDenseGraph) From(n graph.Node) []graph.Node {
var neighbors []graph.Node
id := n.ID()
r := g.mat.Symmetric()
for i := 0; i < r; i++ {
if i == id {
continue
}
if !isSame(g.mat.At(id, i), g.absent) {
neighbors = append(neighbors, Node(i))
}
}
return neighbors
}
func (g *UndirectedDenseGraph) HasEdge(u, v graph.Node) bool {
uid := u.ID()
vid := v.ID()
return uid != vid && !isSame(g.mat.At(uid, vid), g.absent)
}
func (g *UndirectedDenseGraph) Edge(u, v graph.Node) graph.Edge {
return g.EdgeBetween(u, v)
}
func (g *UndirectedDenseGraph) EdgeBetween(u, v graph.Node) graph.Edge {
if g.HasEdge(u, v) {
return Edge{u, v}
}
return nil
}
func (g *UndirectedDenseGraph) Weight(e graph.Edge) float64 {
return g.mat.At(e.From().ID(), e.To().ID())
}
func (g *UndirectedDenseGraph) SetEdgeWeight(e graph.Edge, weight float64) {
fid := e.From().ID()
tid := e.To().ID()
if fid == tid {
panic("concrete: set edge cost of illegal edge")
}
g.mat.SetSym(fid, tid, weight)
}
func (g *UndirectedDenseGraph) RemoveEdge(e graph.Edge) {
g.mat.SetSym(e.From().ID(), e.To().ID(), g.absent)
}
func (g *UndirectedDenseGraph) Matrix() mat64.Matrix {
// Prevent alteration of dimensions of the returned matrix.
m := *g.mat
return &m
}
+269
View File
@@ -0,0 +1,269 @@
// Copyright ©2014 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 concrete
import (
"fmt"
"github.com/gonum/graph"
)
// A Directed graph is a highly generalized MutableDirectedGraph.
//
// In most cases it's likely more desireable to use a graph specific to your
// problem domain.
type DirectedGraph struct {
successors map[int]map[int]WeightedEdge
predecessors map[int]map[int]WeightedEdge
nodeMap map[int]graph.Node
// Add/remove convenience variables
maxID int
freeMap map[int]struct{}
}
func NewDirectedGraph() *DirectedGraph {
return &DirectedGraph{
successors: make(map[int]map[int]WeightedEdge),
predecessors: make(map[int]map[int]WeightedEdge),
nodeMap: make(map[int]graph.Node),
maxID: 0,
freeMap: make(map[int]struct{}),
}
}
func (g *DirectedGraph) NewNodeID() int {
if g.maxID != maxInt {
g.maxID++
return g.maxID
}
// Implicitly checks if len(g.freeMap) == 0
for id := range g.freeMap {
return id
}
// I cannot foresee this ever happening, but just in case
if len(g.nodeMap) == maxInt {
panic("cannot allocate node: graph too large")
}
for i := 0; i < maxInt; i++ {
if _, ok := g.nodeMap[i]; !ok {
return i
}
}
// Should not happen.
panic("cannot allocate node id: no free id found")
}
// Adds a node to the graph. Implementation note: if you add a node close to or at
// the max int on your machine NewNode will become slower.
func (g *DirectedGraph) AddNode(n graph.Node) {
if _, exists := g.nodeMap[n.ID()]; exists {
panic(fmt.Sprintf("concrete: node ID collision: %d", n.ID()))
}
g.nodeMap[n.ID()] = n
g.successors[n.ID()] = make(map[int]WeightedEdge)
g.predecessors[n.ID()] = make(map[int]WeightedEdge)
delete(g.freeMap, n.ID())
g.maxID = max(g.maxID, n.ID())
}
func (g *DirectedGraph) SetEdge(e graph.Edge, cost float64) {
var (
from = e.From()
fid = from.ID()
to = e.To()
tid = to.ID()
)
if fid == tid {
panic("concrete: adding self edge")
}
if !g.Has(from) {
g.AddNode(from)
}
if !g.Has(to) {
g.AddNode(to)
}
g.successors[fid][tid] = WeightedEdge{Edge: e, Cost: cost}
g.predecessors[tid][fid] = WeightedEdge{Edge: e, Cost: cost}
}
func (g *DirectedGraph) RemoveNode(n graph.Node) {
if _, ok := g.nodeMap[n.ID()]; !ok {
return
}
delete(g.nodeMap, n.ID())
for succ := range g.successors[n.ID()] {
delete(g.predecessors[succ], n.ID())
}
delete(g.successors, n.ID())
for pred := range g.predecessors[n.ID()] {
delete(g.successors[pred], n.ID())
}
delete(g.predecessors, n.ID())
g.maxID-- // Fun facts: even if this ID doesn't exist this still works!
g.freeMap[n.ID()] = struct{}{}
}
func (g *DirectedGraph) RemoveEdge(e graph.Edge) {
from, to := e.From(), e.To()
if _, ok := g.nodeMap[from.ID()]; !ok {
return
} else if _, ok := g.nodeMap[to.ID()]; !ok {
return
}
delete(g.successors[from.ID()], to.ID())
delete(g.predecessors[to.ID()], from.ID())
}
func (g *DirectedGraph) EmptyGraph() {
g.successors = make(map[int]map[int]WeightedEdge)
g.predecessors = make(map[int]map[int]WeightedEdge)
g.nodeMap = make(map[int]graph.Node)
}
/* Graph implementation */
func (g *DirectedGraph) From(n graph.Node) []graph.Node {
if _, ok := g.successors[n.ID()]; !ok {
return nil
}
successors := make([]graph.Node, len(g.successors[n.ID()]))
i := 0
for succ := range g.successors[n.ID()] {
successors[i] = g.nodeMap[succ]
i++
}
return successors
}
func (g *DirectedGraph) HasEdge(x, y graph.Node) bool {
xid := x.ID()
yid := y.ID()
if _, ok := g.nodeMap[xid]; !ok {
return false
}
if _, ok := g.nodeMap[yid]; !ok {
return false
}
if _, ok := g.successors[xid][yid]; ok {
return true
}
_, ok := g.successors[yid][xid]
return ok
}
func (g *DirectedGraph) Edge(u, v graph.Node) graph.Edge {
if _, ok := g.nodeMap[u.ID()]; !ok {
return nil
}
if _, ok := g.nodeMap[v.ID()]; !ok {
return nil
}
edge, ok := g.successors[u.ID()][v.ID()]
if !ok {
return nil
}
return edge.Edge
}
func (g *DirectedGraph) HasEdgeFromTo(u, v graph.Node) bool {
if _, ok := g.nodeMap[u.ID()]; !ok {
return false
}
if _, ok := g.nodeMap[v.ID()]; !ok {
return false
}
if _, ok := g.successors[u.ID()][v.ID()]; !ok {
return false
}
return true
}
func (g *DirectedGraph) To(n graph.Node) []graph.Node {
if _, ok := g.successors[n.ID()]; !ok {
return nil
}
predecessors := make([]graph.Node, len(g.predecessors[n.ID()]))
i := 0
for succ := range g.predecessors[n.ID()] {
predecessors[i] = g.nodeMap[succ]
i++
}
return predecessors
}
func (g *DirectedGraph) Node(id int) graph.Node {
return g.nodeMap[id]
}
func (g *DirectedGraph) Has(n graph.Node) bool {
_, ok := g.nodeMap[n.ID()]
return ok
}
func (g *DirectedGraph) Degree(n graph.Node) int {
if _, ok := g.nodeMap[n.ID()]; !ok {
return 0
}
return len(g.successors[n.ID()]) + len(g.predecessors[n.ID()])
}
func (g *DirectedGraph) Nodes() []graph.Node {
nodes := make([]graph.Node, len(g.successors))
i := 0
for _, n := range g.nodeMap {
nodes[i] = n
i++
}
return nodes
}
func (g *DirectedGraph) Weight(e graph.Edge) float64 {
if s, ok := g.successors[e.From().ID()]; ok {
if we, ok := s[e.To().ID()]; ok {
return we.Cost
}
}
return inf
}
func (g *DirectedGraph) Edges() []graph.Edge {
edgeList := make([]graph.Edge, 0, len(g.successors))
edgeMap := make(map[int]map[int]struct{}, len(g.successors))
for n, succMap := range g.successors {
edgeMap[n] = make(map[int]struct{}, len(succMap))
for succ, edge := range succMap {
if doneMap, ok := edgeMap[succ]; ok {
if _, ok := doneMap[n]; ok {
continue
}
}
edgeList = append(edgeList, edge)
edgeMap[n][succ] = struct{}{}
}
}
return edgeList
}
+250
View File
@@ -0,0 +1,250 @@
// Copyright ©2014 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 concrete
import (
"fmt"
"github.com/gonum/graph"
)
// A simple int alias.
type Node int
func (n Node) ID() int {
return int(n)
}
// Just a collection of two nodes
type Edge struct {
F, T graph.Node
}
func (e Edge) From() graph.Node {
return e.F
}
func (e Edge) To() graph.Node {
return e.T
}
type WeightedEdge struct {
graph.Edge
Cost float64
}
// A GonumGraph is a very generalized graph that can handle an arbitrary number of vertices and
// edges -- as well as act as either directed or undirected.
//
// Internally, it uses a map of successors AND predecessors, to speed up some operations (such as
// getting all successors/predecessors). It also speeds up things like adding edges (assuming both
// edges exist).
//
// However, its generality is also its weakness (and partially a flaw in needing to satisfy
// MutableGraph). For most purposes, creating your own graph is probably better. For instance,
// see TileGraph for an example of an immutable 2D grid of tiles that also implements the Graph
// interface, but would be more suitable if all you needed was a simple undirected 2D grid.
type Graph struct {
neighbors map[int]map[int]WeightedEdge
nodeMap map[int]graph.Node
// Node add/remove convenience vars
maxID int
freeMap map[int]struct{}
}
func NewGraph() *Graph {
return &Graph{
neighbors: make(map[int]map[int]WeightedEdge),
nodeMap: make(map[int]graph.Node),
maxID: 0,
freeMap: make(map[int]struct{}),
}
}
func (g *Graph) NewNodeID() int {
if g.maxID != maxInt {
g.maxID++
return g.maxID
}
// Implicitly checks if len(g.freeMap) == 0
for id := range g.freeMap {
return id
}
// I cannot foresee this ever happening, but just in case, we check.
if len(g.nodeMap) == maxInt {
panic("cannot allocate node: graph too large")
}
for i := 0; i < maxInt; i++ {
if _, ok := g.nodeMap[i]; !ok {
return i
}
}
// Should not happen.
panic("cannot allocate node id: no free id found")
}
func (g *Graph) AddNode(n graph.Node) {
if _, exists := g.nodeMap[n.ID()]; exists {
panic(fmt.Sprintf("concrete: node ID collision: %d", n.ID()))
}
g.nodeMap[n.ID()] = n
g.neighbors[n.ID()] = make(map[int]WeightedEdge)
delete(g.freeMap, n.ID())
g.maxID = max(g.maxID, n.ID())
}
func (g *Graph) SetEdge(e graph.Edge, cost float64) {
var (
from = e.From()
fid = from.ID()
to = e.To()
tid = to.ID()
)
if fid == tid {
panic("concrete: adding self edge")
}
if !g.Has(from) {
g.AddNode(from)
}
if !g.Has(to) {
g.AddNode(to)
}
g.neighbors[fid][tid] = WeightedEdge{Edge: e, Cost: cost}
g.neighbors[tid][fid] = WeightedEdge{Edge: e, Cost: cost}
}
func (g *Graph) RemoveNode(n graph.Node) {
if _, ok := g.nodeMap[n.ID()]; !ok {
return
}
delete(g.nodeMap, n.ID())
for neigh := range g.neighbors[n.ID()] {
delete(g.neighbors[neigh], n.ID())
}
delete(g.neighbors, n.ID())
if g.maxID != 0 && n.ID() == g.maxID {
g.maxID--
}
g.freeMap[n.ID()] = struct{}{}
}
func (g *Graph) RemoveEdge(e graph.Edge) {
from, to := e.From(), e.To()
if _, ok := g.nodeMap[from.ID()]; !ok {
return
} else if _, ok := g.nodeMap[to.ID()]; !ok {
return
}
delete(g.neighbors[from.ID()], to.ID())
delete(g.neighbors[to.ID()], from.ID())
}
func (g *Graph) EmptyGraph() {
g.neighbors = make(map[int]map[int]WeightedEdge)
g.nodeMap = make(map[int]graph.Node)
}
/* Graph implementation */
func (g *Graph) From(n graph.Node) []graph.Node {
if !g.Has(n) {
return nil
}
neighbors := make([]graph.Node, len(g.neighbors[n.ID()]))
i := 0
for id := range g.neighbors[n.ID()] {
neighbors[i] = g.nodeMap[id]
i++
}
return neighbors
}
func (g *Graph) HasEdge(n, neigh graph.Node) bool {
_, ok := g.neighbors[n.ID()][neigh.ID()]
return ok
}
func (g *Graph) Edge(u, v graph.Node) graph.Edge {
return g.EdgeBetween(u, v)
}
func (g *Graph) EdgeBetween(u, v graph.Node) graph.Edge {
// We don't need to check if neigh exists because
// it's implicit in the neighbors access.
if !g.Has(u) {
return nil
}
return g.neighbors[u.ID()][v.ID()].Edge
}
func (g *Graph) Node(id int) graph.Node {
return g.nodeMap[id]
}
func (g *Graph) Has(n graph.Node) bool {
_, ok := g.nodeMap[n.ID()]
return ok
}
func (g *Graph) Nodes() []graph.Node {
nodes := make([]graph.Node, len(g.nodeMap))
i := 0
for _, n := range g.nodeMap {
nodes[i] = n
i++
}
return nodes
}
func (g *Graph) Weight(e graph.Edge) float64 {
if n, ok := g.neighbors[e.From().ID()]; ok {
if we, ok := n[e.To().ID()]; ok {
return we.Cost
}
}
return inf
}
func (g *Graph) Edges() []graph.Edge {
m := make(map[WeightedEdge]struct{})
toReturn := make([]graph.Edge, 0)
for _, neighs := range g.neighbors {
for _, we := range neighs {
if _, ok := m[we]; !ok {
m[we] = struct{}{}
toReturn = append(toReturn, we.Edge)
}
}
}
return toReturn
}
func (g *Graph) Degree(n graph.Node) int {
if _, ok := g.nodeMap[n.ID()]; !ok {
return 0
}
return len(g.neighbors[n.ID()])
}
+44
View File
@@ -0,0 +1,44 @@
// Copyright ©2014 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 concrete
import (
"math"
"github.com/gonum/graph"
)
type nodeSorter []graph.Node
func (ns nodeSorter) Less(i, j int) bool {
return ns[i].ID() < ns[j].ID()
}
func (ns nodeSorter) Swap(i, j int) {
ns[i], ns[j] = ns[j], ns[i]
}
func (ns nodeSorter) Len() int {
return len(ns)
}
// The math package only provides explicitly sized max
// values. This ensures we get the max for the actual
// type int.
const maxInt int = int(^uint(0) >> 1)
var inf = math.Inf(1)
func isSame(a, b float64) bool {
return a == b || (math.IsNaN(a) && math.IsNaN(b))
}
func max(a, b int) int {
if a > b {
return a
} else {
return b
}
}
+38
View File
@@ -0,0 +1,38 @@
// Copyright ©2014 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 graph implements functions and interfaces to deal with formal discrete graphs. It aims to
be first and foremost flexible, with speed as a strong second priority.
In this package, graphs are taken to be directed, and undirected graphs are considered to be a
special case of directed graphs that happen to have reciprocal edges. Graphs are, by default,
unweighted, but functions that require weighted edges have several methods of dealing with this.
In order of precedence:
1. These functions have an argument called Cost (and in some cases, HeuristicCost). If this is
present, it will always be used to determine the cost between two nodes.
2. These functions will check if your graph implements the Coster (and/or HeuristicCoster)
interface. If this is present, and the Cost (or HeuristicCost) argument is nil, these functions
will be used.
3. Finally, if no user data is supplied, it will use the functions UniformCost (always returns 1)
and/or NulLHeuristic (always returns 0).
For information on the specification for Cost functions, please see the Coster interface.
Finally, although the functions take in a Graph -- they will always use the correct behavior.
If your graph implements DirectedGraph, it will use Successors and To where applicable,
if undirected, it will use From instead. If it implements neither, it will scan the edge list
for successors and predecessors where applicable. (This is slow, you should always implement either
Directed or Undirected)
This package will never modify a graph that is not Mutable (and the interface does not allow it to
do so). However, return values are free to be modified, so never pass a reference to your own edge
list or node list. It also guarantees that any nodes passed back to the user will be the same
nodes returned to it -- that is, it will never take a Node's ID and then wrap the ID in a new
struct and return that. You'll always get back your original data.
*/
package graph
+383
View File
@@ -0,0 +1,383 @@
// 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 dot implements GraphViz DOT marshaling of graphs.
//
// See the GraphViz DOT Guide and the DOT grammar for more information
// on using specific aspects of the DOT language:
//
// DOT Guide: http://www.graphviz.org/Documentation/dotguide.pdf
//
// DOT grammar: http://www.graphviz.org/doc/info/lang.html
//
package dot
import (
"bytes"
"errors"
"fmt"
"sort"
"strings"
"github.com/gonum/graph"
)
// Node is a DOT graph node.
type Node interface {
// DOTID returns a DOT node ID.
//
// An ID is one of the following:
//
// - a string of alphabetic ([a-zA-Z\x80-\xff]) characters, underscores ('_').
// digits ([0-9]), not beginning with a digit.
// - a numeral [-]?(.[0-9]+ | [0-9]+(.[0-9]*)?).
// - a double-quoted string ("...") possibly containing escaped quotes (\").
// - an HTML string (<...>).
DOTID() string
}
// Attributers are graph.Graph values that specify top-level DOT
// attributes.
type Attributers interface {
DOTAttributers() (graph, node, edge Attributer)
}
// Attributer defines graph.Node or graph.Edge values that can
// specify DOT attributes.
type Attributer interface {
DOTAttributes() []Attribute
}
// Attribute is a DOT language key value attribute pair.
type Attribute struct {
Key, Value string
}
// Porter defines the behavior of graph.Edge values that can specify
// connection ports for their end points. The returned port corresponds
// to the the DOT node port to be used by the edge, compass corresponds
// to DOT compass point to which the edge will be aimed.
type Porter interface {
FromPort() (port, compass string)
ToPort() (port, compass string)
}
// Structurer represents a graph.Graph that can define subgraphs.
type Structurer interface {
Structure() []Graph
}
// Graph wraps named graph.Graph values.
type Graph interface {
graph.Graph
DOTID() string
}
// Subgrapher wraps graph.Node values that represent subgraphs.
type Subgrapher interface {
Subgraph() graph.Graph
}
// Marshal returns the DOT encoding for the graph g, applying the prefix
// and indent to the encoding. Name is used to specify the graph name. If
// name is empty and g implements Graph, the returned string from DOTID
// will be used. If strict is true the output bytes will be prefixed with
// the DOT "strict" keyword.
//
// Graph serialization will work for a graph.Graph without modification,
// however, advanced GraphViz DOT features provided by Marshal depend on
// implementation of the Node, Attributer, Porter, Attributers, Structurer,
// Subgrapher and Graph interfaces.
func Marshal(g graph.Graph, name, prefix, indent string, strict bool) ([]byte, error) {
var p printer
p.indent = indent
p.prefix = prefix
p.visited = make(map[edge]bool)
if strict {
p.buf.WriteString("strict ")
}
err := p.print(g, name, false, false)
if err != nil {
return nil, err
}
return p.buf.Bytes(), nil
}
type printer struct {
buf bytes.Buffer
prefix string
indent string
depth int
visited map[edge]bool
err error
}
type edge struct {
inGraph string
from, to int
}
func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool) error {
nodes := g.Nodes()
sort.Sort(byID(nodes))
p.buf.WriteString(p.prefix)
if needsIndent {
for i := 0; i < p.depth; i++ {
p.buf.WriteString(p.indent)
}
}
_, isDirected := g.(graph.Directed)
if isSubgraph {
p.buf.WriteString("sub")
} else if isDirected {
p.buf.WriteString("di")
}
p.buf.WriteString("graph")
if name == "" {
if g, ok := g.(Graph); ok {
name = g.DOTID()
}
}
if name != "" {
p.buf.WriteByte(' ')
p.buf.WriteString(name)
}
p.openBlock(" {")
if a, ok := g.(Attributers); ok {
p.writeAttributeComplex(a)
}
if s, ok := g.(Structurer); ok {
for _, g := range s.Structure() {
_, subIsDirected := g.(graph.Directed)
if subIsDirected != isDirected {
return errors.New("dot: mismatched graph type")
}
p.buf.WriteByte('\n')
p.print(g, g.DOTID(), true, true)
}
}
havePrintedNodeHeader := false
for _, n := range nodes {
if s, ok := n.(Subgrapher); ok {
// If the node is not linked to any other node
// the graph needs to be written now.
if len(g.From(n)) == 0 {
g := s.Subgraph()
_, subIsDirected := g.(graph.Directed)
if subIsDirected != isDirected {
return errors.New("dot: mismatched graph type")
}
if !havePrintedNodeHeader {
p.newline()
p.buf.WriteString("// Node definitions.")
havePrintedNodeHeader = true
}
p.newline()
p.print(g, graphID(g, n), false, true)
}
continue
}
if !havePrintedNodeHeader {
p.newline()
p.buf.WriteString("// Node definitions.")
havePrintedNodeHeader = true
}
p.newline()
p.writeNode(n)
if a, ok := n.(Attributer); ok {
p.writeAttributeList(a)
}
p.buf.WriteByte(';')
}
havePrintedEdgeHeader := false
for _, n := range nodes {
to := g.From(n)
sort.Sort(byID(to))
for _, t := range to {
if isDirected {
if p.visited[edge{inGraph: name, from: n.ID(), to: t.ID()}] {
continue
}
p.visited[edge{inGraph: name, from: n.ID(), to: t.ID()}] = true
} else {
if p.visited[edge{inGraph: name, from: n.ID(), to: t.ID()}] {
continue
}
p.visited[edge{inGraph: name, from: n.ID(), to: t.ID()}] = true
p.visited[edge{inGraph: name, from: t.ID(), to: n.ID()}] = true
}
if !havePrintedEdgeHeader {
p.buf.WriteByte('\n')
p.buf.WriteString(strings.TrimRight(p.prefix, " \t\xa0")) // Trim whitespace suffix.
p.newline()
p.buf.WriteString("// Edge definitions.")
havePrintedEdgeHeader = true
}
p.newline()
if s, ok := n.(Subgrapher); ok {
g := s.Subgraph()
_, subIsDirected := g.(graph.Directed)
if subIsDirected != isDirected {
return errors.New("dot: mismatched graph type")
}
p.print(g, graphID(g, n), false, true)
} else {
p.writeNode(n)
}
e, edgeIsPorter := g.Edge(n, t).(Porter)
if edgeIsPorter {
p.writePorts(e.FromPort())
}
if isDirected {
p.buf.WriteString(" -> ")
} else {
p.buf.WriteString(" -- ")
}
if s, ok := t.(Subgrapher); ok {
g := s.Subgraph()
_, subIsDirected := g.(graph.Directed)
if subIsDirected != isDirected {
return errors.New("dot: mismatched graph type")
}
p.print(g, graphID(g, t), false, true)
} else {
p.writeNode(t)
}
if edgeIsPorter {
p.writePorts(e.ToPort())
}
if a, ok := g.Edge(n, t).(Attributer); ok {
p.writeAttributeList(a)
}
p.buf.WriteByte(';')
}
}
p.closeBlock("}")
return nil
}
func (p *printer) writeNode(n graph.Node) {
p.buf.WriteString(nodeID(n))
}
func (p *printer) writePorts(port, cp string) {
if port != "" {
p.buf.WriteByte(':')
p.buf.WriteString(port)
}
if cp != "" {
p.buf.WriteByte(':')
p.buf.WriteString(cp)
}
}
func nodeID(n graph.Node) string {
switch n := n.(type) {
case Node:
return n.DOTID()
default:
return fmt.Sprint(n.ID())
}
}
func graphID(g graph.Graph, n graph.Node) string {
switch g := g.(type) {
case Node:
return g.DOTID()
default:
return nodeID(n)
}
}
func (p *printer) writeAttributeList(a Attributer) {
attributes := a.DOTAttributes()
switch len(attributes) {
case 0:
case 1:
p.buf.WriteString(" [")
p.buf.WriteString(attributes[0].Key)
p.buf.WriteByte('=')
p.buf.WriteString(attributes[0].Value)
p.buf.WriteString("]")
default:
p.openBlock(" [")
for _, att := range attributes {
p.newline()
p.buf.WriteString(att.Key)
p.buf.WriteByte('=')
p.buf.WriteString(att.Value)
}
p.closeBlock("]")
}
}
var attType = []string{"graph", "node", "edge"}
func (p *printer) writeAttributeComplex(ca Attributers) {
g, n, e := ca.DOTAttributers()
haveWrittenBlock := false
for i, a := range []Attributer{g, n, e} {
attributes := a.DOTAttributes()
if len(attributes) == 0 {
continue
}
if haveWrittenBlock {
p.buf.WriteByte(';')
}
p.newline()
p.buf.WriteString(attType[i])
p.openBlock(" [")
for _, att := range attributes {
p.newline()
p.buf.WriteString(att.Key)
p.buf.WriteByte('=')
p.buf.WriteString(att.Value)
}
p.closeBlock("]")
haveWrittenBlock = true
}
if haveWrittenBlock {
p.buf.WriteString(";\n")
}
}
func (p *printer) newline() {
p.buf.WriteByte('\n')
p.buf.WriteString(p.prefix)
for i := 0; i < p.depth; i++ {
p.buf.WriteString(p.indent)
}
}
func (p *printer) openBlock(b string) {
p.buf.WriteString(b)
p.depth++
}
func (p *printer) closeBlock(b string) {
p.depth--
p.newline()
p.buf.WriteString(b)
}
type byID []graph.Node
func (n byID) Len() int { return len(n) }
func (n byID) Less(i, j int) bool { return n[i].ID() < n[j].ID() }
func (n byID) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
+173
View File
@@ -0,0 +1,173 @@
// Copyright ©2014 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 graph
import "math"
// Node is a graph node. It returns a graph-unique integer ID.
type Node interface {
ID() int
}
// Edge is a graph edge. In directed graphs, the direction of the
// edge is given from -> to, otherwise the edge is semantically
// unordered.
type Edge interface {
From() Node
To() Node
}
// Graph is a generalized graph.
type Graph interface {
// Has returns whether the node exists within the graph.
Has(Node) bool
// Nodes returns all the nodes in the graph.
Nodes() []Node
// From returns all nodes that can be reached directly
// from the given node.
From(Node) []Node
// HasEdge returns whether an edge exists between
// nodes x and y without considering direction.
HasEdge(x, y Node) bool
// Edge returns the edge from u to v if such an edge
// exists and nil otherwise. The node v must be directly
// reachable from u as defined by the From method.
Edge(u, v Node) Edge
}
// Undirected is an undirected graph.
type Undirected interface {
Graph
// EdgeBetween returns the edge between nodes x and y.
EdgeBetween(x, y Node) Edge
}
// Directed is a directed graph.
type Directed interface {
Graph
// HasEdgeFromTo returns whether an edge exists
// in the graph from u to v.
HasEdgeFromTo(u, v Node) bool
// To returns all nodes that can reach directly
// to the given node.
To(Node) []Node
}
// Weighter defines graphs that can report edge weights.
type Weighter interface {
// Weight returns the weight for the given edge.
Weight(Edge) float64
}
// Mutable is an interface for generalized graph mutation.
type Mutable interface {
// NewNodeID returns a new unique arbitrary ID.
NewNodeID() int
// Adds a node to the graph. AddNode panics if
// the added node ID matches an existing node ID.
AddNode(Node)
// RemoveNode removes a node from the graph, as
// well as any edges attached to it. If the node
// is not in the graph it is a no-op.
RemoveNode(Node)
// SetEdge adds an edge from one node to another.
// If the nodes do not exist, they are added.
// SetEdge will panic if the IDs of the e.From
// and e.To are equal.
SetEdge(e Edge, cost float64)
// RemoveEdge removes the given edge, leaving the
// terminal nodes. If the edge does not exist it
// is a no-op.
RemoveEdge(Edge)
}
// MutableUndirected is an undirected graph that can be arbitrarily altered.
type MutableUndirected interface {
Undirected
Mutable
}
// MutableDirected is a directed graph that can be arbitrarily altered.
type MutableDirected interface {
Directed
Mutable
}
// WeightFunc is a mapping between an edge and an edge weight.
type WeightFunc func(Edge) float64
// UniformCost is a WeightFunc that returns an edge cost of 1 for a non-nil Edge
// and Inf for a nil Edge.
func UniformCost(e Edge) float64 {
if e == nil {
return math.Inf(1)
}
return 1
}
// CopyUndirected copies nodes and edges as undirected edges from the source to the
// destination without first clearing the destination. CopyUndirected will panic if
// a node ID in the source graph matches a node ID in the destination. If the source
// does not implement Weighter, UniformCost is used to define edge weights.
//
// Note that if the source is a directed graph and a fundamental cycle exists with
// two nodes where the edge weights differ, the resulting destination graph's edge
// weight between those nodes is undefined.
func CopyUndirected(dst MutableUndirected, src Graph) {
var weight WeightFunc
if g, ok := src.(Weighter); ok {
weight = g.Weight
} else {
weight = UniformCost
}
nodes := src.Nodes()
for _, n := range nodes {
dst.AddNode(n)
}
for _, u := range nodes {
for _, v := range src.From(u) {
edge := src.Edge(u, v)
dst.SetEdge(edge, weight(edge))
}
}
}
// CopyDirected copies nodes and edges as directed edges from the source to the
// destination without first clearing the destination. CopyDirected will panic if
// a node ID in the source graph matches a node ID in the destination. If the
// source is undirected both directions will be present in the destination after
// the copy is complete. If the source does not implement Weighter, UniformCost
// is used to define edge weights.
func CopyDirected(dst MutableDirected, src Graph) {
var weight WeightFunc
if g, ok := src.(Weighter); ok {
weight = g.Weight
} else {
weight = UniformCost
}
nodes := src.Nodes()
for _, n := range nodes {
dst.AddNode(n)
}
for _, u := range nodes {
for _, v := range src.From(u) {
edge := src.Edge(u, v)
dst.SetEdge(edge, weight(edge))
}
}
}
+73
View File
@@ -0,0 +1,73 @@
// 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 internal
import (
"github.com/gonum/graph"
)
// NodeStack implements a LIFO stack of graph.Node.
type NodeStack []graph.Node
// Len returns the number of graph.Nodes on the stack.
func (s *NodeStack) Len() int { return len(*s) }
// Pop returns the last graph.Node on the stack and removes it
// from the stack.
func (s *NodeStack) Pop() graph.Node {
v := *s
v, n := v[:len(v)-1], v[len(v)-1]
*s = v
return n
}
// Push adds the node n to the stack at the last position.
func (s *NodeStack) Push(n graph.Node) { *s = append(*s, n) }
// NodeQueue implements a FIFO queue.
type NodeQueue struct {
head int
data []graph.Node
}
// Len returns the number of graph.Nodes in the queue.
func (q *NodeQueue) Len() int { return len(q.data) - q.head }
// Enqueue adds the node n to the back of the queue.
func (q *NodeQueue) Enqueue(n graph.Node) {
if len(q.data) == cap(q.data) && q.head > 0 {
l := q.Len()
copy(q.data, q.data[q.head:])
q.head = 0
q.data = append(q.data[:l], n)
} else {
q.data = append(q.data, n)
}
}
// Dequeue returns the graph.Node at the front of the queue and
// removes it from the queue.
func (q *NodeQueue) Dequeue() graph.Node {
if q.Len() == 0 {
panic("queue: empty queue")
}
var n graph.Node
n, q.data[q.head] = q.data[q.head], nil
q.head++
if q.Len() == 0 {
q.head = 0
q.data = q.data[:0]
}
return n
}
// Reset clears the queue for reuse.
func (q *NodeQueue) Reset() {
q.head = 0
q.data = q.data[:0]
}
+211
View File
@@ -0,0 +1,211 @@
// Copyright ©2014 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 internal
import (
"unsafe"
"github.com/gonum/graph"
)
// IntSet is a set of integer identifiers.
type IntSet map[int]struct{}
// The simple accessor methods for Set are provided to allow ease of
// implementation change should the need arise.
// Add inserts an element into the set.
func (s IntSet) Add(e int) {
s[e] = struct{}{}
}
// Has reports the existence of the element in the set.
func (s IntSet) Has(e int) bool {
_, ok := s[e]
return ok
}
// Remove deletes the specified element from the set.
func (s IntSet) Remove(e int) {
delete(s, e)
}
// Count reports the number of elements stored in the set.
func (s IntSet) Count() int {
return len(s)
}
// Same determines whether two sets are backed by the same store. In the
// current implementation using hash maps it makes use of the fact that
// hash maps (at least in the gc implementation) are passed as a pointer
// to a runtime Hmap struct.
//
// A map is not seen by the runtime as a pointer though, so we cannot
// directly compare the sets converted to unsafe.Pointer and need to take
// the sets' addressed and dereference them as pointers to some comparable
// type.
func Same(s1, s2 Set) bool {
return *(*uintptr)(unsafe.Pointer(&s1)) == *(*uintptr)(unsafe.Pointer(&s2))
}
// A set is a set of nodes keyed in their integer identifiers.
type Set map[int]graph.Node
// The simple accessor methods for Set are provided to allow ease of
// implementation change should the need arise.
// Add inserts an element into the set.
func (s Set) Add(n graph.Node) {
s[n.ID()] = n
}
// Remove deletes the specified element from the set.
func (s Set) Remove(e graph.Node) {
delete(s, e.ID())
}
// Has reports the existence of the element in the set.
func (s Set) Has(n graph.Node) bool {
_, ok := s[n.ID()]
return ok
}
// Clear returns an empty set, possibly using the same backing store.
// Clear is not provided as a method since there is no way to replace
// the calling value if clearing is performed by a make(set). Clear
// should never be called without keeping the returned value.
func Clear(s Set) Set {
if len(s) == 0 {
return s
}
return make(Set)
}
// Copy performs a perfect copy from s1 to dst (meaning the sets will
// be equal).
func (dst Set) Copy(src Set) Set {
if Same(src, dst) {
return dst
}
if len(dst) > 0 {
dst = make(Set, len(src))
}
for e, n := range src {
dst[e] = n
}
return dst
}
// Equal reports set equality between the parameters. Sets are equal if
// and only if they have the same elements.
func Equal(s1, s2 Set) bool {
if Same(s1, s2) {
return true
}
if len(s1) != len(s2) {
return false
}
for e := range s1 {
if _, ok := s2[e]; !ok {
return false
}
}
return true
}
// Union takes the union of s1 and s2, and stores it in dst.
//
// The union of two sets, s1 and s2, is the set containing all the
// elements of each, for instance:
//
// {a,b,c} UNION {d,e,f} = {a,b,c,d,e,f}
//
// Since sets may not have repetition, unions of two sets that overlap
// do not contain repeat elements, that is:
//
// {a,b,c} UNION {b,c,d} = {a,b,c,d}
//
func (dst Set) Union(s1, s2 Set) Set {
if Same(s1, s2) {
return dst.Copy(s1)
}
if !Same(s1, dst) && !Same(s2, dst) {
dst = Clear(dst)
}
if !Same(dst, s1) {
for e, n := range s1 {
dst[e] = n
}
}
if !Same(dst, s2) {
for e, n := range s2 {
dst[e] = n
}
}
return dst
}
// Intersect takes the intersection of s1 and s2, and stores it in dst.
//
// The intersection of two sets, s1 and s2, is the set containing all
// the elements shared between the two sets, for instance:
//
// {a,b,c} INTERSECT {b,c,d} = {b,c}
//
// The intersection between a set and itself is itself, and thus
// effectively a copy operation:
//
// {a,b,c} INTERSECT {a,b,c} = {a,b,c}
//
// The intersection between two sets that share no elements is the empty
// set:
//
// {a,b,c} INTERSECT {d,e,f} = {}
//
func (dst Set) Intersect(s1, s2 Set) Set {
var swap Set
if Same(s1, s2) {
return dst.Copy(s1)
}
if Same(s1, dst) {
swap = s2
} else if Same(s2, dst) {
swap = s1
} else {
dst = Clear(dst)
if len(s1) > len(s2) {
s1, s2 = s2, s1
}
for e, n := range s1 {
if _, ok := s2[e]; ok {
dst[e] = n
}
}
return dst
}
for e := range dst {
if _, ok := swap[e]; !ok {
delete(dst, e)
}
}
return dst
}
+28
View File
@@ -0,0 +1,28 @@
// 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 internal
// BySliceValues implements the sort.Interface sorting a slice of
// []int lexically by the values of the []int.
type BySliceValues [][]int
func (c BySliceValues) Len() int { return len(c) }
func (c BySliceValues) Less(i, j int) bool {
a, b := c[i], c[j]
l := len(a)
if len(b) < l {
l = len(b)
}
for k, v := range a[:l] {
if v < b[k] {
return true
}
if v > b[k] {
return false
}
}
return len(a) < len(b)
}
func (c BySliceValues) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
+157
View File
@@ -0,0 +1,157 @@
// Copyright ©2014 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 path
import (
"container/heap"
"github.com/gonum/graph"
"github.com/gonum/graph/internal"
)
// Heuristic returns an estimate of the cost of travelling between two nodes.
type Heuristic func(x, y graph.Node) float64
// HeuristicCoster wraps the HeuristicCost method. A graph implementing the
// interface provides a heuristic between any two given nodes.
type HeuristicCoster interface {
HeuristicCost(x, y graph.Node) float64
}
// AStar finds the A*-shortest path from s to t in g using the heuristic h. The path and
// its cost are returned in a Shortest along with paths and costs to all nodes explored
// during the search. The number of expanded nodes is also returned. This value may help
// with heuristic tuning.
//
// The path will be the shortest path if the heuristic is admissible. A heuristic is
// admissible if for any node, n, in the graph, the heuristic estimate of the cost of
// the path from n to t is less than or equal to the true cost of that path.
//
// If h is nil, AStar will use the g.HeuristicCost method if g implements HeuristicCoster,
// falling back to NullHeuristic otherwise. If the graph does not implement graph.Weighter,
// graph.UniformCost is used. AStar will panic if g has an A*-reachable negative edge weight.
func AStar(s, t graph.Node, g graph.Graph, h Heuristic) (path Shortest, expanded int) {
if !g.Has(s) || !g.Has(t) {
return Shortest{from: s}, 0
}
var weight graph.WeightFunc
if g, ok := g.(graph.Weighter); ok {
weight = g.Weight
} else {
weight = graph.UniformCost
}
if h == nil {
if g, ok := g.(HeuristicCoster); ok {
h = g.HeuristicCost
} else {
h = NullHeuristic
}
}
path = newShortestFrom(s, g.Nodes())
tid := t.ID()
visited := make(internal.IntSet)
open := &aStarQueue{indexOf: make(map[int]int)}
heap.Push(open, aStarNode{node: s, gscore: 0, fscore: h(s, t)})
for open.Len() != 0 {
u := heap.Pop(open).(aStarNode)
uid := u.node.ID()
i := path.indexOf[uid]
expanded++
if uid == tid {
break
}
visited.Add(uid)
for _, v := range g.From(u.node) {
vid := v.ID()
if visited.Has(vid) {
continue
}
j := path.indexOf[vid]
w := weight(g.Edge(u.node, v))
if w < 0 {
panic("A*: negative edge weight")
}
g := u.gscore + w
if n, ok := open.node(vid); !ok {
path.set(j, g, i)
heap.Push(open, aStarNode{node: v, gscore: g, fscore: g + h(v, t)})
} else if g < n.gscore {
path.set(j, g, i)
open.update(vid, g, g+h(v, t))
}
}
}
return path, expanded
}
// NullHeuristic is an admissible, consistent heuristic that will not speed up computation.
func NullHeuristic(_, _ graph.Node) float64 {
return 0
}
// aStarNode adds A* accounting to a graph.Node.
type aStarNode struct {
node graph.Node
gscore float64
fscore float64
}
// aStarQueue is an A* priority queue.
type aStarQueue struct {
indexOf map[int]int
nodes []aStarNode
}
func (q *aStarQueue) Less(i, j int) bool {
return q.nodes[i].fscore < q.nodes[j].fscore
}
func (q *aStarQueue) Swap(i, j int) {
q.indexOf[q.nodes[i].node.ID()] = j
q.indexOf[q.nodes[j].node.ID()] = i
q.nodes[i], q.nodes[j] = q.nodes[j], q.nodes[i]
}
func (q *aStarQueue) Len() int {
return len(q.nodes)
}
func (q *aStarQueue) Push(x interface{}) {
n := x.(aStarNode)
q.indexOf[n.node.ID()] = len(q.nodes)
q.nodes = append(q.nodes, n)
}
func (q *aStarQueue) Pop() interface{} {
n := q.nodes[len(q.nodes)-1]
q.nodes = q.nodes[:len(q.nodes)-1]
delete(q.indexOf, n.node.ID())
return n
}
func (q *aStarQueue) update(id int, g, f float64) {
i, ok := q.indexOf[id]
if !ok {
return
}
q.nodes[i].gscore = g
q.nodes[i].fscore = f
heap.Fix(q, i)
}
func (q *aStarQueue) node(id int) (aStarNode, bool) {
loc, ok := q.indexOf[id]
if ok {
return q.nodes[loc], true
}
return aStarNode{}, false
}
+59
View File
@@ -0,0 +1,59 @@
// 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 path
import "github.com/gonum/graph"
// BellmanFordFrom returns a shortest-path tree for a shortest path from u to all nodes in
// the graph g, or false indicating that a negative cycle exists in the graph. If the graph
// does not implement graph.Weighter, graph.UniformCost is used.
//
// The time complexity of BellmanFordFrom is O(|V|.|E|).
func BellmanFordFrom(u graph.Node, g graph.Graph) (path Shortest, ok bool) {
if !g.Has(u) {
return Shortest{from: u}, true
}
var weight graph.WeightFunc
if g, ok := g.(graph.Weighter); ok {
weight = g.Weight
} else {
weight = graph.UniformCost
}
nodes := g.Nodes()
path = newShortestFrom(u, nodes)
path.dist[path.indexOf[u.ID()]] = 0
// TODO(kortschak): Consider adding further optimisations
// from http://arxiv.org/abs/1111.5414.
for i := 1; i < len(nodes); i++ {
changed := false
for j, u := range nodes {
for _, v := range g.From(u) {
k := path.indexOf[v.ID()]
joint := path.dist[j] + weight(g.Edge(u, v))
if joint < path.dist[k] {
path.set(k, joint, j)
changed = true
}
}
}
if !changed {
break
}
}
for j, u := range nodes {
for _, v := range g.From(u) {
k := path.indexOf[v.ID()]
if path.dist[j]+weight(g.Edge(u, v)) < path.dist[k] {
return path, false
}
}
}
return path, true
}
+118
View File
@@ -0,0 +1,118 @@
// Copyright ©2014 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 path
import (
"github.com/gonum/graph"
"github.com/gonum/graph/internal"
)
// PostDominatores returns all dominators for all nodes in g. It does not
// prune for strict post-dominators, immediate dominators etc.
//
// A dominates B if and only if the only path through B travels through A.
func Dominators(start graph.Node, g graph.Graph) map[int]internal.Set {
allNodes := make(internal.Set)
nlist := g.Nodes()
dominators := make(map[int]internal.Set, len(nlist))
for _, node := range nlist {
allNodes.Add(node)
}
var to func(graph.Node) []graph.Node
switch g := g.(type) {
case graph.Directed:
to = g.To
default:
to = g.From
}
for _, node := range nlist {
dominators[node.ID()] = make(internal.Set)
if node.ID() == start.ID() {
dominators[node.ID()].Add(start)
} else {
dominators[node.ID()].Copy(allNodes)
}
}
for somethingChanged := true; somethingChanged; {
somethingChanged = false
for _, node := range nlist {
if node.ID() == start.ID() {
continue
}
preds := to(node)
if len(preds) == 0 {
continue
}
tmp := make(internal.Set).Copy(dominators[preds[0].ID()])
for _, pred := range preds[1:] {
tmp.Intersect(tmp, dominators[pred.ID()])
}
dom := make(internal.Set)
dom.Add(node)
dom.Union(dom, tmp)
if !internal.Equal(dom, dominators[node.ID()]) {
dominators[node.ID()] = dom
somethingChanged = true
}
}
}
return dominators
}
// PostDominatores returns all post-dominators for all nodes in g. It does not
// prune for strict post-dominators, immediate post-dominators etc.
//
// A post-dominates B if and only if all paths from B travel through A.
func PostDominators(end graph.Node, g graph.Graph) map[int]internal.Set {
allNodes := make(internal.Set)
nlist := g.Nodes()
dominators := make(map[int]internal.Set, len(nlist))
for _, node := range nlist {
allNodes.Add(node)
}
for _, node := range nlist {
dominators[node.ID()] = make(internal.Set)
if node.ID() == end.ID() {
dominators[node.ID()].Add(end)
} else {
dominators[node.ID()].Copy(allNodes)
}
}
for somethingChanged := true; somethingChanged; {
somethingChanged = false
for _, node := range nlist {
if node.ID() == end.ID() {
continue
}
succs := g.From(node)
if len(succs) == 0 {
continue
}
tmp := make(internal.Set).Copy(dominators[succs[0].ID()])
for _, succ := range succs[1:] {
tmp.Intersect(tmp, dominators[succ.ID()])
}
dom := make(internal.Set)
dom.Add(node)
dom.Union(dom, tmp)
if !internal.Equal(dom, dominators[node.ID()]) {
dominators[node.ID()] = dom
somethingChanged = true
}
}
}
return dominators
}
+136
View File
@@ -0,0 +1,136 @@
// 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 path
import (
"container/heap"
"github.com/gonum/graph"
)
// DijkstraFrom returns a shortest-path tree for a shortest path from u to all nodes in
// the graph g. If the graph does not implement graph.Weighter, graph.UniformCost is used.
// DijkstraFrom will panic if g has a u-reachable negative edge weight.
//
// The time complexity of DijkstrFrom is O(|E|+|V|.log|V|).
func DijkstraFrom(u graph.Node, g graph.Graph) Shortest {
if !g.Has(u) {
return Shortest{from: u}
}
var weight graph.WeightFunc
if g, ok := g.(graph.Weighter); ok {
weight = g.Weight
} else {
weight = graph.UniformCost
}
nodes := g.Nodes()
path := newShortestFrom(u, nodes)
// Dijkstra's algorithm here is implemented essentially as
// described in Function B.2 in figure 6 of UTCS Technical
// Report TR-07-54.
//
// http://www.cs.utexas.edu/ftp/techreports/tr07-54.pdf
Q := priorityQueue{{node: u, dist: 0}}
for Q.Len() != 0 {
mid := heap.Pop(&Q).(distanceNode)
k := path.indexOf[mid.node.ID()]
if mid.dist < path.dist[k] {
path.dist[k] = mid.dist
}
for _, v := range g.From(mid.node) {
j := path.indexOf[v.ID()]
w := weight(g.Edge(mid.node, v))
if w < 0 {
panic("dijkstra: negative edge weight")
}
joint := path.dist[k] + w
if joint < path.dist[j] {
heap.Push(&Q, distanceNode{node: v, dist: joint})
path.set(j, joint, k)
}
}
}
return path
}
// DijkstraAllPaths returns a shortest-path tree for shortest paths in the graph g.
// If the graph does not implement graph.Weighter, graph.UniformCost is used.
// DijkstraAllPaths will panic if g has a negative edge weight.
//
// The time complexity of DijkstrAllPaths is O(|V|.|E|+|V|^2.log|V|).
func DijkstraAllPaths(g graph.Graph) (paths AllShortest) {
paths = newAllShortest(g.Nodes(), false)
dijkstraAllPaths(g, paths)
return paths
}
// dijkstraAllPaths is the all-paths implementation of Dijkstra. It is shared
// between DijkstraAllPaths and JohnsonAllPaths to avoid repeated allocation
// of the nodes slice and the indexOf map. It returns nothing, but stores the
// result of the work in the paths parameter which is a reference type.
func dijkstraAllPaths(g graph.Graph, paths AllShortest) {
var weight graph.WeightFunc
if g, ok := g.(graph.Weighter); ok {
weight = g.Weight
} else {
weight = graph.UniformCost
}
var Q priorityQueue
for i, u := range paths.nodes {
// Dijkstra's algorithm here is implemented essentially as
// described in Function B.2 in figure 6 of UTCS Technical
// Report TR-07-54 with the addition of handling multiple
// co-equal paths.
//
// http://www.cs.utexas.edu/ftp/techreports/tr07-54.pdf
// Q must be empty at this point.
heap.Push(&Q, distanceNode{node: u, dist: 0})
for Q.Len() != 0 {
mid := heap.Pop(&Q).(distanceNode)
k := paths.indexOf[mid.node.ID()]
if mid.dist < paths.dist.At(i, k) {
paths.dist.Set(i, k, mid.dist)
}
for _, v := range g.From(mid.node) {
j := paths.indexOf[v.ID()]
w := weight(g.Edge(mid.node, v))
if w < 0 {
panic("dijkstra: negative edge weight")
}
joint := paths.dist.At(i, k) + w
if joint < paths.dist.At(i, j) {
heap.Push(&Q, distanceNode{node: v, dist: joint})
paths.set(i, j, joint, k)
} else if joint == paths.dist.At(i, j) {
paths.add(i, j, k)
}
}
}
}
}
type distanceNode struct {
node graph.Node
dist float64
}
// priorityQueue implements a no-dec priority queue.
type priorityQueue []distanceNode
func (q priorityQueue) Len() int { return len(q) }
func (q priorityQueue) Less(i, j int) bool { return q[i].dist < q[j].dist }
func (q priorityQueue) Swap(i, j int) { q[i], q[j] = q[j], q[i] }
func (q *priorityQueue) Push(n interface{}) { *q = append(*q, n.(distanceNode)) }
func (q *priorityQueue) Pop() interface{} {
t := *q
var n interface{}
n, *q = t[len(t)-1], t[:len(t)-1]
return n
}
+87
View File
@@ -0,0 +1,87 @@
// Copyright ©2014 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 path
// A disjoint set is a collection of non-overlapping sets. That is, for any two sets in the
// disjoint set, their intersection is the empty set.
//
// A disjoint set has three principle operations: Make Set, Find, and Union.
//
// Make set creates a new set for an element (presuming it does not already exist in any set in
// the disjoint set), Find finds the set containing that element (if any), and Union merges two
// sets in the disjoint set. In general, algorithms operating on disjoint sets are "union-find"
// algorithms, where two sets are found with Find, and then joined with Union.
//
// A concrete example of a union-find algorithm can be found as discrete.Kruskal -- which unions
// two sets when an edge is created between two vertices, and refuses to make an edge between two
// vertices if they're part of the same set.
type disjointSet struct {
master map[int]*disjointSetNode
}
type disjointSetNode struct {
parent *disjointSetNode
rank int
}
func newDisjointSet() *disjointSet {
return &disjointSet{master: make(map[int]*disjointSetNode)}
}
// If the element isn't already somewhere in there, adds it to the master set and its own tiny set.
func (ds *disjointSet) makeSet(e int) {
if _, ok := ds.master[e]; ok {
return
}
dsNode := &disjointSetNode{rank: 0}
dsNode.parent = dsNode
ds.master[e] = dsNode
}
// Returns the set the element belongs to, or nil if none.
func (ds *disjointSet) find(e int) *disjointSetNode {
dsNode, ok := ds.master[e]
if !ok {
return nil
}
return find(dsNode)
}
func find(dsNode *disjointSetNode) *disjointSetNode {
if dsNode.parent != dsNode {
dsNode.parent = find(dsNode.parent)
}
return dsNode.parent
}
// Unions two subsets within the disjointSet.
//
// If x or y are not in this disjoint set, the behavior is undefined. If either pointer is nil,
// this function will panic.
func (ds *disjointSet) union(x, y *disjointSetNode) {
if x == nil || y == nil {
panic("Disjoint Set union on nil sets")
}
xRoot := find(x)
yRoot := find(y)
if xRoot == nil || yRoot == nil {
return
}
if xRoot == yRoot {
return
}
if xRoot.rank < yRoot.rank {
xRoot.parent = yRoot
} else if yRoot.rank < xRoot.rank {
yRoot.parent = xRoot
} else {
yRoot.parent = xRoot
xRoot.rank += 1
}
}
+55
View File
@@ -0,0 +1,55 @@
// 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 path
import "github.com/gonum/graph"
// FloydWarshall returns a shortest-path tree for the graph g or false indicating
// that a negative cycle exists in the graph. If the graph does not implement
// graph.Weighter, graph.UniformCost is used.
//
// The time complexity of FloydWarshall is O(|V|^3).
func FloydWarshall(g graph.Graph) (paths AllShortest, ok bool) {
var weight graph.WeightFunc
if g, ok := g.(graph.Weighter); ok {
weight = g.Weight
} else {
weight = graph.UniformCost
}
nodes := g.Nodes()
paths = newAllShortest(nodes, true)
for i, u := range nodes {
paths.dist.Set(i, i, 0)
for _, v := range g.From(u) {
j := paths.indexOf[v.ID()]
paths.set(i, j, weight(g.Edge(u, v)), j)
}
}
for k := range nodes {
for i := range nodes {
for j := range nodes {
ij := paths.dist.At(i, j)
joint := paths.dist.At(i, k) + paths.dist.At(k, j)
if ij > joint {
paths.set(i, j, joint, paths.at(i, k)...)
} else if ij-joint == 0 {
paths.add(i, j, paths.at(i, k)...)
}
}
}
}
ok = true
for i := range nodes {
if paths.dist.At(i, i) < 0 {
ok = false
break
}
}
return paths, ok
}
+137
View File
@@ -0,0 +1,137 @@
// 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 path
import (
"math"
"math/rand"
"github.com/gonum/graph"
"github.com/gonum/graph/concrete"
)
// JohnsonAllPaths returns a shortest-path tree for shortest paths in the graph g.
// If the graph does not implement graph.Weighter, graph.UniformCost is used.
//
// The time complexity of JohnsonAllPaths is O(|V|.|E|+|V|^2.log|V|).
func JohnsonAllPaths(g graph.Graph) (paths AllShortest, ok bool) {
jg := johnsonWeightAdjuster{
g: g,
from: g.From,
edgeTo: g.Edge,
}
if g, ok := g.(graph.Weighter); ok {
jg.weight = g.Weight
} else {
jg.weight = graph.UniformCost
}
paths = newAllShortest(g.Nodes(), false)
sign := -1
for {
// Choose a random node ID until we find
// one that is not in g.
jg.q = sign * rand.Int()
if _, exists := paths.indexOf[jg.q]; !exists {
break
}
sign *= -1
}
jg.bellmanFord = true
jg.adjustBy, ok = BellmanFordFrom(johnsonGraphNode(jg.q), jg)
if !ok {
return paths, false
}
jg.bellmanFord = false
dijkstraAllPaths(jg, paths)
for i, u := range paths.nodes {
hu := jg.adjustBy.WeightTo(u)
for j, v := range paths.nodes {
if i == j {
continue
}
hv := jg.adjustBy.WeightTo(v)
paths.dist.Set(i, j, paths.dist.At(i, j)-hu+hv)
}
}
return paths, ok
}
type johnsonWeightAdjuster struct {
q int
g graph.Graph
from func(graph.Node) []graph.Node
edgeTo func(graph.Node, graph.Node) graph.Edge
weight graph.WeightFunc
bellmanFord bool
adjustBy Shortest
}
var (
// johnsonWeightAdjuster has the behaviour
// of a directed graph, but we don't need
// to be explicit with the type since it
// is not exported.
_ graph.Graph = johnsonWeightAdjuster{}
_ graph.Weighter = johnsonWeightAdjuster{}
)
func (g johnsonWeightAdjuster) Has(n graph.Node) bool {
if g.bellmanFord && n.ID() == g.q {
return true
}
return g.g.Has(n)
}
func (g johnsonWeightAdjuster) Nodes() []graph.Node {
if g.bellmanFord {
return append(g.g.Nodes(), johnsonGraphNode(g.q))
}
return g.g.Nodes()
}
func (g johnsonWeightAdjuster) From(n graph.Node) []graph.Node {
if g.bellmanFord && n.ID() == g.q {
return g.g.Nodes()
}
return g.from(n)
}
func (g johnsonWeightAdjuster) Edge(u, v graph.Node) graph.Edge {
if g.bellmanFord && u.ID() == g.q && g.g.Has(v) {
return concrete.Edge{johnsonGraphNode(g.q), v}
}
return g.edgeTo(u, v)
}
func (g johnsonWeightAdjuster) Weight(e graph.Edge) float64 {
if g.bellmanFord {
switch g.q {
case e.From().ID():
return 0
case e.To().ID():
return math.Inf(1)
default:
return g.weight(e)
}
}
return g.weight(e) + g.adjustBy.WeightTo(e.From()) - g.adjustBy.WeightTo(e.To())
}
func (johnsonWeightAdjuster) HasEdge(_, _ graph.Node) bool {
panic("search: unintended use of johnsonWeightAdjuster")
}
type johnsonGraphNode int
func (n johnsonGraphNode) ID() int { return int(n) }
+319
View File
@@ -0,0 +1,319 @@
// 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 path
import (
"math"
"math/rand"
"github.com/gonum/graph"
"github.com/gonum/matrix/mat64"
)
// Shortest is a shortest-path tree created by the BellmanFordFrom or DijkstraFrom
// single-source shortest path functions.
type Shortest struct {
// from holds the source node given to
// DijkstraFrom.
from graph.Node
// nodes hold the nodes of the analysed
// graph.
nodes []graph.Node
// indexOf contains a mapping between
// the id-dense representation of the
// graph and the potentially id-sparse
// nodes held in nodes.
indexOf map[int]int
// dist and next represent the shortest
// paths between nodes.
//
// Indices into dist and next are
// mapped through indexOf.
//
// dist contains the distances
// from the from node for each
// node in the graph.
dist []float64
// next contains the shortest-path
// tree of the graph. The index is a
// linear mapping of to-dense-id.
next []int
}
func newShortestFrom(u graph.Node, nodes []graph.Node) Shortest {
indexOf := make(map[int]int, len(nodes))
uid := u.ID()
for i, n := range nodes {
indexOf[n.ID()] = i
if n.ID() == uid {
u = n
}
}
p := Shortest{
from: u,
nodes: nodes,
indexOf: indexOf,
dist: make([]float64, len(nodes)),
next: make([]int, len(nodes)),
}
for i := range nodes {
p.dist[i] = math.Inf(1)
p.next[i] = -1
}
p.dist[indexOf[uid]] = 0
return p
}
func (p Shortest) set(to int, weight float64, mid int) {
p.dist[to] = weight
p.next[to] = mid
}
// From returns the starting node of the paths held by the Shortest.
func (p Shortest) From() graph.Node { return p.from }
// WeightTo returns the weight of the minimum path to v.
func (p Shortest) WeightTo(v graph.Node) float64 {
to, toOK := p.indexOf[v.ID()]
if !toOK {
return math.Inf(1)
}
return p.dist[to]
}
// To returns a shortest path to v and the weight of the path.
func (p Shortest) To(v graph.Node) (path []graph.Node, weight float64) {
to, toOK := p.indexOf[v.ID()]
if !toOK || math.IsInf(p.dist[to], 1) {
return nil, math.Inf(1)
}
from := p.indexOf[p.from.ID()]
path = []graph.Node{p.nodes[to]}
for to != from {
path = append(path, p.nodes[p.next[to]])
to = p.next[to]
}
reverse(path)
return path, p.dist[p.indexOf[v.ID()]]
}
// AllShortest is a shortest-path tree created by the DijkstraAllPaths, FloydWarshall
// or JohnsonAllPaths all-pairs shortest paths functions.
type AllShortest struct {
// nodes hold the nodes of the analysed
// graph.
nodes []graph.Node
// indexOf contains a mapping between
// the id-dense representation of the
// graph and the potentially id-sparse
// nodes held in nodes.
indexOf map[int]int
// dist, next and forward represent
// the shortest paths between nodes.
//
// Indices into dist and next are
// mapped through indexOf.
//
// dist contains the pairwise
// distances between nodes.
dist *mat64.Dense
// next contains the shortest-path
// tree of the graph. The first index
// is a linear mapping of from-dense-id
// and to-dense-id, to-major with a
// stride equal to len(nodes); the
// slice indexed to is the list of
// intermediates leading from the 'from'
// node to the 'to' node represented
// by dense id.
// The interpretation of next is
// dependent on the state of forward.
next [][]int
// forward indicates the direction of
// path reconstruction. Forward
// reconstruction is used for Floyd-
// Warshall and reverse is used for
// Dijkstra.
forward bool
}
func newAllShortest(nodes []graph.Node, forward bool) AllShortest {
indexOf := make(map[int]int, len(nodes))
for i, n := range nodes {
indexOf[n.ID()] = i
}
dist := make([]float64, len(nodes)*len(nodes))
for i := range dist {
dist[i] = math.Inf(1)
}
return AllShortest{
nodes: nodes,
indexOf: indexOf,
dist: mat64.NewDense(len(nodes), len(nodes), dist),
next: make([][]int, len(nodes)*len(nodes)),
forward: forward,
}
}
func (p AllShortest) at(from, to int) (mid []int) {
return p.next[from+to*len(p.nodes)]
}
func (p AllShortest) set(from, to int, weight float64, mid ...int) {
p.dist.Set(from, to, weight)
p.next[from+to*len(p.nodes)] = append(p.next[from+to*len(p.nodes)][:0], mid...)
}
func (p AllShortest) add(from, to int, mid ...int) {
loop: // These are likely to be rare, so just loop over collisions.
for _, k := range mid {
for _, v := range p.next[from+to*len(p.nodes)] {
if k == v {
continue loop
}
}
p.next[from+to*len(p.nodes)] = append(p.next[from+to*len(p.nodes)], k)
}
}
// Weight returns the weight of the minimum path between u and v.
func (p AllShortest) Weight(u, v graph.Node) float64 {
from, fromOK := p.indexOf[u.ID()]
to, toOK := p.indexOf[v.ID()]
if !fromOK || !toOK {
return math.Inf(1)
}
return p.dist.At(from, to)
}
// Between returns a shortest path from u to v and the weight of the path. If more than
// one shortest path exists between u and v, a randomly chosen path will be returned and
// unique is returned false. If a cycle with zero weight exists in the path, it will not
// be included, but unique will be returned false.
func (p AllShortest) Between(u, v graph.Node) (path []graph.Node, weight float64, unique bool) {
from, fromOK := p.indexOf[u.ID()]
to, toOK := p.indexOf[v.ID()]
if !fromOK || !toOK || len(p.at(from, to)) == 0 {
if u.ID() == v.ID() {
return []graph.Node{p.nodes[from]}, 0, true
}
return nil, math.Inf(1), false
}
seen := make([]int, len(p.nodes))
for i := range seen {
seen[i] = -1
}
var n graph.Node
if p.forward {
n = p.nodes[from]
seen[from] = 0
} else {
n = p.nodes[to]
seen[to] = 0
}
path = []graph.Node{n}
weight = p.dist.At(from, to)
unique = true
var next int
for from != to {
c := p.at(from, to)
if len(c) != 1 {
unique = false
next = c[rand.Intn(len(c))]
} else {
next = c[0]
}
if seen[next] >= 0 {
path = path[:seen[next]]
}
seen[next] = len(path)
path = append(path, p.nodes[next])
if p.forward {
from = next
} else {
to = next
}
}
if !p.forward {
reverse(path)
}
return path, weight, unique
}
// AllBetween returns all shortest paths from u to v and the weight of the paths. Paths
// containing zero-weight cycles are not returned.
func (p AllShortest) AllBetween(u, v graph.Node) (paths [][]graph.Node, weight float64) {
from, fromOK := p.indexOf[u.ID()]
to, toOK := p.indexOf[v.ID()]
if !fromOK || !toOK || len(p.at(from, to)) == 0 {
if u.ID() == v.ID() {
return [][]graph.Node{{p.nodes[from]}}, 0
}
return nil, math.Inf(1)
}
var n graph.Node
if p.forward {
n = u
} else {
n = v
}
seen := make([]bool, len(p.nodes))
paths = p.allBetween(from, to, seen, []graph.Node{n}, nil)
return paths, p.dist.At(from, to)
}
func (p AllShortest) allBetween(from, to int, seen []bool, path []graph.Node, paths [][]graph.Node) [][]graph.Node {
if p.forward {
seen[from] = true
} else {
seen[to] = true
}
if from == to {
if path == nil {
return paths
}
if !p.forward {
reverse(path)
}
return append(paths, path)
}
first := true
for _, n := range p.at(from, to) {
if seen[n] {
continue
}
if first {
path = append([]graph.Node(nil), path...)
first = false
}
if p.forward {
from = n
} else {
to = n
}
paths = p.allBetween(from, to, append([]bool(nil), seen...), append(path, p.nodes[n]), paths)
}
return paths
}
func reverse(p []graph.Node) {
for i, j := 0, len(p)-1; i < j; i, j = i+1, j-1 {
p[i], p[j] = p[j], p[i]
}
}
+108
View File
@@ -0,0 +1,108 @@
// Copyright ©2014 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 path
import (
"sort"
"github.com/gonum/graph"
"github.com/gonum/graph/concrete"
"github.com/gonum/graph/internal"
)
// EdgeListerGraph is an undirected graph than returns its complete set of edges.
type EdgeListerGraph interface {
graph.Undirected
Edges() []graph.Edge
}
// Prim generates a minimum spanning tree of g by greedy tree extension, placing
// the result in the destination. The destination is not cleared first.
func Prim(dst graph.MutableUndirected, g EdgeListerGraph) {
var weight graph.WeightFunc
if g, ok := g.(graph.Weighter); ok {
weight = g.Weight
} else {
weight = graph.UniformCost
}
nlist := g.Nodes()
if nlist == nil || len(nlist) == 0 {
return
}
dst.AddNode(nlist[0])
remainingNodes := make(internal.IntSet)
for _, node := range nlist[1:] {
remainingNodes.Add(node.ID())
}
edgeList := g.Edges()
for remainingNodes.Count() != 0 {
var edges []concrete.WeightedEdge
for _, edge := range edgeList {
if (dst.Has(edge.From()) && remainingNodes.Has(edge.To().ID())) ||
(dst.Has(edge.To()) && remainingNodes.Has(edge.From().ID())) {
edges = append(edges, concrete.WeightedEdge{Edge: edge, Cost: weight(edge)})
}
}
sort.Sort(byWeight(edges))
myEdge := edges[0]
dst.SetEdge(myEdge.Edge, myEdge.Cost)
remainingNodes.Remove(myEdge.Edge.From().ID())
}
}
// Kruskal generates a minimum spanning tree of g by greedy tree coalesence, placing
// the result in the destination. The destination is not cleared first.
func Kruskal(dst graph.MutableUndirected, g EdgeListerGraph) {
var weight graph.WeightFunc
if g, ok := g.(graph.Weighter); ok {
weight = g.Weight
} else {
weight = graph.UniformCost
}
edgeList := g.Edges()
edges := make([]concrete.WeightedEdge, 0, len(edgeList))
for _, edge := range edgeList {
edges = append(edges, concrete.WeightedEdge{Edge: edge, Cost: weight(edge)})
}
sort.Sort(byWeight(edges))
ds := newDisjointSet()
for _, node := range g.Nodes() {
ds.makeSet(node.ID())
}
for _, edge := range edges {
// The disjoint set doesn't really care for which is head and which is tail so this
// should work fine without checking both ways
if s1, s2 := ds.find(edge.Edge.From().ID()), ds.find(edge.Edge.To().ID()); s1 != s2 {
ds.union(s1, s2)
dst.SetEdge(edge.Edge, edge.Cost)
}
}
}
type byWeight []concrete.WeightedEdge
func (e byWeight) Len() int {
return len(e)
}
func (e byWeight) Less(i, j int) bool {
return e[i].Cost < e[j].Cost
}
func (e byWeight) Swap(i, j int) {
e[i], e[j] = e[j], e[i]
}
+225
View File
@@ -0,0 +1,225 @@
// 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 topo
import (
"github.com/gonum/graph"
"github.com/gonum/graph/internal"
)
// VertexOrdering returns the vertex ordering and the k-cores of
// the undirected graph g.
func VertexOrdering(g graph.Undirected) (order []graph.Node, cores [][]graph.Node) {
nodes := g.Nodes()
// The algorithm used here is essentially as described at
// http://en.wikipedia.org/w/index.php?title=Degeneracy_%28graph_theory%29&oldid=640308710
// Initialize an output list L.
var l []graph.Node
// Compute a number d_v for each vertex v in G,
// the number of neighbors of v that are not already in L.
// Initially, these numbers are just the degrees of the vertices.
dv := make(map[int]int, len(nodes))
var (
maxDegree int
neighbours = make(map[int][]graph.Node)
)
for _, n := range nodes {
adj := g.From(n)
neighbours[n.ID()] = adj
dv[n.ID()] = len(adj)
if len(adj) > maxDegree {
maxDegree = len(adj)
}
}
// Initialize an array D such that D[i] contains a list of the
// vertices v that are not already in L for which d_v = i.
d := make([][]graph.Node, maxDegree+1)
for _, n := range nodes {
deg := dv[n.ID()]
d[deg] = append(d[deg], n)
}
// Initialize k to 0.
k := 0
// Repeat n times:
s := []int{0}
for _ = range nodes { // TODO(kortschak): Remove blank assignment when go1.3.3 is no longer supported.
// Scan the array cells D[0], D[1], ... until
// finding an i for which D[i] is nonempty.
var (
i int
di []graph.Node
)
for i, di = range d {
if len(di) != 0 {
break
}
}
// Set k to max(k,i).
if i > k {
k = i
s = append(s, make([]int, k-len(s)+1)...)
}
// Select a vertex v from D[i]. Add v to the
// beginning of L and remove it from D[i].
var v graph.Node
v, d[i] = di[len(di)-1], di[:len(di)-1]
l = append(l, v)
s[k]++
delete(dv, v.ID())
// For each neighbor w of v not already in L,
// subtract one from d_w and move w to the
// cell of D corresponding to the new value of d_w.
for _, w := range neighbours[v.ID()] {
dw, ok := dv[w.ID()]
if !ok {
continue
}
for i, n := range d[dw] {
if n.ID() == w.ID() {
d[dw][i], d[dw] = d[dw][len(d[dw])-1], d[dw][:len(d[dw])-1]
dw--
d[dw] = append(d[dw], w)
break
}
}
dv[w.ID()] = dw
}
}
for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {
l[i], l[j] = l[j], l[i]
}
cores = make([][]graph.Node, len(s))
offset := len(l)
for i, n := range s {
cores[i] = l[offset-n : offset]
offset -= n
}
return l, cores
}
// BronKerbosch returns the set of maximal cliques of the undirected graph g.
func BronKerbosch(g graph.Undirected) [][]graph.Node {
nodes := g.Nodes()
// The algorithm used here is essentially BronKerbosch3 as described at
// http://en.wikipedia.org/w/index.php?title=Bron%E2%80%93Kerbosch_algorithm&oldid=656805858
p := make(internal.Set, len(nodes))
for _, n := range nodes {
p.Add(n)
}
x := make(internal.Set)
var bk bronKerbosch
order, _ := VertexOrdering(g)
for _, v := range order {
neighbours := g.From(v)
nv := make(internal.Set, len(neighbours))
for _, n := range neighbours {
nv.Add(n)
}
bk.maximalCliquePivot(g, []graph.Node{v}, make(internal.Set).Intersect(p, nv), make(internal.Set).Intersect(x, nv))
p.Remove(v)
x.Add(v)
}
return bk
}
type bronKerbosch [][]graph.Node
func (bk *bronKerbosch) maximalCliquePivot(g graph.Undirected, r []graph.Node, p, x internal.Set) {
if len(p) == 0 && len(x) == 0 {
*bk = append(*bk, r)
return
}
neighbours := bk.choosePivotFrom(g, p, x)
nu := make(internal.Set, len(neighbours))
for _, n := range neighbours {
nu.Add(n)
}
for _, v := range p {
if nu.Has(v) {
continue
}
neighbours := g.From(v)
nv := make(internal.Set, len(neighbours))
for _, n := range neighbours {
nv.Add(n)
}
var found bool
for _, n := range r {
if n.ID() == v.ID() {
found = true
break
}
}
var sr []graph.Node
if !found {
sr = append(r[:len(r):len(r)], v)
}
bk.maximalCliquePivot(g, sr, make(internal.Set).Intersect(p, nv), make(internal.Set).Intersect(x, nv))
p.Remove(v)
x.Add(v)
}
}
func (*bronKerbosch) choosePivotFrom(g graph.Undirected, p, x internal.Set) (neighbors []graph.Node) {
// TODO(kortschak): Investigate the impact of pivot choice that maximises
// |p ⋂ neighbours(u)| as a function of input size. Until then, leave as
// compile time option.
if !tomitaTanakaTakahashi {
for _, n := range p {
return g.From(n)
}
for _, n := range x {
return g.From(n)
}
panic("bronKerbosch: empty set")
}
var (
max = -1
pivot graph.Node
)
maxNeighbors := func(s internal.Set) {
outer:
for _, u := range s {
nb := g.From(u)
c := len(nb)
if c <= max {
continue
}
for n := range nb {
if _, ok := p[n]; ok {
continue
}
c--
if c <= max {
continue outer
}
}
max = c
pivot = u
neighbors = nb
}
}
maxNeighbors(p)
maxNeighbors(x)
if pivot == nil {
panic("bronKerbosch: empty set")
}
return neighbors
}
+285
View File
@@ -0,0 +1,285 @@
// 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 topo
import (
"sort"
"github.com/gonum/graph"
"github.com/gonum/graph/internal"
)
// johnson implements Johnson's "Finding all the elementary
// circuits of a directed graph" algorithm. SIAM J. Comput. 4(1):1975.
//
// Comments in the johnson methods are kept in sync with the comments
// and labels from the paper.
type johnson struct {
adjacent johnsonGraph // SCC adjacency list.
b []internal.IntSet // Johnson's "B-list".
blocked []bool
s int
stack []graph.Node
result [][]graph.Node
}
// CyclesIn returns the set of elementary cycles in the graph g.
func CyclesIn(g graph.Directed) [][]graph.Node {
jg := johnsonGraphFrom(g)
j := johnson{
adjacent: jg,
b: make([]internal.IntSet, len(jg.orig)),
blocked: make([]bool, len(jg.orig)),
}
// len(j.nodes) is the order of g.
for j.s < len(j.adjacent.orig)-1 {
// We use the previous SCC adjacency to reduce the work needed.
sccs := TarjanSCC(j.adjacent.subgraph(j.s))
// A_k = adjacency structure of strong component K with least
// vertex in subgraph of G induced by {s, s+1, ... ,n}.
j.adjacent = j.adjacent.sccSubGraph(sccs, 2) // Only allow SCCs with >= 2 vertices.
if j.adjacent.order() == 0 {
break
}
// s = least vertex in V_k
if s := j.adjacent.leastVertexIndex(); s < j.s {
j.s = s
}
for i, v := range j.adjacent.orig {
if !j.adjacent.nodes.Has(v.ID()) {
continue
}
if len(j.adjacent.succ[v.ID()]) > 0 {
j.blocked[i] = false
j.b[i] = make(internal.IntSet)
}
}
//L3:
_ = j.circuit(j.s)
j.s++
}
return j.result
}
// circuit is the CIRCUIT sub-procedure in the paper.
func (j *johnson) circuit(v int) bool {
f := false
n := j.adjacent.orig[v]
j.stack = append(j.stack, n)
j.blocked[v] = true
//L1:
for w := range j.adjacent.succ[n.ID()] {
w = j.adjacent.indexOf(w)
if w == j.s {
// Output circuit composed of stack followed by s.
r := make([]graph.Node, len(j.stack)+1)
copy(r, j.stack)
r[len(r)-1] = j.adjacent.orig[j.s]
j.result = append(j.result, r)
f = true
} else if !j.blocked[w] {
if j.circuit(w) {
f = true
}
}
}
//L2:
if f {
j.unblock(v)
} else {
for w := range j.adjacent.succ[n.ID()] {
j.b[j.adjacent.indexOf(w)].Add(v)
}
}
j.stack = j.stack[:len(j.stack)-1]
return f
}
// unblock is the UNBLOCK sub-procedure in the paper.
func (j *johnson) unblock(u int) {
j.blocked[u] = false
for w := range j.b[u] {
j.b[u].Remove(w)
if j.blocked[w] {
j.unblock(w)
}
}
}
// johnsonGraph is an edge list representation of a graph with helpers
// necessary for Johnson's algorithm
type johnsonGraph struct {
// Keep the original graph nodes and a
// look-up to into the non-sparse
// collection of potentially sparse IDs.
orig []graph.Node
index map[int]int
nodes internal.IntSet
succ map[int]internal.IntSet
}
// johnsonGraphFrom returns a deep copy of the graph g.
func johnsonGraphFrom(g graph.Directed) johnsonGraph {
nodes := g.Nodes()
sort.Sort(byID(nodes))
c := johnsonGraph{
orig: nodes,
index: make(map[int]int, len(nodes)),
nodes: make(internal.IntSet, len(nodes)),
succ: make(map[int]internal.IntSet),
}
for i, u := range nodes {
c.index[u.ID()] = i
for _, v := range g.From(u) {
if c.succ[u.ID()] == nil {
c.succ[u.ID()] = make(internal.IntSet)
c.nodes.Add(u.ID())
}
c.nodes.Add(v.ID())
c.succ[u.ID()].Add(v.ID())
}
}
return c
}
type byID []graph.Node
func (n byID) Len() int { return len(n) }
func (n byID) Less(i, j int) bool { return n[i].ID() < n[j].ID() }
func (n byID) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
// order returns the order of the graph.
func (g johnsonGraph) order() int { return g.nodes.Count() }
// indexOf returns the index of the retained node for the given node ID.
func (g johnsonGraph) indexOf(id int) int {
return g.index[id]
}
// leastVertexIndex returns the index into orig of the least vertex.
func (g johnsonGraph) leastVertexIndex() int {
for _, v := range g.orig {
if g.nodes.Has(v.ID()) {
return g.indexOf(v.ID())
}
}
panic("johnsonCycles: empty set")
}
// subgraph returns a subgraph of g induced by {s, s+1, ... , n}. The
// subgraph is destructively generated in g.
func (g johnsonGraph) subgraph(s int) johnsonGraph {
sn := g.orig[s].ID()
for u, e := range g.succ {
if u < sn {
g.nodes.Remove(u)
delete(g.succ, u)
continue
}
for v := range e {
if v < sn {
g.succ[u].Remove(v)
}
}
}
return g
}
// sccSubGraph returns the graph of the tarjan's strongly connected
// components with each SCC containing at least min vertices.
// sccSubGraph returns nil if there is no SCC with at least min
// members.
func (g johnsonGraph) sccSubGraph(sccs [][]graph.Node, min int) johnsonGraph {
if len(g.nodes) == 0 {
g.nodes = nil
g.succ = nil
return g
}
sub := johnsonGraph{
orig: g.orig,
index: g.index,
nodes: make(internal.IntSet),
succ: make(map[int]internal.IntSet),
}
var n int
for _, scc := range sccs {
if len(scc) < min {
continue
}
n++
for _, u := range scc {
for _, v := range scc {
if _, ok := g.succ[u.ID()][v.ID()]; ok {
if sub.succ[u.ID()] == nil {
sub.succ[u.ID()] = make(internal.IntSet)
sub.nodes.Add(u.ID())
}
sub.nodes.Add(v.ID())
sub.succ[u.ID()].Add(v.ID())
}
}
}
}
if n == 0 {
g.nodes = nil
g.succ = nil
return g
}
return sub
}
// Nodes is required to satisfy Tarjan.
func (g johnsonGraph) Nodes() []graph.Node {
n := make([]graph.Node, 0, len(g.nodes))
for id := range g.nodes {
n = append(n, johnsonGraphNode(id))
}
return n
}
// Successors is required to satisfy Tarjan.
func (g johnsonGraph) From(n graph.Node) []graph.Node {
adj := g.succ[n.ID()]
if len(adj) == 0 {
return nil
}
succ := make([]graph.Node, 0, len(adj))
for n := range adj {
succ = append(succ, johnsonGraphNode(n))
}
return succ
}
func (johnsonGraph) Has(graph.Node) bool {
panic("search: unintended use of johnsonGraph")
}
func (johnsonGraph) HasEdge(_, _ graph.Node) bool {
panic("search: unintended use of johnsonGraph")
}
func (johnsonGraph) Edge(_, _ graph.Node) graph.Edge {
panic("search: unintended use of johnsonGraph")
}
func (johnsonGraph) HasEdgeFromTo(_, _ graph.Node) bool {
panic("search: unintended use of johnsonGraph")
}
func (johnsonGraph) To(graph.Node) []graph.Node {
panic("search: unintended use of johnsonGraph")
}
type johnsonGraphNode int
func (n johnsonGraphNode) ID() int { return int(n) }
+9
View File
@@ -0,0 +1,9 @@
// 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.
//+build !tomita
package topo
const tomitaTanakaTakahashi = false
+161
View File
@@ -0,0 +1,161 @@
// 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 topo
import (
"fmt"
"sort"
"github.com/gonum/graph"
"github.com/gonum/graph/internal"
)
// Unorderable is an error containing sets of unorderable graph.Nodes.
type Unorderable [][]graph.Node
// Error satisfies the error interface.
func (e Unorderable) Error() string {
const maxNodes = 10
var n int
for _, c := range e {
n += len(c)
}
if n > maxNodes {
// Don't return errors that are too long.
return fmt.Sprintf("topo: no topological ordering: %d nodes in %d cyclic components", n, len(e))
}
return fmt.Sprintf("topo: no topological ordering: cyclic components: %v", [][]graph.Node(e))
}
// Sort performs a topological sort of the directed graph g returning the 'from' to 'to'
// sort order. If a topological ordering is not possible, an Unorderable error is returned
// listing cyclic components in g with each cyclic component's members sorted by ID. When
// an Unorderable error is returned, each cyclic component's topological position within
// the sorted nodes is marked with a nil graph.Node.
func Sort(g graph.Directed) (sorted []graph.Node, err error) {
sccs := TarjanSCC(g)
sorted = make([]graph.Node, 0, len(sccs))
var sc Unorderable
for _, s := range sccs {
if len(s) != 1 {
sort.Sort(byID(s))
sc = append(sc, s)
sorted = append(sorted, nil)
continue
}
sorted = append(sorted, s[0])
}
if sc != nil {
for i, j := 0, len(sc)-1; i < j; i, j = i+1, j-1 {
sc[i], sc[j] = sc[j], sc[i]
}
err = sc
}
reverse(sorted)
return sorted, err
}
func reverse(p []graph.Node) {
for i, j := 0, len(p)-1; i < j; i, j = i+1, j-1 {
p[i], p[j] = p[j], p[i]
}
}
// TarjanSCC returns the strongly connected components of the graph g using Tarjan's algorithm.
//
// A strongly connected component of a graph is a set of vertices where it's possible to reach any
// vertex in the set from any other (meaning there's a cycle between them.)
//
// Generally speaking, a directed graph where the number of strongly connected components is equal
// to the number of nodes is acyclic, unless you count reflexive edges as a cycle (which requires
// only a little extra testing.)
//
func TarjanSCC(g graph.Directed) [][]graph.Node {
nodes := g.Nodes()
t := tarjan{
succ: g.From,
indexTable: make(map[int]int, len(nodes)),
lowLink: make(map[int]int, len(nodes)),
onStack: make(internal.IntSet, len(nodes)),
}
for _, v := range nodes {
if t.indexTable[v.ID()] == 0 {
t.strongconnect(v)
}
}
return t.sccs
}
// tarjan implements Tarjan's strongly connected component finding
// algorithm. The implementation is from the pseudocode at
//
// http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm?oldid=642744644
//
type tarjan struct {
succ func(graph.Node) []graph.Node
index int
indexTable map[int]int
lowLink map[int]int
onStack internal.IntSet
stack []graph.Node
sccs [][]graph.Node
}
// strongconnect is the strongconnect function described in the
// wikipedia article.
func (t *tarjan) strongconnect(v graph.Node) {
vID := v.ID()
// Set the depth index for v to the smallest unused index.
t.index++
t.indexTable[vID] = t.index
t.lowLink[vID] = t.index
t.stack = append(t.stack, v)
t.onStack.Add(vID)
// Consider successors of v.
for _, w := range t.succ(v) {
wID := w.ID()
if t.indexTable[wID] == 0 {
// Successor w has not yet been visited; recur on it.
t.strongconnect(w)
t.lowLink[vID] = min(t.lowLink[vID], t.lowLink[wID])
} else if t.onStack.Has(wID) {
// Successor w is in stack s and hence in the current SCC.
t.lowLink[vID] = min(t.lowLink[vID], t.indexTable[wID])
}
}
// If v is a root node, pop the stack and generate an SCC.
if t.lowLink[vID] == t.indexTable[vID] {
// Start a new strongly connected component.
var (
scc []graph.Node
w graph.Node
)
for {
w, t.stack = t.stack[len(t.stack)-1], t.stack[:len(t.stack)-1]
t.onStack.Remove(w.ID())
// Add w to current strongly connected component.
scc = append(scc, w)
if w.ID() == vID {
break
}
}
// Output the current strongly connected component.
t.sccs = append(t.sccs, scc)
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
+9
View File
@@ -0,0 +1,9 @@
// 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.
//+build tomita
package topo
const tomitaTanakaTakahashi = true
+58
View File
@@ -0,0 +1,58 @@
// Copyright ©2014 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 topo
import (
"github.com/gonum/graph"
"github.com/gonum/graph/traverse"
)
// IsPathIn returns whether path is a path in g.
//
// As special cases, IsPathIn returns true for a zero length path or for
// a path of length 1 when the node in path exists in the graph.
func IsPathIn(g graph.Graph, path []graph.Node) bool {
switch len(path) {
case 0:
return true
case 1:
return g.Has(path[0])
default:
var canReach func(u, v graph.Node) bool
switch g := g.(type) {
case graph.Directed:
canReach = g.HasEdgeFromTo
default:
canReach = g.HasEdge
}
for i, u := range path[:len(path)-1] {
if !canReach(u, path[i+1]) {
return false
}
}
return true
}
}
// ConnectedComponents returns the connected components of the undirected graph g.
func ConnectedComponents(g graph.Undirected) [][]graph.Node {
var (
w traverse.DepthFirst
c []graph.Node
cc [][]graph.Node
)
during := func(n graph.Node) {
c = append(c, n)
}
after := func() {
cc = append(cc, []graph.Node(nil))
cc[len(cc)-1] = append(cc[len(cc)-1], c...)
c = c[:0]
}
w.WalkAll(g, nil, after, during)
return cc
}
+182
View File
@@ -0,0 +1,182 @@
// 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 traverse provides basic graph traversal primitives.
package traverse
import (
"github.com/gonum/graph"
"github.com/gonum/graph/internal"
)
// BreadthFirst implements stateful breadth-first graph traversal.
type BreadthFirst struct {
EdgeFilter func(graph.Edge) bool
Visit func(u, v graph.Node)
queue internal.NodeQueue
visited internal.IntSet
}
// Walk performs a breadth-first traversal of the graph g starting from the given node,
// depending on the the EdgeFilter field and the until parameter if they are non-nil. The
// traversal follows edges for which EdgeFilter(edge) is true and returns the first node
// for which until(node, depth) is true. During the traversal, if the Visit field is
// non-nil, it is called with the nodes joined by each followed edge.
func (b *BreadthFirst) Walk(g graph.Graph, from graph.Node, until func(n graph.Node, d int) bool) graph.Node {
if b.visited == nil {
b.visited = make(internal.IntSet)
}
b.queue.Enqueue(from)
b.visited.Add(from.ID())
var (
depth int
children int
untilNext = 1
)
for b.queue.Len() > 0 {
t := b.queue.Dequeue()
if until != nil && until(t, depth) {
return t
}
for _, n := range g.From(t) {
if b.EdgeFilter != nil && !b.EdgeFilter(g.Edge(t, n)) {
continue
}
if b.visited.Has(n.ID()) {
continue
}
if b.Visit != nil {
b.Visit(t, n)
}
b.visited.Add(n.ID())
children++
b.queue.Enqueue(n)
}
if untilNext--; untilNext == 0 {
depth++
untilNext = children
children = 0
}
}
return nil
}
// WalkAll calls Walk for each unvisited node of the graph g using edges independent
// of their direction. The functions before and after are called prior to commencing
// and after completing each walk if they are non-nil respectively. The function
// during is called on each node as it is traversed.
func (b *BreadthFirst) WalkAll(g graph.Undirected, before, after func(), during func(graph.Node)) {
b.Reset()
for _, from := range g.Nodes() {
if b.Visited(from) {
continue
}
if before != nil {
before()
}
b.Walk(g, from, func(n graph.Node, _ int) bool {
if during != nil {
during(n)
}
return false
})
if after != nil {
after()
}
}
}
// Visited returned whether the node n was visited during a traverse.
func (b *BreadthFirst) Visited(n graph.Node) bool {
_, ok := b.visited[n.ID()]
return ok
}
// Reset resets the state of the traverser for reuse.
func (b *BreadthFirst) Reset() {
b.queue.Reset()
b.visited = nil
}
// DepthFirst implements stateful depth-first graph traversal.
type DepthFirst struct {
EdgeFilter func(graph.Edge) bool
Visit func(u, v graph.Node)
stack internal.NodeStack
visited internal.IntSet
}
// Walk performs a depth-first traversal of the graph g starting from the given node,
// depending on the the EdgeFilter field and the until parameter if they are non-nil. The
// traversal follows edges for which EdgeFilter(edge) is true and returns the first node
// for which until(node) is true. During the traversal, if the Visit field is non-nil, it
// is called with the nodes joined by each followed edge.
func (d *DepthFirst) Walk(g graph.Graph, from graph.Node, until func(graph.Node) bool) graph.Node {
if d.visited == nil {
d.visited = make(internal.IntSet)
}
d.stack.Push(from)
d.visited.Add(from.ID())
for d.stack.Len() > 0 {
t := d.stack.Pop()
if until != nil && until(t) {
return t
}
for _, n := range g.From(t) {
if d.EdgeFilter != nil && !d.EdgeFilter(g.Edge(t, n)) {
continue
}
if d.visited.Has(n.ID()) {
continue
}
if d.Visit != nil {
d.Visit(t, n)
}
d.visited.Add(n.ID())
d.stack.Push(n)
}
}
return nil
}
// WalkAll calls Walk for each unvisited node of the graph g using edges independent
// of their direction. The functions before and after are called prior to commencing
// and after completing each walk if they are non-nil respectively. The function
// during is called on each node as it is traversed.
func (d *DepthFirst) WalkAll(g graph.Undirected, before, after func(), during func(graph.Node)) {
d.Reset()
for _, from := range g.Nodes() {
if d.Visited(from) {
continue
}
if before != nil {
before()
}
d.Walk(g, from, func(n graph.Node) bool {
if during != nil {
during(n)
}
return false
})
if after != nil {
after()
}
}
}
// Visited returned whether the node n was visited during a traverse.
func (d *DepthFirst) Visited(n graph.Node) bool {
_, ok := d.visited[n.ID()]
return ok
}
// Reset resets the state of the traverser for reuse.
func (d *DepthFirst) Reset() {
d.stack = d.stack[:0]
d.visited = nil
}
+22
View File
@@ -0,0 +1,22 @@
// Generated code do not edit. Run `go generate`.
// 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 asm
// The extra z parameter is needed because of floats.AddScaledTo
func CaxpyUnitary(alpha complex64, x, y, z []complex64) {
for i, v := range x {
z[i] = alpha*v + y[i]
}
}
func CaxpyInc(alpha complex64, x, y []complex64, n, incX, incY, ix, iy uintptr) {
for i := 0; i < int(n); i++ {
y[iy] += alpha * x[ix]
ix += incX
iy += incY
}
}
+23
View File
@@ -0,0 +1,23 @@
// Generated code do not edit. Run `go generate`.
// 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 asm
func CdotcUnitary(x, y []complex64) (sum complex64) {
for i, v := range x {
sum += y[i] * conj(v)
}
return
}
func CdotcInc(x, y []complex64, n, incX, incY, ix, iy uintptr) (sum complex64) {
for i := 0; i < int(n); i++ {
sum += y[iy] * conj(x[ix])
ix += incX
iy += incY
}
return
}
+23
View File
@@ -0,0 +1,23 @@
// Generated code do not edit. Run `go generate`.
// 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 asm
func CdotuUnitary(x, y []complex64) (sum complex64) {
for i, v := range x {
sum += y[i] * v
}
return
}
func CdotuInc(x, y []complex64, n, incX, incY, ix, iy uintptr) (sum complex64) {
for i := 0; i < int(n); i++ {
sum += y[iy] * x[ix]
ix += incX
iy += incY
}
return
}
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
# 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.
echo Generating zdotu.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > zdotu.go
cat ddot.go \
| grep -v '//+build' \
| gofmt -r 'float64 -> complex128' \
| sed 's/Ddot/Zdotu/' \
>> zdotu.go
echo Generating zdotc.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > zdotc.go
cat ddot.go \
| grep -v '//+build' \
| gofmt -r 'float64 -> complex128' \
| gofmt -r 'y[i] * v -> y[i] * cmplx.Conj(v)' \
| sed 's/Ddot/Zdotc/' \
| goimports \
>> zdotc.go
echo Generating zaxpy.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > zaxpy.go
cat daxpy.go \
| grep -v '//+build' \
| gofmt -r 'float64 -> complex128' \
| sed 's/Daxpy/Zaxpy/' \
>> zaxpy.go
echo Generating cdotu.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > cdotu.go
cat ddot.go \
| grep -v '//+build' \
| gofmt -r 'float64 -> complex64' \
| sed 's/Ddot/Cdotu/' \
>> cdotu.go
echo Generating cdotc.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > cdotc.go
cat ddot.go \
| grep -v '//+build' \
| gofmt -r 'float64 -> complex64' \
| gofmt -r 'y[i] * v -> y[i] * conj(v)' \
| sed 's/Ddot/Cdotc/' \
| goimports \
>> cdotc.go
echo Generating caxpy.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > caxpy.go
cat daxpy.go \
| grep -v '//+build' \
| gofmt -r 'float64 -> complex64' \
| sed 's/Daxpy/Caxpy/' \
>> caxpy.go
+7
View File
@@ -0,0 +1,7 @@
// 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 asm
func conj(c complex64) complex64 { return complex(real(c), -imag(c)) }
+22
View File
@@ -0,0 +1,22 @@
// 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.
//+build !amd64 noasm
package asm
// The extra z parameter is needed because of floats.AddScaledTo
func DaxpyUnitary(alpha float64, x, y, z []float64) {
for i, v := range x {
z[i] = alpha*v + y[i]
}
}
func DaxpyInc(alpha float64, x, y []float64, n, incX, incY, ix, iy uintptr) {
for i := 0; i < int(n); i++ {
y[iy] += alpha * x[ix]
ix += incX
iy += incY
}
}
+12
View File
@@ -0,0 +1,12 @@
// 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.
//+build !noasm
package asm
// The extra z parameter is needed because of floats.AddScaledTo
func DaxpyUnitary(alpha float64, x, y, z []float64)
func DaxpyInc(alpha float64, x, y []float64, n, incX, incY, ix, iy uintptr)
+140
View File
@@ -0,0 +1,140 @@
// 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.
//
// Some of the loop unrolling code is copied from:
// http://golang.org/src/math/big/arith_amd64.s
// which is distributed under these terms:
//
// Copyright (c) 2012 The Go Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//+build !noasm
// TODO(fhs): use textflag.h after we drop Go 1.3 support
//#include "textflag.h"
// Don't insert stack check preamble.
#define NOSPLIT 4
// func DaxpyUnitary(alpha float64, x, y, z []float64)
// This function assumes len(y) >= len(x).
TEXT ·DaxpyUnitary(SB),NOSPLIT,$0
MOVHPD alpha+0(FP), X7
MOVLPD alpha+0(FP), X7
MOVQ x_len+16(FP), DI // n = len(x)
MOVQ x+8(FP), R8
MOVQ y+32(FP), R9
MOVQ z+56(FP), R10
MOVQ $0, SI // i = 0
SUBQ $2, DI // n -= 2
JL V1 // if n < 0 goto V1
U1: // n >= 0
// y[i] += alpha * x[i] unrolled 2x.
MOVUPD 0(R8)(SI*8), X0
MOVUPD 0(R9)(SI*8), X1
MULPD X7, X0
ADDPD X0, X1
MOVUPD X1, 0(R10)(SI*8)
ADDQ $2, SI // i += 2
SUBQ $2, DI // n -= 2
JGE U1 // if n >= 0 goto U1
V1:
ADDQ $2, DI // n += 2
JLE E1 // if n <= 0 goto E1
// y[i] += alpha * x[i] for last iteration if n is odd.
MOVSD 0(R8)(SI*8), X0
MOVSD 0(R9)(SI*8), X1
MULSD X7, X0
ADDSD X0, X1
MOVSD X1, 0(R10)(SI*8)
E1:
RET
// func DaxpyInc(alpha float64, x, y []float64, n, incX, incY, ix, iy uintptr)
TEXT ·DaxpyInc(SB),NOSPLIT,$0
MOVHPD alpha+0(FP), X7
MOVLPD alpha+0(FP), X7
MOVQ x+8(FP), R8
MOVQ y+32(FP), R9
MOVQ n+56(FP), CX
MOVQ incX+64(FP), R11
MOVQ incY+72(FP), R12
MOVQ ix+80(FP), SI
MOVQ iy+88(FP), DI
MOVQ SI, AX // nextX = ix
MOVQ DI, BX // nextY = iy
ADDQ R11, AX // nextX += incX
ADDQ R12, BX // nextY += incX
SHLQ $1, R11 // indX *= 2
SHLQ $1, R12 // indY *= 2
SUBQ $2, CX // n -= 2
JL V2 // if n < 0 goto V2
U2: // n >= 0
// y[i] += alpha * x[i] unrolled 2x.
MOVHPD 0(R8)(SI*8), X0
MOVHPD 0(R9)(DI*8), X1
MOVLPD 0(R8)(AX*8), X0
MOVLPD 0(R9)(BX*8), X1
MULPD X7, X0
ADDPD X0, X1
MOVHPD X1, 0(R9)(DI*8)
MOVLPD X1, 0(R9)(BX*8)
ADDQ R11, SI // ix += incX
ADDQ R12, DI // iy += incY
ADDQ R11, AX // nextX += incX
ADDQ R12, BX // nextY += incY
SUBQ $2, CX // n -= 2
JGE U2 // if n >= 0 goto U2
V2:
ADDQ $2, CX // n += 2
JLE E2 // if n <= 0 goto E2
// y[i] += alpha * x[i] for the last iteration if n is odd.
MOVSD 0(R8)(SI*8), X0
MOVSD 0(R9)(DI*8), X1
MULSD X7, X0
ADDSD X0, X1
MOVSD X1, 0(R9)(DI*8)
E2:
RET
+23
View File
@@ -0,0 +1,23 @@
// 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.
//+build !amd64 noasm
package asm
func DdotUnitary(x, y []float64) (sum float64) {
for i, v := range x {
sum += y[i] * v
}
return
}
func DdotInc(x, y []float64, n, incX, incY, ix, iy uintptr) (sum float64) {
for i := 0; i < int(n); i++ {
sum += y[iy] * x[ix]
ix += incX
iy += incY
}
return
}
+10
View File
@@ -0,0 +1,10 @@
// 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.
//+build !noasm
package asm
func DdotUnitary(x, y []float64) (sum float64)
func DdotInc(x, y []float64, n, incX, incY, ix, iy uintptr) (sum float64)
+140
View File
@@ -0,0 +1,140 @@
// 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.
//
// Some of the loop unrolling code is copied from:
// http://golang.org/src/math/big/arith_amd64.s
// which is distributed under these terms:
//
// Copyright (c) 2012 The Go Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//+build !noasm
// TODO(fhs): use textflag.h after we drop Go 1.3 support
//#include "textflag.h"
// Don't insert stack check preamble.
#define NOSPLIT 4
// func DdotUnitary(x, y []float64) (sum float64)
// This function assumes len(y) >= len(x).
TEXT ·DdotUnitary(SB),NOSPLIT,$0
MOVQ x_len+8(FP), DI // n = len(x)
MOVQ x+0(FP), R8
MOVQ y+24(FP), R9
MOVQ $0, SI // i = 0
MOVSD $(0.0), X7 // sum = 0
SUBQ $2, DI // n -= 2
JL V1 // if n < 0 goto V1
U1: // n >= 0
// sum += x[i] * y[i] unrolled 2x.
MOVUPD 0(R8)(SI*8), X0
MOVUPD 0(R9)(SI*8), X1
MULPD X1, X0
ADDPD X0, X7
ADDQ $2, SI // i += 2
SUBQ $2, DI // n -= 2
JGE U1 // if n >= 0 goto U1
V1: // n > 0
ADDQ $2, DI // n += 2
JLE E1 // if n <= 0 goto E1
// sum += x[i] * y[i] for last iteration if n is odd.
MOVSD 0(R8)(SI*8), X0
MOVSD 0(R9)(SI*8), X1
MULSD X1, X0
ADDSD X0, X7
E1:
// Add the two sums together.
MOVSD X7, X0
UNPCKHPD X7, X7
ADDSD X0, X7
MOVSD X7, sum+48(FP) // return final sum
RET
// func DdotInc(x, y []float64, n, incX, incY, ix, iy uintptr) (sum float64)
TEXT ·DdotInc(SB),NOSPLIT,$0
MOVQ x+0(FP), R8
MOVQ y+24(FP), R9
MOVQ n+48(FP), CX
MOVQ incX+56(FP), R11
MOVQ incY+64(FP), R12
MOVQ ix+72(FP), R13
MOVQ iy+80(FP), R14
MOVSD $(0.0), X7 // sum = 0
LEAQ (R8)(R13*8), SI // p = &x[ix]
LEAQ (R9)(R14*8), DI // q = &y[ix]
SHLQ $3, R11 // incX *= sizeof(float64)
SHLQ $3, R12 // indY *= sizeof(float64)
SUBQ $2, CX // n -= 2
JL V2 // if n < 0 goto V2
U2: // n >= 0
// sum += *p * *q unrolled 2x.
MOVHPD (SI), X0
MOVHPD (DI), X1
ADDQ R11, SI // p += incX
ADDQ R12, DI // q += incY
MOVLPD (SI), X0
MOVLPD (DI), X1
ADDQ R11, SI // p += incX
ADDQ R12, DI // q += incY
MULPD X1, X0
ADDPD X0, X7
SUBQ $2, CX // n -= 2
JGE U2 // if n >= 0 goto U2
V2:
ADDQ $2, CX // n += 2
JLE E2 // if n <= 0 goto E2
// sum += *p * *q for the last iteration if n is odd.
MOVSD (SI), X0
MULSD (DI), X0
ADDSD X0, X7
E2:
// Add the two sums together.
MOVSD X7, X0
UNPCKHPD X7, X7
ADDSD X0, X7
MOVSD X7, sum+88(FP) // return final sum
RET
+23
View File
@@ -0,0 +1,23 @@
// Generated code do not edit. Run `go generate`.
// 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 asm
func DsdotUnitary(x, y []float32) (sum float64) {
for i, v := range x {
sum += float64(y[i]) * float64(v)
}
return
}
func DsdotInc(x, y []float32, n, incX, incY, ix, iy uintptr) (sum float64) {
for i := 0; i < int(n); i++ {
sum += float64(y[iy]) * float64(x[ix])
ix += incX
iy += incY
}
return
}
+8
View File
@@ -0,0 +1,8 @@
// 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.
//go:generate ./single_precision
//go:generate ./complex
package asm
+22
View File
@@ -0,0 +1,22 @@
// Generated code do not edit. Run `go generate`.
// 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 asm
// The extra z parameter is needed because of floats.AddScaledTo
func SaxpyUnitary(alpha float32, x, y, z []float32) {
for i, v := range x {
z[i] = alpha*v + y[i]
}
}
func SaxpyInc(alpha float32, x, y []float32, n, incX, incY, ix, iy uintptr) {
for i := 0; i < int(n); i++ {
y[iy] += alpha * x[ix]
ix += incX
iy += incY
}
}
+23
View File
@@ -0,0 +1,23 @@
// Generated code do not edit. Run `go generate`.
// 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 asm
func SdotUnitary(x, y []float32) (sum float32) {
for i, v := range x {
sum += y[i] * v
}
return
}
func SdotInc(x, y []float32, n, incX, incY, ix, iy uintptr) (sum float32) {
for i := 0; i < int(n); i++ {
sum += y[iy] * x[ix]
ix += incX
iy += incY
}
return
}
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
# 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.
echo Generating dsdot.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > dsdot.go
cat ddot.go \
| grep -v '//+build' \
| gofmt -r '[]float64 -> []float32' \
| gofmt -r 'a * b -> float64(a) * float64(b)' \
| sed 's/Ddot/Dsdot/' \
>> dsdot.go
echo Generating sdot.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > sdot.go
cat ddot.go \
| grep -v '//+build' \
| gofmt -r 'float64 -> float32' \
| sed 's/Ddot/Sdot/' \
>> sdot.go
echo Generating saxpy.go
echo -e '// Generated code do not edit. Run `go generate`.\n' > saxpy.go
cat daxpy.go \
| grep -v '//+build' \
| gofmt -r 'float64 -> float32' \
| sed 's/Daxpy/Saxpy/' \
>> saxpy.go
+22
View File
@@ -0,0 +1,22 @@
// Generated code do not edit. Run `go generate`.
// 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 asm
// The extra z parameter is needed because of floats.AddScaledTo
func ZaxpyUnitary(alpha complex128, x, y, z []complex128) {
for i, v := range x {
z[i] = alpha*v + y[i]
}
}
func ZaxpyInc(alpha complex128, x, y []complex128, n, incX, incY, ix, iy uintptr) {
for i := 0; i < int(n); i++ {
y[iy] += alpha * x[ix]
ix += incX
iy += incY
}
}
+25
View File
@@ -0,0 +1,25 @@
// Generated code do not edit. Run `go generate`.
// 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 asm
import "math/cmplx"
func ZdotcUnitary(x, y []complex128) (sum complex128) {
for i, v := range x {
sum += y[i] * cmplx.Conj(v)
}
return
}
func ZdotcInc(x, y []complex128, n, incX, incY, ix, iy uintptr) (sum complex128) {
for i := 0; i < int(n); i++ {
sum += y[iy] * cmplx.Conj(x[ix])
ix += incX
iy += incY
}
return
}
+23
View File
@@ -0,0 +1,23 @@
// Generated code do not edit. Run `go generate`.
// 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 asm
func ZdotuUnitary(x, y []complex128) (sum complex128) {
for i, v := range x {
sum += y[i] * v
}
return
}
func ZdotuInc(x, y []complex128, n, incX, incY, ix, iy uintptr) (sum complex128) {
for i := 0; i < int(n); i++ {
sum += y[iy] * x[ix]
ix += incX
iy += incY
}
return
}
+1
View File
@@ -0,0 +1 @@
clapack/lapack.go
+38
View File
@@ -0,0 +1,38 @@
sudo: required
language: go
env:
matrix:
- BLAS_LIB=OpenBLAS
- BLAS_LIB=gonum
# Does not currently link correctly. Note that there is an issue with drotgm in ATLAS.
# - BLAS_LIB=ATLAS
# If we can get multiarch builds on travis.
# There are some issues with the Accellerate implementation.
#- BLAS_LIB=Accellerate
# Versions of go that are explicitly supported by gonum.
go:
- 1.5beta1
- 1.3.3
- 1.4.2
# Required for coverage.
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
# Install the appropriate BLAS library.
install:
- bash .travis/$TRAVIS_OS_NAME/$BLAS_LIB/install.sh
# Get deps, build, test, and ensure the code is gofmt'ed.
# If we are building as gonum, then we have access to the coveralls api key, so we can run coverage as well.
script:
- if [[ "$BLAS_LIB" == "gonum" ]]; then pushd native; fi
- go get -d -t -v ./...
- go build -v ./...
- go test -v ./...
- diff <(gofmt -d *.go) <("")
- if [[ $TRAVIS_SECURE_ENV_VARS = "true" ]]; then bash -c "${TRAVIS_BUILD_DIR}/.travis/test-coverage.sh"; fi
+58
View File
@@ -0,0 +1,58 @@
Gonum LAPACK [![Build Status](https://travis-ci.org/gonum/lapack.svg?branch=master)](https://travis-ci.org/gonum/lapack) [![Coverage Status](https://img.shields.io/coveralls/gonum/lapack.svg)](https://coveralls.io/r/gonum/lapack)
======
A collection of packages to provide LAPACK functionality for the Go programming
language (http://golang.org). This provides a partial implementation in native go
and a wrapper using cgo to a c-based implementation.
## Installation
```
go get github.com/gonum/blas
```
Install OpenBLAS:
```
git clone https://github.com/xianyi/OpenBLAS
cd OpenBLAS
make
```
Then install the lapack/cgo package:
```sh
CGO_LDFLAGS="-L/path/to/OpenBLAS -lopenblas" go install github.com/gonum/lapack/cgo
```
For Windows you can download binary packages for OpenBLAS at
http://sourceforge.net/projects/openblas/files/
If you want to use a different BLAS package such as the Intel MKL you can
adjust the `CGO_LDFLAGS` variable:
```sh
CGO_LDFLAGS="-lmkl_rt" go install github.com/gonum/lapack/cgo
```
## Packages
### lapack
Defines the LAPACK API based on http://www.netlib.org/lapack/lapacke.html
### lapack/clapack
Binding to a C implementation of the lapacke interface (e.g. OpenBLAS or intel MKL)
The linker flags (i.e. path to the BLAS library and library name) might have to be adapted.
The recommended (free) option for good performance on both linux and darwin is OpenBLAS.
## Issues
If you find any bugs, feel free to file an issue on the github issue tracker. Discussions on API changes, added features, code review, or similar requests are preferred on the gonum-dev Google Group.
https://groups.google.com/forum/#!forum/gonum-dev
## License
Please see github.com/gonum/license for general license information, contributors, authors, etc on the Gonum suite of packages.
+60
View File
@@ -0,0 +1,60 @@
// 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 lapack
import "github.com/gonum/blas"
const None = 'N'
type Job byte
// CompSV determines if the singular values are to be computed in compact form.
type CompSV byte
const (
Compact CompSV = 'P'
Explicit CompSV = 'I'
)
// Complex128 defines the public complex128 LAPACK API supported by gonum/lapack.
type Complex128 interface{}
// Float64 defines the public float64 LAPACK API supported by gonum/lapack.
type Float64 interface {
Dpotrf(ul blas.Uplo, n int, a []float64, lda int) (ok bool)
}
// Direct specifies the direction of the multiplication for the Householder matrix.
type Direct byte
const (
Forward Direct = 'F' // Reflectors are right-multiplied, H_1 * H_2 * ... * H_k
Backward Direct = 'B' // Reflectors are left-multiplied, H_k * ... * H_2 * H_1
)
// StoreV indicates the storage direction of elementary reflectors.
type StoreV byte
const (
ColumnWise StoreV = 'C' // Reflector stored in a column of the matrix.
RowWise StoreV = 'R' // Reflector stored in a row of the matrix.
)
// MatrixNorm represents the kind of matrix norm to compute.
type MatrixNorm byte
const (
MaxAbs MatrixNorm = 'M' // max(abs(A(i,j))) ('M')
MaxColumnSum MatrixNorm = 'O' // Maximum column sum (one norm) ('1', 'O')
MaxRowSum MatrixNorm = 'I' // Maximum row sum (infinity norm) ('I', 'i')
NormFrob MatrixNorm = 'F' // Frobenium norm (sqrt of sum of squares) ('F', 'f', E, 'e')
)
// MatrixType represents the kind of matrix represented in the data.
type MatrixType byte
const (
General MatrixType = 'G' // A dense matrix (like blas64.General).
)
+49
View File
@@ -0,0 +1,49 @@
// 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 lapack64 provides a set of convenient wrapper functions for LAPACK
// calls, as specified in the netlib standard (www.netlib.org).
//
// The native Go routines are used by default, and the Use function can be used
// to set an alternate implementation.
//
// If the type of matrix (General, Symmetric, etc.) is known and fixed, it is
// used in the wrapper signature. In many cases, however, the type of the matrix
// changes during the call to the routine, for example the matrix is symmetric on
// entry and is triangular on exit. In these cases the correct types should be checked
// in the documentation.
//
// The full set of Lapack functions is very large, and it is not clear that a
// full implementation is desirable, let alone feasible. Please open up an issue
// if there is a specific function you need and/or are willing to implement.
package lapack64
import (
"github.com/gonum/blas"
"github.com/gonum/blas/blas64"
"github.com/gonum/lapack"
"github.com/gonum/lapack/native"
)
var lapack64 lapack.Float64 = native.Implementation{}
// Use sets the LAPACK float64 implementation to be used by subsequent BLAS calls.
// The default implementation is native.Implementation.
func Use(l lapack.Float64) {
lapack64 = l
}
// Potrf computes the cholesky factorization of a.
// A = U^T * U if ul == blas.Upper
// A = L * L^T if ul == blas.Lower
// The underlying data between the input matrix and output matrix is shared.
func Potrf(a blas64.Symmetric) (t blas64.Triangular, ok bool) {
ok = lapack64.Dpotrf(a.Uplo, a.N, a.Data, a.Stride)
t.Uplo = a.Uplo
t.N = a.N
t.Data = a.Data
t.Stride = a.Stride
t.Diag = blas.NonUnit
return
}
+44
View File
@@ -0,0 +1,44 @@
// 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 native
import "github.com/gonum/blas"
// Dgelq2 computes the LQ factorization of the m×n matrix a.
//
// During Dgelq2, a is modified to contain the information to construct Q and L.
// The lower triangle of a contains the matrix L. The upper triangular elements
// (not including the diagonal) contain the elementary reflectors. Tau is modified
// to contain the reflector scales. Tau must have length of at least k = min(m,n)
// and this function will panic otherwise.
//
// See Dgeqr2 for a description of the elementary reflectors and orthonormal
// matrix Q. Q is constructed as a product of these elementary reflectors,
// Q = H_k ... H_2*H_1.
//
// Work is temporary storage of length at least m and this function will panic otherwise.
func (impl Implementation) Dgelq2(m, n int, a []float64, lda int, tau, work []float64) {
checkMatrix(m, n, a, lda)
k := min(m, n)
if len(tau) < k {
panic(badTau)
}
if len(work) < m {
panic(badWork)
}
for i := 0; i < k; i++ {
a[i*lda+i], tau[i] = impl.Dlarfg(n-i, a[i*lda+i], a[i*lda+min(i+1, n-1):], 1)
if i < m-1 {
aii := a[i*lda+i]
a[i*lda+i] = 1
impl.Dlarf(blas.Right, m-i-1, n-i,
a[i*lda+i:], 1,
tau[i],
a[(i+1)*lda+i:], lda,
work)
a[i*lda+i] = aii
}
}
}
+84
View File
@@ -0,0 +1,84 @@
// 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 native
import (
"github.com/gonum/blas"
"github.com/gonum/lapack"
)
// Dgelqf computes the LQ factorization of the m×n matrix a using a blocked
// algorithm. Please see the documentation for Dgelq2 for a description of the
// parameters at entry and exit.
//
// Work is temporary storage, and lwork specifies the usable memory length.
// At minimum, lwork >= m, and this function will panic otherwise.
// Dgelqf is a blocked LQ factorization, but the block size is limited
// by the temporary space available. If lwork == -1, instead of performing Dgelqf,
// the optimal work length will be stored into work[0].
//
// tau must have length at least min(m,n), and this function will panic otherwise.
func (impl Implementation) Dgelqf(m, n int, a []float64, lda int, tau, work []float64, lwork int) {
nb := impl.Ilaenv(1, "DGELQF", " ", m, n, -1, -1)
lworkopt := m * max(nb, 1)
if lwork == -1 {
work[0] = float64(lworkopt)
return
}
checkMatrix(m, n, a, lda)
if len(work) < lwork {
panic(shortWork)
}
if lwork < m {
panic(badWork)
}
k := min(m, n)
if len(tau) < k {
panic(badTau)
}
if k == 0 {
return
}
// Find the optimal blocking size based on the size of available memory
// and optimal machine parameters.
nbmin := 2
var nx int
iws := m
ldwork := nb
if nb > 1 && k > nb {
nx = max(0, impl.Ilaenv(3, "DGELQF", " ", m, n, -1, -1))
if nx < k {
iws = m * nb
if lwork < iws {
nb = lwork / m
nbmin = max(2, impl.Ilaenv(2, "DGELQF", " ", m, n, -1, -1))
}
}
}
// Computed blocked LQ factorization.
var i int
if nb >= nbmin && nb < k && nx < k {
for i = 0; i < k-nx; i += nb {
ib := min(k-i, nb)
impl.Dgelq2(ib, n-i, a[i*lda+i:], lda, tau[i:], work)
if i+ib < m {
impl.Dlarft(lapack.Forward, lapack.RowWise, n-i, ib,
a[i*lda+i:], lda,
tau[i:],
work, ldwork)
impl.Dlarfb(blas.Right, blas.NoTrans, lapack.Forward, lapack.RowWise,
m-i-ib, n-i, ib,
a[i*lda+i:], lda,
work, ldwork,
a[(i+ib)*lda+i:], lda,
work[ib*ldwork:], ldwork)
}
}
}
// Perform unblocked LQ factorization on the remainder.
if i < k {
impl.Dgelq2(m-i, n-i, a[i*lda+i:], lda, tau[i:], work)
}
}
+200
View File
@@ -0,0 +1,200 @@
// 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 native
import (
"github.com/gonum/blas"
"github.com/gonum/lapack"
)
// Dgels finds a minimum-norm solution based on the matrices a and b using the
// QR or LQ factorization. Dgels returns false if the matrix
// A is singular, and true if this solution was successfully found.
//
// The minimization problem solved depends on the input parameters.
//
// 1. If m >= n and trans == blas.NoTrans, Dgels finds X such that || A*X - B||_2
// is minimized.
// 2. If m < n and trans == blas.NoTrans, Dgels finds the minimum norm solution of
// A * X = B.
// 3. If m >= n and trans == blas.Trans, Dgels finds the minimum norm solution of
// A^T * X = B.
// 4. If m < n and trans == blas.Trans, Dgels finds X such that || A*X - B||_2
// is minimized.
// Note that the least-squares solutions (cases 1 and 3) perform the minimization
// per column of B. This is not the same as finding the minimum-norm matrix.
//
// The matrix a is a general matrix of size m×n and is modified during this call.
// The input matrix b is of size max(m,n)×nrhs, and serves two purposes. On entry,
// the elements of b specify the input matrix B. B has size m×nrhs if
// trans == blas.NoTrans, and n×nrhs if trans == blas.Trans. On exit, the
// leading submatrix of b contains the solution vectors X. If trans == blas.NoTrans,
// this submatrix is of size n×nrhs, and of size m×nrhs otherwise.
//
// Work is temporary storage, and lwork specifies the usable memory length.
// At minimum, lwork >= max(m,n) + max(m,n,nrhs), and this function will panic
// otherwise. A longer work will enable blocked algorithms to be called.
// In the special case that lwork == -1, work[0] will be set to the optimal working
// length.
func (impl Implementation) Dgels(trans blas.Transpose, m, n, nrhs int, a []float64, lda int, b []float64, ldb int, work []float64, lwork int) bool {
notran := trans == blas.NoTrans
checkMatrix(m, n, a, lda)
mn := min(m, n)
checkMatrix(mn, nrhs, b, ldb)
// Find optimal block size.
tpsd := true
if notran {
tpsd = false
}
var nb int
if m >= n {
nb = impl.Ilaenv(1, "DGEQRF", " ", m, n, -1, -1)
if tpsd {
nb = max(nb, impl.Ilaenv(1, "DORMQR", "LN", m, nrhs, n, -1))
} else {
nb = max(nb, impl.Ilaenv(1, "DORMQR", "LT", m, nrhs, n, -1))
}
} else {
nb = impl.Ilaenv(1, "DGELQF", " ", m, n, -1, -1)
if tpsd {
nb = max(nb, impl.Ilaenv(1, "DORMLQ", "LT", n, nrhs, m, -1))
} else {
nb = max(nb, impl.Ilaenv(1, "DORMLQ", "LN", n, nrhs, m, -1))
}
}
if lwork == -1 {
work[0] = float64(max(1, mn+max(mn, nrhs)*nb))
return true
}
if len(work) < lwork {
panic(shortWork)
}
if lwork < mn+max(mn, nrhs) {
panic(badWork)
}
if m == 0 || n == 0 || nrhs == 0 {
impl.Dlaset(blas.All, max(m, n), nrhs, 0, 0, b, ldb)
return true
}
// Scale the input matrices if they contain extreme values.
smlnum := dlamchS / dlamchP
bignum := 1 / smlnum
anrm := impl.Dlange(lapack.MaxAbs, m, n, a, lda, nil)
var iascl int
if anrm > 0 && anrm < smlnum {
impl.Dlascl(lapack.General, 0, 0, anrm, smlnum, m, n, a, lda)
iascl = 1
} else if anrm > bignum {
impl.Dlascl(lapack.General, 0, 0, anrm, bignum, m, n, a, lda)
} else if anrm == 0 {
// Matrix all zeros
impl.Dlaset(blas.All, max(m, n), nrhs, 0, 0, b, ldb)
return true
}
brow := m
if tpsd {
brow = n
}
bnrm := impl.Dlange(lapack.MaxAbs, brow, nrhs, b, ldb, nil)
ibscl := 0
if bnrm > 0 && bnrm < smlnum {
impl.Dlascl(lapack.General, 0, 0, bnrm, smlnum, brow, nrhs, b, ldb)
ibscl = 1
} else if bnrm > bignum {
impl.Dlascl(lapack.General, 0, 0, bnrm, bignum, brow, nrhs, b, ldb)
ibscl = 2
}
// Solve the minimization problem using a QR or an LQ decomposition.
var scllen int
if m >= n {
impl.Dgeqrf(m, n, a, lda, work, work[mn:], lwork-mn)
if !tpsd {
impl.Dormqr(blas.Left, blas.Trans, m, nrhs, n,
a, lda,
work,
b, ldb,
work[mn:], lwork-mn)
ok := impl.Dtrtrs(blas.Upper, blas.NoTrans, blas.NonUnit, n, nrhs,
a, lda,
b, ldb)
if !ok {
return false
}
scllen = n
} else {
ok := impl.Dtrtrs(blas.Upper, blas.Trans, blas.NonUnit, n, nrhs,
a, lda,
b, ldb)
if !ok {
return false
}
for i := n; i < m; i++ {
for j := 0; j < nrhs; j++ {
b[i*ldb+j] = 0
}
}
impl.Dormqr(blas.Left, blas.NoTrans, m, nrhs, n,
a, lda,
work,
b, ldb,
work[mn:], lwork-mn)
scllen = m
}
} else {
impl.Dgelqf(m, n, a, lda, work, work[mn:], lwork-mn)
if !tpsd {
ok := impl.Dtrtrs(blas.Lower, blas.NoTrans, blas.NonUnit,
m, nrhs,
a, lda,
b, ldb)
if !ok {
return false
}
for i := m; i < n; i++ {
for j := 0; j < nrhs; j++ {
b[i*ldb+j] = 0
}
}
impl.Dormlq(blas.Left, blas.Trans, n, nrhs, m,
a, lda,
work,
b, ldb,
work[mn:], lwork-mn)
scllen = n
} else {
impl.Dormlq(blas.Left, blas.NoTrans, n, nrhs, m,
a, lda,
work,
b, ldb,
work[mn:], lwork-mn)
ok := impl.Dtrtrs(blas.Lower, blas.Trans, blas.NonUnit,
m, nrhs,
a, lda,
b, ldb)
if !ok {
return false
}
}
}
// Adjust answer vector based on scaling.
if iascl == 1 {
impl.Dlascl(lapack.General, 0, 0, anrm, smlnum, scllen, nrhs, b, ldb)
}
if iascl == 2 {
impl.Dlascl(lapack.General, 0, 0, anrm, bignum, scllen, nrhs, b, ldb)
}
if ibscl == 1 {
impl.Dlascl(lapack.General, 0, 0, smlnum, bnrm, scllen, nrhs, b, ldb)
}
if ibscl == 2 {
impl.Dlascl(lapack.General, 0, 0, bignum, bnrm, scllen, nrhs, b, ldb)
}
return true
}
+57
View File
@@ -0,0 +1,57 @@
// 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 native
import "github.com/gonum/blas"
// Dgeqr2 computes a QR factorization of the m×n matrix a.
//
// In a QR factorization, Q is an m×m orthonormal matrix, and R is an
// upper triangular m×n matrix.
//
// During Dgeqr2, a is modified to contain the information to construct Q and R.
// The upper triangle of a contains the matrix R. The lower triangular elements
// (not including the diagonal) contain the elementary reflectors. Tau is modified
// to contain the reflector scales. Tau must have length at least k = min(m,n), and
// this function will panic otherwise.
//
// The ith elementary reflector can be explicitly constructed by first extracting
// the
// v[j] = 0 j < i
// v[j] = i j == i
// v[j] = a[i*lda+j] j > i
// and computing h_i = I - tau[i] * v * v^T.
//
// The orthonormal matrix Q can be constucted from a product of these elementary
// reflectors, Q = H_1*H_2 ... H_k, where k = min(m,n).
//
// Work is temporary storage of length at least n and this function will panic otherwise.
func (impl Implementation) Dgeqr2(m, n int, a []float64, lda int, tau, work []float64) {
// TODO(btracey): This is oriented such that columns of a are eliminated.
// This likely could be re-arranged to take better advantage of row-major
// storage.
checkMatrix(m, n, a, lda)
if len(work) < n {
panic(badWork)
}
k := min(m, n)
if len(tau) < k {
panic(badTau)
}
for i := 0; i < k; i++ {
// Generate elementary reflector H(i).
a[i*lda+i], tau[i] = impl.Dlarfg(m-i, a[i*lda+i], a[min((i+1), m-1)*lda+i:], lda)
if i < n-1 {
aii := a[i*lda+i]
a[i*lda+i] = 1
impl.Dlarf(blas.Left, m-i, n-i-1,
a[i*lda+i:], lda,
tau[i],
a[i*lda+i+1:], lda,
work)
a[i*lda+i] = aii
}
}
}
+98
View File
@@ -0,0 +1,98 @@
// 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 native
import (
"github.com/gonum/blas"
"github.com/gonum/lapack"
)
// Dgeqrf computes the QR factorization of the m×n matrix a using a blocked
// algorithm. Please see the documentation for Dgeqr2 for a description of the
// parameters at entry and exit.
//
// Work is temporary storage, and lwork specifies the usable memory length.
// At minimum, lwork >= m and this function will panic otherwise.
// Dgeqrf is a blocked LQ factorization, but the block size is limited
// by the temporary space available. If lwork == -1, instead of performing Dgelqf,
// the optimal work length will be stored into work[0].
//
// tau must be at least len min(m,n), and this function will panic otherwise.
func (impl Implementation) Dgeqrf(m, n int, a []float64, lda int, tau, work []float64, lwork int) {
// TODO(btracey): This algorithm is oriented for column-major storage.
// Consider modifying the algorithm to better suit row-major storage.
// nb is the optimal blocksize, i.e. the number of columns transformed at a time.
nb := impl.Ilaenv(1, "DGEQRF", " ", m, n, -1, -1)
lworkopt := n * max(nb, 1)
lworkopt = max(n, lworkopt)
if lwork == -1 {
work[0] = float64(lworkopt)
return
}
checkMatrix(m, n, a, lda)
if len(work) < lwork {
panic(shortWork)
}
if lwork < n {
panic(badWork)
}
k := min(m, n)
if len(tau) < k {
panic(badTau)
}
if k == 0 {
return
}
nbmin := 2 // Minimal number of blocks
var nx int // Use unblocked (unless changed in the next for loop)
iws := n
ldwork := nb
// Only consider blocked if the suggested number of blocks is > 1 and the
// number of columns is sufficiently large.
if nb > 1 && k > nb {
// nx is the crossover point. Above this value the blocked routine should be used.
nx = max(0, impl.Ilaenv(3, "DGEQRF", " ", m, n, -1, -1))
if k > nx {
iws = ldwork * n
if lwork < iws {
// Not enough workspace to use the optimal number of blocks. Instead,
// get the maximum allowable number of blocks.
nb = lwork / n
nbmin = max(2, impl.Ilaenv(2, "DGEQRF", " ", m, n, -1, -1))
}
}
}
for i := range work {
work[i] = 0
}
// Compute QR using a blocked algorithm.
var i int
if nb >= nbmin && nb < k && nx < k {
for i = 0; i < k-nx; i += nb {
ib := min(k-i, nb)
// Compute the QR factorization of the current block.
impl.Dgeqr2(m-i, ib, a[i*lda+i:], lda, tau[i:], work)
if i+ib < n {
// Form the triangular factor of the block reflector and apply H^T
// In Dlarft, work becomes the T matrix.
impl.Dlarft(lapack.Forward, lapack.ColumnWise, m-i, ib,
a[i*lda+i:], lda,
tau[i:],
work, ldwork)
impl.Dlarfb(blas.Left, blas.Trans, lapack.Forward, lapack.ColumnWise,
m-i, n-i-ib, ib,
a[i*lda+i:], lda,
work, ldwork,
a[i*lda+i+ib:], lda,
work[ib*ldwork:], ldwork)
}
}
}
// Call unblocked code on the remaining columns.
if i < k {
impl.Dgeqr2(m-i, n-i, a[i*lda+i:], lda, tau[i:], work)
}
}
+76
View File
@@ -0,0 +1,76 @@
// 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 native
import (
"math"
"github.com/gonum/lapack"
)
// Dlange computes the matrix norm of the general m×n matrix a. The input norm
// specifies the norm computed.
// lapack.MaxAbs: the maximum absolute value of an element.
// lapack.MaxColumnSum: the maximum column sum of the absolute values of the entries.
// lapack.MaxRowSum: the maximum row sum of the absolute values of the entries.
// lapack.Frobenius: the square root of the sum of the squares of the entries.
// If norm == lapack.MaxColumnSum, work must be of length n, and this function will panic otherwise.
// There are no restrictions on work for the other matrix norms.
func (impl Implementation) Dlange(norm lapack.MatrixNorm, m, n int, a []float64, lda int, work []float64) float64 {
// TODO(btracey): These should probably be refactored to use BLAS calls.
checkMatrix(m, n, a, lda)
if m == 0 && n == 0 {
return 0
}
if norm == lapack.MaxAbs {
var value float64
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
value = math.Max(value, math.Abs(a[i*lda+j]))
}
}
return value
}
if norm == lapack.MaxColumnSum {
if len(work) < n {
panic(badWork)
}
for i := 0; i < n; i++ {
work[i] = 0
}
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
work[j] += math.Abs(a[i*lda+j])
}
}
var value float64
for i := 0; i < n; i++ {
value = math.Max(value, work[i])
}
return value
}
if norm == lapack.MaxRowSum {
var value float64
for i := 0; i < m; i++ {
var sum float64
for j := 0; j < n; j++ {
sum += math.Abs(a[i*lda+j])
}
value = math.Max(value, sum)
}
return value
}
if norm == lapack.NormFrob {
var value float64
scale := 0.0
sum := 1.0
for i := 0; i < m; i++ {
scale, sum = impl.Dlassq(n, a[i*lda:], 1, scale, sum)
}
value = scale * math.Sqrt(sum)
return value
}
panic("lapack: bad matrix norm")
}
+12
View File
@@ -0,0 +1,12 @@
// 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 native
import "math"
// Dlapy2 is the LAPACK version of math.Hypot.
func (Implementation) Dlapy2(x, y float64) float64 {
return math.Hypot(x, y)
}
+82
View File
@@ -0,0 +1,82 @@
// 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 native
import (
"github.com/gonum/blas"
"github.com/gonum/blas/blas64"
)
// Dlarf applies an elementary reflector to a general rectangular matrix c.
// This computes
// c = h * c if side == Left
// c = c * h if side == right
// where
// h = 1 - tau * v * v^T
// and c is an m * n matrix.
//
// Work is temporary storage of length at least m if side == Left and at least
// n if side == Right. This function will panic if this length requirement is not met.
func (impl Implementation) Dlarf(side blas.Side, m, n int, v []float64, incv int, tau float64, c []float64, ldc int, work []float64) {
applyleft := side == blas.Left
if (applyleft && len(work) < n) || (!applyleft && len(work) < m) {
panic(badWork)
}
checkMatrix(m, n, c, ldc)
// v has length m if applyleft and n otherwise.
lenV := n
if applyleft {
lenV = m
}
checkVector(lenV, v, incv)
lastv := 0 // last non-zero element of v
lastc := 0 // last non-zero row/column of c
if tau != 0 {
var i int
if applyleft {
lastv = m - 1
} else {
lastv = n - 1
}
if incv > 0 {
i = lastv * incv
}
// Look for the last non-zero row in v.
for lastv >= 0 && v[i] == 0 {
lastv--
i -= incv
}
if applyleft {
// Scan for the last non-zero column in C[0:lastv, :]
lastc = impl.Iladlc(lastv+1, n, c, ldc)
} else {
// Scan for the last non-zero row in C[:, 0:lastv]
lastc = impl.Iladlr(m, lastv+1, c, ldc)
}
}
if lastv == -1 || lastc == -1 {
return
}
// Sometimes 1-indexing is nicer ...
bi := blas64.Implementation()
if applyleft {
// Form H * C
// w[0:lastc+1] = c[1:lastv+1, 1:lastc+1]^T * v[1:lastv+1,1]
bi.Dgemv(blas.Trans, lastv+1, lastc+1, 1, c, ldc, v, incv, 0, work, 1)
// c[0: lastv, 0: lastc] = c[...] - w[0:lastv, 1] * v[1:lastc, 1]^T
bi.Dger(lastv+1, lastc+1, -tau, v, incv, work, 1, c, ldc)
return
}
// Form C*H
// w[0:lastc+1,1] := c[0:lastc+1,0:lastv+1] * v[0:lastv+1,1]
bi.Dgemv(blas.NoTrans, lastc+1, lastv+1, 1, c, ldc, v, incv, 0, work, 1)
// c[0:lastc+1,0:lastv+1] = c[...] - w[0:lastc+1,0] * v[0:lastv+1,0]^T
bi.Dger(lastc+1, lastv+1, -tau, work, 1, v, incv, c, ldc)
}
+424
View File
@@ -0,0 +1,424 @@
// 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 native
import (
"github.com/gonum/blas"
"github.com/gonum/blas/blas64"
"github.com/gonum/lapack"
)
// Dlarfb applies a block reflector to a matrix.
//
// In the call to Dlarfb, the mxn c is multiplied by the implicitly defined matrix h as follows:
// c = h * c if side == Left and trans == NoTrans
// c = c * h if side == Right and trans == NoTrans
// c = h^T * c if side == Left and trans == Trans
// c = c * h^t if side == Right and trans == Trans
// h is a product of elementary reflectors. direct sets the direction of multiplication
// h = h_1 * h_2 * ... * h_k if direct == Forward
// h = h_k * h_k-1 * ... * h_1 if direct == Backward
// The combination of direct and store defines the orientation of the elementary
// reflectors. In all cases the ones on the diagonal are implicitly represented.
//
// If direct == lapack.Forward and store == lapack.ColumnWise
// V = ( 1 )
// ( v1 1 )
// ( v1 v2 1 )
// ( v1 v2 v3 )
// ( v1 v2 v3 )
// If direct == lapack.Forward and store == lapack.RowWise
// V = ( 1 v1 v1 v1 v1 )
// ( 1 v2 v2 v2 )
// ( 1 v3 v3 )
// If direct == lapack.Backward and store == lapack.ColumnWise
// V = ( v1 v2 v3 )
// ( v1 v2 v3 )
// ( 1 v2 v3 )
// ( 1 v3 )
// ( 1 )
// If direct == lapack.Backward and store == lapack.RowWise
// V = ( v1 v1 1 )
// ( v2 v2 v2 1 )
// ( v3 v3 v3 v3 1 )
// An elementary reflector can be explicitly constructed by extracting the
// corresponding elements of v, placing a 1 where the diagonal would be, and
// placing zeros in the remaining elements.
//
// t is a k×k matrix containing the block reflector, and this function will panic
// if t is not of sufficient size. See Dlarft for more information.
//
// Work is a temporary storage matrix with stride ldwork.
// Work must be of size at least n×k side == Left and m×k if side == Right, and
// this function will panic if this size is not met.
func (Implementation) Dlarfb(side blas.Side, trans blas.Transpose, direct lapack.Direct,
store lapack.StoreV, m, n, k int, v []float64, ldv int, t []float64, ldt int,
c []float64, ldc int, work []float64, ldwork int) {
checkMatrix(m, n, c, ldc)
if m == 0 || n == 0 {
return
}
if k < 0 {
panic("lapack: negative number of transforms")
}
if side != blas.Left && side != blas.Right {
panic(badSide)
}
if trans != blas.Trans && trans != blas.NoTrans {
panic(badTrans)
}
if direct != lapack.Forward && direct != lapack.Backward {
panic(badDirect)
}
if store != lapack.ColumnWise && store != lapack.RowWise {
panic(badStore)
}
rowsWork := n
if side == blas.Right {
rowsWork = m
}
checkMatrix(rowsWork, k, work, ldwork)
bi := blas64.Implementation()
transt := blas.Trans
if trans == blas.Trans {
transt = blas.NoTrans
}
// TODO(btracey): This follows the original Lapack code where the
// elements are copied into the columns of the working array. The
// loops should go in the other direction so the data is written
// into the rows of work so the copy is not strided. A bigger change
// would be to replace work with work^T, but benchmarks would be
// needed to see if the change is merited.
if store == lapack.ColumnWise {
if direct == lapack.Forward {
// V1 is the first k rows of C. V2 is the remaining rows.
if side == blas.Left {
// W = C^T V = C1^T V1 + C2^T V2 (stored in work).
// W = C1.
for j := 0; j < k; j++ {
bi.Dcopy(n, c[j*ldc:], 1, work[j:], ldwork)
}
// W = W * V1.
bi.Dtrmm(blas.Right, blas.Lower, blas.NoTrans, blas.Unit,
n, k, 1,
v, ldv,
work, ldwork)
if m > k {
// W = W + C2^T V2.
bi.Dgemm(blas.Trans, blas.NoTrans, n, k, m-k,
1, c[k*ldc:], ldc, v[k*ldv:], ldv,
1, work, ldwork)
}
// W = W * T^T or W * T.
bi.Dtrmm(blas.Right, blas.Upper, transt, blas.NonUnit, n, k,
1, t, ldt,
work, ldwork)
// C -= V * W^T.
if m > k {
// C2 -= V2 * W^T.
bi.Dgemm(blas.NoTrans, blas.Trans, m-k, n, k,
-1, v[k*ldv:], ldv, work, ldwork,
1, c[k*ldc:], ldc)
}
// W *= V1^T.
bi.Dtrmm(blas.Right, blas.Lower, blas.Trans, blas.Unit, n, k,
1, v, ldv,
work, ldwork)
// C1 -= W^T.
// TODO(btracey): This should use blas.Axpy.
for i := 0; i < n; i++ {
for j := 0; j < k; j++ {
c[j*ldc+i] -= work[i*ldwork+j]
}
}
return
}
// Form C = C * H or C * H^T, where C = (C1 C2).
// W = C1.
for i := 0; i < k; i++ {
bi.Dcopy(m, c[i:], ldc, work[i:], ldwork)
}
// W *= V1.
bi.Dtrmm(blas.Right, blas.Lower, blas.NoTrans, blas.Unit, m, k,
1, v, ldv,
work, ldwork)
if n > k {
bi.Dgemm(blas.NoTrans, blas.NoTrans, m, k, n-k,
1, c[k:], ldc, v[k*ldv:], ldv,
1, work, ldwork)
}
// W *= T or T^T.
bi.Dtrmm(blas.Right, blas.Upper, trans, blas.NonUnit, m, k,
1, t, ldt,
work, ldwork)
if n > k {
bi.Dgemm(blas.NoTrans, blas.Trans, m, n-k, k,
-1, work, ldwork, v[k*ldv:], ldv,
1, c[k:], ldc)
}
// C -= W * V^T.
bi.Dtrmm(blas.Right, blas.Lower, blas.Trans, blas.Unit, m, k,
1, v, ldv,
work, ldwork)
// C -= W.
// TODO(btracey): This should use blas.Axpy.
for i := 0; i < m; i++ {
for j := 0; j < k; j++ {
c[i*ldc+j] -= work[i*ldwork+j]
}
}
return
}
// V = (V1)
// = (V2) (last k rows)
// Where V2 is unit upper triangular.
if side == blas.Left {
// Form H * C or
// W = C^T V.
// W = C2^T.
for j := 0; j < k; j++ {
bi.Dcopy(n, c[(m-k+j)*ldc:], 1, work[j:], ldwork)
}
// W *= V2.
bi.Dtrmm(blas.Right, blas.Upper, blas.NoTrans, blas.Unit, n, k,
1, v[(m-k)*ldv:], ldv,
work, ldwork)
if m > k {
// W += C1^T * V1.
bi.Dgemm(blas.Trans, blas.NoTrans, n, k, m-k,
1, c, ldc, v, ldv,
1, work, ldwork)
}
// W *= T or T^T.
bi.Dtrmm(blas.Right, blas.Lower, transt, blas.NonUnit, n, k,
1, t, ldt,
work, ldwork)
// C -= V * W^T.
if m > k {
bi.Dgemm(blas.NoTrans, blas.Trans, m-k, n, k,
-1, v, ldv, work, ldwork,
1, c, ldc)
}
// W *= V2^T.
bi.Dtrmm(blas.Right, blas.Upper, blas.Trans, blas.Unit, n, k,
1, v[(m-k)*ldv:], ldv,
work, ldwork)
// C2 -= W^T.
// TODO(btracey): This should use blas.Axpy.
for i := 0; i < n; i++ {
for j := 0; j < k; j++ {
c[(m-k+j)*ldc+i] -= work[i*ldwork+j]
}
}
return
}
// Form C * H or C * H^T where C = (C1 C2).
// W = C * V.
// W = C2.
for j := 0; j < k; j++ {
bi.Dcopy(m, c[n-k+j:], ldc, work[j:], ldwork)
}
// W = W * V2.
bi.Dtrmm(blas.Right, blas.Upper, blas.NoTrans, blas.Unit, m, k,
1, v[(n-k)*ldv:], ldv,
work, ldwork)
if n > k {
bi.Dgemm(blas.NoTrans, blas.NoTrans, m, k, n-k,
1, c, ldc, v, ldv,
1, work, ldwork)
}
// W *= T or T^T.
bi.Dtrmm(blas.Right, blas.Lower, trans, blas.NonUnit, m, k,
1, t, ldt,
work, ldwork)
// C -= W * V^T.
if n > k {
// C1 -= W * V1^T.
bi.Dgemm(blas.NoTrans, blas.Trans, m, n-k, k,
-1, work, ldwork, v, ldv,
1, c, ldc)
}
// W *= V2^T.
bi.Dtrmm(blas.Right, blas.Upper, blas.Trans, blas.Unit, m, k,
1, v[(n-k)*ldv:], ldv,
work, ldwork)
// C2 -= W.
// TODO(btracey): This should use blas.Axpy.
for i := 0; i < m; i++ {
for j := 0; j < k; j++ {
c[i*ldc+n-k+j] -= work[i*ldwork+j]
}
}
return
}
// Store = Rowwise.
if direct == lapack.Forward {
// V = (V1 V2) where v1 is unit upper triangular.
if side == blas.Left {
// Form H * C or H^T * C where C = (C1; C2).
// W = C^T * V^T.
// W = C1^T.
for j := 0; j < k; j++ {
bi.Dcopy(n, c[j*ldc:], 1, work[j:], ldwork)
}
// W *= V1^T.
bi.Dtrmm(blas.Right, blas.Upper, blas.Trans, blas.Unit, n, k,
1, v, ldv,
work, ldwork)
if m > k {
bi.Dgemm(blas.Trans, blas.Trans, n, k, m-k,
1, c[k*ldc:], ldc, v[k:], ldv,
1, work, ldwork)
}
// W *= T or T^T.
bi.Dtrmm(blas.Right, blas.Upper, transt, blas.NonUnit, n, k,
1, t, ldt,
work, ldwork)
// C -= V^T * W^T.
if m > k {
bi.Dgemm(blas.Trans, blas.Trans, m-k, n, k,
-1, v[k:], ldv, work, ldwork,
1, c[k*ldc:], ldc)
}
// W *= V1.
bi.Dtrmm(blas.Right, blas.Upper, blas.NoTrans, blas.Unit, n, k,
1, v, ldv,
work, ldwork)
// C1 -= W^T.
// TODO(btracey): This should use blas.Axpy.
for i := 0; i < n; i++ {
for j := 0; j < k; j++ {
c[j*ldc+i] -= work[i*ldwork+j]
}
}
return
}
// Form C * H or C * H^T where C = (C1 C2).
// W = C * V^T.
// W = C1.
for j := 0; j < k; j++ {
bi.Dcopy(m, c[j:], ldc, work[j:], ldwork)
}
// W *= V1^T.
bi.Dtrmm(blas.Right, blas.Upper, blas.Trans, blas.Unit, m, k,
1, v, ldv,
work, ldwork)
if n > k {
bi.Dgemm(blas.NoTrans, blas.Trans, m, k, n-k,
1, c[k:], ldc, v[k:], ldv,
1, work, ldwork)
}
// W *= T or T^T.
bi.Dtrmm(blas.Right, blas.Upper, trans, blas.NonUnit, m, k,
1, t, ldt,
work, ldwork)
// C -= W * V.
if n > k {
bi.Dgemm(blas.NoTrans, blas.NoTrans, m, n-k, k,
-1, work, ldwork, v[k:], ldv,
1, c[k:], ldc)
}
// W *= V1.
bi.Dtrmm(blas.Right, blas.Upper, blas.NoTrans, blas.Unit, m, k,
1, v, ldv,
work, ldwork)
// C1 -= W.
// TODO(btracey): This should use blas.Axpy.
for i := 0; i < m; i++ {
for j := 0; j < k; j++ {
c[i*ldc+j] -= work[i*ldwork+j]
}
}
return
}
// V = (V1 V2) where V2 is the last k columns and is lower unit triangular.
if side == blas.Left {
// Form H * C or H^T C where C = (C1 ; C2).
// W = C^T * V^T.
// W = C2^T.
for j := 0; j < k; j++ {
bi.Dcopy(n, c[(m-k+j)*ldc:], 1, work[j:], ldwork)
}
// W *= V2^T.
bi.Dtrmm(blas.Right, blas.Lower, blas.Trans, blas.Unit, n, k,
1, v[m-k:], ldv,
work, ldwork)
if m > k {
bi.Dgemm(blas.Trans, blas.Trans, n, k, m-k,
1, c, ldc, v, ldv,
1, work, ldwork)
}
// W *= T or T^T.
bi.Dtrmm(blas.Right, blas.Lower, transt, blas.NonUnit, n, k,
1, t, ldt,
work, ldwork)
// C -= V^T * W^T.
if m > k {
bi.Dgemm(blas.Trans, blas.Trans, m-k, n, k,
-1, v, ldv, work, ldwork,
1, c, ldc)
}
// W *= V2.
bi.Dtrmm(blas.Right, blas.Lower, blas.NoTrans, blas.Unit, n, k,
1, v[m-k:], ldv,
work, ldwork)
// C2 -= W^T.
// TODO(btracey): This should use blas.Axpy.
for i := 0; i < n; i++ {
for j := 0; j < k; j++ {
c[(m-k+j)*ldc+i] -= work[i*ldwork+j]
}
}
return
}
// Form C * H or C * H^T where C = (C1 C2).
// W = C * V^T.
// W = C2.
for j := 0; j < k; j++ {
bi.Dcopy(m, c[n-k+j:], ldc, work[j:], ldwork)
}
// W *= V2^T.
bi.Dtrmm(blas.Right, blas.Lower, blas.Trans, blas.Unit, m, k,
1, v[n-k:], ldv,
work, ldwork)
if n > k {
bi.Dgemm(blas.NoTrans, blas.Trans, m, k, n-k,
1, c, ldc, v, ldv,
1, work, ldwork)
}
// W *= T or T^T.
bi.Dtrmm(blas.Right, blas.Lower, trans, blas.NonUnit, m, k,
1, t, ldt,
work, ldwork)
// C -= W * V.
if n > k {
bi.Dgemm(blas.NoTrans, blas.NoTrans, m, n-k, k,
-1, work, ldwork, v, ldv,
1, c, ldc)
}
// W *= V2.
bi.Dtrmm(blas.Right, blas.Lower, blas.NoTrans, blas.Unit, m, k,
1, v[n-k:], ldv,
work, ldwork)
// C1 -= W.
// TODO(btracey): This should use blas.Axpy.
for i := 0; i < m; i++ {
for j := 0; j < k; j++ {
c[i*ldc+n-k+j] -= work[i*ldwork+j]
}
}
}
+60
View File
@@ -0,0 +1,60 @@
// 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 native
import (
"math"
"github.com/gonum/blas/blas64"
)
// Dlarfg generates an elementary reflector for a Householder matrix. It creates
// a real elementary reflector of order n such that
// H * (alpha) = (beta)
// ( x) ( 0)
// H^T * H = I
// H is represented in the form
// H = 1 - tau * (1; v) * (1 v^T)
// where tau is a real scalar.
//
// On entry, x contains the vector x, on exit it contains v.
func (impl Implementation) Dlarfg(n int, alpha float64, x []float64, incX int) (beta, tau float64) {
if n < 0 {
panic(nLT0)
}
if n <= 1 {
return alpha, 0
}
checkVector(n-1, x, incX)
bi := blas64.Implementation()
xnorm := bi.Dnrm2(n-1, x, incX)
if xnorm == 0 {
return alpha, 0
}
beta = -math.Copysign(impl.Dlapy2(alpha, xnorm), alpha)
safmin := dlamchS / dlamchE
knt := 0
if math.Abs(beta) < safmin {
// xnorm and beta may be innacurate, scale x and recompute.
rsafmn := 1 / safmin
for {
knt++
bi.Dscal(n-1, rsafmn, x, incX)
beta *= rsafmn
alpha *= rsafmn
if math.Abs(beta) >= safmin {
break
}
}
xnorm = bi.Dnrm2(n-1, x, incX)
beta = -math.Copysign(impl.Dlapy2(alpha, xnorm), alpha)
}
tau = (beta - alpha) / beta
bi.Dscal(n-1, 1/(alpha-beta), x, incX)
for j := 0; j < knt; j++ {
beta *= safmin
}
return beta, tau
}
+148
View File
@@ -0,0 +1,148 @@
// 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 native
import (
"github.com/gonum/blas"
"github.com/gonum/blas/blas64"
"github.com/gonum/lapack"
)
// Dlarft forms the triangular factor t of a block reflector, storing the answer
// in t.
// H = 1 - V * T * V^T if store == lapack.ColumnWise
// H = 1 - V^T * T * V if store == lapack.RowWise
// H is defined by a product of the elementary reflectors where
// H = H_1 * H_2 * ... * H_k if direct == lapack.Forward
// H = H_k * H_k-1 * ... * H_1 if direct == lapack.Backward
//
// t is a k×k triangular matrix. t is upper triangular if direct = lapack.Forward
// and lower triangular otherwise. This function will panic if t is not of
// sufficient size.
//
// store describes the storage of the elementary reflectors in v. Please see
// Dlarfb for a description of layout.
//
// tau contains the scalar factor of the elementary reflectors h.
func (Implementation) Dlarft(direct lapack.Direct, store lapack.StoreV, n, k int,
v []float64, ldv int, tau []float64, t []float64, ldt int) {
if n == 0 {
return
}
if n < 0 || k < 0 {
panic(negDimension)
}
if direct != lapack.Forward && direct != lapack.Backward {
panic(badDirect)
}
if store != lapack.RowWise && store != lapack.ColumnWise {
panic(badStore)
}
if len(tau) < k {
panic(badTau)
}
checkMatrix(k, k, t, ldt)
bi := blas64.Implementation()
// TODO(btracey): There are a number of minor obvious loop optimizations here.
// TODO(btracey): It may be possible to rearrange some of the code so that
// index of 1 is more common in the Dgemv.
if direct == lapack.Forward {
prevlastv := n - 1
for i := 0; i < k; i++ {
prevlastv = max(i, prevlastv)
if tau[i] == 0 {
for j := 0; j <= i; j++ {
t[j*ldt+i] = 0
}
continue
}
var lastv int
if store == lapack.ColumnWise {
// skip trailing zeros
for lastv = n - 1; lastv >= i+1; lastv-- {
if v[lastv*ldv+i] != 0 {
break
}
}
for j := 0; j < i; j++ {
t[j*ldt+i] = -tau[i] * v[i*ldv+j]
}
j := min(lastv, prevlastv)
bi.Dgemv(blas.Trans, j-i, i,
-tau[i], v[(i+1)*ldv:], ldv, v[(i+1)*ldv+i:], ldv,
1, t[i:], ldt)
} else {
for lastv = n - 1; lastv >= i+1; lastv-- {
if v[i*ldv+lastv] != 0 {
break
}
}
for j := 0; j < i; j++ {
t[j*ldt+i] = -tau[i] * v[j*ldv+i]
}
j := min(lastv, prevlastv)
bi.Dgemv(blas.NoTrans, i, j-i,
-tau[i], v[i+1:], ldv, v[i*ldv+i+1:], 1,
1, t[i:], ldt)
}
bi.Dtrmv(blas.Upper, blas.NoTrans, blas.NonUnit, i, t, ldt, t[i:], ldt)
t[i*ldt+i] = tau[i]
if i > 1 {
prevlastv = max(prevlastv, lastv)
} else {
prevlastv = lastv
}
}
return
}
prevlastv := 0
for i := k - 1; i >= 0; i-- {
if tau[i] == 0 {
for j := i; j < k; j++ {
t[j*ldt+i] = 0
}
continue
}
var lastv int
if i < k-1 {
if store == lapack.ColumnWise {
for lastv = 0; lastv < i; lastv++ {
if v[lastv*ldv+i] != 0 {
break
}
}
for j := i + 1; j < k; j++ {
t[j*ldt+i] = -tau[i] * v[(n-k+i)*ldv+j]
}
j := max(lastv, prevlastv)
bi.Dgemv(blas.Trans, n-k+i-j, k-i-1,
-tau[i], v[j*ldv+i+1:], ldv, v[j*ldv+i:], ldv,
1, t[(i+1)*ldt+i:], ldt)
} else {
for lastv := 0; lastv < i; lastv++ {
if v[i*ldv+lastv] != 0 {
break
}
}
for j := i + 1; j < k; j++ {
t[j*ldt+i] = -tau[i] * v[j*ldv+n-k+i]
}
j := max(lastv, prevlastv)
bi.Dgemv(blas.NoTrans, k-i-1, n-k+i-j,
-tau[i], v[(i+1)*ldv+j:], ldv, v[i*ldv+j:], 1,
1, t[(i+1)*ldt+i:], ldt)
}
bi.Dtrmv(blas.Lower, blas.NoTrans, blas.NonUnit, k-i-1,
t[(i+1)*ldt+i+1:], ldt,
t[(i+1)*ldt+i:], ldt)
if i > 0 {
prevlastv = min(prevlastv, lastv)
} else {
prevlastv = lastv
}
}
t[i*ldt+i] = tau[i]
}
}
+72
View File
@@ -0,0 +1,72 @@
// 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 native
import (
"math"
"github.com/gonum/lapack"
)
// Dlascl multiplies a rectangular matrix by a scalar.
func (impl Implementation) Dlascl(kind lapack.MatrixType, kl, ku int, cfrom, cto float64, m, n int, a []float64, lda int) {
checkMatrix(m, n, a, lda)
if cfrom == 0 {
panic("dlascl: zero divisor")
}
if math.IsNaN(cfrom) || math.IsNaN(cto) {
panic("dlascl: NaN scale factor")
}
if n == 0 || m == 0 {
return
}
smlnum := dlamchS
bignum := 1 / smlnum
cfromc := cfrom
ctoc := cto
cfrom1 := cfromc * smlnum
for {
var done bool
var mul, ctol float64
if cfrom1 == cfromc {
// cfromc is inf
mul = ctoc / cfromc
done = true
ctol = ctoc
} else {
ctol = ctoc / bignum
if ctol == ctoc {
// ctoc is either 0 or inf.
mul = ctoc
done = true
cfromc = 1
} else if math.Abs(cfrom1) > math.Abs(ctoc) && ctoc != 0 {
mul = smlnum
done = false
cfromc = cfrom1
} else if math.Abs(ctol) > math.Abs(cfromc) {
mul = bignum
done = false
ctoc = ctol
} else {
mul = ctoc / cfromc
done = true
}
}
switch kind {
default:
panic("lapack: not implemented")
case lapack.General:
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
a[i*lda+j] = a[i*lda+j] * mul
}
}
}
if done {
break
}
}
}
+37
View File
@@ -0,0 +1,37 @@
// 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 native
import "github.com/gonum/blas"
// Dlaset sets the off-diagonal elements of a to alpha, and the diagonal elements
// of a to beta. If uplo == blas.Upper, only the upper diagonal elements are set.
// If uplo == blas.Lower, only the lower diagonal elements are set. If uplo is
// otherwise, all of the elements of a are set.
func (impl Implementation) Dlaset(uplo blas.Uplo, m, n int, alpha, beta float64, a []float64, lda int) {
checkMatrix(m, n, a, lda)
if uplo == blas.Upper {
for i := 0; i < m; i++ {
for j := i + 1; j < n; j++ {
a[i*lda+j] = alpha
}
}
} else if uplo == blas.Lower {
for i := 0; i < m; i++ {
for j := 0; j < i; j++ {
a[i*lda+j] = alpha
}
}
} else {
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
a[i*lda+j] = alpha
}
}
}
for i := 0; i < min(m, n); i++ {
a[i*lda+i] = beta
}
}
+29
View File
@@ -0,0 +1,29 @@
// 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 native
import "math"
// Dlassq updates a sum of squares in scaled form. The input parameters scale and
// sumsq represent the current scale and total sum of squares. These values are
// updated with the information in the first n elements of the vector specified
// by x and incX.
func (impl Implementation) Dlassq(n int, x []float64, incx int, scale float64, sumsq float64) (scl, smsq float64) {
if n <= 0 {
return scale, sumsq
}
for ix := 0; ix <= (n-1)*incx; ix += incx {
absxi := math.Abs(x[ix])
if absxi > 0 || math.IsNaN(absxi) {
if scale < absxi {
sumsq = 1 + sumsq*(scale/absxi)*(scale/absxi)
scale = absxi
} else {
sumsq += (absxi / scale) * (absxi / scale)
}
}
}
return scale, sumsq
}
+28
View File
@@ -0,0 +1,28 @@
// 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 native is a pure-go implementation of the LAPACK API. The LAPACK API defines
// a set of algorithms for advanced matrix operations.
//
// The function definitions and implementations follow that of the netlib reference
// implementation. Please see http://www.netlib.org/lapack/explore-html/ for more
// information, and http://www.netlib.org/lapack/explore-html/d4/de1/_l_i_c_e_n_s_e_source.html
// for more license information.
//
// Slice function arguments frequently represent vectors and matrices. The data
// layout is identical to that found in https://godoc.org/github.com/gonum/blas/native.
//
// Most LAPACK functions are built on top the routines defined in the BLAS API,
// and as such the computation time for many LAPACK functions is
// dominated by BLAS calls. Here, BLAS is accessed through the
// the blas64 package (https://godoc.org/github.com/gonum/blas/blas64). In particular,
// this implies that an external BLAS library will be used if it is
// registered in blas64.
//
// The full LAPACK capability has not been implemented at present. The full
// API is very large, containing approximately 200 functions for double precision
// alone. Future additions will be focused on supporting the gonum matrix
// package (https://godoc.org/github.com/gonum/matrix/mat64), though pull requests
// with implementations and tests for LAPACK function are encouraged.
package native
+86
View File
@@ -0,0 +1,86 @@
// 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 native
import "github.com/gonum/blas"
// Dorm2r multiplies a general matrix c by an orthogonal matrix from a QR factorization
// determined by Dgeqrf.
// C = Q * C if side == blas.Left and trans == blas.NoTrans
// C = Q^T * C if side == blas.Left and trans == blas.Trans
// C = C * Q if side == blas.Right and trans == blas.NoTrans
// C = C * Q^T if side == blas.Right and trans == blas.Trans
// If side == blas.Left, a is a matrix of size m×k, and if side == blas.Right
// a is of size n×k.
//
// Tau contains the householder factors and is of length at least k and this function
// will panic otherwise.
//
// Work is temporary storage of length at least n if side == blas.Left
// and at least m if side == blas.Right and this function will panic otherwise.
func (impl Implementation) Dorm2r(side blas.Side, trans blas.Transpose, m, n, k int, a []float64, lda int, tau, c []float64, ldc int, work []float64) {
if side != blas.Left && side != blas.Right {
panic(badSide)
}
if trans != blas.Trans && trans != blas.NoTrans {
panic(badTrans)
}
left := side == blas.Left
notran := trans == blas.NoTrans
if left {
// Q is m x m
checkMatrix(m, k, a, lda)
if len(work) < n {
panic(badWork)
}
} else {
// Q is n x n
checkMatrix(n, k, a, lda)
if len(work) < m {
panic(badWork)
}
}
checkMatrix(m, n, c, ldc)
if m == 0 || n == 0 || k == 0 {
return
}
if len(tau) < k {
panic(badTau)
}
if left {
if notran {
for i := k - 1; i >= 0; i-- {
aii := a[i*lda+i]
a[i*lda+i] = 1
impl.Dlarf(side, m-i, n, a[i*lda+i:], lda, tau[i], c[i*ldc:], ldc, work)
a[i*lda+i] = aii
}
return
}
for i := 0; i < k; i++ {
aii := a[i*lda+i]
a[i*lda+i] = 1
impl.Dlarf(side, m-i, n, a[i*lda+i:], lda, tau[i], c[i*ldc:], ldc, work)
a[i*lda+i] = aii
}
return
}
if notran {
for i := 0; i < k; i++ {
aii := a[i*lda+i]
a[i*lda+i] = 1
impl.Dlarf(side, m, n-i, a[i*lda+i:], lda, tau[i], c[i:], ldc, work)
a[i*lda+i] = aii
}
return
}
for i := k - 1; i >= 0; i-- {
aii := a[i*lda+i]
a[i*lda+i] = 1
impl.Dlarf(side, m, n-i, a[i*lda+i:], lda, tau[i], c[i:], ldc, work)
a[i*lda+i] = aii
}
}
+83
View File
@@ -0,0 +1,83 @@
// 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 native
import "github.com/gonum/blas"
// Dorml2 multiplies a general matrix c by an orthogonal matrix from an LQ factorization
// determined by Dgelqf.
// C = Q * C if side == blas.Left and trans == blas.NoTrans
// C = Q^T * C if side == blas.Left and trans == blas.Trans
// C = C * Q if side == blas.Right and trans == blas.NoTrans
// C = C * Q^T if side == blas.Right and trans == blas.Trans
// If side == blas.Left, a is a matrix of side k×m, and if side == blas.Right
// a is of size k×n.
//
//
// Tau contains the householder factors and is of length at least k and this function will
// panic otherwise.
//
// Work is temporary storage of length at least n if side == blas.Left
// and at least m if side == blas.Right and this function will panic otherwise.
func (impl Implementation) Dorml2(side blas.Side, trans blas.Transpose, m, n, k int, a []float64, lda int, tau, c []float64, ldc int, work []float64) {
if side != blas.Left && side != blas.Right {
panic(badSide)
}
if trans != blas.Trans && trans != blas.NoTrans {
panic(badTrans)
}
left := side == blas.Left
notran := trans == blas.NoTrans
if left {
checkMatrix(k, m, a, lda)
if len(work) < n {
panic(badWork)
}
} else {
checkMatrix(k, n, a, lda)
if len(work) < m {
panic(badWork)
}
}
checkMatrix(m, n, c, ldc)
if m == 0 || n == 0 || k == 0 {
return
}
switch {
case left && notran:
for i := 0; i < k; i++ {
aii := a[i*lda+i]
a[i*lda+i] = 1
impl.Dlarf(side, m-i, n, a[i*lda+i:], 1, tau[i], c[i*ldc:], ldc, work)
a[i*lda+i] = aii
}
return
case left && !notran:
for i := k - 1; i >= 0; i-- {
aii := a[i*lda+i]
a[i*lda+i] = 1
impl.Dlarf(side, m-i, n, a[i*lda+i:], 1, tau[i], c[i*ldc:], ldc, work)
a[i*lda+i] = aii
}
return
case !left && notran:
for i := k - 1; i >= 0; i-- {
aii := a[i*lda+i]
a[i*lda+i] = 1
impl.Dlarf(side, m, n-i, a[i*lda+i:], 1, tau[i], c[i:], ldc, work)
a[i*lda+i] = aii
}
return
case !left && !notran:
for i := 0; i < k; i++ {
aii := a[i*lda+i]
a[i*lda+i] = 1
impl.Dlarf(side, m, n-i, a[i*lda+i:], 1, tau[i], c[i:], ldc, work)
a[i*lda+i] = aii
}
return
}
}
+155
View File
@@ -0,0 +1,155 @@
// 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 native
import (
"github.com/gonum/blas"
"github.com/gonum/lapack"
)
// Dormlq multiplies the matrix c by the othogonal matrix q defined by the
// slices a and tau. A and tau are as returned from Dgelqf.
// C = Q * C if side == blas.Left and trans == blas.NoTrans
// C = Q^T * C if side == blas.Left and trans == blas.Trans
// C = C * Q if side == blas.Right and trans == blas.NoTrans
// C = C * Q^T if side == blas.Right and trans == blas.Trans
// If side == blas.Left, a is a matrix of side k×m, and if side == blas.Right
// a is of size k×n. This uses a blocked algorithm.
//
// Work is temporary storage, and lwork specifies the usable memory length.
// At minimum, lwork >= m if side == blas.Left and lwork >= n if side == blas.Right,
// and this function will panic otherwise.
// Dormlq uses a block algorithm, but the block size is limited
// by the temporary space available. If lwork == -1, instead of performing Dormlq,
// the optimal work length will be stored into work[0].
//
// Tau contains the householder scales and must have length at least k, and
// this function will panic otherwise.
func (impl Implementation) Dormlq(side blas.Side, trans blas.Transpose, m, n, k int, a []float64, lda int, tau, c []float64, ldc int, work []float64, lwork int) {
if side != blas.Left && side != blas.Right {
panic(badSide)
}
if trans != blas.Trans && trans != blas.NoTrans {
panic(badTrans)
}
left := side == blas.Left
notran := trans == blas.NoTrans
if left {
checkMatrix(k, m, a, lda)
} else {
checkMatrix(k, n, a, lda)
}
checkMatrix(m, n, c, ldc)
if len(tau) < k {
panic(badTau)
}
const nbmax = 64
nw := n
if !left {
nw = m
}
opts := string(side) + string(trans)
nb := min(nbmax, impl.Ilaenv(1, "DORMLQ", opts, m, n, k, -1))
lworkopt := max(1, nw) * nb
if lwork == -1 {
work[0] = float64(lworkopt)
return
}
if left {
if lwork < n {
panic(badWork)
}
} else {
if lwork < m {
panic(badWork)
}
}
if m == 0 || n == 0 || k == 0 {
return
}
nbmin := 2
ldwork := nb
if nb > 1 && nb < k {
iws := nw * nb
if lwork < iws {
nb = lwork / nw
nbmin = max(2, impl.Ilaenv(2, "DORMLQ", opts, m, n, k, -1))
}
}
if nb < nbmin || nb >= k {
// Call unblocked code
impl.Dorml2(side, trans, m, n, k, a, lda, tau, c, ldc, work)
return
}
ldt := nb
t := make([]float64, nb*ldt)
transt := blas.NoTrans
if notran {
transt = blas.Trans
}
switch {
case left && notran:
for i := 0; i < k; i += nb {
ib := min(nb, k-i)
impl.Dlarft(lapack.Forward, lapack.RowWise, m-i, ib,
a[i*lda+i:], lda,
tau[i:],
t, ldt)
impl.Dlarfb(side, transt, lapack.Forward, lapack.RowWise, m-i, n, ib,
a[i*lda+i:], lda,
t, ldt,
c[i*ldc:], ldc,
work, ldwork)
}
return
case left && !notran:
for i := ((k - 1) / nb) * nb; i >= 0; i -= nb {
ib := min(nb, k-i)
impl.Dlarft(lapack.Forward, lapack.RowWise, m-i, ib,
a[i*lda+i:], lda,
tau[i:],
t, ldt)
impl.Dlarfb(side, transt, lapack.Forward, lapack.RowWise, m-i, n, ib,
a[i*lda+i:], lda,
t, ldt,
c[i*ldc:], ldc,
work, ldwork)
}
return
case !left && notran:
for i := ((k - 1) / nb) * nb; i >= 0; i -= nb {
ib := min(nb, k-i)
impl.Dlarft(lapack.Forward, lapack.RowWise, n-i, ib,
a[i*lda+i:], lda,
tau[i:],
t, ldt)
impl.Dlarfb(side, transt, lapack.Forward, lapack.RowWise, m, n-i, ib,
a[i*lda+i:], lda,
t, ldt,
c[i:], ldc,
work, ldwork)
}
return
case !left && !notran:
for i := 0; i < k; i += nb {
ib := min(nb, k-i)
impl.Dlarft(lapack.Forward, lapack.RowWise, n-i, ib,
a[i*lda+i:], lda,
tau[i:],
t, ldt)
impl.Dlarfb(side, transt, lapack.Forward, lapack.RowWise, m, n-i, ib,
a[i*lda+i:], lda,
t, ldt,
c[i:], ldc,
work, ldwork)
}
return
}
}
+139
View File
@@ -0,0 +1,139 @@
// 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 native
import (
"github.com/gonum/blas"
"github.com/gonum/lapack"
)
// Dormqr multiplies the matrix c by the othogonal matrix q defined by the
// slices a and tau. A and tau are as returned from Dgeqrf.
// C = Q * C if side == blas.Left and trans == blas.NoTrans
// C = Q^T * C if side == blas.Left and trans == blas.Trans
// C = C * Q if side == blas.Right and trans == blas.NoTrans
// C = C * Q^T if side == blas.Right and trans == blas.Trans
// If side == blas.Left, a is a matrix of side k×m, and if side == blas.Right
// a is of size k×n. This uses a blocked algorithm.
//
// Work is temporary storage, and lwork specifies the usable memory length.
// At minimum, lwork >= m if side == blas.Left and lwork >= n if side == blas.Right,
// and this function will panic otherwise.
// Dormqr uses a block algorithm, but the block size is limited
// by the temporary space available. If lwork == -1, instead of performing Dormqr,
// the optimal work length will be stored into work[0].
//
// Tau contains the householder scales and must have length at least k, and
// this function will panic otherwise.
func (impl Implementation) Dormqr(side blas.Side, trans blas.Transpose, m, n, k int, a []float64, lda int, tau, c []float64, ldc int, work []float64, lwork int) {
left := side == blas.Left
notran := trans == blas.NoTrans
if left {
checkMatrix(m, k, a, lda)
} else {
checkMatrix(n, k, a, lda)
}
checkMatrix(m, n, c, ldc)
const nbmax = 64
nw := n
if side == blas.Right {
nw = m
}
opts := string(side) + string(trans)
nb := min(nbmax, impl.Ilaenv(1, "DORMQR", opts, m, n, k, -1))
lworkopt := max(1, nw) * nb
if lwork == -1 {
work[0] = float64(lworkopt)
return
}
if left {
if lwork < n {
panic(badWork)
}
} else {
if lwork < m {
panic(badWork)
}
}
if m == 0 || n == 0 || k == 0 {
return
}
nbmin := 2
ldwork := nb
if nb > 1 && nb < k {
iws := nw * nb
if lwork < iws {
nb = lwork / nw
nbmin = max(2, impl.Ilaenv(2, "DORMQR", opts, m, n, k, -1))
}
}
if nb < nbmin || nb >= k {
// Call unblocked code
impl.Dorm2r(side, trans, m, n, k, a, lda, tau, c, ldc, work)
return
}
ldt := nb
t := make([]float64, nb*ldt)
switch {
case left && notran:
for i := ((k - 1) / nb) * nb; i >= 0; i -= nb {
ib := min(nb, k-i)
impl.Dlarft(lapack.Forward, lapack.ColumnWise, m-i, ib,
a[i*lda+i:], lda,
tau[i:],
t, ldt)
impl.Dlarfb(side, trans, lapack.Forward, lapack.ColumnWise, m-i, n, ib,
a[i*lda+i:], lda,
t, ldt,
c[i*ldc:], ldc,
work, ldwork)
}
return
case left && !notran:
for i := 0; i < k; i += nb {
ib := min(nb, k-i)
impl.Dlarft(lapack.Forward, lapack.ColumnWise, m-i, ib,
a[i*lda+i:], lda,
tau[i:],
t, ldt)
impl.Dlarfb(side, trans, lapack.Forward, lapack.ColumnWise, m-i, n, ib,
a[i*lda+i:], lda,
t, ldt,
c[i*ldc:], ldc,
work, ldwork)
}
return
case !left && notran:
for i := 0; i < k; i += nb {
ib := min(nb, k-i)
impl.Dlarft(lapack.Forward, lapack.ColumnWise, n-i, ib,
a[i*lda+i:], lda,
tau[i:],
t, ldt)
impl.Dlarfb(side, trans, lapack.Forward, lapack.ColumnWise, m, n-i, ib,
a[i*lda+i:], lda,
t, ldt,
c[i:], ldc,
work, ldwork)
}
return
case !left && !notran:
for i := ((k - 1) / nb) * nb; i >= 0; i -= nb {
ib := min(nb, k-i)
impl.Dlarft(lapack.Forward, lapack.ColumnWise, n-i, ib,
a[i*lda+i:], lda,
tau[i:],
t, ldt)
impl.Dlarfb(side, trans, lapack.Forward, lapack.ColumnWise, m, n-i, ib,
a[i*lda+i:], lda,
t, ldt,
c[i:], ldc,
work, ldwork)
}
return
}
}
+73
View File
@@ -0,0 +1,73 @@
// 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 native
import (
"math"
"github.com/gonum/blas"
"github.com/gonum/blas/blas64"
)
// Dpotf2 computes the cholesky decomposition of the symmetric positive definite
// matrix a. If ul == blas.Upper, then a is stored as an upper-triangular matrix,
// and a = U^T U is stored in place into a. If ul == blas.Lower, then a = L L^T
// is computed and stored in-place into a. If a is not positive definite, false
// is returned. This is the unblocked version of the algorithm.
func (Implementation) Dpotf2(ul blas.Uplo, n int, a []float64, lda int) (ok bool) {
if ul != blas.Upper && ul != blas.Lower {
panic(badUplo)
}
if n < 0 {
panic(nLT0)
}
if lda < n {
panic(badLdA)
}
if n == 0 {
return true
}
bi := blas64.Implementation()
if ul == blas.Upper {
for j := 0; j < n; j++ {
ajj := a[j*lda+j]
if j != 0 {
ajj -= bi.Ddot(j, a[j:], lda, a[j:], lda)
}
if ajj <= 0 || math.IsNaN(ajj) {
a[j*lda+j] = ajj
return false
}
ajj = math.Sqrt(ajj)
a[j*lda+j] = ajj
if j < n-1 {
bi.Dgemv(blas.Trans, j, n-j-1,
-1, a[j+1:], lda, a[j:], lda,
1, a[j*lda+j+1:], 1)
bi.Dscal(n-j-1, 1/ajj, a[j*lda+j+1:], 1)
}
}
return true
}
for j := 0; j < n; j++ {
ajj := a[j*lda+j]
if j != 0 {
ajj -= bi.Ddot(j, a[j*lda:], 1, a[j*lda:], 1)
}
if ajj <= 0 || math.IsNaN(ajj) {
a[j*lda+j] = ajj
return false
}
ajj = math.Sqrt(ajj)
a[j*lda+j] = ajj
if j < n-1 {
bi.Dgemv(blas.NoTrans, n-j-1, j,
-1, a[(j+1)*lda:], lda, a[j*lda:], 1,
1, a[(j+1)*lda+j:], lda)
bi.Dscal(n-j-1, 1/ajj, a[(j+1)*lda+j:], lda)
}
}
return true
}

Some files were not shown because too many files have changed in this diff Show More