diff --git a/lapack/gonum/dlapmr.go b/lapack/gonum/dlapmr.go new file mode 100644 index 00000000..bd484eaf --- /dev/null +++ b/lapack/gonum/dlapmr.go @@ -0,0 +1,85 @@ +// Copyright ©2022 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 gonum + +import "gonum.org/v1/gonum/blas/blas64" + +// Dlapmr rearranges the rows of the m×n matrix X as specified by the permutation +// k[0],k[1],...,k[m-1] of the integers 0,...,m-1. +// +// If forward is true, a forward permutation is applied: +// X[k[i],0:n] is moved to X[i,0:n] for i=0,1,...,m-1. +// If forward is false, a backward permutation is applied: +// X[i,0:n] is moved to X[k[i],0:n] for i=0,1,...,m-1. +// +// k must have length m, otherwise Dlapmr will panic. +func (impl Implementation) Dlapmr(forward bool, m, n int, x []float64, ldx int, k []int) { + switch { + case m < 0: + panic(mLT0) + case n < 0: + panic(nLT0) + case ldx < max(1, n): + panic(badLdX) + } + + // Quick return if possible. + if m == 0 || n == 0 { + return + } + + switch { + case len(x) < (m-1)*ldx+n: + panic(shortX) + case len(k) != m: + panic(badLenK) + } + + // Quick return if possible. + if m == 1 { + return + } + + bi := blas64.Implementation() + + for i, ki := range k { + k[i] = -(ki + 1) + } + if forward { + for i, ki := range k { + if ki >= 0 { + continue + } + j := i + k[j] = -k[j] - 1 + in := k[j] + for { + if k[in] >= 0 { + break + } + bi.Dswap(n, x[j*ldx:], 1, x[in*ldx:], 1) + k[in] = -k[in] - 1 + j = in + in = k[in] + } + } + } else { + for i, ki := range k { + if ki >= 0 { + continue + } + k[i] = -ki - 1 + j := k[i] + for { + if j == i { + break + } + bi.Dswap(n, x[i*ldx:], 1, x[j*ldx:], 1) + k[j] = -k[j] - 1 + j = k[j] + } + } + } +} diff --git a/lapack/gonum/lapack_test.go b/lapack/gonum/lapack_test.go index 50ae3e0f..5a3c418c 100644 --- a/lapack/gonum/lapack_test.go +++ b/lapack/gonum/lapack_test.go @@ -248,6 +248,11 @@ func TestDlapll(t *testing.T) { testlapack.DlapllTest(t, impl) } +func TestDlapmr(t *testing.T) { + t.Parallel() + testlapack.DlapmrTest(t, impl) +} + func TestDlapmt(t *testing.T) { t.Parallel() testlapack.DlapmtTest(t, impl) diff --git a/lapack/testlapack/dlapmr.go b/lapack/testlapack/dlapmr.go new file mode 100644 index 00000000..c71ae030 --- /dev/null +++ b/lapack/testlapack/dlapmr.go @@ -0,0 +1,85 @@ +// Copyright ©2022 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 testlapack + +import ( + "fmt" + "testing" + + "golang.org/x/exp/rand" + "gonum.org/v1/gonum/blas" + "gonum.org/v1/gonum/blas/blas64" +) + +type Dlapmrer interface { + Dlapmr(forwrd bool, m, n int, x []float64, ldx int, k []int) +} + +func DlapmrTest(t *testing.T, impl Dlapmrer) { + rnd := rand.New(rand.NewSource(1)) + for _, fwd := range []bool{true, false} { + for _, m := range []int{0, 1, 2, 3, 4, 5, 10} { + for _, n := range []int{0, 1, 4} { + for _, ldx := range []int{max(1, n), n + 3} { + dlapmrTest(t, impl, rnd, fwd, m, n, ldx) + } + } + } + } +} + +func dlapmrTest(t *testing.T, impl Dlapmrer, rnd *rand.Rand, fwd bool, m, n, ldx int) { + name := fmt.Sprintf("forwrd=%v,m=%d,n=%d,ldx=%d", fwd, m, n, ldx) + + bi := blas64.Implementation() + + // Generate a random permutation and simultaneously apply it to the rows of the identity matrix. + k := make([]int, m) + for i := range k { + k[i] = i + } + p := eye(m, m) + for i := 0; i < m-1; i++ { + j := i + rnd.Intn(m-i) + k[i], k[j] = k[j], k[i] + bi.Dswap(m, p.Data[i*p.Stride:], 1, p.Data[j*p.Stride:], 1) + } + kCopy := make([]int, len(k)) + copy(kCopy, k) + + // Generate a random matrix X. + x := randomGeneral(m, n, ldx, rnd) + + // Applying the permutation k with Dlapmr is the same as multiplying X with P or Pᵀ from the left: + // - forward permutation: P * X + // - backward permutation: Pᵀ* X + trans := blas.NoTrans + if !fwd { + trans = blas.Trans + } + want := zeros(m, n, n) + bi.Dgemm(trans, blas.NoTrans, m, n, m, 1, p.Data, p.Stride, x.Data, x.Stride, 0, want.Data, want.Stride) + + // Apply the permutation in k to X. + impl.Dlapmr(fwd, m, n, x.Data, x.Stride, k) + got := x + + // Check that k hasn't been modified in Dlapmr. + var kmod bool + for i, ki := range k { + if ki != kCopy[i] { + kmod = true + break + } + } + if kmod { + t.Errorf("%s: unexpected modification of k", name) + } + + // Check that Dlapmr yields the same result as multiplication with P. + if !equalGeneral(got, want) { + t.Errorf("%s: unexpected result", name) + } +}