From e93997dfac76281ca5f52a27067a567e66c4956f Mon Sep 17 00:00:00 2001 From: nomissbowling Date: Fri, 21 Oct 2022 16:40:56 +0900 Subject: [PATCH] new branch ComplexLA_develop to solve complex eigenvalues eigenvectors and inv etc for general (symmetry and asymmetry) Matrix neo/cla.nim neo/clasolve.nim neo/private/clacommon.nim neo/private/clareim.nim tests/tcla.nim tests/cla/cbase.nim tests/cla/cpauli.nim tests/cla/chermitian.nim tests/cla/cdet.nim tests/cla/cinv.nim tests/cla/csolve.nim tests/cla/ceig.nim use [c|z]axpy [c|z]dotu [c|z]dotc [c|z]gemv [c|z]gemm [c|z]scal (nimblas) use [c|z]geev [c|z]gesv overload with macro(czfortran) (nimlapack) include nimlapack instead of import nimlapack to use private types template czelColMajor, czelRowMajor for cast[ptr Complex[T]] unary operator `-` template compareApprox[T](x, y: complex.Complex[T]): bool proc all*[T](v: Vector[Complex[T]]; pred: proc(i: int, x: complex.Complex[T]): bool {.closure.}): bool proc all*[T](a: Matrix[Complex[T]]; pred: proc(i, j: int, x: complex.Complex[T]): bool {.closure.}): bool template `!=~`*[T](x, y: complex.Complex[T]): bool template `!=~`*[T](v, w: Vector[Complex[T]]): bool template `!=~`*[T](a, b: Matrix[Complex[T]]): bool scal for operator `*='*[T](v: var Vector[Complex[T]]; c: complex.Complex[T]) scal for operator `*='*[T](a: var Matrix[Complex[T]]; c: complex.Complex[T]) dotu dotc[Vector[Complex[T]]] Hermitian Inner Product trace and tr and dotc[Matrix[Complex[T]]] Hilbert-Schmidt Inner Product TODO: support and check for sparse matrix TODO: support and check for cuda cudadense cudasparse --- neo/cla.nim | 393 ++++++++++++++++++++++ neo/clasolve.nim | 79 +++++ neo/private/clacommon.nim | 52 +++ neo/private/clareim.nim | 65 ++++ tests/cla/cbase.nim | 672 ++++++++++++++++++++++++++++++++++++++ tests/cla/cdet.nim | 32 ++ tests/cla/ceig.nim | 139 ++++++++ tests/cla/chermitian.nim | 36 ++ tests/cla/cinv.nim | 76 +++++ tests/cla/cpauli.nim | 76 +++++ tests/cla/csolve.nim | 55 ++++ tests/tcla.nim | 9 + 12 files changed, 1684 insertions(+) create mode 100644 neo/cla.nim create mode 100644 neo/clasolve.nim create mode 100644 neo/private/clacommon.nim create mode 100644 neo/private/clareim.nim create mode 100644 tests/cla/cbase.nim create mode 100644 tests/cla/cdet.nim create mode 100644 tests/cla/ceig.nim create mode 100644 tests/cla/chermitian.nim create mode 100644 tests/cla/cinv.nim create mode 100644 tests/cla/cpauli.nim create mode 100644 tests/cla/csolve.nim create mode 100644 tests/tcla.nim diff --git a/neo/cla.nim b/neo/cla.nim new file mode 100644 index 0000000..f795638 --- /dev/null +++ b/neo/cla.nim @@ -0,0 +1,393 @@ +# complex linear algebra +# +# see also clasolve.nim +# + +import nimblas +import sequtils, complex +import sugar +import ./dense + +template czelColMajor[T](ap: CPointer[T]; a, i, j: untyped): untyped= + (cast[ptr Complex[T]](ap[2 * (j * a.ld + i)].addr))[] # complex size + +template czelRowMajor[T](ap: CPointer[T]; a, i, j: untyped): untyped= + (cast[ptr Complex[T]](ap[2 * (i * a.ld + j)].addr))[] # complex size + +proc all*[T](v: Vector[Complex[T]]; + pred: proc(i: int; x: complex.Complex[T]): bool {.closure.}): bool= + result = true + for i, e in v: + if not pred(i, e): + result = false + break + +proc all*[T](a: Matrix[Complex[T]]; + pred: proc(i, j: int; x: complex.Complex[T]): bool {.closure.}): bool= + result = true + for ij, e in a: + if not pred(ij[0], ij[1], e): + result = false + break + +template tr*[T](a: Matrix[T]): T= + a.trace + +template tr*[T](a: Matrix[Complex[T]]): complex.Complex[T]= + a.trace + +template trace*[T](a: Matrix[T]): T= + a.vdiag.sum + +template trace*[T](a: Matrix[Complex[T]]): complex.Complex[T]= + a.vdiag.sum + +proc vdiag*[T](a: Matrix[T]): Vector[T] {.noInit.}= + let k = min(a.M, a.N) + result = zeros(k, T) + for i in 0.. x.re), v.map(x => x.im)) + +proc realize*[T](a: Matrix[Complex[T]]): + tuple[real: Matrix[T], img: Matrix[T]]= + result = (a.map(x => x.re), a.map(x => x.im)) + +proc toComplex*[T](re: T, im: T): complex.Complex[T]= + result = complex.complex[T](re, im) + +proc toComplex*[T](re: T): complex.Complex[T]= + result = complex.complex[T](re, 0.0) + +proc toComplex*[T](re: Vector[T], im: Vector[T]): Vector[Complex[T]]= + result = makeVector(re.len, + proc(i: int): Complex[T]= complex.complex[T](re[i], im[i])) + +proc toComplex*[T](re: Vector[T]): Vector[Complex[T]]= + result = re.toComplex(zeros(re.len, T)) + +proc toComplex*[T](re: Matrix[T], im: Matrix[T]): Matrix[Complex[T]]= + result = makeMatrix(re.M, re.N, + proc(i, j: int): Complex[T]= complex.complex[T](re[i, j], im[i, j]), + order=re.order) + +proc toComplex*[T](re: Matrix[T]): Matrix[Complex[T]]= + result = re.toComplex(zeros(re.M, re.N, T, order=re.order)) + +proc to32*(c: complex.Complex[float64]): complex.Complex[float32]= + let (re, im) = c.realize + result = toComplex[float32](re, im) # not use re.toComplex[float32](im) + +proc to64*(c: complex.Complex[float32]): complex.Complex[float64]= + let (re, im) = c.realize + result = toComplex[float64](re, im) # not use re.toComplex[float64](im) + +proc to32*(v: Vector[Complex[float64]]): Vector[Complex[float32]]= + let (re, im) = v.realize + result = re.to32.toComplex(im.to32) + +proc to64*(v: Vector[Complex[float32]]): Vector[Complex[float64]]= + let (re, im) = v.realize + result = re.to64.toComplex(im.to64) + +proc to32*(a: Matrix[Complex[float64]]): Matrix[Complex[float32]]= + let (re, im) = a.realize + result = re.to32.toComplex(im.to32) + +proc to64*(a: Matrix[Complex[float32]]): Matrix[Complex[float64]]= + let (re, im) = a.realize + result = re.to64.toComplex(im.to64) + +proc conjugate*[T](v: Vector[Complex[T]]): Vector[Complex[T]]= + result = v.map(x => x.conjugate) + +proc conjugate*[T](a: Matrix[Complex[T]]): Matrix[Complex[T]]= + result = a.map(x => x.conjugate) + +proc H*[T](a: Matrix[Complex[T]]): Matrix[Complex[T]]= + result = a.T.conjugate # or a.conjugate.T + +template compareApprox[T](x, y: complex.Complex[T]): bool= + # TODO: more fast + const epsilon = 0.000001 + const czepsilonabs2 = complex.complex[T](epsilon, epsilon).abs2 + (x - y).abs2 < czepsilonabs2 + +proc `=~`*[T](x, y: complex.Complex[T]): bool= + result = compareApprox(x, y) + +template `!=~`*[T](x, y: complex.Complex[T]): bool= + not (x =~ y) + +proc `=~`*[T](v, w: Vector[Complex[T]]): bool= + # TODO: more fast + result = v.all(proc(i: int; x: complex.Complex[T]): bool= x =~ w[i]) + +template `!=~`*[T](v, w: Vector[Complex[T]]): bool= + not (v =~ w) + +proc `=~`*[T](a, b: Matrix[Complex[T]]): bool= + # TODO: more fast + result = a.all(proc(i, j: int; x: complex.Complex[T]): bool= x =~ b[i, j]) + +template `!=~`*[T](a, b: Matrix[Complex[T]]): bool= + not (a =~ b) + +proc `+=`*[T](v: var Vector[Complex[T]]; w: Vector[Complex[T]])= + assert v.len == w.len + var + k = v.len + alpha = complex.complex[T](1.0, 0.0) # v := w + v + vw = w # needless .clone + incx = 1 + incy = 1 + axpy(k, alpha.addr, vw.fp, incx, v.fp, incy) + +proc `+`*[T](v, w: Vector[Complex[T]]): Vector[Complex[T]]= + result = v.clone + result += w + +proc `+=`*[T](a: var Matrix[Complex[T]]; b: Matrix[Complex[T]])= + assert a.M == b.M and a.N == b.N # plus for same shape Matrix + if a.isFull and b.isFull and a.order == b.order: + var + k = a.M * a.N + alpha = complex.complex[T](1.0, 0.0) # a := b + a + vw = b # needless .clone + incx = 1 + incy = 1 + axpy(k, alpha.addr, vw.fp, incx, a.fp, incy) + else: + let ap = cast[CPointer[T]](a.fp) # complex size + if a.order == colMajor: + for t, x in b: + let (i, j) = t + czelColMajor(ap, a, i, j) += x + else: + for t, x in b: + let (i, j) = t + czelRowMajor(ap, a, i, j) += x + +proc `+`*[T](a, b: Matrix[Complex[T]]): Matrix[Complex[T]]= + result = a.clone + result += b + +proc `-=`*[T](v: var Vector[Complex[T]]; w: Vector[Complex[T]])= + assert v.len == w.len + var + k = v.len + alpha = complex.complex[T](-1.0, 0.0) # v := -w + v + vw = w # needless .clone + incx = 1 + incy = 1 + axpy(k, alpha.addr, vw.fp, incx, v.fp, incy) + +proc `-`*[T](v, w: Vector[Complex[T]]): Vector[Complex[T]]= + result = v.clone + result -= w + +proc `-=`*[T](a: var Matrix[Complex[T]]; b: Matrix[Complex[T]])= + assert a.M == b.M and a.N == b.N # plus for same shape Matrix + if a.isFull and b.isFull and a.order == b.order: + var + k = a.M * a.N + alpha = complex.complex[T](-1.0, 0.0) # a := -b + a + vw = b # needless .clone + incx = 1 + incy = 1 + axpy(k, alpha.addr, vw.fp, incx, a.fp, incy) + else: + let ap = cast[CPointer[T]](a.fp) # complex size + if a.order == colMajor: + for t, x in b: + let (i, j) = t + czelColMajor(ap, a, i, j) -= x + else: + for t, x in b: + let (i, j) = t + czelRowMajor(ap, a, i, j) -= x + +proc `-`*[T](a, b: Matrix[Complex[T]]): Matrix[Complex[T]]= + result = a.clone + result -= b + +proc `*`*[T](v: Vector[T]; c: complex.Complex[T]): Vector[Complex[T]]= + result = v.toComplex + result *= c + +proc `*=`*[T](v: var Vector[Complex[T]]; c: complex.Complex[T])= + var + k = v.len + alpha = c # v := cv + incx = 1 + scal(k, alpha.addr, v.fp, incx) + +proc `*`*[T](v: Vector[Complex[T]]; c: complex.Complex[T]): + Vector[Complex[T]]= + result = v.clone + result *= c + +proc `*`*[T](v, w: Vector[Complex[T]]): complex.Complex[T]= + result = v.dotu(w) + +proc dotu*[T](v, w: Vector[Complex[T]]): complex.Complex[T]= + assert v.len == w.len + var + k = v.len + vv = v # needless .clone + vw = w # needless .clone + incx = 1 + incy = 1 + result = complex.complex[T](0.0, 0.0) + dotu(k, vv.fp, incx, vw.fp, incy, result.addr) + +proc dotc*[T](v, w: Vector[Complex[T]]): complex.Complex[T]= + # v.dotc(w) == v.conjugate.dotu(w) + assert v.len == w.len + var + k = v.len + vv = v # needless .clone + vw = w # needless .clone + incx = 1 + incy = 1 + result = complex.complex[T](0.0, 0.0) + dotc(k, vv.fp, incx, vw.fp, incy, result.addr) + +proc `*`*[T](a: Matrix[T]; c: complex.Complex[T]): Matrix[Complex[T]]= + result = a.toComplex + result *= c + +proc `*=`*[T](a: var Matrix[Complex[T]]; c: complex.Complex[T])= + var + k = a.M * a.N + alpha = c # a := ca + incx = 1 + scal(k, alpha.addr, a.fp, incx) + +proc `*`*[T](a: Matrix[Complex[T]]; c: complex.Complex[T]): + Matrix[Complex[T]]= + result = a.clone + result *= c + +proc `*=`*[T](a: var Matrix[Complex[T]]; b: Matrix[Complex[T]])= + a = a * b + +proc `*`*[T](a, b: Matrix[Complex[T]]): Matrix[Complex[T]]= + assert a.N == b.M + var + m = a.M + n = b.N + k = b.M # == a.N + alpha = complex.complex[T](1.0, 0.0) + va = a # needless .clone + vb = b # needless .clone + beta = complex.complex[T](-1.0, 0.0) + result = constantMatrix(m, n, complex.complex[T](0.0, 0.0), order=a.order) + if a.order == colMajor and b.order == colMajor: + gemm(result.order, noTranspose, noTranspose, m, n, k, + alpha.addr, va.fp, va.ld, vb.fp, vb.ld, + beta.addr, result.fp, result.ld) # result colMajor + elif a.order == rowMajor and b.order == rowMajor: + gemm(result.order, noTranspose, noTranspose, m, n, k, + alpha.addr, va.fp, va.ld, vb.fp, vb.ld, + beta.addr, result.fp, result.ld) # result rowMajor + elif a.order == colMajor and b.order == rowMajor: + gemm(result.order, noTranspose, transpose, m, n, k, + alpha.addr, va.fp, va.ld, vb.fp, vb.ld, + beta.addr, result.fp, result.ld) # result colMajor + else: # a.order == rowMajor and b.order == colMajor + result.order = colMajor + result.ld = m + gemm(result.order, transpose, noTranspose, m, n, k, + alpha.addr, va.fp, va.ld, vb.fp, vb.ld, + beta.addr, result.fp, result.ld) # result colMajor != a.order + +template dotc*[T](a, b: Matrix[Complex[T]]): complex.Complex[T]= + # Hilbert-Schmidt Inner Product + result = (a.H * b).tr + +proc `*`*[T](a: Matrix[Complex[T]]; v: Vector[Complex[T]]): Vector[Complex[T]]= + assert a.N == v.len + var + m = a.M + n = v.len # == a.N + alpha = complex.complex[T](1.0, 0.0) + va = a # needless .clone + vv = v # needless .clone + beta = complex.complex[T](-1.0, 0.0) + incx = 1 + incy = 1 + result = constantVector(n, complex.complex[T](0.0, 0.0)) + gemv(a.order, noTranspose, m, n, + alpha.addr, va.fp, m, vv.fp, incx, + beta.addr, result.fp, incy) + +template `*`*[T](c: complex.Complex[T], + v: Vector[T] or Matrix[T]): auto= v * c + +template `*`*[T](c: complex.Complex[T], + v: Vector[Complex[T]] or Matrix[Complex[T]]): auto= v * c + +template `*=`*[T](v: var Vector[Complex[T]] or var Matrix[Complex[T]]; k: T)= + v *= k.toComplex + +template `*`*[T](v: Vector[Complex[T]] or Matrix[Complex[T]]; k: T): auto= + v * k.toComplex + +template `*`*[T](k: T, + v: Vector[Complex[T]] or Matrix[Complex[T]]): auto= v * k + +template `/=`*[T](v: var Vector[T] or Matrix[T], + c: complex.Complex[T])= + v *= (complex.complex[T](1.0, 0.0) / c) + +template `/`*[T](v: Vector[T] or Matrix[T], + c: complex.Complex[T]): auto= + v * (complex.complex[T](1.0, 0.0) / c) + +template `/=`*[T](v: var Vector[Complex[T]] or Matrix[Complex[T]], + c: complex.Complex[T])= + v *= (complex.complex[T](1.0, 0.0) / c) + +template `/`*[T](v: Vector[Complex[T]] or Matrix[Complex[T]], + c: complex.Complex[T]): auto= + v * (complex.complex[T](1.0, 0.0) / c) + +template `/=`*[T](v: var Vector[Complex[T]] or var Matrix[Complex[T]]; k: T)= + v /= k.toComplex + +template `/`*[T](v: Vector[Complex[T]] or Matrix[Complex[T]]; k: T): auto= + v / k.toComplex + +template sameVector*[T](a, b: Matrix[T]): bool= + a.asVector == b.asVector + +template sameVector*[T](a, b: Matrix[Complex[T]]): bool= + a.asVector == b.asVector + +template `-`*[T](v: Vector[Complex[T]] or Matrix[Complex[T]]): auto= + v * complex.complex[T](-1.0, 0.0) + +template `-`*[T](v: Vector[T] or Matrix[T]): auto= + v * -1.0 diff --git a/neo/clasolve.nim b/neo/clasolve.nim new file mode 100644 index 0000000..0a99ada --- /dev/null +++ b/neo/clasolve.nim @@ -0,0 +1,79 @@ +# complex linear algebra solvers +# +# see also cla.nim +# + +import sequtils, complex +import ./dense, ./private/neocommon +import ./cla, ./private/clacommon + +## use czfortran() for lapack_complex_* forked from fortran() in neocommon.nim +overload(geev, cgeev, zgeev) +overload(gesv, cgesv, zgesv) + +proc usvd*[T: SomeFloat](a: Matrix[Complex[T]]): + tuple[U: Matrix[Complex[T]], S: Matrix[Complex[T]], Vh: Matrix[Complex[T]]]= + var + m = a.M.int32 + n = a.N.int32 + vS = a.clone # overwritten by result (Diag or Triangular) + vW = constantVector(n, complex.complex[T](0.0, 0.0)) # W: EigenValues + jobvl = cstring"V" + ldvl = m + vVL = constantMatrix(m, m, complex.complex[T](0.0, 0.0), order=a.order) + jobvr = cstring"V" + ldvr = n + vVR = constantMatrix(n, n, complex.complex[T](0.0, 0.0), order=a.order) # VR: EigenVectors + lwork = n * 4 # or get before 2pass [c|z]geev + vwork = constantVector(lwork, complex.complex[T](0.0, 0.0)) + rwork = zeros(lwork, T) + info = 0'i32 + czfortran(geev, jobvl, jobvr, n, vS, m, vW, + vVL, ldvl, vVR, ldvr, vwork, lwork, rwork, info) + # TODO: convert vS to vS.vdiag and vVL to vVR.inv for compatible GE SVD + assert vW == vS.vdiag + result = (vVL, vS, vVR) + +proc usvd*[T: SomeFloat](a: Matrix[T]): + tuple[U: Matrix[Complex[T]], S: Matrix[Complex[T]], Vh: Matrix[Complex[T]]]= + result = a.toComplex.usvd + +proc geneig*[T](a: Matrix[Complex[T]]): (EigenValues[T], EigenVectors[T])= + let (_, S, Vh) = a.usvd # (U, S, Vh) + var (ers, eis) = (newSeq[T](a.N), newSeq[T](a.N)) + var (vrs, vis) = (newSeq[Vector[T]](a.N), newSeq[Vector[T]](a.N)) + for i in 0..21.17f}, {a[i, j].im:>21.17f})") + r.add("]\n") + r.add("]") + result = r.join + +proc fmtM*[T: SomeFloat](a: Matrix[T]): string= + var r = @["[\n"] + for i in 0..21.17f}") + r.add("]\n") + r.add("]") + result = r.join + +proc checkEvsEvecs*[T](a: Matrix[Complex[T]], + aev: seq[complex.Complex[T]], aevec: seq[Vector[Complex[T]]]): bool= + let (evs, evecs) = a.geneig + for j in 0..