mirror of
https://github.com/paboyle/Grid.git
synced 2026-08-20 09:29:35 +01:00
Compare commits
197
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b31547c903 | ||
|
|
2d56323012 | ||
|
|
63fefb8ea5 | ||
|
|
e4ec5d0009 | ||
|
|
a5e4ce3e48 | ||
|
|
2900ce33b5 | ||
|
|
ea5bf89955 | ||
|
|
109b77ea75 | ||
|
|
333315569c | ||
|
|
995e149256 | ||
|
|
971b0f3546 | ||
|
|
ab9603b28d | ||
|
|
adfd5853cc | ||
|
|
cc54c90ddc | ||
|
|
6a4071cdc2 | ||
|
|
e92be14df8 | ||
|
|
e9be1a91b3 | ||
|
|
7db3b96d2d | ||
|
|
429ad2a71a | ||
|
|
0b1598e9ef | ||
|
|
8d04b91932 | ||
|
|
665094d94b | ||
|
|
919d126af1 | ||
|
|
abd5df1729 | ||
|
|
3243ad91e8 | ||
|
|
c10d451f4d | ||
|
|
9c4a61850e | ||
|
|
5e3fe24412 | ||
|
|
7b81ca6b52 | ||
|
|
8c9ea4cde2 | ||
|
|
0ac72cb6a3 | ||
|
|
c4b089cf15 | ||
|
|
85e1bbf4bc | ||
|
|
032f111c8b | ||
|
|
e38528888a | ||
|
|
7c9a6d354e | ||
|
|
3ec90803ac | ||
|
|
c22473f15d | ||
|
|
55c064de28 | ||
|
|
06ce057920 | ||
|
|
76b4bd6d12 | ||
|
|
b5541aab55 | ||
|
|
a7160ac513 | ||
|
|
02d0301c9f | ||
|
|
a6cdf20c18 | ||
|
|
ad9a413892 | ||
|
|
1fddd2c29b | ||
|
|
2f75067569 | ||
|
|
6e8a00f215 | ||
|
|
702773e5fb | ||
|
|
4dfbd850ff | ||
|
|
b039e659af | ||
|
|
d16d44dda0 | ||
|
|
1c19389ba6 | ||
|
|
02fdff674c | ||
|
|
fd8b6a23a6 | ||
|
|
9e3a51d078 | ||
|
|
6f7a2ad7c7 | ||
|
|
499d656949 | ||
|
|
ba68f09026 | ||
|
|
3bdeeb73ef | ||
|
|
19868a800f | ||
|
|
df908ee872 | ||
|
|
84715ff4b9 | ||
|
|
5792195073 | ||
|
|
fb5662a449 | ||
|
|
6b2ad3db80 | ||
|
|
f1a969f0c3 | ||
|
|
f18320a152 | ||
|
|
bfcde59199 | ||
|
|
f23e9e70cd | ||
|
|
06fcd31da0 | ||
|
|
7e4fe99b1e | ||
|
|
159ea3d64d | ||
|
|
1f55c13bc4 | ||
|
|
d68b111d06 | ||
|
|
41f5a02204 | ||
|
|
7647576863 | ||
|
|
f11ba18df2 | ||
|
|
cf8587e401 | ||
|
|
7dd35ef749 | ||
|
|
41e570ddce | ||
|
|
a452131b50 | ||
|
|
4e49ca55ab | ||
|
|
c3f4474401 | ||
|
|
3d3eff86f3 | ||
|
|
fc9f154ac1 | ||
|
|
4aa0bca4dc | ||
|
|
905da6f083 | ||
|
|
86c7f29183 | ||
|
|
b0c99f876e | ||
|
|
bf5fcdc860 | ||
|
|
b58a1508fa | ||
|
|
4d527e81fa | ||
|
|
7803580aa6 | ||
|
|
32654db366 | ||
|
|
cd340cfab3 | ||
|
|
f32866b2ff | ||
|
|
1cd1dc091e | ||
|
|
0493656e86 | ||
|
|
66fd504c4d | ||
|
|
be4dd2b52f | ||
|
|
707d059766 | ||
|
|
f08c755ae6 | ||
|
|
dbbfdd4e4b | ||
|
|
f967fb40bf | ||
|
|
74e0f846cb | ||
|
|
303a4d26e5 | ||
|
|
119888653c | ||
|
|
a9f42c08f9 | ||
|
|
e79adc9d31 | ||
|
|
5a9056cd93 | ||
|
|
012c36ab5a | ||
|
|
5c4574f9aa | ||
|
|
a424775884 | ||
|
|
d6b1388741 | ||
|
|
796c6cae4e | ||
|
|
1a8064d6d9 | ||
|
|
43648924c3 | ||
|
|
bf2140e74d | ||
|
|
a1119266c1 | ||
|
|
a0f00c0eca | ||
|
|
d358954a84 | ||
|
|
aee00bdfb5 | ||
|
|
cf324b0fa1 | ||
|
|
b314dc224d | ||
|
|
1bbd62498e | ||
|
|
f3c3b1c04b | ||
|
|
069f98b253 | ||
|
|
dfd0503eae | ||
|
|
c629b2e87e | ||
|
|
7c8462abd1 | ||
|
|
95a6a0bde7 | ||
|
|
bba328fac5 | ||
|
|
41362349f3 | ||
|
|
12e3499b6d | ||
|
|
9576011011 | ||
|
|
155b34c1aa | ||
|
|
982ffe9ebe | ||
|
|
0251ecaeab | ||
|
|
372a27d645 | ||
|
|
72b4a061f3 | ||
|
|
29198efabe | ||
|
|
50aa51f93a | ||
|
|
79ccc81a86 | ||
|
|
3f0fdbb597 | ||
|
|
ea57bd8f03 | ||
|
|
bdba5b8403 | ||
|
|
58cc6ca9c0 | ||
|
|
e5996b440d | ||
|
|
ad9d03fd85 | ||
|
|
4de160ce20 | ||
|
|
fc8c8ce6e7 | ||
|
|
ddbb7f07c8 | ||
|
|
a5a04929fb | ||
|
|
1e29c59bcc | ||
|
|
b6abdc3845 | ||
|
|
77b8657fcc | ||
|
|
2fadd8bb62 | ||
|
|
60df2dd5d0 | ||
|
|
66b529b345 | ||
|
|
1304172a93 | ||
|
|
1315d4604d | ||
|
|
a31af31328 | ||
|
|
26c3c7d8f9 | ||
|
|
0650d7c7eb | ||
|
|
068f95ad2d | ||
|
|
f4fbf7c9ca | ||
|
|
843d6497b2 | ||
|
|
747c167658 | ||
|
|
fca2c5dba0 | ||
|
|
e12bc7f07c | ||
|
|
dc6ae51cab | ||
|
|
baa70d8ec9 | ||
|
|
c93b338bdd | ||
|
|
c0472aa0ec | ||
|
|
09552cfd73 | ||
|
|
003fec509c | ||
|
|
773a82d87f | ||
|
|
286c29d6fb | ||
|
|
969b0a3922 | ||
|
|
f8b2eacf99 | ||
|
|
6140ac6864 | ||
|
|
c6c2834e03 | ||
|
|
856545a1db | ||
|
|
e2d607f6c7 | ||
|
|
66da4e0657 | ||
|
|
b37390bb5a | ||
|
|
829dc8cceb | ||
|
|
13cc2c39f5 | ||
|
|
66ea3b271c | ||
|
|
d293b58a20 | ||
|
|
ce093b2bf3 | ||
|
|
e4404efe5a | ||
|
|
5ce270f1de | ||
|
|
af43b067a0 | ||
|
|
34b44d1fee |
@@ -0,0 +1,183 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## What This Is
|
||||||
|
|
||||||
|
Grid is a data-parallel C++ library for lattice QCD. It provides SIMD-vectorised lattice containers, MPI-based domain decomposition, GPU acceleration (CUDA/HIP/SYCL), and a full suite of QCD algorithms including HMC.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Uses GNU Autotools. The bootstrap step only needs to run once (or after `configure.ac` changes).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./bootstrap.sh # downloads Eigen 3.4.0, generates configure
|
||||||
|
mkdir build && cd build
|
||||||
|
../configure [options]
|
||||||
|
make -j$(nproc)
|
||||||
|
make check # run root-level tests
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
Key configure options:
|
||||||
|
|
||||||
|
| Option | Common values |
|
||||||
|
|--------|---------------|
|
||||||
|
| `--enable-simd=` | `AVX2`, `AVX512`, `KNL`, `A64FX`, `NEONv8`, `GPU` |
|
||||||
|
| `--enable-comms=` | `mpi-auto`, `mpi3-auto`, `none` |
|
||||||
|
| `--enable-accelerator=` | `cuda`, `hip`, `sycl` |
|
||||||
|
| `--enable-shm=` | `shmopen`, `hugetlbfs`, `nvlink` |
|
||||||
|
| `--enable-Nc=` | `3` (default), `2`, `4`, `5` |
|
||||||
|
| `--with-gmp=`, `--with-mpfr=`, `--with-fftw=`, `--with-lime=` | paths to libs |
|
||||||
|
| `--enable-hdf5`, `--enable-mkl`, `--enable-lapack` | optional features |
|
||||||
|
|
||||||
|
GPU builds additionally need `--enable-gen-simd-width=64` (sets 512-bit SIMD width for GPU warp/wavefront sizing) and `--enable-unified=no --enable-shm=nvlink` for multi-GPU runs.
|
||||||
|
|
||||||
|
To speed up compilation, `--disable-fermion-reps --disable-gparity` skips instantiating G-parity and higher-representation fermion operators.
|
||||||
|
|
||||||
|
Platform recipes from `README.md`:
|
||||||
|
- **KNL**: `--enable-simd=KNL --enable-comms=mpi3-auto --enable-mkl`
|
||||||
|
- **Skylake/Haswell**: `--enable-simd=AVX512` or `AVX2` + `--enable-comms=mpi3-auto`
|
||||||
|
- **AMD EPYC**: `--enable-simd=AVX2 --enable-comms=mpi3`
|
||||||
|
- **A64FX (Fugaku)**: `--enable-simd=A64FX --enable-comms=mpi3 --enable-shm=shmget` (see `SVE_README.txt`)
|
||||||
|
|
||||||
|
Complete, working `configure` invocations for specific HPC systems (Frontier/ROCm, Perlmutter/CUDA, Summit, SDCC-A100, etc.) live in `systems/<platform>/config-command`. These are the canonical references for production builds.
|
||||||
|
|
||||||
|
Required external libs: GMP, MPFR, OpenSSL, zlib.
|
||||||
|
|
||||||
|
### Use `systems/` for real machines
|
||||||
|
|
||||||
|
`systems/<machine>/` holds the known-good build for each production platform (`Frontier`, `Aurora`, `Perlmutter`, `Summit`, `Tursa`, `Lumi`, `Booster`, `Crusher`, `SDCC-*`, `mac-arm`, …). Each contains a `config-command` (the exact `../../configure` invocation) and a `sourceme.sh` (module loads and env). **Prefer copying/adapting these over hand-rolling configure flags** — they encode compiler workarounds, `LDFLAGS`, and shared-memory settings that are easy to get wrong. `systems/WorkArounds.txt` records known vendor bugs.
|
||||||
|
|
||||||
|
Note the GPU builds use `--enable-simd=GPU --enable-gen-simd-width=64`, so `Nsimd` is *not* 1 on device (it is `64/sizeof(scalar)`).
|
||||||
|
|
||||||
|
### Regenerating `Make.inc` — required after adding or deleting source files
|
||||||
|
|
||||||
|
`Make.inc` files are generated, not tracked in git (`.gitignore`d). `scripts/filelist` walks `Grid/`, `tests/*`, `benchmarks/`, `examples/`, and `HMC/` and writes the file lists and per-test `bin_PROGRAMS` rules. Every new `.cc`/`.h` in `Grid/`, and every new `Test_*.cc` / `Benchmark_*.cc` / `Example_*.cc`, is invisible to the build until you run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/filelist # from the source root, then re-run configure/make
|
||||||
|
```
|
||||||
|
|
||||||
|
`bootstrap.sh` runs it for you on the first setup.
|
||||||
|
|
||||||
|
## Running Tests and Benchmarks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From build directory
|
||||||
|
make check # root-level tests (Test_simd, Test_cshift, etc.)
|
||||||
|
make -C tests/<subdir> tests # build tests in a subdirectory
|
||||||
|
make tests # build all tests across all subdirectories
|
||||||
|
./tests/core/Test_simd # run a single test binary directly
|
||||||
|
mpirun -n 4 ./tests/core/Test_cshift --grid 16.16.16.16 --mpi 1.1.1.4
|
||||||
|
```
|
||||||
|
|
||||||
|
`make check` is a thin smoke test — building a subdirectory with `make -C tests/<subdir> tests` and running the relevant binaries directly is the normal development loop. Test binaries take Grid's standard command-line arguments (`--grid`, `--mpi`, `--accelerator-threads`, `--threads`, `--debug-signals`, `--log`); see `Grid/util/Init.cc`.
|
||||||
|
|
||||||
|
Test subdirectories and their focus: `core` (SIMD, stencil, comms), `solver` (CG, GMRES, eigensolvers), `hmc` (MD integrators), `forces` (fermion forces), `lanczos`, `IO`, `smearing`, `sp2n`, `debug`.
|
||||||
|
|
||||||
|
Tests and benchmarks that need optional fermion representations are guarded by `disable_tests_without_instantiations.h` / `disable_benchmarks_without_instantiations.h`, so a `--disable-fermion-reps --disable-gparity` build silently compiles them to no-ops.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Layer stack (bottom to top)
|
||||||
|
|
||||||
|
1. **SIMD layer** (`Grid/simd/`) — platform-specific intrinsics wrapped into `vRealF`, `vComplexD`, etc. The SIMD width and layout are compile-time constants controlled by `--enable-simd`.
|
||||||
|
|
||||||
|
2. **Tensor layer** (`Grid/tensors/`) — Lorentz/colour/spin tensor algebra built on top of SIMD types. `iMatrix`, `iVector`, `iScalar` templates compose into QCD types like `ColourMatrix`, `SpinColourVector`.
|
||||||
|
|
||||||
|
3. **Lattice layer** (`Grid/lattice/`) — `Lattice<T>` container: a site-local tensor replicated across a distributed Cartesian grid. All arithmetic is site-parallel and expression-template-fused.
|
||||||
|
|
||||||
|
4. **Cartesian/comms layer** (`Grid/cartesian/`, `Grid/communicator/`) — `GridCartesian` holds the MPI topology and local/global geometry. `Grid/cshift/` implements nearest-neighbour halo exchange; `Grid/stencil/` is the optimised multi-hop stencil used by Dirac operators.
|
||||||
|
|
||||||
|
5. **Algorithm layer** (`Grid/algorithms/`) — iterative solvers (CG, GMRES, BiCGSTAB, mixed-precision), eigensolvers (Lanczos, LAPACK), FFT, smearing, and multigrid.
|
||||||
|
|
||||||
|
6. **QCD layer** (`Grid/qcd/`) — gauge and fermion actions, HMC integrators, observables.
|
||||||
|
|
||||||
|
### QCD subsystem (`Grid/qcd/`)
|
||||||
|
|
||||||
|
- `action/fermion/` — Wilson, Clover, DWF (Mobius), Staggered, twisted-mass, G-parity variants
|
||||||
|
- `action/gauge/` — Wilson gauge, Symanzik, Iwasaki, DBW2, plaquette+rect
|
||||||
|
- `representations/` — Fundamental, Adjoint, Two-index, Sp(2n)
|
||||||
|
- `hmc/` — Leapfrog, OMF2/OMF4 integrators; pseudofermion refreshment; Metropolis accept/reject
|
||||||
|
- `smearing/` — APE, Stout, HEX, gradient flow
|
||||||
|
- `observables/` — Polyakov loop, plaquette, topological charge
|
||||||
|
|
||||||
|
### GPU acceleration and the view/memory-manager discipline
|
||||||
|
### Multigrid (`Grid/algorithms/multigrid/`)
|
||||||
|
|
||||||
|
Aggregation-based algebraic multigrid for Wilson-type fermions. Key files: `CoarsenedMatrix.h` (coarse operator), `GeneralCoarsenedMatrix.h` and `GeneralCoarsenedMatrixMultiRHS.h` (general coarsening supporting multi-RHS solves), `Aggregates.h` (near-null vector construction), `Geometry.h` (coarse-grid geometry). `MultiGrid.h` is the top-level include.
|
||||||
|
|
||||||
|
### GPU acceleration
|
||||||
|
|
||||||
|
GPU support is injected via macros in `Grid/threads/Accelerator.h` — `accelerator_for(i, n, nsimd, {...})`, `accelerator_forNB` (non-blocking, must be followed by `accelerator_barrier()`), `accelerator_for2dNB`, and `accelerator_inline`. On a CPU build these degrade to `thread_for` (OpenMP). Unified virtual memory is on by default (`--enable-unified=yes`); device-aware MPI (`--enable-accelerator-aware-mpi`) avoids device→host copies on transfers.
|
||||||
|
|
||||||
|
Lattice data is **not** directly addressable inside a kernel. You must open a view with the correct access mode so `Grid/allocator/MemoryManager.h` can move/mark the data:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
autoView(out_v, out, AcceleratorWriteDiscard); // RAII; closes at end of scope
|
||||||
|
autoView(in_v, in, AcceleratorRead);
|
||||||
|
accelerator_for(ss, grid->oSites(), Nsimd, {
|
||||||
|
coalescedWrite(out_v[ss], coalescedRead(in_v[ss]));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Modes are `AcceleratorRead/Write/WriteDiscard` and `CpuRead/Write/WriteDiscard`. Getting the mode wrong (e.g. `AcceleratorRead` on a field you write) produces stale-data bugs that only appear on GPU builds. Inside kernels use `coalescedRead`/`coalescedWrite` rather than raw `operator[]` — they map the SIMD lane onto `threadIdx.x` so accesses stay coalesced.
|
||||||
|
|
||||||
|
### Repo-local debugging skills (`skills/`)
|
||||||
|
|
||||||
|
`skills/` contains hard-won, Grid-specific playbooks written as invocable skill files. Consult them before debugging in these areas rather than reasoning from first principles:
|
||||||
|
|
||||||
|
| File | Covers |
|
||||||
|
|---|---|
|
||||||
|
| `gpu-memory-performance.md` | `acceleratorThreads()`, LambdaApply thread mapping, `coalescedRead` idiom, fused vs staged HBM access |
|
||||||
|
| `gpu-runtime-correctness.md` | GPU runtime returning early from sync, silent wrong answers |
|
||||||
|
| `communication-overlap.md` | 7-phase halo pipeline, per-packet events, host-staging vs GPU-direct RDMA |
|
||||||
|
| `mpi-heterogeneous.md` | `MPI_Sendrecv` device-buffer aliasing, deterministic reductions |
|
||||||
|
| `compiler-validation.md` | Isolating GPU compiler codegen bugs, minimal reproducers |
|
||||||
|
| `correctness-verification.md` | Double-run fingerprinting, per-packet checksums, flight recorder |
|
||||||
|
| `hang-diagnosis.md` | Diagnosing MPI/accelerator hangs |
|
||||||
|
|
||||||
|
The key loop macros (defined in `Grid/threads/Accelerator.h`) are:
|
||||||
|
- `accelerator_for(iter, num, nsimd, {...})` — maps to CUDA/HIP kernel or OpenMP loop; `nsimd` is the innermost SIMD lane count
|
||||||
|
- `accelerator_forNB(...)` — non-blocking variant (no implicit barrier)
|
||||||
|
- `accelerator_for2dNB(iter1, num1, iter2, num2, nsimd, {...})` — 2D kernel launch
|
||||||
|
- `thread_for(iter, num, {...})` — CPU OpenMP loop (never dispatches to GPU)
|
||||||
|
|
||||||
|
On CPU builds, `accelerator_for` aliases to `thread_for`.
|
||||||
|
|
||||||
|
### Solver patterns
|
||||||
|
|
||||||
|
`SchurRedBlack` (`Grid/algorithms/iterative/SchurRedBlack.h`) implements red-black (even/odd) preconditioning for fermion operators. Most production fermion solves use `SchurRedBlackDiagMooeeSolve` or similar wrappers that internally call a `ConjugateGradient` on the Schur complement.
|
||||||
|
|
||||||
|
Mixed-precision solvers (`ConjugateGradientMixedPrec`, `BiCGSTABMixedPrec`) drive a double-precision outer loop with single-precision inner solves.
|
||||||
|
|
||||||
|
### Memory and I/O
|
||||||
|
|
||||||
|
- `Grid/allocator/` — aligned/NUMA-aware allocators; caching allocator via `--enable-alloc-cache`
|
||||||
|
- `Grid/parallelIO/` — distributed parallel reader/writer for ILDG (via LIME), SciDAC, and native binary formats
|
||||||
|
- `Grid/serialisation/` — text, binary, HDF5, XML/JSON serialisation of arbitrary Grid objects
|
||||||
|
|
||||||
|
### Executables
|
||||||
|
|
||||||
|
- `HMC/` — production HMC driver programmes (e.g. `Mobius2p1f.cc`, `DWF_plus_DSDR_nf2plus1_Shamir_Gparity.cc`)
|
||||||
|
- `benchmarks/` — `Benchmark_dwf`, `Benchmark_ITT`, `Benchmark_comms`, `Benchmark_memory_bandwidth`, … used to qualify a new machine
|
||||||
|
- `examples/` — small, readable programmes (`Example_plaquette.cc`, `Example_Mobius_spectrum.cc`) that are the best starting point for learning the API
|
||||||
|
|
||||||
|
Each of these directories auto-builds every top-level `.cc` as its own binary via `scripts/filelist`.
|
||||||
|
|
||||||
|
Every programme is wrapped in `Grid_init(&argc, &argv)` / `Grid_finalize()` (`Grid/util/Init.h`).
|
||||||
|
|
||||||
|
## Key Conventions
|
||||||
|
|
||||||
|
- **C++17** is required throughout.
|
||||||
|
- Template structure: most classes are templated on `<_FImpl>` (fermion impl) or `<Gimpl>` (gauge impl), which encode the representation and precision. Instantiation is controlled by `--enable-fermion-instantiations`.
|
||||||
|
- **Tensor indices are positional, not labelled.** The `Grid/tensors/` arithmetic recurses structurally over the `iScalar`/`iVector`/`iMatrix` nest: each level defines only the {scalar,vector,matrix}² products at its own level, with element types resolved by automatic type deduction, so every colour/spin/lorentz combination composes from ~200 lines (versus the pre-C++11 QDP++/PETE approach of machine-generating every case). An index's meaning derives entirely from its nesting depth counted from the outside; `iScalar` is the identity/broadcast case at every level. Never insert or remove a nesting level casually — the multiplication tables contract by position.
|
||||||
|
- **Multigrid coarsening deepens the tensor nest by one level.** A coarse site vector is `iVector<CComplex,nbasis>`, and `innerProduct` on it returns `iScalar<CComplex>` — one level deeper than the fine block scalar. So the block-inner-product scalar type gains one `iScalar` wrapper per MG level (fine: `vTComplex`; level 2: `iScalar<vTComplex>`; see `examples/Example_pvdagm_3level.cc`). When calling `blockInnerProduct`/`blockZAXPY`/`blockOrthogonalise` on coarse fields, the coarse scalar type must match `decltype(innerProduct(siteVector(),siteVector()))` exactly; a wrong depth fails to compile (no viable `operator=` deep in the instantiation chain) rather than mis-contracting.
|
||||||
|
- The `RealD`/`RealF`/`ComplexD`/`ComplexF` typedefs are used everywhere; avoid raw `double`/`float`.
|
||||||
|
- Use `GRID_ASSERT(cond)` (defined in `Grid/GridStd.h`), not bare `assert` — it prints a Grid-formatted message and aborts cleanly under MPI.
|
||||||
|
- Logging is stream-based, not macro-based: `std::cout << GridLogMessage << ... << std::endl;`. Channels declared in `Grid/log/Log.h` include `GridLogError`, `GridLogWarning`, `GridLogDebug`, `GridLogPerformance`, `GridLogIterative`, `GridLogSolver`, `GridLogHMC`, `GridLogComms`, `GridLogMemory`, `GridLogDslash`, `GridLogIRL`, `GridLogMG`. A subset is switched on at runtime with e.g. `--log Error,Warning,Message,Performance,Iterative,Integrator,Debug,Colours` (names given without the `GridLog` prefix).
|
||||||
|
- Performance-critical paths use `GRID_TRACE(name)` from `Grid/perfmon/Tracing.h` (compiled out unless `--enable-tracing` selects a backend) and the `GridStopWatch` timers in `Grid/perfmon/Timer.h`.
|
||||||
|
- Reductions across MPI ranks go through `GridBase::GlobalSum` / `GlobalMax`; never reduce with bare MPI calls inside library code.
|
||||||
|
- Everything lives in `NAMESPACE_BEGIN(Grid)` / `NAMESPACE_END(Grid)` macros; follow the surrounding file rather than writing `namespace Grid { }`.
|
||||||
|
- British spelling is used in identifiers and comments (`colour`, `neighbour`, `serialisation`).
|
||||||
@@ -75,7 +75,6 @@ NAMESPACE_CHECK(BiCGSTAB);
|
|||||||
#include <Grid/algorithms/iterative/FlexibleCommunicationAvoidingGeneralisedMinimalResidual.h>
|
#include <Grid/algorithms/iterative/FlexibleCommunicationAvoidingGeneralisedMinimalResidual.h>
|
||||||
#include <Grid/algorithms/iterative/MixedPrecisionFlexibleGeneralisedMinimalResidual.h>
|
#include <Grid/algorithms/iterative/MixedPrecisionFlexibleGeneralisedMinimalResidual.h>
|
||||||
#include <Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h>
|
#include <Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h>
|
||||||
#include <Grid/algorithms/iterative/SimpleLanczos.h>
|
|
||||||
#include <Grid/algorithms/iterative/PowerMethod.h>
|
#include <Grid/algorithms/iterative/PowerMethod.h>
|
||||||
#include <Grid/algorithms/iterative/AdefGeneric.h>
|
#include <Grid/algorithms/iterative/AdefGeneric.h>
|
||||||
#include <Grid/algorithms/iterative/AdefMrhs.h>
|
#include <Grid/algorithms/iterative/AdefMrhs.h>
|
||||||
@@ -84,9 +83,4 @@ NAMESPACE_CHECK(PowerMethod);
|
|||||||
NAMESPACE_CHECK(multigrid);
|
NAMESPACE_CHECK(multigrid);
|
||||||
#include <Grid/algorithms/FFT.h>
|
#include <Grid/algorithms/FFT.h>
|
||||||
|
|
||||||
#include <Grid/algorithms/iterative/KrylovSchur.h>
|
|
||||||
#include <Grid/algorithms/iterative/Arnoldi.h>
|
|
||||||
#include <Grid/algorithms/iterative/LanczosBidiagonalization.h>
|
|
||||||
#include <Grid/algorithms/iterative/RestartedLanczosBidiagonalization.h>
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+169
-172
@@ -75,7 +75,6 @@ public:
|
|||||||
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
||||||
hipfftResult rv;
|
hipfftResult rv;
|
||||||
if ( sign == forward ) rv =hipfftExecZ2Z(p,in,out,HIPFFT_FORWARD);
|
if ( sign == forward ) rv =hipfftExecZ2Z(p,in,out,HIPFFT_FORWARD);
|
||||||
@@ -83,9 +82,7 @@ public:
|
|||||||
accelerator_barrier();
|
accelerator_barrier();
|
||||||
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
||||||
}
|
}
|
||||||
inline static void fftw_destroy_plan(const FFTW_plan p) {
|
inline static void fftw_destroy_plan(const FFTW_plan p) { hipfftDestroy(p); }
|
||||||
hipfftDestroy(p);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
template<> struct FFTW<ComplexF> {
|
template<> struct FFTW<ComplexF> {
|
||||||
public:
|
public:
|
||||||
@@ -93,7 +90,6 @@ public:
|
|||||||
static const int backward=FFTW_BACKWARD;
|
static const int backward=FFTW_BACKWARD;
|
||||||
typedef hipfftComplex FFTW_scalar;
|
typedef hipfftComplex FFTW_scalar;
|
||||||
typedef hipfftHandle FFTW_plan;
|
typedef hipfftHandle FFTW_plan;
|
||||||
|
|
||||||
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
||||||
FFTW_scalar *in, int *inembed,
|
FFTW_scalar *in, int *inembed,
|
||||||
int istride, int idist,
|
int istride, int idist,
|
||||||
@@ -105,7 +101,6 @@ public:
|
|||||||
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
||||||
hipfftResult rv;
|
hipfftResult rv;
|
||||||
if ( sign == forward ) rv =hipfftExecC2C(p,in,out,HIPFFT_FORWARD);
|
if ( sign == forward ) rv =hipfftExecC2C(p,in,out,HIPFFT_FORWARD);
|
||||||
@@ -113,9 +108,7 @@ public:
|
|||||||
accelerator_barrier();
|
accelerator_barrier();
|
||||||
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
||||||
}
|
}
|
||||||
inline static void fftw_destroy_plan(const FFTW_plan p) {
|
inline static void fftw_destroy_plan(const FFTW_plan p) { hipfftDestroy(p); }
|
||||||
hipfftDestroy(p);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -126,7 +119,6 @@ public:
|
|||||||
static const int backward=FFTW_BACKWARD;
|
static const int backward=FFTW_BACKWARD;
|
||||||
typedef cufftDoubleComplex FFTW_scalar;
|
typedef cufftDoubleComplex FFTW_scalar;
|
||||||
typedef cufftHandle FFTW_plan;
|
typedef cufftHandle FFTW_plan;
|
||||||
|
|
||||||
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
||||||
FFTW_scalar *in, int *inembed,
|
FFTW_scalar *in, int *inembed,
|
||||||
int istride, int idist,
|
int istride, int idist,
|
||||||
@@ -137,15 +129,12 @@ public:
|
|||||||
cufftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,CUFFT_Z2Z,howmany);
|
cufftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,CUFFT_Z2Z,howmany);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
||||||
if ( sign == forward ) cufftExecZ2Z(p,in,out,CUFFT_FORWARD);
|
if ( sign == forward ) cufftExecZ2Z(p,in,out,CUFFT_FORWARD);
|
||||||
else cufftExecZ2Z(p,in,out,CUFFT_INVERSE);
|
else cufftExecZ2Z(p,in,out,CUFFT_INVERSE);
|
||||||
accelerator_barrier();
|
accelerator_barrier();
|
||||||
}
|
}
|
||||||
inline static void fftw_destroy_plan(const FFTW_plan p) {
|
inline static void fftw_destroy_plan(const FFTW_plan p) { cufftDestroy(p); }
|
||||||
cufftDestroy(p);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
template<> struct FFTW<ComplexF> {
|
template<> struct FFTW<ComplexF> {
|
||||||
public:
|
public:
|
||||||
@@ -153,7 +142,6 @@ public:
|
|||||||
static const int backward=FFTW_BACKWARD;
|
static const int backward=FFTW_BACKWARD;
|
||||||
typedef cufftComplex FFTW_scalar;
|
typedef cufftComplex FFTW_scalar;
|
||||||
typedef cufftHandle FFTW_plan;
|
typedef cufftHandle FFTW_plan;
|
||||||
|
|
||||||
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
||||||
FFTW_scalar *in, int *inembed,
|
FFTW_scalar *in, int *inembed,
|
||||||
int istride, int idist,
|
int istride, int idist,
|
||||||
@@ -164,15 +152,12 @@ public:
|
|||||||
cufftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,CUFFT_C2C,howmany);
|
cufftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,CUFFT_C2C,howmany);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
||||||
if ( sign == forward ) cufftExecC2C(p,in,out,CUFFT_FORWARD);
|
if ( sign == forward ) cufftExecC2C(p,in,out,CUFFT_FORWARD);
|
||||||
else cufftExecC2C(p,in,out,CUFFT_INVERSE);
|
else cufftExecC2C(p,in,out,CUFFT_INVERSE);
|
||||||
accelerator_barrier();
|
accelerator_barrier();
|
||||||
}
|
}
|
||||||
inline static void fftw_destroy_plan(const FFTW_plan p) {
|
inline static void fftw_destroy_plan(const FFTW_plan p) { cufftDestroy(p); }
|
||||||
cufftDestroy(p);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -190,13 +175,10 @@ public:
|
|||||||
int sign, unsigned flags) {
|
int sign, unsigned flags) {
|
||||||
return ::fftw_plan_many_dft(rank,n,howmany,in,inembed,istride,idist,out,onembed,ostride,odist,sign,flags);
|
return ::fftw_plan_many_dft(rank,n,howmany,in,inembed,istride,idist,out,onembed,ostride,odist,sign,flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
||||||
::fftw_execute_dft(p,in,out);
|
::fftw_execute_dft(p,in,out);
|
||||||
}
|
}
|
||||||
inline static void fftw_destroy_plan(const FFTW_plan p) {
|
inline static void fftw_destroy_plan(const FFTW_plan p) { ::fftw_destroy_plan(p); }
|
||||||
::fftw_destroy_plan(p);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
template<> struct FFTW<ComplexF> {
|
template<> struct FFTW<ComplexF> {
|
||||||
public:
|
public:
|
||||||
@@ -210,25 +192,19 @@ public:
|
|||||||
int sign, unsigned flags) {
|
int sign, unsigned flags) {
|
||||||
return ::fftwf_plan_many_dft(rank,n,howmany,in,inembed,istride,idist,out,onembed,ostride,odist,sign,flags);
|
return ::fftwf_plan_many_dft(rank,n,howmany,in,inembed,istride,idist,out,onembed,ostride,odist,sign,flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
||||||
::fftwf_execute_dft(p,in,out);
|
::fftwf_execute_dft(p,in,out);
|
||||||
}
|
}
|
||||||
inline static void fftw_destroy_plan(const FFTW_plan p) {
|
inline static void fftw_destroy_plan(const FFTW_plan p) { ::fftwf_destroy_plan(p); }
|
||||||
::fftwf_destroy_plan(p);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class FFT {
|
struct FFTbase {
|
||||||
private:
|
|
||||||
|
|
||||||
double flops;
|
double flops;
|
||||||
double flops_call;
|
double flops_call;
|
||||||
uint64_t usec;
|
uint64_t usec;
|
||||||
|
GridCartesian *_grid;
|
||||||
public:
|
|
||||||
|
|
||||||
static const int forward = FFTW_FORWARD;
|
static const int forward = FFTW_FORWARD;
|
||||||
static const int backward = FFTW_BACKWARD;
|
static const int backward = FFTW_BACKWARD;
|
||||||
@@ -237,168 +213,87 @@ public:
|
|||||||
double MFlops(void) { return flops / usec; }
|
double MFlops(void) { return flops / usec; }
|
||||||
double USec(void) { return (double)usec; }
|
double USec(void) { return (double)usec; }
|
||||||
|
|
||||||
FFT ( GridCartesian * grid )
|
FFTbase(GridCartesian *grid) : _grid(grid), flops(0), flops_call(0), usec(0) {}
|
||||||
{
|
|
||||||
flops=0;
|
|
||||||
usec =0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
~FFT ( void) {
|
// Barrel-shift gather, FFT execute, and insert. Called by both FFT and PlannedFFT.
|
||||||
// delete sgrid;
|
// The caller is responsible for plan acquisition and destruction.
|
||||||
}
|
|
||||||
|
|
||||||
template<class vobj>
|
template<class vobj>
|
||||||
void FFT_dim_mask(Lattice<vobj> &result,const Lattice<vobj> &source,Coordinate mask,int sign){
|
static void FFT_dim_execute(
|
||||||
|
Lattice<vobj> &result,
|
||||||
// vgrid=result.Grid();
|
const Lattice<vobj> &source,
|
||||||
// conformable(result.Grid(),vgrid);
|
int dim, int sign,
|
||||||
// conformable(source.Grid(),vgrid);
|
typename FFTW<typename vobj::scalar_type>::FFTW_plan p,
|
||||||
const int Ndim = source.Grid()->Nd();
|
GridCartesian *grid,
|
||||||
Lattice<vobj> tmp = source;
|
double &flops, double &flops_call, uint64_t &usec)
|
||||||
for(int d=0;d<Ndim;d++){
|
{
|
||||||
if( mask[d] ) {
|
|
||||||
FFT_dim(result,tmp,d,sign);
|
|
||||||
tmp=result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class vobj>
|
|
||||||
void FFT_all_dim(Lattice<vobj> &result,const Lattice<vobj> &source,int sign){
|
|
||||||
const int Ndim = source.Grid()->Nd();
|
|
||||||
Coordinate mask(Ndim,1);
|
|
||||||
FFT_dim_mask(result,source,mask,sign);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<class vobj>
|
|
||||||
void FFT_dim(Lattice<vobj> &result,const Lattice<vobj> &source,int dim, int sign){
|
|
||||||
const int Ndim = source.Grid()->Nd();
|
|
||||||
GridBase *grid = source.Grid();
|
|
||||||
conformable(result.Grid(),source.Grid());
|
|
||||||
|
|
||||||
int L = grid->_ldimensions[dim];
|
|
||||||
int G = grid->_fdimensions[dim];
|
|
||||||
|
|
||||||
Coordinate layout(Ndim,1);
|
|
||||||
|
|
||||||
// Construct pencils
|
|
||||||
typedef typename vobj::scalar_object sobj;
|
|
||||||
typedef typename vobj::scalar_type scalar;
|
typedef typename vobj::scalar_type scalar;
|
||||||
|
typedef typename vobj::scalar_object sobj;
|
||||||
typedef typename vobj::scalar_type scalar_type;
|
typedef typename vobj::scalar_type scalar_type;
|
||||||
typedef typename vobj::vector_type vector_type;
|
typedef typename vobj::vector_type vector_type;
|
||||||
|
|
||||||
//std::cout << "CPU view" << std::endl;
|
|
||||||
|
|
||||||
typedef typename FFTW<scalar>::FFTW_scalar FFTW_scalar;
|
typedef typename FFTW<scalar>::FFTW_scalar FFTW_scalar;
|
||||||
typedef typename FFTW<scalar>::FFTW_plan FFTW_plan;
|
|
||||||
|
|
||||||
|
const int Ndim = grid->Nd();
|
||||||
|
int L = grid->_ldimensions[dim];
|
||||||
|
int G = grid->_fdimensions[dim];
|
||||||
int Ncomp = sizeof(sobj) / sizeof(scalar);
|
int Ncomp = sizeof(sobj) / sizeof(scalar);
|
||||||
int64_t Nlow = 1;
|
int64_t Nlow = 1, Nhigh = 1;
|
||||||
int64_t Nhigh = 1;
|
for (int d = 0; d < dim; d++) Nlow *= grid->_ldimensions[d];
|
||||||
|
for (int d = dim+1; d < Ndim; d++) Nhigh *= grid->_ldimensions[d];
|
||||||
for(int d=0;d<dim;d++){
|
|
||||||
Nlow*=grid->_ldimensions[d];
|
|
||||||
}
|
|
||||||
for(int d=dim+1;d<Ndim;d++){
|
|
||||||
Nhigh*=grid->_ldimensions[d];
|
|
||||||
}
|
|
||||||
int64_t Nperp = Nlow * Nhigh;
|
int64_t Nperp = Nlow * Nhigh;
|
||||||
|
|
||||||
deviceVector<scalar> pgbuf; // Layout is [perp][component][dim]
|
deviceVector<scalar> pgbuf(Nperp * Ncomp * G);
|
||||||
pgbuf.resize(Nperp*Ncomp*G);
|
|
||||||
scalar *pgbuf_v = &pgbuf[0];
|
scalar *pgbuf_v = &pgbuf[0];
|
||||||
|
|
||||||
int rank = 1; /* 1d transforms */
|
|
||||||
int n[] = {G}; /* 1d transforms of length G */
|
|
||||||
int howmany = Ncomp * Nperp;
|
int howmany = Ncomp * Nperp;
|
||||||
int odist,idist,istride,ostride;
|
|
||||||
idist = odist = G; /* Distance between consecutive FT's */
|
|
||||||
istride = ostride = 1; /* Distance between two elements in the same FT */
|
|
||||||
int *inembed = n, *onembed = n;
|
|
||||||
|
|
||||||
scalar div;
|
scalar div;
|
||||||
if ( sign == backward ) div = 1.0/G;
|
if (sign == FFTW_BACKWARD) div = 1.0 / G;
|
||||||
else if ( sign == forward ) div = 1.0;
|
else if (sign == FFTW_FORWARD) div = 1.0;
|
||||||
else GRID_ASSERT(0);
|
else GRID_ASSERT(0);
|
||||||
|
|
||||||
double t_pencil=0;
|
double t_pencil = 0, t_fft = 0, t_copy = 0, t_shift = 0;
|
||||||
double t_fft =0;
|
|
||||||
double t_total = -usecond();
|
double t_total = -usecond();
|
||||||
// std::cout << GridLogPerformance<<"Making FFTW plan" << std::endl;
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
FFTW_plan p;
|
|
||||||
{
|
|
||||||
FFTW_scalar *in = (FFTW_scalar *)&pgbuf_v[0];
|
|
||||||
FFTW_scalar *out= (FFTW_scalar *)&pgbuf_v[0];
|
|
||||||
p = FFTW<scalar>::fftw_plan_many_dft(rank,n,howmany,
|
|
||||||
in,inembed,
|
|
||||||
istride,idist,
|
|
||||||
out,onembed,
|
|
||||||
ostride, odist,
|
|
||||||
sign,FFTW_ESTIMATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Barrel shift and collect global pencil
|
|
||||||
// std::cout << GridLogPerformance<<"Making pencil" << std::endl;
|
|
||||||
Coordinate lcoor(Ndim), gcoor(Ndim);
|
|
||||||
double t_copy=0;
|
|
||||||
double t_shift=0;
|
|
||||||
t_pencil = -usecond();
|
|
||||||
result = source;
|
result = source;
|
||||||
int pc = grid->_processor_coor[dim];
|
int pc = grid->_processor_coor[dim];
|
||||||
|
|
||||||
const Coordinate ldims = grid->_ldimensions;
|
const Coordinate ldims = grid->_ldimensions;
|
||||||
const Coordinate rdims = grid->_rdimensions;
|
const Coordinate rdims = grid->_rdimensions;
|
||||||
const Coordinate sdims = grid->_simd_layout;
|
const Coordinate sdims = grid->_simd_layout;
|
||||||
|
const Coordinate processors = grid->_processors;
|
||||||
|
|
||||||
Coordinate processors = grid->_processors;
|
|
||||||
Coordinate pgdims(Ndim);
|
Coordinate pgdims(Ndim);
|
||||||
pgdims[0] = G;
|
pgdims[0] = G;
|
||||||
for(int d=0, dd=1;d<Ndim;d++){
|
for (int d = 0, dd = 1; d < Ndim; d++)
|
||||||
if (d != dim) pgdims[dd++] = ldims[d];
|
if (d != dim) pgdims[dd++] = ldims[d];
|
||||||
}
|
|
||||||
int64_t pgvol = 1;
|
int64_t pgvol = 1;
|
||||||
for (int d = 0; d < Ndim; d++) pgvol *= pgdims[d];
|
for (int d = 0; d < Ndim; d++) pgvol *= pgdims[d];
|
||||||
|
|
||||||
const int Nsimd = vobj::Nsimd();
|
const int Nsimd = vobj::Nsimd();
|
||||||
for(int p=0;p<processors[dim];p++) {
|
t_pencil = -usecond();
|
||||||
|
for (int p_idx = 0; p_idx < processors[dim]; p_idx++) {
|
||||||
t_copy -= usecond();
|
t_copy -= usecond();
|
||||||
autoView(r_v, result, AcceleratorRead);
|
autoView(r_v, result, AcceleratorRead);
|
||||||
accelerator_for(idx, grid->oSites(), vobj::Nsimd(), {
|
accelerator_for(idx, grid->oSites(), vobj::Nsimd(), {
|
||||||
#ifdef GRID_SIMT
|
#ifdef GRID_SIMT
|
||||||
{
|
{
|
||||||
int lane=acceleratorSIMTlane(Nsimd); // buffer lane
|
int lane = acceleratorSIMTlane(Nsimd);
|
||||||
#else
|
#else
|
||||||
for (int lane = 0; lane < Nsimd; lane++) {
|
for (int lane = 0; lane < Nsimd; lane++) {
|
||||||
#endif
|
#endif
|
||||||
Coordinate icoor;
|
Coordinate icoor, ocoor, pgcoor;
|
||||||
Coordinate ocoor;
|
|
||||||
Coordinate pgcoor;
|
|
||||||
|
|
||||||
Lexicographic::CoorFromIndex(icoor, lane, sdims);
|
Lexicographic::CoorFromIndex(icoor, lane, sdims);
|
||||||
Lexicographic::CoorFromIndex(ocoor, idx, rdims);
|
Lexicographic::CoorFromIndex(ocoor, idx, rdims);
|
||||||
|
pgcoor[0] = ocoor[dim] + icoor[dim]*rdims[dim] + ((pc+p_idx)%processors[dim])*L;
|
||||||
pgcoor[0] = ocoor[dim] + icoor[dim]*rdims[dim] + ((pc+p)%processors[dim])*L;
|
for (int d = 0, dd = 1; d < Ndim; d++)
|
||||||
for(int d=0,dd=1;d<Ndim;d++){
|
if (d != dim) { pgcoor[dd] = ocoor[d] + icoor[d]*rdims[d]; dd++; }
|
||||||
if ( d!=dim ) {
|
|
||||||
pgcoor[dd] = ocoor[d] + icoor[d]*rdims[d];
|
|
||||||
dd++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map coordinates in lattice layout to FFTW index
|
|
||||||
int64_t pgidx;
|
int64_t pgidx;
|
||||||
Lexicographic::IndexFromCoor(pgcoor, pgidx, pgdims);
|
Lexicographic::IndexFromCoor(pgcoor, pgidx, pgdims);
|
||||||
|
|
||||||
vector_type *from = (vector_type *)&r_v[idx];
|
vector_type *from = (vector_type *)&r_v[idx];
|
||||||
scalar_type stmp;
|
scalar_type stmp;
|
||||||
for (int w = 0; w < Ncomp; w++) {
|
for (int w = 0; w < Ncomp; w++) {
|
||||||
int64_t pg_idx = pgidx + w*pgvol;
|
|
||||||
stmp = getlane(from[w], lane);
|
stmp = getlane(from[w], lane);
|
||||||
pgbuf_v[pg_idx] = stmp;
|
pgbuf_v[pgidx + w*pgvol] = stmp;
|
||||||
}
|
}
|
||||||
#ifdef GRID_SIMT
|
#ifdef GRID_SIMT
|
||||||
}
|
}
|
||||||
@@ -406,9 +301,8 @@ public:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
});
|
});
|
||||||
|
|
||||||
t_copy += usecond();
|
t_copy += usecond();
|
||||||
if (p != processors[dim] - 1) {
|
if (p_idx != processors[dim] - 1) {
|
||||||
Lattice<vobj> temp(grid);
|
Lattice<vobj> temp(grid);
|
||||||
t_shift -= usecond();
|
t_shift -= usecond();
|
||||||
temp = Cshift(result, dim, L); result = temp;
|
temp = Cshift(result, dim, L); result = temp;
|
||||||
@@ -423,49 +317,35 @@ public:
|
|||||||
FFTW<scalar>::fftw_execute_dft(p, in, out, sign);
|
FFTW<scalar>::fftw_execute_dft(p, in, out, sign);
|
||||||
t_fft += usecond();
|
t_fft += usecond();
|
||||||
|
|
||||||
// performance counting
|
|
||||||
flops_call = 5.0 * howmany * G * log2(G);
|
flops_call = 5.0 * howmany * G * log2(G);
|
||||||
usec = t_fft;
|
usec = t_fft;
|
||||||
flops = flops_call;
|
flops = flops_call;
|
||||||
|
|
||||||
result = Zero();
|
result = Zero();
|
||||||
|
|
||||||
double t_insert = -usecond();
|
double t_insert = -usecond();
|
||||||
{
|
{
|
||||||
autoView(r_v, result, AcceleratorWrite);
|
autoView(r_v, result, AcceleratorWrite);
|
||||||
accelerator_for(idx, grid->oSites(), Nsimd, {
|
accelerator_for(idx, grid->oSites(), Nsimd, {
|
||||||
#ifdef GRID_SIMT
|
#ifdef GRID_SIMT
|
||||||
{
|
{
|
||||||
int lane=acceleratorSIMTlane(Nsimd); // buffer lane
|
int lane = acceleratorSIMTlane(Nsimd);
|
||||||
#else
|
#else
|
||||||
for (int lane = 0; lane < Nsimd; lane++) {
|
for (int lane = 0; lane < Nsimd; lane++) {
|
||||||
#endif
|
#endif
|
||||||
Coordinate icoor(Ndim);
|
Coordinate icoor(Ndim), ocoor(Ndim), pgcoor(Ndim);
|
||||||
Coordinate ocoor(Ndim);
|
|
||||||
Coordinate pgcoor(Ndim);
|
|
||||||
|
|
||||||
Lexicographic::CoorFromIndex(icoor, lane, sdims);
|
Lexicographic::CoorFromIndex(icoor, lane, sdims);
|
||||||
Lexicographic::CoorFromIndex(ocoor, idx, rdims);
|
Lexicographic::CoorFromIndex(ocoor, idx, rdims);
|
||||||
|
|
||||||
pgcoor[0] = ocoor[dim] + icoor[dim]*rdims[dim] + pc*L;
|
pgcoor[0] = ocoor[dim] + icoor[dim]*rdims[dim] + pc*L;
|
||||||
for(int d=0,dd=1;d<Ndim;d++){
|
for (int d = 0, dd = 1; d < Ndim; d++)
|
||||||
if ( d!=dim ) {
|
if (d != dim) { pgcoor[dd] = ocoor[d] + icoor[d]*rdims[d]; dd++; }
|
||||||
pgcoor[dd] = ocoor[d] + icoor[d]*rdims[d];
|
|
||||||
dd++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Map coordinates in lattice layout to FFTW index
|
|
||||||
int64_t pgidx;
|
int64_t pgidx;
|
||||||
Lexicographic::IndexFromCoor(pgcoor, pgidx, pgdims);
|
Lexicographic::IndexFromCoor(pgcoor, pgidx, pgdims);
|
||||||
|
|
||||||
vector_type *to = (vector_type *)&r_v[idx];
|
vector_type *to = (vector_type *)&r_v[idx];
|
||||||
scalar_type stmp;
|
scalar_type stmp;
|
||||||
for (int w = 0; w < Ncomp; w++) {
|
for (int w = 0; w < Ncomp; w++) {
|
||||||
int64_t pg_idx = pgidx + w*pgvol;
|
stmp = pgbuf_v[pgidx + w*pgvol];
|
||||||
stmp = pgbuf_v[pg_idx];
|
|
||||||
putlane(to[w], stmp, lane);
|
putlane(to[w], stmp, lane);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef GRID_SIMT
|
#ifdef GRID_SIMT
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -473,14 +353,8 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
result = result * div;
|
result = result * div;
|
||||||
|
|
||||||
t_insert += usecond();
|
t_insert += usecond();
|
||||||
|
|
||||||
// destroying plan
|
|
||||||
FFTW<scalar>::fftw_destroy_plan(p);
|
|
||||||
|
|
||||||
t_total += usecond();
|
t_total += usecond();
|
||||||
|
|
||||||
std::cout << GridLogPerformance << " FFT took " << t_total/1.0e6 << " s" << std::endl;
|
std::cout << GridLogPerformance << " FFT took " << t_total/1.0e6 << " s" << std::endl;
|
||||||
@@ -489,7 +363,130 @@ public:
|
|||||||
std::cout << GridLogPerformance << " of which shift" << t_shift/1.0e6 << " s" << std::endl;
|
std::cout << GridLogPerformance << " of which shift" << t_shift/1.0e6 << " s" << std::endl;
|
||||||
std::cout << GridLogPerformance << " FFT kernels " << t_fft/1.0e6 << " s" << std::endl;
|
std::cout << GridLogPerformance << " FFT kernels " << t_fft/1.0e6 << " s" << std::endl;
|
||||||
std::cout << GridLogPerformance << " FFT insert " << t_insert/1.0e6 << " s" << std::endl;
|
std::cout << GridLogPerformance << " FFT insert " << t_insert/1.0e6 << " s" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
class FFT : public FFTbase {
|
||||||
|
public:
|
||||||
|
FFT(GridCartesian *grid) : FFTbase(grid) {}
|
||||||
|
~FFT() {}
|
||||||
|
|
||||||
|
template<class vobj>
|
||||||
|
void FFT_dim_mask(Lattice<vobj> &result, const Lattice<vobj> &source, Coordinate mask, int sign) {
|
||||||
|
const int Ndim = _grid->Nd();
|
||||||
|
Lattice<vobj> tmp = source;
|
||||||
|
for (int d = 0; d < Ndim; d++) {
|
||||||
|
if (mask[d]) {
|
||||||
|
FFT_dim(result, tmp, d, sign);
|
||||||
|
tmp = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class vobj>
|
||||||
|
void FFT_all_dim(Lattice<vobj> &result, const Lattice<vobj> &source, int sign) {
|
||||||
|
Coordinate mask(_grid->Nd(), 1);
|
||||||
|
FFT_dim_mask(result, source, mask, sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class vobj>
|
||||||
|
void FFT_dim(Lattice<vobj> &result, const Lattice<vobj> &source, int dim, int sign) {
|
||||||
|
GRID_ASSERT(source.Grid() == _grid);
|
||||||
|
GRID_ASSERT(result.Grid() == _grid);
|
||||||
|
conformable(result.Grid(), source.Grid());
|
||||||
|
|
||||||
|
typedef typename vobj::scalar_type scalar;
|
||||||
|
typedef typename vobj::scalar_object sobj;
|
||||||
|
typedef typename FFTW<scalar>::FFTW_scalar FFTW_scalar;
|
||||||
|
typedef typename FFTW<scalar>::FFTW_plan FFTW_plan;
|
||||||
|
|
||||||
|
const int Ndim = _grid->Nd();
|
||||||
|
int G = _grid->_fdimensions[dim];
|
||||||
|
int Ncomp = sizeof(sobj) / sizeof(scalar);
|
||||||
|
int64_t Nperp = 1;
|
||||||
|
for (int d = 0; d < Ndim; d++)
|
||||||
|
if (d != dim) Nperp *= _grid->_ldimensions[d];
|
||||||
|
int n[] = {G};
|
||||||
|
int howmany = Ncomp * Nperp;
|
||||||
|
|
||||||
|
deviceVector<scalar> dummy(2);
|
||||||
|
FFTW_scalar *buf = (FFTW_scalar *)&dummy[0];
|
||||||
|
FFTW_plan p = FFTW<scalar>::fftw_plan_many_dft(1, n, howmany,
|
||||||
|
buf, n, 1, G,
|
||||||
|
buf, n, 1, G,
|
||||||
|
sign, FFTW_ESTIMATE);
|
||||||
|
FFT_dim_execute(result, source, dim, sign, p, _grid, flops, flops_call, usec);
|
||||||
|
FFTW<scalar>::fftw_destroy_plan(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class vobj>
|
||||||
|
class PlannedFFT : public FFTbase {
|
||||||
|
private:
|
||||||
|
typedef typename vobj::scalar_type scalar;
|
||||||
|
typedef typename vobj::scalar_object sobj;
|
||||||
|
typedef typename vobj::vector_type vector_type;
|
||||||
|
typedef typename FFTW<scalar>::FFTW_scalar FFTW_scalar;
|
||||||
|
typedef typename FFTW<scalar>::FFTW_plan FFTW_plan;
|
||||||
|
|
||||||
|
std::vector<FFTW_plan> forward_plans;
|
||||||
|
std::vector<FFTW_plan> backward_plans;
|
||||||
|
|
||||||
|
void PlanCreate() {
|
||||||
|
const int Ndim = _grid->Nd();
|
||||||
|
forward_plans.resize(Ndim);
|
||||||
|
backward_plans.resize(Ndim);
|
||||||
|
|
||||||
|
for (int d = 0; d < Ndim; d++) {
|
||||||
|
int G = _grid->_fdimensions[d];
|
||||||
|
int Ncomp = sizeof(sobj) / sizeof(scalar);
|
||||||
|
int64_t Nperp = 1;
|
||||||
|
for (int dd = 0; dd < Ndim; dd++)
|
||||||
|
if (dd != d) Nperp *= _grid->_ldimensions[dd];
|
||||||
|
int howmany = Ncomp * (int)Nperp;
|
||||||
|
int n[] = {G};
|
||||||
|
|
||||||
|
deviceVector<scalar> dummy(2);
|
||||||
|
FFTW_scalar *buf = (FFTW_scalar *)&dummy[0];
|
||||||
|
|
||||||
|
forward_plans[d] = FFTW<scalar>::fftw_plan_many_dft(1, n, howmany, buf, n, 1, G, buf, n, 1, G, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||||
|
backward_plans[d] = FFTW<scalar>::fftw_plan_many_dft(1, n, howmany, buf, n, 1, G, buf, n, 1, G, FFTW_BACKWARD, FFTW_ESTIMATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlanDestroy() {
|
||||||
|
for (auto p : forward_plans) FFTW<scalar>::fftw_destroy_plan(p);
|
||||||
|
for (auto p : backward_plans) FFTW<scalar>::fftw_destroy_plan(p);
|
||||||
|
forward_plans.clear();
|
||||||
|
backward_plans.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
PlannedFFT(GridCartesian *grid) : FFTbase(grid) { PlanCreate(); }
|
||||||
|
~PlannedFFT() { PlanDestroy(); }
|
||||||
|
|
||||||
|
void FFT_dim_mask(Lattice<vobj> &result, const Lattice<vobj> &source, Coordinate mask, int sign) {
|
||||||
|
const int Ndim = _grid->Nd();
|
||||||
|
Lattice<vobj> tmp = source;
|
||||||
|
for (int d = 0; d < Ndim; d++) {
|
||||||
|
if (mask[d]) {
|
||||||
|
FFT_dim(result, tmp, d, sign);
|
||||||
|
tmp = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFT_all_dim(Lattice<vobj> &result, const Lattice<vobj> &source, int sign) {
|
||||||
|
Coordinate mask(_grid->Nd(), 1);
|
||||||
|
FFT_dim_mask(result, source, mask, sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFT_dim(Lattice<vobj> &result, const Lattice<vobj> &source, int dim, int sign) {
|
||||||
|
GRID_ASSERT(source.Grid() == _grid);
|
||||||
|
GRID_ASSERT(result.Grid() == _grid);
|
||||||
|
GRID_ASSERT((int)forward_plans.size() == _grid->Nd());
|
||||||
|
conformable(result.Grid(), source.Grid());
|
||||||
|
FFTW_plan p = (sign == forward ? forward_plans : backward_plans)[dim];
|
||||||
|
FFT_dim_execute(result, source, dim, sign, p, _grid, flops, flops_call, usec);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,59 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
enum GridBLASOperation_t { GridBLAS_OP_N, GridBLAS_OP_T, GridBLAS_OP_C } ;
|
enum GridBLASOperation_t { GridBLAS_OP_N, GridBLAS_OP_T, GridBLAS_OP_C } ;
|
||||||
enum GridBLASPrecision_t { GridBLAS_PRECISION_DEFAULT, GridBLAS_PRECISION_16F, GridBLAS_PRECISION_16BF, GridBLAS_PRECISION_TF32 };
|
enum GridBLASPrecision_t { GridBLAS_PRECISION_DEFAULT, GridBLAS_PRECISION_16F, GridBLAS_PRECISION_16BF, GridBLAS_PRECISION_TF32 };
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// BLAS scalar constants: the put() wrapper OWNS the residency policy so
|
||||||
|
// call sites just pass values (scalars-live-on-the-host rule).
|
||||||
|
//
|
||||||
|
// Policy per backend:
|
||||||
|
// - CUDA / HIP : handles are put in HOST pointer mode at Init (cuBLAS docs
|
||||||
|
// 2.2.7: host mode is the documented default; 2.1.5: host-mode scalars
|
||||||
|
// are consumed AT CALL TIME, "can be freed just after the return of the
|
||||||
|
// call even though the kernel launch is asynchronous"). put() stores
|
||||||
|
// the value in persistent host memory and returns its address: ZERO
|
||||||
|
// host->device copies.
|
||||||
|
// - SYCL : the oneMKL group-API alpha/beta arrays are dereferenced
|
||||||
|
// USM-side (spec is silent for the group API; implementation observed
|
||||||
|
// to require USM-accessible storage -- host stack pointers fault).
|
||||||
|
// put() keeps a device-resident copy with VALUE CACHING: the copy is
|
||||||
|
// issued only when the value changes (accumulation pattern
|
||||||
|
// beta = (p==0 ? 0 : 1) costs two copies per Mult instead of npoint).
|
||||||
|
//
|
||||||
|
// Motivation (rocprof, Frontier, 2026-08-13): per-call alpha/beta device
|
||||||
|
// staging generated ~92k tiny staged hipMemcpys in a 12s solve window
|
||||||
|
// (~26% of host API time) at the latency-bound coarse level.
|
||||||
|
// NB not thread safe -- matches the single-threaded host BLAS call
|
||||||
|
// pattern of the per-call staging it replaces.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
template<class T>
|
||||||
|
class GridBLASDeviceConstant {
|
||||||
|
#ifdef GRID_SYCL
|
||||||
|
deviceVector<T> dev;
|
||||||
|
T host;
|
||||||
|
int valid;
|
||||||
|
public:
|
||||||
|
GridBLASDeviceConstant() : dev(1), valid(0) {};
|
||||||
|
T * put(T v) {
|
||||||
|
if ( (!valid) || (v != host) ) {
|
||||||
|
acceleratorCopyToDevice((void *)&v,(void *)&dev[0],sizeof(T));
|
||||||
|
host = v;
|
||||||
|
valid = 1;
|
||||||
|
}
|
||||||
|
return &dev[0];
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// CUDA / HIP in HOST pointer mode (and CPU/Eigen, where the pointer is
|
||||||
|
// unused): persistent host storage, no copies ever.
|
||||||
|
T host;
|
||||||
|
public:
|
||||||
|
GridBLASDeviceConstant() {};
|
||||||
|
T * put(T v) {
|
||||||
|
host = v;
|
||||||
|
return &host;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
class GridBLAS {
|
class GridBLAS {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -81,11 +134,31 @@ public:
|
|||||||
#ifdef GRID_CUDA
|
#ifdef GRID_CUDA
|
||||||
std::cout << "cublasCreate"<<std::endl;
|
std::cout << "cublasCreate"<<std::endl;
|
||||||
cublasCreate(&gridblasHandle);
|
cublasCreate(&gridblasHandle);
|
||||||
cublasSetPointerMode(gridblasHandle, CUBLAS_POINTER_MODE_DEVICE);
|
// HOST pointer mode: scalars consumed at call time from host memory
|
||||||
|
// (cuBLAS docs 2.1.5/2.2.7) -- no device staging of alpha/beta.
|
||||||
|
// DEVICE mode would be a deliberate opt-in for device-produced
|
||||||
|
// scalars (e.g. a future graph-captured solver).
|
||||||
|
cublasSetPointerMode(gridblasHandle, CUBLAS_POINTER_MODE_HOST);
|
||||||
|
{
|
||||||
|
cublasPointerMode_t pm;
|
||||||
|
cublasGetPointerMode(gridblasHandle,&pm);
|
||||||
|
std::cout << "GridBLAS: cuBLAS pointer mode "
|
||||||
|
<< ((pm==CUBLAS_POINTER_MODE_DEVICE)?"DEVICE":"HOST") <<std::endl;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef GRID_HIP
|
#ifdef GRID_HIP
|
||||||
std::cout << "hipblasCreate"<<std::endl;
|
std::cout << "hipblasCreate"<<std::endl;
|
||||||
hipblasCreate(&gridblasHandle);
|
hipblasCreate(&gridblasHandle);
|
||||||
|
// Explicit HOST mode: the hipBLAS default is UNDOCUMENTED in the
|
||||||
|
// headers (enum 0 == HOST by cuBLAS-mirroring convention only);
|
||||||
|
// set it and print it so every log carries the ground truth.
|
||||||
|
hipblasSetPointerMode(gridblasHandle, HIPBLAS_POINTER_MODE_HOST);
|
||||||
|
{
|
||||||
|
hipblasPointerMode_t pm;
|
||||||
|
hipblasGetPointerMode(gridblasHandle,&pm);
|
||||||
|
std::cout << "GridBLAS: hipBLAS pointer mode "
|
||||||
|
<< ((pm==HIPBLAS_POINTER_MODE_DEVICE)?"DEVICE":"HOST") <<std::endl;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef GRID_SYCL
|
#ifdef GRID_SYCL
|
||||||
gridblasHandle = theGridAccelerator;
|
gridblasHandle = theGridAccelerator;
|
||||||
@@ -240,11 +313,11 @@ public:
|
|||||||
if(OpB!=GridBLAS_OP_N)
|
if(OpB!=GridBLAS_OP_N)
|
||||||
ldb = n;
|
ldb = n;
|
||||||
|
|
||||||
static deviceVector<ComplexD> alpha_p(1);
|
// Cached device constants: copy only on value change (see GridBLASDeviceConstant)
|
||||||
static deviceVector<ComplexD> beta_p(1);
|
static GridBLASDeviceConstant<ComplexD> alpha_c;
|
||||||
// can prestore the 1 and the zero on device
|
static GridBLASDeviceConstant<ComplexD> beta_c;
|
||||||
acceleratorCopyToDevice((void *)&alpha,(void *)&alpha_p[0],sizeof(ComplexD));
|
ComplexD *alpha_p = alpha_c.put(alpha);
|
||||||
acceleratorCopyToDevice((void *)&beta ,(void *)&beta_p[0],sizeof(ComplexD));
|
ComplexD *beta_p = beta_c.put(beta);
|
||||||
RealD t0=usecond();
|
RealD t0=usecond();
|
||||||
// std::cout << "ZgemmBatched mnk "<<m<<","<<n<<","<<k<<" count "<<batchCount<<std::endl;
|
// std::cout << "ZgemmBatched mnk "<<m<<","<<n<<","<<k<<" count "<<batchCount<<std::endl;
|
||||||
#ifdef GRID_HIP
|
#ifdef GRID_HIP
|
||||||
@@ -279,7 +352,7 @@ public:
|
|||||||
(hipblasDoubleComplex **)&Cmn[0], ldc,
|
(hipblasDoubleComplex **)&Cmn[0], ldc,
|
||||||
batchCount);
|
batchCount);
|
||||||
#endif
|
#endif
|
||||||
// std::cout << " hipblas return code " <<(int)err<<std::endl;
|
// std::cout << " hipblas return code " <<(int)err<<" "<<__LINE__<<std::endl;
|
||||||
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
||||||
#endif
|
#endif
|
||||||
#ifdef GRID_CUDA
|
#ifdef GRID_CUDA
|
||||||
@@ -498,11 +571,11 @@ public:
|
|||||||
lda = k;
|
lda = k;
|
||||||
if(OpB!=GridBLAS_OP_N)
|
if(OpB!=GridBLAS_OP_N)
|
||||||
ldb = n;
|
ldb = n;
|
||||||
static deviceVector<ComplexF> alpha_p(1);
|
// Cached device constants: copy only on value change (see GridBLASDeviceConstant)
|
||||||
static deviceVector<ComplexF> beta_p(1);
|
static GridBLASDeviceConstant<ComplexF> alpha_c;
|
||||||
// can prestore the 1 and the zero on device
|
static GridBLASDeviceConstant<ComplexF> beta_c;
|
||||||
acceleratorCopyToDevice((void *)&alpha,(void *)&alpha_p[0],sizeof(ComplexF));
|
ComplexF *alpha_p = alpha_c.put(alpha);
|
||||||
acceleratorCopyToDevice((void *)&beta ,(void *)&beta_p[0],sizeof(ComplexF));
|
ComplexF *beta_p = beta_c.put(beta);
|
||||||
RealD t0=usecond();
|
RealD t0=usecond();
|
||||||
|
|
||||||
GRID_ASSERT(Bkn.size()==batchCount);
|
GRID_ASSERT(Bkn.size()==batchCount);
|
||||||
@@ -541,6 +614,7 @@ public:
|
|||||||
batchCount);
|
batchCount);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
// std::cout << " hipblas return code " <<(int)err<<" "<<__LINE__<<std::endl;
|
||||||
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
||||||
#endif
|
#endif
|
||||||
#ifdef GRID_CUDA
|
#ifdef GRID_CUDA
|
||||||
@@ -577,6 +651,7 @@ public:
|
|||||||
(void **)&Cmn[0], CUDA_C_32F, ldc,
|
(void **)&Cmn[0], CUDA_C_32F, ldc,
|
||||||
batchCount, compute_precision, CUBLAS_GEMM_DEFAULT);
|
batchCount, compute_precision, CUBLAS_GEMM_DEFAULT);
|
||||||
}
|
}
|
||||||
|
// std::cout << " hipblas return code " <<(int)err<<" "<<__LINE__<<std::endl;
|
||||||
GRID_ASSERT(err==CUBLAS_STATUS_SUCCESS);
|
GRID_ASSERT(err==CUBLAS_STATUS_SUCCESS);
|
||||||
#endif
|
#endif
|
||||||
#ifdef GRID_SYCL
|
#ifdef GRID_SYCL
|
||||||
@@ -693,6 +768,456 @@ public:
|
|||||||
RealD bytes = 1.0*sizeof(ComplexF)*(m*k+k*n+m*n)*batchCount;
|
RealD bytes = 1.0*sizeof(ComplexF)*(m*k+k*n+m*n)*batchCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Explicit-leading-dimension complex single GEMM.
|
||||||
|
//
|
||||||
|
// A,B,C may be SLICES of larger parent allocations: lda/ldb/ldc are the
|
||||||
|
// PARENT strides (>= the compact values the ten-argument overload derives).
|
||||||
|
// Motivating use: software split-K for tiny-output/huge-K dense multiplies
|
||||||
|
// (arXiv:2409.03904 fig 11; cf MultiRHSBlockCGLinalg) -- batch over K-chunks
|
||||||
|
// of a dense slab by pointer offset j*Kchunk with lda = the full K extent,
|
||||||
|
// then reduce the partial C's. Backends pass lda straight through; only
|
||||||
|
// the compact overload invented them.
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void gemmBatched(GridBLASOperation_t OpA,
|
||||||
|
GridBLASOperation_t OpB,
|
||||||
|
int m,int n, int k,
|
||||||
|
ComplexF alpha,
|
||||||
|
deviceVector<ComplexF*> &Amk, int lda,
|
||||||
|
deviceVector<ComplexF*> &Bkn, int ldb,
|
||||||
|
ComplexF beta,
|
||||||
|
deviceVector<ComplexF*> &Cmn, int ldc,
|
||||||
|
GridBLASPrecision_t precision = GridBLAS_PRECISION_DEFAULT)
|
||||||
|
{
|
||||||
|
RealD t2=usecond();
|
||||||
|
int32_t batchCount = Amk.size();
|
||||||
|
|
||||||
|
GRID_ASSERT( lda >= ((OpA==GridBLAS_OP_N) ? m : k) );
|
||||||
|
GRID_ASSERT( ldb >= ((OpB==GridBLAS_OP_N) ? k : n) );
|
||||||
|
GRID_ASSERT( ldc >= m );
|
||||||
|
|
||||||
|
// Cached device constants: copy only on value change (see GridBLASDeviceConstant)
|
||||||
|
static GridBLASDeviceConstant<ComplexF> alpha_c;
|
||||||
|
static GridBLASDeviceConstant<ComplexF> beta_c;
|
||||||
|
ComplexF *alpha_p = alpha_c.put(alpha);
|
||||||
|
ComplexF *beta_p = beta_c.put(beta);
|
||||||
|
RealD t0=usecond();
|
||||||
|
|
||||||
|
GRID_ASSERT(Bkn.size()==batchCount);
|
||||||
|
GRID_ASSERT(Cmn.size()==batchCount);
|
||||||
|
#ifdef GRID_HIP
|
||||||
|
GRID_ASSERT(precision == GridBLAS_PRECISION_DEFAULT);
|
||||||
|
hipblasOperation_t hOpA;
|
||||||
|
hipblasOperation_t hOpB;
|
||||||
|
if ( OpA == GridBLAS_OP_N ) hOpA = HIPBLAS_OP_N;
|
||||||
|
if ( OpA == GridBLAS_OP_T ) hOpA = HIPBLAS_OP_T;
|
||||||
|
if ( OpA == GridBLAS_OP_C ) hOpA = HIPBLAS_OP_C;
|
||||||
|
if ( OpB == GridBLAS_OP_N ) hOpB = HIPBLAS_OP_N;
|
||||||
|
if ( OpB == GridBLAS_OP_T ) hOpB = HIPBLAS_OP_T;
|
||||||
|
if ( OpB == GridBLAS_OP_C ) hOpB = HIPBLAS_OP_C;
|
||||||
|
#if defined(HIP_VERSION_MAJOR) && (HIP_VERSION_MAJOR >=7)
|
||||||
|
auto err = hipblasCgemmBatched(gridblasHandle,
|
||||||
|
hOpA,
|
||||||
|
hOpB,
|
||||||
|
m,n,k,
|
||||||
|
(hipComplex *) &alpha_p[0],
|
||||||
|
(hipComplex **)&Amk[0], lda,
|
||||||
|
(hipComplex **)&Bkn[0], ldb,
|
||||||
|
(hipComplex *) &beta_p[0],
|
||||||
|
(hipComplex **)&Cmn[0], ldc,
|
||||||
|
batchCount);
|
||||||
|
#else
|
||||||
|
auto err = hipblasCgemmBatched(gridblasHandle,
|
||||||
|
hOpA,
|
||||||
|
hOpB,
|
||||||
|
m,n,k,
|
||||||
|
(hipblasComplex *) &alpha_p[0],
|
||||||
|
(hipblasComplex **)&Amk[0], lda,
|
||||||
|
(hipblasComplex **)&Bkn[0], ldb,
|
||||||
|
(hipblasComplex *) &beta_p[0],
|
||||||
|
(hipblasComplex **)&Cmn[0], ldc,
|
||||||
|
batchCount);
|
||||||
|
#endif
|
||||||
|
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
||||||
|
#endif
|
||||||
|
#ifdef GRID_CUDA
|
||||||
|
cublasOperation_t hOpA;
|
||||||
|
cublasOperation_t hOpB;
|
||||||
|
if ( OpA == GridBLAS_OP_N ) hOpA = CUBLAS_OP_N;
|
||||||
|
if ( OpA == GridBLAS_OP_T ) hOpA = CUBLAS_OP_T;
|
||||||
|
if ( OpA == GridBLAS_OP_C ) hOpA = CUBLAS_OP_C;
|
||||||
|
if ( OpB == GridBLAS_OP_N ) hOpB = CUBLAS_OP_N;
|
||||||
|
if ( OpB == GridBLAS_OP_T ) hOpB = CUBLAS_OP_T;
|
||||||
|
if ( OpB == GridBLAS_OP_C ) hOpB = CUBLAS_OP_C;
|
||||||
|
cublasStatus_t err;
|
||||||
|
if (precision == GridBLAS_PRECISION_DEFAULT) {
|
||||||
|
err = cublasCgemmBatched(gridblasHandle,
|
||||||
|
hOpA,
|
||||||
|
hOpB,
|
||||||
|
m,n,k,
|
||||||
|
(cuComplex *) &alpha_p[0],
|
||||||
|
(cuComplex **)&Amk[0], lda,
|
||||||
|
(cuComplex **)&Bkn[0], ldb,
|
||||||
|
(cuComplex *) &beta_p[0],
|
||||||
|
(cuComplex **)&Cmn[0], ldc,
|
||||||
|
batchCount);
|
||||||
|
} else {
|
||||||
|
cublasComputeType_t compute_precision = toDataType(precision);
|
||||||
|
err = cublasGemmBatchedEx(gridblasHandle,
|
||||||
|
hOpA,
|
||||||
|
hOpB,
|
||||||
|
m,n,k,
|
||||||
|
(void *) &alpha_p[0],
|
||||||
|
(void **)&Amk[0], CUDA_C_32F, lda,
|
||||||
|
(void **)&Bkn[0], CUDA_C_32F, ldb,
|
||||||
|
(void *) &beta_p[0],
|
||||||
|
(void **)&Cmn[0], CUDA_C_32F, ldc,
|
||||||
|
batchCount, compute_precision, CUBLAS_GEMM_DEFAULT);
|
||||||
|
}
|
||||||
|
GRID_ASSERT(err==CUBLAS_STATUS_SUCCESS);
|
||||||
|
#endif
|
||||||
|
#ifdef GRID_SYCL
|
||||||
|
GRID_ASSERT(precision == GridBLAS_PRECISION_DEFAULT);
|
||||||
|
int64_t m64=m;
|
||||||
|
int64_t n64=n;
|
||||||
|
int64_t k64=k;
|
||||||
|
int64_t lda64=lda;
|
||||||
|
int64_t ldb64=ldb;
|
||||||
|
int64_t ldc64=ldc;
|
||||||
|
int64_t batchCount64=batchCount;
|
||||||
|
|
||||||
|
oneapi::mkl::transpose iOpA;
|
||||||
|
oneapi::mkl::transpose iOpB;
|
||||||
|
|
||||||
|
if ( OpA == GridBLAS_OP_N ) iOpA = oneapi::mkl::transpose::N;
|
||||||
|
if ( OpA == GridBLAS_OP_T ) iOpA = oneapi::mkl::transpose::T;
|
||||||
|
if ( OpA == GridBLAS_OP_C ) iOpA = oneapi::mkl::transpose::C;
|
||||||
|
if ( OpB == GridBLAS_OP_N ) iOpB = oneapi::mkl::transpose::N;
|
||||||
|
if ( OpB == GridBLAS_OP_T ) iOpB = oneapi::mkl::transpose::T;
|
||||||
|
if ( OpB == GridBLAS_OP_C ) iOpB = oneapi::mkl::transpose::C;
|
||||||
|
|
||||||
|
oneapi::mkl::blas::column_major::gemm_batch(*gridblasHandle,
|
||||||
|
&iOpA,
|
||||||
|
&iOpB,
|
||||||
|
&m64,&n64,&k64,
|
||||||
|
(ComplexF *) &alpha_p[0],
|
||||||
|
(const ComplexF **)&Amk[0], (const int64_t *)&lda64,
|
||||||
|
(const ComplexF **)&Bkn[0], (const int64_t *)&ldb64,
|
||||||
|
(ComplexF *) &beta_p[0],
|
||||||
|
(ComplexF **)&Cmn[0], (const int64_t *)&ldc64,
|
||||||
|
(int64_t)1,&batchCount64,std::vector<sycl::event>());
|
||||||
|
synchronise();
|
||||||
|
#endif
|
||||||
|
#if !defined(GRID_SYCL) && !defined(GRID_CUDA) && !defined(GRID_HIP)
|
||||||
|
GRID_ASSERT(precision == GridBLAS_PRECISION_DEFAULT);
|
||||||
|
// Reference implementation: Eigen with explicit outer stride
|
||||||
|
typedef Eigen::Map<Eigen::MatrixXcf,0,Eigen::OuterStride<> > eMat;
|
||||||
|
if ( (OpA == GridBLAS_OP_N ) && (OpB == GridBLAS_OP_N) ) {
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
eMat eAmk(Amk[p],m,k,Eigen::OuterStride<>(lda));
|
||||||
|
eMat eBkn(Bkn[p],k,n,Eigen::OuterStride<>(ldb));
|
||||||
|
eMat eCmn(Cmn[p],m,n,Eigen::OuterStride<>(ldc));
|
||||||
|
if (std::abs(beta) != 0.0)
|
||||||
|
eCmn = beta * eCmn + alpha * eAmk * eBkn ;
|
||||||
|
else
|
||||||
|
eCmn = alpha * eAmk * eBkn ;
|
||||||
|
});
|
||||||
|
} else if ( (OpA == GridBLAS_OP_C ) && (OpB == GridBLAS_OP_N) ) {
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
eMat eAmk(Amk[p],k,m,Eigen::OuterStride<>(lda));
|
||||||
|
eMat eBkn(Bkn[p],k,n,Eigen::OuterStride<>(ldb));
|
||||||
|
eMat eCmn(Cmn[p],m,n,Eigen::OuterStride<>(ldc));
|
||||||
|
if (std::abs(beta) != 0.0)
|
||||||
|
eCmn = beta * eCmn + alpha * eAmk.adjoint() * eBkn ;
|
||||||
|
else
|
||||||
|
eCmn = alpha * eAmk.adjoint() * eBkn ;
|
||||||
|
});
|
||||||
|
} else if ( (OpA == GridBLAS_OP_T ) && (OpB == GridBLAS_OP_N) ) {
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
eMat eAmk(Amk[p],k,m,Eigen::OuterStride<>(lda));
|
||||||
|
eMat eBkn(Bkn[p],k,n,Eigen::OuterStride<>(ldb));
|
||||||
|
eMat eCmn(Cmn[p],m,n,Eigen::OuterStride<>(ldc));
|
||||||
|
if (std::abs(beta) != 0.0)
|
||||||
|
eCmn = beta * eCmn + alpha * eAmk.transpose() * eBkn ;
|
||||||
|
else
|
||||||
|
eCmn = alpha * eAmk.transpose() * eBkn ;
|
||||||
|
});
|
||||||
|
} else if ( (OpA == GridBLAS_OP_N ) && (OpB == GridBLAS_OP_C) ) {
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
eMat eAmk(Amk[p],m,k,Eigen::OuterStride<>(lda));
|
||||||
|
eMat eBkn(Bkn[p],n,k,Eigen::OuterStride<>(ldb));
|
||||||
|
eMat eCmn(Cmn[p],m,n,Eigen::OuterStride<>(ldc));
|
||||||
|
if (std::abs(beta) != 0.0)
|
||||||
|
eCmn = beta * eCmn + alpha * eAmk * eBkn.adjoint() ;
|
||||||
|
else
|
||||||
|
eCmn = alpha * eAmk * eBkn.adjoint() ;
|
||||||
|
});
|
||||||
|
} else if ( (OpA == GridBLAS_OP_N ) && (OpB == GridBLAS_OP_T) ) {
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
eMat eAmk(Amk[p],m,k,Eigen::OuterStride<>(lda));
|
||||||
|
eMat eBkn(Bkn[p],n,k,Eigen::OuterStride<>(ldb));
|
||||||
|
eMat eCmn(Cmn[p],m,n,Eigen::OuterStride<>(ldc));
|
||||||
|
if (std::abs(beta) != 0.0)
|
||||||
|
eCmn = beta * eCmn + alpha * eAmk * eBkn.transpose() ;
|
||||||
|
else
|
||||||
|
eCmn = alpha * eAmk * eBkn.transpose() ;
|
||||||
|
});
|
||||||
|
} else if ( (OpA == GridBLAS_OP_C ) && (OpB == GridBLAS_OP_C) ) {
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
eMat eAmk(Amk[p],k,m,Eigen::OuterStride<>(lda));
|
||||||
|
eMat eBkn(Bkn[p],n,k,Eigen::OuterStride<>(ldb));
|
||||||
|
eMat eCmn(Cmn[p],m,n,Eigen::OuterStride<>(ldc));
|
||||||
|
if (std::abs(beta) != 0.0)
|
||||||
|
eCmn = beta * eCmn + alpha * eAmk.adjoint() * eBkn.adjoint() ;
|
||||||
|
else
|
||||||
|
eCmn = alpha * eAmk.adjoint() * eBkn.adjoint() ;
|
||||||
|
} );
|
||||||
|
} else if ( (OpA == GridBLAS_OP_T ) && (OpB == GridBLAS_OP_T) ) {
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
eMat eAmk(Amk[p],k,m,Eigen::OuterStride<>(lda));
|
||||||
|
eMat eBkn(Bkn[p],n,k,Eigen::OuterStride<>(ldb));
|
||||||
|
eMat eCmn(Cmn[p],m,n,Eigen::OuterStride<>(ldc));
|
||||||
|
if (std::abs(beta) != 0.0)
|
||||||
|
eCmn = beta * eCmn + alpha * eAmk.transpose() * eBkn.transpose() ;
|
||||||
|
else
|
||||||
|
eCmn = alpha * eAmk.transpose() * eBkn.transpose() ;
|
||||||
|
} );
|
||||||
|
} else {
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
RealD t1=usecond();
|
||||||
|
RealD flops = 8.0*m*n*k*batchCount;
|
||||||
|
RealD bytes = 1.0*sizeof(ComplexF)*(m*k+k*n+m*n)*batchCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Explicit-leading-dimension complex double GEMM. Mirror of the ComplexF
|
||||||
|
// overload above; motivating use is the fp64 distributed recursive Schur
|
||||||
|
// inversion (RecursiveSchurInverse), whose operands are column windows of
|
||||||
|
// larger row-slab allocations.
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void gemmBatched(GridBLASOperation_t OpA,
|
||||||
|
GridBLASOperation_t OpB,
|
||||||
|
int m,int n, int k,
|
||||||
|
ComplexD alpha,
|
||||||
|
deviceVector<ComplexD*> &Amk, int lda,
|
||||||
|
deviceVector<ComplexD*> &Bkn, int ldb,
|
||||||
|
ComplexD beta,
|
||||||
|
deviceVector<ComplexD*> &Cmn, int ldc)
|
||||||
|
{
|
||||||
|
RealD t2=usecond();
|
||||||
|
int32_t batchCount = Amk.size();
|
||||||
|
|
||||||
|
GRID_ASSERT( lda >= ((OpA==GridBLAS_OP_N) ? m : k) );
|
||||||
|
GRID_ASSERT( ldb >= ((OpB==GridBLAS_OP_N) ? k : n) );
|
||||||
|
GRID_ASSERT( ldc >= m );
|
||||||
|
|
||||||
|
// Cached device constants: copy only on value change (see GridBLASDeviceConstant)
|
||||||
|
static GridBLASDeviceConstant<ComplexD> alpha_c;
|
||||||
|
static GridBLASDeviceConstant<ComplexD> beta_c;
|
||||||
|
ComplexD *alpha_p = alpha_c.put(alpha);
|
||||||
|
ComplexD *beta_p = beta_c.put(beta);
|
||||||
|
RealD t0=usecond();
|
||||||
|
|
||||||
|
GRID_ASSERT(Bkn.size()==batchCount);
|
||||||
|
GRID_ASSERT(Cmn.size()==batchCount);
|
||||||
|
#ifdef GRID_HIP
|
||||||
|
hipblasOperation_t hOpA;
|
||||||
|
hipblasOperation_t hOpB;
|
||||||
|
if ( OpA == GridBLAS_OP_N ) hOpA = HIPBLAS_OP_N;
|
||||||
|
if ( OpA == GridBLAS_OP_T ) hOpA = HIPBLAS_OP_T;
|
||||||
|
if ( OpA == GridBLAS_OP_C ) hOpA = HIPBLAS_OP_C;
|
||||||
|
if ( OpB == GridBLAS_OP_N ) hOpB = HIPBLAS_OP_N;
|
||||||
|
if ( OpB == GridBLAS_OP_T ) hOpB = HIPBLAS_OP_T;
|
||||||
|
if ( OpB == GridBLAS_OP_C ) hOpB = HIPBLAS_OP_C;
|
||||||
|
#if defined(HIP_VERSION_MAJOR) && (HIP_VERSION_MAJOR >=7)
|
||||||
|
auto err = hipblasZgemmBatched(gridblasHandle,
|
||||||
|
hOpA,
|
||||||
|
hOpB,
|
||||||
|
m,n,k,
|
||||||
|
(hipDoubleComplex *) &alpha_p[0],
|
||||||
|
(hipDoubleComplex **)&Amk[0], lda,
|
||||||
|
(hipDoubleComplex **)&Bkn[0], ldb,
|
||||||
|
(hipDoubleComplex *) &beta_p[0],
|
||||||
|
(hipDoubleComplex **)&Cmn[0], ldc,
|
||||||
|
batchCount);
|
||||||
|
#else
|
||||||
|
auto err = hipblasZgemmBatched(gridblasHandle,
|
||||||
|
hOpA,
|
||||||
|
hOpB,
|
||||||
|
m,n,k,
|
||||||
|
(hipblasDoubleComplex *) &alpha_p[0],
|
||||||
|
(hipblasDoubleComplex **)&Amk[0], lda,
|
||||||
|
(hipblasDoubleComplex **)&Bkn[0], ldb,
|
||||||
|
(hipblasDoubleComplex *) &beta_p[0],
|
||||||
|
(hipblasDoubleComplex **)&Cmn[0], ldc,
|
||||||
|
batchCount);
|
||||||
|
#endif
|
||||||
|
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
||||||
|
#endif
|
||||||
|
#ifdef GRID_CUDA
|
||||||
|
cublasOperation_t hOpA;
|
||||||
|
cublasOperation_t hOpB;
|
||||||
|
if ( OpA == GridBLAS_OP_N ) hOpA = CUBLAS_OP_N;
|
||||||
|
if ( OpA == GridBLAS_OP_T ) hOpA = CUBLAS_OP_T;
|
||||||
|
if ( OpA == GridBLAS_OP_C ) hOpA = CUBLAS_OP_C;
|
||||||
|
if ( OpB == GridBLAS_OP_N ) hOpB = CUBLAS_OP_N;
|
||||||
|
if ( OpB == GridBLAS_OP_T ) hOpB = CUBLAS_OP_T;
|
||||||
|
if ( OpB == GridBLAS_OP_C ) hOpB = CUBLAS_OP_C;
|
||||||
|
auto err = cublasZgemmBatched(gridblasHandle,
|
||||||
|
hOpA,
|
||||||
|
hOpB,
|
||||||
|
m,n,k,
|
||||||
|
(cuDoubleComplex *) &alpha_p[0],
|
||||||
|
(cuDoubleComplex **)&Amk[0], lda,
|
||||||
|
(cuDoubleComplex **)&Bkn[0], ldb,
|
||||||
|
(cuDoubleComplex *) &beta_p[0],
|
||||||
|
(cuDoubleComplex **)&Cmn[0], ldc,
|
||||||
|
batchCount);
|
||||||
|
GRID_ASSERT(err==CUBLAS_STATUS_SUCCESS);
|
||||||
|
#endif
|
||||||
|
#ifdef GRID_SYCL
|
||||||
|
int64_t m64=m;
|
||||||
|
int64_t n64=n;
|
||||||
|
int64_t k64=k;
|
||||||
|
int64_t lda64=lda;
|
||||||
|
int64_t ldb64=ldb;
|
||||||
|
int64_t ldc64=ldc;
|
||||||
|
int64_t batchCount64=batchCount;
|
||||||
|
|
||||||
|
oneapi::mkl::transpose iOpA;
|
||||||
|
oneapi::mkl::transpose iOpB;
|
||||||
|
|
||||||
|
if ( OpA == GridBLAS_OP_N ) iOpA = oneapi::mkl::transpose::N;
|
||||||
|
if ( OpA == GridBLAS_OP_T ) iOpA = oneapi::mkl::transpose::T;
|
||||||
|
if ( OpA == GridBLAS_OP_C ) iOpA = oneapi::mkl::transpose::C;
|
||||||
|
if ( OpB == GridBLAS_OP_N ) iOpB = oneapi::mkl::transpose::N;
|
||||||
|
if ( OpB == GridBLAS_OP_T ) iOpB = oneapi::mkl::transpose::T;
|
||||||
|
if ( OpB == GridBLAS_OP_C ) iOpB = oneapi::mkl::transpose::C;
|
||||||
|
|
||||||
|
oneapi::mkl::blas::column_major::gemm_batch(*gridblasHandle,
|
||||||
|
&iOpA,
|
||||||
|
&iOpB,
|
||||||
|
&m64,&n64,&k64,
|
||||||
|
(ComplexD *) &alpha_p[0],
|
||||||
|
(const ComplexD **)&Amk[0], (const int64_t *)&lda64,
|
||||||
|
(const ComplexD **)&Bkn[0], (const int64_t *)&ldb64,
|
||||||
|
(ComplexD *) &beta_p[0],
|
||||||
|
(ComplexD **)&Cmn[0], (const int64_t *)&ldc64,
|
||||||
|
(int64_t)1,&batchCount64,std::vector<sycl::event>());
|
||||||
|
synchronise();
|
||||||
|
#endif
|
||||||
|
#if !defined(GRID_SYCL) && !defined(GRID_CUDA) && !defined(GRID_HIP)
|
||||||
|
// Reference implementation: Eigen with explicit outer stride
|
||||||
|
typedef Eigen::Map<Eigen::MatrixXcd,0,Eigen::OuterStride<> > eMat;
|
||||||
|
if ( (OpA == GridBLAS_OP_N ) && (OpB == GridBLAS_OP_N) ) {
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
eMat eAmk(Amk[p],m,k,Eigen::OuterStride<>(lda));
|
||||||
|
eMat eBkn(Bkn[p],k,n,Eigen::OuterStride<>(ldb));
|
||||||
|
eMat eCmn(Cmn[p],m,n,Eigen::OuterStride<>(ldc));
|
||||||
|
if (std::abs(beta) != 0.0)
|
||||||
|
{
|
||||||
|
eCmn = beta * eCmn + alpha * eAmk * eBkn;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eCmn = alpha * eAmk * eBkn;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if ( (OpA == GridBLAS_OP_C ) && (OpB == GridBLAS_OP_N) ) {
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
eMat eAmk(Amk[p],k,m,Eigen::OuterStride<>(lda));
|
||||||
|
eMat eBkn(Bkn[p],k,n,Eigen::OuterStride<>(ldb));
|
||||||
|
eMat eCmn(Cmn[p],m,n,Eigen::OuterStride<>(ldc));
|
||||||
|
if (std::abs(beta) != 0.0)
|
||||||
|
{
|
||||||
|
eCmn = beta * eCmn + alpha * eAmk.adjoint() * eBkn;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eCmn = alpha * eAmk.adjoint() * eBkn;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if ( (OpA == GridBLAS_OP_T ) && (OpB == GridBLAS_OP_N) ) {
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
eMat eAmk(Amk[p],k,m,Eigen::OuterStride<>(lda));
|
||||||
|
eMat eBkn(Bkn[p],k,n,Eigen::OuterStride<>(ldb));
|
||||||
|
eMat eCmn(Cmn[p],m,n,Eigen::OuterStride<>(ldc));
|
||||||
|
if (std::abs(beta) != 0.0)
|
||||||
|
{
|
||||||
|
eCmn = beta * eCmn + alpha * eAmk.transpose() * eBkn;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eCmn = alpha * eAmk.transpose() * eBkn;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if ( (OpA == GridBLAS_OP_N ) && (OpB == GridBLAS_OP_C) ) {
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
eMat eAmk(Amk[p],m,k,Eigen::OuterStride<>(lda));
|
||||||
|
eMat eBkn(Bkn[p],n,k,Eigen::OuterStride<>(ldb));
|
||||||
|
eMat eCmn(Cmn[p],m,n,Eigen::OuterStride<>(ldc));
|
||||||
|
if (std::abs(beta) != 0.0)
|
||||||
|
{
|
||||||
|
eCmn = beta * eCmn + alpha * eAmk * eBkn.adjoint();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eCmn = alpha * eAmk * eBkn.adjoint();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if ( (OpA == GridBLAS_OP_N ) && (OpB == GridBLAS_OP_T) ) {
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
eMat eAmk(Amk[p],m,k,Eigen::OuterStride<>(lda));
|
||||||
|
eMat eBkn(Bkn[p],n,k,Eigen::OuterStride<>(ldb));
|
||||||
|
eMat eCmn(Cmn[p],m,n,Eigen::OuterStride<>(ldc));
|
||||||
|
if (std::abs(beta) != 0.0)
|
||||||
|
{
|
||||||
|
eCmn = beta * eCmn + alpha * eAmk * eBkn.transpose();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eCmn = alpha * eAmk * eBkn.transpose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if ( (OpA == GridBLAS_OP_C ) && (OpB == GridBLAS_OP_C) ) {
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
eMat eAmk(Amk[p],k,m,Eigen::OuterStride<>(lda));
|
||||||
|
eMat eBkn(Bkn[p],n,k,Eigen::OuterStride<>(ldb));
|
||||||
|
eMat eCmn(Cmn[p],m,n,Eigen::OuterStride<>(ldc));
|
||||||
|
if (std::abs(beta) != 0.0)
|
||||||
|
{
|
||||||
|
eCmn = beta * eCmn + alpha * eAmk.adjoint() * eBkn.adjoint();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eCmn = alpha * eAmk.adjoint() * eBkn.adjoint();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if ( (OpA == GridBLAS_OP_T ) && (OpB == GridBLAS_OP_T) ) {
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
eMat eAmk(Amk[p],k,m,Eigen::OuterStride<>(lda));
|
||||||
|
eMat eBkn(Bkn[p],n,k,Eigen::OuterStride<>(ldb));
|
||||||
|
eMat eCmn(Cmn[p],m,n,Eigen::OuterStride<>(ldc));
|
||||||
|
if (std::abs(beta) != 0.0)
|
||||||
|
{
|
||||||
|
eCmn = beta * eCmn + alpha * eAmk.transpose() * eBkn.transpose();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eCmn = alpha * eAmk.transpose() * eBkn.transpose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
RealD t1=usecond();
|
||||||
|
RealD flops = 8.0*m*n*k*batchCount;
|
||||||
|
RealD bytes = 1.0*sizeof(ComplexD)*(m*k+k*n+m*n)*batchCount;
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Single precision real GEMM
|
// Single precision real GEMM
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
@@ -719,11 +1244,11 @@ public:
|
|||||||
lda = k;
|
lda = k;
|
||||||
if(OpB!=GridBLAS_OP_N)
|
if(OpB!=GridBLAS_OP_N)
|
||||||
ldb = n;
|
ldb = n;
|
||||||
static deviceVector<RealF> alpha_p(1);
|
// Cached device constants: copy only on value change (see GridBLASDeviceConstant)
|
||||||
static deviceVector<RealF> beta_p(1);
|
static GridBLASDeviceConstant<RealF> alpha_c;
|
||||||
// can prestore the 1 and the zero on device
|
static GridBLASDeviceConstant<RealF> beta_c;
|
||||||
acceleratorCopyToDevice((void *)&alpha,(void *)&alpha_p[0],sizeof(RealF));
|
RealF *alpha_p = alpha_c.put(alpha);
|
||||||
acceleratorCopyToDevice((void *)&beta ,(void *)&beta_p[0],sizeof(RealF));
|
RealF *beta_p = beta_c.put(beta);
|
||||||
RealD t0=usecond();
|
RealD t0=usecond();
|
||||||
|
|
||||||
GRID_ASSERT(Bkn.size()==batchCount);
|
GRID_ASSERT(Bkn.size()==batchCount);
|
||||||
@@ -747,6 +1272,7 @@ public:
|
|||||||
(float *) &beta_p[0],
|
(float *) &beta_p[0],
|
||||||
(float **)&Cmn[0], ldc,
|
(float **)&Cmn[0], ldc,
|
||||||
batchCount);
|
batchCount);
|
||||||
|
// std::cout << " hipblas return code " <<(int)err<<" "<<__LINE__<<std::endl;
|
||||||
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
||||||
#endif
|
#endif
|
||||||
#ifdef GRID_CUDA
|
#ifdef GRID_CUDA
|
||||||
@@ -879,11 +1405,11 @@ public:
|
|||||||
if(OpB!=GridBLAS_OP_N)
|
if(OpB!=GridBLAS_OP_N)
|
||||||
ldb = n;
|
ldb = n;
|
||||||
|
|
||||||
static deviceVector<RealD> alpha_p(1);
|
// Cached device constants: copy only on value change (see GridBLASDeviceConstant)
|
||||||
static deviceVector<RealD> beta_p(1);
|
static GridBLASDeviceConstant<RealD> alpha_c;
|
||||||
// can prestore the 1 and the zero on device
|
static GridBLASDeviceConstant<RealD> beta_c;
|
||||||
acceleratorCopyToDevice((void *)&alpha,(void *)&alpha_p[0],sizeof(RealD));
|
RealD *alpha_p = alpha_c.put(alpha);
|
||||||
acceleratorCopyToDevice((void *)&beta ,(void *)&beta_p[0],sizeof(RealD));
|
RealD *beta_p = beta_c.put(beta);
|
||||||
RealD t0=usecond();
|
RealD t0=usecond();
|
||||||
|
|
||||||
GRID_ASSERT(Bkn.size()==batchCount);
|
GRID_ASSERT(Bkn.size()==batchCount);
|
||||||
@@ -907,6 +1433,7 @@ public:
|
|||||||
(double *) &beta_p[0],
|
(double *) &beta_p[0],
|
||||||
(double **)&Cmn[0], ldc,
|
(double **)&Cmn[0], ldc,
|
||||||
batchCount);
|
batchCount);
|
||||||
|
// std::cout << " hipblas return code " <<(int)err<<" "<<__LINE__<<std::endl;
|
||||||
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
||||||
#endif
|
#endif
|
||||||
#ifdef GRID_CUDA
|
#ifdef GRID_CUDA
|
||||||
@@ -1134,6 +1661,7 @@ public:
|
|||||||
(int*) &info[0],
|
(int*) &info[0],
|
||||||
(int)batchCount);
|
(int)batchCount);
|
||||||
#endif
|
#endif
|
||||||
|
// std::cout << " hipblas return code " <<(int)err<<" "<<__LINE__<<std::endl;
|
||||||
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
||||||
#endif
|
#endif
|
||||||
#ifdef GRID_CUDA
|
#ifdef GRID_CUDA
|
||||||
@@ -1173,6 +1701,7 @@ public:
|
|||||||
(int)batchCount);
|
(int)batchCount);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// std::cout << " hipblas return code " <<(int)err<<" "<<__LINE__<<std::endl;
|
||||||
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
||||||
#endif
|
#endif
|
||||||
#ifdef GRID_CUDA
|
#ifdef GRID_CUDA
|
||||||
@@ -1261,6 +1790,7 @@ public:
|
|||||||
(int)batchCount);
|
(int)batchCount);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
// std::cout << " hipblas return code " <<(int)err<<" "<<__LINE__<<std::endl;
|
||||||
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
||||||
#endif
|
#endif
|
||||||
#ifdef GRID_CUDA
|
#ifdef GRID_CUDA
|
||||||
@@ -1304,6 +1834,7 @@ public:
|
|||||||
(int*) &info[0],
|
(int*) &info[0],
|
||||||
(int)batchCount);
|
(int)batchCount);
|
||||||
#endif
|
#endif
|
||||||
|
// std::cout << " hipblas return code " <<(int)err<<" "<<__LINE__<<std::endl;
|
||||||
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS);
|
||||||
#endif
|
#endif
|
||||||
#ifdef GRID_CUDA
|
#ifdef GRID_CUDA
|
||||||
|
|||||||
@@ -0,0 +1,282 @@
|
|||||||
|
/*************************************************************************************
|
||||||
|
|
||||||
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
|
Source file: BatchedInverse.h
|
||||||
|
|
||||||
|
Copyright (C) 2026
|
||||||
|
|
||||||
|
Author: Peter Boyle <pboyle@bnl.gov>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
|
*************************************************************************************/
|
||||||
|
/* END LEGAL */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Grid/algorithms/blas/BatchedBlas.h>
|
||||||
|
|
||||||
|
#ifdef GRID_HIP
|
||||||
|
#include <rocsolver/rocsolver.h>
|
||||||
|
#endif
|
||||||
|
// GRID_CUDA: batched LU inversion lives in cuBLAS (getrfBatched/getriBatched);
|
||||||
|
// cublas_v2.h already included via BatchedBlas.h.
|
||||||
|
// GRID_SYCL: oneapi/mkl.hpp already included via BatchedBlas.h (lapack::getrf/getri).
|
||||||
|
|
||||||
|
NAMESPACE_BEGIN(Grid);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// GridBLASInverse: cross-platform batched dense matrix inversion.
|
||||||
|
//
|
||||||
|
// HIGH LEVEL contract (deliberately NOT a getrf/getrs interface): invert a
|
||||||
|
// batch of dense N x N matrices IN PLACE,
|
||||||
|
//
|
||||||
|
// A[i] <- A[i]^{-1} i = 0 .. batchCount-1
|
||||||
|
//
|
||||||
|
// Layout: column major, lda = N, contiguous per batch element; pointer list
|
||||||
|
// exactly as GridBLAS::gemmBatched (deviceVector<T*> of device pointers).
|
||||||
|
// Each backend chooses HOW:
|
||||||
|
// HIP : rocSOLVER getrf_batched + getri_batched
|
||||||
|
// CUDA : cuBLAS getrfBatched + getriBatched (out-of-place getri; workspace
|
||||||
|
// hidden here, result copied back so the surface stays in-place)
|
||||||
|
// SYCL : oneMKL LAPACK getrf + getri per batch element (USM, in-order queue)
|
||||||
|
// CPU : Eigen PartialPivLU (the correctness oracle for all of the above)
|
||||||
|
//
|
||||||
|
// The int32 vendor-batched entry points bound N < 2^31 (asserted); the huge
|
||||||
|
// single-matrix ILP64 path (getrf_64 + blocked identity-getrs harvest, proven
|
||||||
|
// in the dense coarse-coarse setup at N=69120) migrates here as a batch==1
|
||||||
|
// large-N dispatch in a follow-up -- the recursive Schur leaves are the
|
||||||
|
// batched consumers this surface is shaped for.
|
||||||
|
//
|
||||||
|
// NB GPU-backend call signatures are written to vendor documentation but the
|
||||||
|
// air-gapped development loop compiles only the CPU/Eigen path; verify the
|
||||||
|
// rocSOLVER/cuBLAS/oneMKL calls against headers on first device compile.
|
||||||
|
// Semantics are locked by the CPU unit test (Test_batched_blas).
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
class GridBLASInverse {
|
||||||
|
public:
|
||||||
|
|
||||||
|
#ifdef GRID_HIP
|
||||||
|
// rocSOLVER runs on a rocblas_handle (distinct type from hipblasHandle_t)
|
||||||
|
static rocblas_handle & Handle(void) {
|
||||||
|
static rocblas_handle h;
|
||||||
|
static int init = 0;
|
||||||
|
if ( !init ) {
|
||||||
|
auto st = rocblas_create_handle(&h);
|
||||||
|
GRID_ASSERT(st == rocblas_status_success);
|
||||||
|
init = 1;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef GRID_CUDA
|
||||||
|
// cuBLAS batched LU shares the GridBLAS handle
|
||||||
|
static cublasHandle_t & Handle(void) {
|
||||||
|
GridBLAS::Init();
|
||||||
|
return GridBLAS::gridblasHandle;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef GRID_SYCL
|
||||||
|
static sycl::queue * & Handle(void) {
|
||||||
|
GridBLAS::Init();
|
||||||
|
return GridBLAS::gridblasHandle;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
GridBLASInverse() {};
|
||||||
|
~GridBLASInverse() {};
|
||||||
|
|
||||||
|
void inverseBatched(int64_t N, deviceVector<ComplexF*> &Amat)
|
||||||
|
{
|
||||||
|
int32_t batchCount = Amat.size();
|
||||||
|
GRID_ASSERT(batchCount > 0);
|
||||||
|
|
||||||
|
#ifdef GRID_HIP
|
||||||
|
GRID_ASSERT( N < 2147483647L );
|
||||||
|
rocblas_int n = (rocblas_int)N;
|
||||||
|
rocblas_int lda = (rocblas_int)N;
|
||||||
|
|
||||||
|
deviceVector<rocblas_int> ipiv((uint64_t)batchCount*N);
|
||||||
|
deviceVector<rocblas_int> info(batchCount);
|
||||||
|
|
||||||
|
auto st1 = rocsolver_cgetrf_batched(Handle(), n, n,
|
||||||
|
(rocblas_float_complex *const *)&Amat[0], lda,
|
||||||
|
&ipiv[0], (rocblas_stride)N,
|
||||||
|
&info[0], batchCount);
|
||||||
|
GRID_ASSERT(st1 == rocblas_status_success);
|
||||||
|
auto st2 = rocsolver_cgetri_batched(Handle(), n,
|
||||||
|
(rocblas_float_complex *const *)&Amat[0], lda,
|
||||||
|
&ipiv[0], (rocblas_stride)N,
|
||||||
|
&info[0], batchCount);
|
||||||
|
GRID_ASSERT(st2 == rocblas_status_success);
|
||||||
|
accelerator_barrier();
|
||||||
|
std::vector<rocblas_int> info_h(batchCount);
|
||||||
|
acceleratorCopyFromDevice(&info[0],&info_h[0],batchCount*sizeof(rocblas_int));
|
||||||
|
for(int i=0;i<batchCount;i++) GRID_ASSERT(info_h[i]==0); // singular pivot => abort loudly
|
||||||
|
#endif
|
||||||
|
#ifdef GRID_CUDA
|
||||||
|
GRID_ASSERT( N < 2147483647L );
|
||||||
|
int n = (int)N;
|
||||||
|
|
||||||
|
deviceVector<int> ipiv((uint64_t)batchCount*N);
|
||||||
|
deviceVector<int> info(batchCount);
|
||||||
|
|
||||||
|
auto st1 = cublasCgetrfBatched(Handle(), n,
|
||||||
|
(cuComplex **)&Amat[0], n,
|
||||||
|
&ipiv[0], &info[0], batchCount);
|
||||||
|
GRID_ASSERT(st1 == CUBLAS_STATUS_SUCCESS);
|
||||||
|
|
||||||
|
// getri is OUT of place: hidden workspace keeps the surface in-place
|
||||||
|
deviceVector<ComplexF> work((uint64_t)batchCount*N*N);
|
||||||
|
deviceVector<ComplexF*> Cptr(batchCount);
|
||||||
|
std::vector<ComplexF*> Cptr_h(batchCount);
|
||||||
|
std::vector<ComplexF*> Aptr_h(batchCount);
|
||||||
|
for(int i=0;i<batchCount;i++) Cptr_h[i] = &work[(uint64_t)i*N*N];
|
||||||
|
acceleratorCopyToDevice(&Cptr_h[0],&Cptr[0],batchCount*sizeof(ComplexF*));
|
||||||
|
acceleratorCopyFromDevice(&Amat[0],&Aptr_h[0],batchCount*sizeof(ComplexF*));
|
||||||
|
|
||||||
|
auto st2 = cublasCgetriBatched(Handle(), n,
|
||||||
|
(const cuComplex *const *)&Amat[0], n,
|
||||||
|
&ipiv[0],
|
||||||
|
(cuComplex **)&Cptr[0], n,
|
||||||
|
&info[0], batchCount);
|
||||||
|
GRID_ASSERT(st2 == CUBLAS_STATUS_SUCCESS);
|
||||||
|
accelerator_barrier();
|
||||||
|
std::vector<int> info_h(batchCount);
|
||||||
|
acceleratorCopyFromDevice(&info[0],&info_h[0],batchCount*sizeof(int));
|
||||||
|
for(int i=0;i<batchCount;i++) GRID_ASSERT(info_h[i]==0);
|
||||||
|
for(int i=0;i<batchCount;i++)
|
||||||
|
acceleratorCopyDeviceToDevice(Cptr_h[i],Aptr_h[i],(uint64_t)N*N*sizeof(ComplexF));
|
||||||
|
#endif
|
||||||
|
#ifdef GRID_SYCL
|
||||||
|
// Per-element oneMKL LAPACK on the in-order queue; group API optimisation later.
|
||||||
|
sycl::queue *q = Handle();
|
||||||
|
std::vector<ComplexF*> Aptr_h(batchCount);
|
||||||
|
acceleratorCopyFromDevice(&Amat[0],&Aptr_h[0],batchCount*sizeof(ComplexF*));
|
||||||
|
|
||||||
|
int64_t lwf = oneapi::mkl::lapack::getrf_scratchpad_size<std::complex<float> >(*q,N,N,N);
|
||||||
|
int64_t lwi = oneapi::mkl::lapack::getri_scratchpad_size<std::complex<float> >(*q,N,N);
|
||||||
|
deviceVector<ComplexF> scratchf(lwf);
|
||||||
|
deviceVector<ComplexF> scratchi(lwi);
|
||||||
|
deviceVector<int64_t> ipiv(N);
|
||||||
|
for(int i=0;i<batchCount;i++){
|
||||||
|
oneapi::mkl::lapack::getrf(*q,N,N,(std::complex<float>*)Aptr_h[i],N,&ipiv[0],
|
||||||
|
(std::complex<float>*)&scratchf[0],lwf);
|
||||||
|
oneapi::mkl::lapack::getri(*q,N, (std::complex<float>*)Aptr_h[i],N,&ipiv[0],
|
||||||
|
(std::complex<float>*)&scratchi[0],lwi);
|
||||||
|
}
|
||||||
|
q->wait();
|
||||||
|
#endif
|
||||||
|
#if !defined(GRID_SYCL) && !defined(GRID_CUDA) && !defined(GRID_HIP)
|
||||||
|
// Reference implementation; the oracle the unit test locks semantics with.
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
Eigen::Map<Eigen::MatrixXcf> eA(Amat[p],N,N);
|
||||||
|
Eigen::PartialPivLU<Eigen::MatrixXcf> lu(eA);
|
||||||
|
eA = lu.inverse();
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void inverseBatched(int64_t N, deviceVector<ComplexD*> &Amat)
|
||||||
|
{
|
||||||
|
int32_t batchCount = Amat.size();
|
||||||
|
GRID_ASSERT(batchCount > 0);
|
||||||
|
|
||||||
|
#ifdef GRID_HIP
|
||||||
|
GRID_ASSERT( N < 2147483647L );
|
||||||
|
rocblas_int n = (rocblas_int)N;
|
||||||
|
rocblas_int lda = (rocblas_int)N;
|
||||||
|
|
||||||
|
deviceVector<rocblas_int> ipiv((uint64_t)batchCount*N);
|
||||||
|
deviceVector<rocblas_int> info(batchCount);
|
||||||
|
|
||||||
|
auto st1 = rocsolver_zgetrf_batched(Handle(), n, n,
|
||||||
|
(rocblas_double_complex *const *)&Amat[0], lda,
|
||||||
|
&ipiv[0], (rocblas_stride)N,
|
||||||
|
&info[0], batchCount);
|
||||||
|
GRID_ASSERT(st1 == rocblas_status_success);
|
||||||
|
auto st2 = rocsolver_zgetri_batched(Handle(), n,
|
||||||
|
(rocblas_double_complex *const *)&Amat[0], lda,
|
||||||
|
&ipiv[0], (rocblas_stride)N,
|
||||||
|
&info[0], batchCount);
|
||||||
|
GRID_ASSERT(st2 == rocblas_status_success);
|
||||||
|
accelerator_barrier();
|
||||||
|
std::vector<rocblas_int> info_h(batchCount);
|
||||||
|
acceleratorCopyFromDevice(&info[0],&info_h[0],batchCount*sizeof(rocblas_int));
|
||||||
|
for(int i=0;i<batchCount;i++) GRID_ASSERT(info_h[i]==0);
|
||||||
|
#endif
|
||||||
|
#ifdef GRID_CUDA
|
||||||
|
GRID_ASSERT( N < 2147483647L );
|
||||||
|
int n = (int)N;
|
||||||
|
|
||||||
|
deviceVector<int> ipiv((uint64_t)batchCount*N);
|
||||||
|
deviceVector<int> info(batchCount);
|
||||||
|
|
||||||
|
auto st1 = cublasZgetrfBatched(Handle(), n,
|
||||||
|
(cuDoubleComplex **)&Amat[0], n,
|
||||||
|
&ipiv[0], &info[0], batchCount);
|
||||||
|
GRID_ASSERT(st1 == CUBLAS_STATUS_SUCCESS);
|
||||||
|
|
||||||
|
deviceVector<ComplexD> work((uint64_t)batchCount*N*N);
|
||||||
|
deviceVector<ComplexD*> Cptr(batchCount);
|
||||||
|
std::vector<ComplexD*> Cptr_h(batchCount);
|
||||||
|
std::vector<ComplexD*> Aptr_h(batchCount);
|
||||||
|
for(int i=0;i<batchCount;i++) Cptr_h[i] = &work[(uint64_t)i*N*N];
|
||||||
|
acceleratorCopyToDevice(&Cptr_h[0],&Cptr[0],batchCount*sizeof(ComplexD*));
|
||||||
|
acceleratorCopyFromDevice(&Amat[0],&Aptr_h[0],batchCount*sizeof(ComplexD*));
|
||||||
|
|
||||||
|
auto st2 = cublasZgetriBatched(Handle(), n,
|
||||||
|
(const cuDoubleComplex *const *)&Amat[0], n,
|
||||||
|
&ipiv[0],
|
||||||
|
(cuDoubleComplex **)&Cptr[0], n,
|
||||||
|
&info[0], batchCount);
|
||||||
|
GRID_ASSERT(st2 == CUBLAS_STATUS_SUCCESS);
|
||||||
|
accelerator_barrier();
|
||||||
|
std::vector<int> info_h(batchCount);
|
||||||
|
acceleratorCopyFromDevice(&info[0],&info_h[0],batchCount*sizeof(int));
|
||||||
|
for(int i=0;i<batchCount;i++) GRID_ASSERT(info_h[i]==0);
|
||||||
|
for(int i=0;i<batchCount;i++)
|
||||||
|
acceleratorCopyDeviceToDevice(Cptr_h[i],Aptr_h[i],(uint64_t)N*N*sizeof(ComplexD));
|
||||||
|
#endif
|
||||||
|
#ifdef GRID_SYCL
|
||||||
|
sycl::queue *q = Handle();
|
||||||
|
std::vector<ComplexD*> Aptr_h(batchCount);
|
||||||
|
acceleratorCopyFromDevice(&Amat[0],&Aptr_h[0],batchCount*sizeof(ComplexD*));
|
||||||
|
|
||||||
|
int64_t lwf = oneapi::mkl::lapack::getrf_scratchpad_size<std::complex<double> >(*q,N,N,N);
|
||||||
|
int64_t lwi = oneapi::mkl::lapack::getri_scratchpad_size<std::complex<double> >(*q,N,N);
|
||||||
|
deviceVector<ComplexD> scratchf(lwf);
|
||||||
|
deviceVector<ComplexD> scratchi(lwi);
|
||||||
|
deviceVector<int64_t> ipiv(N);
|
||||||
|
for(int i=0;i<batchCount;i++){
|
||||||
|
oneapi::mkl::lapack::getrf(*q,N,N,(std::complex<double>*)Aptr_h[i],N,&ipiv[0],
|
||||||
|
(std::complex<double>*)&scratchf[0],lwf);
|
||||||
|
oneapi::mkl::lapack::getri(*q,N, (std::complex<double>*)Aptr_h[i],N,&ipiv[0],
|
||||||
|
(std::complex<double>*)&scratchi[0],lwi);
|
||||||
|
}
|
||||||
|
q->wait();
|
||||||
|
#endif
|
||||||
|
#if !defined(GRID_SYCL) && !defined(GRID_CUDA) && !defined(GRID_HIP)
|
||||||
|
thread_for (p, batchCount, {
|
||||||
|
Eigen::Map<Eigen::MatrixXcd> eA(Amat[p],N,N);
|
||||||
|
Eigen::PartialPivLU<Eigen::MatrixXcd> lu(eA);
|
||||||
|
eA = lu.inverse();
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
NAMESPACE_END(Grid);
|
||||||
@@ -61,6 +61,17 @@ public:
|
|||||||
uint64_t coarse_vol;
|
uint64_t coarse_vol;
|
||||||
uint64_t words;
|
uint64_t words;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Blocking geometry in full local coordinates. Addressing in lSites rather
|
||||||
|
// than (lane,oSite) lets the fine and coarse spaces carry different SIMD
|
||||||
|
// layouts, so an unvectorised coarse space may block a vectorised fine one.
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
Coordinate fine_ldimensions;
|
||||||
|
Coordinate coarse_ldimensions;
|
||||||
|
Coordinate block_ldimensions;
|
||||||
|
Coordinate fine_simd;
|
||||||
|
Coordinate coarse_simd;
|
||||||
|
|
||||||
// Row major layout "C" order:
|
// Row major layout "C" order:
|
||||||
// BLAS_V[coarse_vol][nbasis][block_vol][words]
|
// BLAS_V[coarse_vol][nbasis][block_vol][words]
|
||||||
// BLAS_F[coarse_vol][nrhs][block_vol][words]
|
// BLAS_F[coarse_vol][nrhs][block_vol][words]
|
||||||
@@ -123,6 +134,23 @@ public:
|
|||||||
|
|
||||||
words = sizeof(scalar_object)/sizeof(scalar);
|
words = sizeof(scalar_object)/sizeof(scalar);
|
||||||
|
|
||||||
|
int nd = coarse_grid->_ndimension;
|
||||||
|
GRID_ASSERT(fine_grid->_ndimension == nd);
|
||||||
|
|
||||||
|
fine_ldimensions.resize(nd);
|
||||||
|
coarse_ldimensions.resize(nd);
|
||||||
|
block_ldimensions.resize(nd);
|
||||||
|
fine_simd = fine_grid->_simd_layout;
|
||||||
|
coarse_simd = coarse_grid->_simd_layout;
|
||||||
|
for(int d=0;d<nd;d++){
|
||||||
|
GRID_ASSERT(fine_grid->_processors[d] == coarse_grid->_processors[d]);
|
||||||
|
fine_ldimensions [d] = fine_grid->_rdimensions [d]*fine_grid->_simd_layout [d];
|
||||||
|
coarse_ldimensions[d] = coarse_grid->_rdimensions[d]*coarse_grid->_simd_layout[d];
|
||||||
|
block_ldimensions [d] = fine_ldimensions[d]/coarse_ldimensions[d];
|
||||||
|
GRID_ASSERT(block_ldimensions[d]*coarse_ldimensions[d] == fine_ldimensions[d]);
|
||||||
|
}
|
||||||
|
GRID_ASSERT(block_vol == fine_vol/coarse_vol);
|
||||||
|
|
||||||
BLAS_V.resize (fine_vol * words * nbasis );
|
BLAS_V.resize (fine_vol * words * nbasis );
|
||||||
}
|
}
|
||||||
void ImportFineGridVectors(std::vector <Field > &vecs, deviceVector<scalar> &blas)
|
void ImportFineGridVectors(std::vector <Field > &vecs, deviceVector<scalar> &blas)
|
||||||
@@ -133,22 +161,16 @@ public:
|
|||||||
|
|
||||||
GRID_ASSERT(vecs[0].Grid()==fine_grid);
|
GRID_ASSERT(vecs[0].Grid()==fine_grid);
|
||||||
|
|
||||||
subdivides(coarse_grid,fine_grid); // require they map
|
|
||||||
|
|
||||||
int _ndimension = coarse_grid->_ndimension;
|
int _ndimension = coarse_grid->_ndimension;
|
||||||
GRID_ASSERT(block_vol == fine_grid->oSites() / coarse_grid->oSites());
|
|
||||||
|
|
||||||
Coordinate block_r (_ndimension);
|
|
||||||
for(int d=0 ; d<_ndimension;d++){
|
|
||||||
block_r[d] = fine_grid->_rdimensions[d] / coarse_grid->_rdimensions[d];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t sz = blas.size();
|
uint64_t sz = blas.size();
|
||||||
|
|
||||||
acceleratorMemSet(&blas[0],0,blas.size()*sizeof(scalar));
|
acceleratorMemSet(&blas[0],0,blas.size()*sizeof(scalar));
|
||||||
|
|
||||||
Coordinate fine_rdimensions = fine_grid->_rdimensions;
|
Coordinate fine_rdimensions = fine_grid->_rdimensions;
|
||||||
Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
|
Coordinate coarse_l = coarse_ldimensions;
|
||||||
|
Coordinate block_l = block_ldimensions;
|
||||||
|
Coordinate fsimd = fine_simd;
|
||||||
int64_t bv= block_vol;
|
int64_t bv= block_vol;
|
||||||
for(int v=0;v<vecs.size();v++){
|
for(int v=0;v<vecs.size();v++){
|
||||||
|
|
||||||
@@ -162,9 +184,7 @@ public:
|
|||||||
|
|
||||||
// loop over fine sites
|
// loop over fine sites
|
||||||
const int Nsimd = vobj::Nsimd();
|
const int Nsimd = vobj::Nsimd();
|
||||||
// std::cout << "sz "<<sz<<std::endl;
|
GRID_ASSERT(sz == coarse_vol * block_vol * nvec * words);
|
||||||
// std::cout << "prod "<<Nsimd * coarse_grid->oSites() * block_vol * nvec * words<<std::endl;
|
|
||||||
GRID_ASSERT(sz == Nsimd * coarse_grid->oSites() * block_vol * nvec * words);
|
|
||||||
uint64_t lwords= words; // local variable for copy in to GPU
|
uint64_t lwords= words; // local variable for copy in to GPU
|
||||||
accelerator_for(sf,osites,Nsimd,{
|
accelerator_for(sf,osites,Nsimd,{
|
||||||
#ifdef GRID_SIMT
|
#ifdef GRID_SIMT
|
||||||
@@ -175,27 +195,27 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
// One thread per fine site
|
// One thread per fine site
|
||||||
Coordinate coor_f(_ndimension);
|
Coordinate coor_f(_ndimension);
|
||||||
|
Coordinate coor_l(_ndimension);
|
||||||
Coordinate coor_b(_ndimension);
|
Coordinate coor_b(_ndimension);
|
||||||
Coordinate coor_c(_ndimension);
|
Coordinate coor_c(_ndimension);
|
||||||
|
|
||||||
// Fine site to fine coor
|
// Fine (oSite,lane) to full local coor
|
||||||
Lexicographic::CoorFromIndex(coor_f,sf,fine_rdimensions);
|
Lexicographic::CoorFromIndex(coor_f,sf,fine_rdimensions);
|
||||||
|
Lexicographic::CoorFromIndex(coor_l,lane,fsimd);
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_f[d] += fine_rdimensions[d]*coor_l[d];
|
||||||
|
|
||||||
for(int d=0;d<_ndimension;d++) coor_b[d] = coor_f[d]%block_r[d];
|
for(int d=0;d<_ndimension;d++) coor_b[d] = coor_f[d]%block_l[d];
|
||||||
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_f[d]/block_r[d];
|
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_f[d]/block_l[d];
|
||||||
|
|
||||||
int sc;// coarse site
|
int sc;// coarse site
|
||||||
int sb;// block site
|
int sb;// block site
|
||||||
Lexicographic::IndexFromCoor(coor_c,sc,coarse_rdimensions);
|
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
|
||||||
Lexicographic::IndexFromCoor(coor_b,sb,block_r);
|
Lexicographic::IndexFromCoor(coor_b,sb,block_l);
|
||||||
|
|
||||||
scalar_object data = extractLane(lane,fineData[sf]);
|
scalar_object data = extractLane(lane,fineData[sf]);
|
||||||
|
|
||||||
// BLAS layout address calculation
|
// BLAS_F[coarse_vol][nvec][block_vol][words]
|
||||||
// words * block_vol * nbasis x coarse_vol
|
int64_t site = (sc*nvec + v)*bv
|
||||||
// coarse oSite x block vole x lanes
|
|
||||||
int64_t site = (lane*osites + sc*bv)*nvec
|
|
||||||
+ v*bv
|
|
||||||
+ sb;
|
+ sb;
|
||||||
|
|
||||||
// GRID_ASSERT(site*lwords<sz);
|
// GRID_ASSERT(site*lwords<sz);
|
||||||
@@ -213,6 +233,318 @@ public:
|
|||||||
// std::cout << " BlockProjector imported vector"<<v<<std::endl;
|
// std::cout << " BlockProjector imported vector"<<v<<std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Import direct from multiRHS fine order, avoiding the unpack to a vector
|
||||||
|
// of single RHS fields.
|
||||||
|
//
|
||||||
|
// fine mrhs grid : rhs is dimension 0, undistributed, unvectorised
|
||||||
|
// Grid order : F[fine_vol][nrhs][words]
|
||||||
|
// BLAS order : BLAS_F[lane][coarse_vol][nrhs][block_vol][words]
|
||||||
|
//
|
||||||
|
// The gather of block_vol from fine_vol, and the transpose of nrhs against
|
||||||
|
// block_vol, are the irreducible part: this is not the identity.
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
void ImportFineGridMrhsVectors(Field &vec_mrhs, deviceVector<scalar> &blas)
|
||||||
|
{
|
||||||
|
typedef typename Field::vector_object vobj;
|
||||||
|
|
||||||
|
GridBase *fine_mrhs_grid = vec_mrhs.Grid();
|
||||||
|
int _ndimension = coarse_grid->_ndimension;
|
||||||
|
|
||||||
|
GRID_ASSERT(fine_mrhs_grid->_ndimension == _ndimension+1);
|
||||||
|
GRID_ASSERT(fine_mrhs_grid->_simd_layout[0] == 1);
|
||||||
|
GRID_ASSERT(fine_mrhs_grid->_processors[0] == 1);
|
||||||
|
for(int d=0;d<_ndimension;d++){
|
||||||
|
GRID_ASSERT(fine_mrhs_grid->_rdimensions[d+1] == fine_grid->_rdimensions[d]);
|
||||||
|
GRID_ASSERT(fine_mrhs_grid->_simd_layout[d+1] == fine_grid->_simd_layout[d]);
|
||||||
|
}
|
||||||
|
int nvec = fine_mrhs_grid->_rdimensions[0]; // nrhs
|
||||||
|
|
||||||
|
uint64_t sz = blas.size();
|
||||||
|
acceleratorMemSet(&blas[0],0,blas.size()*sizeof(scalar));
|
||||||
|
|
||||||
|
Coordinate fine_mrhs_rdimensions = fine_mrhs_grid->_rdimensions;
|
||||||
|
Coordinate fine_rdimensions = fine_grid->_rdimensions;
|
||||||
|
Coordinate coarse_l = coarse_ldimensions;
|
||||||
|
Coordinate block_l = block_ldimensions;
|
||||||
|
Coordinate fsimd = fine_simd;
|
||||||
|
int64_t bv= block_vol;
|
||||||
|
|
||||||
|
autoView( fineData , vec_mrhs, AcceleratorRead);
|
||||||
|
auto blasData_p = &blas[0];
|
||||||
|
auto fineData_p = &fineData[0];
|
||||||
|
|
||||||
|
int64_t osites = fine_grid->oSites(); // D dimensional
|
||||||
|
int64_t osites_hi = fine_mrhs_grid->oSites(); // nvec * osites
|
||||||
|
|
||||||
|
const int Nsimd = vobj::Nsimd();
|
||||||
|
GRID_ASSERT(sz == coarse_vol * block_vol * nvec * words);
|
||||||
|
uint64_t lwords= words;
|
||||||
|
int64_t lnvec = nvec;
|
||||||
|
|
||||||
|
accelerator_for(sfr,osites_hi,Nsimd,{
|
||||||
|
#ifdef GRID_SIMT
|
||||||
|
{
|
||||||
|
int lane=acceleratorSIMTlane(Nsimd); // buffer lane
|
||||||
|
#else
|
||||||
|
for(int lane=0;lane<Nsimd;lane++) {
|
||||||
|
#endif
|
||||||
|
Coordinate coor_hi(_ndimension+1);
|
||||||
|
Coordinate coor_f(_ndimension);
|
||||||
|
Coordinate coor_l(_ndimension);
|
||||||
|
Coordinate coor_b(_ndimension);
|
||||||
|
Coordinate coor_c(_ndimension);
|
||||||
|
|
||||||
|
// rhs is dimension 0 of the D+1 grid
|
||||||
|
Lexicographic::CoorFromIndex(coor_hi,sfr,fine_mrhs_rdimensions);
|
||||||
|
int v = coor_hi[0];
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_f[d] = coor_hi[d+1];
|
||||||
|
|
||||||
|
Lexicographic::CoorFromIndex(coor_l,lane,fsimd);
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_f[d] += fine_rdimensions[d]*coor_l[d];
|
||||||
|
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_b[d] = coor_f[d]%block_l[d];
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_f[d]/block_l[d];
|
||||||
|
|
||||||
|
int sc;// coarse site
|
||||||
|
int sb;// block site
|
||||||
|
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
|
||||||
|
Lexicographic::IndexFromCoor(coor_b,sb,block_l);
|
||||||
|
|
||||||
|
scalar_object data = extractLane(lane,fineData[sfr]);
|
||||||
|
|
||||||
|
int64_t site = (sc*lnvec + v)*bv
|
||||||
|
+ sb;
|
||||||
|
|
||||||
|
scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords];
|
||||||
|
*ptr = data;
|
||||||
|
#ifdef GRID_SIMT
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Export direct to multiRHS coarse order.
|
||||||
|
//
|
||||||
|
// Grid order : C[coarse_vol][nrhs][nbasis]
|
||||||
|
// BLAS order : BLAS_C[lane][coarse_vol][nrhs][nbasis]
|
||||||
|
//
|
||||||
|
// At Nsimd()==1 these are the same sequence of addresses.
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<class vobj>
|
||||||
|
void ExportCoarseGridMrhsVectors(Lattice<vobj> &vec_mrhs, deviceVector<scalar> &blas)
|
||||||
|
{
|
||||||
|
typedef typename vobj::scalar_object coarse_scalar_object;
|
||||||
|
|
||||||
|
GridBase *coarse_mrhs_grid = vec_mrhs.Grid();
|
||||||
|
int _ndimension = coarse_grid->_ndimension;
|
||||||
|
|
||||||
|
GRID_ASSERT(coarse_mrhs_grid->_ndimension == _ndimension+1);
|
||||||
|
GRID_ASSERT(coarse_mrhs_grid->_simd_layout[0] == 1);
|
||||||
|
GRID_ASSERT(coarse_mrhs_grid->_processors[0] == 1);
|
||||||
|
for(int d=0;d<_ndimension;d++){
|
||||||
|
GRID_ASSERT(coarse_mrhs_grid->_rdimensions[d+1] == coarse_grid->_rdimensions[d]);
|
||||||
|
GRID_ASSERT(coarse_mrhs_grid->_simd_layout[d+1] == coarse_grid->_simd_layout[d]);
|
||||||
|
}
|
||||||
|
int nvec = coarse_mrhs_grid->_rdimensions[0]; // nrhs
|
||||||
|
|
||||||
|
Coordinate coarse_mrhs_rdimensions = coarse_mrhs_grid->_rdimensions;
|
||||||
|
Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
|
||||||
|
Coordinate coarse_l = coarse_ldimensions;
|
||||||
|
Coordinate csimd = coarse_simd;
|
||||||
|
|
||||||
|
autoView( coarseData , vec_mrhs, AcceleratorWrite);
|
||||||
|
auto blasData_p = &blas[0];
|
||||||
|
auto coarseData_p = &coarseData[0];
|
||||||
|
|
||||||
|
int64_t osites = coarse_grid->oSites(); // D dimensional
|
||||||
|
int64_t osites_hi = coarse_mrhs_grid->oSites(); // nvec * osites
|
||||||
|
|
||||||
|
const int Nsimd = vobj::Nsimd();
|
||||||
|
uint64_t cwords=sizeof(typename vobj::scalar_object)/sizeof(scalar);
|
||||||
|
GRID_ASSERT(cwords==nbasis);
|
||||||
|
int64_t lnvec = nvec;
|
||||||
|
|
||||||
|
accelerator_for(scr,osites_hi,Nsimd,{
|
||||||
|
#ifdef GRID_SIMT
|
||||||
|
{
|
||||||
|
int lane=acceleratorSIMTlane(Nsimd); // buffer lane
|
||||||
|
#else
|
||||||
|
for(int lane=0;lane<Nsimd;lane++) {
|
||||||
|
#endif
|
||||||
|
Coordinate coor_hi(_ndimension+1);
|
||||||
|
Coordinate coor_l(_ndimension);
|
||||||
|
Coordinate coor_c(_ndimension);
|
||||||
|
|
||||||
|
Lexicographic::CoorFromIndex(coor_hi,scr,coarse_mrhs_rdimensions);
|
||||||
|
int v = coor_hi[0];
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_hi[d+1];
|
||||||
|
|
||||||
|
Lexicographic::CoorFromIndex(coor_l,lane,csimd);
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_c[d] += coarse_rdimensions[d]*coor_l[d];
|
||||||
|
|
||||||
|
int sc;
|
||||||
|
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
|
||||||
|
|
||||||
|
int64_t blas_site = (sc*lnvec + v)*cwords;
|
||||||
|
coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site];
|
||||||
|
coarse_scalar_object data = *ptr;
|
||||||
|
insertLane(lane,coarseData[scr],data);
|
||||||
|
#ifdef GRID_SIMT
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Reverse directions: BLAS_F -> fine mrhs field, coarse mrhs field -> BLAS_C
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
void ExportFineGridMrhsVectors(Field &vec_mrhs, deviceVector<scalar> &blas)
|
||||||
|
{
|
||||||
|
typedef typename Field::vector_object vobj;
|
||||||
|
|
||||||
|
GridBase *fine_mrhs_grid = vec_mrhs.Grid();
|
||||||
|
int _ndimension = coarse_grid->_ndimension;
|
||||||
|
|
||||||
|
GRID_ASSERT(fine_mrhs_grid->_ndimension == _ndimension+1);
|
||||||
|
GRID_ASSERT(fine_mrhs_grid->_simd_layout[0] == 1);
|
||||||
|
for(int d=0;d<_ndimension;d++){
|
||||||
|
GRID_ASSERT(fine_mrhs_grid->_rdimensions[d+1] == fine_grid->_rdimensions[d]);
|
||||||
|
GRID_ASSERT(fine_mrhs_grid->_simd_layout[d+1] == fine_grid->_simd_layout[d]);
|
||||||
|
}
|
||||||
|
int nvec = fine_mrhs_grid->_rdimensions[0];
|
||||||
|
|
||||||
|
Coordinate fine_mrhs_rdimensions = fine_mrhs_grid->_rdimensions;
|
||||||
|
Coordinate fine_rdimensions = fine_grid->_rdimensions;
|
||||||
|
Coordinate coarse_l = coarse_ldimensions;
|
||||||
|
Coordinate block_l = block_ldimensions;
|
||||||
|
Coordinate fsimd = fine_simd;
|
||||||
|
int64_t bv= block_vol;
|
||||||
|
|
||||||
|
autoView( fineData , vec_mrhs, AcceleratorWrite);
|
||||||
|
auto blasData_p = &blas[0];
|
||||||
|
auto fineData_p = &fineData[0];
|
||||||
|
|
||||||
|
int64_t osites = fine_grid->oSites();
|
||||||
|
int64_t osites_hi = fine_mrhs_grid->oSites();
|
||||||
|
|
||||||
|
const int Nsimd = vobj::Nsimd();
|
||||||
|
uint64_t lwords= words;
|
||||||
|
int64_t lnvec = nvec;
|
||||||
|
|
||||||
|
accelerator_for(sfr,osites_hi,Nsimd,{
|
||||||
|
#ifdef GRID_SIMT
|
||||||
|
{
|
||||||
|
int lane=acceleratorSIMTlane(Nsimd);
|
||||||
|
#else
|
||||||
|
for(int lane=0;lane<Nsimd;lane++) {
|
||||||
|
#endif
|
||||||
|
Coordinate coor_hi(_ndimension+1);
|
||||||
|
Coordinate coor_f(_ndimension);
|
||||||
|
Coordinate coor_l(_ndimension);
|
||||||
|
Coordinate coor_b(_ndimension);
|
||||||
|
Coordinate coor_c(_ndimension);
|
||||||
|
|
||||||
|
Lexicographic::CoorFromIndex(coor_hi,sfr,fine_mrhs_rdimensions);
|
||||||
|
int v = coor_hi[0];
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_f[d] = coor_hi[d+1];
|
||||||
|
|
||||||
|
Lexicographic::CoorFromIndex(coor_l,lane,fsimd);
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_f[d] += fine_rdimensions[d]*coor_l[d];
|
||||||
|
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_b[d] = coor_f[d]%block_l[d];
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_f[d]/block_l[d];
|
||||||
|
|
||||||
|
int sc,sb;
|
||||||
|
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
|
||||||
|
Lexicographic::IndexFromCoor(coor_b,sb,block_l);
|
||||||
|
|
||||||
|
int64_t site = (sc*lnvec + v)*bv
|
||||||
|
+ sb;
|
||||||
|
|
||||||
|
scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords];
|
||||||
|
scalar_object data = *ptr;
|
||||||
|
insertLane(lane,fineData[sfr],data);
|
||||||
|
#ifdef GRID_SIMT
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class vobj>
|
||||||
|
void ImportCoarseGridMrhsVectors(Lattice<vobj> &vec_mrhs, deviceVector<scalar> &blas)
|
||||||
|
{
|
||||||
|
typedef typename vobj::scalar_object coarse_scalar_object;
|
||||||
|
|
||||||
|
GridBase *coarse_mrhs_grid = vec_mrhs.Grid();
|
||||||
|
int _ndimension = coarse_grid->_ndimension;
|
||||||
|
|
||||||
|
GRID_ASSERT(coarse_mrhs_grid->_ndimension == _ndimension+1);
|
||||||
|
GRID_ASSERT(coarse_mrhs_grid->_simd_layout[0] == 1);
|
||||||
|
for(int d=0;d<_ndimension;d++){
|
||||||
|
GRID_ASSERT(coarse_mrhs_grid->_rdimensions[d+1] == coarse_grid->_rdimensions[d]);
|
||||||
|
GRID_ASSERT(coarse_mrhs_grid->_simd_layout[d+1] == coarse_grid->_simd_layout[d]);
|
||||||
|
}
|
||||||
|
int nvec = coarse_mrhs_grid->_rdimensions[0];
|
||||||
|
|
||||||
|
Coordinate coarse_mrhs_rdimensions = coarse_mrhs_grid->_rdimensions;
|
||||||
|
Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
|
||||||
|
Coordinate coarse_l = coarse_ldimensions;
|
||||||
|
Coordinate csimd = coarse_simd;
|
||||||
|
|
||||||
|
autoView( coarseData , vec_mrhs, AcceleratorRead);
|
||||||
|
auto blasData_p = &blas[0];
|
||||||
|
auto coarseData_p = &coarseData[0];
|
||||||
|
|
||||||
|
int64_t osites = coarse_grid->oSites();
|
||||||
|
int64_t osites_hi = coarse_mrhs_grid->oSites();
|
||||||
|
|
||||||
|
const int Nsimd = vobj::Nsimd();
|
||||||
|
uint64_t cwords=sizeof(typename vobj::scalar_object)/sizeof(scalar);
|
||||||
|
GRID_ASSERT(cwords==nbasis);
|
||||||
|
int64_t lnvec = nvec;
|
||||||
|
|
||||||
|
accelerator_for(scr,osites_hi,Nsimd,{
|
||||||
|
#ifdef GRID_SIMT
|
||||||
|
{
|
||||||
|
int lane=acceleratorSIMTlane(Nsimd);
|
||||||
|
#else
|
||||||
|
for(int lane=0;lane<Nsimd;lane++) {
|
||||||
|
#endif
|
||||||
|
Coordinate coor_hi(_ndimension+1);
|
||||||
|
Coordinate coor_l(_ndimension);
|
||||||
|
Coordinate coor_c(_ndimension);
|
||||||
|
|
||||||
|
Lexicographic::CoorFromIndex(coor_hi,scr,coarse_mrhs_rdimensions);
|
||||||
|
int v = coor_hi[0];
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_hi[d+1];
|
||||||
|
|
||||||
|
Lexicographic::CoorFromIndex(coor_l,lane,csimd);
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_c[d] += coarse_rdimensions[d]*coor_l[d];
|
||||||
|
|
||||||
|
int sc;
|
||||||
|
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
|
||||||
|
|
||||||
|
coarse_scalar_object data = extractLane(lane,coarseData[scr]);
|
||||||
|
|
||||||
|
int64_t blas_site = (sc*lnvec + v)*cwords;
|
||||||
|
coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site];
|
||||||
|
*ptr = data;
|
||||||
|
#ifdef GRID_SIMT
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void ExportFineGridVectors(std::vector <Field> &vecs, deviceVector<scalar> &blas)
|
void ExportFineGridVectors(std::vector <Field> &vecs, deviceVector<scalar> &blas)
|
||||||
{
|
{
|
||||||
typedef typename Field::vector_object vobj;
|
typedef typename Field::vector_object vobj;
|
||||||
@@ -221,17 +553,12 @@ public:
|
|||||||
|
|
||||||
GRID_ASSERT(vecs[0].Grid()==fine_grid);
|
GRID_ASSERT(vecs[0].Grid()==fine_grid);
|
||||||
|
|
||||||
subdivides(coarse_grid,fine_grid); // require they map
|
|
||||||
|
|
||||||
int _ndimension = coarse_grid->_ndimension;
|
int _ndimension = coarse_grid->_ndimension;
|
||||||
GRID_ASSERT(block_vol == fine_grid->oSites() / coarse_grid->oSites());
|
|
||||||
|
|
||||||
Coordinate block_r (_ndimension);
|
|
||||||
for(int d=0 ; d<_ndimension;d++){
|
|
||||||
block_r[d] = fine_grid->_rdimensions[d] / coarse_grid->_rdimensions[d];
|
|
||||||
}
|
|
||||||
Coordinate fine_rdimensions = fine_grid->_rdimensions;
|
Coordinate fine_rdimensions = fine_grid->_rdimensions;
|
||||||
Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
|
Coordinate coarse_l = coarse_ldimensions;
|
||||||
|
Coordinate block_l = block_ldimensions;
|
||||||
|
Coordinate fsimd = fine_simd;
|
||||||
|
|
||||||
// std::cout << " export fine Blas norm "<<blasNorm2(blas)<<std::endl;
|
// std::cout << " export fine Blas norm "<<blasNorm2(blas)<<std::endl;
|
||||||
|
|
||||||
@@ -259,23 +586,24 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
// One thread per fine site
|
// One thread per fine site
|
||||||
Coordinate coor_f(_ndimension);
|
Coordinate coor_f(_ndimension);
|
||||||
|
Coordinate coor_l(_ndimension);
|
||||||
Coordinate coor_b(_ndimension);
|
Coordinate coor_b(_ndimension);
|
||||||
Coordinate coor_c(_ndimension);
|
Coordinate coor_c(_ndimension);
|
||||||
|
|
||||||
Lexicographic::CoorFromIndex(coor_f,sf,fine_rdimensions);
|
Lexicographic::CoorFromIndex(coor_f,sf,fine_rdimensions);
|
||||||
|
Lexicographic::CoorFromIndex(coor_l,lane,fsimd);
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_f[d] += fine_rdimensions[d]*coor_l[d];
|
||||||
|
|
||||||
for(int d=0;d<_ndimension;d++) coor_b[d] = coor_f[d]%block_r[d];
|
for(int d=0;d<_ndimension;d++) coor_b[d] = coor_f[d]%block_l[d];
|
||||||
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_f[d]/block_r[d];
|
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_f[d]/block_l[d];
|
||||||
|
|
||||||
int sc;
|
int sc;
|
||||||
int sb;
|
int sb;
|
||||||
Lexicographic::IndexFromCoor(coor_c,sc,coarse_rdimensions);
|
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
|
||||||
Lexicographic::IndexFromCoor(coor_b,sb,block_r);
|
Lexicographic::IndexFromCoor(coor_b,sb,block_l);
|
||||||
|
|
||||||
// BLAS layout address calculation
|
// BLAS_F[coarse_vol][nvec][block_vol][words]
|
||||||
// words * block_vol * nbasis x coarse_vol
|
int64_t site = (sc*nvec + v)*bv
|
||||||
int64_t site = (lane*osites + sc*bv)*nvec
|
|
||||||
+ v*bv
|
|
||||||
+ sb;
|
+ sb;
|
||||||
|
|
||||||
scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords];
|
scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords];
|
||||||
@@ -306,6 +634,8 @@ public:
|
|||||||
uint64_t sz = blas.size();
|
uint64_t sz = blas.size();
|
||||||
|
|
||||||
Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
|
Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
|
||||||
|
Coordinate coarse_l = coarse_ldimensions;
|
||||||
|
Coordinate csimd = coarse_simd;
|
||||||
|
|
||||||
for(int v=0;v<vecs.size();v++){
|
for(int v=0;v<vecs.size();v++){
|
||||||
|
|
||||||
@@ -330,7 +660,16 @@ public:
|
|||||||
for(int lane=0;lane<Nsimd;lane++) {
|
for(int lane=0;lane<Nsimd;lane++) {
|
||||||
#endif
|
#endif
|
||||||
// C_br per site
|
// C_br per site
|
||||||
int64_t blas_site = (lane*osites + sc)*nvec*cwords + v*cwords;
|
Coordinate coor_c(_ndimension);
|
||||||
|
Coordinate coor_l(_ndimension);
|
||||||
|
Lexicographic::CoorFromIndex(coor_c,sc,coarse_rdimensions);
|
||||||
|
Lexicographic::CoorFromIndex(coor_l,lane,csimd);
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_c[d] += coarse_rdimensions[d]*coor_l[d];
|
||||||
|
|
||||||
|
int scl;
|
||||||
|
Lexicographic::IndexFromCoor(coor_c,scl,coarse_l);
|
||||||
|
|
||||||
|
int64_t blas_site = (scl*nvec + v)*cwords;
|
||||||
|
|
||||||
coarse_scalar_object data = extractLane(lane,coarseData[sc]);
|
coarse_scalar_object data = extractLane(lane,coarseData[sc]);
|
||||||
|
|
||||||
@@ -360,6 +699,8 @@ public:
|
|||||||
uint64_t sz = blas.size();
|
uint64_t sz = blas.size();
|
||||||
|
|
||||||
Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
|
Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
|
||||||
|
Coordinate coarse_l = coarse_ldimensions;
|
||||||
|
Coordinate csimd = coarse_simd;
|
||||||
|
|
||||||
// std::cout << " export coarsee Blas norm "<<blasNorm2(blas)<<std::endl;
|
// std::cout << " export coarsee Blas norm "<<blasNorm2(blas)<<std::endl;
|
||||||
for(int v=0;v<vecs.size();v++){
|
for(int v=0;v<vecs.size();v++){
|
||||||
@@ -385,7 +726,16 @@ public:
|
|||||||
#else
|
#else
|
||||||
for(int lane=0;lane<Nsimd;lane++) {
|
for(int lane=0;lane<Nsimd;lane++) {
|
||||||
#endif
|
#endif
|
||||||
int64_t blas_site = (lane*osites + sc)*nvec*cwords + v*cwords;
|
Coordinate coor_c(_ndimension);
|
||||||
|
Coordinate coor_l(_ndimension);
|
||||||
|
Lexicographic::CoorFromIndex(coor_c,sc,coarse_rdimensions);
|
||||||
|
Lexicographic::CoorFromIndex(coor_l,lane,csimd);
|
||||||
|
for(int d=0;d<_ndimension;d++) coor_c[d] += coarse_rdimensions[d]*coor_l[d];
|
||||||
|
|
||||||
|
int scl;
|
||||||
|
Lexicographic::IndexFromCoor(coor_c,scl,coarse_l);
|
||||||
|
|
||||||
|
int64_t blas_site = (scl*nvec + v)*cwords;
|
||||||
coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site];
|
coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site];
|
||||||
coarse_scalar_object data = *ptr;
|
coarse_scalar_object data = *ptr;
|
||||||
insertLane(lane,coarseData[sc],data);
|
insertLane(lane,coarseData[sc],data);
|
||||||
@@ -508,6 +858,168 @@ public:
|
|||||||
ExportFineGridVectors(fine, BLAS_F);
|
ExportFineGridVectors(fine, BLAS_F);
|
||||||
// std::cout << " exported "<<std::endl;
|
// std::cout << " exported "<<std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// multiRHS ordered interfaces. The GEMM is identical; only the import and
|
||||||
|
// export differ, so those are the whole of the layout question.
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<class cobj>
|
||||||
|
void blockProject(Field &fine_mrhs,Lattice<cobj> &coarse_mrhs)
|
||||||
|
{
|
||||||
|
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
|
||||||
|
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
||||||
|
GRID_ASSERT(nbasis==_nbasis);
|
||||||
|
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
|
||||||
|
|
||||||
|
BLAS_F.resize (fine_vol * words * nrhs );
|
||||||
|
BLAS_C.resize (coarse_vol * nbasis * nrhs );
|
||||||
|
|
||||||
|
ImportFineGridMrhsVectors(fine_mrhs,BLAS_F);
|
||||||
|
ProjectBLAS(nrhs);
|
||||||
|
ExportCoarseGridMrhsVectors(coarse_mrhs,BLAS_C);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class cobj>
|
||||||
|
void blockPromote(Field &fine_mrhs,Lattice<cobj> &coarse_mrhs)
|
||||||
|
{
|
||||||
|
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
|
||||||
|
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
||||||
|
GRID_ASSERT(nbasis==_nbasis);
|
||||||
|
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
|
||||||
|
|
||||||
|
BLAS_F.resize (fine_vol * words * nrhs );
|
||||||
|
BLAS_C.resize (coarse_vol * nbasis * nrhs );
|
||||||
|
|
||||||
|
ImportCoarseGridMrhsVectors(coarse_mrhs,BLAS_C);
|
||||||
|
PromoteBLAS(nrhs);
|
||||||
|
ExportFineGridMrhsVectors(fine_mrhs,BLAS_F);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Mixed orderings. A single RHS fine operator produces a vector of fine
|
||||||
|
// fields with no packing; the coarse side is still wanted in mrhs order.
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<class cobj>
|
||||||
|
void blockProject(std::vector<Field> &fine,Lattice<cobj> &coarse_mrhs)
|
||||||
|
{
|
||||||
|
int nrhs = fine.size();
|
||||||
|
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
||||||
|
GRID_ASSERT(nbasis==_nbasis);
|
||||||
|
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
|
||||||
|
|
||||||
|
BLAS_F.resize (fine_vol * words * nrhs );
|
||||||
|
BLAS_C.resize (coarse_vol * nbasis * nrhs );
|
||||||
|
|
||||||
|
ImportFineGridVectors(fine,BLAS_F);
|
||||||
|
ProjectBLAS(nrhs);
|
||||||
|
ExportCoarseGridMrhsVectors(coarse_mrhs,BLAS_C);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class cobj>
|
||||||
|
void blockProject(Field &fine_mrhs,std::vector< Lattice<cobj> > &coarse)
|
||||||
|
{
|
||||||
|
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
|
||||||
|
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
||||||
|
GRID_ASSERT(nbasis==_nbasis);
|
||||||
|
GRID_ASSERT(coarse.size()==nrhs);
|
||||||
|
|
||||||
|
BLAS_F.resize (fine_vol * words * nrhs );
|
||||||
|
BLAS_C.resize (coarse_vol * nbasis * nrhs );
|
||||||
|
|
||||||
|
ImportFineGridMrhsVectors(fine_mrhs,BLAS_F);
|
||||||
|
ProjectBLAS(nrhs);
|
||||||
|
ExportCoarseGridVectors(coarse,BLAS_C);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class cobj>
|
||||||
|
void blockPromote(std::vector<Field> &fine,Lattice<cobj> &coarse_mrhs)
|
||||||
|
{
|
||||||
|
int nrhs = fine.size();
|
||||||
|
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
||||||
|
GRID_ASSERT(nbasis==_nbasis);
|
||||||
|
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
|
||||||
|
|
||||||
|
BLAS_F.resize (fine_vol * words * nrhs );
|
||||||
|
BLAS_C.resize (coarse_vol * nbasis * nrhs );
|
||||||
|
|
||||||
|
ImportCoarseGridMrhsVectors(coarse_mrhs,BLAS_C);
|
||||||
|
PromoteBLAS(nrhs);
|
||||||
|
ExportFineGridVectors(fine,BLAS_F);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class cobj>
|
||||||
|
void blockPromote(Field &fine_mrhs,std::vector< Lattice<cobj> > &coarse)
|
||||||
|
{
|
||||||
|
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
|
||||||
|
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
|
||||||
|
GRID_ASSERT(nbasis==_nbasis);
|
||||||
|
GRID_ASSERT(coarse.size()==nrhs);
|
||||||
|
|
||||||
|
BLAS_F.resize (fine_vol * words * nrhs );
|
||||||
|
BLAS_C.resize (coarse_vol * nbasis * nrhs );
|
||||||
|
|
||||||
|
ImportCoarseGridVectors(coarse,BLAS_C);
|
||||||
|
PromoteBLAS(nrhs);
|
||||||
|
ExportFineGridMrhsVectors(fine_mrhs,BLAS_F);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Pointer tables and the batched GEMM, shared by both orderings
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
void BLASPointers(int nrhs,
|
||||||
|
deviceVector<scalar *> &Vd,
|
||||||
|
deviceVector<scalar *> &Fd,
|
||||||
|
deviceVector<scalar *> &Cd)
|
||||||
|
{
|
||||||
|
for(int c=0;c<coarse_vol;c++){
|
||||||
|
scalar * Vh = & BLAS_V[c*nbasis*block_vol*words];
|
||||||
|
scalar * Fh = & BLAS_F[c*nrhs*block_vol*words];
|
||||||
|
scalar * Ch = & BLAS_C[c*nrhs*nbasis];
|
||||||
|
acceleratorPut(Vd[c],Vh);
|
||||||
|
acceleratorPut(Fd[c],Fh);
|
||||||
|
acceleratorPut(Cd[c],Ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// C_br = V^dag F
|
||||||
|
void ProjectBLAS(int nrhs)
|
||||||
|
{
|
||||||
|
deviceVector<scalar *> Vd(coarse_vol);
|
||||||
|
deviceVector<scalar *> Fd(coarse_vol);
|
||||||
|
deviceVector<scalar *> Cd(coarse_vol);
|
||||||
|
BLASPointers(nrhs,Vd,Fd,Cd);
|
||||||
|
|
||||||
|
GridBLAS BLAS;
|
||||||
|
int64_t vw = block_vol * words;
|
||||||
|
BLAS.gemmBatched(GridBLAS_OP_C,GridBLAS_OP_N,
|
||||||
|
nbasis,nrhs,vw,
|
||||||
|
scalar(1.0),
|
||||||
|
Vd,
|
||||||
|
Fd,
|
||||||
|
scalar(0.0), // wipe out C
|
||||||
|
Cd);
|
||||||
|
BLAS.synchronise();
|
||||||
|
}
|
||||||
|
|
||||||
|
// F_xr = Vxb Cbr
|
||||||
|
void PromoteBLAS(int nrhs)
|
||||||
|
{
|
||||||
|
deviceVector<scalar *> Vd(coarse_vol);
|
||||||
|
deviceVector<scalar *> Fd(coarse_vol);
|
||||||
|
deviceVector<scalar *> Cd(coarse_vol);
|
||||||
|
BLASPointers(nrhs,Vd,Fd,Cd);
|
||||||
|
|
||||||
|
GridBLAS BLAS;
|
||||||
|
int64_t vw = block_vol * words;
|
||||||
|
BLAS.gemmBatched(GridBLAS_OP_N,GridBLAS_OP_N,
|
||||||
|
vw,nrhs,nbasis,
|
||||||
|
scalar(1.0),
|
||||||
|
Vd,
|
||||||
|
Cd,
|
||||||
|
scalar(0.0), // wipe out F
|
||||||
|
Fd);
|
||||||
|
BLAS.synchronise();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
NAMESPACE_END(Grid);
|
||||||
|
|||||||
@@ -54,6 +54,21 @@ class TwoLevelCG : public LinearFunction<Field>
|
|||||||
LinearOperatorBase<Field> &_FineLinop;
|
LinearOperatorBase<Field> &_FineLinop;
|
||||||
LinearFunction<Field> &_Smoother;
|
LinearFunction<Field> &_Smoother;
|
||||||
|
|
||||||
|
GridStopWatch ProjectTimer;
|
||||||
|
GridStopWatch PromoteTimer;
|
||||||
|
GridStopWatch CoarseTimer;
|
||||||
|
GridStopWatch SmoothTimer;
|
||||||
|
GridStopWatch MatrixTimer;
|
||||||
|
GridStopWatch M3Timer;
|
||||||
|
GridStopWatch LinalgTimer;
|
||||||
|
|
||||||
|
int64_t M3Calls;
|
||||||
|
int64_t SmoothCalls;
|
||||||
|
int64_t MatrixCalls;
|
||||||
|
int64_t ProjectCalls;
|
||||||
|
int64_t CoarseCalls;
|
||||||
|
int64_t PromoteCalls;
|
||||||
|
|
||||||
// more most opertor functions
|
// more most opertor functions
|
||||||
TwoLevelCG(RealD tol,
|
TwoLevelCG(RealD tol,
|
||||||
Integer maxit,
|
Integer maxit,
|
||||||
@@ -103,6 +118,14 @@ class TwoLevelCG : public LinearFunction<Field>
|
|||||||
RealD tn;
|
RealD tn;
|
||||||
|
|
||||||
GridStopWatch HDCGTimer;
|
GridStopWatch HDCGTimer;
|
||||||
|
ProjectTimer.Reset();
|
||||||
|
PromoteTimer.Reset();
|
||||||
|
CoarseTimer.Reset();
|
||||||
|
SmoothTimer.Reset();
|
||||||
|
MatrixTimer.Reset();
|
||||||
|
M3Timer.Reset();
|
||||||
|
LinalgTimer.Reset();
|
||||||
|
M3Calls = SmoothCalls = MatrixCalls = ProjectCalls = CoarseCalls = PromoteCalls = 0;
|
||||||
HDCGTimer.Start();
|
HDCGTimer.Start();
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
// x0 = Vstart -- possibly modify guess
|
// x0 = Vstart -- possibly modify guess
|
||||||
@@ -145,14 +168,19 @@ class TwoLevelCG : public LinearFunction<Field>
|
|||||||
int peri_kp = (k+1) % mmax;
|
int peri_kp = (k+1) % mmax;
|
||||||
|
|
||||||
rtz=rtzp;
|
rtz=rtzp;
|
||||||
|
M3Timer.Start();
|
||||||
d= PcgM3(p[peri_k],mmp[peri_k]);
|
d= PcgM3(p[peri_k],mmp[peri_k]);
|
||||||
|
M3Timer.Stop();
|
||||||
|
M3Calls++;
|
||||||
a = rtz/d;
|
a = rtz/d;
|
||||||
|
|
||||||
// Memorise this
|
// Memorise this
|
||||||
pAp[peri_k] = d;
|
pAp[peri_k] = d;
|
||||||
|
|
||||||
|
LinalgTimer.Start();
|
||||||
axpy(x,a,p[peri_k],x);
|
axpy(x,a,p[peri_k],x);
|
||||||
RealD rn = axpy_norm(r,-a,mmp[peri_k],r);
|
RealD rn = axpy_norm(r,-a,mmp[peri_k],r);
|
||||||
|
LinalgTimer.Stop();
|
||||||
|
|
||||||
// Compute z = M x
|
// Compute z = M x
|
||||||
PcgM1(r,z);
|
PcgM1(r,z);
|
||||||
@@ -163,7 +191,9 @@ class TwoLevelCG : public LinearFunction<Field>
|
|||||||
n2=norm2(z);
|
n2=norm2(z);
|
||||||
std::cout << GridLogMessage<<"HDCG::fPcg iteration "<<k<<" : vector r,z "<<n1<<" "<<n2<<"\n";
|
std::cout << GridLogMessage<<"HDCG::fPcg iteration "<<k<<" : vector r,z "<<n1<<" "<<n2<<"\n";
|
||||||
}
|
}
|
||||||
|
LinalgTimer.Start();
|
||||||
rtzp =real(innerProduct(r,z));
|
rtzp =real(innerProduct(r,z));
|
||||||
|
LinalgTimer.Stop();
|
||||||
std::cout << GridLogMessage<<"HDCG::fPcg iteration "<<k<<" : inner rtzp "<<rtzp<<"\n";
|
std::cout << GridLogMessage<<"HDCG::fPcg iteration "<<k<<" : inner rtzp "<<rtzp<<"\n";
|
||||||
|
|
||||||
// PcgM2(z,p[0]);
|
// PcgM2(z,p[0]);
|
||||||
@@ -202,7 +232,24 @@ class TwoLevelCG : public LinearFunction<Field>
|
|||||||
if ( rn <= rsq ) {
|
if ( rn <= rsq ) {
|
||||||
|
|
||||||
HDCGTimer.Stop();
|
HDCGTimer.Stop();
|
||||||
std::cout<<GridLogMessage<<"HDCG: fPcg converged in "<<k<<" iterations and "<<HDCGTimer.Elapsed()<<std::endl;;
|
std::cout<<GridLogMessage<<"HDCG: fPcg converged in "<<k<<" iterations and "<<HDCGTimer.Elapsed()<<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"HDCG: fPcg breakdown"<<std::endl;
|
||||||
|
auto mspc = [](GridStopWatch &sw, int64_t n) -> double {
|
||||||
|
return (n > 0) ? sw.useconds() * 1e-3 / n : 0.0;
|
||||||
|
};
|
||||||
|
std::cout<<GridLogMessage<<"HDCG: fPcg M3 (fine MVM) "<<M3Timer.Elapsed()
|
||||||
|
<<" "<<M3Calls<<" calls "<<mspc(M3Timer,M3Calls)<<" ms/call"<<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"HDCG: fPcg linalg "<<LinalgTimer.Elapsed()<<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"HDCG: fPcg smoother "<<SmoothTimer.Elapsed()
|
||||||
|
<<" "<<SmoothCalls<<" calls "<<mspc(SmoothTimer,SmoothCalls)<<" ms/call"<<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"HDCG: fPcg matrix (in M1) "<<MatrixTimer.Elapsed()
|
||||||
|
<<" "<<MatrixCalls<<" calls "<<mspc(MatrixTimer,MatrixCalls)<<" ms/call"<<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"HDCG: fPcg project "<<ProjectTimer.Elapsed()
|
||||||
|
<<" "<<ProjectCalls<<" calls "<<mspc(ProjectTimer,ProjectCalls)<<" ms/call"<<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"HDCG: fPcg coarse "<<CoarseTimer.Elapsed()
|
||||||
|
<<" "<<CoarseCalls<<" calls "<<mspc(CoarseTimer,CoarseCalls)<<" ms/call"<<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"HDCG: fPcg promote "<<PromoteTimer.Elapsed()
|
||||||
|
<<" "<<PromoteCalls<<" calls "<<mspc(PromoteTimer,PromoteCalls)<<" ms/call"<<std::endl;
|
||||||
|
|
||||||
_FineLinop.HermOp(x,mmp[0]);
|
_FineLinop.HermOp(x,mmp[0]);
|
||||||
axpy(tmp,-1.0,src,mmp[0]);
|
axpy(tmp,-1.0,src,mmp[0]);
|
||||||
@@ -475,35 +522,29 @@ class TwoLevelADEF2 : public TwoLevelCG<Field>
|
|||||||
CoarseField PleftProj(this->coarsegrid);
|
CoarseField PleftProj(this->coarsegrid);
|
||||||
CoarseField PleftMss_proj(this->coarsegrid);
|
CoarseField PleftMss_proj(this->coarsegrid);
|
||||||
|
|
||||||
GridStopWatch SmootherTimer;
|
this->SmoothTimer.Start();
|
||||||
GridStopWatch MatrixTimer;
|
|
||||||
SmootherTimer.Start();
|
|
||||||
this->_Smoother(in,Min);
|
this->_Smoother(in,Min);
|
||||||
SmootherTimer.Stop();
|
this->SmoothTimer.Stop();
|
||||||
|
this->SmoothCalls++;
|
||||||
|
|
||||||
MatrixTimer.Start();
|
this->MatrixTimer.Start();
|
||||||
this->_FineLinop.HermOp(Min,out);
|
this->_FineLinop.HermOp(Min,out);
|
||||||
MatrixTimer.Stop();
|
this->MatrixTimer.Stop();
|
||||||
|
this->MatrixCalls++;
|
||||||
axpy(tmp,-1.0,out,in); // tmp = in - A Min
|
axpy(tmp,-1.0,out,in); // tmp = in - A Min
|
||||||
|
|
||||||
GridStopWatch ProjTimer;
|
this->ProjectTimer.Start();
|
||||||
GridStopWatch CoarseTimer;
|
|
||||||
GridStopWatch PromTimer;
|
|
||||||
ProjTimer.Start();
|
|
||||||
this->_Aggregates.ProjectToSubspace(PleftProj,tmp);
|
this->_Aggregates.ProjectToSubspace(PleftProj,tmp);
|
||||||
ProjTimer.Stop();
|
this->ProjectTimer.Stop();
|
||||||
CoarseTimer.Start();
|
this->ProjectCalls++;
|
||||||
|
this->CoarseTimer.Start();
|
||||||
this->_CoarseSolver(PleftProj,PleftMss_proj); // Ass^{-1} [in - A Min]_s
|
this->_CoarseSolver(PleftProj,PleftMss_proj); // Ass^{-1} [in - A Min]_s
|
||||||
CoarseTimer.Stop();
|
this->CoarseTimer.Stop();
|
||||||
PromTimer.Start();
|
this->CoarseCalls++;
|
||||||
|
this->PromoteTimer.Start();
|
||||||
this->_Aggregates.PromoteFromSubspace(PleftMss_proj,tmp);// tmp = Q[in - A Min]
|
this->_Aggregates.PromoteFromSubspace(PleftMss_proj,tmp);// tmp = Q[in - A Min]
|
||||||
PromTimer.Stop();
|
this->PromoteTimer.Stop();
|
||||||
std::cout << GridLogPerformance << "PcgM1 breakdown "<<std::endl;
|
this->PromoteCalls++;
|
||||||
std::cout << GridLogPerformance << "\tSmoother " << SmootherTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogPerformance << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogPerformance << "\tProj " << ProjTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogPerformance << "\tCoarse " << CoarseTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogPerformance << "\tProm " << PromTimer.Elapsed() <<std::endl;
|
|
||||||
|
|
||||||
axpy(out,1.0,Min,tmp); // Min+tmp
|
axpy(out,1.0,Min,tmp); // Min+tmp
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,433 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/Arnoldi.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|
||||||
Author: paboyle <paboyle@ph.ed.ac.uk>
|
|
||||||
Author: Patrick Oare <poare@bnl.gov>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
|
||||||
*************************************************************************************/
|
|
||||||
/* END LEGAL */
|
|
||||||
#ifndef GRID_ARNOLDI_H
|
|
||||||
#define GRID_ARNOLDI_H
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
//Moved to KrylovSchur
|
|
||||||
#if 0
|
|
||||||
/**
|
|
||||||
<<<<<<< HEAD
|
|
||||||
* Options for which Ritz values to keep in implicit restart.
|
|
||||||
*/
|
|
||||||
enum RitzFilter {
|
|
||||||
EvalNormSmall, // Keep evals with smallest norm
|
|
||||||
EvalNormLarge, // Keep evals with largest norm
|
|
||||||
EvalReSmall, // Keep evals with smallest real part
|
|
||||||
EvalReLarge // Keep evals with largest real part
|
|
||||||
};
|
|
||||||
|
|
||||||
// Select comparison function from RitzFilter
|
|
||||||
struct ComplexComparator
|
|
||||||
{
|
|
||||||
RitzFilter f;
|
|
||||||
ComplexComparator (RitzFilter _f) : f(_f) {}
|
|
||||||
bool operator()(std::complex<double> z1, std::complex<double> z2) {
|
|
||||||
switch (f) {
|
|
||||||
RealD tmp1, tmp2;
|
|
||||||
tmp1=std::abs(std::imag(z1));
|
|
||||||
tmp2=std::abs(std::imag(z2));
|
|
||||||
case EvalNormSmall:
|
|
||||||
return std::abs(z1) < std::abs(z2);
|
|
||||||
case EvalNormLarge:
|
|
||||||
return std::abs(z1) > std::abs(z2);
|
|
||||||
// Terrible hack
|
|
||||||
// return std::abs(std::real(z1)) < std::abs(std::real(z2));
|
|
||||||
// if ( std::abs(std::real(z1)) >4.) tmp1 +=1.;
|
|
||||||
// if ( std::abs(std::real(z2)) >4.) tmp2 +=1.;
|
|
||||||
case EvalReSmall:
|
|
||||||
return tmp1 < tmp2;
|
|
||||||
// return std::abs(std::imag(z1)) < std::abs(std::imag(z2));
|
|
||||||
case EvalReLarge:
|
|
||||||
return tmp1 > tmp2;
|
|
||||||
// return std::abs(std::real(z1)) > std::abs(std::real(z2));
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
=======
|
|
||||||
>>>>>>> 68af1bba67dd62881ead5ab1e54962a5486a0791
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation of the Arnoldi algorithm.
|
|
||||||
*/
|
|
||||||
template<class Field>
|
|
||||||
class Arnoldi {
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::string cname = std::string("Arnoldi");
|
|
||||||
int MaxIter; // Max iterations
|
|
||||||
RealD Tolerance;
|
|
||||||
RealD ssq;
|
|
||||||
RealD rtol;
|
|
||||||
int Nm; // Number of basis vectors to track (equals MaxIter if no restart)
|
|
||||||
int Nk; // Number of basis vectors to keep every restart (equals -1 if no restart)
|
|
||||||
int Nstop; // Stop after converging Nstop eigenvectors.
|
|
||||||
|
|
||||||
LinearOperatorBase<Field> &Linop;
|
|
||||||
GridBase *Grid;
|
|
||||||
|
|
||||||
RealD approxLambdaMax;
|
|
||||||
RealD beta_k;
|
|
||||||
Field f;
|
|
||||||
std::vector<Field> basis; // orthonormal Arnoldi basis
|
|
||||||
Eigen::MatrixXcd Hess; // Hessenberg matrix of size Nbasis (after construction)
|
|
||||||
Eigen::MatrixXcd Qt; // Transpose of basis rotation which projects out high modes.
|
|
||||||
|
|
||||||
Eigen::VectorXcd evals; // evals of Hess
|
|
||||||
Eigen::MatrixXcd littleEvecs; // Nm x Nm evecs matrix
|
|
||||||
std::vector<Field> evecs; // Vector of evec fields
|
|
||||||
|
|
||||||
RitzFilter ritzFilter; // how to sort evals
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Arnoldi(LinearOperatorBase<Field> &_Linop, GridBase *_Grid, RealD _Tolerance, RitzFilter filter = EvalReSmall)
|
|
||||||
: Linop(_Linop), Grid(_Grid), Tolerance(_Tolerance), ritzFilter(filter), f(_Grid), MaxIter(-1), Nm(-1), Nk(-1),
|
|
||||||
Nstop (-1), evals (0), evecs (), ssq (0.0), rtol (0.0), beta_k (0.0), approxLambdaMax (0.0)
|
|
||||||
{
|
|
||||||
f = Zero();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs the Arnoldi loop with(out) implicit restarting. For each iteration:
|
|
||||||
* - Runs an Arnoldi step.
|
|
||||||
* - Computes the eigensystem of the Hessenberg matrix.
|
|
||||||
* - Performs implicit restarting.
|
|
||||||
*/
|
|
||||||
void operator()(const Field& v0, int _maxIter, int _Nm, int _Nk, int _Nstop, bool doubleOrthog = false) {
|
|
||||||
MaxIter = _maxIter;
|
|
||||||
Nm = _Nm; Nk = _Nk;
|
|
||||||
Nstop = _Nstop;
|
|
||||||
|
|
||||||
ssq = norm2(v0);
|
|
||||||
RealD approxLambdaMax = approxMaxEval(v0);
|
|
||||||
rtol = Tolerance * approxLambdaMax;
|
|
||||||
|
|
||||||
ComplexComparator compareComplex (ritzFilter);
|
|
||||||
std::cout << GridLogMessage << "Comparing Ritz values with: " << ritzFilter << std::endl;
|
|
||||||
|
|
||||||
int start = 1;
|
|
||||||
Field startVec = v0;
|
|
||||||
littleEvecs = Eigen::MatrixXcd::Zero(Nm, Nm);
|
|
||||||
for (int i = 0; i < MaxIter; i++) {
|
|
||||||
std::cout << GridLogMessage << "Restart Iteration " << i << std::endl;
|
|
||||||
|
|
||||||
// Perform Arnoldi steps to compute Krylov basis and Rayleigh quotient (Hess)
|
|
||||||
arnoldiIteration(startVec, Nm, start, doubleOrthog);
|
|
||||||
startVec = f;
|
|
||||||
|
|
||||||
// compute eigensystem and sort evals
|
|
||||||
// compute_eigensystem();
|
|
||||||
compute_eigensystem(Hess);
|
|
||||||
std::cout << GridLogMessage << "Eigenvalues after Arnoldi step: " << std::endl << evals << std::endl;
|
|
||||||
|
|
||||||
std::sort(evals.begin(), evals.end(), compareComplex);
|
|
||||||
std::cout << GridLogMessage << "Ritz values after sorting (first Nk preserved): " << std::endl << evals << std::endl;
|
|
||||||
// SU(N)::tepidConfiguration
|
|
||||||
|
|
||||||
// Implicit restart to de-weight unwanted eigenvalues
|
|
||||||
implicitRestart(_Nm, _Nk); // probably can delete _Nm and _Nk from function args
|
|
||||||
start = Nk;
|
|
||||||
|
|
||||||
// check convergence and return if needed.
|
|
||||||
int Nconv = converged();
|
|
||||||
std::cout << GridLogMessage << "Number of evecs converged: " << Nconv << std::endl;
|
|
||||||
if (Nconv >= Nstop || i == MaxIter - 1) {
|
|
||||||
std::cout << GridLogMessage << "Converged with " << Nconv << " / " << Nstop << " eigenvectors on iteration "
|
|
||||||
<< i << "." << std::endl;
|
|
||||||
basisRotate(evecs, Qt, 0, Nk, 0, Nk, Nm);
|
|
||||||
std::cout << GridLogMessage << "Eigenvalues [first " << Nconv << " converged]: " << std::endl << evals << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Approximates the maximum eigenvalue of Linop.Op to normalize the residual and test for convergence.
|
|
||||||
*
|
|
||||||
* Parameters
|
|
||||||
* ----------
|
|
||||||
* Field& v0
|
|
||||||
* Source field to start with. Must have non-zero norm.
|
|
||||||
* int MAX_ITER (default = 50)
|
|
||||||
* Maximum number of iterations for power approximation.
|
|
||||||
*
|
|
||||||
* Returns
|
|
||||||
* -------
|
|
||||||
* RealD lamApprox
|
|
||||||
* Approximation of largest eigenvalue.
|
|
||||||
*/
|
|
||||||
RealD approxMaxEval(const Field& v0, int MAX_ITER = 50) {
|
|
||||||
assert (norm2(v0) > 1e-8); // must have relatively large source norm to start
|
|
||||||
RealD lamApprox = 0.0;
|
|
||||||
RealD denom = 1.0; RealD num = 1.0;
|
|
||||||
Field v0cp (Grid); Field tmp (Grid);
|
|
||||||
v0cp = v0;
|
|
||||||
denom = std::sqrt(norm2(v0cp));
|
|
||||||
for (int i = 0; i < MAX_ITER; i++) {
|
|
||||||
Linop.Op(v0cp, tmp); // CAREFUL: do not do Op(tmp, tmp)
|
|
||||||
v0cp = tmp;
|
|
||||||
num = std::sqrt(norm2(v0cp)); // num = |A^{n+1} v0|
|
|
||||||
lamApprox = num / denom; // lam = |A^{n+1} v0| / |A^n v0|
|
|
||||||
std::cout << GridLogDebug << "Approx for max eval: " << lamApprox << std::endl;
|
|
||||||
denom = num; // denom = |A^{n} v0|
|
|
||||||
}
|
|
||||||
return lamApprox;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the Arnoldi basis for the Krylov space K_n(D, src). (TODO make private)
|
|
||||||
*
|
|
||||||
* Parameters
|
|
||||||
* ----------
|
|
||||||
* v0 : Field&
|
|
||||||
* Source to generate Krylov basis.
|
|
||||||
* Nm : int
|
|
||||||
* Final size of the basis desired. If the basis becomes complete before a basis of size Nm is constructed
|
|
||||||
* (determined by relative tolerance Tolerance), stops iteration there.
|
|
||||||
* doubleOrthog : bool (default = false)
|
|
||||||
* Whether to double orthogonalize the basis (for numerical cancellations) or not.
|
|
||||||
* start : int (default = 0)
|
|
||||||
* If non-zero, assumes part of the Arnoldi basis has already been constructed.
|
|
||||||
*/
|
|
||||||
void arnoldiIteration(const Field& v0, int Nm, int start = 1, bool doubleOrthog = false)
|
|
||||||
{
|
|
||||||
|
|
||||||
ComplexD coeff;
|
|
||||||
Field w (Grid); // A acting on last Krylov vector.
|
|
||||||
|
|
||||||
if (start == 1) { // initialize everything that we need.
|
|
||||||
RealD v0Norm = 1 / std::sqrt(ssq);
|
|
||||||
basis.push_back(v0Norm * v0); // normalized source
|
|
||||||
|
|
||||||
Hess = Eigen::MatrixXcd::Zero(Nm, Nm);
|
|
||||||
f = Zero();
|
|
||||||
} else {
|
|
||||||
assert( start == basis.size() ); // should be starting at the end of basis (start = Nk)
|
|
||||||
Eigen::MatrixXcd HessCp = Hess;
|
|
||||||
Hess = Eigen::MatrixXcd::Zero(Nm, Nm);
|
|
||||||
Hess(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk)) = HessCp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct next Arnoldi vector by normalizing w_i = Dv_i - \sum_j v_j h_{ji}
|
|
||||||
for (int i = start - 1; i < Nm; i++) {
|
|
||||||
|
|
||||||
Linop.Op(basis.back(), w);
|
|
||||||
for (int j = 0; j < basis.size(); j++) {
|
|
||||||
coeff = innerProduct(basis[j], w); // coeff = h_{ij}. Note that since {vi} is ONB it's OK to subtract it off after.
|
|
||||||
Hess(j, i) = coeff;
|
|
||||||
w -= coeff * basis[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doubleOrthog) {
|
|
||||||
// TODO implement
|
|
||||||
}
|
|
||||||
|
|
||||||
// add w_i to the pile
|
|
||||||
if (i < Nm - 1) {
|
|
||||||
coeff = std::sqrt(norm2(w));
|
|
||||||
Hess(i+1, i) = coeff;
|
|
||||||
basis.push_back(
|
|
||||||
(1.0/coeff) * w
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// after iterations, update f and beta_k = ||f||
|
|
||||||
f = w; // make sure f is not normalized
|
|
||||||
beta_k = std::sqrt(norm2(f)); // beta_k = ||f_k|| determines convergence.
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "|f|^2 after Arnoldi step = " << norm2(f) << std::endl;
|
|
||||||
std::cout << GridLogDebug << "Computed Hessenberg matrix = " << std::endl << Hess << std::endl;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Approximates the eigensystem of the linear operator by computing the eigensystem of
|
|
||||||
* the Hessenberg matrix. Assumes that the Hessenberg matrix has already been constructed (by
|
|
||||||
* calling the operator() function).
|
|
||||||
*
|
|
||||||
* TODO implement in parent class eventually.
|
|
||||||
*
|
|
||||||
* Parameters
|
|
||||||
* ----------
|
|
||||||
* Eigen::MatrixXcd& S
|
|
||||||
* Schur matrix (upper triangular) similar to original Rayleigh quotient.
|
|
||||||
*/
|
|
||||||
void compute_eigensystem(Eigen::MatrixXcd& S)
|
|
||||||
{
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "Computing eigenvalues." << std::endl;
|
|
||||||
|
|
||||||
evecs.clear();
|
|
||||||
|
|
||||||
Eigen::ComplexEigenSolver<Eigen::MatrixXcd> es;
|
|
||||||
es.compute(S);
|
|
||||||
evals = es.eigenvalues();
|
|
||||||
littleEvecs = es.eigenvectors();
|
|
||||||
|
|
||||||
// Convert evecs to lattice fields
|
|
||||||
for (int k = 0; k < evals.size(); k++) {
|
|
||||||
Eigen::VectorXcd vec = littleEvecs.col(k);
|
|
||||||
Field tmp (basis[0].Grid());
|
|
||||||
tmp = Zero();
|
|
||||||
for (int j = 0; j < basis.size(); j++) {
|
|
||||||
tmp = tmp + vec[j] * basis[j];
|
|
||||||
}
|
|
||||||
evecs.push_back(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "Eigenvalues: " << std::endl << evals << std::endl;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies the factorization DV = V^\dag H + f e^\dag with the last-computed
|
|
||||||
* V, H, f.
|
|
||||||
*/
|
|
||||||
// RealD verifyFactorization() {
|
|
||||||
// int k = basis.size(); // number of basis vectors, also the size of H.
|
|
||||||
// std::vector<Field> factorized (k, Zero());
|
|
||||||
// Field tmp (FGrid); tmp = Zero();
|
|
||||||
// for (int i = 0; i < basis.size(); i++) {
|
|
||||||
// Linop.Op(basis[i], tmp);
|
|
||||||
// }
|
|
||||||
// // basisRotate(basis, Q, 0, Nk, 0, Nk, Nm);
|
|
||||||
// // Linop.Op(, )
|
|
||||||
// }
|
|
||||||
|
|
||||||
/* Getters */
|
|
||||||
Eigen::MatrixXcd getHessenbergMat() { return Hess; }
|
|
||||||
Field getF() { return f; }
|
|
||||||
std::vector<Field> getBasis() { return basis; }
|
|
||||||
Eigen::VectorXcd getEvals() { return evals; }
|
|
||||||
std::vector<Field> getEvecs() { return evecs; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements implicit restarting for Arnoldi. Assumes eigenvalues are sorted.
|
|
||||||
*
|
|
||||||
* Parameters
|
|
||||||
* ----------
|
|
||||||
* int _Nm
|
|
||||||
* Size of basis to keep (Hessenberg is MxM).
|
|
||||||
* int Nk
|
|
||||||
* Number of basis vectors to keep at each restart.
|
|
||||||
*/
|
|
||||||
void implicitRestart(int _Nm, int _Nk) {
|
|
||||||
assert ( _Nk <= _Nm );
|
|
||||||
Nm = _Nm; Nk = _Nk;
|
|
||||||
int Np = Nm - Nk; // keep Nk smallest (or largest, depends on sort function) evecs
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "Computing QR Factorizations." << std::endl;
|
|
||||||
|
|
||||||
Eigen::MatrixXcd Q = Eigen::MatrixXcd::Identity(Nm, Nm);
|
|
||||||
Eigen::MatrixXcd Qi (Nm, Nm);
|
|
||||||
Eigen::MatrixXcd R (Nm, Nm);
|
|
||||||
|
|
||||||
for (int i = Nk; i < Nm; i++) { // keep the first Nk eigenvalues and iterate through the last Np. Should loop Np times
|
|
||||||
|
|
||||||
// Useful debugging output
|
|
||||||
std::cout << GridLogDebug << "Computing QR factorization for i = " << i << std::endl;
|
|
||||||
std::cout << GridLogDebug << "Eval shift = " << evals[i] << std::endl;
|
|
||||||
std::cout << GridLogDebug << "Hess before rotation: " << Hess << std::endl;
|
|
||||||
|
|
||||||
// QR factorize
|
|
||||||
Eigen::HouseholderQR<Eigen::MatrixXcd> QR (Hess - evals[i] * Eigen::MatrixXcd::Identity(Nm, Nm));
|
|
||||||
Qi = QR.householderQ();
|
|
||||||
Q = Q * Qi;
|
|
||||||
Hess = Qi.adjoint() * Hess * Qi;
|
|
||||||
|
|
||||||
std::cout << GridLogDebug << "Qt up to i = " << Q.transpose() << std::endl;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogDebug << "Hess after all rotations: " << std::endl << Hess << std::endl;
|
|
||||||
|
|
||||||
// form Arnoldi vector f: f is normal to the basis vectors and its norm \beta is used to determine the Ritz estimate.
|
|
||||||
std::complex<double> beta = Hess(Nk, Nk-1);
|
|
||||||
std::complex<double> sigma = Q(Nm-1, Nk-1);
|
|
||||||
f = basis[Nk] * beta + f * sigma;
|
|
||||||
RealD betak = std::sqrt(norm2(f));
|
|
||||||
std::cout << GridLogMessage << "|f|^2 after implicit restart = " << norm2(f) << std::endl;
|
|
||||||
|
|
||||||
// Rotate basis by Qt
|
|
||||||
Qt = Q.transpose();
|
|
||||||
basisRotate(basis, Qt, 0, Nk + 1, 0, Nm, Nm);
|
|
||||||
|
|
||||||
// rotate
|
|
||||||
basisRotate(evecs, Qt, 0, Nk + 1, 0, Nm, Nm);
|
|
||||||
|
|
||||||
// Truncate the basis and restart
|
|
||||||
basis = std::vector<Field> (basis.begin(), basis.begin() + Nk);
|
|
||||||
// evecs = std::vector<Field> (evecs.begin(), evecs.begin() + Nk);
|
|
||||||
Hess = Hess(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk));
|
|
||||||
|
|
||||||
std::cout << "evecs size: " << evecs.size() << std::endl;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes the number of Arnoldi eigenvectors that have converged. An eigenvector s is considered converged
|
|
||||||
* for a tolerance epsilon if
|
|
||||||
* r(s) := |\beta e_m^T s| < epsilon
|
|
||||||
* where beta is the norm of f_{m+1}.
|
|
||||||
*
|
|
||||||
* Parameters
|
|
||||||
* ----------
|
|
||||||
*
|
|
||||||
* Returns
|
|
||||||
* -------
|
|
||||||
* int : Number of converged eigenvectors.
|
|
||||||
*/
|
|
||||||
int converged() {
|
|
||||||
int Nconv = 0;
|
|
||||||
for (int k = 0; k < evecs.size(); k++) {
|
|
||||||
RealD emTs = std::abs(littleEvecs(Nm - 1, k)); // e_m^T s
|
|
||||||
RealD ritzEstimate = beta_k * emTs;
|
|
||||||
// TODO should be ritzEstimate < Tolerance * lambda_max
|
|
||||||
std::cout << GridLogMessage << "Ritz estimate for evec " << k << " = " << ritzEstimate << std::endl;
|
|
||||||
if (ritzEstimate < rtol) {
|
|
||||||
Nconv++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Nconv;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
|
||||||
@@ -212,13 +212,17 @@ public:
|
|||||||
<< "\tTarget " << Tolerance << std::endl;
|
<< "\tTarget " << Tolerance << std::endl;
|
||||||
|
|
||||||
// std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl;
|
// std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl;
|
||||||
std::cout << GridLogMessage << "\tSolver Elapsed " << SolverTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl;
|
||||||
std::cout << GridLogPerformance << "Time breakdown "<<std::endl;
|
std::cout << GridLogMessage << "\tConstruct " << ConstructTimer.Elapsed() <<std::endl;
|
||||||
std::cout << GridLogPerformance << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tNorm " << NormTimer.Elapsed() <<std::endl;
|
||||||
std::cout << GridLogPerformance << "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tAssign " << AssignTimer.Elapsed() <<std::endl;
|
||||||
std::cout << GridLogPerformance << "\t\tInner " << InnerTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tSolver " << SolverTimer.Elapsed() <<std::endl;
|
||||||
std::cout << GridLogPerformance << "\t\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "Solver breakdown "<<std::endl;
|
||||||
std::cout << GridLogPerformance << "\t\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||||
|
std::cout << GridLogMessage << "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
||||||
|
std::cout << GridLogMessage << "\t\tInner " << InnerTimer.Elapsed() <<std::endl;
|
||||||
|
std::cout << GridLogMessage << "\t\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
|
||||||
|
std::cout << GridLogMessage << "\t\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
|
||||||
|
|
||||||
std::cout << GridLogDebug << "\tMobius flop rate " << DwfFlops/ usecs<< " Gflops " <<std::endl;
|
std::cout << GridLogDebug << "\tMobius flop rate " << DwfFlops/ usecs<< " Gflops " <<std::endl;
|
||||||
|
|
||||||
|
|||||||
@@ -1,277 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/ConjugateGradientTimeslice.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
|
||||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|
||||||
Author: paboyle <paboyle@ph.ed.ac.uk>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution
|
|
||||||
directory
|
|
||||||
*************************************************************************************/
|
|
||||||
/* END LEGAL */
|
|
||||||
#ifndef GRID_CONJUGATE_GRADIENT_TIMESLICE_H
|
|
||||||
#define GRID_CONJUGATE_GRADIENT_TIMESLICE_H
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
// Base classes for iterative processes based on operators
|
|
||||||
// single input vec, single output vec.
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple modification of conjugate gradient that outputs the residual as a function
|
|
||||||
* of time, in order to study the large wavelength behavior of the solver.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
template <class Field>
|
|
||||||
class ConjugateGradientTimeslice : public OperatorFunction<Field> {
|
|
||||||
public:
|
|
||||||
|
|
||||||
using OperatorFunction<Field>::operator();
|
|
||||||
|
|
||||||
bool ErrorOnNoConverge; // throw an assert when the CG fails to converge.
|
|
||||||
// Defaults true.
|
|
||||||
RealD Tolerance;
|
|
||||||
Integer MaxIterations;
|
|
||||||
Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion
|
|
||||||
RealD TrueResidual;
|
|
||||||
|
|
||||||
ConjugateGradientTimeslice(RealD tol, Integer maxit, bool err_on_no_conv = true)
|
|
||||||
: Tolerance(tol),
|
|
||||||
MaxIterations(maxit),
|
|
||||||
ErrorOnNoConverge(err_on_no_conv)
|
|
||||||
{};
|
|
||||||
|
|
||||||
virtual void LogIteration(int k,RealD a,RealD b){
|
|
||||||
// std::cout << "ConjugageGradient::LogIteration() "<<std::endl;
|
|
||||||
};
|
|
||||||
virtual void LogBegin(void){
|
|
||||||
std::cout << "ConjugageGradient::LogBegin() "<<std::endl;
|
|
||||||
};
|
|
||||||
|
|
||||||
void operator()(LinearOperatorBase<Field> &Linop, const Field &src, Field &psi) {
|
|
||||||
|
|
||||||
this->LogBegin();
|
|
||||||
|
|
||||||
GRID_TRACE("ConjugateGradientTimeslice");
|
|
||||||
GridStopWatch PreambleTimer;
|
|
||||||
GridStopWatch ConstructTimer;
|
|
||||||
GridStopWatch NormTimer;
|
|
||||||
GridStopWatch AssignTimer;
|
|
||||||
PreambleTimer.Start();
|
|
||||||
psi.Checkerboard() = src.Checkerboard();
|
|
||||||
|
|
||||||
conformable(psi, src);
|
|
||||||
|
|
||||||
RealD cp, c, a, d, b, ssq, qq;
|
|
||||||
//RealD b_pred;
|
|
||||||
|
|
||||||
// Was doing copies
|
|
||||||
ConstructTimer.Start();
|
|
||||||
Field p (src.Grid());
|
|
||||||
Field mmp(src.Grid());
|
|
||||||
Field r (src.Grid());
|
|
||||||
ConstructTimer.Stop();
|
|
||||||
|
|
||||||
// Initial residual computation & set up
|
|
||||||
NormTimer.Start();
|
|
||||||
ssq = norm2(src); // Norm of source vector ||b||^2
|
|
||||||
|
|
||||||
ssqtx = localNorm2(src); // Norm |b(x, t)|^2 as a field
|
|
||||||
std::vector<RealD> ssqt; // Norm of source not summed over time slices, ssq(t) = \sum_x |b(x, t)|^2
|
|
||||||
sliceSum(ssqtx, ssqt, Tdir); // TODO make sure Tdir is globally defined
|
|
||||||
|
|
||||||
RealD guess = norm2(psi); // Norm of initial guess ||psi||^2
|
|
||||||
NormTimer.Stop();
|
|
||||||
assert(std::isnan(guess) == 0);
|
|
||||||
AssignTimer.Start();
|
|
||||||
if ( guess == 0.0 ) {
|
|
||||||
r = src;
|
|
||||||
p = r;
|
|
||||||
a = ssq;
|
|
||||||
} else {
|
|
||||||
Linop.HermOpAndNorm(psi, mmp, d, b); //
|
|
||||||
r = src - mmp; // Initial residual r0 = b - A guess
|
|
||||||
p = r; // initial conj vector p0 = r0
|
|
||||||
a = norm2(p);
|
|
||||||
}
|
|
||||||
cp = a;
|
|
||||||
AssignTimer.Stop();
|
|
||||||
|
|
||||||
// Handle trivial case of zero src
|
|
||||||
if (ssq == 0.){
|
|
||||||
psi = Zero();
|
|
||||||
IterationsToComplete = 1;
|
|
||||||
TrueResidual = 0.;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: guess " << guess << std::endl;
|
|
||||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: src " << ssq << std::endl;
|
|
||||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: mp " << d << std::endl;
|
|
||||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: mmp " << b << std::endl;
|
|
||||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: cp,r " << cp << std::endl;
|
|
||||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: p " << a << std::endl;
|
|
||||||
|
|
||||||
RealD rsq = Tolerance * Tolerance * ssq;
|
|
||||||
|
|
||||||
// Check if guess is really REALLY good :)
|
|
||||||
if (cp <= rsq) {
|
|
||||||
TrueResidual = std::sqrt(a/ssq);
|
|
||||||
std::cout << GridLogMessage << "ConjugateGradient guess is converged already " << std::endl;
|
|
||||||
IterationsToComplete = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << std::setprecision(8)
|
|
||||||
<< "ConjugateGradient: k=0 residual " << cp << " target " << rsq << std::endl;
|
|
||||||
|
|
||||||
PreambleTimer.Stop();
|
|
||||||
GridStopWatch LinalgTimer;
|
|
||||||
GridStopWatch InnerTimer;
|
|
||||||
GridStopWatch AxpyNormTimer;
|
|
||||||
GridStopWatch LinearCombTimer;
|
|
||||||
GridStopWatch MatrixTimer;
|
|
||||||
GridStopWatch SolverTimer;
|
|
||||||
|
|
||||||
RealD usecs = -usecond();
|
|
||||||
SolverTimer.Start();
|
|
||||||
int k;
|
|
||||||
for (k = 1; k <= MaxIterations; k++) {
|
|
||||||
|
|
||||||
GridStopWatch IterationTimer;
|
|
||||||
IterationTimer.Start();
|
|
||||||
c = cp;
|
|
||||||
|
|
||||||
MatrixTimer.Start();
|
|
||||||
Linop.HermOp(p, mmp); // Computes mmp = Ap
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
|
|
||||||
InnerTimer.Start();
|
|
||||||
ComplexD dc = innerProduct(p,mmp); // p^\dagger A p
|
|
||||||
InnerTimer.Stop();
|
|
||||||
d = dc.real();
|
|
||||||
a = c / d;
|
|
||||||
|
|
||||||
// What is axpy? Some accelerator or something? Check Lattice_arith.h
|
|
||||||
AxpyNormTimer.Start();
|
|
||||||
|
|
||||||
// axpy_norm computes ax+by for vectors x and y compatible with a GPU. Here b is set to 1 (see the function in Lattice_reduction.h).
|
|
||||||
// The first argument passes r by reference, so it stores r --> -a * Ap + 1 * r, i.e. it performs an update on
|
|
||||||
// r_k --> r_{k+1} = r_k - \alpha_k A p_k. The function returns the norm squared of the first variable, i.e. ||r_{k+1}||^2.
|
|
||||||
cp = axpy_norm(r, -a, mmp, r);
|
|
||||||
AxpyNormTimer.Stop();
|
|
||||||
b = cp / c;
|
|
||||||
|
|
||||||
LinearCombTimer.Start();
|
|
||||||
{
|
|
||||||
autoView( psi_v , psi, AcceleratorWrite);
|
|
||||||
autoView( p_v , p, AcceleratorWrite);
|
|
||||||
autoView( r_v , r, AcceleratorWrite);
|
|
||||||
accelerator_for(ss,p_v.size(), Field::vector_object::Nsimd(),{
|
|
||||||
coalescedWrite(psi_v[ss], a * p_v(ss) + psi_v(ss));
|
|
||||||
coalescedWrite(p_v[ss] , b * p_v(ss) + r_v (ss));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
LinearCombTimer.Stop();
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
LogIteration(k,a,b);
|
|
||||||
|
|
||||||
IterationTimer.Stop();
|
|
||||||
if ( (k % 500) == 0 ) {
|
|
||||||
std::cout << GridLogMessage << "ConjugateGradient: Iteration " << k
|
|
||||||
<< " residual " << sqrt(cp/ssq) << " target " << Tolerance << std::endl;
|
|
||||||
} else {
|
|
||||||
std::cout << GridLogIterative << "ConjugateGradient: Iteration " << k
|
|
||||||
<< " residual " << sqrt(cp/ssq) << " target " << Tolerance << " took " << IterationTimer.Elapsed() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stopping condition
|
|
||||||
if (cp <= rsq) {
|
|
||||||
usecs +=usecond();
|
|
||||||
SolverTimer.Stop();
|
|
||||||
Linop.HermOpAndNorm(psi, mmp, d, qq);
|
|
||||||
p = mmp - src;
|
|
||||||
GridBase *grid = src.Grid();
|
|
||||||
RealD DwfFlops = (1452. )*grid->gSites()*4*k
|
|
||||||
+ (8+4+8+4+4)*12*grid->gSites()*k; // CG linear algebra
|
|
||||||
RealD srcnorm = std::sqrt(norm2(src));
|
|
||||||
RealD resnorm = std::sqrt(norm2(p));
|
|
||||||
RealD true_residual = resnorm / srcnorm;
|
|
||||||
std::cout << GridLogMessage << "ConjugateGradient Converged on iteration " << k
|
|
||||||
<< "\tComputed residual " << std::sqrt(cp / ssq)
|
|
||||||
<< "\tTrue residual " << true_residual
|
|
||||||
<< "\tTarget " << Tolerance << std::endl;
|
|
||||||
|
|
||||||
// GridLogMessage logs the message to the terminal output; GridLogPerformance probably writes to a log file?
|
|
||||||
// std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tSolver Elapsed " << SolverTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogPerformance << "Time breakdown "<<std::endl;
|
|
||||||
std::cout << GridLogPerformance << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogPerformance << "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogPerformance << "\t\tInner " << InnerTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogPerformance << "\t\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogPerformance << "\t\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
|
|
||||||
|
|
||||||
std::cout << GridLogDebug << "\tMobius flop rate " << DwfFlops/ usecs<< " Gflops " <<std::endl;
|
|
||||||
|
|
||||||
if (ErrorOnNoConverge) assert(true_residual / Tolerance < 10000.0);
|
|
||||||
|
|
||||||
IterationsToComplete = k;
|
|
||||||
TrueResidual = true_residual;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Failed. Calculate true residual before giving up
|
|
||||||
// Linop.HermOpAndNorm(psi, mmp, d, qq);
|
|
||||||
// p = mmp - src;
|
|
||||||
//TrueResidual = sqrt(norm2(p)/ssq);
|
|
||||||
// TrueResidual = 1;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "ConjugateGradient did NOT converge "<<k<<" / "<< MaxIterations
|
|
||||||
<<" residual "<< std::sqrt(cp / ssq)<< std::endl;
|
|
||||||
SolverTimer.Stop();
|
|
||||||
std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tConstruct " << ConstructTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tNorm " << NormTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tAssign " << AssignTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tSolver " << SolverTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "Solver breakdown "<<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage<< "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogPerformance << "\t\tInner " << InnerTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogPerformance << "\t\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogPerformance << "\t\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
|
|
||||||
|
|
||||||
if (ErrorOnNoConverge) assert(0);
|
|
||||||
IterationsToComplete = k;
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
|
||||||
@@ -53,18 +53,6 @@ enum IRLdiagonalisation {
|
|||||||
IRLdiagonaliseWithEigen
|
IRLdiagonaliseWithEigen
|
||||||
};
|
};
|
||||||
|
|
||||||
enum IRLeigsort {
|
|
||||||
IRLeigsortMax,
|
|
||||||
IRLeigsortSqMin
|
|
||||||
};
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
bool square_comp(RealD a, RealD b){
|
|
||||||
if (a*a<b*b) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template<class Field> class ImplicitlyRestartedLanczosHermOpTester : public ImplicitlyRestartedLanczosTester<Field>
|
template<class Field> class ImplicitlyRestartedLanczosHermOpTester : public ImplicitlyRestartedLanczosTester<Field>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -131,9 +119,8 @@ class ImplicitlyRestartedLanczos {
|
|||||||
/////////////////////////
|
/////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
public:
|
|
||||||
IRLeigsort EigSort;
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
// PAB:
|
// PAB:
|
||||||
@@ -167,7 +154,6 @@ class ImplicitlyRestartedLanczos {
|
|||||||
Nstop(_Nstop) , Nk(_Nk), Nm(_Nm),
|
Nstop(_Nstop) , Nk(_Nk), Nm(_Nm),
|
||||||
eresid(_eresid), betastp(_betastp),
|
eresid(_eresid), betastp(_betastp),
|
||||||
MaxIter(_MaxIter) , MinRestart(_MinRestart),
|
MaxIter(_MaxIter) , MinRestart(_MinRestart),
|
||||||
EigSort(IRLeigsortMax),
|
|
||||||
orth_period(_orth_period), diagonalisation(_diagonalisation) { };
|
orth_period(_orth_period), diagonalisation(_diagonalisation) { };
|
||||||
|
|
||||||
ImplicitlyRestartedLanczos(LinearFunction<Field> & PolyOp,
|
ImplicitlyRestartedLanczos(LinearFunction<Field> & PolyOp,
|
||||||
@@ -184,7 +170,6 @@ class ImplicitlyRestartedLanczos {
|
|||||||
Nstop(_Nstop) , Nk(_Nk), Nm(_Nm),
|
Nstop(_Nstop) , Nk(_Nk), Nm(_Nm),
|
||||||
eresid(_eresid), betastp(_betastp),
|
eresid(_eresid), betastp(_betastp),
|
||||||
MaxIter(_MaxIter) , MinRestart(_MinRestart),
|
MaxIter(_MaxIter) , MinRestart(_MinRestart),
|
||||||
EigSort(IRLeigsortMax),
|
|
||||||
orth_period(_orth_period), diagonalisation(_diagonalisation) { };
|
orth_period(_orth_period), diagonalisation(_diagonalisation) { };
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
@@ -331,12 +316,8 @@ until convergence
|
|||||||
// sorting
|
// sorting
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
eval2_copy = eval2;
|
eval2_copy = eval2;
|
||||||
// if (EigSort==IRLeigsortMax)
|
|
||||||
// std::partial_sort(eval2.begin(),eval2.begin()+Nm,eval2.end(),square_comp);
|
|
||||||
// else
|
|
||||||
std::partial_sort(eval2.begin(),eval2.begin()+Nm,eval2.end(),std::greater<RealD>());
|
std::partial_sort(eval2.begin(),eval2.begin()+Nm,eval2.end(),std::greater<RealD>());
|
||||||
std::cout<<GridLogIRL <<" evals sorted "<<std::endl;
|
std::cout<<GridLogIRL <<" evals sorted "<<std::endl;
|
||||||
// eval2_copy = eval2;
|
|
||||||
const int chunk=8;
|
const int chunk=8;
|
||||||
for(int io=0; io<k2;io+=chunk){
|
for(int io=0; io<k2;io+=chunk){
|
||||||
std::cout<<GridLogIRL << "eval "<< std::setw(3) << io ;
|
std::cout<<GridLogIRL << "eval "<< std::setw(3) << io ;
|
||||||
@@ -352,7 +333,6 @@ until convergence
|
|||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
Qt = Eigen::MatrixXd::Identity(Nm,Nm);
|
Qt = Eigen::MatrixXd::Identity(Nm,Nm);
|
||||||
for(int ip=k2; ip<Nm; ++ip){
|
for(int ip=k2; ip<Nm; ++ip){
|
||||||
// std::cout<<GridLogIRL <<"QR decompose "<<eval2[ip]<<std::endl;
|
|
||||||
QR_decomp(eval,lme,Nm,Nm,Qt,eval2[ip],k1,Nm);
|
QR_decomp(eval,lme,Nm,Nm,Qt,eval2[ip],k1,Nm);
|
||||||
}
|
}
|
||||||
std::cout<<GridLogIRL <<"QR decomposed "<<std::endl;
|
std::cout<<GridLogIRL <<"QR decomposed "<<std::endl;
|
||||||
@@ -395,8 +375,7 @@ until convergence
|
|||||||
|
|
||||||
// power of two search pattern; not every evalue in eval2 is assessed.
|
// power of two search pattern; not every evalue in eval2 is assessed.
|
||||||
int allconv =1;
|
int allconv =1;
|
||||||
// for(int jj = 1; jj<=Nstop; jj*=2){
|
for(int jj = 1; jj<=Nstop; jj*=2){
|
||||||
for(int jj = 1; jj<=Nstop; jj++){
|
|
||||||
int j = Nstop-jj;
|
int j = Nstop-jj;
|
||||||
RealD e = eval2_copy[j]; // Discard the evalue
|
RealD e = eval2_copy[j]; // Discard the evalue
|
||||||
basisRotateJ(B,evec,Qt,j,0,Nk,Nm);
|
basisRotateJ(B,evec,Qt,j,0,Nk,Nm);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,276 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./Grid/algorithms/iterative/LanczosBidiagonalization.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Chulwoo Jung <chulwoo@bnl.gov>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
|
||||||
*************************************************************************************/
|
|
||||||
/* END LEGAL */
|
|
||||||
#ifndef GRID_LANCZOS_BIDIAGONALIZATION_H
|
|
||||||
#define GRID_LANCZOS_BIDIAGONALIZATION_H
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lanczos Bidiagonalization (Golub-Kahan)
|
|
||||||
*
|
|
||||||
* For a linear operator A with adjoint A^dag, constructs the bidiagonal
|
|
||||||
* decomposition:
|
|
||||||
*
|
|
||||||
* A V_m = U_m B_m
|
|
||||||
* A^dag U_m = V_m B_m^T + beta_{m+1} v_{m+1} e_m^T
|
|
||||||
*
|
|
||||||
* where:
|
|
||||||
* V_m = [v_1, ..., v_m] right Lanczos vectors (orthonormal)
|
|
||||||
* U_m = [u_1, ..., u_m] left Lanczos vectors (orthonormal)
|
|
||||||
* B_m is upper bidiagonal with diag(alpha_1,...,alpha_m) and
|
|
||||||
* superdiag(beta_2,...,beta_m)
|
|
||||||
*
|
|
||||||
* The singular values of A are approximated by those of B_m.
|
|
||||||
* The singular values of B_m are the square roots of the eigenvalues of
|
|
||||||
* the symmetric tridiagonal matrix B_m^T B_m.
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* LanczosBidiagonalization<Field> lb(Linop, grid);
|
|
||||||
* lb.run(src, Nm, tol);
|
|
||||||
* // Access results via getters.
|
|
||||||
*/
|
|
||||||
template <class Field>
|
|
||||||
class LanczosBidiagonalization {
|
|
||||||
|
|
||||||
public:
|
|
||||||
LinearOperatorBase<Field> &Linop;
|
|
||||||
GridBase *Grid;
|
|
||||||
|
|
||||||
int Nm; // number of Lanczos steps taken
|
|
||||||
RealD Tolerance; // convergence threshold on beta_{k+1} / alpha_k
|
|
||||||
|
|
||||||
std::vector<Field> V; // right Lanczos vectors v_1 ... v_m
|
|
||||||
std::vector<Field> U; // left Lanczos vectors u_1 ... u_m
|
|
||||||
std::vector<RealD> alpha; // diagonal of bidiagonal matrix
|
|
||||||
std::vector<RealD> beta; // super-diagonal (beta[k] couples u_k and v_{k+1})
|
|
||||||
|
|
||||||
// SVD of the bidiagonal matrix (filled after computeSVD())
|
|
||||||
Eigen::VectorXd singularValues;
|
|
||||||
Eigen::MatrixXd leftSVecs; // columns are left singular vectors of B
|
|
||||||
Eigen::MatrixXd rightSVecs; // columns are right singular vectors of B
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
LanczosBidiagonalization(LinearOperatorBase<Field> &_Linop, GridBase *_Grid,
|
|
||||||
RealD _tol = 1.0e-8)
|
|
||||||
: Linop(_Linop), Grid(_Grid), Tolerance(_tol), Nm(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the Golub-Kahan Lanczos bidiagonalization.
|
|
||||||
*
|
|
||||||
* Parameters
|
|
||||||
* ----------
|
|
||||||
* src : starting vector (need not be normalised)
|
|
||||||
* Nmax : maximum number of Lanczos steps
|
|
||||||
* reorth : if true, full reorthogonalisation of both V and U bases
|
|
||||||
*/
|
|
||||||
void run(const Field &src, int Nmax, bool reorth = true)
|
|
||||||
{
|
|
||||||
assert(norm2(src) > 0.0);
|
|
||||||
|
|
||||||
V.clear(); U.clear();
|
|
||||||
alpha.clear(); beta.clear();
|
|
||||||
Nm = 0;
|
|
||||||
|
|
||||||
Field p(Grid), r(Grid);
|
|
||||||
|
|
||||||
// --- initialise: v_1 = src / ||src|| ---
|
|
||||||
Field v(Grid);
|
|
||||||
v = src;
|
|
||||||
RealD nrm = std::sqrt(norm2(v));
|
|
||||||
v = (1.0 / nrm) * v;
|
|
||||||
V.push_back(v);
|
|
||||||
|
|
||||||
for (int k = 0; k < Nmax; ++k) {
|
|
||||||
|
|
||||||
// p = A v_k
|
|
||||||
Linop.Op(V[k], p);
|
|
||||||
|
|
||||||
// p = p - beta_k * u_{k-1} (remove previous left vector)
|
|
||||||
if (k > 0) {
|
|
||||||
p = p - beta[k-1] * U[k-1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// alpha_k = ||p||
|
|
||||||
RealD ak = std::sqrt(norm2(p));
|
|
||||||
if (ak < 1.0e-14) {
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "LanczosBidiagonalization: lucky breakdown at step "
|
|
||||||
<< k << " (alpha = " << ak << ")" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
alpha.push_back(ak);
|
|
||||||
|
|
||||||
// u_k = p / alpha_k
|
|
||||||
Field u(Grid);
|
|
||||||
u = (1.0 / ak) * p;
|
|
||||||
|
|
||||||
// full reorthogonalisation of u against previous U
|
|
||||||
if (reorth) {
|
|
||||||
for (int j = 0; j < (int)U.size(); ++j) {
|
|
||||||
ComplexD ip = innerProduct(U[j], u);
|
|
||||||
u = u - ip * U[j];
|
|
||||||
}
|
|
||||||
RealD unrm = std::sqrt(norm2(u));
|
|
||||||
if (unrm > 1.0e-14) u = (1.0 / unrm) * u;
|
|
||||||
}
|
|
||||||
U.push_back(u);
|
|
||||||
|
|
||||||
// r = A^dag u_k - alpha_k * v_k
|
|
||||||
Linop.AdjOp(U[k], r);
|
|
||||||
r = r - ak * V[k];
|
|
||||||
|
|
||||||
// full reorthogonalisation of r against previous V
|
|
||||||
if (reorth) {
|
|
||||||
for (int j = 0; j < (int)V.size(); ++j) {
|
|
||||||
ComplexD ip = innerProduct(V[j], r);
|
|
||||||
r = r - ip * V[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// beta_{k+1} = ||r||
|
|
||||||
RealD bk = std::sqrt(norm2(r));
|
|
||||||
beta.push_back(bk);
|
|
||||||
|
|
||||||
Nm = k + 1;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "LanczosBidiagonalization step " << k
|
|
||||||
<< " alpha = " << ak
|
|
||||||
<< " beta = " << bk << std::endl;
|
|
||||||
|
|
||||||
// convergence: residual beta / alpha small enough
|
|
||||||
if (bk / ak < Tolerance) {
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "LanczosBidiagonalization converged at step " << k
|
|
||||||
<< " (beta/alpha = " << bk / ak << ")" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (k == Nmax - 1) break; // no v_{k+2} needed after last step
|
|
||||||
|
|
||||||
// v_{k+1} = r / beta_{k+1}
|
|
||||||
Field vnext(Grid);
|
|
||||||
vnext = (1.0 / bk) * r;
|
|
||||||
V.push_back(vnext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the SVD of the bidiagonal matrix B using Eigen.
|
|
||||||
* Singular values are stored in descending order.
|
|
||||||
*/
|
|
||||||
void computeSVD()
|
|
||||||
{
|
|
||||||
int m = Nm;
|
|
||||||
Eigen::MatrixXd B = Eigen::MatrixXd::Zero(m, m);
|
|
||||||
|
|
||||||
for (int k = 0; k < m; ++k) {
|
|
||||||
B(k, k) = alpha[k];
|
|
||||||
if (k + 1 < m && k < (int)beta.size())
|
|
||||||
B(k, k+1) = beta[k];
|
|
||||||
}
|
|
||||||
|
|
||||||
Eigen::JacobiSVD<Eigen::MatrixXd> svd(B,
|
|
||||||
Eigen::ComputeThinU | Eigen::ComputeThinV);
|
|
||||||
|
|
||||||
singularValues = svd.singularValues(); // already sorted descending
|
|
||||||
leftSVecs = svd.matrixU();
|
|
||||||
rightSVecs = svd.matrixV();
|
|
||||||
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "LanczosBidiagonalization: singular values of B_" << m
|
|
||||||
<< std::endl;
|
|
||||||
for (int k = 0; k < m; ++k)
|
|
||||||
std::cout << GridLogMessage << " sigma[" << k << "] = "
|
|
||||||
<< singularValues(k) << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the k-th approximate left singular vector of A in the full
|
|
||||||
* lattice space. computeSVD() must have been called first.
|
|
||||||
*/
|
|
||||||
Field leftSingularVector(int k)
|
|
||||||
{
|
|
||||||
assert(k < (int)leftSVecs.cols());
|
|
||||||
Field svec(Grid);
|
|
||||||
svec = Zero();
|
|
||||||
for (int j = 0; j < Nm; ++j)
|
|
||||||
svec = svec + leftSVecs(j, k) * U[j];
|
|
||||||
return svec;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the k-th approximate right singular vector of A in the full
|
|
||||||
* lattice space. computeSVD() must have been called first.
|
|
||||||
*/
|
|
||||||
Field rightSingularVector(int k)
|
|
||||||
{
|
|
||||||
assert(k < (int)rightSVecs.cols());
|
|
||||||
Field svec(Grid);
|
|
||||||
svec = Zero();
|
|
||||||
for (int j = 0; j < Nm; ++j)
|
|
||||||
svec = svec + rightSVecs(j, k) * V[j];
|
|
||||||
return svec;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify the bidiagonalization: returns max residual
|
|
||||||
* max_k || A v_k - alpha_k u_k - beta_k u_{k-1} ||
|
|
||||||
*/
|
|
||||||
RealD verify()
|
|
||||||
{
|
|
||||||
Field tmp(Grid);
|
|
||||||
RealD maxres = 0.0;
|
|
||||||
for (int k = 0; k < Nm; ++k) {
|
|
||||||
Linop.Op(V[k], tmp);
|
|
||||||
tmp = tmp - alpha[k] * U[k];
|
|
||||||
if (k > 0 && k-1 < (int)beta.size())
|
|
||||||
tmp = tmp - beta[k-1] * U[k-1];
|
|
||||||
RealD res = std::sqrt(norm2(tmp));
|
|
||||||
if (res > maxres) maxres = res;
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "LanczosBidiagonalization verify step " << k
|
|
||||||
<< " ||A v_k - alpha_k u_k - beta_{k-1} u_{k-1}|| = "
|
|
||||||
<< res << std::endl;
|
|
||||||
}
|
|
||||||
return maxres;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Getters */
|
|
||||||
int getNm() const { return Nm; }
|
|
||||||
const std::vector<Field>& getV() const { return V; }
|
|
||||||
const std::vector<Field>& getU() const { return U; }
|
|
||||||
const std::vector<RealD>& getAlpha() const { return alpha; }
|
|
||||||
const std::vector<RealD>& getBeta() const { return beta; }
|
|
||||||
Eigen::VectorXd getSingularValues() const { return singularValues; }
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
|
||||||
@@ -236,4 +236,5 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
NAMESPACE_END(Grid);
|
NAMESPACE_END(Grid);
|
||||||
|
#undef GCRLogLevel
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -38,13 +38,14 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
NAMESPACE_BEGIN(Grid);
|
NAMESPACE_BEGIN(Grid);
|
||||||
|
|
||||||
#define GCRLogLevel std::cout << GridLogMessage <<std::string(level,'\t')<< " Level "<<level<<" "
|
#define GCRLogLevel std::cout << GridLogMessage <<std::string(level,'\t')<< name<<" "
|
||||||
|
|
||||||
template<class Field>
|
template<class Field>
|
||||||
class PrecGeneralisedConjugateResidualNonHermitian : public LinearFunction<Field> {
|
class PrecGeneralisedConjugateResidualNonHermitian : public LinearFunction<Field> {
|
||||||
public:
|
public:
|
||||||
using LinearFunction<Field>::operator();
|
using LinearFunction<Field>::operator();
|
||||||
RealD Tolerance;
|
RealD Tolerance;
|
||||||
|
RealD SSQ;
|
||||||
Integer MaxIterations;
|
Integer MaxIterations;
|
||||||
int verbose;
|
int verbose;
|
||||||
int mmax;
|
int mmax;
|
||||||
@@ -54,11 +55,18 @@ public:
|
|||||||
GridStopWatch PrecTimer;
|
GridStopWatch PrecTimer;
|
||||||
GridStopWatch MatTimer;
|
GridStopWatch MatTimer;
|
||||||
GridStopWatch LinalgTimer;
|
GridStopWatch LinalgTimer;
|
||||||
|
std::string name;
|
||||||
|
int ZeroGuess = 0; // caller contract: guess is always zero => first-cycle r0 = src, skip the apply
|
||||||
|
int FirstCycle = 0;
|
||||||
|
|
||||||
LinearFunction<Field> &Preconditioner;
|
LinearFunction<Field> &Preconditioner;
|
||||||
LinearOperatorBase<Field> &Linop;
|
LinearOperatorBase<Field> &Linop;
|
||||||
|
|
||||||
void Level(int lv) { level=lv; };
|
void Name(std::string _name) { name = _name; };
|
||||||
|
|
||||||
|
void Level(int n) { Name("Level " + std::to_string(n)); level = n; }
|
||||||
|
|
||||||
|
void SetZeroGuess(int z) { ZeroGuess = z; };
|
||||||
|
|
||||||
PrecGeneralisedConjugateResidualNonHermitian(RealD tol,Integer maxit,LinearOperatorBase<Field> &_Linop,LinearFunction<Field> &Prec,int _mmax,int _nstep) :
|
PrecGeneralisedConjugateResidualNonHermitian(RealD tol,Integer maxit,LinearOperatorBase<Field> &_Linop,LinearFunction<Field> &Prec,int _mmax,int _nstep) :
|
||||||
Tolerance(tol),
|
Tolerance(tol),
|
||||||
@@ -66,28 +74,18 @@ public:
|
|||||||
Linop(_Linop),
|
Linop(_Linop),
|
||||||
Preconditioner(Prec),
|
Preconditioner(Prec),
|
||||||
mmax(_mmax),
|
mmax(_mmax),
|
||||||
nstep(_nstep) // what is nstep vs mmax? one is the number of inner iterations
|
nstep(_nstep)
|
||||||
{
|
{
|
||||||
level=1;
|
Level(1);
|
||||||
verbose=1;
|
verbose=1;
|
||||||
};
|
};
|
||||||
|
|
||||||
// virtual method stubs for updating GCR polynomial
|
|
||||||
virtual void LogBegin(void){
|
|
||||||
std::cout << "GCR::LogBegin() "<<std::endl;
|
|
||||||
};
|
|
||||||
virtual void LogIteration(int k, ComplexD a, std::vector<ComplexD> betas){
|
|
||||||
std::cout << "GCR::LogIteration() "<<std::endl;
|
|
||||||
};
|
|
||||||
virtual void LogComplete(std::vector<ComplexD>& alphas, std::vector<std::vector<ComplexD>>& betas) {
|
|
||||||
std::cout << "GCR::LogComplete() "<<std::endl;
|
|
||||||
};
|
|
||||||
|
|
||||||
void operator() (const Field &src, Field &psi){
|
void operator() (const Field &src, Field &psi){
|
||||||
|
|
||||||
// psi=Zero();
|
// psi=Zero();
|
||||||
RealD cp, ssq,rsq;
|
RealD cp, ssq,rsq;
|
||||||
ssq=norm2(src);
|
ssq=norm2(src);
|
||||||
|
SSQ=ssq;
|
||||||
rsq=Tolerance*Tolerance*ssq;
|
rsq=Tolerance*Tolerance*ssq;
|
||||||
|
|
||||||
Field r(src.Grid());
|
Field r(src.Grid());
|
||||||
@@ -100,13 +98,15 @@ public:
|
|||||||
SolverTimer.Start();
|
SolverTimer.Start();
|
||||||
|
|
||||||
steps=0;
|
steps=0;
|
||||||
|
FirstCycle=1;
|
||||||
for(int k=0;k<MaxIterations;k++){
|
for(int k=0;k<MaxIterations;k++){
|
||||||
|
|
||||||
cp=GCRnStep(src,psi,rsq);
|
cp=GCRnStep(src,psi,rsq);
|
||||||
|
|
||||||
GCRLogLevel <<"PGCR("<<mmax<<","<<nstep<<") "<< steps <<" steps cp = "<<cp<<" target "<<rsq <<std::endl;
|
GCRLogLevel <<"PGCR("<<mmax<<","<<nstep<<") "<< steps <<" steps cp = "<<sqrt(cp/ssq)<<" target "<<sqrt(rsq/ssq) <<std::endl;
|
||||||
|
|
||||||
if(cp<rsq) {
|
if(cp<rsq) {
|
||||||
|
|
||||||
SolverTimer.Stop();
|
SolverTimer.Stop();
|
||||||
|
|
||||||
Linop.Op(psi,r);
|
Linop.Op(psi,r);
|
||||||
@@ -145,29 +145,31 @@ public:
|
|||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
// history for flexible orthog
|
// history for flexible orthog
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
std::vector<Field> q(mmax,grid); // q = Ap
|
std::vector<Field> q(mmax,grid);
|
||||||
std::vector<Field> p(mmax,grid); // store mmax conjugate momenta
|
std::vector<Field> p(mmax,grid);
|
||||||
std::vector<RealD> qq(mmax); // qq = (Ap)^2 = <p|A^\dagger A |p> (denom of \alpha)
|
std::vector<RealD> qq(mmax);
|
||||||
|
|
||||||
GCRLogLevel<< "PGCR nStep("<<nstep<<")"<<std::endl;
|
GCRLogLevel<< "PGCR nStep("<<nstep<<")"<<std::endl;
|
||||||
|
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
// initial guess x0 is taken as nonzero.
|
// r0 = src - A x0. ZeroGuess: on the first cycle x0==0 by caller
|
||||||
// r0=src-A x0 = src
|
// contract (enforced here), so r0 = src exactly; skip the apply.
|
||||||
|
// Restart cycles (psi!=0) always do the full computation.
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
|
if (ZeroGuess && FirstCycle) {
|
||||||
|
psi = Zero();
|
||||||
|
LinalgTimer.Start();
|
||||||
|
r = src;
|
||||||
|
LinalgTimer.Stop();
|
||||||
|
} else {
|
||||||
MatTimer.Start();
|
MatTimer.Start();
|
||||||
Linop.Op(psi,Az);
|
Linop.Op(psi,Az);
|
||||||
// zAz = innerProduct(Az,psi);
|
|
||||||
zAAz= norm2(Az);
|
|
||||||
MatTimer.Stop();
|
MatTimer.Stop();
|
||||||
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
LinalgTimer.Start();
|
||||||
r=src-Az;
|
r=src-Az;
|
||||||
LinalgTimer.Stop();
|
LinalgTimer.Stop();
|
||||||
GCRLogLevel<< "PGCR true residual r = src - A psi "<< norm2(r) <<std::endl;
|
}
|
||||||
|
FirstCycle=0;
|
||||||
this->LogBegin(); // initialize polynomial GCR if needed (TODO think about placement of this)
|
|
||||||
|
|
||||||
/////////////////////
|
/////////////////////
|
||||||
// p = Prec(r)
|
// p = Prec(r)
|
||||||
@@ -191,45 +193,32 @@ public:
|
|||||||
q[0]= Az;
|
q[0]= Az;
|
||||||
qq[0]= zAAz;
|
qq[0]= zAAz;
|
||||||
|
|
||||||
std::cout << "||init p - src||: " << norm2(p[0] - src) << std::endl; // for debugging
|
|
||||||
|
|
||||||
cp =norm2(r);
|
cp =norm2(r);
|
||||||
LinalgTimer.Stop();
|
LinalgTimer.Stop();
|
||||||
|
GCRLogLevel<< "PGCR true residual "<< sqrt(cp/SSQ) <<std::endl;
|
||||||
std::vector<ComplexD> all_alphas;
|
|
||||||
std::vector<std::vector<ComplexD>> all_betas;
|
|
||||||
|
|
||||||
for(int k=0;k<nstep;k++){
|
for(int k=0;k<nstep;k++){
|
||||||
|
|
||||||
steps++;
|
steps++;
|
||||||
|
|
||||||
int kp = k+1;
|
int kp = k+1;
|
||||||
int peri_k = k %mmax; // only store mmax vectors; just roll around if needed
|
int peri_k = k %mmax;
|
||||||
int peri_kp= kp%mmax;
|
int peri_kp= kp%mmax;
|
||||||
|
|
||||||
// std::cout << "peri_kp = " << peri_kp << std::endl;
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
LinalgTimer.Start();
|
||||||
rq= innerProduct(q[peri_k],r); // what if rAr not real?
|
rq= innerProduct(q[peri_k],r); // what if rAr not real?
|
||||||
a = rq/qq[peri_k]; // compute alpha_j
|
a = rq/qq[peri_k];
|
||||||
|
|
||||||
all_alphas.push_back(a);
|
axpy(psi,a,p[peri_k],psi);
|
||||||
|
|
||||||
axpy(psi,a,p[peri_k],psi); // update psi --> psi + \alpha p
|
cp = axpy_norm(r,-a,q[peri_k],r);
|
||||||
|
|
||||||
cp = axpy_norm(r,-a,q[peri_k],r); // update r --> r - \alpha D p. Note q = Dp
|
|
||||||
LinalgTimer.Stop();
|
LinalgTimer.Stop();
|
||||||
|
|
||||||
// LogIterationA(k + 1, a);
|
GCRLogLevel<< "PGCR step["<<steps<<"] resid " << sqrt(cp/SSQ)<<std::endl;
|
||||||
|
|
||||||
GCRLogLevel<< "GCR step["<<steps<<"] resid " << cp << " target " <<rsq<<std::endl;
|
|
||||||
|
|
||||||
// moving this to end of loop so that it doesn't exit beforehand
|
|
||||||
// TODO if I want to uncomment this, I have to split the LogIteration again and put LogIterationA() beforehand
|
|
||||||
// if((k==nstep-1)||(cp<rsq)){
|
|
||||||
// return cp;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
if((k==nstep-1)||(cp<rsq)){
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
PrecTimer.Start();
|
PrecTimer.Start();
|
||||||
Preconditioner(r,z);// solve Az = r
|
Preconditioner(r,z);// solve Az = r
|
||||||
@@ -246,205 +235,24 @@ public:
|
|||||||
q[peri_kp]=Az;
|
q[peri_kp]=Az;
|
||||||
p[peri_kp]=z;
|
p[peri_kp]=z;
|
||||||
|
|
||||||
// Field Dsrc (grid);
|
|
||||||
// Linop.Op(src, Dsrc);
|
|
||||||
// std::cout << "||q[peri_kp] - D(src)||: " << norm2(q[peri_kp] - Dsrc) << std::endl; // for debugging
|
|
||||||
|
|
||||||
// // delete after testing
|
|
||||||
// std::cout << "Testing Dsq on one for GCR: " << std::endl;
|
|
||||||
// Field myField (grid);
|
|
||||||
// myField = 1.0;
|
|
||||||
// Field out1 (grid); Field out2 (grid);
|
|
||||||
// Linop.HermOp(myField, out1);
|
|
||||||
// Linop.Op(myField, out2);
|
|
||||||
// std::cout << "Dsq.Hermop(ones) has norm " << norm2(out1) << std::endl;
|
|
||||||
// std::cout << "Dsq.Op(ones) has norm " << norm2(out2) << std::endl;
|
|
||||||
|
|
||||||
// basically northog = k+1 if mmax is large
|
|
||||||
int northog = ((kp)>(mmax-1))?(mmax-1):(kp); // if more than mmax done, we orthog all mmax history.
|
int northog = ((kp)>(mmax-1))?(mmax-1):(kp); // if more than mmax done, we orthog all mmax history.
|
||||||
// std::cout << "northog: " << northog << std::endl;
|
|
||||||
std::vector<ComplexD> betas (northog);
|
|
||||||
// std::cout << "peri_kp: " << peri_kp << std::endl;
|
|
||||||
// we iterate backwards counting down from the current k+1 index (peri_kp) because we
|
|
||||||
for(int back=0;back<northog;back++){
|
for(int back=0;back<northog;back++){
|
||||||
|
|
||||||
int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0);
|
int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0);
|
||||||
|
|
||||||
// b=-real(innerProduct(q[peri_back],Az))/qq[peri_back];
|
b=-real(innerProduct(q[peri_back],Az))/qq[peri_back];
|
||||||
b=-(innerProduct(q[peri_back],Az))/qq[peri_back]; // TODO try complex beta
|
|
||||||
p[peri_kp]=p[peri_kp]+b*p[peri_back];
|
p[peri_kp]=p[peri_kp]+b*p[peri_back];
|
||||||
q[peri_kp]=q[peri_kp]+b*q[peri_back];
|
q[peri_kp]=q[peri_kp]+b*q[peri_back];
|
||||||
|
|
||||||
// LogIterationB(peri_back, b);
|
|
||||||
// betas[back] = b; // may need to change the indexing if I ever do it with restarts
|
|
||||||
// std::cout << "[DEBUG] pushing beta for back = " << back << ", peri_back = " << peri_back << std::endl;
|
|
||||||
|
|
||||||
betas[peri_back] = b; // may need to change the indexing if I ever do it with restarts
|
|
||||||
|
|
||||||
}
|
}
|
||||||
qq[peri_kp]=norm2(q[peri_kp]); // could use axpy_norm
|
qq[peri_kp]=norm2(q[peri_kp]); // could use axpy_norm
|
||||||
LinalgTimer.Stop();
|
LinalgTimer.Stop();
|
||||||
|
|
||||||
// log iteration and update GCR polynomial if necessary.
|
|
||||||
all_betas.push_back(betas);
|
|
||||||
LogIteration(k + 1, a, betas);
|
|
||||||
|
|
||||||
// finish if necessary
|
|
||||||
if((k==nstep-1)||(cp<rsq)){
|
|
||||||
std::cout << "All alphas: " << std::endl << all_alphas << std::endl;
|
|
||||||
std::cout << "All betas: " << std::endl << all_betas << std::endl;
|
|
||||||
LogComplete(all_alphas, all_betas);
|
|
||||||
std::cout << "Exiting GCR." << std::endl;
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
GRID_ASSERT(0); // never reached
|
GRID_ASSERT(0); // never reached
|
||||||
return cp;
|
return cp;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class PolynomialFile: Serializable {
|
|
||||||
public:
|
|
||||||
GRID_SERIALIZABLE_CLASS_MEMBERS(PolynomialFile,
|
|
||||||
std::vector<std::vector<std::complex<double>>>, data,
|
|
||||||
std::vector<std::vector<std::complex<double>>>, betas,
|
|
||||||
std::vector<std::complex<double>>, alphas
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Optionally record the GCR polynomial. [PO]: TODO
|
|
||||||
template <class Field>
|
|
||||||
class PGCRPolynomial : public PrecGeneralisedConjugateResidualNonHermitian<Field> {
|
|
||||||
public:
|
|
||||||
std::vector<ComplexD> ak;
|
|
||||||
std::vector<std::vector<ComplexD>> bk;
|
|
||||||
// std::vector<ComplexD> poly_p;
|
|
||||||
std::vector<std::vector<ComplexD>> poly_p;
|
|
||||||
std::vector<ComplexD> poly_Ap; // polynomial in Ap_j (only store it for last p)
|
|
||||||
std::vector<ComplexD> poly_r;
|
|
||||||
std::vector<ComplexD> polynomial;
|
|
||||||
|
|
||||||
PolynomialFile& PF;
|
|
||||||
|
|
||||||
public:
|
|
||||||
PGCRPolynomial(RealD tol, Integer maxit,LinearOperatorBase<Field> &_Linop, LinearFunction<Field> &Prec, int _mmax, int _nstep, PolynomialFile& _PF)
|
|
||||||
: PrecGeneralisedConjugateResidualNonHermitian<Field>(tol, maxit, _Linop, Prec, _mmax, _nstep), PF(_PF)
|
|
||||||
{};
|
|
||||||
|
|
||||||
// think this applies the polynomial in A = Linop to a field src. The coeffs are
|
|
||||||
// stored in the vector `polynomial`.
|
|
||||||
void PolyOp(const Field &src, Field &psi)
|
|
||||||
{
|
|
||||||
Field tmp(src.Grid());
|
|
||||||
Field AtoN(src.Grid());
|
|
||||||
AtoN = src;
|
|
||||||
psi=AtoN*polynomial[0];
|
|
||||||
for(int n=1;n<polynomial.size();n++){
|
|
||||||
tmp = AtoN;
|
|
||||||
this->Linop.Op(tmp,AtoN); // iterate A^n
|
|
||||||
psi = psi + polynomial[n]*AtoN; // psi += poly_n A^n src
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// [PO TODO] debug this
|
|
||||||
void PGCRsequence(const Field &src, Field &x)
|
|
||||||
{
|
|
||||||
Field Ap(src.Grid());
|
|
||||||
Field r(src.Grid());
|
|
||||||
// Field p(src.Grid());
|
|
||||||
// p=src;
|
|
||||||
std::vector<Field> p;
|
|
||||||
p.push_back(src);
|
|
||||||
r=src;
|
|
||||||
x=Zero();
|
|
||||||
x.Checkerboard()=src.Checkerboard();
|
|
||||||
for(int k=0;k<ak.size();k++){
|
|
||||||
x = x + ak[k]*p[k];
|
|
||||||
this->Linop.Op(p[k], Ap);
|
|
||||||
r = r - ak[k] * Ap;
|
|
||||||
// p[k] = r;
|
|
||||||
p.push_back(r);
|
|
||||||
for (int i = 0; i < k; i++) { // [PO TODO] check indices
|
|
||||||
p[k+1] += bk[i, k+1] * p[i];
|
|
||||||
}
|
|
||||||
// p = r + bk[k] * p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Solve(const Field &src, Field &psi)
|
|
||||||
{
|
|
||||||
psi=Zero();
|
|
||||||
this->operator()(src, psi);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void LogBegin(void)
|
|
||||||
{
|
|
||||||
std::cout << "PGCR::LogBegin() "<<std::endl;
|
|
||||||
ak.resize(0);
|
|
||||||
bk.resize(0);
|
|
||||||
polynomial.resize(0);
|
|
||||||
poly_Ap.push_back(0.0); // start with (0.0); during first iteration should change to (0.0, 1.0)
|
|
||||||
std::vector<ComplexD> p0_tmp;
|
|
||||||
p0_tmp.push_back(1.0);
|
|
||||||
poly_p.push_back(p0_tmp);
|
|
||||||
poly_r.push_back(1.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Updates vector psi and r and initializes vector p[k+1]
|
|
||||||
virtual void LogIteration(int k, ComplexD a, std::vector<ComplexD> betas){
|
|
||||||
std::cout << "PGCR::LogIteration(k = " << k << ")" << std::endl;
|
|
||||||
ak.push_back(a);
|
|
||||||
bk.push_back(betas);
|
|
||||||
|
|
||||||
// update Ap by pushing p[k] to the right
|
|
||||||
poly_Ap.push_back(0.0); // need to pad the end with an element
|
|
||||||
poly_Ap[0] = 0.0; // technically this should be unnecessary, as the first component is never set
|
|
||||||
for(int i = 0; i < k; i++){
|
|
||||||
poly_Ap[i+1]=poly_p[k-1][i]; // A\vec{p} = (0, \vec{p}) bc A shifts components of p to the right
|
|
||||||
}
|
|
||||||
|
|
||||||
// update psi_{k+1} --> psi_k + a_k p_k
|
|
||||||
polynomial.push_back(0.0);
|
|
||||||
for(int i = 0; i < k; i++) {
|
|
||||||
polynomial[i] += a * poly_p[k-1][i];
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::vector<std::complex<double>> poly_stdcmplx(polynomial.begin(), polynomial.end());
|
|
||||||
PF.data.push_back(poly_stdcmplx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// r_{k+1} --> r_k - a_k A p_k
|
|
||||||
// p_{k+1} --> r_k + \sum_{i=0}^k \beta_{ik} p_i, input betas = (\beta_{ik})_i
|
|
||||||
poly_r.push_back(0.0); // should be of size k+1 if we start with k = 1
|
|
||||||
std::vector<ComplexD> p_next (k + 1, ComplexD(0.0)); // p_{k+1} = same size as r_{k+1}
|
|
||||||
for(int i = 0; i < k + 1; i++){
|
|
||||||
poly_r[i] = poly_r[i] - a * poly_Ap[i]; // update r_{k+1} --> r_k - \alpha_k A p_k
|
|
||||||
p_next[i] = poly_r[i]; // init new vector as r_{k+1}
|
|
||||||
}
|
|
||||||
|
|
||||||
// p_{k+1} --> p_{k+1} + \sum_i \beta_{ij} p_i
|
|
||||||
int nbeta = betas.size();
|
|
||||||
std::cout << "Betas: " << betas << std::endl;
|
|
||||||
for (int j = 0; j < nbeta; j++) {
|
|
||||||
for (int i = 0; i < j+1; i++) {
|
|
||||||
p_next[i] += betas[j] * poly_p[j][i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
poly_p.push_back(p_next); // add p_{k+1} to the list of p's
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void LogComplete(std::vector<ComplexD>& alphas, std::vector<std::vector<ComplexD>>& betas) {
|
|
||||||
/** Logs all alphas and betas to complete the iterations. */
|
|
||||||
std::cout << "PGCR::LogComplete() "<<std::endl;
|
|
||||||
for (int i = 0; i < alphas.size(); i++) {
|
|
||||||
PF.alphas.push_back(std::complex<double>(alphas[i].real(), alphas[i].imag()));
|
|
||||||
std::vector<std::complex<double>> beta_stdcmplx(betas[i].begin(), betas[i].end());
|
|
||||||
PF.betas.push_back(beta_stdcmplx);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
NAMESPACE_END(Grid);
|
||||||
|
|
||||||
|
#undef GCRLogLevel
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,623 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./Grid/algorithms/iterative/RestartedLanczosBidiagonalization.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Chulwoo Jung <chulwoo@bnl.gov>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
|
||||||
*************************************************************************************/
|
|
||||||
/* END LEGAL */
|
|
||||||
#ifndef GRID_RESTARTED_LANCZOS_BIDIAGONALIZATION_H
|
|
||||||
#define GRID_RESTARTED_LANCZOS_BIDIAGONALIZATION_H
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implicitly Restarted Lanczos Bidiagonalization (IRLBA)
|
|
||||||
*
|
|
||||||
* Computes the p largest (or p smallest) singular triplets of a linear
|
|
||||||
* operator A using the Golub-Kahan-Lanczos bidiagonalization with implicit
|
|
||||||
* restart via thick-restart / QR shifts.
|
|
||||||
*
|
|
||||||
* Algorithm (Baglama & Reichel, SIAM J. Sci. Comput. 27(1):19-42, 2005):
|
|
||||||
*
|
|
||||||
* Outer loop:
|
|
||||||
* 1. Extend the p-step (or seed) bidiagonalization to k steps:
|
|
||||||
* A V_k = U_k B_k
|
|
||||||
* A^dag U_k = V_k B_k^T + beta_{k+1} v_{k+1} e_k^T
|
|
||||||
* 2. Compute SVD: B_k = X Sigma Y^T
|
|
||||||
* 3. Check convergence of the p desired singular values via
|
|
||||||
* |beta_{k+1} * y_{k,i}| < tol * sigma_i
|
|
||||||
* where y_{k,i} is the last component of the i-th right singular vector.
|
|
||||||
* 4. Apply k-p implicit QR shifts to implicitly compress the basis
|
|
||||||
* to p steps (Sorensen-Lehoucq thick restart):
|
|
||||||
* B_p^+ = X_p^T B_k Y_p (upper bidiagonal, p x p)
|
|
||||||
* and update the lattice vectors:
|
|
||||||
* V_p^+ = V_k Y_p
|
|
||||||
* U_p^+ = U_k X_p
|
|
||||||
* The new residual coupling is
|
|
||||||
* beta_p^+ v_{p+1}^+ = beta_{k+1} v_{k+1} * (e_k^T Y_p)_p
|
|
||||||
* + B_k(p,p+1) * (orthogonal tail from QR)
|
|
||||||
* 5. Go to step 1.
|
|
||||||
*
|
|
||||||
* Template parameter
|
|
||||||
* ------------------
|
|
||||||
* Field : lattice field type (must support Grid algebra operations)
|
|
||||||
*
|
|
||||||
* Usage
|
|
||||||
* -----
|
|
||||||
* RestartedLanczosBidiagonalization<Field> irlba(Linop, grid, p, k, tol, maxIter);
|
|
||||||
* irlba.run(src);
|
|
||||||
* // Results available via getters.
|
|
||||||
*/
|
|
||||||
template <class Field>
|
|
||||||
class RestartedLanczosBidiagonalization {
|
|
||||||
|
|
||||||
public:
|
|
||||||
LinearOperatorBase<Field> &Linop;
|
|
||||||
GridBase *Grid;
|
|
||||||
|
|
||||||
int Nk; // number of desired singular triplets
|
|
||||||
int Nm; // Lanczos basis size (Nm > Nk)
|
|
||||||
RealD Tolerance;
|
|
||||||
int MaxIter;
|
|
||||||
bool largest; // if true, target largest singular values; otherwise smallest
|
|
||||||
|
|
||||||
// Converged singular triplets (filled after run())
|
|
||||||
std::vector<RealD> singularValues; // sigma_0 >= sigma_1 >= ...
|
|
||||||
std::vector<Field> leftVectors; // approximate left singular vectors
|
|
||||||
std::vector<Field> rightVectors; // approximate right singular vectors
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Working bases (size up to Nm+1)
|
|
||||||
std::vector<Field> V; // right Lanczos vectors
|
|
||||||
std::vector<Field> U; // left Lanczos vectors
|
|
||||||
std::vector<RealD> alpha;
|
|
||||||
std::vector<RealD> beta;
|
|
||||||
|
|
||||||
// After a thick restart, the column at index restart_col of U^dag A V
|
|
||||||
// has extra non-zero entries (rows 0..restart_col-2) beyond what the
|
|
||||||
// upper bidiagonal captures. fvec[j] = <U[j] | A V[restart_col]> for
|
|
||||||
// j = 0..restart_col-1. (fvec[restart_col-1] == beta[restart_col-1].)
|
|
||||||
// reset_col == -1 means no restart has occurred yet (pure bidiagonal).
|
|
||||||
std::vector<RealD> fvec;
|
|
||||||
int restart_col;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
RestartedLanczosBidiagonalization(LinearOperatorBase<Field> &_Linop,
|
|
||||||
GridBase *_Grid,
|
|
||||||
int _Nk, int _Nm,
|
|
||||||
RealD _tol = 1.0e-8,
|
|
||||||
int _maxIt = 300,
|
|
||||||
bool _largest = true)
|
|
||||||
: Linop(_Linop), Grid(_Grid),
|
|
||||||
Nk(_Nk), Nm(_Nm),
|
|
||||||
Tolerance(_tol), MaxIter(_maxIt),
|
|
||||||
largest(_largest)
|
|
||||||
{
|
|
||||||
assert(Nm > Nk);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run IRLBA starting from src.
|
|
||||||
* On exit, singularValues, leftVectors, rightVectors are filled with
|
|
||||||
* the Nk converged singular triplets.
|
|
||||||
*/
|
|
||||||
void run(const Field &src)
|
|
||||||
{
|
|
||||||
assert(norm2(src) > 0.0);
|
|
||||||
|
|
||||||
singularValues.clear();
|
|
||||||
leftVectors.clear();
|
|
||||||
rightVectors.clear();
|
|
||||||
|
|
||||||
// Allocate working bases
|
|
||||||
V.clear(); U.clear();
|
|
||||||
alpha.clear(); beta.clear();
|
|
||||||
fvec.clear(); restart_col = -1;
|
|
||||||
V.reserve(Nm + 1);
|
|
||||||
U.reserve(Nm);
|
|
||||||
|
|
||||||
// Seed: v_0 = src / ||src||
|
|
||||||
Field vtmp(Grid);
|
|
||||||
vtmp = src;
|
|
||||||
RealD nrm = std::sqrt(norm2(vtmp));
|
|
||||||
vtmp = (1.0 / nrm) * vtmp;
|
|
||||||
V.push_back(vtmp);
|
|
||||||
|
|
||||||
int pStart = 0; // current basis size at start of extension
|
|
||||||
RealD betaRestart = 0.0; // coupling from previous restart
|
|
||||||
|
|
||||||
for (int iter = 0; iter < MaxIter; ++iter) {
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
|
||||||
// Step 1: extend from pStart steps to Nm steps
|
|
||||||
// ----------------------------------------------------------------
|
|
||||||
extendBasis(pStart, Nm, betaRestart);
|
|
||||||
verify();
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
|
||||||
// Step 2: SVD of the Nm x Nm B matrix.
|
|
||||||
// iter=0 (pStart==0): B is exactly bidiagonal — use buildBidiagonal.
|
|
||||||
// iter>0 (pStart==Nk): after a thick restart, column restart_col of
|
|
||||||
// U^dag A V has extra off-diagonal entries captured by fvec; use
|
|
||||||
// buildFullB so the Ritz values and restart vectors are computed from
|
|
||||||
// the exact projected matrix A V = U B_full.
|
|
||||||
// ----------------------------------------------------------------
|
|
||||||
Eigen::MatrixXd B = (pStart == 0) ? buildBidiagonal(Nm) : buildFullB(Nm);
|
|
||||||
Eigen::JacobiSVD<Eigen::MatrixXd> svd(B,
|
|
||||||
Eigen::ComputeThinU | Eigen::ComputeThinV);
|
|
||||||
|
|
||||||
Eigen::VectorXd sigma = svd.singularValues(); // descending
|
|
||||||
Eigen::MatrixXd X = svd.matrixU(); // Nm x Nm left SVecs of B
|
|
||||||
Eigen::MatrixXd Y = svd.matrixV(); // Nm x Nm right SVecs of B
|
|
||||||
|
|
||||||
// If targeting smallest, reorder so desired ones come first
|
|
||||||
Eigen::VectorXi order = sortOrder(sigma);
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
|
||||||
// Step 3: check convergence of the Nk desired singular values
|
|
||||||
// ----------------------------------------------------------------
|
|
||||||
RealD betaK = beta.back(); // beta_{k+1}
|
|
||||||
// In our convention A V = U B (exact), the residual is in the A^dag
|
|
||||||
// direction: A^dag u_j - sigma_j v_j = betaK * X[Nm-1,j] * V[Nm].
|
|
||||||
// Convergence criterion: |betaK * X[Nm-1, idx]| < tol * sigma_idx.
|
|
||||||
int nconv = 0;
|
|
||||||
for (int i = 0; i < Nk; ++i) {
|
|
||||||
int idx = order(i);
|
|
||||||
RealD res = std::abs(betaK * X(Nm - 1, idx));
|
|
||||||
RealD thr = Tolerance * std::max(sigma(idx), 1.0e-14);
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "IRLBA iter " << iter
|
|
||||||
<< " sigma[" << i << "] = " << sigma(idx)
|
|
||||||
<< " res = " << res
|
|
||||||
<< " thr = " << thr << std::endl;
|
|
||||||
if (res < thr) ++nconv;
|
|
||||||
else break; // residuals not strictly ordered but break is conservative
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nconv >= Nk) {
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "IRLBA converged: " << nconv << " singular values after "
|
|
||||||
<< iter + 1 << " iterations." << std::endl;
|
|
||||||
// Collect converged triplets
|
|
||||||
extractTriplets(Nm, sigma, X, Y, order, Nk);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
|
||||||
// Step 4: implicit restart — compress to Nk steps
|
|
||||||
// ----------------------------------------------------------------
|
|
||||||
implicitRestart(Nm, Nk, sigma, X, Y, order, betaK, betaRestart);
|
|
||||||
verify();
|
|
||||||
|
|
||||||
// Lucky breakdown: exact invariant subspace found; convergence is exact.
|
|
||||||
// B_p^+ = diag(alpha[0..Nk-1]); extract directly from restart basis.
|
|
||||||
if (betaRestart < 1.0e-14) {
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "IRLBA: lucky breakdown after restart (betaRestart = 0)."
|
|
||||||
<< " Extracting " << Nk << " exact Ritz triplets." << std::endl;
|
|
||||||
// Re-run SVD on the p-step diagonal B^+ to get sorted Ritz triplets.
|
|
||||||
Eigen::MatrixXd Bp = buildBidiagonal(Nk);
|
|
||||||
Eigen::JacobiSVD<Eigen::MatrixXd> svdp(Bp,
|
|
||||||
Eigen::ComputeThinU | Eigen::ComputeThinV);
|
|
||||||
Eigen::VectorXi ordp = sortOrder(svdp.singularValues());
|
|
||||||
extractTriplets(Nk, svdp.singularValues(), svdp.matrixU(),
|
|
||||||
svdp.matrixV(), ordp, Nk);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pStart = Nk;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "IRLBA: did not converge in " << MaxIter
|
|
||||||
<< " iterations. Returning best approximations." << std::endl;
|
|
||||||
|
|
||||||
// Return best available approximations
|
|
||||||
Eigen::MatrixXd B = buildFullB((int)alpha.size());
|
|
||||||
Eigen::JacobiSVD<Eigen::MatrixXd> svd(B,
|
|
||||||
Eigen::ComputeThinU | Eigen::ComputeThinV);
|
|
||||||
Eigen::VectorXd sigma = svd.singularValues();
|
|
||||||
Eigen::MatrixXd X = svd.matrixU();
|
|
||||||
Eigen::MatrixXd Y = svd.matrixV();
|
|
||||||
Eigen::VectorXi order = sortOrder(sigma);
|
|
||||||
int nout = std::min(Nk, (int)alpha.size());
|
|
||||||
extractTriplets((int)alpha.size(), sigma, X, Y, order, nout);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Getters */
|
|
||||||
int getNk() const { return (int)singularValues.size(); }
|
|
||||||
const std::vector<RealD>& getSingularValues() const { return singularValues; }
|
|
||||||
const std::vector<Field>& getLeftVectors() const { return leftVectors; }
|
|
||||||
const std::vector<Field>& getRightVectors() const { return rightVectors; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print B_k and U^dag A V to verify the bidiagonalization relation
|
|
||||||
* A V_m = U_m B_m (exact in our GK convention)
|
|
||||||
* On the first call (pStart=0), max|B - U^dag A V| should be ~machine epsilon.
|
|
||||||
* After a restart and extension, the column p of U^dag A V deviates from B
|
|
||||||
* by O(betaK): this is expected because the thick restart breaks the Krylov
|
|
||||||
* structure at column p, introducing off-diagonal terms proportional to betaK.
|
|
||||||
* These terms vanish as betaK -> 0 (convergence), so the algorithm is correct.
|
|
||||||
*/
|
|
||||||
void verify()
|
|
||||||
{
|
|
||||||
int m = (int)alpha.size();
|
|
||||||
int nU = (int)U.size();
|
|
||||||
int nV = (int)V.size();
|
|
||||||
if (m == 0) { std::cout << GridLogMessage << "IRLBA verify: empty basis" << std::endl; return; }
|
|
||||||
|
|
||||||
// Build reference matrix Bref (nU x nV):
|
|
||||||
// Columns 0..m-1 : buildFullB(m) (bidiagonal + fvec column at restart_col)
|
|
||||||
// Column m : residual column, two cases:
|
|
||||||
// (a) restart_col == m (right after implicitRestart, before extendBasis):
|
|
||||||
// V[m] = sgn*V_old[Nm], so <U[i]|A|V[m]> = fvec[i] for all i
|
|
||||||
// (b) otherwise (pure GK or after extendBasis):
|
|
||||||
// only entry (m-1, m) = beta[m-1] (GK recurrence residual)
|
|
||||||
Eigen::MatrixXd Bref = Eigen::MatrixXd::Zero(nU, nV);
|
|
||||||
{
|
|
||||||
Eigen::MatrixXd Bfull = buildFullB(m);
|
|
||||||
int cols = std::min(m, nV);
|
|
||||||
Bref.block(0, 0, m, cols) = Bfull.block(0, 0, m, cols);
|
|
||||||
}
|
|
||||||
if (nV > m && m > 0) {
|
|
||||||
if (restart_col == m && (int)fvec.size() == m) {
|
|
||||||
// Case (a): right after implicitRestart
|
|
||||||
for (int i = 0; i < m; ++i) Bref(i, m) = fvec[i];
|
|
||||||
} else if ((int)beta.size() >= m) {
|
|
||||||
// Case (b): standard GK residual column
|
|
||||||
Bref(m - 1, m) = beta[m - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute M[i,j] = <U[i] | A | V[j]>
|
|
||||||
Eigen::MatrixXd M = Eigen::MatrixXd::Zero(nU, nV);
|
|
||||||
Field Avj(Grid);
|
|
||||||
for (int j = 0; j < nV; ++j) {
|
|
||||||
Linop.Op(V[j], Avj);
|
|
||||||
for (int i = 0; i < nU; ++i) {
|
|
||||||
ComplexD ip = innerProduct(U[i], Avj);
|
|
||||||
M(i, j) = ip.real();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print Bref
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "IRLBA verify: Bref (" << nU << "x" << nV << "):" << std::endl;
|
|
||||||
for (int i = 0; i < nU; ++i) {
|
|
||||||
std::cout << GridLogMessage << " row " << i << ": ";
|
|
||||||
for (int j = 0; j < nV; ++j) std::cout << Bref(i,j) << " ";
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print U^dag A V
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "IRLBA verify: U^dag A V (" << nU << "x" << nV << "):" << std::endl;
|
|
||||||
for (int i = 0; i < nU; ++i) {
|
|
||||||
std::cout << GridLogMessage << " row " << i << ": ";
|
|
||||||
for (int j = 0; j < nV; ++j) std::cout << M(i,j) << " ";
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Max deviation over the full nU x nV matrix
|
|
||||||
RealD maxdev = (Bref - M).cwiseAbs().maxCoeff();
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "IRLBA verify: max|Bref - U^dag A V| = " << maxdev << std::endl;
|
|
||||||
|
|
||||||
// Beta
|
|
||||||
std::cout << GridLogMessage << "IRLBA verify: beta[0.." << (int)beta.size()-1 << "] = ";
|
|
||||||
for (auto b : beta) std::cout << b << " ";
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// Build the m x m upper-bidiagonal matrix from alpha[0..m-1], beta[0..m-2]
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
Eigen::MatrixXd buildBidiagonal(int m) const
|
|
||||||
{
|
|
||||||
Eigen::MatrixXd B = Eigen::MatrixXd::Zero(m, m);
|
|
||||||
for (int k = 0; k < m; ++k) {
|
|
||||||
B(k, k) = alpha[k];
|
|
||||||
if (k + 1 < m && k < (int)beta.size())
|
|
||||||
B(k, k + 1) = beta[k];
|
|
||||||
}
|
|
||||||
return B;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// Build the full m x m B matrix, including the non-bidiagonal column
|
|
||||||
// at restart_col that arises after a thick restart.
|
|
||||||
//
|
|
||||||
// After restart, A V[restart_col] has projections onto all U[0..restart_col-1]
|
|
||||||
// (not just U[restart_col-1]). These are stored in fvec[0..restart_col-1]
|
|
||||||
// and make column restart_col of U^dag A V non-bidiagonal.
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
Eigen::MatrixXd buildFullB(int m) const
|
|
||||||
{
|
|
||||||
Eigen::MatrixXd B = buildBidiagonal(m);
|
|
||||||
if (restart_col >= 0 && restart_col < m && (int)fvec.size() > 0) {
|
|
||||||
for (int j = 0; j < restart_col && j < (int)fvec.size(); ++j){
|
|
||||||
B(j, restart_col) = fvec[j];
|
|
||||||
std::cout << GridLogMessage << "buildFullB: B " <<j<<" "<<restart_col<<B(j, restart_col);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return B;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// Return a permutation vector that puts the desired Nk singular values
|
|
||||||
// first (largest first if largest==true, smallest first otherwise).
|
|
||||||
// Eigen's JacobiSVD already returns sigma in descending order, so for
|
|
||||||
// largest we just return 0,1,...,m-1; for smallest we reverse.
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
Eigen::VectorXi sortOrder(const Eigen::VectorXd &sigma) const
|
|
||||||
{
|
|
||||||
int m = (int)sigma.size();
|
|
||||||
Eigen::VectorXi ord(m);
|
|
||||||
if (largest) {
|
|
||||||
for (int i = 0; i < m; ++i) ord(i) = i;
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < m; ++i) ord(i) = m - 1 - i;
|
|
||||||
}
|
|
||||||
return ord;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// Extend the Lanczos bidiagonalization from pStart to kEnd steps.
|
|
||||||
// On first call pStart==0 (V[0] already set).
|
|
||||||
// On restart calls V[0..pStart], U[0..pStart-1], alpha[0..pStart-1],
|
|
||||||
// beta[0..pStart-1] are already set; betaRestart is the coupling
|
|
||||||
// beta_{pStart} that drives the first new U step.
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
void extendBasis(int pStart, int kEnd, RealD betaRestart)
|
|
||||||
{
|
|
||||||
// Truncate containers to pStart (Lattice has no default constructor)
|
|
||||||
if ((int)V.size() > pStart + 1) V.erase(V.begin() + pStart + 1, V.end());
|
|
||||||
if ((int)U.size() > pStart) U.erase(U.begin() + pStart, U.end());
|
|
||||||
alpha.resize(pStart);
|
|
||||||
beta.resize(pStart);
|
|
||||||
|
|
||||||
Field p(Grid), r(Grid);
|
|
||||||
|
|
||||||
for (int k = pStart; k < kEnd; ++k) {
|
|
||||||
|
|
||||||
// p = A v_k
|
|
||||||
Linop.Op(V[k], p);
|
|
||||||
|
|
||||||
// Remove previous left vector coupling
|
|
||||||
if (k > 0) {
|
|
||||||
p = p - beta[k - 1] * U[k - 1];
|
|
||||||
}
|
|
||||||
// On the first step after a restart, beta[pStart-1] was already set;
|
|
||||||
// but V[pStart] was already constructed including the beta correction,
|
|
||||||
// so no extra subtraction needed here beyond the standard recurrence.
|
|
||||||
|
|
||||||
// Reorthogonalize p against U, then alpha_k = ||p||, u_k = p/alpha_k
|
|
||||||
reorthogonalize(p, U);
|
|
||||||
RealD ak = std::sqrt(norm2(p));
|
|
||||||
if (ak < 1.0e-14) {
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "IRLBA extendBasis: lucky breakdown at step " << k
|
|
||||||
<< " (alpha = " << ak << ")" << std::endl;
|
|
||||||
alpha.push_back(ak);
|
|
||||||
Field zero(Grid); zero = Zero();
|
|
||||||
U.push_back(zero);
|
|
||||||
beta.push_back(0.0);
|
|
||||||
V.push_back(zero);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
alpha.push_back(ak);
|
|
||||||
|
|
||||||
Field u(Grid);
|
|
||||||
u = (1.0 / ak) * p;
|
|
||||||
U.push_back(u);
|
|
||||||
|
|
||||||
// r = A^dag u_k - alpha_k v_k, reorthogonalize, then beta_{k+1} = ||r||
|
|
||||||
Linop.AdjOp(U[k], r);
|
|
||||||
r = r - ak * V[k];
|
|
||||||
reorthogonalize(r, V);
|
|
||||||
|
|
||||||
RealD bk = std::sqrt(norm2(r));
|
|
||||||
beta.push_back(bk);
|
|
||||||
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "IRLBA extend step " << k
|
|
||||||
<< " alpha = " << ak
|
|
||||||
<< " beta = " << bk << std::endl;
|
|
||||||
|
|
||||||
// Always push v_{k+1} (needed as residual direction for restart)
|
|
||||||
if (bk < 1.0e-14) {
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "IRLBA extendBasis: lucky breakdown (beta = 0) at step "
|
|
||||||
<< k << std::endl;
|
|
||||||
Field zero(Grid); zero = Zero();
|
|
||||||
V.push_back(zero);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Field vnext(Grid);
|
|
||||||
vnext = (1.0 / bk) * r;
|
|
||||||
V.push_back(vnext);
|
|
||||||
|
|
||||||
if (k == kEnd - 1) break; // v_{k+1} pushed above; stop here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// Full reorthogonalization of vec against the vectors in basis.
|
|
||||||
// Subtracts projections only — does NOT normalize.
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
void reorthogonalize(Field &vec, const std::vector<Field> &basis)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < (int)basis.size(); ++j) {
|
|
||||||
ComplexD ip = innerProduct(basis[j], vec);
|
|
||||||
vec = vec - ip * basis[j];
|
|
||||||
}
|
|
||||||
// Second pass for numerical stability
|
|
||||||
for (int j = 0; j < (int)basis.size(); ++j) {
|
|
||||||
ComplexD ip = innerProduct(basis[j], vec);
|
|
||||||
vec = vec - ip * basis[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// Implicit restart: given the Nm-step bidiagonalization and its SVD,
|
|
||||||
// compress to Nk steps via implicit QR shifts applied to B_k.
|
|
||||||
//
|
|
||||||
// The "shifts" are the Nm - Nk singular values we want to deflate
|
|
||||||
// (those NOT in the desired set). We apply them as implicit QR steps
|
|
||||||
// to the bidiagonal matrix, then update the lattice bases accordingly.
|
|
||||||
//
|
|
||||||
// After this call:
|
|
||||||
// V[0..Nk], U[0..Nk-1], alpha[0..Nk-1], beta[0..Nk-1] are updated.
|
|
||||||
// betaRestart ← new beta_Nk coupling for the next extension.
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
void implicitRestart(int k, int p,
|
|
||||||
const Eigen::VectorXd &sigma,
|
|
||||||
const Eigen::MatrixXd &X,
|
|
||||||
const Eigen::MatrixXd &Y,
|
|
||||||
const Eigen::VectorXi &order,
|
|
||||||
RealD betaK,
|
|
||||||
RealD &betaRestart)
|
|
||||||
{
|
|
||||||
// Thick restart (Baglama & Reichel, Sec. 2.2):
|
|
||||||
//
|
|
||||||
// Given B_k = X Sigma Y^T, define the new p-step basis by:
|
|
||||||
// V^+_i = V_k * y_{order(i)} (right sing. vec. of B_k)
|
|
||||||
// U^+_i = U_k * x_{order(i)} (left sing. vec. of B_k)
|
|
||||||
//
|
|
||||||
// Then A V^+_i = A V_k y_{order(i)} = U_k B_k y_{order(i)}
|
|
||||||
// = sigma_{order(i)} U_k x_{order(i)} = sigma_{order(i)} U^+_i
|
|
||||||
//
|
|
||||||
// So B_p^+ = diag(sigma_{order(0)}, ..., sigma_{order(p-1)}) — DIAGONAL,
|
|
||||||
// all internal betas are zero.
|
|
||||||
//
|
|
||||||
// The residual coupling comes from A^dag U_k = V_k B_k^T + betaK V[k] e_{k-1}^T:
|
|
||||||
// A^dag U^+_{p-1} - sigma_{order(p-1)} V^+_{p-1}
|
|
||||||
// = V_k (B_k^T x_{order(p-1)} - sigma_{order(p-1)} y_{order(p-1)})
|
|
||||||
// + betaK * X(k-1, order(p-1)) * V[k]
|
|
||||||
// = betaK * X(k-1, order(p-1)) * V[k] (since B_k^T x_j = sigma_j y_j)
|
|
||||||
//
|
|
||||||
// Therefore: betaRestart = |betaK * X(k-1, order(p-1))|
|
|
||||||
// V[p] = sign(X(k-1, order(p-1))) * V[k]
|
|
||||||
|
|
||||||
// ---- Build new lattice vectors ----
|
|
||||||
std::vector<Field> Vnew, Unew;
|
|
||||||
Vnew.reserve(p + 1);
|
|
||||||
Unew.reserve(p);
|
|
||||||
|
|
||||||
for (int i = 0; i < p; ++i) {
|
|
||||||
int idx = order(i);
|
|
||||||
Field vi(Grid); vi = Zero();
|
|
||||||
for (int j = 0; j < k; ++j)
|
|
||||||
vi = vi + Y(j, idx) * V[j];
|
|
||||||
Vnew.push_back(vi);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < p; ++i) {
|
|
||||||
int idx = order(i);
|
|
||||||
Field ui(Grid); ui = Zero();
|
|
||||||
for (int j = 0; j < k; ++j)
|
|
||||||
ui = ui + X(j, idx) * U[j];
|
|
||||||
Unew.push_back(ui);
|
|
||||||
}
|
|
||||||
|
|
||||||
// New v_{p} (0-indexed: V[p]) = sign * V[k]
|
|
||||||
// From A^dag U_k = V_k B_k^T + betaK V[k] e_{k-1}^T:
|
|
||||||
// A^dag U^+_j - sigma_j V^+_j = betaK * X(k-1, order(j)) * V[k]
|
|
||||||
// The last Ritz pair (j=p-1) defines betaRestart and the sign of V[p].
|
|
||||||
// All p couplings (j=0..p-1) are stored in fvec so that buildFullB can
|
|
||||||
// reconstruct the exact column p of U^dag A V after the next extension.
|
|
||||||
RealD coeff = betaK * X(k - 1, order(p - 1));
|
|
||||||
betaRestart = std::abs(coeff);
|
|
||||||
RealD sgn = (coeff >= 0.0) ? 1.0 : -1.0;
|
|
||||||
|
|
||||||
fvec.resize(p);
|
|
||||||
for (int j = 0; j < p; ++j)
|
|
||||||
fvec[j] = betaK * X(k - 1, order(j)) * sgn;
|
|
||||||
// fvec[p-1] == betaRestart by construction
|
|
||||||
restart_col = p;
|
|
||||||
|
|
||||||
Field vp(Grid);
|
|
||||||
if (betaRestart > 1.0e-14) {
|
|
||||||
vp = sgn * V[k];
|
|
||||||
} else {
|
|
||||||
betaRestart = 0.0;
|
|
||||||
vp = Zero();
|
|
||||||
}
|
|
||||||
Vnew.push_back(vp); // V[p]
|
|
||||||
|
|
||||||
// ---- New alpha, beta ----
|
|
||||||
// B_p^+ is diagonal: alpha^+_i = sigma_{order(i)}, all internal beta = 0
|
|
||||||
std::vector<RealD> alpha_new(p), beta_new(p);
|
|
||||||
for (int i = 0; i < p; ++i) alpha_new[i] = sigma(order(i));
|
|
||||||
for (int i = 0; i < p - 1; ++i) beta_new[i] = 0.0;
|
|
||||||
beta_new[p - 1] = betaRestart;
|
|
||||||
|
|
||||||
// ---- Commit new state ----
|
|
||||||
V = Vnew;
|
|
||||||
U = Unew;
|
|
||||||
alpha = alpha_new;
|
|
||||||
beta = beta_new;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage
|
|
||||||
<< "IRLBA restart: compressed to " << p << " steps,"
|
|
||||||
<< " new beta_p = " << betaRestart << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// Extract the desired singular triplets into the public output vectors.
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
void extractTriplets(int m,
|
|
||||||
const Eigen::VectorXd &sigma,
|
|
||||||
const Eigen::MatrixXd &X,
|
|
||||||
const Eigen::MatrixXd &Y,
|
|
||||||
const Eigen::VectorXi &order,
|
|
||||||
int nout)
|
|
||||||
{
|
|
||||||
singularValues.resize(nout);
|
|
||||||
leftVectors.clear(); leftVectors.reserve(nout);
|
|
||||||
rightVectors.clear(); rightVectors.reserve(nout);
|
|
||||||
|
|
||||||
for (int i = 0; i < nout; ++i) {
|
|
||||||
int idx = order(i);
|
|
||||||
singularValues[i] = sigma(idx);
|
|
||||||
|
|
||||||
// Left singular vector of A: svec_L = U_m * x_i
|
|
||||||
Field svL(Grid); svL = Zero();
|
|
||||||
for (int j = 0; j < m && j < (int)U.size(); ++j)
|
|
||||||
svL = svL + X(j, idx) * U[j];
|
|
||||||
leftVectors.push_back(svL);
|
|
||||||
|
|
||||||
// Right singular vector of A: svec_R = V_m * y_i
|
|
||||||
Field svR(Grid); svR = Zero();
|
|
||||||
for (int j = 0; j < m && j < (int)V.size(); ++j)
|
|
||||||
svR = svR + Y(j, idx) * V[j];
|
|
||||||
rightVectors.push_back(svR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
|
||||||
@@ -1,931 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/ImplicitlyRestartedLanczos.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Chulwoo Jung <chulwoo@bnl.gov>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
|
||||||
*************************************************************************************/
|
|
||||||
/* END LEGAL */
|
|
||||||
#ifndef GRID_LANC_H
|
|
||||||
#define GRID_LANC_H
|
|
||||||
|
|
||||||
#include <string.h> //memset
|
|
||||||
|
|
||||||
#ifdef USE_LAPACK
|
|
||||||
#ifdef USE_MKL
|
|
||||||
#include<mkl_lapack.h>
|
|
||||||
#else
|
|
||||||
void LAPACK_dstegr (char *jobz, char *range, int *n, double *d, double *e,
|
|
||||||
double *vl, double *vu, int *il, int *iu, double *abstol,
|
|
||||||
int *m, double *w, double *z, int *ldz, int *isuppz,
|
|
||||||
double *work, int *lwork, int *iwork, int *liwork,
|
|
||||||
int *info);
|
|
||||||
//#include <lapacke/lapacke.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//#include <Grid/algorithms/densematrix/DenseMatrix.h>
|
|
||||||
|
|
||||||
// eliminate temorary vector in calc()
|
|
||||||
#define MEM_SAVE
|
|
||||||
|
|
||||||
namespace Grid
|
|
||||||
{
|
|
||||||
|
|
||||||
struct Bisection
|
|
||||||
{
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
static void get_eig2 (int row_num, std::vector < RealD > &ALPHA,
|
|
||||||
std::vector < RealD > &BETA,
|
|
||||||
std::vector < RealD > &eig)
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
std::vector < RealD > evec1 (row_num + 3);
|
|
||||||
std::vector < RealD > evec2 (row_num + 3);
|
|
||||||
RealD eps2;
|
|
||||||
ALPHA[1] = 0.;
|
|
||||||
BETHA[1] = 0.;
|
|
||||||
for (i = 0; i < row_num - 1; i++)
|
|
||||||
{
|
|
||||||
ALPHA[i + 1] = A[i * (row_num + 1)].real ();
|
|
||||||
BETHA[i + 2] = A[i * (row_num + 1) + 1].real ();
|
|
||||||
}
|
|
||||||
ALPHA[row_num] = A[(row_num - 1) * (row_num + 1)].real ();
|
|
||||||
bisec (ALPHA, BETHA, row_num, 1, row_num, 1e-10, 1e-10, evec1, eps2);
|
|
||||||
bisec (ALPHA, BETHA, row_num, 1, row_num, 1e-16, 1e-16, evec2, eps2);
|
|
||||||
|
|
||||||
// Do we really need to sort here?
|
|
||||||
int begin = 1;
|
|
||||||
int end = row_num;
|
|
||||||
int swapped = 1;
|
|
||||||
while (swapped)
|
|
||||||
{
|
|
||||||
swapped = 0;
|
|
||||||
for (i = begin; i < end; i++)
|
|
||||||
{
|
|
||||||
if (mag (evec2[i]) > mag (evec2[i + 1]))
|
|
||||||
{
|
|
||||||
swap (evec2 + i, evec2 + i + 1);
|
|
||||||
swapped = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end--;
|
|
||||||
for (i = end - 1; i >= begin; i--)
|
|
||||||
{
|
|
||||||
if (mag (evec2[i]) > mag (evec2[i + 1]))
|
|
||||||
{
|
|
||||||
swap (evec2 + i, evec2 + i + 1);
|
|
||||||
swapped = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
begin++;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < row_num; i++)
|
|
||||||
{
|
|
||||||
for (j = 0; j < row_num; j++)
|
|
||||||
{
|
|
||||||
if (i == j)
|
|
||||||
H[i * row_num + j] = evec2[i + 1];
|
|
||||||
else
|
|
||||||
H[i * row_num + j] = 0.;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void bisec (std::vector < RealD > &c,
|
|
||||||
std::vector < RealD > &b,
|
|
||||||
int n,
|
|
||||||
int m1,
|
|
||||||
int m2,
|
|
||||||
RealD eps1,
|
|
||||||
RealD relfeh, std::vector < RealD > &x, RealD & eps2)
|
|
||||||
{
|
|
||||||
std::vector < RealD > wu (n + 2);
|
|
||||||
|
|
||||||
RealD h, q, x1, xu, x0, xmin, xmax;
|
|
||||||
int i, a, k;
|
|
||||||
|
|
||||||
b[1] = 0.0;
|
|
||||||
xmin = c[n] - fabs (b[n]);
|
|
||||||
xmax = c[n] + fabs (b[n]);
|
|
||||||
for (i = 1; i < n; i++)
|
|
||||||
{
|
|
||||||
h = fabs (b[i]) + fabs (b[i + 1]);
|
|
||||||
if (c[i] + h > xmax)
|
|
||||||
xmax = c[i] + h;
|
|
||||||
if (c[i] - h < xmin)
|
|
||||||
xmin = c[i] - h;
|
|
||||||
}
|
|
||||||
xmax *= 2.;
|
|
||||||
|
|
||||||
eps2 = relfeh * ((xmin + xmax) > 0.0 ? xmax : -xmin);
|
|
||||||
if (eps1 <= 0.0)
|
|
||||||
eps1 = eps2;
|
|
||||||
eps2 = 0.5 * eps1 + 7.0 * (eps2);
|
|
||||||
x0 = xmax;
|
|
||||||
for (i = m1; i <= m2; i++)
|
|
||||||
{
|
|
||||||
x[i] = xmax;
|
|
||||||
wu[i] = xmin;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (k = m2; k >= m1; k--)
|
|
||||||
{
|
|
||||||
xu = xmin;
|
|
||||||
i = k;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (xu < wu[i])
|
|
||||||
{
|
|
||||||
xu = wu[i];
|
|
||||||
i = m1 - 1;
|
|
||||||
}
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
while (i >= m1);
|
|
||||||
if (x0 > x[k])
|
|
||||||
x0 = x[k];
|
|
||||||
while ((x0 - xu) > 2 * relfeh * (fabs (xu) + fabs (x0)) + eps1)
|
|
||||||
{
|
|
||||||
x1 = (xu + x0) / 2;
|
|
||||||
|
|
||||||
a = 0;
|
|
||||||
q = 1.0;
|
|
||||||
for (i = 1; i <= n; i++)
|
|
||||||
{
|
|
||||||
q =
|
|
||||||
c[i] - x1 -
|
|
||||||
((q != 0.0) ? b[i] * b[i] / q : fabs (b[i]) / relfeh);
|
|
||||||
if (q < 0)
|
|
||||||
a++;
|
|
||||||
}
|
|
||||||
// printf("x1=%0.14e a=%d\n",x1,a);
|
|
||||||
if (a < k)
|
|
||||||
{
|
|
||||||
if (a < m1)
|
|
||||||
{
|
|
||||||
xu = x1;
|
|
||||||
wu[m1] = x1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xu = x1;
|
|
||||||
wu[a + 1] = x1;
|
|
||||||
if (x[a] > x1)
|
|
||||||
x[a] = x1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
x0 = x1;
|
|
||||||
}
|
|
||||||
printf ("x0=%0.14e xu=%0.14e k=%d\n", x0, xu, k);
|
|
||||||
x[k] = (x0 + xu) / 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
// Implicitly restarted lanczos
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
template < class Field > class SimpleLanczos
|
|
||||||
{
|
|
||||||
|
|
||||||
const RealD small = 1.0e-16;
|
|
||||||
public:
|
|
||||||
int lock;
|
|
||||||
int get;
|
|
||||||
int Niter;
|
|
||||||
int converged;
|
|
||||||
|
|
||||||
int Nstop; // Number of evecs checked for convergence
|
|
||||||
int Nk; // Number of converged sought
|
|
||||||
int Np; // Np -- Number of spare vecs in kryloc space
|
|
||||||
int Nm; // Nm -- total number of vectors
|
|
||||||
|
|
||||||
|
|
||||||
RealD OrthoTime;
|
|
||||||
|
|
||||||
RealD eresid;
|
|
||||||
|
|
||||||
// SortEigen < Field > _sort;
|
|
||||||
|
|
||||||
LinearFunction < Field > &_Linop;
|
|
||||||
|
|
||||||
// OperatorFunction < Field > &_poly;
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
// Constructor
|
|
||||||
/////////////////////////
|
|
||||||
void init (void)
|
|
||||||
{
|
|
||||||
};
|
|
||||||
// void Abort (int ff, std::vector < RealD > &evals, DenseVector < Denstd::vector < RealD > >&evecs);
|
|
||||||
|
|
||||||
SimpleLanczos (LinearFunction < Field > &Linop, // op
|
|
||||||
// OperatorFunction < Field > &poly, // polynmial
|
|
||||||
int _Nstop, // sought vecs
|
|
||||||
int _Nk, // sought vecs
|
|
||||||
int _Nm, // spare vecs
|
|
||||||
RealD _eresid, // resid in lmdue deficit
|
|
||||||
int _Niter): // Max iterations
|
|
||||||
|
|
||||||
_Linop (Linop),
|
|
||||||
// _poly (poly),
|
|
||||||
Nstop (_Nstop), Nk (_Nk), Nm (_Nm), eresid (_eresid), Niter (_Niter)
|
|
||||||
{
|
|
||||||
Np = Nm - Nk;
|
|
||||||
assert (Np > 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
// Sanity checked this routine (step) against Saad.
|
|
||||||
/////////////////////////
|
|
||||||
void RitzMatrix (std::vector < Field > &evec, int k)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
GridBase *grid = evec[0].Grid();
|
|
||||||
Field w (grid);
|
|
||||||
std::cout << GridLogMessage << "RitzMatrix " << std::endl;
|
|
||||||
for (int i = 0; i < k; i++)
|
|
||||||
{
|
|
||||||
_Linop(evec[i], w);
|
|
||||||
// _poly(_Linop,evec[i],w);
|
|
||||||
std::cout << GridLogMessage << "[" << i << "] ";
|
|
||||||
for (int j = 0; j < k; j++)
|
|
||||||
{
|
|
||||||
ComplexD in = innerProduct (evec[j], w);
|
|
||||||
if (fabs ((double) i - j) > 1)
|
|
||||||
{
|
|
||||||
if (abs (in) > 1.0e-9)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage << "oops" << std::endl;
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
std::cout << GridLogMessage << " 0 ";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage << " " << in << " ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::cout << GridLogMessage << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void step (std::vector < RealD > &lmd,
|
|
||||||
std::vector < RealD > &lme,
|
|
||||||
Field & last, Field & current, Field & next, uint64_t k)
|
|
||||||
{
|
|
||||||
if (lmd.size () <= k)
|
|
||||||
lmd.resize (k + Nm);
|
|
||||||
if (lme.size () <= k)
|
|
||||||
lme.resize (k + Nm);
|
|
||||||
|
|
||||||
|
|
||||||
// _poly(_Linop,current,next ); // 3. wk:=Avk−βkv_{k−1}
|
|
||||||
_Linop(current, next); // 3. wk:=Avk−βkv_{k−1}
|
|
||||||
if (k > 0)
|
|
||||||
{
|
|
||||||
next -= lme[k - 1] * last;
|
|
||||||
}
|
|
||||||
// std::cout<<GridLogMessage << "<last|next>" << innerProduct(last,next) <<std::endl;
|
|
||||||
|
|
||||||
ComplexD zalph = innerProduct (current, next); // 4. αk:=(wk,vk)
|
|
||||||
RealD alph = real (zalph);
|
|
||||||
|
|
||||||
next = next - alph * current; // 5. wk:=wk−αkvk
|
|
||||||
// std::cout<<GridLogMessage << "<current|next>" << innerProduct(current,next) <<std::endl;
|
|
||||||
|
|
||||||
RealD beta = normalise (next); // 6. βk+1 := ∥wk∥2. If βk+1 = 0 then Stop
|
|
||||||
// 7. vk+1 := wk/βk+1
|
|
||||||
// norm=beta;
|
|
||||||
|
|
||||||
int interval = Nm / 100 + 1;
|
|
||||||
if ((k % interval) == 0)
|
|
||||||
std::
|
|
||||||
cout << GridLogMessage << k << " : alpha = " << zalph << " beta " <<
|
|
||||||
beta << std::endl;
|
|
||||||
const RealD tiny = 1.0e-20;
|
|
||||||
if (beta < tiny)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage << " beta is tiny " << beta << std::
|
|
||||||
endl;
|
|
||||||
}
|
|
||||||
lmd[k] = alph;
|
|
||||||
lme[k] = beta;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void qr_decomp (std::vector < RealD > &lmd,
|
|
||||||
std::vector < RealD > &lme,
|
|
||||||
int Nk,
|
|
||||||
int Nm,
|
|
||||||
std::vector < RealD > &Qt, RealD Dsh, int kmin, int kmax)
|
|
||||||
{
|
|
||||||
int k = kmin - 1;
|
|
||||||
RealD x;
|
|
||||||
|
|
||||||
RealD Fden = 1.0 / hypot (lmd[k] - Dsh, lme[k]);
|
|
||||||
RealD c = (lmd[k] - Dsh) * Fden;
|
|
||||||
RealD s = -lme[k] * Fden;
|
|
||||||
|
|
||||||
RealD tmpa1 = lmd[k];
|
|
||||||
RealD tmpa2 = lmd[k + 1];
|
|
||||||
RealD tmpb = lme[k];
|
|
||||||
|
|
||||||
lmd[k] = c * c * tmpa1 + s * s * tmpa2 - 2.0 * c * s * tmpb;
|
|
||||||
lmd[k + 1] = s * s * tmpa1 + c * c * tmpa2 + 2.0 * c * s * tmpb;
|
|
||||||
lme[k] = c * s * (tmpa1 - tmpa2) + (c * c - s * s) * tmpb;
|
|
||||||
x = -s * lme[k + 1];
|
|
||||||
lme[k + 1] = c * lme[k + 1];
|
|
||||||
|
|
||||||
for (int i = 0; i < Nk; ++i)
|
|
||||||
{
|
|
||||||
RealD Qtmp1 = Qt[i + Nm * k];
|
|
||||||
RealD Qtmp2 = Qt[i + Nm * (k + 1)];
|
|
||||||
Qt[i + Nm * k] = c * Qtmp1 - s * Qtmp2;
|
|
||||||
Qt[i + Nm * (k + 1)] = s * Qtmp1 + c * Qtmp2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Givens transformations
|
|
||||||
for (int k = kmin; k < kmax - 1; ++k)
|
|
||||||
{
|
|
||||||
|
|
||||||
RealD Fden = 1.0 / hypot (x, lme[k - 1]);
|
|
||||||
RealD c = lme[k - 1] * Fden;
|
|
||||||
RealD s = -x * Fden;
|
|
||||||
|
|
||||||
RealD tmpa1 = lmd[k];
|
|
||||||
RealD tmpa2 = lmd[k + 1];
|
|
||||||
RealD tmpb = lme[k];
|
|
||||||
|
|
||||||
lmd[k] = c * c * tmpa1 + s * s * tmpa2 - 2.0 * c * s * tmpb;
|
|
||||||
lmd[k + 1] = s * s * tmpa1 + c * c * tmpa2 + 2.0 * c * s * tmpb;
|
|
||||||
lme[k] = c * s * (tmpa1 - tmpa2) + (c * c - s * s) * tmpb;
|
|
||||||
lme[k - 1] = c * lme[k - 1] - s * x;
|
|
||||||
|
|
||||||
if (k != kmax - 2)
|
|
||||||
{
|
|
||||||
x = -s * lme[k + 1];
|
|
||||||
lme[k + 1] = c * lme[k + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < Nk; ++i)
|
|
||||||
{
|
|
||||||
RealD Qtmp1 = Qt[i + Nm * k];
|
|
||||||
RealD Qtmp2 = Qt[i + Nm * (k + 1)];
|
|
||||||
Qt[i + Nm * k] = c * Qtmp1 - s * Qtmp2;
|
|
||||||
Qt[i + Nm * (k + 1)] = s * Qtmp1 + c * Qtmp2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
#ifdef USE_LAPACK
|
|
||||||
#ifdef USE_MKL
|
|
||||||
#define LAPACK_INT MKL_INT
|
|
||||||
#else
|
|
||||||
#define LAPACK_INT long long
|
|
||||||
#endif
|
|
||||||
void diagonalize_lapack (std::vector < RealD > &lmd, std::vector < RealD > &lme, int N1, // all
|
|
||||||
int N2, // get
|
|
||||||
GridBase * grid)
|
|
||||||
{
|
|
||||||
const int size = Nm;
|
|
||||||
LAPACK_INT NN = N1;
|
|
||||||
double evals_tmp[NN];
|
|
||||||
double DD[NN];
|
|
||||||
double EE[NN];
|
|
||||||
for (int i = 0; i < NN; i++)
|
|
||||||
for (int j = i - 1; j <= i + 1; j++)
|
|
||||||
if (j < NN && j >= 0)
|
|
||||||
{
|
|
||||||
if (i == j)
|
|
||||||
DD[i] = lmd[i];
|
|
||||||
if (i == j)
|
|
||||||
evals_tmp[i] = lmd[i];
|
|
||||||
if (j == (i - 1))
|
|
||||||
EE[j] = lme[j];
|
|
||||||
}
|
|
||||||
LAPACK_INT evals_found;
|
|
||||||
LAPACK_INT lwork =
|
|
||||||
((18 * NN) >
|
|
||||||
(1 + 4 * NN + NN * NN) ? (18 * NN) : (1 + 4 * NN + NN * NN));
|
|
||||||
LAPACK_INT liwork = 3 + NN * 10;
|
|
||||||
LAPACK_INT iwork[liwork];
|
|
||||||
double work[lwork];
|
|
||||||
LAPACK_INT isuppz[2 * NN];
|
|
||||||
char jobz = 'N'; // calculate evals only
|
|
||||||
char range = 'I'; // calculate il-th to iu-th evals
|
|
||||||
// char range = 'A'; // calculate all evals
|
|
||||||
char uplo = 'U'; // refer to upper half of original matrix
|
|
||||||
char compz = 'I'; // Compute eigenvectors of tridiagonal matrix
|
|
||||||
int ifail[NN];
|
|
||||||
LAPACK_INT info;
|
|
||||||
// int total = QMP_get_number_of_nodes();
|
|
||||||
// int node = QMP_get_node_number();
|
|
||||||
// GridBase *grid = evec[0]._grid;
|
|
||||||
int total = grid->_Nprocessors;
|
|
||||||
int node = grid->_processor;
|
|
||||||
int interval = (NN / total) + 1;
|
|
||||||
double vl = 0.0, vu = 0.0;
|
|
||||||
LAPACK_INT il = interval * node + 1, iu = interval * (node + 1);
|
|
||||||
if (iu > NN)
|
|
||||||
iu = NN;
|
|
||||||
double tol = 0.0;
|
|
||||||
if (1)
|
|
||||||
{
|
|
||||||
memset (evals_tmp, 0, sizeof (double) * NN);
|
|
||||||
if (il <= NN)
|
|
||||||
{
|
|
||||||
printf ("total=%d node=%d il=%d iu=%d\n", total, node, il, iu);
|
|
||||||
#ifdef USE_MKL
|
|
||||||
dstegr (&jobz, &range, &NN,
|
|
||||||
#else
|
|
||||||
LAPACK_dstegr (&jobz, &range, &NN,
|
|
||||||
#endif
|
|
||||||
(double *) DD, (double *) EE, &vl, &vu, &il, &iu, // these four are ignored if second parameteris 'A'
|
|
||||||
&tol, // tolerance
|
|
||||||
&evals_found, evals_tmp, (double *) NULL, &NN,
|
|
||||||
isuppz, work, &lwork, iwork, &liwork, &info);
|
|
||||||
for (int i = iu - 1; i >= il - 1; i--)
|
|
||||||
{
|
|
||||||
printf ("node=%d evals_found=%d evals_tmp[%d] = %g\n", node,
|
|
||||||
evals_found, i - (il - 1), evals_tmp[i - (il - 1)]);
|
|
||||||
evals_tmp[i] = evals_tmp[i - (il - 1)];
|
|
||||||
if (il > 1)
|
|
||||||
evals_tmp[i - (il - 1)] = 0.;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
grid->GlobalSumVector (evals_tmp, NN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// cheating a bit. It is better to sort instead of just reversing it, but the document of the routine says evals are sorted in increasing order. qr gives evals in decreasing order.
|
|
||||||
}
|
|
||||||
#undef LAPACK_INT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
void diagonalize (std::vector < RealD > &lmd,
|
|
||||||
std::vector < RealD > &lme,
|
|
||||||
int N2, int N1, GridBase * grid)
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef USE_LAPACK
|
|
||||||
const int check_lapack = 0; // just use lapack if 0, check against lapack if 1
|
|
||||||
|
|
||||||
if (!check_lapack)
|
|
||||||
return diagonalize_lapack (lmd, lme, N2, N1, grid);
|
|
||||||
|
|
||||||
// diagonalize_lapack(lmd2,lme2,Nm2,Nm,Qt,grid);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static RealD normalise (Field & v)
|
|
||||||
{
|
|
||||||
RealD nn = norm2 (v);
|
|
||||||
nn = sqrt (nn);
|
|
||||||
v = v * (1.0 / nn);
|
|
||||||
return nn;
|
|
||||||
}
|
|
||||||
|
|
||||||
void orthogonalize (Field & w, std::vector < Field > &evec, int k)
|
|
||||||
{
|
|
||||||
double t0 = -usecond () / 1e6;
|
|
||||||
typedef typename Field::scalar_type MyComplex;
|
|
||||||
MyComplex ip;
|
|
||||||
|
|
||||||
if (0)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < k; ++j)
|
|
||||||
{
|
|
||||||
normalise (evec[j]);
|
|
||||||
for (int i = 0; i < j; i++)
|
|
||||||
{
|
|
||||||
ip = innerProduct (evec[i], evec[j]); // are the evecs normalised? ; this assumes so.
|
|
||||||
evec[j] = evec[j] - ip * evec[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = 0; j < k; ++j)
|
|
||||||
{
|
|
||||||
ip = innerProduct (evec[j], w); // are the evecs normalised? ; this assumes so.
|
|
||||||
w = w - ip * evec[j];
|
|
||||||
}
|
|
||||||
normalise (w);
|
|
||||||
t0 += usecond () / 1e6;
|
|
||||||
OrthoTime += t0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setUnit_Qt (int Nm, std::vector < RealD > &Qt)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < Qt.size (); ++i)
|
|
||||||
Qt[i] = 0.0;
|
|
||||||
for (int k = 0; k < Nm; ++k)
|
|
||||||
Qt[k + k * Nm] = 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void calc (std::vector < RealD > &eval, const Field & src, int &Nconv)
|
|
||||||
{
|
|
||||||
|
|
||||||
GridBase *grid = src.Grid();
|
|
||||||
// assert(grid == src._grid);
|
|
||||||
|
|
||||||
std::
|
|
||||||
cout << GridLogMessage << " -- Nk = " << Nk << " Np = " << Np << std::
|
|
||||||
endl;
|
|
||||||
std::cout << GridLogMessage << " -- Nm = " << Nm << std::endl;
|
|
||||||
std::cout << GridLogMessage << " -- size of eval = " << eval.
|
|
||||||
size () << std::endl;
|
|
||||||
|
|
||||||
// assert(c.size() && Nm == eval.size());
|
|
||||||
|
|
||||||
std::vector < RealD > lme (Nm);
|
|
||||||
std::vector < RealD > lmd (Nm);
|
|
||||||
|
|
||||||
|
|
||||||
Field current (grid);
|
|
||||||
Field last (grid);
|
|
||||||
Field next (grid);
|
|
||||||
|
|
||||||
Nconv = 0;
|
|
||||||
|
|
||||||
RealD beta_k;
|
|
||||||
|
|
||||||
// Set initial vector
|
|
||||||
// (uniform vector) Why not src??
|
|
||||||
// evec[0] = 1.0;
|
|
||||||
current = src;
|
|
||||||
std::cout << GridLogMessage << "norm2(src)= " << norm2 (src) << std::
|
|
||||||
endl;
|
|
||||||
normalise (current);
|
|
||||||
std::
|
|
||||||
cout << GridLogMessage << "norm2(evec[0])= " << norm2 (current) <<
|
|
||||||
std::endl;
|
|
||||||
|
|
||||||
// Initial Nk steps
|
|
||||||
OrthoTime = 0.;
|
|
||||||
double t0 = usecond () / 1e6;
|
|
||||||
RealD norm; // sqrt norm of last vector
|
|
||||||
|
|
||||||
uint64_t iter = 0;
|
|
||||||
|
|
||||||
bool initted = false;
|
|
||||||
std::vector < RealD > low (Nstop * 10);
|
|
||||||
std::vector < RealD > high (Nstop * 10);
|
|
||||||
RealD cont = 0.;
|
|
||||||
while (1) {
|
|
||||||
cont = 0.;
|
|
||||||
std::vector < RealD > lme2 (Nm);
|
|
||||||
std::vector < RealD > lmd2 (Nm);
|
|
||||||
for (uint64_t k = 0; k < Nm; ++k, iter++) {
|
|
||||||
step (lmd, lme, last, current, next, iter);
|
|
||||||
last = current;
|
|
||||||
current = next;
|
|
||||||
}
|
|
||||||
double t1 = usecond () / 1e6;
|
|
||||||
std::cout << GridLogMessage << "IRL::Initial steps: " << t1 -
|
|
||||||
t0 << "seconds" << std::endl;
|
|
||||||
t0 = t1;
|
|
||||||
std::
|
|
||||||
cout << GridLogMessage << "IRL::Initial steps:OrthoTime " <<
|
|
||||||
OrthoTime << "seconds" << std::endl;
|
|
||||||
|
|
||||||
// getting eigenvalues
|
|
||||||
lmd2.resize (iter + 2);
|
|
||||||
lme2.resize (iter + 2);
|
|
||||||
for (uint64_t k = 0; k < iter; ++k) {
|
|
||||||
lmd2[k + 1] = lmd[k];
|
|
||||||
lme2[k + 2] = lme[k];
|
|
||||||
}
|
|
||||||
t1 = usecond () / 1e6;
|
|
||||||
std::cout << GridLogMessage << "IRL:: copy: " << t1 -
|
|
||||||
t0 << "seconds" << std::endl;
|
|
||||||
t0 = t1;
|
|
||||||
{
|
|
||||||
int total = grid->_Nprocessors;
|
|
||||||
int node = grid->_processor;
|
|
||||||
int interval = (Nstop / total) + 1;
|
|
||||||
int iu = (iter + 1) - (interval * node + 1);
|
|
||||||
int il = (iter + 1) - (interval * (node + 1));
|
|
||||||
std::vector < RealD > eval2 (iter + 3);
|
|
||||||
RealD eps2;
|
|
||||||
Bisection::bisec (lmd2, lme2, iter, il, iu, 1e-16, 1e-10, eval2,
|
|
||||||
eps2);
|
|
||||||
// diagonalize(eval2,lme2,iter,Nk,grid);
|
|
||||||
RealD diff = 0.;
|
|
||||||
for (int i = il; i <= iu; i++) {
|
|
||||||
if (initted)
|
|
||||||
diff =
|
|
||||||
fabs (eval2[i] - high[iu-i]) / (fabs (eval2[i]) +
|
|
||||||
fabs (high[iu-i]));
|
|
||||||
if (initted && (diff > eresid))
|
|
||||||
cont = 1.;
|
|
||||||
if (initted)
|
|
||||||
printf ("eval[%d]=%0.14e %0.14e, %0.14e\n", i, eval2[i],
|
|
||||||
high[iu-i], diff);
|
|
||||||
high[iu-i] = eval2[i];
|
|
||||||
}
|
|
||||||
il = (interval * node + 1);
|
|
||||||
iu = (interval * (node + 1));
|
|
||||||
Bisection::bisec (lmd2, lme2, iter, il, iu, 1e-16, 1e-10, eval2,
|
|
||||||
eps2);
|
|
||||||
for (int i = il; i <= iu; i++) {
|
|
||||||
if (initted)
|
|
||||||
diff =
|
|
||||||
fabs (eval2[i] - low[i]) / (fabs (eval2[i]) +
|
|
||||||
fabs (low[i]));
|
|
||||||
if (initted && (diff > eresid))
|
|
||||||
cont = 1.;
|
|
||||||
if (initted)
|
|
||||||
printf ("eval[%d]=%0.14e %0.14e, %0.14e\n", i, eval2[i],
|
|
||||||
low[i], diff);
|
|
||||||
low[i] = eval2[i];
|
|
||||||
}
|
|
||||||
t1 = usecond () / 1e6;
|
|
||||||
std::cout << GridLogMessage << "IRL:: diagonalize: " << t1 -
|
|
||||||
t0 << "seconds" << std::endl;
|
|
||||||
t0 = t1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint64_t k = 0; k < Nk; ++k) {
|
|
||||||
// eval[k] = eval2[k];
|
|
||||||
}
|
|
||||||
if (initted)
|
|
||||||
{
|
|
||||||
grid->GlobalSumVector (&cont, 1);
|
|
||||||
if (cont < 1.) return;
|
|
||||||
}
|
|
||||||
initted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
|
|
||||||
/**
|
|
||||||
There is some matrix Q such that for any vector y
|
|
||||||
Q.e_1 = y and Q is unitary.
|
|
||||||
**/
|
|
||||||
template < class T >
|
|
||||||
static T orthQ (DenseMatrix < T > &Q, std::vector < T > y)
|
|
||||||
{
|
|
||||||
int N = y.size (); //Matrix Size
|
|
||||||
Fill (Q, 0.0);
|
|
||||||
T tau;
|
|
||||||
for (int i = 0; i < N; i++)
|
|
||||||
{
|
|
||||||
Q[i][0] = y[i];
|
|
||||||
}
|
|
||||||
T sig = conj (y[0]) * y[0];
|
|
||||||
T tau0 = fabs (sqrt (sig));
|
|
||||||
|
|
||||||
for (int j = 1; j < N; j++)
|
|
||||||
{
|
|
||||||
sig += conj (y[j]) * y[j];
|
|
||||||
tau = abs (sqrt (sig));
|
|
||||||
|
|
||||||
if (abs (tau0) > 0.0)
|
|
||||||
{
|
|
||||||
|
|
||||||
T gam = conj ((y[j] / tau) / tau0);
|
|
||||||
for (int k = 0; k <= j - 1; k++)
|
|
||||||
{
|
|
||||||
Q[k][j] = -gam * y[k];
|
|
||||||
}
|
|
||||||
Q[j][j] = tau0 / tau;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Q[j - 1][j] = 1.0;
|
|
||||||
}
|
|
||||||
tau0 = tau;
|
|
||||||
}
|
|
||||||
return tau;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
There is some matrix Q such that for any vector y
|
|
||||||
Q.e_k = y and Q is unitary.
|
|
||||||
**/
|
|
||||||
template < class T >
|
|
||||||
static T orthU (DenseMatrix < T > &Q, std::vector < T > y)
|
|
||||||
{
|
|
||||||
T tau = orthQ (Q, y);
|
|
||||||
SL (Q);
|
|
||||||
return tau;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Wind up with a matrix with the first con rows untouched
|
|
||||||
|
|
||||||
say con = 2
|
|
||||||
Q is such that Qdag H Q has {x, x, val, 0, 0, 0, 0, ...} as 1st colum
|
|
||||||
and the matrix is upper hessenberg
|
|
||||||
and with f and Q appropriately modidied with Q is the arnoldi factorization
|
|
||||||
|
|
||||||
**/
|
|
||||||
|
|
||||||
template < class T > static void Lock (DenseMatrix < T > &H, ///Hess mtx
|
|
||||||
DenseMatrix < T > &Q, ///Lock Transform
|
|
||||||
T val, ///value to be locked
|
|
||||||
int con, ///number already locked
|
|
||||||
RealD small, int dfg, bool herm)
|
|
||||||
{
|
|
||||||
//ForceTridiagonal(H);
|
|
||||||
|
|
||||||
int M = H.dim;
|
|
||||||
DenseVector < T > vec;
|
|
||||||
Resize (vec, M - con);
|
|
||||||
|
|
||||||
DenseMatrix < T > AH;
|
|
||||||
Resize (AH, M - con, M - con);
|
|
||||||
AH = GetSubMtx (H, con, M, con, M);
|
|
||||||
|
|
||||||
DenseMatrix < T > QQ;
|
|
||||||
Resize (QQ, M - con, M - con);
|
|
||||||
|
|
||||||
Unity (Q);
|
|
||||||
Unity (QQ);
|
|
||||||
|
|
||||||
DenseVector < T > evals;
|
|
||||||
Resize (evals, M - con);
|
|
||||||
DenseMatrix < T > evecs;
|
|
||||||
Resize (evecs, M - con, M - con);
|
|
||||||
|
|
||||||
Wilkinson < T > (AH, evals, evecs, small);
|
|
||||||
|
|
||||||
int k = 0;
|
|
||||||
RealD cold = abs (val - evals[k]);
|
|
||||||
for (int i = 1; i < M - con; i++)
|
|
||||||
{
|
|
||||||
RealD cnew = abs (val - evals[i]);
|
|
||||||
if (cnew < cold)
|
|
||||||
{
|
|
||||||
k = i;
|
|
||||||
cold = cnew;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vec = evecs[k];
|
|
||||||
|
|
||||||
ComplexD tau;
|
|
||||||
orthQ (QQ, vec);
|
|
||||||
//orthQM(QQ,AH,vec);
|
|
||||||
|
|
||||||
AH = Hermitian (QQ) * AH;
|
|
||||||
AH = AH * QQ;
|
|
||||||
|
|
||||||
for (int i = con; i < M; i++)
|
|
||||||
{
|
|
||||||
for (int j = con; j < M; j++)
|
|
||||||
{
|
|
||||||
Q[i][j] = QQ[i - con][j - con];
|
|
||||||
H[i][j] = AH[i - con][j - con];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = M - 1; j > con + 2; j--)
|
|
||||||
{
|
|
||||||
|
|
||||||
DenseMatrix < T > U;
|
|
||||||
Resize (U, j - 1 - con, j - 1 - con);
|
|
||||||
DenseVector < T > z;
|
|
||||||
Resize (z, j - 1 - con);
|
|
||||||
T nm = norm (z);
|
|
||||||
for (int k = con + 0; k < j - 1; k++)
|
|
||||||
{
|
|
||||||
z[k - con] = conj (H (j, k + 1));
|
|
||||||
}
|
|
||||||
normalise (z);
|
|
||||||
|
|
||||||
RealD tmp = 0;
|
|
||||||
for (int i = 0; i < z.size () - 1; i++)
|
|
||||||
{
|
|
||||||
tmp = tmp + abs (z[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tmp < small / ((RealD) z.size () - 1.0))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
tau = orthU (U, z);
|
|
||||||
|
|
||||||
DenseMatrix < T > Hb;
|
|
||||||
Resize (Hb, j - 1 - con, M);
|
|
||||||
|
|
||||||
for (int a = 0; a < M; a++)
|
|
||||||
{
|
|
||||||
for (int b = 0; b < j - 1 - con; b++)
|
|
||||||
{
|
|
||||||
T sum = 0;
|
|
||||||
for (int c = 0; c < j - 1 - con; c++)
|
|
||||||
{
|
|
||||||
sum += H[a][con + 1 + c] * U[c][b];
|
|
||||||
} //sum += H(a,con+1+c)*U(c,b);}
|
|
||||||
Hb[b][a] = sum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int k = con + 1; k < j; k++)
|
|
||||||
{
|
|
||||||
for (int l = 0; l < M; l++)
|
|
||||||
{
|
|
||||||
H[l][k] = Hb[k - 1 - con][l];
|
|
||||||
}
|
|
||||||
} //H(Hb[k-1-con][l] , l,k);}}
|
|
||||||
|
|
||||||
DenseMatrix < T > Qb;
|
|
||||||
Resize (Qb, M, M);
|
|
||||||
|
|
||||||
for (int a = 0; a < M; a++)
|
|
||||||
{
|
|
||||||
for (int b = 0; b < j - 1 - con; b++)
|
|
||||||
{
|
|
||||||
T sum = 0;
|
|
||||||
for (int c = 0; c < j - 1 - con; c++)
|
|
||||||
{
|
|
||||||
sum += Q[a][con + 1 + c] * U[c][b];
|
|
||||||
} //sum += Q(a,con+1+c)*U(c,b);}
|
|
||||||
Qb[b][a] = sum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int k = con + 1; k < j; k++)
|
|
||||||
{
|
|
||||||
for (int l = 0; l < M; l++)
|
|
||||||
{
|
|
||||||
Q[l][k] = Qb[k - 1 - con][l];
|
|
||||||
}
|
|
||||||
} //Q(Qb[k-1-con][l] , l,k);}}
|
|
||||||
|
|
||||||
DenseMatrix < T > Hc;
|
|
||||||
Resize (Hc, M, M);
|
|
||||||
|
|
||||||
for (int a = 0; a < j - 1 - con; a++)
|
|
||||||
{
|
|
||||||
for (int b = 0; b < M; b++)
|
|
||||||
{
|
|
||||||
T sum = 0;
|
|
||||||
for (int c = 0; c < j - 1 - con; c++)
|
|
||||||
{
|
|
||||||
sum += conj (U[c][a]) * H[con + 1 + c][b];
|
|
||||||
} //sum += conj( U(c,a) )*H(con+1+c,b);}
|
|
||||||
Hc[b][a] = sum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int k = 0; k < M; k++)
|
|
||||||
{
|
|
||||||
for (int l = con + 1; l < j; l++)
|
|
||||||
{
|
|
||||||
H[l][k] = Hc[k][l - 1 - con];
|
|
||||||
}
|
|
||||||
} //H(Hc[k][l-1-con] , l,k);}}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -66,7 +66,21 @@ public:
|
|||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void GlobalOrthonormalise(void)
|
||||||
|
{
|
||||||
|
// Normalise all vectors
|
||||||
|
for(int i=0;i<nbasis; i++){
|
||||||
|
RealD scale = std::pow(norm2(subspace[i]),-0.5);
|
||||||
|
subspace[i] = subspace[i]*scale;
|
||||||
|
}
|
||||||
|
for(int i=0;i<nbasis; i++){
|
||||||
|
for(int j=0;j<i; j++){
|
||||||
|
basisOrthogonalize(subspace,subspace[i],j);
|
||||||
|
}
|
||||||
|
RealD scale = std::pow(norm2(subspace[i]),-0.5);
|
||||||
|
subspace[i] = subspace[i]*scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
void Orthogonalise(void){
|
void Orthogonalise(void){
|
||||||
CoarseScalar InnerProd(CoarseGrid);
|
CoarseScalar InnerProd(CoarseGrid);
|
||||||
// std::cout << GridLogMessage <<" Block Gramm-Schmidt pass 1"<<std::endl;
|
// std::cout << GridLogMessage <<" Block Gramm-Schmidt pass 1"<<std::endl;
|
||||||
@@ -110,7 +124,7 @@ public:
|
|||||||
|
|
||||||
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "noise ["<<b<<"] <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "noise ["<<b<<"] <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
||||||
|
|
||||||
for(int i=0;i<4;i++){
|
for(int i=0;i<2;i++){
|
||||||
|
|
||||||
CG(hermop,noise,subspace[b]);
|
CG(hermop,noise,subspace[b]);
|
||||||
|
|
||||||
@@ -118,6 +132,8 @@ public:
|
|||||||
scale = std::pow(norm2(noise),-0.5);
|
scale = std::pow(norm2(noise),-0.5);
|
||||||
noise=noise*scale;
|
noise=noise*scale;
|
||||||
|
|
||||||
|
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "intermediate["<<i<<"] <i|MdagM|i> "<<norm2(Mn)<<std::endl;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "filtered["<<b<<"] <f|MdagM|f> "<<norm2(Mn)<<std::endl;
|
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "filtered["<<b<<"] <f|MdagM|f> "<<norm2(Mn)<<std::endl;
|
||||||
@@ -134,7 +150,8 @@ public:
|
|||||||
// PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.001,10,DiracOp,simple_fine,30,30);
|
// PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.001,10,DiracOp,simple_fine,30,30);
|
||||||
// PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.001,10,DiracOp,simple_fine,12,12);
|
// PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.001,10,DiracOp,simple_fine,12,12);
|
||||||
// PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.001,30,DiracOp,simple_fine,12,12);
|
// PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.001,30,DiracOp,simple_fine,12,12);
|
||||||
PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.001,30,DiracOp,simple_fine,10,10);
|
// PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.0005,30,DiracOp,simple_fine,20,20);
|
||||||
|
PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.0005,30,DiracOp,simple_fine,10,10);
|
||||||
FineField noise(FineGrid);
|
FineField noise(FineGrid);
|
||||||
FineField src(FineGrid);
|
FineField src(FineGrid);
|
||||||
FineField guess(FineGrid);
|
FineField guess(FineGrid);
|
||||||
@@ -168,12 +185,15 @@ public:
|
|||||||
scale = std::pow(norm2(noise),-0.5);
|
scale = std::pow(norm2(noise),-0.5);
|
||||||
noise=noise*scale;
|
noise=noise*scale;
|
||||||
|
|
||||||
|
DiracOp.Op(noise,Mn); std::cout<<GridLogMessage << "intermediate["<<i<<"] <f|Op|f> "<<innerProduct(noise,Mn)<<" <f|OpDagOp|f>"<<norm2(Mn)<<std::endl;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DiracOp.Op(noise,Mn); std::cout<<GridLogMessage << "filtered["<<b<<"] <f|Op|f> "<<innerProduct(noise,Mn)<<" <f|OpDagOp|f>"<<norm2(Mn)<<std::endl;
|
DiracOp.Op(noise,Mn); std::cout<<GridLogMessage << "filtered["<<b<<"] <f|Op|f> "<<innerProduct(noise,Mn)<<" <f|OpDagOp|f>"<<norm2(Mn)<<std::endl;
|
||||||
subspace[b] = noise;
|
subspace[b] = noise;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
GlobalOrthonormalise();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,7 @@ Author: Peter Boyle <pboyle@bnl.gov>
|
|||||||
|
|
||||||
#include <Grid/lattice/PaddedCell.h>
|
#include <Grid/lattice/PaddedCell.h>
|
||||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||||
|
#include <Grid/algorithms/deflation/MultiRHSBlockProject.h>
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
NAMESPACE_BEGIN(Grid);
|
||||||
|
|
||||||
@@ -66,6 +67,10 @@ public:
|
|||||||
std::vector<CoarseMatrix> _Adag;
|
std::vector<CoarseMatrix> _Adag;
|
||||||
std::vector<CoarseVector> MultTemporaries;
|
std::vector<CoarseVector> MultTemporaries;
|
||||||
|
|
||||||
|
int64_t MultCalls;
|
||||||
|
double MultFlopsAccum;
|
||||||
|
double MultUsecAccum;
|
||||||
|
|
||||||
///////////////////////
|
///////////////////////
|
||||||
// Interface
|
// Interface
|
||||||
///////////////////////
|
///////////////////////
|
||||||
@@ -104,19 +109,20 @@ public:
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
GeneralCoarsenedMatrix(NonLocalStencilGeometry &_geom,GridBase *FineGrid, GridCartesian * CoarseGrid)
|
GeneralCoarsenedMatrix(NonLocalStencilGeometry &_geom,GridBase *FineGrid, GridCartesian * CoarseGrid,int _herm=1)
|
||||||
: geom(_geom),
|
: geom(_geom),
|
||||||
_FineGrid(FineGrid),
|
_FineGrid(FineGrid),
|
||||||
_CoarseGrid(CoarseGrid),
|
_CoarseGrid(CoarseGrid),
|
||||||
hermitian(1),
|
hermitian(_herm),
|
||||||
Cell(_geom.Depth(),_CoarseGrid),
|
Cell(_geom.Depth(),_CoarseGrid),
|
||||||
Stencil(Cell.grids.back(),geom.shifts)
|
Stencil(Cell.grids.back(),geom.shifts),
|
||||||
|
MultCalls(0), MultFlopsAccum(0.0), MultUsecAccum(0.0)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
int npoint = _geom.npoint;
|
int npoint = _geom.npoint;
|
||||||
}
|
}
|
||||||
_A.resize(geom.npoint,CoarseGrid);
|
_A.resize(geom.npoint,CoarseGrid);
|
||||||
// _Adag.resize(geom.npoint,CoarseGrid);
|
if ( !hermitian ) _Adag.resize(geom.npoint,CoarseGrid);
|
||||||
}
|
}
|
||||||
void M (const CoarseVector &in, CoarseVector &out)
|
void M (const CoarseVector &in, CoarseVector &out)
|
||||||
{
|
{
|
||||||
@@ -124,10 +130,10 @@ public:
|
|||||||
}
|
}
|
||||||
void Mdag (const CoarseVector &in, CoarseVector &out)
|
void Mdag (const CoarseVector &in, CoarseVector &out)
|
||||||
{
|
{
|
||||||
GRID_ASSERT(hermitian);
|
if(hermitian)
|
||||||
Mult(_A,in,out);
|
Mult(_A,in,out);
|
||||||
// if ( hermitian ) M(in,out);
|
else
|
||||||
// else Mult(_Adag,in,out);
|
Mult(_Adag,in,out);
|
||||||
}
|
}
|
||||||
void Mult (std::vector<CoarseMatrix> &A,const CoarseVector &in, CoarseVector &out)
|
void Mult (std::vector<CoarseMatrix> &A,const CoarseVector &in, CoarseVector &out)
|
||||||
{
|
{
|
||||||
@@ -227,25 +233,24 @@ public:
|
|||||||
text+=usecond();
|
text+=usecond();
|
||||||
ttot+=usecond();
|
ttot+=usecond();
|
||||||
|
|
||||||
std::cout << GridLogPerformance<<"Coarse 1rhs Mult Aviews "<<tviews<<" us"<<std::endl;
|
MultCalls++;
|
||||||
std::cout << GridLogPerformance<<"Coarse Mult exch "<<texch<<" us"<<std::endl;
|
MultFlopsAccum += flops;
|
||||||
std::cout << GridLogPerformance<<"Coarse Mult mult "<<tmult<<" us"<<std::endl;
|
MultUsecAccum += ttot;
|
||||||
std::cout << GridLogPerformance<<" of which mult2 "<<tmult2<<" us"<<std::endl;
|
std::cout << GridLogPerformance
|
||||||
std::cout << GridLogPerformance<<"Coarse Mult ext "<<text<<" us"<<std::endl;
|
<< "Coarse Mult call " << MultCalls
|
||||||
std::cout << GridLogPerformance<<"Coarse Mult temps "<<ttemps<<" us"<<std::endl;
|
<< " tot " << ttot << " us"
|
||||||
std::cout << GridLogPerformance<<"Coarse Mult copy "<<tcopy<<" us"<<std::endl;
|
<< " kernel " << tmult << " us"
|
||||||
std::cout << GridLogPerformance<<"Coarse Mult tot "<<ttot<<" us"<<std::endl;
|
<< " kernel " << flops/tmult*1e-3 << " GFlop/s"
|
||||||
// std::cout << GridLogPerformance<<std::endl;
|
<< " overall " << MultFlopsAccum/MultUsecAccum*1e-3 << " GFlop/s (cumul)"
|
||||||
std::cout << GridLogPerformance<<"Coarse Kernel flops "<< flops<<std::endl;
|
<< " bw " << bytes/tmult*1e-3 << " GB/s"
|
||||||
std::cout << GridLogPerformance<<"Coarse Kernel flop/s "<< flops/tmult<<" mflop/s"<<std::endl;
|
<< std::endl;
|
||||||
std::cout << GridLogPerformance<<"Coarse Kernel bytes/s "<< bytes/tmult<<" MB/s"<<std::endl;
|
|
||||||
std::cout << GridLogPerformance<<"Coarse overall flops/s "<< flops/ttot<<" mflop/s"<<std::endl;
|
|
||||||
std::cout << GridLogPerformance<<"Coarse total bytes "<< bytes/1e6<<" MB"<<std::endl;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void PopulateAdag(void)
|
void PopulateAdag(void)
|
||||||
{
|
{
|
||||||
|
#if 0
|
||||||
|
// Serial global peek/poke reference implementation
|
||||||
for(int64_t bidx=0;bidx<CoarseGrid()->gSites() ;bidx++){
|
for(int64_t bidx=0;bidx<CoarseGrid()->gSites() ;bidx++){
|
||||||
Coordinate bcoor;
|
Coordinate bcoor;
|
||||||
CoarseGrid()->GlobalIndexToGlobalCoor(bidx,bcoor);
|
CoarseGrid()->GlobalIndexToGlobalCoor(bidx,bcoor);
|
||||||
@@ -262,6 +267,36 @@ public:
|
|||||||
pokeSite(adj(link),_Adag[pp],bcoor);
|
pokeSite(adj(link),_Adag[pp],bcoor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
// Parallel: _Adag[pp](x) = adj( _A[p](x + s_pp) ), pp = Reverse(p), s_pp = -s_p.
|
||||||
|
// The neighbour fetch reuses the same padded-cell + stencil machinery as Mult,
|
||||||
|
// reading one matrix element per coalesced access so no whole site matrix
|
||||||
|
// (230KB at nbasis=60) ever lands on a GPU thread stack (HIP limit 128KB).
|
||||||
|
// Halo sites compute garbage neighbours; Cell.Extract discards them.
|
||||||
|
// Must run on the unpadded _A, i.e. before ExchangeCoarseLinks.
|
||||||
|
const int Nsimd = CComplex::Nsimd();
|
||||||
|
for(int p=0;p<geom.npoint;p++){
|
||||||
|
int pp = geom.Reverse(p);
|
||||||
|
CoarseMatrix Apad = Cell.ExchangePeriodic(_A[p]);
|
||||||
|
CoarseMatrix Dpad(Apad.Grid());
|
||||||
|
int64_t osites = Apad.Grid()->oSites();
|
||||||
|
{
|
||||||
|
autoView( Apad_v , Apad, AcceleratorRead);
|
||||||
|
autoView( Dpad_v , Dpad, AcceleratorWriteDiscard);
|
||||||
|
autoView( Stencil_v, Stencil, AcceleratorRead);
|
||||||
|
accelerator_for(sj, osites*nbasis, Nsimd, {
|
||||||
|
int32_t ss = sj/nbasis;
|
||||||
|
int32_t j = sj%nbasis;
|
||||||
|
auto SE = Stencil_v.GetEntry(pp,ss);
|
||||||
|
for(int i=0;i<nbasis;i++){
|
||||||
|
auto z = coalescedReadGeneralPermute(Apad_v[SE->_offset](i,j),SE->_permute,Nd);
|
||||||
|
coalescedWrite(Dpad_v[ss](j,i),conjugate(z));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_Adag[pp] = Cell.Extract(Dpad);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
@@ -417,10 +452,19 @@ public:
|
|||||||
int osites=CoarseGrid()->oSites();
|
int osites=CoarseGrid()->oSites();
|
||||||
autoView( A_v , _A[k], AcceleratorWrite);
|
autoView( A_v , _A[k], AcceleratorWrite);
|
||||||
autoView( FT_v , FT[k], AcceleratorRead);
|
autoView( FT_v , FT[k], AcceleratorRead);
|
||||||
accelerator_for(sss, osites, 1, {
|
accelerator_for(sss, osites, nbasis, {
|
||||||
|
#ifdef GRID_SIMT
|
||||||
|
int j = acceleratorSIMTlane(nbasis);
|
||||||
|
A_v[sss](i,j) = FT_v[sss](j);
|
||||||
|
#else
|
||||||
|
// CPU build: acceleratorSIMTlane()==0 -- an un-looped SIMT tensor
|
||||||
|
// index writes ONLY j=0 and silently drops the other nbasis-1
|
||||||
|
// columns (caught by Test_schur_dense_coarse import certificate,
|
||||||
|
// 2026-08-14). Loop explicitly.
|
||||||
for(int j=0;j<nbasis;j++){
|
for(int j=0;j<nbasis;j++){
|
||||||
A_v[sss](i,j) = FT_v[sss](j);
|
A_v[sss](i,j) = FT_v[sss](j);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
tinv+=usecond();
|
tinv+=usecond();
|
||||||
@@ -428,8 +472,8 @@ public:
|
|||||||
|
|
||||||
// Only needed if nonhermitian
|
// Only needed if nonhermitian
|
||||||
if ( ! hermitian ) {
|
if ( ! hermitian ) {
|
||||||
// std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
||||||
// PopulateAdag();
|
PopulateAdag();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to write something to populate Adag from A
|
// Need to write something to populate Adag from A
|
||||||
@@ -517,13 +561,9 @@ public:
|
|||||||
// Now compute the matrix elements of linop between the orthonormal
|
// Now compute the matrix elements of linop between the orthonormal
|
||||||
// set of vectors.
|
// set of vectors.
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
FineField phaV(grid); // Phased block basis vector
|
|
||||||
FineField MphaV(grid);// Matrix applied
|
|
||||||
std::vector<FineComplexField> phaF(npoint,grid);
|
std::vector<FineComplexField> phaF(npoint,grid);
|
||||||
std::vector<CoarseComplexField> pha(npoint,CoarseGrid());
|
std::vector<CoarseComplexField> pha(npoint,CoarseGrid());
|
||||||
|
|
||||||
CoarseVector coarseInner(CoarseGrid());
|
|
||||||
|
|
||||||
typedef typename CComplex::scalar_type SComplex;
|
typedef typename CComplex::scalar_type SComplex;
|
||||||
FineComplexField one(grid); one=SComplex(1.0);
|
FineComplexField one(grid); one=SComplex(1.0);
|
||||||
FineComplexField zz(grid); zz = Zero();
|
FineComplexField zz(grid); zz = Zero();
|
||||||
@@ -546,33 +586,48 @@ public:
|
|||||||
}
|
}
|
||||||
tphase+=usecond();
|
tphase+=usecond();
|
||||||
|
|
||||||
|
// Import basis into BLAS layout once; blockProject then reads it once per
|
||||||
|
// basis vector rather than once per (i,p) as in scalar blockProject.
|
||||||
|
// Process all npoint in a single batch.
|
||||||
|
MultiRHSBlockProject<FineField> Projector;
|
||||||
|
Projector.Allocate(nbasis, grid, CoarseGrid());
|
||||||
|
Projector.ImportBasis(U.subspace);
|
||||||
|
|
||||||
|
std::vector<FineField> phaV_batch(npoint, grid);
|
||||||
|
std::vector<FineField> MphaV_batch(npoint, grid);
|
||||||
|
std::vector<CoarseVector> proj_batch(npoint, CoarseGrid());
|
||||||
std::vector<CoarseVector> ComputeProj(npoint, CoarseGrid());
|
std::vector<CoarseVector> ComputeProj(npoint, CoarseGrid());
|
||||||
std::vector<CoarseVector> FT(npoint, CoarseGrid());
|
std::vector<CoarseVector> FT(npoint, CoarseGrid());
|
||||||
|
|
||||||
|
// Pre-allocate BLAS_F and BLAS_C to avoid repeated hipMalloc/hipFree of
|
||||||
|
// ~5.6 GB per blockProject call, which hangs on ROCm for large allocations.
|
||||||
|
Projector.BLAS_F.resize(Projector.fine_vol * Projector.words * npoint);
|
||||||
|
Projector.BLAS_C.resize(Projector.coarse_vol * nbasis * npoint);
|
||||||
|
|
||||||
for(int i=0;i<nbasis;i++){// Loop over basis vectors
|
for(int i=0;i<nbasis;i++){// Loop over basis vectors
|
||||||
|
accelerator_barrier(); // ensure prior iteration's async writes are retired
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl;
|
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl;
|
||||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
|
||||||
tphaseBZ-=usecond();
|
tphaseBZ-=usecond();
|
||||||
phaV = phaF[p]*V.subspace[i];
|
for(int p=0;p<npoint;p++)
|
||||||
|
phaV_batch[p] = phaF[p] * V.subspace[i];
|
||||||
tphaseBZ+=usecond();
|
tphaseBZ+=usecond();
|
||||||
|
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" phaseBZ done"<< std::endl;
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// Multiple phased subspace vector by matrix and project to subspace
|
|
||||||
// Remove local bulk phase to leave relative phases
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
tmat-=usecond();
|
tmat-=usecond();
|
||||||
linop.Op(phaV,MphaV);
|
for(int p=0;p<npoint;p++)
|
||||||
|
linop.Op(phaV_batch[p], MphaV_batch[p]);
|
||||||
tmat+=usecond();
|
tmat+=usecond();
|
||||||
// std::cout << i << " " <<p << " MphaV "<<norm2(MphaV)<<" "<<norm2(phaV)<<std::endl;
|
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" mat done"<< std::endl;
|
||||||
|
|
||||||
|
// One batched GEMM reads BLAS_V once for all npoint vectors.
|
||||||
tproj-=usecond();
|
tproj-=usecond();
|
||||||
blockProject(coarseInner,MphaV,U.subspace);
|
Projector.blockProject(MphaV_batch, proj_batch);
|
||||||
coarseInner = conjugate(pha[p]) * coarseInner;
|
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" blockProject done"<< std::endl;
|
||||||
|
for(int p=0;p<npoint;p++)
|
||||||
ComputeProj[p] = coarseInner;
|
ComputeProj[p] = conjugate(pha[p]) * proj_batch[p];
|
||||||
tproj+=usecond();
|
tproj+=usecond();
|
||||||
// std::cout << i << " " <<p << " ComputeProj "<<norm2(ComputeProj[p])<<std::endl;
|
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" proj done"<< std::endl;
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
tinv-=usecond();
|
tinv-=usecond();
|
||||||
for(int k=0;k<npoint;k++){
|
for(int k=0;k<npoint;k++){
|
||||||
@@ -584,10 +639,19 @@ public:
|
|||||||
int osites=CoarseGrid()->oSites();
|
int osites=CoarseGrid()->oSites();
|
||||||
autoView( A_v , _A[k], AcceleratorWrite);
|
autoView( A_v , _A[k], AcceleratorWrite);
|
||||||
autoView( FT_v , FT[k], AcceleratorRead);
|
autoView( FT_v , FT[k], AcceleratorRead);
|
||||||
accelerator_for(sss, osites, 1, {
|
accelerator_for(sss, osites, nbasis, {
|
||||||
|
#ifdef GRID_SIMT
|
||||||
|
int j = acceleratorSIMTlane(nbasis);
|
||||||
|
A_v[sss](i,j) = FT_v[sss](j);
|
||||||
|
#else
|
||||||
|
// CPU build: acceleratorSIMTlane()==0 -- an un-looped SIMT tensor
|
||||||
|
// index writes ONLY j=0 and silently drops the other nbasis-1
|
||||||
|
// columns (caught by Test_schur_dense_coarse import certificate,
|
||||||
|
// 2026-08-14). Loop explicitly.
|
||||||
for(int j=0;j<nbasis;j++){
|
for(int j=0;j<nbasis;j++){
|
||||||
A_v[sss](i,j) = FT_v[sss](j);
|
A_v[sss](i,j) = FT_v[sss](j);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
tinv+=usecond();
|
tinv+=usecond();
|
||||||
@@ -595,13 +659,13 @@ public:
|
|||||||
|
|
||||||
// Only needed if nonhermitian
|
// Only needed if nonhermitian
|
||||||
if ( ! hermitian ) {
|
if ( ! hermitian ) {
|
||||||
// std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
||||||
// PopulateAdag();
|
PopulateAdag();
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
// for(int p=0;p<geom.npoint;p++){
|
||||||
std::cout << " _A["<<p<<"] "<<norm2(_A[p])<<std::endl;
|
// std::cout << " _A["<<p<<"] "<<norm2(_A[p])<<std::endl;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Need to write something to populate Adag from A
|
// Need to write something to populate Adag from A
|
||||||
ExchangeCoarseLinks();
|
ExchangeCoarseLinks();
|
||||||
@@ -616,7 +680,7 @@ public:
|
|||||||
void ExchangeCoarseLinks(void){
|
void ExchangeCoarseLinks(void){
|
||||||
for(int p=0;p<geom.npoint;p++){
|
for(int p=0;p<geom.npoint;p++){
|
||||||
_A[p] = Cell.ExchangePeriodic(_A[p]);
|
_A[p] = Cell.ExchangePeriodic(_A[p]);
|
||||||
// _Adag[p]= Cell.ExchangePeriodic(_Adag[p]);
|
if ( !hermitian ) _Adag[p]= Cell.ExchangePeriodic(_Adag[p]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void Mdiag (const Field &in, Field &out){ GRID_ASSERT(0);};
|
virtual void Mdiag (const Field &in, Field &out){ GRID_ASSERT(0);};
|
||||||
|
|||||||
@@ -0,0 +1,851 @@
|
|||||||
|
/*************************************************************************************
|
||||||
|
|
||||||
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
|
Source file: ./lib/algorithms/GeneralCoarsenedMatrixMultiRHS.h
|
||||||
|
|
||||||
|
Copyright (C) 2015
|
||||||
|
|
||||||
|
Author: Peter Boyle <pboyle@bnl.gov>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
|
*************************************************************************************/
|
||||||
|
/* END LEGAL */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
NAMESPACE_BEGIN(Grid);
|
||||||
|
|
||||||
|
|
||||||
|
// Fine Object == (per site) type of fine field
|
||||||
|
// nbasis == number of deflation vectors
|
||||||
|
template<class Fobj,class CComplex,int nbasis>
|
||||||
|
class MultiGeneralCoarsenedOperatorV2 : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
|
||||||
|
public:
|
||||||
|
typedef typename CComplex::scalar_object SComplex;
|
||||||
|
typedef GeneralCoarsenedMatrix<Fobj,CComplex,nbasis> GeneralCoarseOp;
|
||||||
|
typedef MultiGeneralCoarsenedOperatorV2<Fobj,CComplex,nbasis> MultiGeneralCoarseOp;
|
||||||
|
|
||||||
|
typedef iVector<CComplex,nbasis > siteVector;
|
||||||
|
typedef iMatrix<CComplex,nbasis > siteMatrix;
|
||||||
|
typedef iVector<SComplex,nbasis > calcVector;
|
||||||
|
typedef iMatrix<SComplex,nbasis > calcMatrix;
|
||||||
|
typedef Lattice<iScalar<CComplex> > CoarseComplexField;
|
||||||
|
typedef Lattice<siteVector> CoarseVector;
|
||||||
|
typedef Lattice<iMatrix<CComplex,nbasis > > CoarseMatrix;
|
||||||
|
typedef iMatrix<CComplex,nbasis > Cobj;
|
||||||
|
typedef iVector<CComplex,nbasis > Cvec;
|
||||||
|
typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field
|
||||||
|
typedef Lattice<Fobj > FineField;
|
||||||
|
typedef CoarseVector Field;
|
||||||
|
|
||||||
|
// Block operations on the fine vectors carry the fine layout, which need
|
||||||
|
// not be the coarse one
|
||||||
|
typedef decltype(innerProduct(Fobj(),Fobj())) FineInner;
|
||||||
|
typedef Lattice<FineInner> FineComplexField;
|
||||||
|
typedef Lattice<FineInner> BlockComplexField;
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
// Data members
|
||||||
|
//
|
||||||
|
// Nrhs independent: the D dimensional coarse grid, the geometry, the padded
|
||||||
|
// cell that supplies the stencil grid, the stencil, and the matrix elements.
|
||||||
|
//
|
||||||
|
// Nrhs dependent: the D+1 grid, its padded cell, and the BLAS B/C buffers
|
||||||
|
// with their pointer tables. Owned by SetNRHS().
|
||||||
|
////////////////////
|
||||||
|
GridCartesian * _CoarseGrid; // D dimensional
|
||||||
|
NonLocalStencilGeometry geom;
|
||||||
|
NonLocalStencilGeometry geom_srhs;
|
||||||
|
PaddedCell CellD; // D dimensional, supplies stencil grid
|
||||||
|
GeneralLocalStencil Stencil; // D dimensional
|
||||||
|
|
||||||
|
int _Nrhs;
|
||||||
|
GridCartesian * _CoarseGridMulti; // D+1 dimensional, SetNRHS
|
||||||
|
PaddedCell * CellMulti; // D+1 dimensional, SetNRHS
|
||||||
|
|
||||||
|
deviceVector<calcVector> BLAS_B;
|
||||||
|
deviceVector<calcVector> BLAS_C;
|
||||||
|
std::vector<deviceVector<calcMatrix> > BLAS_A;
|
||||||
|
|
||||||
|
std::vector<deviceVector<ComplexD *> > BLAS_AP;
|
||||||
|
std::vector<deviceVector<ComplexD *> > BLAS_BP;
|
||||||
|
deviceVector<ComplexD *> BLAS_CP;
|
||||||
|
|
||||||
|
///////////////////////
|
||||||
|
// Interface
|
||||||
|
///////////////////////
|
||||||
|
GridBase * Grid(void) { CheckGridSet(); return _CoarseGridMulti; };
|
||||||
|
GridCartesian * CoarseGrid(void) { CheckGridSet(); return _CoarseGridMulti; };
|
||||||
|
GridCartesian * CoarseGridD(void) { return _CoarseGrid; }; // lower dimensional grid
|
||||||
|
int Nrhs(void) { CheckGridSet(); return _Nrhs; };
|
||||||
|
|
||||||
|
void CheckGridSet(void)
|
||||||
|
{
|
||||||
|
if ( _CoarseGridMulti == nullptr ) {
|
||||||
|
std::cout << GridLogError
|
||||||
|
<< "MultiGeneralCoarsenedOperatorV2: the multiRHS grid has not been set."
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << GridLogError
|
||||||
|
<< " Call SetGrid(CoarseGridMulti) with the D+1 dimensional grid your"
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << GridLogError
|
||||||
|
<< " coarse vectors live on, before Grid(), Nrhs() or M()."
|
||||||
|
<< std::endl;
|
||||||
|
GRID_ASSERT(_CoarseGridMulti != nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can be used to do I/O on the operator matrices externally
|
||||||
|
void SetMatrix (int p,CoarseMatrix & A)
|
||||||
|
{
|
||||||
|
GRID_ASSERT(A.size()==geom_srhs.npoint);
|
||||||
|
GridtoBLAS(A[p],BLAS_A[p]);
|
||||||
|
}
|
||||||
|
void GetMatrix (int p,CoarseMatrix & A)
|
||||||
|
{
|
||||||
|
GRID_ASSERT(A.size()==geom_srhs.npoint);
|
||||||
|
BLAStoGrid(A[p],BLAS_A[p]);
|
||||||
|
}
|
||||||
|
void CopyMatrix (GeneralCoarseOp &_Op)
|
||||||
|
{
|
||||||
|
for(int p=0;p<geom.npoint;p++){
|
||||||
|
auto Aup = _Op.Cell.Extract(_Op._A[p]);
|
||||||
|
//Unpadded
|
||||||
|
GridtoBLAS(Aup,BLAS_A[p]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
void CheckMatrix (GeneralCoarseOp &_Op)
|
||||||
|
{
|
||||||
|
std::cout <<"************* Checking the little direc operator mRHS"<<std::endl;
|
||||||
|
for(int p=0;p<geom.npoint;p++){
|
||||||
|
//Unpadded
|
||||||
|
auto Aup = _Op.Cell.Extract(_Op._A[p]);
|
||||||
|
auto Ack = Aup;
|
||||||
|
BLAStoGrid(Ack,BLAS_A[p]);
|
||||||
|
std::cout << p<<" Ack "<<norm2(Ack)<<std::endl;
|
||||||
|
std::cout << p<<" Aup "<<norm2(Aup)<<std::endl;
|
||||||
|
}
|
||||||
|
std::cout <<"************* "<<std::endl;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor takes the D dimensional coarse grid. Everything built here
|
||||||
|
// is independent of Nrhs, in particular the matrix elements, which must
|
||||||
|
// survive a change of Nrhs untouched.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
MultiGeneralCoarsenedOperatorV2(NonLocalStencilGeometry &_geom,GridCartesian *CoarseGrid) :
|
||||||
|
_CoarseGrid(CoarseGrid),
|
||||||
|
geom_srhs(_geom),
|
||||||
|
geom(CoarseGrid,_geom.hops,_geom.skip),
|
||||||
|
CellD(geom.Depth(),CoarseGrid),
|
||||||
|
Stencil(CellD.grids.back(),geom.shifts), // D dimensional padded cell stencil
|
||||||
|
_Nrhs(-1),
|
||||||
|
_CoarseGridMulti(nullptr),
|
||||||
|
CellMulti(nullptr)
|
||||||
|
{
|
||||||
|
int32_t unpadded_sites = _CoarseGrid->lSites();
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
// Matrix elements and their pointer table
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
BLAS_A.resize(geom.npoint);
|
||||||
|
BLAS_AP.resize(geom.npoint);
|
||||||
|
for(int p=0;p<geom.npoint;p++){
|
||||||
|
BLAS_A[p].resize (unpadded_sites); // no ghost zone, npoint elements
|
||||||
|
BLAS_AP[p].resize(unpadded_sites);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Site identity mapping for A
|
||||||
|
for(int p=0;p<geom.npoint;p++){
|
||||||
|
for(int ss=0;ss<unpadded_sites;ss++){
|
||||||
|
ComplexD *ptr = (ComplexD *)&BLAS_A[p][ss];
|
||||||
|
acceleratorPut(BLAS_AP[p][ss],ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~MultiGeneralCoarsenedOperatorV2()
|
||||||
|
{
|
||||||
|
ReleaseGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Free everything SetGrid allocated. The D+1 grid is borrowed from the
|
||||||
|
// caller and is never deleted here. Safe to call repeatedly and before
|
||||||
|
// the destructor.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void ReleaseGrid(void)
|
||||||
|
{
|
||||||
|
if ( CellMulti != nullptr ) { delete CellMulti; CellMulti = nullptr; }
|
||||||
|
|
||||||
|
_CoarseGridMulti = nullptr; // borrowed, not owned
|
||||||
|
_Nrhs = -1;
|
||||||
|
|
||||||
|
BLAS_B.resize(0);
|
||||||
|
BLAS_C.resize(0);
|
||||||
|
for(int p=0;p<BLAS_BP.size();p++){
|
||||||
|
BLAS_BP[p].resize(0);
|
||||||
|
}
|
||||||
|
BLAS_BP.resize(0);
|
||||||
|
BLAS_CP.resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Everything that depends on Nrhs. Idempotent; lazy called on demand.
|
||||||
|
//
|
||||||
|
// The stencil lives on the D dimensional padded grid. PaddedCell only pads
|
||||||
|
// a dimension when it is distributed, and the rhs direction never is, so
|
||||||
|
// the D+1 padded grid is exactly Nrhs copies of the D dimensional padded
|
||||||
|
// grid with rhs innermost. The neighbour offset therefore carries an Nrhs
|
||||||
|
// factor, in the same way the Nsimd factor is carried.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void SetGrid(GridCartesian *CoarseGridMulti)
|
||||||
|
{
|
||||||
|
GRID_ASSERT(CoarseGridMulti != nullptr);
|
||||||
|
|
||||||
|
if ( CoarseGridMulti == _CoarseGridMulti ) return; // idempotent on identity
|
||||||
|
|
||||||
|
ReleaseGrid();
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
// The D+1 grid is supplied and owned by the caller. Two operators over
|
||||||
|
// the same coarse space must share one grid object or their fields will
|
||||||
|
// not conform, so this is never manufactured internally.
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
int nd = _CoarseGrid->_ndimension;
|
||||||
|
|
||||||
|
GRID_ASSERT(CoarseGridMulti->_ndimension == nd+1);
|
||||||
|
GRID_ASSERT(CoarseGridMulti->_processors[0] == 1); // rhs is not distributed
|
||||||
|
for(int d=0;d<nd;d++){
|
||||||
|
GRID_ASSERT(CoarseGridMulti->_fdimensions[d+1] == _CoarseGrid->_fdimensions[d]);
|
||||||
|
GRID_ASSERT(CoarseGridMulti->_processors [d+1] == _CoarseGrid->_processors [d]);
|
||||||
|
GRID_ASSERT(CoarseGridMulti->_simd_layout[d+1] == _CoarseGrid->_simd_layout[d]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_CoarseGridMulti = CoarseGridMulti;
|
||||||
|
_Nrhs = CoarseGridMulti->_fdimensions[0];
|
||||||
|
GRID_ASSERT(_Nrhs>=1);
|
||||||
|
|
||||||
|
int nrhs = _Nrhs;
|
||||||
|
|
||||||
|
CellMulti = new PaddedCell(geom.Depth(),_CoarseGridMulti);
|
||||||
|
|
||||||
|
int32_t padded_sites = CellD.grids.back()->lSites(); // D dimensional
|
||||||
|
int32_t unpadded_sites = _CoarseGrid->lSites(); // D dimensional
|
||||||
|
|
||||||
|
// The neighbour offset multiplication by nrhs is exact only if the D+1
|
||||||
|
// padded volume is nrhs copies of the D dimensional one. Check it.
|
||||||
|
GRID_ASSERT(CellMulti->grids.back()->lSites() == nrhs*padded_sites);
|
||||||
|
GRID_ASSERT(_CoarseGridMulti->lSites() == nrhs*unpadded_sites);
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
// Device data vector storage
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
BLAS_B.resize(nrhs *padded_sites); // includes ghost zone
|
||||||
|
BLAS_C.resize(nrhs *unpadded_sites); // no ghost zone
|
||||||
|
BLAS_BP.resize(geom.npoint);
|
||||||
|
for(int p=0;p<geom.npoint;p++){
|
||||||
|
BLAS_BP[p].resize(unpadded_sites);
|
||||||
|
}
|
||||||
|
BLAS_CP.resize(unpadded_sites);
|
||||||
|
|
||||||
|
// Site identity mapping for C
|
||||||
|
for(int ss=0;ss<unpadded_sites;ss++){
|
||||||
|
ComplexD *ptr = (ComplexD *)&BLAS_C[ss*nrhs];
|
||||||
|
acceleratorPut(BLAS_CP[ss],ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neighbour table is more complicated
|
||||||
|
int32_t j=0; // Interior point counter (unpadded)
|
||||||
|
for(int32_t s=0;s<padded_sites;s++){ // D volume, padded
|
||||||
|
int ghost_zone=0;
|
||||||
|
for(int32_t point = 0 ; point < geom.npoint; point++){
|
||||||
|
int i=s*geom.npoint+point;
|
||||||
|
if( Stencil._entries[i]._wrap ) { // stencil is indexed by the oSite of the D dim grid
|
||||||
|
ghost_zone=1; // If general stencil wrapped in any direction, wrap=1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ghost_zone==0) {
|
||||||
|
for(int32_t point = 0 ; point < geom.npoint; point++){
|
||||||
|
int i=s*geom.npoint+point;
|
||||||
|
int32_t nbr = Stencil._entries[i]._offset*CComplex::Nsimd(); // oSite -> lSite, D dim
|
||||||
|
nbr = nbr*nrhs; // D -> D+1, rhs innermost
|
||||||
|
GRID_ASSERT(nbr<BLAS_B.size());
|
||||||
|
ComplexD * ptr = (ComplexD *)&BLAS_B[nbr];
|
||||||
|
acceleratorPut(BLAS_BP[point][j],ptr); // neighbour indexing in ghost zone volume
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GRID_ASSERT(j==unpadded_sites);
|
||||||
|
}
|
||||||
|
template<class vobj> void GridtoBLAS(const Lattice<vobj> &from,deviceVector<typename vobj::scalar_object> &to)
|
||||||
|
{
|
||||||
|
typedef typename vobj::scalar_object sobj;
|
||||||
|
typedef typename vobj::scalar_type scalar_type;
|
||||||
|
typedef typename vobj::vector_type vector_type;
|
||||||
|
|
||||||
|
GridBase *Fg = from.Grid();
|
||||||
|
GRID_ASSERT(!Fg->_isCheckerBoarded);
|
||||||
|
int nd = Fg->_ndimension;
|
||||||
|
|
||||||
|
to.resize(Fg->lSites());
|
||||||
|
|
||||||
|
Coordinate LocalLatt = Fg->LocalDimensions();
|
||||||
|
size_t nsite = 1;
|
||||||
|
for(int i=0;i<nd;i++) nsite *= LocalLatt[i];
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// do the index calc on the GPU
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Coordinate f_ostride = Fg->_ostride;
|
||||||
|
Coordinate f_istride = Fg->_istride;
|
||||||
|
Coordinate f_rdimensions = Fg->_rdimensions;
|
||||||
|
|
||||||
|
autoView(from_v,from,AcceleratorRead);
|
||||||
|
auto to_v = &to[0];
|
||||||
|
|
||||||
|
const int words=sizeof(vobj)/sizeof(vector_type);
|
||||||
|
accelerator_for(idx,nsite,1,{
|
||||||
|
|
||||||
|
Coordinate from_coor, base;
|
||||||
|
Lexicographic::CoorFromIndex(base,idx,LocalLatt);
|
||||||
|
for(int i=0;i<nd;i++){
|
||||||
|
from_coor[i] = base[i];
|
||||||
|
}
|
||||||
|
int from_oidx = 0; for(int d=0;d<nd;d++) from_oidx+=f_ostride[d]*(from_coor[d]%f_rdimensions[d]);
|
||||||
|
int from_lane = 0; for(int d=0;d<nd;d++) from_lane+=f_istride[d]*(from_coor[d]/f_rdimensions[d]);
|
||||||
|
|
||||||
|
const vector_type* from = (const vector_type *)&from_v[from_oidx];
|
||||||
|
scalar_type* to = (scalar_type *)&to_v[idx];
|
||||||
|
|
||||||
|
scalar_type stmp;
|
||||||
|
for(int w=0;w<words;w++){
|
||||||
|
stmp = getlane(from[w], from_lane);
|
||||||
|
to[w] = stmp;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
template<class vobj> void BLAStoGrid(Lattice<vobj> &grid,deviceVector<typename vobj::scalar_object> &in)
|
||||||
|
{
|
||||||
|
typedef typename vobj::scalar_object sobj;
|
||||||
|
typedef typename vobj::scalar_type scalar_type;
|
||||||
|
typedef typename vobj::vector_type vector_type;
|
||||||
|
|
||||||
|
GridBase *Tg = grid.Grid();
|
||||||
|
GRID_ASSERT(!Tg->_isCheckerBoarded);
|
||||||
|
int nd = Tg->_ndimension;
|
||||||
|
|
||||||
|
GRID_ASSERT(in.size()==Tg->lSites());
|
||||||
|
|
||||||
|
Coordinate LocalLatt = Tg->LocalDimensions();
|
||||||
|
size_t nsite = 1;
|
||||||
|
for(int i=0;i<nd;i++) nsite *= LocalLatt[i];
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// do the index calc on the GPU
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Coordinate t_ostride = Tg->_ostride;
|
||||||
|
Coordinate t_istride = Tg->_istride;
|
||||||
|
Coordinate t_rdimensions = Tg->_rdimensions;
|
||||||
|
|
||||||
|
autoView(to_v,grid,AcceleratorWrite);
|
||||||
|
auto from_v = &in[0];
|
||||||
|
|
||||||
|
const int words=sizeof(vobj)/sizeof(vector_type);
|
||||||
|
accelerator_for(idx,nsite,1,{
|
||||||
|
|
||||||
|
Coordinate to_coor, base;
|
||||||
|
Lexicographic::CoorFromIndex(base,idx,LocalLatt);
|
||||||
|
for(int i=0;i<nd;i++){
|
||||||
|
to_coor[i] = base[i];
|
||||||
|
}
|
||||||
|
int to_oidx = 0; for(int d=0;d<nd;d++) to_oidx+=t_ostride[d]*(to_coor[d]%t_rdimensions[d]);
|
||||||
|
int to_lane = 0; for(int d=0;d<nd;d++) to_lane+=t_istride[d]*(to_coor[d]/t_rdimensions[d]);
|
||||||
|
|
||||||
|
vector_type* to = (vector_type *)&to_v[to_oidx];
|
||||||
|
scalar_type* from = (scalar_type *)&from_v[idx];
|
||||||
|
|
||||||
|
scalar_type stmp;
|
||||||
|
for(int w=0;w<words;w++){
|
||||||
|
stmp=from[w];
|
||||||
|
putlane(to[w], stmp, to_lane);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Shared by both CoarsenOperator variants
|
||||||
|
//
|
||||||
|
// conj(pha[block]) proj[k (which mom)][j (basis vec cpt)][block]
|
||||||
|
// = \sum_{l in ball} e^{i q_k . delta_l} < phi_{block,j} | MdagM | phi_{(block+delta_l),i} >
|
||||||
|
// = \sum_{l in ball} e^{iqk.delta_l} A_ji^{b.b+l}
|
||||||
|
// = M_{kl} A_ji^{b.b+l}
|
||||||
|
//
|
||||||
|
// Where q_k = delta_k . (2*M_PI/global_nb[mu])
|
||||||
|
// Then A{ji}^{b,b+l} = M^{-1}_{lm} ComputeProj_{m,b,i,j}
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void CoarsenFourierMatrix(GridBase *CoarseGrid,Eigen::MatrixXcd &invMkl)
|
||||||
|
{
|
||||||
|
const int npoint = geom_srhs.npoint;
|
||||||
|
Coordinate clatt = CoarseGrid->GlobalDimensions();
|
||||||
|
int Nd = CoarseGrid->Nd();
|
||||||
|
|
||||||
|
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
||||||
|
ComplexD ci(0.0,1.0);
|
||||||
|
for(int k=0;k<npoint;k++){ // Loop over momenta
|
||||||
|
for(int l=0;l<npoint;l++){ // Loop over nbr relative
|
||||||
|
ComplexD phase(0.0,0.0);
|
||||||
|
for(int mu=0;mu<Nd;mu++){
|
||||||
|
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
||||||
|
phase=phase+TwoPiL*geom_srhs.shifts[k][mu]*geom_srhs.shifts[l][mu];
|
||||||
|
}
|
||||||
|
phase=exp(phase*ci);
|
||||||
|
Mkl(k,l) = phase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
invMkl = Mkl.inverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// blockOrthogonalise and blockZAXPY are block operations on the fine
|
||||||
|
// vectors, using a coarse shaped field only as an index set. They need a
|
||||||
|
// grid carrying the fine SIMD layout, which the coarse space no longer
|
||||||
|
// does. Constructed local to the caller so it cannot be mistaken for the
|
||||||
|
// coarse grid.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void CoarsenBlockGridLayout(GridBase *grid,GridBase *CoarseGrid,
|
||||||
|
Coordinate &latt,Coordinate &simd,Coordinate &mpi)
|
||||||
|
{
|
||||||
|
int nd = CoarseGrid->_ndimension;
|
||||||
|
latt.resize(nd); simd.resize(nd); mpi.resize(nd);
|
||||||
|
for(int d=0;d<nd;d++){
|
||||||
|
latt[d] = CoarseGrid->_fdimensions[d];
|
||||||
|
simd[d] = grid->_simd_layout[d];
|
||||||
|
mpi [d] = CoarseGrid->_processors[d];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// D+1 coarse grid holding the batch, rhs innermost and unvectorised
|
||||||
|
void CoarsenBatchGridLayout(GridBase *CoarseGrid,int batch,
|
||||||
|
Coordinate &latt,Coordinate &simd,Coordinate &mpi)
|
||||||
|
{
|
||||||
|
latt.resize(1,batch); simd.resize(1,1); mpi.resize(1,1);
|
||||||
|
latt[0]=batch; simd[0]=1; mpi[0]=1;
|
||||||
|
for(int d=0;d<CoarseGrid->_ndimension;d++){
|
||||||
|
latt.push_back(CoarseGrid->_fdimensions[d]);
|
||||||
|
simd.push_back(CoarseGrid->_simd_layout[d]);
|
||||||
|
mpi .push_back(CoarseGrid->_processors[d]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// The Fourier inverse needs the phase in the coarse layout and the basis
|
||||||
|
// phasing needs it in the fine layout; each is built from its own
|
||||||
|
// coordinates rather than transferred.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void CoarsenPhases(GridBase *grid,GridBase *CoarseGrid,GridCartesian *BlockGrid,
|
||||||
|
std::vector<CoarseComplexField> &pha,
|
||||||
|
std::vector<FineComplexField> &phaF)
|
||||||
|
{
|
||||||
|
const int npoint = geom_srhs.npoint;
|
||||||
|
Coordinate clatt = CoarseGrid->GlobalDimensions();
|
||||||
|
int Nd = CoarseGrid->Nd();
|
||||||
|
ComplexD ci(0.0,1.0);
|
||||||
|
|
||||||
|
typedef typename CComplex::scalar_type SComplex;
|
||||||
|
FineComplexField one(grid); one=SComplex(1.0);
|
||||||
|
FineComplexField zz(grid); zz = Zero();
|
||||||
|
BlockComplexField pha_blk (BlockGrid);
|
||||||
|
BlockComplexField blk_coor(BlockGrid);
|
||||||
|
|
||||||
|
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
||||||
|
CoarseComplexField coor(CoarseGrid);
|
||||||
|
pha[p] =Zero();
|
||||||
|
pha_blk=Zero();
|
||||||
|
for(int mu=0;mu<Nd;mu++){
|
||||||
|
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
||||||
|
LatticeCoordinate(coor,mu);
|
||||||
|
pha[p] = pha[p] + (TwoPiL * geom_srhs.shifts[p][mu]) * coor;
|
||||||
|
LatticeCoordinate(blk_coor,mu);
|
||||||
|
pha_blk = pha_blk + (TwoPiL * geom_srhs.shifts[p][mu]) * blk_coor;
|
||||||
|
}
|
||||||
|
pha[p] =exp(pha[p] *ci);
|
||||||
|
pha_blk=exp(pha_blk*ci);
|
||||||
|
|
||||||
|
blockZAXPY(phaF[p],pha_blk,one,zz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Remove the bulk phase from the batch of coarse projections and
|
||||||
|
// accumulate the Fourier inverse into A. Both variants reach here with
|
||||||
|
// TmpProj in the same batch coarse order, so this is shared verbatim.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void CoarsenAccumulate(int p,int i0,int nbv,int batch,
|
||||||
|
Eigen::MatrixXcd &invMkl,
|
||||||
|
std::vector<CoarseComplexField> &pha,
|
||||||
|
CoarseComplexField &phaB,
|
||||||
|
CoarseVector &TmpProj,
|
||||||
|
std::vector<CoarseMatrix> &_A,
|
||||||
|
GridBase *CoarseGrid)
|
||||||
|
{
|
||||||
|
typedef typename CComplex::scalar_type SComplex;
|
||||||
|
const int npoint = geom_srhs.npoint;
|
||||||
|
|
||||||
|
for(int b=0;b<batch;b++) InsertSliceFast(pha[p],phaB,b,0);
|
||||||
|
TmpProj = conjugate(phaB)*TmpProj;
|
||||||
|
|
||||||
|
int osites=CoarseGrid->oSites();
|
||||||
|
for(int k=0;k<npoint;k++){
|
||||||
|
SComplex sc(invMkl(p,k).real(),invMkl(p,k).imag());
|
||||||
|
CComplex coef(sc);
|
||||||
|
autoView( A_v , _A[k], AcceleratorWrite);
|
||||||
|
autoView( TP_v , TmpProj, AcceleratorRead);
|
||||||
|
accelerator_for(sss, osites, 1, {
|
||||||
|
for(int b=0;b<nbv;b++){
|
||||||
|
for(int j=0;j<nbasis;j++){
|
||||||
|
A_v[sss](i0+b,j) = A_v[sss](i0+b,j) + coef*TP_v[b+batch*sss](j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoarsenReport(RealD tphase,RealD tphaseBZ,RealD tslice,
|
||||||
|
RealD tmat,RealD tproj,RealD tinv)
|
||||||
|
{
|
||||||
|
std::cout << GridLogMessage<<"CoarsenOperator phase "<<tphase<<" us"<<std::endl;
|
||||||
|
std::cout << GridLogMessage<<"CoarsenOperator phaseBZ "<<tphaseBZ<<" us"<<std::endl;
|
||||||
|
std::cout << GridLogMessage<<"CoarsenOperator slice "<<tslice <<" us"<<std::endl;
|
||||||
|
std::cout << GridLogMessage<<"CoarsenOperator mat "<<tmat <<" us"<<std::endl;
|
||||||
|
std::cout << GridLogMessage<<"CoarsenOperator proj "<<tproj<<" us"<<std::endl;
|
||||||
|
std::cout << GridLogMessage<<"CoarsenOperator inv "<<tinv<<" us"<<std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Coarsen a NATIVELY multiRHS fine operator.
|
||||||
|
//
|
||||||
|
// linop acts on the D+1 dimensional fine grid FineGridMulti, with the batch
|
||||||
|
// of phased basis vectors carried in the rhs direction. A single RHS
|
||||||
|
// operator can be promoted with MrhsPromotedOperator, but that pays an
|
||||||
|
// ExtractSlice/InsertSlice pair per rhs; prefer the single RHS variant
|
||||||
|
// below in that case.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
||||||
|
GridCartesian *FineGridMulti,
|
||||||
|
std::vector<FineField> &Subspace,
|
||||||
|
GridBase *CoarseGrid)
|
||||||
|
{
|
||||||
|
RealD tproj=0.0, tmat=0.0, tphase=0.0, tphaseBZ=0.0, tslice=0.0, tinv=0.0;
|
||||||
|
|
||||||
|
std::cout << GridLogMessage<< "GeneralCoarsenMatrixMrhs (multiRHS fine operator)"<< std::endl;
|
||||||
|
|
||||||
|
GRID_ASSERT(Subspace.size()==nbasis);
|
||||||
|
GridBase *grid = Subspace[0].Grid();
|
||||||
|
|
||||||
|
GRID_ASSERT(FineGridMulti->_ndimension == grid->_ndimension+1);
|
||||||
|
GRID_ASSERT(FineGridMulti->_processors[0] == 1);
|
||||||
|
for(int d=0;d<grid->_ndimension;d++){
|
||||||
|
GRID_ASSERT(FineGridMulti->_fdimensions[d+1] == grid->_fdimensions[d]);
|
||||||
|
GRID_ASSERT(FineGridMulti->_processors [d+1] == grid->_processors [d]);
|
||||||
|
}
|
||||||
|
int batch = FineGridMulti->_fdimensions[0];
|
||||||
|
|
||||||
|
Coordinate blatt,bsimd,bmpi;
|
||||||
|
CoarsenBlockGridLayout(grid,CoarseGrid,blatt,bsimd,bmpi);
|
||||||
|
GridCartesian BlockGrid(blatt,bsimd,bmpi);
|
||||||
|
|
||||||
|
BlockComplexField InnerProd(&BlockGrid);
|
||||||
|
blockOrthogonalise(InnerProd,Subspace);
|
||||||
|
|
||||||
|
MultiRHSBlockProject<Lattice<Fobj> > Projector;
|
||||||
|
Projector.Allocate(nbasis,grid,CoarseGrid);
|
||||||
|
Projector.ImportBasis(Subspace);
|
||||||
|
|
||||||
|
const int npoint = geom_srhs.npoint;
|
||||||
|
|
||||||
|
Eigen::MatrixXcd invMkl;
|
||||||
|
CoarsenFourierMatrix(CoarseGrid,invMkl);
|
||||||
|
|
||||||
|
FineField phaV(grid);
|
||||||
|
std::vector<FineComplexField> phaF(npoint,grid);
|
||||||
|
std::vector<CoarseComplexField> pha (npoint,CoarseGrid);
|
||||||
|
|
||||||
|
tphase=-usecond();
|
||||||
|
CoarsenPhases(grid,CoarseGrid,&BlockGrid,pha,phaF);
|
||||||
|
tphase+=usecond();
|
||||||
|
|
||||||
|
std::vector<CoarseMatrix> _A;
|
||||||
|
_A.resize(npoint,CoarseGrid);
|
||||||
|
for(int k=0;k<npoint;k++) _A[k] = Zero();
|
||||||
|
|
||||||
|
Coordinate cmlatt,cmsimd,cmmpi;
|
||||||
|
CoarsenBatchGridLayout(CoarseGrid,batch,cmlatt,cmsimd,cmmpi);
|
||||||
|
GridCartesian CoarseBatchGrid(cmlatt,cmsimd,cmmpi);
|
||||||
|
|
||||||
|
CoarseVector TmpProj(&CoarseBatchGrid);
|
||||||
|
CoarseComplexField phaB(&CoarseBatchGrid);
|
||||||
|
|
||||||
|
FineField hi_in (FineGridMulti);
|
||||||
|
FineField hi_out(FineGridMulti);
|
||||||
|
FineField zzF(grid); zzF = Zero();
|
||||||
|
|
||||||
|
for(int i0=0;i0<nbasis;i0+=batch){ // Loop over batches of basis vectors
|
||||||
|
|
||||||
|
int nbv = MIN(batch,nbasis-i0);
|
||||||
|
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i0<<"/"<<nbasis<< std::endl;
|
||||||
|
|
||||||
|
for(int p=0;p<npoint;p++){ // Loop over momenta
|
||||||
|
|
||||||
|
// One phase, applied to the whole batch. Tail slices are zeroed so
|
||||||
|
// the operator never sees undefined data.
|
||||||
|
for(int b=0;b<nbv;b++){
|
||||||
|
tphaseBZ-=usecond();
|
||||||
|
phaV = phaF[p]*Subspace[i0+b];
|
||||||
|
tphaseBZ+=usecond();
|
||||||
|
tslice-=usecond();
|
||||||
|
InsertSliceFast(phaV,hi_in,b,0);
|
||||||
|
tslice+=usecond();
|
||||||
|
}
|
||||||
|
tslice-=usecond();
|
||||||
|
for(int b=nbv;b<batch;b++){
|
||||||
|
InsertSliceFast(zzF,hi_in,b,0);
|
||||||
|
}
|
||||||
|
tslice+=usecond();
|
||||||
|
|
||||||
|
tmat-=usecond();
|
||||||
|
linop.Op(hi_in,hi_out);
|
||||||
|
tmat+=usecond();
|
||||||
|
|
||||||
|
tproj-=usecond();
|
||||||
|
Projector.blockProject(hi_out,TmpProj);
|
||||||
|
tproj+=usecond();
|
||||||
|
|
||||||
|
tinv-=usecond();
|
||||||
|
CoarsenAccumulate(p,i0,nbv,batch,invMkl,pha,phaB,TmpProj,_A,CoarseGrid);
|
||||||
|
tinv+=usecond();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int p=0;p<npoint;p++){
|
||||||
|
GridtoBLAS(_A[p],BLAS_A[p]);
|
||||||
|
}
|
||||||
|
CoarsenReport(tphase,tphaseBZ,tslice,tmat,tproj,tinv);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Coarsen a SINGLE RHS fine operator.
|
||||||
|
//
|
||||||
|
// No multiRHS packing: the operator is applied once per phased basis
|
||||||
|
// vector and the batch is assembled on the coarse side by the mixed
|
||||||
|
// blockProject, which takes a vector of fine fields and writes the batch
|
||||||
|
// coarse field the accumulate expects. Only nbv applications per momentum,
|
||||||
|
// so a batch that does not divide nbasis wastes nothing, and the live fine
|
||||||
|
// storage is batch fields rather than two D+1 fields of extent batch.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
||||||
|
std::vector<FineField> &Subspace,
|
||||||
|
GridBase *CoarseGrid,
|
||||||
|
int batch)
|
||||||
|
{
|
||||||
|
RealD tproj=0.0, tmat=0.0, tphase=0.0, tphaseBZ=0.0, tslice=0.0, tinv=0.0;
|
||||||
|
|
||||||
|
std::cout << GridLogMessage<< "GeneralCoarsenMatrixMrhs (single RHS fine operator)"<< std::endl;
|
||||||
|
|
||||||
|
GRID_ASSERT(Subspace.size()==nbasis);
|
||||||
|
GRID_ASSERT(batch>=1);
|
||||||
|
GridBase *grid = Subspace[0].Grid();
|
||||||
|
|
||||||
|
Coordinate blatt,bsimd,bmpi;
|
||||||
|
CoarsenBlockGridLayout(grid,CoarseGrid,blatt,bsimd,bmpi);
|
||||||
|
GridCartesian BlockGrid(blatt,bsimd,bmpi);
|
||||||
|
|
||||||
|
BlockComplexField InnerProd(&BlockGrid);
|
||||||
|
blockOrthogonalise(InnerProd,Subspace);
|
||||||
|
|
||||||
|
MultiRHSBlockProject<Lattice<Fobj> > Projector;
|
||||||
|
Projector.Allocate(nbasis,grid,CoarseGrid);
|
||||||
|
Projector.ImportBasis(Subspace);
|
||||||
|
|
||||||
|
const int npoint = geom_srhs.npoint;
|
||||||
|
|
||||||
|
Eigen::MatrixXcd invMkl;
|
||||||
|
CoarsenFourierMatrix(CoarseGrid,invMkl);
|
||||||
|
|
||||||
|
FineField phaV(grid);
|
||||||
|
std::vector<FineComplexField> phaF(npoint,grid);
|
||||||
|
std::vector<CoarseComplexField> pha (npoint,CoarseGrid);
|
||||||
|
|
||||||
|
tphase=-usecond();
|
||||||
|
CoarsenPhases(grid,CoarseGrid,&BlockGrid,pha,phaF);
|
||||||
|
tphase+=usecond();
|
||||||
|
|
||||||
|
std::vector<CoarseMatrix> _A;
|
||||||
|
_A.resize(npoint,CoarseGrid);
|
||||||
|
for(int k=0;k<npoint;k++) _A[k] = Zero();
|
||||||
|
|
||||||
|
Coordinate cmlatt,cmsimd,cmmpi;
|
||||||
|
CoarsenBatchGridLayout(CoarseGrid,batch,cmlatt,cmsimd,cmmpi);
|
||||||
|
GridCartesian CoarseBatchGrid(cmlatt,cmsimd,cmmpi);
|
||||||
|
|
||||||
|
CoarseVector TmpProj(&CoarseBatchGrid);
|
||||||
|
CoarseComplexField phaB(&CoarseBatchGrid);
|
||||||
|
|
||||||
|
std::vector<FineField> MphaV(batch,grid);
|
||||||
|
|
||||||
|
for(int i0=0;i0<nbasis;i0+=batch){ // Loop over batches of basis vectors
|
||||||
|
|
||||||
|
int nbv = MIN(batch,nbasis-i0);
|
||||||
|
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i0<<"/"<<nbasis<< std::endl;
|
||||||
|
|
||||||
|
for(int p=0;p<npoint;p++){ // Loop over momenta
|
||||||
|
|
||||||
|
for(int b=0;b<nbv;b++){
|
||||||
|
tphaseBZ-=usecond();
|
||||||
|
phaV = phaF[p]*Subspace[i0+b];
|
||||||
|
tphaseBZ+=usecond();
|
||||||
|
tmat-=usecond();
|
||||||
|
linop.Op(phaV,MphaV[b]);
|
||||||
|
tmat+=usecond();
|
||||||
|
}
|
||||||
|
// The accumulate reads only the first nbv slices, but the projector
|
||||||
|
// sees the whole vector, so the tail must not be undefined.
|
||||||
|
for(int b=nbv;b<batch;b++) MphaV[b] = Zero();
|
||||||
|
|
||||||
|
tproj-=usecond();
|
||||||
|
Projector.blockProject(MphaV,TmpProj);
|
||||||
|
tproj+=usecond();
|
||||||
|
|
||||||
|
tinv-=usecond();
|
||||||
|
CoarsenAccumulate(p,i0,nbv,batch,invMkl,pha,phaB,TmpProj,_A,CoarseGrid);
|
||||||
|
tinv+=usecond();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int p=0;p<npoint;p++){
|
||||||
|
GridtoBLAS(_A[p],BLAS_A[p]);
|
||||||
|
}
|
||||||
|
CoarsenReport(tphase,tphaseBZ,tslice,tmat,tproj,tinv);
|
||||||
|
}
|
||||||
|
void Mdag(const CoarseVector &in, CoarseVector &out)
|
||||||
|
{
|
||||||
|
this->M(in,out);
|
||||||
|
}
|
||||||
|
void M (const CoarseVector &in, CoarseVector &out)
|
||||||
|
{
|
||||||
|
// std::cout << GridLogMessage << "New Mrhs coarse"<<std::endl;
|
||||||
|
conformable(CoarseGrid(),in.Grid());
|
||||||
|
conformable(in.Grid(),out.Grid());
|
||||||
|
out.Checkerboard() = in.Checkerboard();
|
||||||
|
|
||||||
|
RealD t_tot;
|
||||||
|
RealD t_exch;
|
||||||
|
RealD t_GtoB;
|
||||||
|
RealD t_BtoG;
|
||||||
|
RealD t_mult;
|
||||||
|
|
||||||
|
CheckGridSet();
|
||||||
|
if ( in.Grid() != _CoarseGridMulti ) {
|
||||||
|
std::cout << GridLogError
|
||||||
|
<< "MultiGeneralCoarsenedOperatorV2::M called with a field on a"
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << GridLogError
|
||||||
|
<< " different grid object from the one given to SetGrid(). Two"
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << GridLogError
|
||||||
|
<< " grids of identical shape do not conform; share one object."
|
||||||
|
<< std::endl;
|
||||||
|
GRID_ASSERT(in.Grid() == _CoarseGridMulti);
|
||||||
|
}
|
||||||
|
|
||||||
|
t_tot=-usecond();
|
||||||
|
CoarseVector tin=in;
|
||||||
|
t_exch=-usecond();
|
||||||
|
CoarseVector pin = CellMulti->ExchangePeriodic(tin); //padded input
|
||||||
|
t_exch+=usecond();
|
||||||
|
|
||||||
|
CoarseVector pout(pin.Grid());
|
||||||
|
|
||||||
|
int npoint = geom.npoint;
|
||||||
|
typedef calcMatrix* Aview;
|
||||||
|
typedef LatticeView<Cvec> Vview;
|
||||||
|
|
||||||
|
const int Nsimd = CComplex::Nsimd();
|
||||||
|
|
||||||
|
int64_t nrhs =pin.Grid()->GlobalDimensions()[0];
|
||||||
|
GRID_ASSERT(nrhs>=1);
|
||||||
|
|
||||||
|
RealD flops,bytes;
|
||||||
|
int64_t osites=in.Grid()->oSites(); // unpadded
|
||||||
|
int64_t unpadded_vol = CoarseGrid()->lSites()/nrhs;
|
||||||
|
|
||||||
|
flops = 1.0* npoint * nbasis * nbasis * 8.0 * osites * CComplex::Nsimd();
|
||||||
|
bytes = 1.0*osites*sizeof(siteMatrix)*npoint/pin.Grid()->GlobalDimensions()[0]
|
||||||
|
+ 2.0*osites*sizeof(siteVector)*npoint;
|
||||||
|
|
||||||
|
|
||||||
|
t_GtoB=-usecond();
|
||||||
|
GridtoBLAS(pin,BLAS_B);
|
||||||
|
t_GtoB+=usecond();
|
||||||
|
|
||||||
|
GridBLAS BLAS;
|
||||||
|
|
||||||
|
t_mult=-usecond();
|
||||||
|
for(int p=0;p<geom.npoint;p++){
|
||||||
|
RealD c = 1.0;
|
||||||
|
if (p==0) c = 0.0;
|
||||||
|
ComplexD beta(c);
|
||||||
|
|
||||||
|
BLAS.gemmBatched(nbasis,nrhs,nbasis,
|
||||||
|
ComplexD(1.0),
|
||||||
|
BLAS_AP[p],
|
||||||
|
BLAS_BP[p],
|
||||||
|
ComplexD(c),
|
||||||
|
BLAS_CP);
|
||||||
|
}
|
||||||
|
BLAS.synchronise();
|
||||||
|
t_mult+=usecond();
|
||||||
|
|
||||||
|
t_BtoG=-usecond();
|
||||||
|
BLAStoGrid(out,BLAS_C);
|
||||||
|
t_BtoG+=usecond();
|
||||||
|
t_tot+=usecond();
|
||||||
|
/*
|
||||||
|
std::cout << GridLogMessage << "New Mrhs coarse DONE "<<std::endl;
|
||||||
|
std::cout << GridLogMessage<<"Coarse Mult exch "<<t_exch<<" us"<<std::endl;
|
||||||
|
std::cout << GridLogMessage<<"Coarse Mult mult "<<t_mult<<" us"<<std::endl;
|
||||||
|
std::cout << GridLogMessage<<"Coarse Mult GtoB "<<t_GtoB<<" us"<<std::endl;
|
||||||
|
std::cout << GridLogMessage<<"Coarse Mult BtoG "<<t_BtoG<<" us"<<std::endl;
|
||||||
|
std::cout << GridLogMessage<<"Coarse Mult tot "<<t_tot<<" us"<<std::endl;
|
||||||
|
*/
|
||||||
|
// std::cout << GridLogMessage<<std::endl;
|
||||||
|
// std::cout << GridLogMessage<<"Coarse Kernel flops "<< flops<<std::endl;
|
||||||
|
// std::cout << GridLogMessage<<"Coarse Kernel flop/s "<< flops/t_mult<<" mflop/s"<<std::endl;
|
||||||
|
// std::cout << GridLogMessage<<"Coarse Kernel bytes/s "<< bytes/t_mult/1000<<" GB/s"<<std::endl;
|
||||||
|
// std::cout << GridLogMessage<<"Coarse overall flops/s "<< flops/t_tot<<" mflop/s"<<std::endl;
|
||||||
|
// std::cout << GridLogMessage<<"Coarse total bytes "<< bytes/1e6<<" MB"<<std::endl;
|
||||||
|
};
|
||||||
|
virtual void Mdiag (const Field &in, Field &out){ GRID_ASSERT(0);};
|
||||||
|
virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);};
|
||||||
|
virtual void MdirAll (const Field &in, std::vector<Field> &out){assert(0);};
|
||||||
|
};
|
||||||
|
|
||||||
|
NAMESPACE_END(Grid);
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
/*************************************************************************************
|
||||||
|
|
||||||
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
|
Source file: ./lib/algorithms/multigrid/MrhsPromotedOperator.h
|
||||||
|
|
||||||
|
Copyright (C) 2026
|
||||||
|
|
||||||
|
Author: Peter Boyle <pboyle@bnl.gov>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
|
*************************************************************************************/
|
||||||
|
/* END LEGAL */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
NAMESPACE_BEGIN(Grid);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Present a D dimensional operator as a D+1 dimensional operator with Nrhs in
|
||||||
|
// dimension 0. Field type is unchanged; only the Grid differs, so this is a
|
||||||
|
// LinearOperatorBase and callers need no template parameter: a native mrhs
|
||||||
|
// operator derives from the same base and substitutes without a call site
|
||||||
|
// change.
|
||||||
|
//
|
||||||
|
// Slices in and out around the wrapped operator. No arithmetic beyond the
|
||||||
|
// wrapped call, but one ExtractSliceFast/InsertSliceFast pair per right hand
|
||||||
|
// side: data motion, not work.
|
||||||
|
//
|
||||||
|
// AdjOp is carried so that A^dag may be coarsened as a separate coarse
|
||||||
|
// operator when needed, rather than doubling coarse storage.
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<class Field>
|
||||||
|
class MrhsPromotedOperator : public LinearOperatorBase<Field>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
LinearOperatorBase<Field> &_LinOp;
|
||||||
|
GridBase *_LowGrid;
|
||||||
|
int _Nrhs;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
MrhsPromotedOperator(LinearOperatorBase<Field> &LinOp,GridBase *LowGrid,int Nrhs)
|
||||||
|
: _LinOp(LinOp), _LowGrid(LowGrid), _Nrhs(Nrhs)
|
||||||
|
{
|
||||||
|
GRID_ASSERT(_Nrhs>=1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GridBase *LowGrid(void) { return _LowGrid; }
|
||||||
|
int Nrhs(void) { return _Nrhs; }
|
||||||
|
|
||||||
|
// Reset on each call; retrieve and accumulate in the caller
|
||||||
|
RealD tslice;
|
||||||
|
RealD top;
|
||||||
|
|
||||||
|
void OpDiag (const Field &in, Field &out)
|
||||||
|
{
|
||||||
|
SliceLoop(in,out,[&](Field &i,Field &o){ _LinOp.OpDiag(i,o); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Op (const Field &in, Field &out)
|
||||||
|
{
|
||||||
|
SliceLoop(in,out,[&](Field &i,Field &o){ _LinOp.Op(i,o); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdjOp (const Field &in, Field &out)
|
||||||
|
{
|
||||||
|
SliceLoop(in,out,[&](Field &i,Field &o){ _LinOp.AdjOp(i,o); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void HermOp (const Field &in, Field &out)
|
||||||
|
{
|
||||||
|
SliceLoop(in,out,[&](Field &i,Field &o){ _LinOp.HermOp(i,o); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpDir (const Field &in, Field &out,int dir,int disp)
|
||||||
|
{
|
||||||
|
SliceLoop(in,out,[&](Field &i,Field &o){ _LinOp.OpDir(i,o,dir,disp); });
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
// Norms of the D+1 field are the sums over slices
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2)
|
||||||
|
{
|
||||||
|
Conformable(in,out);
|
||||||
|
Field lo_in (_LowGrid);
|
||||||
|
Field lo_out(_LowGrid);
|
||||||
|
n1=0.0;
|
||||||
|
n2=0.0;
|
||||||
|
for(int r=0;r<_Nrhs;r++){
|
||||||
|
RealD r1,r2;
|
||||||
|
ExtractSliceFast(lo_in,in,r,0);
|
||||||
|
_LinOp.HermOpAndNorm(lo_in,lo_out,r1,r2);
|
||||||
|
InsertSliceFast(lo_out,out,r,0);
|
||||||
|
n1=n1+r1;
|
||||||
|
n2=n2+r2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpDirAll(const Field &in, std::vector<Field> &out)
|
||||||
|
{
|
||||||
|
int npoint = out.size();
|
||||||
|
Field lo_in(_LowGrid);
|
||||||
|
std::vector<Field> lo_out(npoint,_LowGrid);
|
||||||
|
for(int r=0;r<_Nrhs;r++){
|
||||||
|
ExtractSliceFast(lo_in,in,r,0);
|
||||||
|
_LinOp.OpDirAll(lo_in,lo_out);
|
||||||
|
for(int p=0;p<npoint;p++){
|
||||||
|
InsertSliceFast(lo_out[p],out[p],r,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void Conformable(const Field &in,const Field &out)
|
||||||
|
{
|
||||||
|
conformable(in.Grid(),out.Grid());
|
||||||
|
GRID_ASSERT(in.Grid()->_ndimension == _LowGrid->_ndimension+1);
|
||||||
|
GRID_ASSERT(in.Grid()->_fdimensions[0] == _Nrhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Kernel>
|
||||||
|
void SliceLoop(const Field &in,Field &out,Kernel K)
|
||||||
|
{
|
||||||
|
Conformable(in,out);
|
||||||
|
Field lo_in (_LowGrid);
|
||||||
|
Field lo_out(_LowGrid);
|
||||||
|
tslice=0.0;
|
||||||
|
top=0.0;
|
||||||
|
for(int r=0;r<_Nrhs;r++){
|
||||||
|
tslice-=usecond();
|
||||||
|
ExtractSliceFast(lo_in,in,r,0);
|
||||||
|
tslice+=usecond();
|
||||||
|
top-=usecond();
|
||||||
|
K(lo_in,lo_out);
|
||||||
|
top+=usecond();
|
||||||
|
tslice-=usecond();
|
||||||
|
InsertSliceFast(lo_out,out,r,0);
|
||||||
|
tslice+=usecond();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
NAMESPACE_END(Grid);
|
||||||
@@ -32,3 +32,5 @@ Author: Peter Boyle <pboyle@bnl.gov>
|
|||||||
#include <Grid/algorithms/multigrid/CoarsenedMatrix.h>
|
#include <Grid/algorithms/multigrid/CoarsenedMatrix.h>
|
||||||
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrix.h>
|
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrix.h>
|
||||||
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHS.h>
|
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHS.h>
|
||||||
|
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHSV2.h>
|
||||||
|
#include <Grid/algorithms/multigrid/MrhsPromotedOperator.h>
|
||||||
|
|||||||
@@ -0,0 +1,617 @@
|
|||||||
|
/*************************************************************************************
|
||||||
|
|
||||||
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
|
Source file: RecursiveSchurInverse.h
|
||||||
|
|
||||||
|
Copyright (C) 2026
|
||||||
|
|
||||||
|
Author: Peter Boyle <pboyle@bnl.gov>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
|
*************************************************************************************/
|
||||||
|
/* END LEGAL */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Grid/algorithms/blas/BatchedBlas.h>
|
||||||
|
#include <Grid/algorithms/blas/BatchedInverse.h>
|
||||||
|
|
||||||
|
NAMESPACE_BEGIN(Grid);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// RecursiveSchurInverse: distributed dense inversion by recursive Schur
|
||||||
|
// complement over a binary rank-range tree.
|
||||||
|
//
|
||||||
|
// Contract: rank r owns global rows [rowStart[r], rowStart[r+1]) of an
|
||||||
|
// N x N matrix in rank-major ordering, and receives its rows of the
|
||||||
|
// inverse in the same layout. Consumes GridBase collectives, GridBLAS
|
||||||
|
// and GridBLASInverse only; Eigen reference backends permit CPU unit
|
||||||
|
// testing under mpirun (Test_schur_inverse).
|
||||||
|
//
|
||||||
|
// Arithmetic is fp64 throughout; the caller rounds once into fp32 storage.
|
||||||
|
//
|
||||||
|
// Execution: SPMD full-tree walk. Every rank makes the identical call
|
||||||
|
// sequence; data participation is ownership-gated; all collectives are
|
||||||
|
// world-wide, so no deadlock surface exists.
|
||||||
|
//
|
||||||
|
// Storage: BlockRows is column-major, ld = rows; element (i,j) at
|
||||||
|
// data[i + j*ld]; a column window is the contiguous slice at data[col0*ld].
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// My rows of a distributed dense matrix: rows x cols, column major, ld = rows.
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
class BlockRows
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
deviceVector<ComplexD> data;
|
||||||
|
int64_t rows;
|
||||||
|
int64_t cols;
|
||||||
|
int64_t ld;
|
||||||
|
|
||||||
|
BlockRows()
|
||||||
|
{
|
||||||
|
rows = 0;
|
||||||
|
cols = 0;
|
||||||
|
ld = 0;
|
||||||
|
}
|
||||||
|
void Resize(int64_t r, int64_t c)
|
||||||
|
{
|
||||||
|
rows = r;
|
||||||
|
cols = c;
|
||||||
|
ld = r;
|
||||||
|
data.resize((uint64_t)r*c);
|
||||||
|
}
|
||||||
|
ComplexD *ColumnWindow(int64_t col0)
|
||||||
|
{
|
||||||
|
GRID_ASSERT( col0 >= 0 );
|
||||||
|
GRID_ASSERT( col0 <= cols );
|
||||||
|
return &data[(uint64_t)col0*ld];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class RecursiveSchurInverse
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GridBase *grid; // world collectives only
|
||||||
|
int64_t N; // global matrix dimension
|
||||||
|
int P; // ranks
|
||||||
|
int me; // this rank
|
||||||
|
std::vector<int64_t> rowStart; // P+1 entries: rank-major row ownership
|
||||||
|
int64_t myRow0;
|
||||||
|
int64_t myNrows;
|
||||||
|
int64_t panelBytes; // gather panel budget (DENSE_PANEL_BYTES)
|
||||||
|
|
||||||
|
GridBLAS BLAS;
|
||||||
|
GridBLASInverse INV;
|
||||||
|
|
||||||
|
// Growth telemetry: one entry per merge node, walk order
|
||||||
|
std::vector<double> telNormB; // ||B||_F = ||A11inv A12||_F
|
||||||
|
std::vector<double> telSratio; // ||S||_F / ||A22||_F
|
||||||
|
double telLeafMaxInv; // max |(leaf inverse)_ij| over leaves
|
||||||
|
|
||||||
|
// Phase timers/counters, reported by ReportTelemetry
|
||||||
|
double tMemset; // device panel zero-fill
|
||||||
|
double tDeposit; // owner rows -> panel (device kernel)
|
||||||
|
double tAllreduce; // GlobalSumVector on device panels
|
||||||
|
double tGemm; // strided gemm + synchronise
|
||||||
|
double tLeaf; // leaf inversions
|
||||||
|
double tARmin; // fastest single panel collective
|
||||||
|
double tARmax; // slowest single panel collective
|
||||||
|
uint64_t bytesAllreduce;
|
||||||
|
uint64_t nAllreduce; // panel collectives
|
||||||
|
uint64_t nGatherGemm; // GatherGemm calls
|
||||||
|
|
||||||
|
// Persistent grow-only device panel; assembly and collectives are
|
||||||
|
// device-resident. Device builds require GPU-aware MPI.
|
||||||
|
deviceVector<ComplexD> dPanelBuf;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Ownership-table validation: a proper partition of [0,N).
|
||||||
|
// Static and communicator-free so synthetic tables unit-test directly.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
static void CheckRowStart(const std::vector<int64_t> &table, int64_t N)
|
||||||
|
{
|
||||||
|
int P = (int)table.size() - 1;
|
||||||
|
GRID_ASSERT( P >= 1 );
|
||||||
|
GRID_ASSERT( table[0] == 0 );
|
||||||
|
GRID_ASSERT( table[P] == N );
|
||||||
|
for(int r=0; r<P; r++)
|
||||||
|
{
|
||||||
|
GRID_ASSERT( table[r+1] >= table[r] ); // zero-row ranks permitted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Build the ownership table from each rank's local row count: zero-fill
|
||||||
|
// allgather (the standing comms idiom) then prefix sum. Every rank
|
||||||
|
// returns the identical table.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
static std::vector<int64_t> MakeRowStart(GridBase *g, int64_t myNrows)
|
||||||
|
{
|
||||||
|
int P = g->ProcessorCount();
|
||||||
|
int me = g->ThisRank();
|
||||||
|
|
||||||
|
std::vector<uint64_t> counts(P, 0);
|
||||||
|
counts[me] = (uint64_t)myNrows;
|
||||||
|
g->GlobalSumVector(&counts[0], P);
|
||||||
|
|
||||||
|
std::vector<int64_t> table(P+1);
|
||||||
|
table[0] = 0;
|
||||||
|
for(int r=0; r<P; r++)
|
||||||
|
{
|
||||||
|
table[r+1] = table[r] + (int64_t)counts[r];
|
||||||
|
}
|
||||||
|
CheckRowStart(table, table[P]);
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecursiveSchurInverse(GridBase *g,
|
||||||
|
int64_t N_,
|
||||||
|
std::vector<int64_t> &rowStart_,
|
||||||
|
int64_t panelBytes_)
|
||||||
|
{
|
||||||
|
grid = g;
|
||||||
|
N = N_;
|
||||||
|
P = g->ProcessorCount();
|
||||||
|
me = g->ThisRank();
|
||||||
|
rowStart = rowStart_;
|
||||||
|
panelBytes = panelBytes_;
|
||||||
|
|
||||||
|
GRID_ASSERT( (int)rowStart.size() == P+1 );
|
||||||
|
CheckRowStart(rowStart, N);
|
||||||
|
|
||||||
|
myRow0 = rowStart[me];
|
||||||
|
myNrows = rowStart[me+1] - rowStart[me];
|
||||||
|
|
||||||
|
telLeafMaxInv = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// The communication primitive.
|
||||||
|
//
|
||||||
|
// C(:, colC : colC+widthB) <- beta * C(:, colC : colC+widthB)
|
||||||
|
// + alpha * A(:, colA : colA+widthA) * Bsub
|
||||||
|
//
|
||||||
|
// Bsub is the widthA x widthB sub-block of a row-distributed operand
|
||||||
|
// owned by ranks [rB0, rB1): owner r contributes its rows of
|
||||||
|
// B(:, colB : colB+widthB) at sub-block row offset
|
||||||
|
// rowStart[r] - rowStart[rB0], gathered in panelBytes row-chunks by
|
||||||
|
// device zero-fill + deposit kernel + GlobalSumVector.
|
||||||
|
//
|
||||||
|
// Every rank calls; non-owners of B add zeros; ranks with A.rows == 0
|
||||||
|
// skip local compute but make every collective call. Column offsets
|
||||||
|
// are local buffer offsets -- non-participants pass 0.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void GatherGemm(ComplexD alpha,
|
||||||
|
BlockRows &A, int64_t colA, int64_t widthA,
|
||||||
|
int rB0, int rB1,
|
||||||
|
BlockRows &B, int64_t colB, int64_t widthB,
|
||||||
|
ComplexD beta,
|
||||||
|
BlockRows &C, int64_t colC)
|
||||||
|
{
|
||||||
|
GRID_ASSERT( rB0 >= 0 );
|
||||||
|
GRID_ASSERT( rB1 > rB0 );
|
||||||
|
GRID_ASSERT( rB1 <= P );
|
||||||
|
|
||||||
|
int64_t k = rowStart[rB1] - rowStart[rB0];
|
||||||
|
int64_t m = A.rows;
|
||||||
|
int64_t n = widthB;
|
||||||
|
GRID_ASSERT( widthA == k );
|
||||||
|
GRID_ASSERT( n >= 1 );
|
||||||
|
|
||||||
|
int owner = ( me >= rB0 ) && ( me < rB1 ) && ( B.rows > 0 );
|
||||||
|
int64_t myOff = 0;
|
||||||
|
if ( owner )
|
||||||
|
{
|
||||||
|
myOff = rowStart[me] - rowStart[rB0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( m > 0 )
|
||||||
|
{
|
||||||
|
GRID_ASSERT( colA + widthA <= A.cols );
|
||||||
|
GRID_ASSERT( colC + widthB <= C.cols );
|
||||||
|
GRID_ASSERT( C.rows == m );
|
||||||
|
}
|
||||||
|
|
||||||
|
nGatherGemm++;
|
||||||
|
GRID_TRACE("GatherGemm");
|
||||||
|
|
||||||
|
if ( owner )
|
||||||
|
{
|
||||||
|
GRID_ASSERT( colB + widthB <= B.cols );
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t kc = panelBytes / ( (int64_t)sizeof(ComplexD) * n );
|
||||||
|
if ( kc < 1 ) kc = 1;
|
||||||
|
if ( kc > k ) kc = k;
|
||||||
|
GRID_ASSERT( kc*n < 2147483647L ); // GlobalSumVector count is int
|
||||||
|
|
||||||
|
deviceVector<ComplexD> &dPanel = dPanelBuf;
|
||||||
|
if ( dPanel.size() < (uint64_t)kc*n ) dPanel.resize((uint64_t)kc*n);
|
||||||
|
deviceVector<ComplexD*> ap(1);
|
||||||
|
deviceVector<ComplexD*> bp(1);
|
||||||
|
deviceVector<ComplexD*> cp(1);
|
||||||
|
std::vector<ComplexD*> ptr(1);
|
||||||
|
|
||||||
|
for(int64_t k0=0; k0<k; k0+=kc)
|
||||||
|
{
|
||||||
|
int64_t kchunk = std::min(kc, k-k0);
|
||||||
|
|
||||||
|
// Zeroing must complete before MPI reads the panel; it is the sole
|
||||||
|
// producer on non-owner ranks.
|
||||||
|
tMemset -= usecond();
|
||||||
|
acceleratorMemSet(&dPanel[0], 0, (uint64_t)kchunk*n*sizeof(ComplexD));
|
||||||
|
tMemset += usecond();
|
||||||
|
if ( owner )
|
||||||
|
{
|
||||||
|
int64_t i0 = std::max(k0, myOff);
|
||||||
|
int64_t i1 = std::min(k0+kchunk, myOff+B.rows);
|
||||||
|
if ( i1 > i0 )
|
||||||
|
{
|
||||||
|
// Deposit my rows: strided block copy, len rows x n columns
|
||||||
|
int64_t len = i1-i0;
|
||||||
|
int64_t brows = B.rows;
|
||||||
|
int64_t dof = i0-k0;
|
||||||
|
int64_t sof = i0-myOff;
|
||||||
|
ComplexD *src = B.ColumnWindow(colB);
|
||||||
|
ComplexD *dst = &dPanel[0];
|
||||||
|
tDeposit -= usecond();
|
||||||
|
accelerator_for(idx, (uint64_t)(len*n), 1, {
|
||||||
|
int64_t j = idx / len;
|
||||||
|
int64_t i = idx % len;
|
||||||
|
dst[(uint64_t)(dof + i + j*kchunk)] = src[(uint64_t)(sof + i + j*brows)];
|
||||||
|
});
|
||||||
|
tDeposit += usecond();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double tar = -usecond();
|
||||||
|
grid->GlobalSumVector(&dPanel[0], (int)(kchunk*n));
|
||||||
|
tar += usecond();
|
||||||
|
tAllreduce += tar;
|
||||||
|
tARmin = std::min(tARmin, tar);
|
||||||
|
tARmax = std::max(tARmax, tar);
|
||||||
|
bytesAllreduce += (uint64_t)kchunk*n*sizeof(ComplexD);
|
||||||
|
nAllreduce++;
|
||||||
|
|
||||||
|
if ( m > 0 )
|
||||||
|
{
|
||||||
|
ComplexD beta_use = ( k0==0 ) ? beta : ComplexD(1.0,0.0);
|
||||||
|
|
||||||
|
ptr[0] = A.ColumnWindow(colA + k0);
|
||||||
|
acceleratorCopyToDevice(&ptr[0], &ap[0], sizeof(ComplexD*));
|
||||||
|
ptr[0] = &dPanel[0];
|
||||||
|
acceleratorCopyToDevice(&ptr[0], &bp[0], sizeof(ComplexD*));
|
||||||
|
ptr[0] = C.ColumnWindow(colC);
|
||||||
|
acceleratorCopyToDevice(&ptr[0], &cp[0], sizeof(ComplexD*));
|
||||||
|
|
||||||
|
tGemm -= usecond();
|
||||||
|
BLAS.gemmBatched(GridBLAS_OP_N, GridBLAS_OP_N,
|
||||||
|
(int)m, (int)n, (int)kchunk,
|
||||||
|
alpha, ap, (int)A.ld,
|
||||||
|
bp, (int)kchunk,
|
||||||
|
beta_use, cp, (int)C.ld);
|
||||||
|
BLAS.synchronise();
|
||||||
|
tGemm += usecond();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Local Frobenius norm-squared of a full-height column window.
|
||||||
|
// NO comms; callers GlobalSum the result. Host staging, setup-scale.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
double FrobNorm2Local(BlockRows &X, int64_t col0, int64_t w)
|
||||||
|
{
|
||||||
|
if ( X.rows == 0 ) return 0.0;
|
||||||
|
GRID_ASSERT( col0 + w <= X.cols );
|
||||||
|
uint64_t len = (uint64_t)X.rows*w;
|
||||||
|
std::vector<ComplexD> h(len);
|
||||||
|
acceleratorCopyFromDevice(X.ColumnWindow(col0), &h[0], len*sizeof(ComplexD));
|
||||||
|
// Member real()/imag(): portable across std::complex (CPU) and
|
||||||
|
// thrust::complex (HIP), where std::norm does not resolve.
|
||||||
|
double s = 0.0;
|
||||||
|
for(uint64_t i=0; i<len; i++)
|
||||||
|
{
|
||||||
|
double re = h[i].real();
|
||||||
|
double im = h[i].imag();
|
||||||
|
s += re*re + im*im;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// dst(:, dcol0 : dcol0+w) = - src(:, 0:w). Both operands have ld == rows
|
||||||
|
// so full-height windows are contiguous: flat elementwise device copy.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void NegateCopy(BlockRows &src, BlockRows &dst, int64_t dcol0, int64_t w)
|
||||||
|
{
|
||||||
|
GRID_ASSERT( src.rows == dst.rows );
|
||||||
|
GRID_ASSERT( w <= src.cols );
|
||||||
|
GRID_ASSERT( dcol0 + w <= dst.cols );
|
||||||
|
if ( src.rows == 0 ) return;
|
||||||
|
uint64_t len = (uint64_t)src.rows*w;
|
||||||
|
ComplexD *s = &src.data[0];
|
||||||
|
ComplexD *d = dst.ColumnWindow(dcol0);
|
||||||
|
accelerator_for(i, len, 1, {
|
||||||
|
d[i] = -s[i];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Leaf inversion: local, in place on the contiguous diagonal window.
|
||||||
|
// No collectives.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void LeafInvert(int64_t col0, int64_t width, BlockRows &Arows)
|
||||||
|
{
|
||||||
|
GRID_TRACE("SchurLeaf");
|
||||||
|
GRID_ASSERT( width == Arows.rows );
|
||||||
|
GRID_ASSERT( col0 + width <= Arows.cols );
|
||||||
|
int64_t w = width;
|
||||||
|
uint64_t len = (uint64_t)w*w;
|
||||||
|
tLeaf -= usecond();
|
||||||
|
|
||||||
|
deviceVector<ComplexD*> bp(1);
|
||||||
|
std::vector<ComplexD*> ptr(1);
|
||||||
|
ptr[0] = Arows.ColumnWindow(col0);
|
||||||
|
acceleratorCopyToDevice(&ptr[0], &bp[0], sizeof(ComplexD*));
|
||||||
|
INV.inverseBatched(w, bp);
|
||||||
|
|
||||||
|
// Telemetry: max |element| of the leaf inverse
|
||||||
|
{
|
||||||
|
std::vector<ComplexD> h(len);
|
||||||
|
acceleratorCopyFromDevice(Arows.ColumnWindow(col0), &h[0], len*sizeof(ComplexD));
|
||||||
|
double mx = 0.0;
|
||||||
|
for(uint64_t i=0; i<len; i++)
|
||||||
|
{
|
||||||
|
double re = h[i].real();
|
||||||
|
double im = h[i].imag();
|
||||||
|
mx = std::max(mx, re*re + im*im);
|
||||||
|
}
|
||||||
|
telLeafMaxInv = std::max(telLeafMaxInv, std::sqrt(mx));
|
||||||
|
}
|
||||||
|
tLeaf += usecond();
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// The recursion (plan 3.5 / 4B.3). Inverts the diagonal block of the
|
||||||
|
// rank-major matrix spanned by ranks [r0, r1), living in every member
|
||||||
|
// rank's column window [col0, col0+width) -- IN PLACE.
|
||||||
|
//
|
||||||
|
// SPMD: every rank calls with IDENTICAL (r0, r1, width) and its own
|
||||||
|
// local (col0, Arows); ranks outside [r0, r1) participate in the
|
||||||
|
// collectives only (dummy operands, zero contributions). The collective
|
||||||
|
// sequence -- 5 GatherGemm calls + 3 scalar GlobalSums per merge node --
|
||||||
|
// is identical on every rank by construction.
|
||||||
|
//
|
||||||
|
// I = [r0, mid) J = [mid, r1) widths WI, WJ
|
||||||
|
// 1. recurse I: A11 -> A11inv
|
||||||
|
// 2. B = A11inv.A12 (I rows)
|
||||||
|
// 3. C = A21.A11inv (J rows)
|
||||||
|
// 4. S = A22 - A21.B in place (J rows) [alpha=-1, beta=1]
|
||||||
|
// 5. recurse J: S -> Sinv
|
||||||
|
// 6. T = Sinv.C (J rows)
|
||||||
|
// 7. U = B.Sinv (I rows)
|
||||||
|
// 8. X11 = A11inv + U.C in place (I rows) [beta=1]
|
||||||
|
// 9. X12 = -U, X21 = -T local negates; X22 = Sinv already in place
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void SchurNode(int r0, int r1, int64_t col0, int64_t width, BlockRows &Arows)
|
||||||
|
{
|
||||||
|
int span = r1 - r0;
|
||||||
|
GRID_ASSERT( span >= 1 );
|
||||||
|
GRID_ASSERT( width == rowStart[r1] - rowStart[r0] );
|
||||||
|
|
||||||
|
if ( span == 1 )
|
||||||
|
{
|
||||||
|
if ( ( me == r0 ) && ( myNrows > 0 ) )
|
||||||
|
{
|
||||||
|
LeafInvert(col0, width, Arows);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mid = ( r0 + r1 ) / 2;
|
||||||
|
int64_t WI = rowStart[mid] - rowStart[r0];
|
||||||
|
int64_t WJ = rowStart[r1] - rowStart[mid];
|
||||||
|
|
||||||
|
// Zero-width child ranges (all ranks of a half owning no rows) are a
|
||||||
|
// KNOWN LIMITATION: fail loudly rather than divide mysteriously.
|
||||||
|
GRID_ASSERT( WI > 0 );
|
||||||
|
GRID_ASSERT( WJ > 0 );
|
||||||
|
|
||||||
|
int inI = ( me >= r0 ) && ( me < mid );
|
||||||
|
int inJ = ( me >= mid ) && ( me < r1 );
|
||||||
|
|
||||||
|
ComplexD one ( 1.0,0.0);
|
||||||
|
ComplexD mone (-1.0,0.0);
|
||||||
|
ComplexD zero ( 0.0,0.0);
|
||||||
|
|
||||||
|
BlockRows dummy;
|
||||||
|
|
||||||
|
// 1. A11 -> A11inv
|
||||||
|
SchurNode(r0, mid, col0, WI, Arows);
|
||||||
|
|
||||||
|
// 2. B = A11inv . A12 (I rows; gather A12 from I owners)
|
||||||
|
BlockRows Bbuf;
|
||||||
|
if ( inI ) Bbuf.Resize(myNrows, WJ);
|
||||||
|
{
|
||||||
|
BlockRows &Aop = inI ? Arows : dummy;
|
||||||
|
BlockRows &Cop = inI ? Bbuf : dummy;
|
||||||
|
int64_t cA = inI ? col0 : 0;
|
||||||
|
GatherGemm(one, Aop, cA, WI,
|
||||||
|
r0, mid,
|
||||||
|
Arows, col0+WI, WJ,
|
||||||
|
zero, Cop, 0);
|
||||||
|
}
|
||||||
|
double nB = FrobNorm2Local(Bbuf, 0, inI ? WJ : 0);
|
||||||
|
grid->GlobalSumVector(&nB, 1);
|
||||||
|
telNormB.push_back(std::sqrt(nB));
|
||||||
|
|
||||||
|
// 3. C = A21 . A11inv (J rows; gather A11inv from I owners)
|
||||||
|
BlockRows Cbuf;
|
||||||
|
if ( inJ ) Cbuf.Resize(myNrows, WI);
|
||||||
|
{
|
||||||
|
BlockRows &Aop = inJ ? Arows : dummy;
|
||||||
|
BlockRows &Cop = inJ ? Cbuf : dummy;
|
||||||
|
int64_t cA = inJ ? col0 : 0;
|
||||||
|
GatherGemm(one, Aop, cA, WI,
|
||||||
|
r0, mid,
|
||||||
|
Arows, col0, WI,
|
||||||
|
zero, Cop, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. S = A22 - A21 . B in place on my A22 window (J rows)
|
||||||
|
double nA22 = FrobNorm2Local( inJ ? Arows : dummy, inJ ? col0+WI : 0, inJ ? WJ : 0 );
|
||||||
|
grid->GlobalSumVector(&nA22, 1);
|
||||||
|
{
|
||||||
|
BlockRows &Aop = inJ ? Arows : dummy;
|
||||||
|
BlockRows &Cop = inJ ? Arows : dummy;
|
||||||
|
int64_t cA = inJ ? col0 : 0;
|
||||||
|
int64_t cC = inJ ? col0+WI : 0;
|
||||||
|
GatherGemm(mone, Aop, cA, WI,
|
||||||
|
r0, mid,
|
||||||
|
Bbuf, 0, WJ,
|
||||||
|
one, Cop, cC);
|
||||||
|
}
|
||||||
|
double nS = FrobNorm2Local( inJ ? Arows : dummy, inJ ? col0+WI : 0, inJ ? WJ : 0 );
|
||||||
|
grid->GlobalSumVector(&nS, 1);
|
||||||
|
telSratio.push_back( std::sqrt(nS) / ( std::sqrt(nA22) + 1.0e-300 ) );
|
||||||
|
|
||||||
|
// 5. S -> Sinv
|
||||||
|
SchurNode(mid, r1, col0+WI, WJ, Arows);
|
||||||
|
|
||||||
|
// 6. T = Sinv . C (J rows; gather C from J owners)
|
||||||
|
BlockRows Tbuf;
|
||||||
|
if ( inJ ) Tbuf.Resize(myNrows, WI);
|
||||||
|
{
|
||||||
|
BlockRows &Aop = inJ ? Arows : dummy;
|
||||||
|
BlockRows &Cop = inJ ? Tbuf : dummy;
|
||||||
|
int64_t cA = inJ ? col0+WI : 0;
|
||||||
|
GatherGemm(one, Aop, cA, WJ,
|
||||||
|
mid, r1,
|
||||||
|
Cbuf, 0, WI,
|
||||||
|
zero, Cop, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. U = B . Sinv (I rows; gather Sinv from J owners)
|
||||||
|
BlockRows Ubuf;
|
||||||
|
if ( inI ) Ubuf.Resize(myNrows, WJ);
|
||||||
|
{
|
||||||
|
BlockRows &Aop = inI ? Bbuf : dummy;
|
||||||
|
BlockRows &Cop = inI ? Ubuf : dummy;
|
||||||
|
GatherGemm(one, Aop, 0, WJ,
|
||||||
|
mid, r1,
|
||||||
|
Arows, col0+WI, WJ,
|
||||||
|
zero, Cop, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. X11 = A11inv + U . C in place (I rows; gather C from J owners)
|
||||||
|
{
|
||||||
|
BlockRows &Aop = inI ? Ubuf : dummy;
|
||||||
|
BlockRows &Cop = inI ? Arows : dummy;
|
||||||
|
int64_t cC = inI ? col0 : 0;
|
||||||
|
GatherGemm(one, Aop, 0, WJ,
|
||||||
|
mid, r1,
|
||||||
|
Cbuf, 0, WI,
|
||||||
|
one, Cop, cC);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. Off-diagonal signs, local
|
||||||
|
if ( inI ) NegateCopy(Ubuf, Arows, col0+WI, WJ);
|
||||||
|
if ( inJ ) NegateCopy(Tbuf, Arows, col0, WI);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// PUBLIC ENTRY. Arows: my rows of the rank-major N x N matrix (fp64).
|
||||||
|
// On exit Arows holds my rows of the inverse, still fp64; the caller
|
||||||
|
// owns the single terminal rounding into its fp32 apply storage.
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
void Invert(BlockRows &Arows)
|
||||||
|
{
|
||||||
|
GRID_ASSERT( Arows.rows == myNrows );
|
||||||
|
GRID_ASSERT( Arows.cols == N );
|
||||||
|
|
||||||
|
telNormB.resize(0);
|
||||||
|
telSratio.resize(0);
|
||||||
|
telLeafMaxInv = 0.0;
|
||||||
|
|
||||||
|
tMemset = 0.0;
|
||||||
|
tDeposit = 0.0;
|
||||||
|
tAllreduce = 0.0;
|
||||||
|
tGemm = 0.0;
|
||||||
|
tLeaf = 0.0;
|
||||||
|
tARmin = 1.0e30;
|
||||||
|
tARmax = 0.0;
|
||||||
|
bytesAllreduce = 0;
|
||||||
|
nAllreduce = 0;
|
||||||
|
nGatherGemm = 0;
|
||||||
|
|
||||||
|
SchurNode(0, P, 0, N, Arows);
|
||||||
|
|
||||||
|
RealD mx = telLeafMaxInv;
|
||||||
|
grid->GlobalMax(mx);
|
||||||
|
telLeafMaxInv = mx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All telemetry values are globally reduced or boss-local; safe to
|
||||||
|
// stream on every rank (Grid quiesces stdout to the boss unless
|
||||||
|
// --debug-stdout). NOTE: tAllreduce INCLUDES wait/imbalance -- a rank
|
||||||
|
// arriving early books its wait here; the min/max spread across ranks
|
||||||
|
// separates true wire time (~min) from skew (max-min).
|
||||||
|
void ReportTelemetry(void)
|
||||||
|
{
|
||||||
|
for(uint64_t i=0; i<telNormB.size(); i++)
|
||||||
|
{
|
||||||
|
std::cout << GridLogPerformance
|
||||||
|
<< "SchurNode " << i
|
||||||
|
<< " ||B||_F " << telNormB[i]
|
||||||
|
<< " ||S||/||A22|| " << telSratio[i]
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
std::cout << GridLogPerformance
|
||||||
|
<< "Schur leaves max|Ainv| " << telLeafMaxInv
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
RealD armax = tAllreduce;
|
||||||
|
RealD armin = -tAllreduce;
|
||||||
|
grid->GlobalMax(armax);
|
||||||
|
grid->GlobalMax(armin);
|
||||||
|
armin = -armin;
|
||||||
|
|
||||||
|
std::cout << GridLogMessage << "Schur phases (boss rank, seconds):"
|
||||||
|
<< " memset " << tMemset/1.0e6
|
||||||
|
<< " deposit " << tDeposit/1.0e6
|
||||||
|
<< " allreduce " << tAllreduce/1.0e6
|
||||||
|
<< " gemm " << tGemm/1.0e6
|
||||||
|
<< " leaf " << tLeaf/1.0e6
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << GridLogMessage << "Schur comms:"
|
||||||
|
<< " GatherGemm calls " << nGatherGemm
|
||||||
|
<< " panel allreduces " << nAllreduce
|
||||||
|
<< " allreduce GB " << bytesAllreduce/1024./1024./1024.
|
||||||
|
<< " allreduce s min/max over ranks " << armin/1.0e6
|
||||||
|
<< " / " << armax/1.0e6
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << GridLogMessage << "Schur comms per-call (boss):"
|
||||||
|
<< " min " << (nAllreduce ? tARmin/1.0e3 : 0.0) << " ms"
|
||||||
|
<< " avg " << (nAllreduce ? tAllreduce/nAllreduce/1.0e3 : 0.0) << " ms"
|
||||||
|
<< " max " << tARmax/1.0e3 << " ms"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
NAMESPACE_END(Grid);
|
||||||
@@ -63,12 +63,10 @@ void MemoryManager::PrintBytes(void)
|
|||||||
std::cout << " MemoryManager : "<<(total_device>>20)<<" accelerator Mbytes "<<std::endl;
|
std::cout << " MemoryManager : "<<(total_device>>20)<<" accelerator Mbytes "<<std::endl;
|
||||||
std::cout << " MemoryManager : "<<(total_host>>20) <<" cpu Mbytes "<<std::endl;
|
std::cout << " MemoryManager : "<<(total_host>>20) <<" cpu Mbytes "<<std::endl;
|
||||||
uint64_t cacheBytes;
|
uint64_t cacheBytes;
|
||||||
cacheBytes = CacheBytes[Cpu];
|
cacheBytes = HostCacheBytes();
|
||||||
std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" cpu cache Mbytes "<<std::endl;
|
std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" cpu alloc cache Mbytes "<<std::endl;
|
||||||
cacheBytes = CacheBytes[Acc];
|
cacheBytes = DeviceCacheBytes();
|
||||||
std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" acc cache Mbytes "<<std::endl;
|
std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" acc alloc cache Mbytes "<<std::endl;
|
||||||
cacheBytes = CacheBytes[Shared];
|
|
||||||
std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" shared cache Mbytes "<<std::endl;
|
|
||||||
|
|
||||||
#ifdef GRID_CUDA
|
#ifdef GRID_CUDA
|
||||||
cuda_mem();
|
cuda_mem();
|
||||||
|
|||||||
@@ -215,6 +215,7 @@ private:
|
|||||||
static void NotifyDeletion(void * CpuPtr);
|
static void NotifyDeletion(void * CpuPtr);
|
||||||
static void Print(void);
|
static void Print(void);
|
||||||
static void PrintAll(void);
|
static void PrintAll(void);
|
||||||
|
static void EvictAll(void);
|
||||||
static void PrintState( void* CpuPtr);
|
static void PrintState( void* CpuPtr);
|
||||||
static int isOpen (void* CpuPtr);
|
static int isOpen (void* CpuPtr);
|
||||||
static void ViewClose(void* CpuPtr,ViewMode mode);
|
static void ViewClose(void* CpuPtr,ViewMode mode);
|
||||||
|
|||||||
@@ -79,6 +79,25 @@ void MemoryManager::EntryErase(uint64_t CpuPtr)
|
|||||||
auto AccCache = EntryLookup(CpuPtr);
|
auto AccCache = EntryLookup(CpuPtr);
|
||||||
AccViewTable.erase(CpuPtr);
|
AccViewTable.erase(CpuPtr);
|
||||||
}
|
}
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// LRU membership invariant:
|
||||||
|
//
|
||||||
|
// LRU_valid == 1 <=> AccPtr != NULL && accLock == 0 && cpuLock == 0
|
||||||
|
//
|
||||||
|
// i.e. the LRU queue contains exactly the device-resident, completely unlocked
|
||||||
|
// entries -- the evictable set. Membership is maintained EAGERLY at the lock
|
||||||
|
// 0<->1 edges, O(1) via the stored LRU_entry iterator:
|
||||||
|
//
|
||||||
|
// AcceleratorViewOpen lock 0->1 : LRUremove (gated on LRU_valid)
|
||||||
|
// AcceleratorViewClose accLock->0: LRUinsert (AccPtr necessarily exists)
|
||||||
|
// CpuViewOpen lock 0->1 : LRUremove (gated on LRU_valid)
|
||||||
|
// CpuViewClose cpuLock->0: LRUinsert (iff AccPtr exists)
|
||||||
|
// Evict/AccDiscard : LRUremove (frees the device copy)
|
||||||
|
//
|
||||||
|
// Consequences: victims taken from LRU.back() are evictable by construction;
|
||||||
|
// Evict() on a locked entry is an invariant violation (asserted), and the
|
||||||
|
// eviction loops (EvictVictims/EvictAll) cannot spin.
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
void MemoryManager::LRUinsert(AcceleratorViewEntry &AccCache)
|
void MemoryManager::LRUinsert(AcceleratorViewEntry &AccCache)
|
||||||
{
|
{
|
||||||
GRID_ASSERT(AccCache.LRU_valid==0);
|
GRID_ASSERT(AccCache.LRU_valid==0);
|
||||||
@@ -130,21 +149,21 @@ void MemoryManager::Evict(AcceleratorViewEntry &AccCache)
|
|||||||
{
|
{
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Make CPU consistent, remove from Accelerator, remove from LRU, LEAVE CPU only entry
|
// Make CPU consistent, remove from Accelerator, remove from LRU, LEAVE CPU only entry
|
||||||
// Cannot be acclocked. If allocated must be in LRU pool.
|
// Cannot be locked. If allocated must be in LRU pool.
|
||||||
//
|
//
|
||||||
// Nov 2022... Felix issue: Allocating two CpuPtrs, can have an entry in LRU-q with CPUlock.
|
// (Historical: a Nov 2022 incident (two CpuPtrs; eviction called from
|
||||||
// and require to evict the AccPtr copy. Eviction was a mistake in CpuViewOpen
|
// CpuViewOpen -- since excised) could present a cpuLocked entry here, and
|
||||||
// but there is a weakness where CpuLock entries are attempted for erase
|
// silent-return guards were added. The LRU membership invariant (see
|
||||||
// Take these OUT LRU queue when CPU locked?
|
// LRUinsert) now excludes ALL locked entries from the queue eagerly at the
|
||||||
// Cannot take out the table as cpuLock data is important.
|
// lock edges, so a locked victim is an invariant violation: asserted.)
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
GRID_ASSERT(AccCache.state!=Empty);
|
GRID_ASSERT(AccCache.state!=Empty);
|
||||||
|
|
||||||
mprintf("MemoryManager: Evict CpuPtr %lx AccPtr %lx cpuLock %ld accLock %ld",
|
mprintf("MemoryManager: Evict CpuPtr %lx AccPtr %lx cpuLock %ld accLock %ld",
|
||||||
(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr,
|
(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr,
|
||||||
(uint64_t)AccCache.cpuLock,(uint64_t)AccCache.accLock);
|
(uint64_t)AccCache.cpuLock,(uint64_t)AccCache.accLock);
|
||||||
if (AccCache.accLock!=0) return;
|
GRID_ASSERT(AccCache.accLock==0);
|
||||||
if (AccCache.cpuLock!=0) return;
|
GRID_ASSERT(AccCache.cpuLock==0);
|
||||||
if(AccCache.state==AccDirty) {
|
if(AccCache.state==AccDirty) {
|
||||||
Flush(AccCache);
|
Flush(AccCache);
|
||||||
}
|
}
|
||||||
@@ -250,6 +269,19 @@ void MemoryManager::EvictVictims(uint64_t bytes)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void MemoryManager::EvictAll(void)
|
||||||
|
{
|
||||||
|
while(LRU.size()>0){
|
||||||
|
if ( DeviceLRUBytes > 0){
|
||||||
|
uint64_t victim = LRU.back(); // From the LRU
|
||||||
|
auto AccCacheIterator = EntryLookup(victim);
|
||||||
|
auto & AccCache = AccCacheIterator->second;
|
||||||
|
Evict(AccCache);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
uint64_t MemoryManager::AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint)
|
uint64_t MemoryManager::AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint)
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -383,6 +415,13 @@ void MemoryManager::CpuViewClose(uint64_t CpuPtr)
|
|||||||
GRID_ASSERT(AccCache.accLock==0);
|
GRID_ASSERT(AccCache.accLock==0);
|
||||||
|
|
||||||
AccCache.cpuLock--;
|
AccCache.cpuLock--;
|
||||||
|
// Return to LRU queue when fully unlocked -- mirrors AcceleratorViewClose.
|
||||||
|
// Asymmetry vs the Acc side: a device copy need not exist for a host view;
|
||||||
|
// only device-resident entries belong in the (evictable) LRU queue.
|
||||||
|
if( (AccCache.cpuLock==0) && (AccCache.AccPtr!=(uint64_t)NULL) ) {
|
||||||
|
dprintf("CpuViewClose %lx cpuLock decremented to zero, move to LRU queue",(uint64_t)CpuPtr);
|
||||||
|
LRUinsert(AccCache);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Action State StateNext Flush Clone
|
* Action State StateNext Flush Clone
|
||||||
@@ -449,6 +488,14 @@ uint64_t MemoryManager::CpuViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,V
|
|||||||
GRID_ASSERT(0); // should be unreachable
|
GRID_ASSERT(0); // should be unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GRID_ASSERT(AccCache.cpuLock>0);
|
||||||
|
// If view is opened on host must remove from LRU -- mirrors AcceleratorViewOpen.
|
||||||
|
// LRU_valid==1 here implies this is the 0->1 lock edge of a device-resident entry.
|
||||||
|
if(AccCache.LRU_valid==1){
|
||||||
|
dprintf("CpuViewOpen: entry removed from LRU ");
|
||||||
|
LRUremove(AccCache);
|
||||||
|
}
|
||||||
|
|
||||||
AccCache.transient= transient? EvictNext : 0;
|
AccCache.transient= transient? EvictNext : 0;
|
||||||
|
|
||||||
return AccCache.CpuPtr;
|
return AccCache.CpuPtr;
|
||||||
|
|||||||
@@ -239,6 +239,16 @@ public:
|
|||||||
void AllToAll(int dim ,void *in,void *out,uint64_t words,uint64_t bytes);
|
void AllToAll(int dim ,void *in,void *out,uint64_t words,uint64_t bytes);
|
||||||
void AllToAll(void *in,void *out,uint64_t words ,uint64_t bytes);
|
void AllToAll(void *in,void *out,uint64_t words ,uint64_t bytes);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Variable count all to all. Counts and displacements are in units of
|
||||||
|
// "bytes" sized words and are indexed by rank within this communicator.
|
||||||
|
// For exchanges that are a permutation but do not divide evenly between
|
||||||
|
// ranks; AllToAll above is the uniform count special case.
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
void AllToAllV(void *in ,const std::vector<int> &sendcounts,const std::vector<int> &senddispls,
|
||||||
|
void *out,const std::vector<int> &recvcounts,const std::vector<int> &recvdispls,
|
||||||
|
uint64_t bytes);
|
||||||
|
|
||||||
template<class obj> void Broadcast(int root,obj &data)
|
template<class obj> void Broadcast(int root,obj &data)
|
||||||
{
|
{
|
||||||
Broadcast(root,(void *)&data,sizeof(data));
|
Broadcast(root,(void *)&data,sizeof(data));
|
||||||
|
|||||||
@@ -945,5 +945,25 @@ void CartesianCommunicator::AllToAll(void *in,void *out,uint64_t words,uint64_t
|
|||||||
MPI_Alltoall(in,iwords,object,out,iwords,object,communicator);
|
MPI_Alltoall(in,iwords,object,out,iwords,object,communicator);
|
||||||
MPI_Type_free(&object);
|
MPI_Type_free(&object);
|
||||||
}
|
}
|
||||||
|
void CartesianCommunicator::AllToAllV(void *in ,const std::vector<int> &sendcounts,const std::vector<int> &senddispls,
|
||||||
|
void *out,const std::vector<int> &recvcounts,const std::vector<int> &recvdispls,
|
||||||
|
uint64_t bytes)
|
||||||
|
{
|
||||||
|
FlightRecorder::StepLog("AllToAllV");
|
||||||
|
GRID_ASSERT(sendcounts.size()==(size_t)_Nprocessors);
|
||||||
|
GRID_ASSERT(senddispls.size()==(size_t)_Nprocessors);
|
||||||
|
GRID_ASSERT(recvcounts.size()==(size_t)_Nprocessors);
|
||||||
|
GRID_ASSERT(recvdispls.size()==(size_t)_Nprocessors);
|
||||||
|
// MPI counts are "int"; the caller sizes the word to keep them in range
|
||||||
|
int ibytes = bytes;
|
||||||
|
GRID_ASSERT(bytes == (uint64_t)ibytes);
|
||||||
|
MPI_Datatype object;
|
||||||
|
MPI_Type_contiguous(ibytes,MPI_BYTE,&object);
|
||||||
|
MPI_Type_commit(&object);
|
||||||
|
int ierr = MPI_Alltoallv(in ,(int *)&sendcounts[0],(int *)&senddispls[0],object,
|
||||||
|
out,(int *)&recvcounts[0],(int *)&recvdispls[0],object,communicator);
|
||||||
|
GRID_ASSERT(ierr==0);
|
||||||
|
MPI_Type_free(&object);
|
||||||
|
}
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
NAMESPACE_END(Grid);
|
||||||
|
|||||||
@@ -113,6 +113,17 @@ void CartesianCommunicator::AllToAll(void *in,void *out,uint64_t words,uint64_t
|
|||||||
{
|
{
|
||||||
bcopy(in,out,bytes*words);
|
bcopy(in,out,bytes*words);
|
||||||
}
|
}
|
||||||
|
void CartesianCommunicator::AllToAllV(void *in ,const std::vector<int> &sendcounts,const std::vector<int> &senddispls,
|
||||||
|
void *out,const std::vector<int> &recvcounts,const std::vector<int> &recvdispls,
|
||||||
|
uint64_t bytes)
|
||||||
|
{
|
||||||
|
// Single rank: the exchange degenerates to a copy of our own segment
|
||||||
|
GRID_ASSERT(sendcounts.size()==1);
|
||||||
|
GRID_ASSERT(recvcounts.size()==1);
|
||||||
|
GRID_ASSERT(sendcounts[0]==recvcounts[0]);
|
||||||
|
bcopy((char *)in +(uint64_t)senddispls[0]*bytes,
|
||||||
|
(char *)out+(uint64_t)recvdispls[0]*bytes,bytes*(uint64_t)sendcounts[0]);
|
||||||
|
}
|
||||||
|
|
||||||
int CartesianCommunicator::RankWorld(void){return 0;}
|
int CartesianCommunicator::RankWorld(void){return 0;}
|
||||||
void CartesianCommunicator::Barrier(void){}
|
void CartesianCommunicator::Barrier(void){}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ accelerator_inline vobj predicatedWhere(const iobj &predicate,
|
|||||||
|
|
||||||
extract(iftrue, truevals);
|
extract(iftrue, truevals);
|
||||||
extract(iffalse, falsevals);
|
extract(iffalse, falsevals);
|
||||||
extract<vInteger, Integer>(TensorRemove(predicate), mask);
|
extract(TensorRemove(predicate), mask);
|
||||||
|
|
||||||
for (int s = 0; s < Nsimd; s++) {
|
for (int s = 0; s < Nsimd; s++) {
|
||||||
if (mask[s]) falsevals[s] = truevals[s];
|
if (mask[s]) falsevals[s] = truevals[s];
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ public:
|
|||||||
///////////////////////////////////////////
|
///////////////////////////////////////////
|
||||||
// move constructor
|
// move constructor
|
||||||
///////////////////////////////////////////
|
///////////////////////////////////////////
|
||||||
Lattice(Lattice && r){
|
Lattice(Lattice && r) noexcept {
|
||||||
this->_grid = r.Grid();
|
this->_grid = r.Grid();
|
||||||
this->_odata = r._odata;
|
this->_odata = r._odata;
|
||||||
this->_odata_size = r._odata_size;
|
this->_odata_size = r._odata_size;
|
||||||
@@ -330,7 +330,7 @@ public:
|
|||||||
///////////////////////////////////////////
|
///////////////////////////////////////////
|
||||||
// Move assignment possible if same type
|
// Move assignment possible if same type
|
||||||
///////////////////////////////////////////
|
///////////////////////////////////////////
|
||||||
inline Lattice<vobj> & operator = (Lattice<vobj> && r){
|
inline Lattice<vobj> & operator = (Lattice<vobj> && r) noexcept {
|
||||||
|
|
||||||
resize(0); // deletes if appropriate
|
resize(0); // deletes if appropriate
|
||||||
this->_grid = r.Grid();
|
this->_grid = r.Grid();
|
||||||
|
|||||||
@@ -40,16 +40,16 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
//Query supporting logical &&, ||,
|
//Query supporting logical &&, ||,
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
typedef iScalar<vInteger> vPredicate ;
|
template<class vobj> using vPredicate = iScalar<IntegerPredicate<vobj> > ;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// compare lattice to lattice
|
// compare lattice to lattice
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template<class vfunctor,class lobj,class robj>
|
template<class vfunctor,class lobj,class robj>
|
||||||
inline Lattice<vPredicate> LLComparison(vfunctor op,const Lattice<lobj> &lhs,const Lattice<robj> &rhs)
|
inline Lattice<vPredicate<lobj> > LLComparison(vfunctor op,const Lattice<lobj> &lhs,const Lattice<robj> &rhs)
|
||||||
{
|
{
|
||||||
Lattice<vPredicate> ret(rhs.Grid());
|
Lattice<vPredicate<lobj> > ret(rhs.Grid());
|
||||||
autoView( lhs_v, lhs, CpuRead);
|
autoView( lhs_v, lhs, CpuRead);
|
||||||
autoView( rhs_v, rhs, CpuRead);
|
autoView( rhs_v, rhs, CpuRead);
|
||||||
autoView( ret_v, ret, CpuWrite);
|
autoView( ret_v, ret, CpuWrite);
|
||||||
@@ -62,9 +62,9 @@ inline Lattice<vPredicate> LLComparison(vfunctor op,const Lattice<lobj> &lhs,con
|
|||||||
// compare lattice to scalar
|
// compare lattice to scalar
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
template<class vfunctor,class lobj,class robj>
|
template<class vfunctor,class lobj,class robj>
|
||||||
inline Lattice<vPredicate> LSComparison(vfunctor op,const Lattice<lobj> &lhs,const robj &rhs)
|
inline Lattice<vPredicate<lobj> > LSComparison(vfunctor op,const Lattice<lobj> &lhs,const robj &rhs)
|
||||||
{
|
{
|
||||||
Lattice<vPredicate> ret(lhs.Grid());
|
Lattice<vPredicate<lobj> > ret(lhs.Grid());
|
||||||
autoView( lhs_v, lhs, CpuRead);
|
autoView( lhs_v, lhs, CpuRead);
|
||||||
autoView( ret_v, ret, CpuWrite);
|
autoView( ret_v, ret, CpuWrite);
|
||||||
thread_for( ss, lhs_v.size(), {
|
thread_for( ss, lhs_v.size(), {
|
||||||
@@ -76,9 +76,9 @@ inline Lattice<vPredicate> LSComparison(vfunctor op,const Lattice<lobj> &lhs,con
|
|||||||
// compare scalar to lattice
|
// compare scalar to lattice
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
template<class vfunctor,class lobj,class robj>
|
template<class vfunctor,class lobj,class robj>
|
||||||
inline Lattice<vPredicate> SLComparison(vfunctor op,const lobj &lhs,const Lattice<robj> &rhs)
|
inline Lattice<vPredicate<robj> > SLComparison(vfunctor op,const lobj &lhs,const Lattice<robj> &rhs)
|
||||||
{
|
{
|
||||||
Lattice<vPredicate> ret(rhs.Grid());
|
Lattice<vPredicate<robj> > ret(rhs.Grid());
|
||||||
autoView( rhs_v, rhs, CpuRead);
|
autoView( rhs_v, rhs, CpuRead);
|
||||||
autoView( ret_v, ret, CpuWrite);
|
autoView( ret_v, ret, CpuWrite);
|
||||||
thread_for( ss, rhs_v.size(), {
|
thread_for( ss, rhs_v.size(), {
|
||||||
@@ -92,87 +92,87 @@ inline Lattice<vPredicate> SLComparison(vfunctor op,const lobj &lhs,const Lattic
|
|||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// Less than
|
// Less than
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator < (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
|
inline Lattice<vPredicate<lobj> > operator < (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
|
||||||
return LLComparison(vlt<lobj,robj>(),lhs,rhs);
|
return LLComparison(vlt<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator < (const Lattice<lobj> & lhs, const robj & rhs) {
|
inline Lattice<vPredicate<lobj> > operator < (const Lattice<lobj> & lhs, const robj & rhs) {
|
||||||
return LSComparison(vlt<lobj,robj>(),lhs,rhs);
|
return LSComparison(vlt<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator < (const lobj & lhs, const Lattice<robj> & rhs) {
|
inline Lattice<vPredicate<robj> > operator < (const lobj & lhs, const Lattice<robj> & rhs) {
|
||||||
return SLComparison(vlt<lobj,robj>(),lhs,rhs);
|
return SLComparison(vlt<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Less than equal
|
// Less than equal
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator <= (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
|
inline Lattice<vPredicate<lobj> > operator <= (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
|
||||||
return LLComparison(vle<lobj,robj>(),lhs,rhs);
|
return LLComparison(vle<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator <= (const Lattice<lobj> & lhs, const robj & rhs) {
|
inline Lattice<vPredicate<lobj> > operator <= (const Lattice<lobj> & lhs, const robj & rhs) {
|
||||||
return LSComparison(vle<lobj,robj>(),lhs,rhs);
|
return LSComparison(vle<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator <= (const lobj & lhs, const Lattice<robj> & rhs) {
|
inline Lattice<vPredicate<robj> > operator <= (const lobj & lhs, const Lattice<robj> & rhs) {
|
||||||
return SLComparison(vle<lobj,robj>(),lhs,rhs);
|
return SLComparison(vle<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Greater than
|
// Greater than
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator > (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
|
inline Lattice<vPredicate<lobj> > operator > (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
|
||||||
return LLComparison(vgt<lobj,robj>(),lhs,rhs);
|
return LLComparison(vgt<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator > (const Lattice<lobj> & lhs, const robj & rhs) {
|
inline Lattice<vPredicate<lobj> > operator > (const Lattice<lobj> & lhs, const robj & rhs) {
|
||||||
return LSComparison(vgt<lobj,robj>(),lhs,rhs);
|
return LSComparison(vgt<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator > (const lobj & lhs, const Lattice<robj> & rhs) {
|
inline Lattice<vPredicate<robj> > operator > (const lobj & lhs, const Lattice<robj> & rhs) {
|
||||||
return SLComparison(vgt<lobj,robj>(),lhs,rhs);
|
return SLComparison(vgt<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Greater than equal
|
// Greater than equal
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator >= (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
|
inline Lattice<vPredicate<lobj> > operator >= (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
|
||||||
return LLComparison(vge<lobj,robj>(),lhs,rhs);
|
return LLComparison(vge<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator >= (const Lattice<lobj> & lhs, const robj & rhs) {
|
inline Lattice<vPredicate<lobj> > operator >= (const Lattice<lobj> & lhs, const robj & rhs) {
|
||||||
return LSComparison(vge<lobj,robj>(),lhs,rhs);
|
return LSComparison(vge<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator >= (const lobj & lhs, const Lattice<robj> & rhs) {
|
inline Lattice<vPredicate<robj> > operator >= (const lobj & lhs, const Lattice<robj> & rhs) {
|
||||||
return SLComparison(vge<lobj,robj>(),lhs,rhs);
|
return SLComparison(vge<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// equal
|
// equal
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator == (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
|
inline Lattice<vPredicate<lobj> > operator == (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
|
||||||
return LLComparison(veq<lobj,robj>(),lhs,rhs);
|
return LLComparison(veq<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator == (const Lattice<lobj> & lhs, const robj & rhs) {
|
inline Lattice<vPredicate<lobj> > operator == (const Lattice<lobj> & lhs, const robj & rhs) {
|
||||||
return LSComparison(veq<lobj,robj>(),lhs,rhs);
|
return LSComparison(veq<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator == (const lobj & lhs, const Lattice<robj> & rhs) {
|
inline Lattice<vPredicate<robj> > operator == (const lobj & lhs, const Lattice<robj> & rhs) {
|
||||||
return SLComparison(veq<lobj,robj>(),lhs,rhs);
|
return SLComparison(veq<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// not equal
|
// not equal
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator != (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
|
inline Lattice<vPredicate<lobj> > operator != (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
|
||||||
return LLComparison(vne<lobj,robj>(),lhs,rhs);
|
return LLComparison(vne<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator != (const Lattice<lobj> & lhs, const robj & rhs) {
|
inline Lattice<vPredicate<lobj> > operator != (const Lattice<lobj> & lhs, const robj & rhs) {
|
||||||
return LSComparison(vne<lobj,robj>(),lhs,rhs);
|
return LSComparison(vne<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
template<class lobj,class robj>
|
template<class lobj,class robj>
|
||||||
inline Lattice<vPredicate> operator != (const lobj & lhs, const Lattice<robj> & rhs) {
|
inline Lattice<vPredicate<robj> > operator != (const lobj & lhs, const Lattice<robj> & rhs) {
|
||||||
return SLComparison(vne<lobj,robj>(),lhs,rhs);
|
return SLComparison(vne<lobj,robj>(),lhs,rhs);
|
||||||
}
|
}
|
||||||
NAMESPACE_END(Grid);
|
NAMESPACE_END(Grid);
|
||||||
|
|||||||
@@ -39,47 +39,53 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
//
|
//
|
||||||
// Should guard this with isGridTensor<> enable if?
|
// Should guard this with isGridTensor<> enable if?
|
||||||
/////////////////////////////////////////
|
/////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Predicate type matching the operand's vectorisation
|
||||||
|
//
|
||||||
|
template<class obj> using IntegerPredicate =
|
||||||
|
typename GridTypeMapper<typename GridTypeMapper<obj>::vector_type>::Integerified;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Generic list of functors
|
// Generic list of functors
|
||||||
//
|
//
|
||||||
template<class lobj,class robj> class veq {
|
template<class lobj,class robj> class veq {
|
||||||
public:
|
public:
|
||||||
accelerator vInteger operator()(const lobj &lhs, const robj &rhs)
|
accelerator IntegerPredicate<lobj> operator()(const lobj &lhs, const robj &rhs)
|
||||||
{
|
{
|
||||||
return (lhs) == (rhs);
|
return (lhs) == (rhs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template<class lobj,class robj> class vne {
|
template<class lobj,class robj> class vne {
|
||||||
public:
|
public:
|
||||||
accelerator vInteger operator()(const lobj &lhs, const robj &rhs)
|
accelerator IntegerPredicate<lobj> operator()(const lobj &lhs, const robj &rhs)
|
||||||
{
|
{
|
||||||
return (lhs) != (rhs);
|
return (lhs) != (rhs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template<class lobj,class robj> class vlt {
|
template<class lobj,class robj> class vlt {
|
||||||
public:
|
public:
|
||||||
accelerator vInteger operator()(const lobj &lhs, const robj &rhs)
|
accelerator IntegerPredicate<lobj> operator()(const lobj &lhs, const robj &rhs)
|
||||||
{
|
{
|
||||||
return (lhs) < (rhs);
|
return (lhs) < (rhs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template<class lobj,class robj> class vle {
|
template<class lobj,class robj> class vle {
|
||||||
public:
|
public:
|
||||||
accelerator vInteger operator()(const lobj &lhs, const robj &rhs)
|
accelerator IntegerPredicate<lobj> operator()(const lobj &lhs, const robj &rhs)
|
||||||
{
|
{
|
||||||
return (lhs) <= (rhs);
|
return (lhs) <= (rhs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template<class lobj,class robj> class vgt {
|
template<class lobj,class robj> class vgt {
|
||||||
public:
|
public:
|
||||||
accelerator vInteger operator()(const lobj &lhs, const robj &rhs)
|
accelerator IntegerPredicate<lobj> operator()(const lobj &lhs, const robj &rhs)
|
||||||
{
|
{
|
||||||
return (lhs) > (rhs);
|
return (lhs) > (rhs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template<class lobj,class robj> class vge {
|
template<class lobj,class robj> class vge {
|
||||||
public:
|
public:
|
||||||
accelerator vInteger operator()(const lobj &lhs, const robj &rhs)
|
accelerator IntegerPredicate<lobj> operator()(const lobj &lhs, const robj &rhs)
|
||||||
{
|
{
|
||||||
return (lhs) >= (rhs);
|
return (lhs) >= (rhs);
|
||||||
}
|
}
|
||||||
@@ -133,78 +139,78 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
// Integer and real get extra relational functions.
|
// Integer and real get extra relational functions.
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
template<class sfunctor, class vsimd,IfNotComplex<vsimd> = 0>
|
template<class sfunctor, class vsimd,IfNotComplex<vsimd> = 0>
|
||||||
accelerator_inline vInteger Comparison(sfunctor sop,const vsimd & lhs, const vsimd & rhs)
|
accelerator_inline IntegerPredicate<vsimd> Comparison(sfunctor sop,const vsimd & lhs, const vsimd & rhs)
|
||||||
{
|
{
|
||||||
typedef typename vsimd::scalar_type scalar;
|
typedef typename vsimd::scalar_type scalar;
|
||||||
ExtractBuffer<scalar> vlhs(vsimd::Nsimd()); // Use functors to reduce this to single implementation
|
ExtractBuffer<scalar> vlhs(vsimd::Nsimd()); // Use functors to reduce this to single implementation
|
||||||
ExtractBuffer<scalar> vrhs(vsimd::Nsimd());
|
ExtractBuffer<scalar> vrhs(vsimd::Nsimd());
|
||||||
ExtractBuffer<Integer> vpred(vsimd::Nsimd());
|
ExtractBuffer<Integer> vpred(vsimd::Nsimd());
|
||||||
vInteger ret;
|
IntegerPredicate<vsimd> ret;
|
||||||
extract<vsimd,scalar>(lhs,vlhs);
|
extract<vsimd,scalar>(lhs,vlhs);
|
||||||
extract<vsimd,scalar>(rhs,vrhs);
|
extract<vsimd,scalar>(rhs,vrhs);
|
||||||
for(int s=0;s<vsimd::Nsimd();s++){
|
for(int s=0;s<vsimd::Nsimd();s++){
|
||||||
vpred[s] = sop(vlhs[s],vrhs[s]);
|
vpred[s] = sop(vlhs[s],vrhs[s]);
|
||||||
}
|
}
|
||||||
merge<vInteger,Integer>(ret,vpred);
|
merge<IntegerPredicate<vsimd>,Integer>(ret,vpred);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class sfunctor, class vsimd,IfNotComplex<vsimd> = 0>
|
template<class sfunctor, class vsimd,IfNotComplex<vsimd> = 0>
|
||||||
accelerator_inline vInteger Comparison(sfunctor sop,const vsimd & lhs, const typename vsimd::scalar_type & rhs)
|
accelerator_inline IntegerPredicate<vsimd> Comparison(sfunctor sop,const vsimd & lhs, const typename vsimd::scalar_type & rhs)
|
||||||
{
|
{
|
||||||
typedef typename vsimd::scalar_type scalar;
|
typedef typename vsimd::scalar_type scalar;
|
||||||
ExtractBuffer<scalar> vlhs(vsimd::Nsimd()); // Use functors to reduce this to single implementation
|
ExtractBuffer<scalar> vlhs(vsimd::Nsimd()); // Use functors to reduce this to single implementation
|
||||||
ExtractBuffer<Integer> vpred(vsimd::Nsimd());
|
ExtractBuffer<Integer> vpred(vsimd::Nsimd());
|
||||||
vInteger ret;
|
IntegerPredicate<vsimd> ret;
|
||||||
extract<vsimd,scalar>(lhs,vlhs);
|
extract<vsimd,scalar>(lhs,vlhs);
|
||||||
for(int s=0;s<vsimd::Nsimd();s++){
|
for(int s=0;s<vsimd::Nsimd();s++){
|
||||||
vpred[s] = sop(vlhs[s],rhs);
|
vpred[s] = sop(vlhs[s],rhs);
|
||||||
}
|
}
|
||||||
merge<vInteger,Integer>(ret,vpred);
|
merge<IntegerPredicate<vsimd>,Integer>(ret,vpred);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class sfunctor, class vsimd,IfNotComplex<vsimd> = 0>
|
template<class sfunctor, class vsimd,IfNotComplex<vsimd> = 0>
|
||||||
accelerator_inline vInteger Comparison(sfunctor sop,const typename vsimd::scalar_type & lhs, const vsimd & rhs)
|
accelerator_inline IntegerPredicate<vsimd> Comparison(sfunctor sop,const typename vsimd::scalar_type & lhs, const vsimd & rhs)
|
||||||
{
|
{
|
||||||
typedef typename vsimd::scalar_type scalar;
|
typedef typename vsimd::scalar_type scalar;
|
||||||
ExtractBuffer<scalar> vrhs(vsimd::Nsimd()); // Use functors to reduce this to single implementation
|
ExtractBuffer<scalar> vrhs(vsimd::Nsimd()); // Use functors to reduce this to single implementation
|
||||||
ExtractBuffer<Integer> vpred(vsimd::Nsimd());
|
ExtractBuffer<Integer> vpred(vsimd::Nsimd());
|
||||||
vInteger ret;
|
IntegerPredicate<vsimd> ret;
|
||||||
extract<vsimd,scalar>(rhs,vrhs);
|
extract<vsimd,scalar>(rhs,vrhs);
|
||||||
for(int s=0;s<vsimd::Nsimd();s++){
|
for(int s=0;s<vsimd::Nsimd();s++){
|
||||||
vpred[s] = sop(lhs,vrhs[s]);
|
vpred[s] = sop(lhs,vrhs[s]);
|
||||||
}
|
}
|
||||||
merge<vInteger,Integer>(ret,vpred);
|
merge<IntegerPredicate<vsimd>,Integer>(ret,vpred);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DECLARE_RELATIONAL_EQ(op,functor) \
|
#define DECLARE_RELATIONAL_EQ(op,functor) \
|
||||||
template<class vsimd,IfSimd<vsimd> = 0>\
|
template<class vsimd,IfSimd<vsimd> = 0>\
|
||||||
accelerator_inline vInteger operator op (const vsimd & lhs, const vsimd & rhs)\
|
accelerator_inline IntegerPredicate<vsimd> operator op (const vsimd & lhs, const vsimd & rhs)\
|
||||||
{\
|
{\
|
||||||
typedef typename vsimd::scalar_type scalar;\
|
typedef typename vsimd::scalar_type scalar;\
|
||||||
return Comparison(functor<scalar,scalar>(),lhs,rhs);\
|
return Comparison(functor<scalar,scalar>(),lhs,rhs);\
|
||||||
}\
|
}\
|
||||||
template<class vsimd,IfSimd<vsimd> = 0>\
|
template<class vsimd,IfSimd<vsimd> = 0>\
|
||||||
accelerator_inline vInteger operator op (const vsimd & lhs, const typename vsimd::scalar_type & rhs) \
|
accelerator_inline IntegerPredicate<vsimd> operator op (const vsimd & lhs, const typename vsimd::scalar_type & rhs) \
|
||||||
{\
|
{\
|
||||||
typedef typename vsimd::scalar_type scalar;\
|
typedef typename vsimd::scalar_type scalar;\
|
||||||
return Comparison(functor<scalar,scalar>(),lhs,rhs);\
|
return Comparison(functor<scalar,scalar>(),lhs,rhs);\
|
||||||
}\
|
}\
|
||||||
template<class vsimd,IfSimd<vsimd> = 0>\
|
template<class vsimd,IfSimd<vsimd> = 0>\
|
||||||
accelerator_inline vInteger operator op (const typename vsimd::scalar_type & lhs, const vsimd & rhs) \
|
accelerator_inline IntegerPredicate<vsimd> operator op (const typename vsimd::scalar_type & lhs, const vsimd & rhs) \
|
||||||
{\
|
{\
|
||||||
typedef typename vsimd::scalar_type scalar;\
|
typedef typename vsimd::scalar_type scalar;\
|
||||||
return Comparison(functor<scalar,scalar>(),lhs,rhs);\
|
return Comparison(functor<scalar,scalar>(),lhs,rhs);\
|
||||||
}\
|
}\
|
||||||
template<class vsimd>\
|
template<class vsimd>\
|
||||||
accelerator_inline vInteger operator op(const iScalar<vsimd> &lhs,const typename vsimd::scalar_type &rhs) \
|
accelerator_inline IntegerPredicate<vsimd> operator op(const iScalar<vsimd> &lhs,const typename vsimd::scalar_type &rhs) \
|
||||||
{ \
|
{ \
|
||||||
return lhs._internal op rhs; \
|
return lhs._internal op rhs; \
|
||||||
} \
|
} \
|
||||||
template<class vsimd>\
|
template<class vsimd>\
|
||||||
accelerator_inline vInteger operator op(const typename vsimd::scalar_type &lhs,const iScalar<vsimd> &rhs) \
|
accelerator_inline IntegerPredicate<vsimd> operator op(const typename vsimd::scalar_type &lhs,const iScalar<vsimd> &rhs) \
|
||||||
{ \
|
{ \
|
||||||
return lhs op rhs._internal; \
|
return lhs op rhs._internal; \
|
||||||
} \
|
} \
|
||||||
@@ -212,7 +218,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
#define DECLARE_RELATIONAL(op,functor) \
|
#define DECLARE_RELATIONAL(op,functor) \
|
||||||
DECLARE_RELATIONAL_EQ(op,functor) \
|
DECLARE_RELATIONAL_EQ(op,functor) \
|
||||||
template<class vsimd>\
|
template<class vsimd>\
|
||||||
accelerator_inline vInteger operator op(const iScalar<vsimd> &lhs,const iScalar<vsimd> &rhs)\
|
accelerator_inline IntegerPredicate<vsimd> operator op(const iScalar<vsimd> &lhs,const iScalar<vsimd> &rhs)\
|
||||||
{ \
|
{ \
|
||||||
return lhs._internal op rhs._internal; \
|
return lhs._internal op rhs._internal; \
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,6 +197,9 @@ __global__ void reduceKernel(const vobj *lat, sobj *buffer, Iterator n) {
|
|||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Possibly promote to double and sum
|
// Possibly promote to double and sum
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#undef GRID_REDUCTION_TIMING
|
||||||
|
|
||||||
template <class vobj>
|
template <class vobj>
|
||||||
inline typename vobj::scalar_objectD sumD_gpu_small(const vobj *lat, Integer osites)
|
inline typename vobj::scalar_objectD sumD_gpu_small(const vobj *lat, Integer osites)
|
||||||
{
|
{
|
||||||
@@ -211,41 +214,188 @@ inline typename vobj::scalar_objectD sumD_gpu_small(const vobj *lat, Integer osi
|
|||||||
GRID_ASSERT(ok);
|
GRID_ASSERT(ok);
|
||||||
|
|
||||||
Integer smemSize = numThreads * sizeof(sobj);
|
Integer smemSize = numThreads * sizeof(sobj);
|
||||||
// Move out of UVM
|
|
||||||
// Turns out I had messed up the synchronise after move to compute stream
|
|
||||||
// as running this on the default stream fools the synchronise
|
|
||||||
deviceVector<sobj> buffer(numBlocks);
|
deviceVector<sobj> buffer(numBlocks);
|
||||||
sobj *buffer_v = &buffer[0];
|
sobj *buffer_v = &buffer[0];
|
||||||
sobj result;
|
sobj result;
|
||||||
|
|
||||||
|
#ifdef GRID_REDUCTION_TIMING
|
||||||
|
RealD t_kernel = -usecond();
|
||||||
|
#endif
|
||||||
reduceKernel<<< numBlocks, numThreads, smemSize, computeStream >>>(lat, buffer_v, size);
|
reduceKernel<<< numBlocks, numThreads, smemSize, computeStream >>>(lat, buffer_v, size);
|
||||||
accelerator_barrier();
|
accelerator_barrier();
|
||||||
|
#ifdef GRID_REDUCTION_TIMING
|
||||||
|
t_kernel += usecond();
|
||||||
|
RealD t_d2h = -usecond();
|
||||||
|
#endif
|
||||||
acceleratorCopyFromDevice(buffer_v,&result,sizeof(result));
|
acceleratorCopyFromDevice(buffer_v,&result,sizeof(result));
|
||||||
|
#ifdef GRID_REDUCTION_TIMING
|
||||||
|
t_d2h += usecond();
|
||||||
|
std::cout << GridLogDebug << " sumD_gpu_small"
|
||||||
|
<< " sizeof(sobj)=" << sizeof(sobj)
|
||||||
|
<< " blocks=" << numBlocks << " threads=" << numThreads
|
||||||
|
<< " kernel+barrier=" << t_kernel << " us"
|
||||||
|
<< " D2H=" << t_d2h << " us" << std::endl;
|
||||||
|
#endif
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fused pack+reduce: reads R words of each vobj at word offset 'base',
|
||||||
|
// accumulates directly into iVector<iScalar<scalarD>,R> without staging
|
||||||
|
// through an intermediate bundle buffer. One HBM pass instead of three.
|
||||||
|
template <int R, class vobj, class sobj, class Iterator>
|
||||||
|
__device__ void packReduceBlocks(
|
||||||
|
const iScalar<typename vobj::vector_type> *idat,
|
||||||
|
sobj *g_odata, Iterator osites, int base, int words)
|
||||||
|
{
|
||||||
|
constexpr Iterator nsimd = vobj::Nsimd();
|
||||||
|
Iterator blockSize = blockDim.x;
|
||||||
|
|
||||||
|
extern __shared__ __align__(COALESCE_GRANULARITY) unsigned char shmem_pointer[];
|
||||||
|
sobj *sdata = (sobj *)shmem_pointer;
|
||||||
|
|
||||||
|
Iterator tid = threadIdx.x;
|
||||||
|
Iterator i = blockIdx.x * (blockSize * 2) + threadIdx.x;
|
||||||
|
Iterator gridSize = blockSize * 2 * gridDim.x;
|
||||||
|
sobj mySum = Zero();
|
||||||
|
|
||||||
|
while (i < osites * nsimd) {
|
||||||
|
Iterator lane = i % nsimd;
|
||||||
|
Iterator ss = i / nsimd;
|
||||||
|
sobj tmpD; zeroit(tmpD);
|
||||||
|
for (int k = 0; k < R; k++) {
|
||||||
|
auto w = extractLane(lane, idat[ss * words + base + k]);
|
||||||
|
iScalar<typename vobj::scalar_typeD> wd; wd = w;
|
||||||
|
tmpD._internal[k] = wd;
|
||||||
|
}
|
||||||
|
mySum += tmpD;
|
||||||
|
|
||||||
|
if (i + blockSize < osites * nsimd) {
|
||||||
|
lane = (i + blockSize) % nsimd;
|
||||||
|
ss = (i + blockSize) / nsimd;
|
||||||
|
sobj tmpD2; zeroit(tmpD2);
|
||||||
|
for (int k = 0; k < R; k++) {
|
||||||
|
auto w = extractLane(lane, idat[ss * words + base + k]);
|
||||||
|
iScalar<typename vobj::scalar_typeD> wd; wd = w;
|
||||||
|
tmpD2._internal[k] = wd;
|
||||||
|
}
|
||||||
|
mySum += tmpD2;
|
||||||
|
}
|
||||||
|
i += gridSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
reduceBlock(sdata, mySum, tid);
|
||||||
|
if (tid == 0) g_odata[blockIdx.x] = sdata[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int R, class vobj, class sobj, class Iterator>
|
||||||
|
__global__ void packReduceKernel(
|
||||||
|
const iScalar<typename vobj::vector_type> *idat,
|
||||||
|
sobj *buffer, Iterator osites, int base, int words)
|
||||||
|
{
|
||||||
|
Iterator blockSize = blockDim.x;
|
||||||
|
|
||||||
|
packReduceBlocks<R, vobj, sobj>(idat, buffer, osites, base, words);
|
||||||
|
|
||||||
|
if (gridDim.x > 1) {
|
||||||
|
const Iterator tid = threadIdx.x;
|
||||||
|
__shared__ bool amLast;
|
||||||
|
extern __shared__ __align__(COALESCE_GRANULARITY) unsigned char shmem_pointer[];
|
||||||
|
sobj *smem = (sobj *)shmem_pointer;
|
||||||
|
|
||||||
|
acceleratorFence();
|
||||||
|
|
||||||
|
if (tid == 0) {
|
||||||
|
unsigned int ticket = atomicInc(&retirementCount, gridDim.x);
|
||||||
|
amLast = (ticket == gridDim.x - 1);
|
||||||
|
}
|
||||||
|
acceleratorSynchroniseAll();
|
||||||
|
|
||||||
|
if (amLast) {
|
||||||
|
Iterator i = tid;
|
||||||
|
sobj mySum = Zero();
|
||||||
|
while (i < (Iterator)gridDim.x) {
|
||||||
|
mySum += buffer[i];
|
||||||
|
i += blockSize;
|
||||||
|
}
|
||||||
|
reduceBlock(smem, mySum, tid);
|
||||||
|
if (tid == 0) {
|
||||||
|
buffer[0] = smem[0];
|
||||||
|
retirementCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int R, class vobj>
|
||||||
|
inline void sumD_gpu_reduce_words(const vobj *lat, Integer osites,
|
||||||
|
typename vobj::scalar_typeD *ret_p, int base)
|
||||||
|
{
|
||||||
|
typedef typename vobj::vector_type vector;
|
||||||
|
typedef typename vobj::scalar_typeD scalarD;
|
||||||
|
using BundleScalarD = iVector<iScalar<scalarD>, R>;
|
||||||
|
|
||||||
|
constexpr int Nsimd = vobj::Nsimd();
|
||||||
|
const int words = sizeof(vobj) / sizeof(vector);
|
||||||
|
const iScalar<vector> *idat = (const iScalar<vector> *)lat;
|
||||||
|
|
||||||
|
Integer size = (Integer)osites * Nsimd;
|
||||||
|
Integer numThreads, numBlocks;
|
||||||
|
int ok = getNumBlocksAndThreads(size, sizeof(BundleScalarD), numThreads, numBlocks);
|
||||||
|
GRID_ASSERT(ok);
|
||||||
|
|
||||||
|
Integer smemSize = numThreads * sizeof(BundleScalarD);
|
||||||
|
deviceVector<BundleScalarD> buffer(numBlocks);
|
||||||
|
BundleScalarD *buffer_v = &buffer[0];
|
||||||
|
BundleScalarD result;
|
||||||
|
|
||||||
|
#ifdef GRID_REDUCTION_TIMING
|
||||||
|
RealD t_kernel = -usecond();
|
||||||
|
#endif
|
||||||
|
packReduceKernel<R, vobj, BundleScalarD, Integer>
|
||||||
|
<<<numBlocks, numThreads, smemSize, computeStream>>>
|
||||||
|
(idat, buffer_v, osites, base, words);
|
||||||
|
accelerator_barrier();
|
||||||
|
#ifdef GRID_REDUCTION_TIMING
|
||||||
|
t_kernel += usecond();
|
||||||
|
RealD t_d2h = -usecond();
|
||||||
|
#endif
|
||||||
|
acceleratorCopyFromDevice(buffer_v, &result, sizeof(result));
|
||||||
|
#ifdef GRID_REDUCTION_TIMING
|
||||||
|
t_d2h += usecond();
|
||||||
|
std::cout << GridLogDebug << " sumD_gpu_reduce_words R=" << R
|
||||||
|
<< " base=" << base
|
||||||
|
<< " kernel=" << t_kernel << " D2H=" << t_d2h << " us" << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int k = 0; k < R; k++)
|
||||||
|
ret_p[base + k] = TensorRemove(result._internal[k]);
|
||||||
|
}
|
||||||
|
|
||||||
template <class vobj>
|
template <class vobj>
|
||||||
inline typename vobj::scalar_objectD sumD_gpu_large(const vobj *lat, Integer osites)
|
inline typename vobj::scalar_objectD sumD_gpu_large(const vobj *lat, Integer osites)
|
||||||
{
|
{
|
||||||
typedef typename vobj::vector_type vector;
|
typedef typename vobj::vector_type vector;
|
||||||
typedef typename vobj::scalar_typeD scalarD;
|
typedef typename vobj::scalar_typeD scalarD;
|
||||||
typedef typename vobj::scalar_objectD sobj;
|
typedef typename vobj::scalar_objectD sobjD;
|
||||||
sobj ret;
|
|
||||||
scalarD *ret_p = (scalarD *)&ret;
|
|
||||||
|
|
||||||
const int words = sizeof(vobj) / sizeof(vector);
|
const int words = sizeof(vobj) / sizeof(vector);
|
||||||
|
sobjD ret; zeroit(ret);
|
||||||
|
scalarD *ret_p = (scalarD *)&ret;
|
||||||
|
|
||||||
deviceVector<vector> buffer(osites);
|
#ifdef GRID_REDUCTION_TIMING
|
||||||
vector *dat = (vector *)lat;
|
RealD t_large = -usecond();
|
||||||
vector *buf = &buffer[0];
|
#endif
|
||||||
iScalar<vector> *tbuf =(iScalar<vector> *) &buffer[0];
|
int w = 0;
|
||||||
for(int w=0;w<words;w++) {
|
while (w + 12 <= words) { sumD_gpu_reduce_words<12>(lat, osites, ret_p, w); w += 12; }
|
||||||
|
while (w + 4 <= words) { sumD_gpu_reduce_words< 4>(lat, osites, ret_p, w); w += 4; }
|
||||||
|
while (w < words) { sumD_gpu_reduce_words< 1>(lat, osites, ret_p, w); w += 1; }
|
||||||
|
#ifdef GRID_REDUCTION_TIMING
|
||||||
|
t_large += usecond();
|
||||||
|
std::cout << GridLogDebug << "sumD_gpu_large"
|
||||||
|
<< " sizeof(sobjD)=" << sizeof(sobjD)
|
||||||
|
<< " words=" << words << " total=" << t_large << " us" << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
accelerator_for(ss,osites,1,{
|
|
||||||
buf[ss] = dat[ss*words+w];
|
|
||||||
});
|
|
||||||
|
|
||||||
ret_p[w] = sumD_gpu_small(tbuf,osites);
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,5 +438,11 @@ inline typename vobj::scalar_object sum_gpu_large(const vobj *lat, Integer osite
|
|||||||
result = sumD_gpu_large(lat,osites);
|
result = sumD_gpu_large(lat,osites);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
template<class Word> Word checksum_gpu(Word *vec,uint64_t L)
|
||||||
|
{
|
||||||
|
Word w;
|
||||||
|
bzero(&w,sizeof(w));
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
NAMESPACE_END(Grid);
|
||||||
|
|||||||
@@ -11,23 +11,22 @@ inline typename vobj::scalar_objectD sumD_gpu_tensor(const vobj *lat, Integer os
|
|||||||
typedef typename vobj::scalar_object sobj;
|
typedef typename vobj::scalar_object sobj;
|
||||||
typedef typename vobj::scalar_objectD sobjD;
|
typedef typename vobj::scalar_objectD sobjD;
|
||||||
|
|
||||||
sobj identity; zeroit(identity);
|
sobjD identity; zeroit(identity);
|
||||||
sobj ret; zeroit(ret);
|
sobjD ret; zeroit(ret);
|
||||||
Integer nsimd= vobj::Nsimd();
|
|
||||||
{
|
{
|
||||||
sycl::buffer<sobj, 1> abuff(&ret, {1});
|
sycl::buffer<sobjD, 1> abuff(&ret, {1});
|
||||||
theGridAccelerator->submit([&](sycl::handler &cgh) {
|
theGridAccelerator->submit([&](sycl::handler &cgh) {
|
||||||
auto Reduction = sycl::reduction(abuff, cgh, identity, std::plus<>());
|
auto Reduction = sycl::reduction(abuff, cgh, identity, std::plus<>());
|
||||||
cgh.parallel_for(sycl::range<1>{osites},
|
cgh.parallel_for(sycl::range<1>{(size_t)osites},
|
||||||
Reduction,
|
Reduction,
|
||||||
[=](sycl::id<1> item, auto &sum) {
|
[=](sycl::id<1> item, auto &sum) {
|
||||||
auto osite = item[0];
|
sobj s = Reduce(lat[item[0]]);
|
||||||
sum +=Reduce(lat[osite]);
|
sobjD sd; sd = s;
|
||||||
|
sum += sd;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
sobjD dret; convertType(dret,ret);
|
return ret;
|
||||||
return dret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class vobj>
|
template <class vobj>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if defined(GRID_CUDA)
|
#if defined(GRID_CUDA)
|
||||||
|
|
||||||
#include <cub/cub.cuh>
|
#include <cub/cub.cuh>
|
||||||
#define gpucub cub
|
#define gpucub cub
|
||||||
#define gpuError_t cudaError_t
|
#define gpuError_t cudaError_t
|
||||||
@@ -57,8 +56,13 @@ inline void sliceSumReduction_cub_small(const vobj *Data,
|
|||||||
//copy offsets to device
|
//copy offsets to device
|
||||||
acceleratorCopyToDeviceAsynch(&offsets[0],d_offsets,sizeof(int)*(rd+1),computeStream);
|
acceleratorCopyToDeviceAsynch(&offsets[0],d_offsets,sizeof(int)*(rd+1),computeStream);
|
||||||
|
|
||||||
|
#if defined(__CUDACC__) && (__CUDACC_VER_MAJOR__ >= 13)
|
||||||
|
#define GRID_CUB_SUM_OP ::cuda::std::plus<>{}
|
||||||
|
#else
|
||||||
|
#define GRID_CUB_SUM_OP ::gpucub::Sum()
|
||||||
|
#endif
|
||||||
|
|
||||||
gpuError_t gpuErr = gpucub::DeviceSegmentedReduce::Reduce(temp_storage_array, temp_storage_bytes, rb_p,d_out, rd, d_offsets, d_offsets+1, ::gpucub::Sum(), zero_init, computeStream);
|
gpuError_t gpuErr = gpucub::DeviceSegmentedReduce::Reduce(temp_storage_array, temp_storage_bytes, rb_p,d_out, rd, d_offsets, d_offsets+1, GRID_CUB_SUM_OP, zero_init, computeStream);
|
||||||
if (gpuErr!=gpuSuccess) {
|
if (gpuErr!=gpuSuccess) {
|
||||||
std::cout << GridLogError << "Lattice_slicesum_gpu.h: Encountered error during gpucub::DeviceSegmentedReduce::Reduce (setup)! Error: " << gpuErr <<std::endl;
|
std::cout << GridLogError << "Lattice_slicesum_gpu.h: Encountered error during gpucub::DeviceSegmentedReduce::Reduce (setup)! Error: " << gpuErr <<std::endl;
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
@@ -82,12 +86,14 @@ inline void sliceSumReduction_cub_small(const vobj *Data,
|
|||||||
});
|
});
|
||||||
|
|
||||||
//issue segmented reductions in computeStream
|
//issue segmented reductions in computeStream
|
||||||
gpuErr = gpucub::DeviceSegmentedReduce::Reduce(temp_storage_array, temp_storage_bytes, rb_p, d_out, rd, d_offsets, d_offsets+1,::gpucub::Sum(), zero_init, computeStream);
|
gpuErr = gpucub::DeviceSegmentedReduce::Reduce(temp_storage_array, temp_storage_bytes, rb_p, d_out, rd, d_offsets, d_offsets+1, GRID_CUB_SUM_OP, zero_init, computeStream);
|
||||||
if (gpuErr!=gpuSuccess) {
|
if (gpuErr!=gpuSuccess) {
|
||||||
std::cout << GridLogError << "Lattice_slicesum_gpu.h: Encountered error during gpucub::DeviceSegmentedReduce::Reduce! Error: " << gpuErr <<std::endl;
|
std::cout << GridLogError << "Lattice_slicesum_gpu.h: Encountered error during gpucub::DeviceSegmentedReduce::Reduce! Error: " << gpuErr <<std::endl;
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef GRID_CUB_SUM_OP
|
||||||
|
|
||||||
acceleratorCopyFromDeviceAsynch(d_out,&lvSum[0],rd*sizeof(vobj),computeStream);
|
acceleratorCopyFromDeviceAsynch(d_out,&lvSum[0],rd*sizeof(vobj),computeStream);
|
||||||
|
|
||||||
//sync after copy
|
//sync after copy
|
||||||
|
|||||||
@@ -124,6 +124,68 @@ Lattice<iScalar<iScalar<iMatrix<vComplexD, N> > > > Inverse(const Lattice<iScala
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<int N>
|
||||||
|
Lattice<iMatrix<iScalar<iScalar<iScalar<vComplexD> > > , N> > Inverse(const Lattice<iMatrix<iScalar<iScalar<iScalar<vComplexD> > >, N> > &Umu)
|
||||||
|
{
|
||||||
|
GridBase *grid=Umu.Grid();
|
||||||
|
auto lvol = grid->lSites();
|
||||||
|
Lattice<iMatrix<iScalar<iScalar<iScalar<vComplexD> > >, N > > ret(grid);
|
||||||
|
|
||||||
|
autoView(Umu_v,Umu,CpuRead);
|
||||||
|
autoView(ret_v,ret,CpuWrite);
|
||||||
|
thread_for(site,lvol,{
|
||||||
|
Eigen::MatrixXcd EigenU = Eigen::MatrixXcd::Zero(N,N);
|
||||||
|
Coordinate lcoor;
|
||||||
|
grid->LocalIndexToLocalCoor(site, lcoor);
|
||||||
|
iMatrix<iScalar<iScalar<iScalar<ComplexD> > >, N > Us;
|
||||||
|
iMatrix<iScalar<iScalar<iScalar<ComplexD> > >, N > Ui;
|
||||||
|
peekLocalSite(Us, Umu_v, lcoor);
|
||||||
|
for(int i=0;i<N;i++){
|
||||||
|
for(int j=0;j<N;j++){
|
||||||
|
EigenU(i,j) = Us(i,j)()()();
|
||||||
|
}}
|
||||||
|
Eigen::MatrixXcd EigenUinv = EigenU.inverse();
|
||||||
|
for(int i=0;i<N;i++){
|
||||||
|
for(int j=0;j<N;j++){
|
||||||
|
Ui(i,j)()()() = EigenUinv(i,j);
|
||||||
|
}}
|
||||||
|
pokeLocalSite(Ui,ret_v,lcoor);
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int N>
|
||||||
|
Lattice<iMatrix<iScalar<iScalar<iScalar<iScalar<vComplexD> > > > , N> > Inverse(const Lattice<iMatrix<iScalar<iScalar<iScalar<iScalar<vComplexD> > > >, N> > &Umu)
|
||||||
|
{
|
||||||
|
GridBase *grid=Umu.Grid();
|
||||||
|
auto lvol = grid->lSites();
|
||||||
|
Lattice<iMatrix<iScalar<iScalar<iScalar<iScalar<vComplexD> > > >, N > > ret(grid);
|
||||||
|
|
||||||
|
autoView(Umu_v,Umu,CpuRead);
|
||||||
|
autoView(ret_v,ret,CpuWrite);
|
||||||
|
thread_for(site,lvol,{
|
||||||
|
Eigen::MatrixXcd EigenU = Eigen::MatrixXcd::Zero(N,N);
|
||||||
|
Coordinate lcoor;
|
||||||
|
grid->LocalIndexToLocalCoor(site, lcoor);
|
||||||
|
iMatrix<iScalar<iScalar<iScalar<iScalar<ComplexD> > > >, N > Us;
|
||||||
|
iMatrix<iScalar<iScalar<iScalar<iScalar<ComplexD> > > >, N > Ui;
|
||||||
|
peekLocalSite(Us, Umu_v, lcoor);
|
||||||
|
for(int i=0;i<N;i++){
|
||||||
|
for(int j=0;j<N;j++){
|
||||||
|
EigenU(i,j) = Us(i,j)()()()();
|
||||||
|
}}
|
||||||
|
Eigen::MatrixXcd EigenUinv = EigenU.inverse();
|
||||||
|
for(int i=0;i<N;i++){
|
||||||
|
for(int j=0;j<N;j++){
|
||||||
|
Ui(i,j)()()()() = EigenUinv(i,j);
|
||||||
|
}}
|
||||||
|
pokeLocalSite(Ui,ret_v,lcoor);
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
NAMESPACE_END(Grid);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -191,6 +191,15 @@ accelerator_inline void convertType(vComplexD & out, const ComplexD & in) {
|
|||||||
accelerator_inline void convertType(vComplexD2 & out, const ComplexD & in) {
|
accelerator_inline void convertType(vComplexD2 & out, const ComplexD & in) {
|
||||||
((ComplexD*)&out)[acceleratorSIMTlane(vComplexD::Nsimd()*2)] = in;
|
((ComplexD*)&out)[acceleratorSIMTlane(vComplexD::Nsimd()*2)] = in;
|
||||||
}
|
}
|
||||||
|
// Lex chart: one lane, so there is no lane to select. Needed because the
|
||||||
|
// coalesced accessor hands the kernel a scalar object while the destination
|
||||||
|
// is still the vector type.
|
||||||
|
accelerator_inline void convertType(sComplexF & out, const ComplexF & in) {
|
||||||
|
out.v = in;
|
||||||
|
}
|
||||||
|
accelerator_inline void convertType(sComplexD & out, const ComplexD & in) {
|
||||||
|
out.v = in;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
accelerator_inline void convertType(vComplexF & out, const vComplexD2 & in) {
|
accelerator_inline void convertType(vComplexF & out, const vComplexD2 & in) {
|
||||||
|
|||||||
@@ -2,3 +2,7 @@
|
|||||||
|
|
||||||
int Grid::BinaryIO::latticeWriteMaxRetry = -1;
|
int Grid::BinaryIO::latticeWriteMaxRetry = -1;
|
||||||
Grid::BinaryIO::IoPerf Grid::BinaryIO::lastPerf;
|
Grid::BinaryIO::IoPerf Grid::BinaryIO::lastPerf;
|
||||||
|
|
||||||
|
// Target size of a single contiguous file extent under BINARYIO_AGGREGATE.
|
||||||
|
// 4MB is around the knee for Lustre; exposed so it can be swept at runtime.
|
||||||
|
uint64_t Grid::BinaryIO::aggregateTargetBytes = 4*1024*1024;
|
||||||
|
|||||||
+462
-11
@@ -39,6 +39,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
NAMESPACE_BEGIN(Grid);
|
||||||
@@ -87,6 +88,7 @@ class BinaryIO {
|
|||||||
|
|
||||||
static IoPerf lastPerf;
|
static IoPerf lastPerf;
|
||||||
static int latticeWriteMaxRetry;
|
static int latticeWriteMaxRetry;
|
||||||
|
static uint64_t aggregateTargetBytes;
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
// more byte manipulation helpers
|
// more byte manipulation helpers
|
||||||
@@ -253,12 +255,402 @@ class BinaryIO {
|
|||||||
// Read or Write distributed lexico array of ANY object to a specific location in file
|
// Read or Write distributed lexico array of ANY object to a specific location in file
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static const int BINARYIO_AGGREGATE = 0x20;
|
||||||
static const int BINARYIO_MASTER_APPEND = 0x10;
|
static const int BINARYIO_MASTER_APPEND = 0x10;
|
||||||
static const int BINARYIO_UNORDERED = 0x08;
|
static const int BINARYIO_UNORDERED = 0x08;
|
||||||
static const int BINARYIO_LEXICOGRAPHIC = 0x04;
|
static const int BINARYIO_LEXICOGRAPHIC = 0x04;
|
||||||
static const int BINARYIO_READ = 0x02;
|
static const int BINARYIO_READ = 0x02;
|
||||||
static const int BINARYIO_WRITE = 0x01;
|
static const int BINARYIO_WRITE = 0x01;
|
||||||
|
|
||||||
|
// Single point of control for the aggregated path. Setting
|
||||||
|
// GRID_BINARYIO_NOAGGREGATE falls back to plain lexicographic I/O.
|
||||||
|
static int DefaultControl(void)
|
||||||
|
{
|
||||||
|
static int ctrl = getenv("GRID_BINARYIO_NOAGGREGATE")
|
||||||
|
? BINARYIO_LEXICOGRAPHIC
|
||||||
|
: BINARYIO_LEXICOGRAPHIC|BINARYIO_AGGREGATE;
|
||||||
|
return ctrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_MPI_IO
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Aggregation: self controlled transposition onto an I/O friendly layout.
|
||||||
|
//
|
||||||
|
// Under BINARYIO_LEXICOGRAPHIC the subarray file view handed to MPI-IO has
|
||||||
|
// contiguous runs of only lLattice[0]*sizeof(fobj) bytes -- a few KB for
|
||||||
|
// typical local volumes. Rather than rely on collective buffering to repair
|
||||||
|
// that, redistribute the payload ourselves so every rank owns a contiguous
|
||||||
|
// range of the global lexicographic site ordering, then issue large plain
|
||||||
|
// contiguous writes.
|
||||||
|
//
|
||||||
|
// "Un-splitting" the nunsplit fastest dimensions means the row of ranks
|
||||||
|
// sharing the remaining process coordinates collectively owns whole global
|
||||||
|
// hyperplanes. All data movement is then confined to that row communicator.
|
||||||
|
// Every rank still owns exactly lSites() sites afterwards, so the exchange is
|
||||||
|
// a pure permutation and needs no divisibility condition on the process grid.
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
struct AggregationPlan {
|
||||||
|
int nunsplit{0}; // number of fastest dimensions un-split
|
||||||
|
int rowsize{0}; // ranks in the aggregation (row) communicator
|
||||||
|
int rowrank{0}; // our logical (lexicographic) index within the row
|
||||||
|
uint64_t lsites{0}; // sites per rank -- invariant under the permutation
|
||||||
|
uint64_t chunk{0}; // sites in one globally contiguous run owned by the row
|
||||||
|
std::unique_ptr<CartesianCommunicator> rowcomm;
|
||||||
|
// counts and displacements are indexed by rank within rowcomm
|
||||||
|
std::vector<int> sendcounts, senddispls, recvcounts, recvdispls;
|
||||||
|
std::vector<uint64_t> scatter; // recv slot -> slot in the aggregated buffer
|
||||||
|
std::vector<uint64_t> extentGsite; // global lex site index of extent start
|
||||||
|
std::vector<uint64_t> extentLocal; // offset of extent within aggregated buffer
|
||||||
|
std::vector<uint64_t> extentSites; // sites in this extent
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void BuildAggregationPlan(GridBase *grid,uint64_t fobjSize,AggregationPlan &p)
|
||||||
|
{
|
||||||
|
int ndim = grid->Dimensions();
|
||||||
|
Coordinate psizes = grid->ProcessorGrid();
|
||||||
|
Coordinate pcoor = grid->ThisProcessorCoor();
|
||||||
|
Coordinate gLattice= grid->GlobalDimensions();
|
||||||
|
Coordinate lLattice= grid->LocalDimensions();
|
||||||
|
Coordinate lstart = grid->LocalStarts();
|
||||||
|
|
||||||
|
uint64_t lsites = grid->lSites();
|
||||||
|
p.lsites = lsites;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Un-splitting dims 0..k-1 gives the row a contiguous run of
|
||||||
|
// chunk(k) = prod_{d<k} gLattice[d] * lLattice[k]
|
||||||
|
// sites, and each rank writes extents of min(chunk,lsites). Take the
|
||||||
|
// smallest k that reaches the target so we disturb as few dimensions --
|
||||||
|
// and move as little data -- as possible.
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
int k = ndim-1;
|
||||||
|
for(int trial=1; trial<ndim; trial++){
|
||||||
|
uint64_t chunk = lLattice[trial];
|
||||||
|
for(int d=0; d<trial; d++) chunk *= gLattice[d];
|
||||||
|
if ( std::min(chunk,lsites)*fobjSize >= aggregateTargetBytes ) { k = trial; break; }
|
||||||
|
}
|
||||||
|
p.nunsplit = k;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// The box the row collectively owns, expressed in global coordinates.
|
||||||
|
// Restricting the global lexicographic order to this box preserves the
|
||||||
|
// ordering, so the row index below is monotone in the global index.
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
Coordinate B(ndim), S(ndim);
|
||||||
|
for(int d=0; d<ndim; d++){
|
||||||
|
if ( d<k ) { B[d] = gLattice[d]; S[d] = 0; }
|
||||||
|
else { B[d] = lLattice[d]; S[d] = lstart[d]; }
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t chunk = lLattice[k];
|
||||||
|
for(int d=0; d<k; d++) chunk *= gLattice[d];
|
||||||
|
p.chunk = chunk;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Row communicator: the ranks sharing the process coordinates of the slow
|
||||||
|
// (still split) dimensions. This is the sub-division the Cartesian
|
||||||
|
// communicator already performs for AllToAll(dim,...), widened from one
|
||||||
|
// dimension to the k fastest.
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
Coordinate row(ndim,1);
|
||||||
|
for(int d=0; d<k; d++) row[d] = psizes[d];
|
||||||
|
int srank;
|
||||||
|
p.rowcomm.reset(new CartesianCommunicator(row,*grid,srank));
|
||||||
|
p.rowsize = p.rowcomm->ProcessorCount();
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Our logical index in the row is the forward lexicographic index of the
|
||||||
|
// un-split process coordinates, so that increasing logical index means
|
||||||
|
// increasing global lexicographic position in the file. The communicator
|
||||||
|
// numbers its own ranks by the reversed (MPI) convention, so build the map
|
||||||
|
// between the two rather than assuming either.
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
int64_t logical=0, lstride=1;
|
||||||
|
for(int d=0; d<k; d++){ logical += pcoor[d]*lstride; lstride *= psizes[d]; }
|
||||||
|
GRID_ASSERT(lstride == (int64_t)p.rowsize);
|
||||||
|
p.rowrank = (int)logical;
|
||||||
|
|
||||||
|
std::vector<uint64_t> commOf(p.rowsize,0);
|
||||||
|
commOf[p.rowrank] = (uint64_t)p.rowcomm->ThisRank();
|
||||||
|
p.rowcomm->GlobalSumVector(&commOf[0],p.rowsize);
|
||||||
|
|
||||||
|
uint64_t mystart = (uint64_t)p.rowrank * lsites;
|
||||||
|
uint64_t myend = mystart + lsites;
|
||||||
|
|
||||||
|
Coordinate lcoor(ndim), bcoor(ndim), gcoor(ndim);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Send side. Walking our local sites in local lexicographic order walks
|
||||||
|
// the row index monotonically, so the send buffer is iodata untouched and
|
||||||
|
// we need only the per destination counts.
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
std::vector<int> sendLogical(p.rowsize,0);
|
||||||
|
for(uint64_t L=0; L<lsites; L++){
|
||||||
|
Lexicographic::CoorFromIndex(lcoor,L,lLattice);
|
||||||
|
for(int d=0; d<ndim; d++) bcoor[d] = (d<k) ? (lstart[d]+lcoor[d]) : lcoor[d];
|
||||||
|
int64_t ri; Lexicographic::IndexFromCoor(bcoor,ri,B);
|
||||||
|
sendLogical[ ri/(int64_t)lsites ]++;
|
||||||
|
}
|
||||||
|
p.sendcounts.assign(p.rowsize,0);
|
||||||
|
p.senddispls.assign(p.rowsize,0);
|
||||||
|
{ int64_t disp=0;
|
||||||
|
for(int d=0; d<p.rowsize; d++){ // send buffer is in logical order
|
||||||
|
int c = (int)commOf[d];
|
||||||
|
p.sendcounts[c] = sendLogical[d];
|
||||||
|
p.senddispls[c] = (int)disp;
|
||||||
|
disp += sendLogical[d];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Receive side. For each slot of our aggregated range work out which rank
|
||||||
|
// of the row owns it. Within one source the slots arrive in increasing row
|
||||||
|
// index order, which is the order the source sends them in.
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
std::vector<int> recvLogical(p.rowsize,0), recvDisplLogical(p.rowsize,0);
|
||||||
|
std::vector<int> source(lsites);
|
||||||
|
for(uint64_t pos=0; pos<lsites; pos++){
|
||||||
|
Lexicographic::CoorFromIndex(bcoor,(int64_t)(mystart+pos),B);
|
||||||
|
int64_t j=0, jstride=1;
|
||||||
|
for(int d=0; d<k; d++){ j += (bcoor[d]/lLattice[d])*jstride; jstride *= psizes[d]; }
|
||||||
|
source[pos] = (int)j;
|
||||||
|
recvLogical[j]++;
|
||||||
|
}
|
||||||
|
p.recvcounts.assign(p.rowsize,0);
|
||||||
|
p.recvdispls.assign(p.rowsize,0);
|
||||||
|
{ int64_t disp=0;
|
||||||
|
for(int s=0; s<p.rowsize; s++){ // recv buffer is in logical order
|
||||||
|
int c = (int)commOf[s];
|
||||||
|
recvDisplLogical[s] = (int)disp;
|
||||||
|
p.recvcounts[c] = recvLogical[s];
|
||||||
|
p.recvdispls[c] = (int)disp;
|
||||||
|
disp += recvLogical[s];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.scatter.resize(lsites);
|
||||||
|
{
|
||||||
|
std::vector<int> fill(p.rowsize,0);
|
||||||
|
for(uint64_t pos=0; pos<lsites; pos++){
|
||||||
|
int j = source[pos];
|
||||||
|
p.scatter[ recvDisplLogical[j] + fill[j]++ ] = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// The two sides are derived independently; make them check each other.
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
{
|
||||||
|
std::vector<uint64_t> sendc(p.rowsize),recvc(p.rowsize);
|
||||||
|
for(int c=0;c<p.rowsize;c++) sendc[c]=(uint64_t)p.sendcounts[c];
|
||||||
|
p.rowcomm->AllToAll(&sendc[0],&recvc[0],1,sizeof(uint64_t));
|
||||||
|
for(int c=0;c<p.rowsize;c++) GRID_ASSERT((int)recvc[c]==p.recvcounts[c]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Decompose our range into globally contiguous file extents.
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
for(uint64_t c = mystart/chunk; c <= (myend-1)/chunk; c++){
|
||||||
|
uint64_t lo = std::max(mystart, c*chunk);
|
||||||
|
uint64_t hi = std::min(myend, (c+1)*chunk);
|
||||||
|
Lexicographic::CoorFromIndex(bcoor,(int64_t)(c*chunk),B);
|
||||||
|
for(int d=0;d<ndim;d++) gcoor[d] = (d<k) ? bcoor[d] : bcoor[d]+S[d];
|
||||||
|
int64_t gbase; Lexicographic::IndexFromCoor(gcoor,gbase,gLattice);
|
||||||
|
p.extentGsite.push_back( (uint64_t)gbase + (lo - c*chunk) );
|
||||||
|
p.extentLocal.push_back( lo - mystart );
|
||||||
|
p.extentSites.push_back( hi - lo );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ReportAggregationPlan(GridBase *grid,const AggregationPlan &p,uint64_t fobjSize,const char *what)
|
||||||
|
{
|
||||||
|
if ( !grid->IsBoss() ) return;
|
||||||
|
std::cout << GridLogMessage << "IOobject: aggregate " << what
|
||||||
|
<< " un-splitting " << p.nunsplit << " fastest dimensions, row of "
|
||||||
|
<< p.rowsize << " ranks" << std::endl;
|
||||||
|
std::cout << GridLogMessage << "IOobject: aggregate " << p.extentSites.size()
|
||||||
|
<< " extent(s)/rank, first " << p.extentSites[0]*fobjSize/1024./1024. << " MB"
|
||||||
|
<< " (target " << aggregateTargetBytes/1024./1024. << " MB)" << std::endl;
|
||||||
|
std::cout << GridLogMessage << "IOobject: aggregate buffer overhead "
|
||||||
|
<< p.lsites*fobjSize/1024./1024. << " MB/rank" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Stage timings. The interesting quantity is the slowest rank, since every
|
||||||
|
// stage is followed sooner or later by a synchronisation, so reduce with
|
||||||
|
// GlobalMax rather than reporting whatever the boss happened to see.
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
static inline void ReportStages(GridBase *grid,const char *what,
|
||||||
|
const std::vector<const char *> &names,
|
||||||
|
std::vector<RealD> &useconds)
|
||||||
|
{
|
||||||
|
GRID_ASSERT(names.size()==useconds.size());
|
||||||
|
for(uint64_t i=0;i<useconds.size();i++) grid->GlobalMax(useconds[i]);
|
||||||
|
if ( grid->IsBoss() ) {
|
||||||
|
std::cout << GridLogMessage << "IOobject: aggregate " << what << " stages (max over ranks, s):";
|
||||||
|
for(uint64_t i=0;i<names.size();i++)
|
||||||
|
std::cout << " " << names[i] << " " << useconds[i]/1.0e6;
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class fobj>
|
||||||
|
static inline void AggregateExchange(GridBase *grid,AggregationPlan &p,std::vector<fobj> &iodata,
|
||||||
|
std::vector<fobj> &aggregated,int forward)
|
||||||
|
{
|
||||||
|
uint64_t lsites = p.lsites;
|
||||||
|
GridStopWatch talloc,tperm,tcomm;
|
||||||
|
|
||||||
|
talloc.Start();
|
||||||
|
std::vector<fobj> tmp(lsites);
|
||||||
|
talloc.Stop();
|
||||||
|
|
||||||
|
if ( forward ) { // iodata (local order) -> aggregated (lexicographic order)
|
||||||
|
tcomm.Start();
|
||||||
|
p.rowcomm->AllToAllV(&iodata[0],p.sendcounts,p.senddispls,
|
||||||
|
&tmp[0], p.recvcounts,p.recvdispls,sizeof(fobj));
|
||||||
|
tcomm.Stop();
|
||||||
|
tperm.Start();
|
||||||
|
thread_for(s,lsites,{ aggregated[p.scatter[s]] = tmp[s]; });
|
||||||
|
tperm.Stop();
|
||||||
|
} else { // aggregated -> iodata, the exact mirror
|
||||||
|
tperm.Start();
|
||||||
|
thread_for(s,lsites,{ tmp[s] = aggregated[p.scatter[s]]; });
|
||||||
|
tperm.Stop();
|
||||||
|
tcomm.Start();
|
||||||
|
p.rowcomm->AllToAllV(&tmp[0], p.recvcounts,p.recvdispls,
|
||||||
|
&iodata[0],p.sendcounts,p.senddispls,sizeof(fobj));
|
||||||
|
tcomm.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<RealD> us = { (RealD)talloc.useconds(), (RealD)tperm.useconds(), (RealD)tcomm.useconds() };
|
||||||
|
ReportStages(grid,forward?"exchange (write)":"exchange (read)",
|
||||||
|
{"alloc","permute","alltoallv"},us);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class fobj>
|
||||||
|
static inline void AggregateWrite(GridBase *grid,AggregationPlan &p,std::vector<fobj> &aggregated,
|
||||||
|
std::string file,uint64_t offset)
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// All ranks write concurrently into a shared file, so the file must exist
|
||||||
|
// before any of them open it for update, but it does NOT have to be the
|
||||||
|
// right length first: the extents tile the record exactly, so writing them
|
||||||
|
// extends a short file to precisely offset+payload.
|
||||||
|
//
|
||||||
|
// Records are created in sequence, so this payload ends the file: the
|
||||||
|
// length must end up precisely offset+payload. Anything beyond is left
|
||||||
|
// over from whatever the file previously held and must not survive -- a
|
||||||
|
// shorter new record written over a longer old one would otherwise leave
|
||||||
|
// a trailing fragment of the previous contents masquerading as data.
|
||||||
|
// That is the only case needing a truncate, so stat first and truncate
|
||||||
|
// afterwards only when the size actually came out wrong. Measured on
|
||||||
|
// Frontier, an unconditional truncate up front cost 0.22 to 5.4 s per
|
||||||
|
// record -- 15 to 25% of a 19 GB write and 100% of a small one -- while
|
||||||
|
// create, open and close together cost a few milliseconds. It is per
|
||||||
|
// record, so multi record files do not amortise it away.
|
||||||
|
//
|
||||||
|
// ::truncate is used because the C++ standard library cannot express this.
|
||||||
|
// std::filebuf has no length operation at all; ios::trunc only truncates to
|
||||||
|
// zero; seeking past the end and writing a byte can grow a file but never
|
||||||
|
// shrink one; and there is no portable way to recover a descriptor from a
|
||||||
|
// stream in order to call ftruncate. C++17 does finally offer
|
||||||
|
// std::filesystem::resize_file, but that would be Grid's first <filesystem>
|
||||||
|
// dependency and needs -lstdc++fs on the older toolchains still in use.
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
GridStopWatch tcreate,ttrunc,tbar,topen,twrite,tclose,tskew;
|
||||||
|
uint64_t need = offset + (uint64_t)grid->_gsites*sizeof(fobj);
|
||||||
|
|
||||||
|
tcreate.Start();
|
||||||
|
if ( grid->IsBoss() ) {
|
||||||
|
// opening for update needs the file to exist; create one only if not
|
||||||
|
std::fstream probe(file,std::ios::binary|std::ios::out|std::ios::in);
|
||||||
|
if ( !probe.is_open() ) {
|
||||||
|
std::ofstream create(file,std::ios::binary|std::ios::out);
|
||||||
|
create.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tcreate.Stop();
|
||||||
|
|
||||||
|
tbar.Start();
|
||||||
|
grid->Barrier();
|
||||||
|
tbar.Stop();
|
||||||
|
|
||||||
|
std::ofstream fout;
|
||||||
|
fout.exceptions( std::fstream::failbit | std::fstream::badbit );
|
||||||
|
try {
|
||||||
|
topen.Start();
|
||||||
|
fout.open(file,std::ios::binary|std::ios::out|std::ios::in);
|
||||||
|
topen.Stop();
|
||||||
|
twrite.Start();
|
||||||
|
for(uint64_t e=0;e<p.extentSites.size();e++){
|
||||||
|
fout.seekp(offset + p.extentGsite[e]*sizeof(fobj));
|
||||||
|
fout.write((char *)&aggregated[p.extentLocal[e]],p.extentSites[e]*sizeof(fobj));
|
||||||
|
}
|
||||||
|
twrite.Stop();
|
||||||
|
tclose.Start();
|
||||||
|
fout.close(); // flushes the stream buffer; does not force writeback
|
||||||
|
tclose.Stop();
|
||||||
|
} catch (const std::fstream::failure& exc) {
|
||||||
|
std::cout << GridLogError << "Error in aggregate write to " << file << std::endl;
|
||||||
|
std::cout << GridLogError << "Exception description: " << exc.what() << std::endl;
|
||||||
|
GridAbort();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// Timed apart from the truncate that follows it. seek+write above is the
|
||||||
|
// slowest rank; this barrier is what the fastest rank then waits, so the
|
||||||
|
// pair separates the write cost from the spread across ranks. Folding it
|
||||||
|
// into the truncate makes a millisecond stat look like a second.
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
tskew.Start();
|
||||||
|
grid->Barrier(); // every extent must be on its way first
|
||||||
|
tskew.Stop();
|
||||||
|
|
||||||
|
ttrunc.Start();
|
||||||
|
if ( grid->IsBoss() ) {
|
||||||
|
struct stat sb;
|
||||||
|
int ierr = ::stat(file.c_str(),&sb);
|
||||||
|
GRID_ASSERT(ierr==0);
|
||||||
|
if ( (uint64_t)sb.st_size != need ) { // only when a longer record preceded us
|
||||||
|
ierr = ::truncate(file.c_str(),(off_t)need);
|
||||||
|
GRID_ASSERT(ierr==0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
grid->Barrier();
|
||||||
|
ttrunc.Stop();
|
||||||
|
|
||||||
|
std::vector<RealD> us = { (RealD)tcreate.useconds(), (RealD)tbar.useconds(),
|
||||||
|
(RealD)topen.useconds(), (RealD)twrite.useconds(),
|
||||||
|
(RealD)tclose.useconds(), (RealD)tskew.useconds(),
|
||||||
|
(RealD)ttrunc.useconds() };
|
||||||
|
ReportStages(grid,"write",{"create","barrier","open","seek+write","close","skew","stat+truncate"},us);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class fobj>
|
||||||
|
static inline void AggregateRead(GridBase *grid,AggregationPlan &p,std::vector<fobj> &aggregated,
|
||||||
|
std::string file,uint64_t offset)
|
||||||
|
{
|
||||||
|
GridStopWatch topen,tread,tclose;
|
||||||
|
std::ifstream fin;
|
||||||
|
topen.Start();
|
||||||
|
fin.open(file,std::ios::binary|std::ios::in);
|
||||||
|
topen.Stop();
|
||||||
|
tread.Start();
|
||||||
|
for(uint64_t e=0;e<p.extentSites.size();e++){
|
||||||
|
fin.seekg(offset + p.extentGsite[e]*sizeof(fobj));
|
||||||
|
fin.read((char *)&aggregated[p.extentLocal[e]],p.extentSites[e]*sizeof(fobj));
|
||||||
|
GRID_ASSERT(fin.fail()==0);
|
||||||
|
}
|
||||||
|
tread.Stop();
|
||||||
|
tclose.Start();
|
||||||
|
fin.close();
|
||||||
|
tclose.Stop();
|
||||||
|
|
||||||
|
std::vector<RealD> us = { (RealD)topen.useconds(), (RealD)tread.useconds(), (RealD)tclose.useconds() };
|
||||||
|
ReportStages(grid,"read",{"open","seek+read","close"},us);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
template<class word,class fobj>
|
template<class word,class fobj>
|
||||||
static inline void IOobject(word w,
|
static inline void IOobject(word w,
|
||||||
GridBase *grid,
|
GridBase *grid,
|
||||||
@@ -302,6 +694,18 @@ class BinaryIO {
|
|||||||
lStart[d] = 0;
|
lStart[d] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Aggregate the lexicographic layout onto contiguous per rank extents
|
||||||
|
// ourselves rather than leaving it to MPI-IO collective buffering
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
int aggregate = (control & BINARYIO_AGGREGATE)
|
||||||
|
&& (control & BINARYIO_LEXICOGRAPHIC)
|
||||||
|
&& !(control & BINARYIO_MASTER_APPEND)
|
||||||
|
&& (nrank > 1);
|
||||||
|
#ifndef USE_MPI_IO
|
||||||
|
GRID_ASSERT(aggregate==0); // BINARYIO_AGGREGATE requires MPI
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_MPI_IO
|
#ifdef USE_MPI_IO
|
||||||
std::vector<int> distribs(ndim,MPI_DISTRIBUTE_BLOCK);
|
std::vector<int> distribs(ndim,MPI_DISTRIBUTE_BLOCK);
|
||||||
std::vector<int> dargs (ndim,MPI_DISTRIBUTE_DFLT_DARG);
|
std::vector<int> dargs (ndim,MPI_DISTRIBUTE_DFLT_DARG);
|
||||||
@@ -329,6 +733,8 @@ class BinaryIO {
|
|||||||
ierr = MPI_Type_contiguous(numword,mpiword,&mpiObject); GRID_ASSERT(ierr==0);
|
ierr = MPI_Type_contiguous(numword,mpiword,&mpiObject); GRID_ASSERT(ierr==0);
|
||||||
ierr = MPI_Type_commit(&mpiObject);
|
ierr = MPI_Type_commit(&mpiObject);
|
||||||
|
|
||||||
|
// The subarray view is what aggregation exists to avoid; do not build it
|
||||||
|
if ( !aggregate ) {
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
// File global array data type
|
// File global array data type
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -340,6 +746,7 @@ class BinaryIO {
|
|||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
ierr=MPI_Type_create_subarray(ndim,&lLattice[0],&lLattice[0],&lStart[0],MPI_ORDER_FORTRAN, mpiObject,&localArray); GRID_ASSERT(ierr==0);
|
ierr=MPI_Type_create_subarray(ndim,&lLattice[0],&lLattice[0],&lStart[0],MPI_ORDER_FORTRAN, mpiObject,&localArray); GRID_ASSERT(ierr==0);
|
||||||
ierr=MPI_Type_commit(&localArray); GRID_ASSERT(ierr==0);
|
ierr=MPI_Type_commit(&localArray); GRID_ASSERT(ierr==0);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -358,7 +765,19 @@ class BinaryIO {
|
|||||||
|
|
||||||
timer.Start();
|
timer.Start();
|
||||||
|
|
||||||
if ( (control & BINARYIO_LEXICOGRAPHIC) && (nrank > 1) ) {
|
if ( aggregate ) {
|
||||||
|
#ifdef USE_MPI_IO
|
||||||
|
std::cout<< GridLogMessage<<"IOobject: aggregate read I/O "<< file<< std::endl;
|
||||||
|
AggregationPlan plan;
|
||||||
|
BuildAggregationPlan(grid,sizeof(fobj),plan);
|
||||||
|
ReportAggregationPlan(grid,plan,sizeof(fobj),"read");
|
||||||
|
std::vector<fobj> aggregated(lsites);
|
||||||
|
AggregateRead(grid,plan,aggregated,file,offset);
|
||||||
|
AggregateExchange(grid,plan,iodata,aggregated,0);
|
||||||
|
#else
|
||||||
|
GRID_ASSERT(0);
|
||||||
|
#endif
|
||||||
|
} else if ( (control & BINARYIO_LEXICOGRAPHIC) && (nrank > 1) ) {
|
||||||
#ifdef USE_MPI_IO
|
#ifdef USE_MPI_IO
|
||||||
std::cout<< GridLogMessage<<"IOobject: MPI read I/O "<< file<< std::endl;
|
std::cout<< GridLogMessage<<"IOobject: MPI read I/O "<< file<< std::endl;
|
||||||
ierr=MPI_File_open(grid->communicator,(char *) file.c_str(), MPI_MODE_RDONLY, MPI_INFO_NULL, &fh); GRID_ASSERT(ierr==0);
|
ierr=MPI_File_open(grid->communicator,(char *) file.c_str(), MPI_MODE_RDONLY, MPI_INFO_NULL, &fh); GRID_ASSERT(ierr==0);
|
||||||
@@ -416,7 +835,25 @@ class BinaryIO {
|
|||||||
grid->Barrier();
|
grid->Barrier();
|
||||||
|
|
||||||
timer.Start();
|
timer.Start();
|
||||||
if ( (control & BINARYIO_LEXICOGRAPHIC) && (nrank > 1) ) {
|
if ( aggregate ) {
|
||||||
|
#ifdef USE_MPI_IO
|
||||||
|
std::cout << GridLogMessage <<"IOobject: aggregate write I/O " << file << std::endl;
|
||||||
|
AggregationPlan plan;
|
||||||
|
BuildAggregationPlan(grid,sizeof(fobj),plan);
|
||||||
|
ReportAggregationPlan(grid,plan,sizeof(fobj),"write");
|
||||||
|
std::vector<fobj> aggregated(lsites);
|
||||||
|
AggregateExchange(grid,plan,iodata,aggregated,1);
|
||||||
|
AggregateWrite(grid,plan,aggregated,file,offset);
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// Not every rank ends at the end of the payload, so the position can
|
||||||
|
// not be recovered from a file handle. Callers (Lime record chaining)
|
||||||
|
// rely on this being the first byte past the record.
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
offset = offset + (uint64_t)grid->_gsites*sizeof(fobj);
|
||||||
|
#else
|
||||||
|
GRID_ASSERT(0);
|
||||||
|
#endif
|
||||||
|
} else if ( (control & BINARYIO_LEXICOGRAPHIC) && (nrank > 1) ) {
|
||||||
#ifdef USE_MPI_IO
|
#ifdef USE_MPI_IO
|
||||||
std::cout << GridLogMessage <<"IOobject: MPI write I/O " << file << std::endl;
|
std::cout << GridLogMessage <<"IOobject: MPI write I/O " << file << std::endl;
|
||||||
ierr = MPI_File_open(grid->communicator, (char *)file.c_str(), MPI_MODE_RDWR | MPI_MODE_CREATE, MPI_INFO_NULL, &fh);
|
ierr = MPI_File_open(grid->communicator, (char *)file.c_str(), MPI_MODE_RDWR | MPI_MODE_CREATE, MPI_INFO_NULL, &fh);
|
||||||
@@ -461,12 +898,26 @@ class BinaryIO {
|
|||||||
|
|
||||||
std::ofstream fout;
|
std::ofstream fout;
|
||||||
fout.exceptions ( std::fstream::failbit | std::fstream::badbit );
|
fout.exceptions ( std::fstream::failbit | std::fstream::badbit );
|
||||||
try {
|
|
||||||
if (offset) { // Must already exist and contain data
|
////////////////////////////////////////////////////////////////////
|
||||||
fout.open(file,std::ios::binary|std::ios::out|std::ios::in);
|
// Grid's model is that the boss rank performs the metadata
|
||||||
} else { // Allow create
|
// operations and every other rank only seeks and writes into a file
|
||||||
fout.open(file,std::ios::binary|std::ios::out);
|
// that already exists. Opening with ios::out on all ranks broke that:
|
||||||
|
// it is O_TRUNC, so a rank opening late truncated the file back to
|
||||||
|
// zero after an earlier rank had written its segment, leaving a hole
|
||||||
|
// in its place. The barriers around this block are outside it and do
|
||||||
|
// not order the opens against the writes. Let the boss create and
|
||||||
|
// empty the file, then everyone opens for update only. Same resulting
|
||||||
|
// length, one metadata operation instead of one per rank, no race.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
if ( !offset && grid->IsBoss() ) { // offset zero: this record starts the file
|
||||||
|
std::ofstream create(file,std::ios::binary|std::ios::out);
|
||||||
|
create.close();
|
||||||
}
|
}
|
||||||
|
grid->Barrier();
|
||||||
|
|
||||||
|
try {
|
||||||
|
fout.open(file,std::ios::binary|std::ios::out|std::ios::in);
|
||||||
} catch (const std::fstream::failure& exc) {
|
} catch (const std::fstream::failure& exc) {
|
||||||
std::cout << GridLogError << "Error in opening the file " << file << " for output" <<std::endl;
|
std::cout << GridLogError << "Error in opening the file " << file << " for output" <<std::endl;
|
||||||
std::cout << GridLogError << "Exception description: " << exc.what() << std::endl;
|
std::cout << GridLogError << "Exception description: " << exc.what() << std::endl;
|
||||||
@@ -548,7 +999,7 @@ class BinaryIO {
|
|||||||
uint32_t &nersc_csum,
|
uint32_t &nersc_csum,
|
||||||
uint32_t &scidac_csuma,
|
uint32_t &scidac_csuma,
|
||||||
uint32_t &scidac_csumb,
|
uint32_t &scidac_csumb,
|
||||||
int control=BINARYIO_LEXICOGRAPHIC
|
int control=DefaultControl()
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
typedef typename vobj::scalar_object sobj;
|
typedef typename vobj::scalar_object sobj;
|
||||||
@@ -587,7 +1038,7 @@ class BinaryIO {
|
|||||||
uint32_t &nersc_csum,
|
uint32_t &nersc_csum,
|
||||||
uint32_t &scidac_csuma,
|
uint32_t &scidac_csuma,
|
||||||
uint32_t &scidac_csumb,
|
uint32_t &scidac_csumb,
|
||||||
int control=BINARYIO_LEXICOGRAPHIC)
|
int control=DefaultControl())
|
||||||
{
|
{
|
||||||
typedef typename vobj::scalar_object sobj;
|
typedef typename vobj::scalar_object sobj;
|
||||||
typedef typename vobj::Realified::scalar_type word; word w=0;
|
typedef typename vobj::Realified::scalar_type word; word w=0;
|
||||||
@@ -674,7 +1125,7 @@ class BinaryIO {
|
|||||||
std::cout << GridLogMessage << "RNG read I/O on file " << file << std::endl;
|
std::cout << GridLogMessage << "RNG read I/O on file " << file << std::endl;
|
||||||
|
|
||||||
std::vector<RNGstate> iodata(lsites);
|
std::vector<RNGstate> iodata(lsites);
|
||||||
IOobject(w,grid,iodata,file,offset,format,BINARYIO_READ|BINARYIO_LEXICOGRAPHIC,
|
IOobject(w,grid,iodata,file,offset,format,BINARYIO_READ|DefaultControl(),
|
||||||
nersc_csum,scidac_csuma,scidac_csumb);
|
nersc_csum,scidac_csuma,scidac_csumb);
|
||||||
|
|
||||||
timer.Start();
|
timer.Start();
|
||||||
@@ -753,7 +1204,7 @@ class BinaryIO {
|
|||||||
});
|
});
|
||||||
timer.Stop();
|
timer.Stop();
|
||||||
|
|
||||||
IOobject(w,grid,iodata,file,offset,format,BINARYIO_WRITE|BINARYIO_LEXICOGRAPHIC,
|
IOobject(w,grid,iodata,file,offset,format,BINARYIO_WRITE|DefaultControl(),
|
||||||
nersc_csum,scidac_csuma,scidac_csumb);
|
nersc_csum,scidac_csuma,scidac_csumb);
|
||||||
iodata.resize(1);
|
iodata.resize(1);
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ class GridLimeReader : public BinaryIO {
|
|||||||
// Read a generic lattice field and verify checksum
|
// Read a generic lattice field and verify checksum
|
||||||
////////////////////////////////////////////
|
////////////////////////////////////////////
|
||||||
template<class vobj>
|
template<class vobj>
|
||||||
void readLimeLatticeBinaryObject(Lattice<vobj> &field,std::string record_name,int control=BINARYIO_LEXICOGRAPHIC)
|
void readLimeLatticeBinaryObject(Lattice<vobj> &field,std::string record_name,int control=BINARYIO_LEXICOGRAPHIC|BINARYIO_AGGREGATE)
|
||||||
{
|
{
|
||||||
typedef typename vobj::scalar_object sobj;
|
typedef typename vobj::scalar_object sobj;
|
||||||
scidacChecksum scidacChecksum_;
|
scidacChecksum scidacChecksum_;
|
||||||
@@ -260,8 +260,7 @@ class GridLimeReader : public BinaryIO {
|
|||||||
<< " / field= " << n2ck << " / rdiff= " << GRID_FIELD_NORM_CALC(FieldNormMetaData_,n2ck) << std::endl;
|
<< " / field= " << n2ck << " / rdiff= " << GRID_FIELD_NORM_CALC(FieldNormMetaData_,n2ck) << std::endl;
|
||||||
GRID_FIELD_NORM_CHECK(FieldNormMetaData_,n2ck);
|
GRID_FIELD_NORM_CHECK(FieldNormMetaData_,n2ck);
|
||||||
}
|
}
|
||||||
// assert(scidacChecksumVerify(scidacChecksum_,scidac_csuma,scidac_csumb)==1);
|
assert(scidacChecksumVerify(scidacChecksum_,scidac_csuma,scidac_csumb)==1);
|
||||||
scidacChecksumVerify(scidacChecksum_,scidac_csuma,scidac_csumb);
|
|
||||||
|
|
||||||
// find out if next field is a GridFieldNorm
|
// find out if next field is a GridFieldNorm
|
||||||
return;
|
return;
|
||||||
@@ -415,7 +414,7 @@ class GridLimeWriter : public BinaryIO
|
|||||||
// in communicator used by the field.Grid()
|
// in communicator used by the field.Grid()
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
template<class vobj>
|
template<class vobj>
|
||||||
void writeLimeLatticeBinaryObject(Lattice<vobj> &field,std::string record_name,int control=BINARYIO_LEXICOGRAPHIC)
|
void writeLimeLatticeBinaryObject(Lattice<vobj> &field,std::string record_name,int control=BINARYIO_LEXICOGRAPHIC|BINARYIO_AGGREGATE)
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// NB: FILE and iostream are jointly writing disjoint sequences in the
|
// NB: FILE and iostream are jointly writing disjoint sequences in the
|
||||||
@@ -520,7 +519,7 @@ class ScidacWriter : public GridLimeWriter {
|
|||||||
template <class vobj, class userRecord>
|
template <class vobj, class userRecord>
|
||||||
void writeScidacFieldRecord(Lattice<vobj> &field,userRecord _userRecord,
|
void writeScidacFieldRecord(Lattice<vobj> &field,userRecord _userRecord,
|
||||||
const unsigned int recordScientificPrec = 0,
|
const unsigned int recordScientificPrec = 0,
|
||||||
int control=BINARYIO_LEXICOGRAPHIC)
|
int control=BINARYIO_LEXICOGRAPHIC|BINARYIO_AGGREGATE)
|
||||||
{
|
{
|
||||||
GridBase * grid = field.Grid();
|
GridBase * grid = field.Grid();
|
||||||
|
|
||||||
@@ -562,7 +561,7 @@ class ScidacReader : public GridLimeReader {
|
|||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
template <class vobj, class userRecord>
|
template <class vobj, class userRecord>
|
||||||
void readScidacFieldRecord(Lattice<vobj> &field,userRecord &_userRecord,
|
void readScidacFieldRecord(Lattice<vobj> &field,userRecord &_userRecord,
|
||||||
int control=BINARYIO_LEXICOGRAPHIC)
|
int control=BINARYIO_LEXICOGRAPHIC|BINARYIO_AGGREGATE)
|
||||||
{
|
{
|
||||||
typedef typename vobj::scalar_object sobj;
|
typedef typename vobj::scalar_object sobj;
|
||||||
GridBase * grid = field.Grid();
|
GridBase * grid = field.Grid();
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ template<class Impl>
|
|||||||
class GaugeStatistics
|
class GaugeStatistics
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void operator()(Lattice<vLorentzColourMatrixD> & data,FieldMetaData &header)
|
void operator()(typename Impl::Field & data,FieldMetaData &header)
|
||||||
{
|
{
|
||||||
header.link_trace = WilsonLoops<Impl>::linkTrace(data);
|
header.link_trace = WilsonLoops<Impl>::linkTrace(data);
|
||||||
header.plaquette = WilsonLoops<Impl>::avgPlaquette(data);
|
header.plaquette = WilsonLoops<Impl>::avgPlaquette(data);
|
||||||
@@ -188,6 +188,8 @@ public:
|
|||||||
};
|
};
|
||||||
typedef GaugeStatistics<PeriodicGimplD> PeriodicGaugeStatistics;
|
typedef GaugeStatistics<PeriodicGimplD> PeriodicGaugeStatistics;
|
||||||
typedef GaugeStatistics<ConjugateGimplD> ConjugateGaugeStatistics;
|
typedef GaugeStatistics<ConjugateGimplD> ConjugateGaugeStatistics;
|
||||||
|
typedef GaugeStatistics<lexPeriodicGimplD> lexPeriodicGaugeStatistics;
|
||||||
|
typedef GaugeStatistics<lexConjugateGimplD> lexConjugateGaugeStatistics;
|
||||||
template<> inline void PrepareMetaData<vLorentzColourMatrixD>(Lattice<vLorentzColourMatrixD> & field, FieldMetaData &header)
|
template<> inline void PrepareMetaData<vLorentzColourMatrixD>(Lattice<vLorentzColourMatrixD> & field, FieldMetaData &header)
|
||||||
{
|
{
|
||||||
GridBase *grid = field.Grid();
|
GridBase *grid = field.Grid();
|
||||||
@@ -197,6 +199,15 @@ template<> inline void PrepareMetaData<vLorentzColourMatrixD>(Lattice<vLorentzCo
|
|||||||
GridMetaData(grid,header);
|
GridMetaData(grid,header);
|
||||||
MachineCharacteristics(header);
|
MachineCharacteristics(header);
|
||||||
}
|
}
|
||||||
|
template<> inline void PrepareMetaData<sLorentzColourMatrixD>(Lattice<sLorentzColourMatrixD> & field, FieldMetaData &header)
|
||||||
|
{
|
||||||
|
GridBase *grid = field.Grid();
|
||||||
|
std::string format = getFormatString<sLorentzColourMatrixD>();
|
||||||
|
header.floating_point = format;
|
||||||
|
header.checksum = 0x0; // Nersc checksum unused in ILDG, Scidac
|
||||||
|
GridMetaData(grid,header);
|
||||||
|
MachineCharacteristics(header);
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Utilities ; these are QCD aware
|
// Utilities ; these are QCD aware
|
||||||
|
|||||||
+73
-16
@@ -122,7 +122,7 @@ public:
|
|||||||
field.checksum = std::stoul(header["CHECKSUM"],0,16);
|
field.checksum = std::stoul(header["CHECKSUM"],0,16);
|
||||||
field.ensemble_id = header["ENSEMBLE_ID"];
|
field.ensemble_id = header["ENSEMBLE_ID"];
|
||||||
field.ensemble_label = header["ENSEMBLE_LABEL"];
|
field.ensemble_label = header["ENSEMBLE_LABEL"];
|
||||||
// field.sequence_number = std::stol(header["SEQUENCE_NUMBER"]);
|
field.sequence_number = std::stol(header["SEQUENCE_NUMBER"]);
|
||||||
field.creator = header["CREATOR"];
|
field.creator = header["CREATOR"];
|
||||||
field.creator_hardware = header["CREATOR_HARDWARE"];
|
field.creator_hardware = header["CREATOR_HARDWARE"];
|
||||||
field.creation_date = header["CREATION_DATE"];
|
field.creation_date = header["CREATION_DATE"];
|
||||||
@@ -136,12 +136,14 @@ public:
|
|||||||
// Now the meat: the object readers
|
// Now the meat: the object readers
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template<class GaugeStats=PeriodicGaugeStatistics>
|
template<class GaugeFieldType,class GaugeStats>
|
||||||
static inline void readConfiguration(GaugeField &Umu,
|
static inline void readConfigurationInternal(GaugeFieldType &Umu,
|
||||||
FieldMetaData& header,
|
FieldMetaData& header,
|
||||||
std::string file,
|
std::string file,
|
||||||
GaugeStats GaugeStatisticsCalculator=GaugeStats())
|
GaugeStats GaugeStatisticsCalculator)
|
||||||
{
|
{
|
||||||
|
typedef typename GaugeFieldType::vector_object vobj;
|
||||||
|
typedef typename GaugeFieldType::scalar_object sobj;
|
||||||
|
|
||||||
GridBase *grid = Umu.Grid();
|
GridBase *grid = Umu.Grid();
|
||||||
uint64_t offset = readHeader(file,Umu.Grid(),header);
|
uint64_t offset = readHeader(file,Umu.Grid(),header);
|
||||||
@@ -162,24 +164,24 @@ public:
|
|||||||
const std::string stNC = std::to_string( Nc ) ;
|
const std::string stNC = std::to_string( Nc ) ;
|
||||||
if ( header.data_type == std::string("4D_SU"+stNC+"_GAUGE") ) {
|
if ( header.data_type == std::string("4D_SU"+stNC+"_GAUGE") ) {
|
||||||
if ( ieee32 || ieee32big ) {
|
if ( ieee32 || ieee32big ) {
|
||||||
BinaryIO::readLatticeObject<vLorentzColourMatrixD, LorentzColour2x3F>
|
BinaryIO::readLatticeObject<vobj, LorentzColour2x3F>
|
||||||
(Umu,file,Gauge3x2munger<LorentzColour2x3F,LorentzColourMatrix>(), offset,format,
|
(Umu,file,Gauge3x2munger<LorentzColour2x3F,sobj>(), offset,format,
|
||||||
nersc_csum,scidac_csuma,scidac_csumb);
|
nersc_csum,scidac_csuma,scidac_csumb);
|
||||||
}
|
}
|
||||||
if ( ieee64 || ieee64big ) {
|
if ( ieee64 || ieee64big ) {
|
||||||
BinaryIO::readLatticeObject<vLorentzColourMatrixD, LorentzColour2x3D>
|
BinaryIO::readLatticeObject<vobj, LorentzColour2x3D>
|
||||||
(Umu,file,Gauge3x2munger<LorentzColour2x3D,LorentzColourMatrix>(),offset,format,
|
(Umu,file,Gauge3x2munger<LorentzColour2x3D,sobj>(),offset,format,
|
||||||
nersc_csum,scidac_csuma,scidac_csumb);
|
nersc_csum,scidac_csuma,scidac_csumb);
|
||||||
}
|
}
|
||||||
} else if ( header.data_type == std::string("4D_SU"+stNC+"_GAUGE_"+stNC+"x"+stNC) ) {
|
} else if ( header.data_type == std::string("4D_SU"+stNC+"_GAUGE_"+stNC+"x"+stNC) ) {
|
||||||
if ( ieee32 || ieee32big ) {
|
if ( ieee32 || ieee32big ) {
|
||||||
BinaryIO::readLatticeObject<vLorentzColourMatrixD,LorentzColourMatrixF>
|
BinaryIO::readLatticeObject<vobj,LorentzColourMatrixF>
|
||||||
(Umu,file,GaugeSimpleMunger<LorentzColourMatrixF,LorentzColourMatrix>(),offset,format,
|
(Umu,file,GaugeSimpleMunger<LorentzColourMatrixF,sobj>(),offset,format,
|
||||||
nersc_csum,scidac_csuma,scidac_csumb);
|
nersc_csum,scidac_csuma,scidac_csumb);
|
||||||
}
|
}
|
||||||
if ( ieee64 || ieee64big ) {
|
if ( ieee64 || ieee64big ) {
|
||||||
BinaryIO::readLatticeObject<vLorentzColourMatrixD,LorentzColourMatrixD>
|
BinaryIO::readLatticeObject<vobj,LorentzColourMatrixD>
|
||||||
(Umu,file,GaugeSimpleMunger<LorentzColourMatrixD,LorentzColourMatrix>(),offset,format,
|
(Umu,file,GaugeSimpleMunger<LorentzColourMatrixD,sobj>(),offset,format,
|
||||||
nersc_csum,scidac_csuma,scidac_csumb);
|
nersc_csum,scidac_csuma,scidac_csumb);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -203,7 +205,6 @@ public:
|
|||||||
std::cerr << " plaqs " << clone.plaquette << " " << header.plaquette << std::endl;
|
std::cerr << " plaqs " << clone.plaquette << " " << header.plaquette << std::endl;
|
||||||
std::cerr << " trace " << clone.link_trace<< " " << header.link_trace<< std::endl;
|
std::cerr << " trace " << clone.link_trace<< " " << header.link_trace<< std::endl;
|
||||||
std::cerr << " nersc_csum " <<std::hex<< nersc_csum << " " << header.checksum<< std::dec<< std::endl;
|
std::cerr << " nersc_csum " <<std::hex<< nersc_csum << " " << header.checksum<< std::dec<< std::endl;
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
if(exitOnReadPlaquetteMismatch()) GRID_ASSERT(fabs(clone.plaquette -header.plaquette ) < 1.0e-5 );
|
if(exitOnReadPlaquetteMismatch()) GRID_ASSERT(fabs(clone.plaquette -header.plaquette ) < 1.0e-5 );
|
||||||
GRID_ASSERT(fabs(clone.link_trace-header.link_trace) < 1.0e-6 );
|
GRID_ASSERT(fabs(clone.link_trace-header.link_trace) < 1.0e-6 );
|
||||||
@@ -212,6 +213,26 @@ public:
|
|||||||
std::cout<<GridLogMessage <<"NERSC Configuration "<<file<< " and plaquette, link trace, and checksum agree"<<std::endl;
|
std::cout<<GridLogMessage <<"NERSC Configuration "<<file<< " and plaquette, link trace, and checksum agree"<<std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constrained frontends: vectorised and lexicographic gauge fields
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
template<class GaugeStats=PeriodicGaugeStatistics>
|
||||||
|
static inline void readConfiguration(Lattice<vLorentzColourMatrixD> &Umu,
|
||||||
|
FieldMetaData& header,
|
||||||
|
std::string file,
|
||||||
|
GaugeStats GaugeStatisticsCalculator=GaugeStats())
|
||||||
|
{
|
||||||
|
readConfigurationInternal(Umu,header,file,GaugeStatisticsCalculator);
|
||||||
|
}
|
||||||
|
template<class GaugeStats=lexPeriodicGaugeStatistics>
|
||||||
|
static inline void readConfiguration(Lattice<sLorentzColourMatrixD> &Umu,
|
||||||
|
FieldMetaData& header,
|
||||||
|
std::string file,
|
||||||
|
GaugeStats GaugeStatisticsCalculator=GaugeStats())
|
||||||
|
{
|
||||||
|
readConfigurationInternal(Umu,header,file,GaugeStatisticsCalculator);
|
||||||
|
}
|
||||||
|
|
||||||
// Preferred interface
|
// Preferred interface
|
||||||
template<class GaugeStats=PeriodicGaugeStatistics>
|
template<class GaugeStats=PeriodicGaugeStatistics>
|
||||||
static inline void writeConfiguration(Lattice<vLorentzColourMatrixD > &Umu,
|
static inline void writeConfiguration(Lattice<vLorentzColourMatrixD > &Umu,
|
||||||
@@ -220,7 +241,16 @@ public:
|
|||||||
std::string ens_id = std::string("UKQCD"),
|
std::string ens_id = std::string("UKQCD"),
|
||||||
unsigned int sequence_number = 1)
|
unsigned int sequence_number = 1)
|
||||||
{
|
{
|
||||||
writeConfiguration(Umu,file,0,1,ens_label,ens_id,sequence_number);
|
writeConfigurationInternal<Lattice<vLorentzColourMatrixD>,GaugeStats>(Umu,file,0,1,ens_label,ens_id,sequence_number);
|
||||||
|
}
|
||||||
|
template<class GaugeStats=lexPeriodicGaugeStatistics>
|
||||||
|
static inline void writeConfiguration(Lattice<sLorentzColourMatrixD > &Umu,
|
||||||
|
std::string file,
|
||||||
|
std::string ens_label = std::string("DWF"),
|
||||||
|
std::string ens_id = std::string("UKQCD"),
|
||||||
|
unsigned int sequence_number = 1)
|
||||||
|
{
|
||||||
|
writeConfigurationInternal<Lattice<sLorentzColourMatrixD>,GaugeStats>(Umu,file,0,1,ens_label,ens_id,sequence_number);
|
||||||
}
|
}
|
||||||
template<class GaugeStats=PeriodicGaugeStatistics>
|
template<class GaugeStats=PeriodicGaugeStatistics>
|
||||||
static inline void writeConfiguration(Lattice<vLorentzColourMatrixD > &Umu,
|
static inline void writeConfiguration(Lattice<vLorentzColourMatrixD > &Umu,
|
||||||
@@ -231,7 +261,29 @@ public:
|
|||||||
std::string ens_id = std::string("UKQCD"),
|
std::string ens_id = std::string("UKQCD"),
|
||||||
unsigned int sequence_number = 1)
|
unsigned int sequence_number = 1)
|
||||||
{
|
{
|
||||||
typedef vLorentzColourMatrixD vobj;
|
writeConfigurationInternal<Lattice<vLorentzColourMatrixD>,GaugeStats>(Umu,file,two_row,bits32,ens_label,ens_id,sequence_number);
|
||||||
|
}
|
||||||
|
template<class GaugeStats=lexPeriodicGaugeStatistics>
|
||||||
|
static inline void writeConfiguration(Lattice<sLorentzColourMatrixD > &Umu,
|
||||||
|
std::string file,
|
||||||
|
int two_row,
|
||||||
|
int bits32,
|
||||||
|
std::string ens_label = std::string("DWF"),
|
||||||
|
std::string ens_id = std::string("UKQCD"),
|
||||||
|
unsigned int sequence_number = 1)
|
||||||
|
{
|
||||||
|
writeConfigurationInternal<Lattice<sLorentzColourMatrixD>,GaugeStats>(Umu,file,two_row,bits32,ens_label,ens_id,sequence_number);
|
||||||
|
}
|
||||||
|
template<class GaugeFieldType,class GaugeStats>
|
||||||
|
static inline void writeConfigurationInternal(GaugeFieldType &Umu,
|
||||||
|
std::string file,
|
||||||
|
int two_row,
|
||||||
|
int bits32,
|
||||||
|
std::string ens_label = std::string("DWF"),
|
||||||
|
std::string ens_id = std::string("UKQCD"),
|
||||||
|
unsigned int sequence_number = 1)
|
||||||
|
{
|
||||||
|
typedef typename GaugeFieldType::vector_object vobj;
|
||||||
typedef typename vobj::scalar_object sobj;
|
typedef typename vobj::scalar_object sobj;
|
||||||
|
|
||||||
FieldMetaData header;
|
FieldMetaData header;
|
||||||
@@ -280,6 +332,9 @@ public:
|
|||||||
if ( grid->IsBoss() ) {
|
if ( grid->IsBoss() ) {
|
||||||
writeHeader(header,file);
|
writeHeader(header,file);
|
||||||
}
|
}
|
||||||
|
// The header is written twice; the checksum is only known after the data.
|
||||||
|
// Other ranks must not proceed to read it until the rewrite has landed.
|
||||||
|
grid->Barrier();
|
||||||
|
|
||||||
std::cout<<GridLogMessage <<"Written NERSC Configuration on "<< file << " checksum "
|
std::cout<<GridLogMessage <<"Written NERSC Configuration on "<< file << " checksum "
|
||||||
<<std::hex<<header.checksum
|
<<std::hex<<header.checksum
|
||||||
@@ -333,6 +388,9 @@ public:
|
|||||||
if ( grid->IsBoss() ) {
|
if ( grid->IsBoss() ) {
|
||||||
offset = writeHeader(header,file);
|
offset = writeHeader(header,file);
|
||||||
}
|
}
|
||||||
|
// As for the gauge field: the checksum is only known after the data, so
|
||||||
|
// the header is rewritten. Hold the other ranks until that has landed.
|
||||||
|
grid->Barrier();
|
||||||
|
|
||||||
std::cout<<GridLogMessage
|
std::cout<<GridLogMessage
|
||||||
<<"Written NERSC RNG STATE "<<file<< " checksum "
|
<<"Written NERSC RNG STATE "<<file<< " checksum "
|
||||||
@@ -374,7 +432,6 @@ public:
|
|||||||
|
|
||||||
if ( nersc_csum != header.checksum ) {
|
if ( nersc_csum != header.checksum ) {
|
||||||
std::cerr << "checksum mismatch "<<std::hex<< nersc_csum <<" "<<header.checksum<<std::dec<<std::endl;
|
std::cerr << "checksum mismatch "<<std::hex<< nersc_csum <<" "<<header.checksum<<std::dec<<std::endl;
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
GRID_ASSERT(nersc_csum == header.checksum );
|
GRID_ASSERT(nersc_csum == header.checksum );
|
||||||
|
|
||||||
|
|||||||
@@ -93,8 +93,7 @@ inline uint64_t cyclecount(void){
|
|||||||
}
|
}
|
||||||
#elif defined __x86_64__
|
#elif defined __x86_64__
|
||||||
inline uint64_t cyclecount(void){
|
inline uint64_t cyclecount(void){
|
||||||
uint64_t ret = __rdtsc();
|
return (uint64_t)0;
|
||||||
return (uint64_t)ret;
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
|||||||
+92
-43
@@ -127,7 +127,6 @@ typedef iSpinMatrix<ComplexD > SpinMatrixD;
|
|||||||
typedef iSpinMatrix<vComplex > vSpinMatrix;
|
typedef iSpinMatrix<vComplex > vSpinMatrix;
|
||||||
typedef iSpinMatrix<vComplexF> vSpinMatrixF;
|
typedef iSpinMatrix<vComplexF> vSpinMatrixF;
|
||||||
typedef iSpinMatrix<vComplexD> vSpinMatrixD;
|
typedef iSpinMatrix<vComplexD> vSpinMatrixD;
|
||||||
typedef iSpinMatrix<vComplexD2> vSpinMatrixD2;
|
|
||||||
|
|
||||||
// Colour Matrix
|
// Colour Matrix
|
||||||
typedef iColourMatrix<Complex > ColourMatrix;
|
typedef iColourMatrix<Complex > ColourMatrix;
|
||||||
@@ -137,7 +136,6 @@ typedef iColourMatrix<ComplexD > ColourMatrixD;
|
|||||||
typedef iColourMatrix<vComplex > vColourMatrix;
|
typedef iColourMatrix<vComplex > vColourMatrix;
|
||||||
typedef iColourMatrix<vComplexF> vColourMatrixF;
|
typedef iColourMatrix<vComplexF> vColourMatrixF;
|
||||||
typedef iColourMatrix<vComplexD> vColourMatrixD;
|
typedef iColourMatrix<vComplexD> vColourMatrixD;
|
||||||
typedef iColourMatrix<vComplexD2> vColourMatrixD2;
|
|
||||||
|
|
||||||
// SpinColour matrix
|
// SpinColour matrix
|
||||||
typedef iSpinColourMatrix<Complex > SpinColourMatrix;
|
typedef iSpinColourMatrix<Complex > SpinColourMatrix;
|
||||||
@@ -147,7 +145,6 @@ typedef iSpinColourMatrix<ComplexD > SpinColourMatrixD;
|
|||||||
typedef iSpinColourMatrix<vComplex > vSpinColourMatrix;
|
typedef iSpinColourMatrix<vComplex > vSpinColourMatrix;
|
||||||
typedef iSpinColourMatrix<vComplexF> vSpinColourMatrixF;
|
typedef iSpinColourMatrix<vComplexF> vSpinColourMatrixF;
|
||||||
typedef iSpinColourMatrix<vComplexD> vSpinColourMatrixD;
|
typedef iSpinColourMatrix<vComplexD> vSpinColourMatrixD;
|
||||||
typedef iSpinColourMatrix<vComplexD2> vSpinColourMatrixD2;
|
|
||||||
|
|
||||||
// SpinColourSpinColour matrix
|
// SpinColourSpinColour matrix
|
||||||
typedef iSpinColourSpinColourMatrix<Complex > SpinColourSpinColourMatrix;
|
typedef iSpinColourSpinColourMatrix<Complex > SpinColourSpinColourMatrix;
|
||||||
@@ -157,17 +154,6 @@ typedef iSpinColourSpinColourMatrix<ComplexD > SpinColourSpinColourMatrixD;
|
|||||||
typedef iSpinColourSpinColourMatrix<vComplex > vSpinColourSpinColourMatrix;
|
typedef iSpinColourSpinColourMatrix<vComplex > vSpinColourSpinColourMatrix;
|
||||||
typedef iSpinColourSpinColourMatrix<vComplexF> vSpinColourSpinColourMatrixF;
|
typedef iSpinColourSpinColourMatrix<vComplexF> vSpinColourSpinColourMatrixF;
|
||||||
typedef iSpinColourSpinColourMatrix<vComplexD> vSpinColourSpinColourMatrixD;
|
typedef iSpinColourSpinColourMatrix<vComplexD> vSpinColourSpinColourMatrixD;
|
||||||
typedef iSpinColourSpinColourMatrix<vComplexD2> vSpinColourSpinColourMatrixD2;
|
|
||||||
|
|
||||||
// SpinColourSpinColour matrix
|
|
||||||
typedef iSpinColourSpinColourMatrix<Complex > SpinColourSpinColourMatrix;
|
|
||||||
typedef iSpinColourSpinColourMatrix<ComplexF > SpinColourSpinColourMatrixF;
|
|
||||||
typedef iSpinColourSpinColourMatrix<ComplexD > SpinColourSpinColourMatrixD;
|
|
||||||
|
|
||||||
typedef iSpinColourSpinColourMatrix<vComplex > vSpinColourSpinColourMatrix;
|
|
||||||
typedef iSpinColourSpinColourMatrix<vComplexF> vSpinColourSpinColourMatrixF;
|
|
||||||
typedef iSpinColourSpinColourMatrix<vComplexD> vSpinColourSpinColourMatrixD;
|
|
||||||
typedef iSpinColourSpinColourMatrix<vComplexD2> vSpinColourSpinColourMatrixD2;
|
|
||||||
|
|
||||||
// LorentzColour
|
// LorentzColour
|
||||||
typedef iLorentzColourMatrix<Complex > LorentzColourMatrix;
|
typedef iLorentzColourMatrix<Complex > LorentzColourMatrix;
|
||||||
@@ -177,7 +163,6 @@ typedef iLorentzColourMatrix<ComplexD > LorentzColourMatrixD;
|
|||||||
typedef iLorentzColourMatrix<vComplex > vLorentzColourMatrix;
|
typedef iLorentzColourMatrix<vComplex > vLorentzColourMatrix;
|
||||||
typedef iLorentzColourMatrix<vComplexF> vLorentzColourMatrixF;
|
typedef iLorentzColourMatrix<vComplexF> vLorentzColourMatrixF;
|
||||||
typedef iLorentzColourMatrix<vComplexD> vLorentzColourMatrixD;
|
typedef iLorentzColourMatrix<vComplexD> vLorentzColourMatrixD;
|
||||||
typedef iLorentzColourMatrix<vComplexD2> vLorentzColourMatrixD2;
|
|
||||||
|
|
||||||
// LorentzComplex
|
// LorentzComplex
|
||||||
typedef iLorentzComplex<Complex > LorentzComplex;
|
typedef iLorentzComplex<Complex > LorentzComplex;
|
||||||
@@ -196,7 +181,6 @@ typedef iDoubleStoredColourMatrix<ComplexD > DoubleStoredColourMatrixD;
|
|||||||
typedef iDoubleStoredColourMatrix<vComplex > vDoubleStoredColourMatrix;
|
typedef iDoubleStoredColourMatrix<vComplex > vDoubleStoredColourMatrix;
|
||||||
typedef iDoubleStoredColourMatrix<vComplexF> vDoubleStoredColourMatrixF;
|
typedef iDoubleStoredColourMatrix<vComplexF> vDoubleStoredColourMatrixF;
|
||||||
typedef iDoubleStoredColourMatrix<vComplexD> vDoubleStoredColourMatrixD;
|
typedef iDoubleStoredColourMatrix<vComplexD> vDoubleStoredColourMatrixD;
|
||||||
typedef iDoubleStoredColourMatrix<vComplexD2> vDoubleStoredColourMatrixD2;
|
|
||||||
|
|
||||||
//G-parity flavour matrix
|
//G-parity flavour matrix
|
||||||
typedef iGparityFlavourMatrix<Complex> GparityFlavourMatrix;
|
typedef iGparityFlavourMatrix<Complex> GparityFlavourMatrix;
|
||||||
@@ -206,7 +190,6 @@ typedef iGparityFlavourMatrix<ComplexD> GparityFlavourMatrixD;
|
|||||||
typedef iGparityFlavourMatrix<vComplex> vGparityFlavourMatrix;
|
typedef iGparityFlavourMatrix<vComplex> vGparityFlavourMatrix;
|
||||||
typedef iGparityFlavourMatrix<vComplexF> vGparityFlavourMatrixF;
|
typedef iGparityFlavourMatrix<vComplexF> vGparityFlavourMatrixF;
|
||||||
typedef iGparityFlavourMatrix<vComplexD> vGparityFlavourMatrixD;
|
typedef iGparityFlavourMatrix<vComplexD> vGparityFlavourMatrixD;
|
||||||
typedef iGparityFlavourMatrix<vComplexD2> vGparityFlavourMatrixD2;
|
|
||||||
|
|
||||||
|
|
||||||
// Spin vector
|
// Spin vector
|
||||||
@@ -217,7 +200,6 @@ typedef iSpinVector<ComplexD> SpinVectorD;
|
|||||||
typedef iSpinVector<vComplex > vSpinVector;
|
typedef iSpinVector<vComplex > vSpinVector;
|
||||||
typedef iSpinVector<vComplexF> vSpinVectorF;
|
typedef iSpinVector<vComplexF> vSpinVectorF;
|
||||||
typedef iSpinVector<vComplexD> vSpinVectorD;
|
typedef iSpinVector<vComplexD> vSpinVectorD;
|
||||||
typedef iSpinVector<vComplexD2> vSpinVectorD2;
|
|
||||||
|
|
||||||
// Colour vector
|
// Colour vector
|
||||||
typedef iColourVector<Complex > ColourVector;
|
typedef iColourVector<Complex > ColourVector;
|
||||||
@@ -227,7 +209,6 @@ typedef iColourVector<ComplexD> ColourVectorD;
|
|||||||
typedef iColourVector<vComplex > vColourVector;
|
typedef iColourVector<vComplex > vColourVector;
|
||||||
typedef iColourVector<vComplexF> vColourVectorF;
|
typedef iColourVector<vComplexF> vColourVectorF;
|
||||||
typedef iColourVector<vComplexD> vColourVectorD;
|
typedef iColourVector<vComplexD> vColourVectorD;
|
||||||
typedef iColourVector<vComplexD2> vColourVectorD2;
|
|
||||||
|
|
||||||
// SpinColourVector
|
// SpinColourVector
|
||||||
typedef iSpinColourVector<Complex > SpinColourVector;
|
typedef iSpinColourVector<Complex > SpinColourVector;
|
||||||
@@ -237,7 +218,6 @@ typedef iSpinColourVector<ComplexD> SpinColourVectorD;
|
|||||||
typedef iSpinColourVector<vComplex > vSpinColourVector;
|
typedef iSpinColourVector<vComplex > vSpinColourVector;
|
||||||
typedef iSpinColourVector<vComplexF> vSpinColourVectorF;
|
typedef iSpinColourVector<vComplexF> vSpinColourVectorF;
|
||||||
typedef iSpinColourVector<vComplexD> vSpinColourVectorD;
|
typedef iSpinColourVector<vComplexD> vSpinColourVectorD;
|
||||||
typedef iSpinColourVector<vComplexD2> vSpinColourVectorD2;
|
|
||||||
|
|
||||||
// HalfSpin vector
|
// HalfSpin vector
|
||||||
typedef iHalfSpinVector<Complex > HalfSpinVector;
|
typedef iHalfSpinVector<Complex > HalfSpinVector;
|
||||||
@@ -247,7 +227,6 @@ typedef iHalfSpinVector<ComplexD> HalfSpinVectorD;
|
|||||||
typedef iHalfSpinVector<vComplex > vHalfSpinVector;
|
typedef iHalfSpinVector<vComplex > vHalfSpinVector;
|
||||||
typedef iHalfSpinVector<vComplexF> vHalfSpinVectorF;
|
typedef iHalfSpinVector<vComplexF> vHalfSpinVectorF;
|
||||||
typedef iHalfSpinVector<vComplexD> vHalfSpinVectorD;
|
typedef iHalfSpinVector<vComplexD> vHalfSpinVectorD;
|
||||||
typedef iHalfSpinVector<vComplexD2> vHalfSpinVectorD2;
|
|
||||||
|
|
||||||
// HalfSpinColour vector
|
// HalfSpinColour vector
|
||||||
typedef iHalfSpinColourVector<Complex > HalfSpinColourVector;
|
typedef iHalfSpinColourVector<Complex > HalfSpinColourVector;
|
||||||
@@ -257,7 +236,6 @@ typedef iHalfSpinColourVector<ComplexD> HalfSpinColourVectorD;
|
|||||||
typedef iHalfSpinColourVector<vComplex > vHalfSpinColourVector;
|
typedef iHalfSpinColourVector<vComplex > vHalfSpinColourVector;
|
||||||
typedef iHalfSpinColourVector<vComplexF> vHalfSpinColourVectorF;
|
typedef iHalfSpinColourVector<vComplexF> vHalfSpinColourVectorF;
|
||||||
typedef iHalfSpinColourVector<vComplexD> vHalfSpinColourVectorD;
|
typedef iHalfSpinColourVector<vComplexD> vHalfSpinColourVectorD;
|
||||||
typedef iHalfSpinColourVector<vComplexD2> vHalfSpinColourVectorD2;
|
|
||||||
|
|
||||||
//G-parity flavour vector
|
//G-parity flavour vector
|
||||||
typedef iGparityFlavourVector<Complex > GparityFlavourVector;
|
typedef iGparityFlavourVector<Complex > GparityFlavourVector;
|
||||||
@@ -267,7 +245,6 @@ typedef iGparityFlavourVector<ComplexD> GparityFlavourVectorD;
|
|||||||
typedef iGparityFlavourVector<vComplex > vGparityFlavourVector;
|
typedef iGparityFlavourVector<vComplex > vGparityFlavourVector;
|
||||||
typedef iGparityFlavourVector<vComplexF> vGparityFlavourVectorF;
|
typedef iGparityFlavourVector<vComplexF> vGparityFlavourVectorF;
|
||||||
typedef iGparityFlavourVector<vComplexD> vGparityFlavourVectorD;
|
typedef iGparityFlavourVector<vComplexD> vGparityFlavourVectorD;
|
||||||
typedef iGparityFlavourVector<vComplexD2> vGparityFlavourVectorD2;
|
|
||||||
|
|
||||||
// singlets
|
// singlets
|
||||||
typedef iSinglet<Complex > TComplex; // FIXME This is painful. Tensor singlet complex type.
|
typedef iSinglet<Complex > TComplex; // FIXME This is painful. Tensor singlet complex type.
|
||||||
@@ -277,7 +254,6 @@ typedef iSinglet<ComplexD> TComplexD; // FIXME This is painful. Tenso
|
|||||||
typedef iSinglet<vComplex > vTComplex ; // what if we don't know the tensor structure
|
typedef iSinglet<vComplex > vTComplex ; // what if we don't know the tensor structure
|
||||||
typedef iSinglet<vComplexF> vTComplexF; // what if we don't know the tensor structure
|
typedef iSinglet<vComplexF> vTComplexF; // what if we don't know the tensor structure
|
||||||
typedef iSinglet<vComplexD> vTComplexD; // what if we don't know the tensor structure
|
typedef iSinglet<vComplexD> vTComplexD; // what if we don't know the tensor structure
|
||||||
typedef iSinglet<vComplexD2> vTComplexD2; // what if we don't know the tensor structure
|
|
||||||
|
|
||||||
typedef iSinglet<Real > TReal; // Shouldn't need these; can I make it work without?
|
typedef iSinglet<Real > TReal; // Shouldn't need these; can I make it work without?
|
||||||
typedef iSinglet<RealF> TRealF; // Shouldn't need these; can I make it work without?
|
typedef iSinglet<RealF> TRealF; // Shouldn't need these; can I make it work without?
|
||||||
@@ -295,27 +271,22 @@ typedef iSinglet<Integer > TInteger;
|
|||||||
typedef Lattice<vColourMatrix> LatticeColourMatrix;
|
typedef Lattice<vColourMatrix> LatticeColourMatrix;
|
||||||
typedef Lattice<vColourMatrixF> LatticeColourMatrixF;
|
typedef Lattice<vColourMatrixF> LatticeColourMatrixF;
|
||||||
typedef Lattice<vColourMatrixD> LatticeColourMatrixD;
|
typedef Lattice<vColourMatrixD> LatticeColourMatrixD;
|
||||||
typedef Lattice<vColourMatrixD2> LatticeColourMatrixD2;
|
|
||||||
|
|
||||||
typedef Lattice<vSpinMatrix> LatticeSpinMatrix;
|
typedef Lattice<vSpinMatrix> LatticeSpinMatrix;
|
||||||
typedef Lattice<vSpinMatrixF> LatticeSpinMatrixF;
|
typedef Lattice<vSpinMatrixF> LatticeSpinMatrixF;
|
||||||
typedef Lattice<vSpinMatrixD> LatticeSpinMatrixD;
|
typedef Lattice<vSpinMatrixD> LatticeSpinMatrixD;
|
||||||
typedef Lattice<vSpinMatrixD2> LatticeSpinMatrixD2;
|
|
||||||
|
|
||||||
typedef Lattice<vSpinColourMatrix> LatticeSpinColourMatrix;
|
typedef Lattice<vSpinColourMatrix> LatticeSpinColourMatrix;
|
||||||
typedef Lattice<vSpinColourMatrixF> LatticeSpinColourMatrixF;
|
typedef Lattice<vSpinColourMatrixF> LatticeSpinColourMatrixF;
|
||||||
typedef Lattice<vSpinColourMatrixD> LatticeSpinColourMatrixD;
|
typedef Lattice<vSpinColourMatrixD> LatticeSpinColourMatrixD;
|
||||||
typedef Lattice<vSpinColourMatrixD2> LatticeSpinColourMatrixD2;
|
|
||||||
|
|
||||||
typedef Lattice<vSpinColourSpinColourMatrix> LatticeSpinColourSpinColourMatrix;
|
typedef Lattice<vSpinColourSpinColourMatrix> LatticeSpinColourSpinColourMatrix;
|
||||||
typedef Lattice<vSpinColourSpinColourMatrixF> LatticeSpinColourSpinColourMatrixF;
|
typedef Lattice<vSpinColourSpinColourMatrixF> LatticeSpinColourSpinColourMatrixF;
|
||||||
typedef Lattice<vSpinColourSpinColourMatrixD> LatticeSpinColourSpinColourMatrixD;
|
typedef Lattice<vSpinColourSpinColourMatrixD> LatticeSpinColourSpinColourMatrixD;
|
||||||
typedef Lattice<vSpinColourSpinColourMatrixD2> LatticeSpinColourSpinColourMatrixD2;
|
|
||||||
|
|
||||||
typedef Lattice<vLorentzColourMatrix> LatticeLorentzColourMatrix;
|
typedef Lattice<vLorentzColourMatrix> LatticeLorentzColourMatrix;
|
||||||
typedef Lattice<vLorentzColourMatrixF> LatticeLorentzColourMatrixF;
|
typedef Lattice<vLorentzColourMatrixF> LatticeLorentzColourMatrixF;
|
||||||
typedef Lattice<vLorentzColourMatrixD> LatticeLorentzColourMatrixD;
|
typedef Lattice<vLorentzColourMatrixD> LatticeLorentzColourMatrixD;
|
||||||
typedef Lattice<vLorentzColourMatrixD2> LatticeLorentzColourMatrixD2;
|
|
||||||
|
|
||||||
typedef Lattice<vLorentzComplex> LatticeLorentzComplex;
|
typedef Lattice<vLorentzComplex> LatticeLorentzComplex;
|
||||||
typedef Lattice<vLorentzComplexF> LatticeLorentzComplexF;
|
typedef Lattice<vLorentzComplexF> LatticeLorentzComplexF;
|
||||||
@@ -325,32 +296,26 @@ typedef Lattice<vLorentzComplexD> LatticeLorentzComplexD;
|
|||||||
typedef Lattice<vDoubleStoredColourMatrix> LatticeDoubleStoredColourMatrix;
|
typedef Lattice<vDoubleStoredColourMatrix> LatticeDoubleStoredColourMatrix;
|
||||||
typedef Lattice<vDoubleStoredColourMatrixF> LatticeDoubleStoredColourMatrixF;
|
typedef Lattice<vDoubleStoredColourMatrixF> LatticeDoubleStoredColourMatrixF;
|
||||||
typedef Lattice<vDoubleStoredColourMatrixD> LatticeDoubleStoredColourMatrixD;
|
typedef Lattice<vDoubleStoredColourMatrixD> LatticeDoubleStoredColourMatrixD;
|
||||||
typedef Lattice<vDoubleStoredColourMatrixD2> LatticeDoubleStoredColourMatrixD2;
|
|
||||||
|
|
||||||
typedef Lattice<vSpinVector> LatticeSpinVector;
|
typedef Lattice<vSpinVector> LatticeSpinVector;
|
||||||
typedef Lattice<vSpinVectorF> LatticeSpinVectorF;
|
typedef Lattice<vSpinVectorF> LatticeSpinVectorF;
|
||||||
typedef Lattice<vSpinVectorD> LatticeSpinVectorD;
|
typedef Lattice<vSpinVectorD> LatticeSpinVectorD;
|
||||||
typedef Lattice<vSpinVectorD2> LatticeSpinVectorD2;
|
|
||||||
|
|
||||||
typedef Lattice<vColourVector> LatticeColourVector;
|
typedef Lattice<vColourVector> LatticeColourVector;
|
||||||
typedef Lattice<vColourVectorF> LatticeColourVectorF;
|
typedef Lattice<vColourVectorF> LatticeColourVectorF;
|
||||||
typedef Lattice<vColourVectorD> LatticeColourVectorD;
|
typedef Lattice<vColourVectorD> LatticeColourVectorD;
|
||||||
typedef Lattice<vColourVectorD2> LatticeColourVectorD2;
|
|
||||||
|
|
||||||
typedef Lattice<vSpinColourVector> LatticeSpinColourVector;
|
typedef Lattice<vSpinColourVector> LatticeSpinColourVector;
|
||||||
typedef Lattice<vSpinColourVectorF> LatticeSpinColourVectorF;
|
typedef Lattice<vSpinColourVectorF> LatticeSpinColourVectorF;
|
||||||
typedef Lattice<vSpinColourVectorD> LatticeSpinColourVectorD;
|
typedef Lattice<vSpinColourVectorD> LatticeSpinColourVectorD;
|
||||||
typedef Lattice<vSpinColourVectorD2> LatticeSpinColourVectorD2;
|
|
||||||
|
|
||||||
typedef Lattice<vHalfSpinVector> LatticeHalfSpinVector;
|
typedef Lattice<vHalfSpinVector> LatticeHalfSpinVector;
|
||||||
typedef Lattice<vHalfSpinVectorF> LatticeHalfSpinVectorF;
|
typedef Lattice<vHalfSpinVectorF> LatticeHalfSpinVectorF;
|
||||||
typedef Lattice<vHalfSpinVectorD> LatticeHalfSpinVectorD;
|
typedef Lattice<vHalfSpinVectorD> LatticeHalfSpinVectorD;
|
||||||
typedef Lattice<vHalfSpinVectorD2> LatticeHalfSpinVectorD2;
|
|
||||||
|
|
||||||
typedef Lattice<vHalfSpinColourVector> LatticeHalfSpinColourVector;
|
typedef Lattice<vHalfSpinColourVector> LatticeHalfSpinColourVector;
|
||||||
typedef Lattice<vHalfSpinColourVectorF> LatticeHalfSpinColourVectorF;
|
typedef Lattice<vHalfSpinColourVectorF> LatticeHalfSpinColourVectorF;
|
||||||
typedef Lattice<vHalfSpinColourVectorD> LatticeHalfSpinColourVectorD;
|
typedef Lattice<vHalfSpinColourVectorD> LatticeHalfSpinColourVectorD;
|
||||||
typedef Lattice<vHalfSpinColourVectorD2> LatticeHalfSpinColourVectorD2;
|
|
||||||
|
|
||||||
typedef Lattice<vTReal> LatticeReal;
|
typedef Lattice<vTReal> LatticeReal;
|
||||||
typedef Lattice<vTRealF> LatticeRealF;
|
typedef Lattice<vTRealF> LatticeRealF;
|
||||||
@@ -359,7 +324,6 @@ typedef Lattice<vTRealD> LatticeRealD;
|
|||||||
typedef Lattice<vTComplex> LatticeComplex;
|
typedef Lattice<vTComplex> LatticeComplex;
|
||||||
typedef Lattice<vTComplexF> LatticeComplexF;
|
typedef Lattice<vTComplexF> LatticeComplexF;
|
||||||
typedef Lattice<vTComplexD> LatticeComplexD;
|
typedef Lattice<vTComplexD> LatticeComplexD;
|
||||||
typedef Lattice<vTComplexD2> LatticeComplexD2;
|
|
||||||
|
|
||||||
typedef Lattice<vTInteger> LatticeInteger; // Predicates for "where"
|
typedef Lattice<vTInteger> LatticeInteger; // Predicates for "where"
|
||||||
|
|
||||||
@@ -370,39 +334,124 @@ typedef Lattice<vTInteger> LatticeInteger; // Predicates for "where"
|
|||||||
typedef LatticeHalfSpinColourVector LatticeHalfFermion;
|
typedef LatticeHalfSpinColourVector LatticeHalfFermion;
|
||||||
typedef LatticeHalfSpinColourVectorF LatticeHalfFermionF;
|
typedef LatticeHalfSpinColourVectorF LatticeHalfFermionF;
|
||||||
typedef LatticeHalfSpinColourVectorD LatticeHalfFermionD;
|
typedef LatticeHalfSpinColourVectorD LatticeHalfFermionD;
|
||||||
typedef LatticeHalfSpinColourVectorD2 LatticeHalfFermionD2;
|
|
||||||
|
|
||||||
typedef LatticeSpinColourVector LatticeFermion;
|
typedef LatticeSpinColourVector LatticeFermion;
|
||||||
typedef LatticeSpinColourVectorF LatticeFermionF;
|
typedef LatticeSpinColourVectorF LatticeFermionF;
|
||||||
typedef LatticeSpinColourVectorD LatticeFermionD;
|
typedef LatticeSpinColourVectorD LatticeFermionD;
|
||||||
typedef LatticeSpinColourVectorD2 LatticeFermionD2;
|
|
||||||
|
|
||||||
typedef LatticeSpinColourMatrix LatticePropagator;
|
typedef LatticeSpinColourMatrix LatticePropagator;
|
||||||
typedef LatticeSpinColourMatrixF LatticePropagatorF;
|
typedef LatticeSpinColourMatrixF LatticePropagatorF;
|
||||||
typedef LatticeSpinColourMatrixD LatticePropagatorD;
|
typedef LatticeSpinColourMatrixD LatticePropagatorD;
|
||||||
typedef LatticeSpinColourMatrixD2 LatticePropagatorD2;
|
|
||||||
|
|
||||||
typedef LatticeLorentzColourMatrix LatticeGaugeField;
|
typedef LatticeLorentzColourMatrix LatticeGaugeField;
|
||||||
typedef LatticeLorentzColourMatrixF LatticeGaugeFieldF;
|
typedef LatticeLorentzColourMatrixF LatticeGaugeFieldF;
|
||||||
typedef LatticeLorentzColourMatrixD LatticeGaugeFieldD;
|
typedef LatticeLorentzColourMatrixD LatticeGaugeFieldD;
|
||||||
typedef LatticeLorentzColourMatrixD2 LatticeGaugeFieldD2;
|
|
||||||
|
|
||||||
typedef LatticeDoubleStoredColourMatrix LatticeDoubledGaugeField;
|
typedef LatticeDoubleStoredColourMatrix LatticeDoubledGaugeField;
|
||||||
typedef LatticeDoubleStoredColourMatrixF LatticeDoubledGaugeFieldF;
|
typedef LatticeDoubleStoredColourMatrixF LatticeDoubledGaugeFieldF;
|
||||||
typedef LatticeDoubleStoredColourMatrixD LatticeDoubledGaugeFieldD;
|
typedef LatticeDoubleStoredColourMatrixD LatticeDoubledGaugeFieldD;
|
||||||
typedef LatticeDoubleStoredColourMatrixD2 LatticeDoubledGaugeFieldD2;
|
|
||||||
|
|
||||||
template<class GF> using LorentzScalar = Lattice<iScalar<typename GF::vector_object::element> >;
|
template<class GF> using LorentzScalar = Lattice<iScalar<typename GF::vector_object::element> >;
|
||||||
|
|
||||||
typedef Lattice<vColourVector> LatticeStaggeredFermion;
|
typedef Lattice<vColourVector> LatticeStaggeredFermion;
|
||||||
typedef Lattice<vColourVectorF> LatticeStaggeredFermionF;
|
typedef Lattice<vColourVectorF> LatticeStaggeredFermionF;
|
||||||
typedef Lattice<vColourVectorD> LatticeStaggeredFermionD;
|
typedef Lattice<vColourVectorD> LatticeStaggeredFermionD;
|
||||||
typedef Lattice<vColourVectorD2> LatticeStaggeredFermionD2;
|
|
||||||
|
|
||||||
typedef Lattice<vColourMatrix> LatticeStaggeredPropagator;
|
typedef Lattice<vColourMatrix> LatticeStaggeredPropagator;
|
||||||
typedef Lattice<vColourMatrixF> LatticeStaggeredPropagatorF;
|
typedef Lattice<vColourMatrixF> LatticeStaggeredPropagatorF;
|
||||||
typedef Lattice<vColourMatrixD> LatticeStaggeredPropagatorD;
|
typedef Lattice<vColourMatrixD> LatticeStaggeredPropagatorD;
|
||||||
typedef Lattice<vColourMatrixD2> LatticeStaggeredPropagatorD2;
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Lexicographic lattice types: Nsimd()==1 containers of the same tensors.
|
||||||
|
// Single and double precision only; no default precision alias.
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Site objects over the width one simd types
|
||||||
|
typedef iSpinMatrix<sComplexF> sSpinMatrixF;
|
||||||
|
typedef iSpinMatrix<sComplexD> sSpinMatrixD;
|
||||||
|
typedef iColourMatrix<sComplexF> sColourMatrixF;
|
||||||
|
typedef iColourMatrix<sComplexD> sColourMatrixD;
|
||||||
|
typedef iSpinColourMatrix<sComplexF> sSpinColourMatrixF;
|
||||||
|
typedef iSpinColourMatrix<sComplexD> sSpinColourMatrixD;
|
||||||
|
typedef iSpinColourSpinColourMatrix<sComplexF> sSpinColourSpinColourMatrixF;
|
||||||
|
typedef iSpinColourSpinColourMatrix<sComplexD> sSpinColourSpinColourMatrixD;
|
||||||
|
typedef iLorentzColourMatrix<sComplexF> sLorentzColourMatrixF;
|
||||||
|
typedef iLorentzColourMatrix<sComplexD> sLorentzColourMatrixD;
|
||||||
|
typedef iLorentzComplex<sComplexF> sLorentzComplexF;
|
||||||
|
typedef iLorentzComplex<sComplexD> sLorentzComplexD;
|
||||||
|
typedef iDoubleStoredColourMatrix<sComplexF> sDoubleStoredColourMatrixF;
|
||||||
|
typedef iDoubleStoredColourMatrix<sComplexD> sDoubleStoredColourMatrixD;
|
||||||
|
typedef iGparityFlavourMatrix<sComplexF> sGparityFlavourMatrixF;
|
||||||
|
typedef iGparityFlavourMatrix<sComplexD> sGparityFlavourMatrixD;
|
||||||
|
typedef iSpinVector<sComplexF> sSpinVectorF;
|
||||||
|
typedef iSpinVector<sComplexD> sSpinVectorD;
|
||||||
|
typedef iColourVector<sComplexF> sColourVectorF;
|
||||||
|
typedef iColourVector<sComplexD> sColourVectorD;
|
||||||
|
typedef iSpinColourVector<sComplexF> sSpinColourVectorF;
|
||||||
|
typedef iSpinColourVector<sComplexD> sSpinColourVectorD;
|
||||||
|
typedef iHalfSpinVector<sComplexF> sHalfSpinVectorF;
|
||||||
|
typedef iHalfSpinVector<sComplexD> sHalfSpinVectorD;
|
||||||
|
typedef iHalfSpinColourVector<sComplexF> sHalfSpinColourVectorF;
|
||||||
|
typedef iHalfSpinColourVector<sComplexD> sHalfSpinColourVectorD;
|
||||||
|
typedef iGparityFlavourVector<sComplexF> sGparityFlavourVectorF;
|
||||||
|
typedef iGparityFlavourVector<sComplexD> sGparityFlavourVectorD;
|
||||||
|
typedef iSinglet<sComplexF> sTComplexF;
|
||||||
|
typedef iSinglet<sComplexD> sTComplexD;
|
||||||
|
typedef iSinglet<sRealF> sTRealF;
|
||||||
|
typedef iSinglet<sRealD> sTRealD;
|
||||||
|
typedef iSinglet<sInteger> sTInteger;
|
||||||
|
|
||||||
|
// Lattices of these
|
||||||
|
typedef Lattice<sColourMatrixF> lexLatticeColourMatrixF;
|
||||||
|
typedef Lattice<sColourMatrixD> lexLatticeColourMatrixD;
|
||||||
|
typedef Lattice<sSpinMatrixF> lexLatticeSpinMatrixF;
|
||||||
|
typedef Lattice<sSpinMatrixD> lexLatticeSpinMatrixD;
|
||||||
|
typedef Lattice<sSpinColourMatrixF> lexLatticeSpinColourMatrixF;
|
||||||
|
typedef Lattice<sSpinColourMatrixD> lexLatticeSpinColourMatrixD;
|
||||||
|
typedef Lattice<sSpinColourSpinColourMatrixF> lexLatticeSpinColourSpinColourMatrixF;
|
||||||
|
typedef Lattice<sSpinColourSpinColourMatrixD> lexLatticeSpinColourSpinColourMatrixD;
|
||||||
|
typedef Lattice<sLorentzColourMatrixF> lexLatticeLorentzColourMatrixF;
|
||||||
|
typedef Lattice<sLorentzColourMatrixD> lexLatticeLorentzColourMatrixD;
|
||||||
|
typedef Lattice<sLorentzComplexF> lexLatticeLorentzComplexF;
|
||||||
|
typedef Lattice<sLorentzComplexD> lexLatticeLorentzComplexD;
|
||||||
|
typedef Lattice<sDoubleStoredColourMatrixF> lexLatticeDoubleStoredColourMatrixF;
|
||||||
|
typedef Lattice<sDoubleStoredColourMatrixD> lexLatticeDoubleStoredColourMatrixD;
|
||||||
|
typedef Lattice<sSpinVectorF> lexLatticeSpinVectorF;
|
||||||
|
typedef Lattice<sSpinVectorD> lexLatticeSpinVectorD;
|
||||||
|
typedef Lattice<sColourVectorF> lexLatticeColourVectorF;
|
||||||
|
typedef Lattice<sColourVectorD> lexLatticeColourVectorD;
|
||||||
|
typedef Lattice<sSpinColourVectorF> lexLatticeSpinColourVectorF;
|
||||||
|
typedef Lattice<sSpinColourVectorD> lexLatticeSpinColourVectorD;
|
||||||
|
typedef Lattice<sHalfSpinVectorF> lexLatticeHalfSpinVectorF;
|
||||||
|
typedef Lattice<sHalfSpinVectorD> lexLatticeHalfSpinVectorD;
|
||||||
|
typedef Lattice<sHalfSpinColourVectorF> lexLatticeHalfSpinColourVectorF;
|
||||||
|
typedef Lattice<sHalfSpinColourVectorD> lexLatticeHalfSpinColourVectorD;
|
||||||
|
typedef Lattice<sTRealF> lexLatticeRealF;
|
||||||
|
typedef Lattice<sTRealD> lexLatticeRealD;
|
||||||
|
typedef Lattice<sTComplexF> lexLatticeComplexF;
|
||||||
|
typedef Lattice<sTComplexD> lexLatticeComplexD;
|
||||||
|
typedef Lattice<sTInteger> lexLatticeInteger;
|
||||||
|
typedef Lattice<sColourVectorF> lexLatticeStaggeredFermionF;
|
||||||
|
typedef Lattice<sColourVectorD> lexLatticeStaggeredFermionD;
|
||||||
|
typedef Lattice<sColourMatrixF> lexLatticeStaggeredPropagatorF;
|
||||||
|
typedef Lattice<sColourMatrixD> lexLatticeStaggeredPropagatorD;
|
||||||
|
|
||||||
|
// Physical names for things
|
||||||
|
typedef lexLatticeHalfSpinColourVectorF lexLatticeHalfFermionF;
|
||||||
|
typedef lexLatticeHalfSpinColourVectorD lexLatticeHalfFermionD;
|
||||||
|
typedef lexLatticeSpinColourVectorF lexLatticeFermionF;
|
||||||
|
typedef lexLatticeSpinColourVectorD lexLatticeFermionD;
|
||||||
|
typedef lexLatticeSpinColourMatrixF lexLatticePropagatorF;
|
||||||
|
typedef lexLatticeSpinColourMatrixD lexLatticePropagatorD;
|
||||||
|
typedef lexLatticeLorentzColourMatrixF lexLatticeGaugeFieldF;
|
||||||
|
typedef lexLatticeLorentzColourMatrixD lexLatticeGaugeFieldD;
|
||||||
|
typedef lexLatticeDoubleStoredColourMatrixF lexLatticeDoubledGaugeFieldF;
|
||||||
|
typedef lexLatticeDoubleStoredColourMatrixD lexLatticeDoubledGaugeFieldD;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// End lexicographic lattice types
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
// Peek and Poke named after physics attributes
|
// Peek and Poke named after physics attributes
|
||||||
|
|||||||
@@ -143,6 +143,8 @@ public:
|
|||||||
static int getNMAX(Lattice<iImplClover<vComplexD2>> &t, RealD R) {return getNMAX(1e-12,R);}
|
static int getNMAX(Lattice<iImplClover<vComplexD2>> &t, RealD R) {return getNMAX(1e-12,R);}
|
||||||
static int getNMAX(Lattice<iImplClover<vComplexD>> &t, RealD R) {return getNMAX(1e-12,R);}
|
static int getNMAX(Lattice<iImplClover<vComplexD>> &t, RealD R) {return getNMAX(1e-12,R);}
|
||||||
static int getNMAX(Lattice<iImplClover<vComplexF>> &t, RealD R) {return getNMAX(1e-6,R);}
|
static int getNMAX(Lattice<iImplClover<vComplexF>> &t, RealD R) {return getNMAX(1e-6,R);}
|
||||||
|
static int getNMAX(Lattice<iImplClover<sComplexD>> &t, RealD R) {return getNMAX(1e-12,R);}
|
||||||
|
static int getNMAX(Lattice<iImplClover<sComplexF>> &t, RealD R) {return getNMAX(1e-6,R);}
|
||||||
|
|
||||||
static void Instantiate(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) {
|
static void Instantiate(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) {
|
||||||
GridBase* grid = Clover.Grid();
|
GridBase* grid = Clover.Grid();
|
||||||
@@ -268,6 +270,8 @@ public:
|
|||||||
|
|
||||||
static int getNMAX(Lattice<iImplClover<vComplexD>> &t, RealD R) {return getNMAX(1e-12,R);}
|
static int getNMAX(Lattice<iImplClover<vComplexD>> &t, RealD R) {return getNMAX(1e-12,R);}
|
||||||
static int getNMAX(Lattice<iImplClover<vComplexF>> &t, RealD R) {return getNMAX(1e-6,R);}
|
static int getNMAX(Lattice<iImplClover<vComplexF>> &t, RealD R) {return getNMAX(1e-6,R);}
|
||||||
|
static int getNMAX(Lattice<iImplClover<sComplexD>> &t, RealD R) {return getNMAX(1e-12,R);}
|
||||||
|
static int getNMAX(Lattice<iImplClover<sComplexF>> &t, RealD R) {return getNMAX(1e-6,R);}
|
||||||
|
|
||||||
static void InstantiateClover(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) {
|
static void InstantiateClover(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) {
|
||||||
|
|
||||||
|
|||||||
@@ -40,15 +40,12 @@ public:
|
|||||||
static const int Dimension = Representation::Dimension;
|
static const int Dimension = Representation::Dimension;
|
||||||
static const bool isFundamental = Representation::isFundamental;
|
static const bool isFundamental = Representation::isFundamental;
|
||||||
static const bool LsVectorised=true;
|
static const bool LsVectorised=true;
|
||||||
static const int Nhcs = Options::Nhcs;
|
|
||||||
|
|
||||||
typedef typename Options::_Coeff_t Coeff_t;
|
typedef typename Options::_Coeff_t Coeff_t;
|
||||||
typedef typename Options::template PrecisionMapper<Simd>::LowerPrecVector SimdL;
|
|
||||||
|
|
||||||
template <typename vtype> using iImplSpinor = iScalar<iVector<iVector<vtype, Dimension>, Ns> >;
|
template <typename vtype> using iImplSpinor = iScalar<iVector<iVector<vtype, Dimension>, Ns> >;
|
||||||
template <typename vtype> using iImplPropagator = iScalar<iMatrix<iMatrix<vtype, Dimension>, Ns> >;
|
template <typename vtype> using iImplPropagator = iScalar<iMatrix<iMatrix<vtype, Dimension>, Ns> >;
|
||||||
template <typename vtype> using iImplHalfSpinor = iScalar<iVector<iVector<vtype, Dimension>, Nhs> >;
|
template <typename vtype> using iImplHalfSpinor = iScalar<iVector<iVector<vtype, Dimension>, Nhs> >;
|
||||||
template <typename vtype> using iImplHalfCommSpinor = iScalar<iVector<iVector<vtype, Dimension>, Nhcs> >;
|
|
||||||
template <typename vtype> using iImplDoubledGaugeField = iVector<iScalar<iMatrix<vtype, Dimension> >, Nds>;
|
template <typename vtype> using iImplDoubledGaugeField = iVector<iScalar<iMatrix<vtype, Dimension> >, Nds>;
|
||||||
template <typename vtype> using iImplGaugeField = iVector<iScalar<iMatrix<vtype, Dimension> >, Nd>;
|
template <typename vtype> using iImplGaugeField = iVector<iScalar<iMatrix<vtype, Dimension> >, Nd>;
|
||||||
template <typename vtype> using iImplGaugeLink = iScalar<iScalar<iMatrix<vtype, Dimension> > >;
|
template <typename vtype> using iImplGaugeLink = iScalar<iScalar<iMatrix<vtype, Dimension> > >;
|
||||||
@@ -56,7 +53,6 @@ public:
|
|||||||
typedef iImplSpinor<Simd> SiteSpinor;
|
typedef iImplSpinor<Simd> SiteSpinor;
|
||||||
typedef iImplPropagator<Simd> SitePropagator;
|
typedef iImplPropagator<Simd> SitePropagator;
|
||||||
typedef iImplHalfSpinor<Simd> SiteHalfSpinor;
|
typedef iImplHalfSpinor<Simd> SiteHalfSpinor;
|
||||||
typedef iImplHalfCommSpinor<SimdL> SiteHalfCommSpinor;
|
|
||||||
typedef Lattice<SiteSpinor> FermionField;
|
typedef Lattice<SiteSpinor> FermionField;
|
||||||
typedef Lattice<SitePropagator> PropagatorField;
|
typedef Lattice<SitePropagator> PropagatorField;
|
||||||
|
|
||||||
@@ -68,7 +64,7 @@ public:
|
|||||||
typedef iImplGaugeLink<typename Simd::scalar_type> SiteScalarGaugeLink; // scalar
|
typedef iImplGaugeLink<typename Simd::scalar_type> SiteScalarGaugeLink; // scalar
|
||||||
typedef Lattice<SiteDoubledGaugeField> DoubledGaugeField;
|
typedef Lattice<SiteDoubledGaugeField> DoubledGaugeField;
|
||||||
|
|
||||||
typedef WilsonCompressor<SiteHalfCommSpinor,SiteHalfSpinor, SiteSpinor> Compressor;
|
typedef WilsonCompressor<SiteHalfSpinor, SiteSpinor> Compressor;
|
||||||
typedef WilsonImplParams ImplParams;
|
typedef WilsonImplParams ImplParams;
|
||||||
typedef WilsonStencil<SiteSpinor, SiteHalfSpinor,ImplParams> StencilImpl;
|
typedef WilsonStencil<SiteSpinor, SiteHalfSpinor,ImplParams> StencilImpl;
|
||||||
typedef typename StencilImpl::View_type StencilView;
|
typedef typename StencilImpl::View_type StencilView;
|
||||||
@@ -201,16 +197,8 @@ typedef DomainWallVec5dImpl<vComplex ,FundamentalRepresentation, CoeffReal> Doma
|
|||||||
typedef DomainWallVec5dImpl<vComplexF,FundamentalRepresentation, CoeffReal> DomainWallVec5dImplF; // Float
|
typedef DomainWallVec5dImpl<vComplexF,FundamentalRepresentation, CoeffReal> DomainWallVec5dImplF; // Float
|
||||||
typedef DomainWallVec5dImpl<vComplexD,FundamentalRepresentation, CoeffReal> DomainWallVec5dImplD; // Double
|
typedef DomainWallVec5dImpl<vComplexD,FundamentalRepresentation, CoeffReal> DomainWallVec5dImplD; // Double
|
||||||
|
|
||||||
typedef DomainWallVec5dImpl<vComplex ,FundamentalRepresentation, CoeffRealHalfComms> DomainWallVec5dImplRL; // Real.. whichever prec
|
|
||||||
typedef DomainWallVec5dImpl<vComplexF,FundamentalRepresentation, CoeffRealHalfComms> DomainWallVec5dImplFH; // Float
|
|
||||||
typedef DomainWallVec5dImpl<vComplexD,FundamentalRepresentation, CoeffRealHalfComms> DomainWallVec5dImplDF; // Double
|
|
||||||
|
|
||||||
typedef DomainWallVec5dImpl<vComplex ,FundamentalRepresentation,CoeffComplex> ZDomainWallVec5dImplR; // Real.. whichever prec
|
typedef DomainWallVec5dImpl<vComplex ,FundamentalRepresentation,CoeffComplex> ZDomainWallVec5dImplR; // Real.. whichever prec
|
||||||
typedef DomainWallVec5dImpl<vComplexF,FundamentalRepresentation,CoeffComplex> ZDomainWallVec5dImplF; // Float
|
typedef DomainWallVec5dImpl<vComplexF,FundamentalRepresentation,CoeffComplex> ZDomainWallVec5dImplF; // Float
|
||||||
typedef DomainWallVec5dImpl<vComplexD,FundamentalRepresentation,CoeffComplex> ZDomainWallVec5dImplD; // Double
|
typedef DomainWallVec5dImpl<vComplexD,FundamentalRepresentation,CoeffComplex> ZDomainWallVec5dImplD; // Double
|
||||||
|
|
||||||
typedef DomainWallVec5dImpl<vComplex ,FundamentalRepresentation,CoeffComplexHalfComms> ZDomainWallVec5dImplRL; // Real.. whichever prec
|
|
||||||
typedef DomainWallVec5dImpl<vComplexF,FundamentalRepresentation,CoeffComplexHalfComms> ZDomainWallVec5dImplFH; // Float
|
|
||||||
typedef DomainWallVec5dImpl<vComplexD,FundamentalRepresentation,CoeffComplexHalfComms> ZDomainWallVec5dImplDF; // Double
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
NAMESPACE_END(Grid);
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ NAMESPACE_CHECK(DWFutils);
|
|||||||
// Cayley 5d
|
// Cayley 5d
|
||||||
NAMESPACE_BEGIN(Grid);
|
NAMESPACE_BEGIN(Grid);
|
||||||
|
|
||||||
typedef WilsonFermion<WilsonImplD2> WilsonFermionD2;
|
//typedef WilsonFermion<WilsonImplD2> WilsonFermionD2;
|
||||||
typedef WilsonFermion<WilsonImplF> WilsonFermionF;
|
typedef WilsonFermion<WilsonImplF> WilsonFermionF;
|
||||||
typedef WilsonFermion<WilsonImplD> WilsonFermionD;
|
typedef WilsonFermion<WilsonImplD> WilsonFermionD;
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ typedef WilsonFermion<SpWilsonTwoIndexSymmetricImplF> SpWilsonTwoIndexSymmetricF
|
|||||||
typedef WilsonFermion<SpWilsonTwoIndexSymmetricImplD> SpWilsonTwoIndexSymmetricFermionD;
|
typedef WilsonFermion<SpWilsonTwoIndexSymmetricImplD> SpWilsonTwoIndexSymmetricFermionD;
|
||||||
|
|
||||||
// Twisted mass fermion
|
// Twisted mass fermion
|
||||||
typedef WilsonTMFermion<WilsonImplD2> WilsonTMFermionD2;
|
//typedef WilsonTMFermion<WilsonImplD2> WilsonTMFermionD2;
|
||||||
typedef WilsonTMFermion<WilsonImplF> WilsonTMFermionF;
|
typedef WilsonTMFermion<WilsonImplF> WilsonTMFermionF;
|
||||||
typedef WilsonTMFermion<WilsonImplD> WilsonTMFermionD;
|
typedef WilsonTMFermion<WilsonImplD> WilsonTMFermionD;
|
||||||
|
|
||||||
@@ -146,11 +146,11 @@ typedef WilsonTMFermion<WilsonImplD> WilsonTMFermionD;
|
|||||||
template <typename WImpl> using WilsonClover = WilsonCloverFermion<WImpl, CloverHelpers<WImpl>>;
|
template <typename WImpl> using WilsonClover = WilsonCloverFermion<WImpl, CloverHelpers<WImpl>>;
|
||||||
template <typename WImpl> using WilsonExpClover = WilsonCloverFermion<WImpl, ExpCloverHelpers<WImpl>>;
|
template <typename WImpl> using WilsonExpClover = WilsonCloverFermion<WImpl, ExpCloverHelpers<WImpl>>;
|
||||||
|
|
||||||
typedef WilsonClover<WilsonImplD2> WilsonCloverFermionD2;
|
//typedef WilsonClover<WilsonImplD2> WilsonCloverFermionD2;
|
||||||
typedef WilsonClover<WilsonImplF> WilsonCloverFermionF;
|
typedef WilsonClover<WilsonImplF> WilsonCloverFermionF;
|
||||||
typedef WilsonClover<WilsonImplD> WilsonCloverFermionD;
|
typedef WilsonClover<WilsonImplD> WilsonCloverFermionD;
|
||||||
|
|
||||||
typedef WilsonExpClover<WilsonImplD2> WilsonExpCloverFermionD2;
|
//typedef WilsonExpClover<WilsonImplD2> WilsonExpCloverFermionD2;
|
||||||
typedef WilsonExpClover<WilsonImplF> WilsonExpCloverFermionF;
|
typedef WilsonExpClover<WilsonImplF> WilsonExpCloverFermionF;
|
||||||
typedef WilsonExpClover<WilsonImplD> WilsonExpCloverFermionD;
|
typedef WilsonExpClover<WilsonImplD> WilsonExpCloverFermionD;
|
||||||
|
|
||||||
@@ -168,15 +168,15 @@ template <typename WImpl> using CompactWilsonClover = CompactWilsonCloverFermion
|
|||||||
template <typename WImpl> using CompactWilsonClover5D = CompactWilsonCloverFermion5D<WImpl, CompactCloverHelpers<WImpl>>;
|
template <typename WImpl> using CompactWilsonClover5D = CompactWilsonCloverFermion5D<WImpl, CompactCloverHelpers<WImpl>>;
|
||||||
template <typename WImpl> using CompactWilsonExpClover = CompactWilsonCloverFermion<WImpl, CompactExpCloverHelpers<WImpl>>;
|
template <typename WImpl> using CompactWilsonExpClover = CompactWilsonCloverFermion<WImpl, CompactExpCloverHelpers<WImpl>>;
|
||||||
|
|
||||||
typedef CompactWilsonClover<WilsonImplD2> CompactWilsonCloverFermionD2;
|
//typedef CompactWilsonClover<WilsonImplD2> CompactWilsonCloverFermionD2;
|
||||||
typedef CompactWilsonClover<WilsonImplF> CompactWilsonCloverFermionF;
|
typedef CompactWilsonClover<WilsonImplF> CompactWilsonCloverFermionF;
|
||||||
typedef CompactWilsonClover<WilsonImplD> CompactWilsonCloverFermionD;
|
typedef CompactWilsonClover<WilsonImplD> CompactWilsonCloverFermionD;
|
||||||
|
|
||||||
typedef CompactWilsonClover5D<WilsonImplD2> CompactWilsonCloverFermion5DD2;
|
//typedef CompactWilsonClover5D<WilsonImplD2> CompactWilsonCloverFermion5DD2;
|
||||||
typedef CompactWilsonClover5D<WilsonImplF> CompactWilsonCloverFermion5DF;
|
typedef CompactWilsonClover5D<WilsonImplF> CompactWilsonCloverFermion5DF;
|
||||||
typedef CompactWilsonClover5D<WilsonImplD> CompactWilsonCloverFermion5DD;
|
typedef CompactWilsonClover5D<WilsonImplD> CompactWilsonCloverFermion5DD;
|
||||||
|
|
||||||
typedef CompactWilsonExpClover<WilsonImplD2> CompactWilsonExpCloverFermionD2;
|
//typedef CompactWilsonExpClover<WilsonImplD2> CompactWilsonExpCloverFermionD2;
|
||||||
typedef CompactWilsonExpClover<WilsonImplF> CompactWilsonExpCloverFermionF;
|
typedef CompactWilsonExpClover<WilsonImplF> CompactWilsonExpCloverFermionF;
|
||||||
typedef CompactWilsonExpClover<WilsonImplD> CompactWilsonExpCloverFermionD;
|
typedef CompactWilsonExpClover<WilsonImplD> CompactWilsonExpCloverFermionD;
|
||||||
|
|
||||||
@@ -192,56 +192,56 @@ typedef CompactWilsonClover<WilsonTwoIndexAntiSymmetricImplD> CompactWilsonClove
|
|||||||
// Domain Wall fermions
|
// Domain Wall fermions
|
||||||
typedef DomainWallFermion<WilsonImplF> DomainWallFermionF;
|
typedef DomainWallFermion<WilsonImplF> DomainWallFermionF;
|
||||||
typedef DomainWallFermion<WilsonImplD> DomainWallFermionD;
|
typedef DomainWallFermion<WilsonImplD> DomainWallFermionD;
|
||||||
typedef DomainWallFermion<WilsonImplD2> DomainWallFermionD2;
|
//typedef DomainWallFermion<WilsonImplD2> DomainWallFermionD2;
|
||||||
|
|
||||||
typedef DomainWallEOFAFermion<WilsonImplD2> DomainWallEOFAFermionD2;
|
//typedef DomainWallEOFAFermion<WilsonImplD2> DomainWallEOFAFermionD2;
|
||||||
typedef DomainWallEOFAFermion<WilsonImplF> DomainWallEOFAFermionF;
|
typedef DomainWallEOFAFermion<WilsonImplF> DomainWallEOFAFermionF;
|
||||||
typedef DomainWallEOFAFermion<WilsonImplD> DomainWallEOFAFermionD;
|
typedef DomainWallEOFAFermion<WilsonImplD> DomainWallEOFAFermionD;
|
||||||
|
|
||||||
typedef MobiusFermion<WilsonImplD2> MobiusFermionD2;
|
//typedef MobiusFermion<WilsonImplD2> MobiusFermionD2;
|
||||||
typedef MobiusFermion<WilsonImplF> MobiusFermionF;
|
typedef MobiusFermion<WilsonImplF> MobiusFermionF;
|
||||||
typedef MobiusFermion<WilsonImplD> MobiusFermionD;
|
typedef MobiusFermion<WilsonImplD> MobiusFermionD;
|
||||||
|
|
||||||
typedef MobiusEOFAFermion<WilsonImplD2> MobiusEOFAFermionD2;
|
//typedef MobiusEOFAFermion<WilsonImplD2> MobiusEOFAFermionD2;
|
||||||
typedef MobiusEOFAFermion<WilsonImplF> MobiusEOFAFermionF;
|
typedef MobiusEOFAFermion<WilsonImplF> MobiusEOFAFermionF;
|
||||||
typedef MobiusEOFAFermion<WilsonImplD> MobiusEOFAFermionD;
|
typedef MobiusEOFAFermion<WilsonImplD> MobiusEOFAFermionD;
|
||||||
|
|
||||||
typedef ZMobiusFermion<ZWilsonImplD2> ZMobiusFermionD2;
|
//typedef ZMobiusFermion<ZWilsonImplD2> ZMobiusFermionD2;
|
||||||
typedef ZMobiusFermion<ZWilsonImplF> ZMobiusFermionF;
|
typedef ZMobiusFermion<ZWilsonImplF> ZMobiusFermionF;
|
||||||
typedef ZMobiusFermion<ZWilsonImplD> ZMobiusFermionD;
|
typedef ZMobiusFermion<ZWilsonImplD> ZMobiusFermionD;
|
||||||
|
|
||||||
typedef ScaledShamirFermion<WilsonImplD2> ScaledShamirFermionD2;
|
//typedef ScaledShamirFermion<WilsonImplD2> ScaledShamirFermionD2;
|
||||||
typedef ScaledShamirFermion<WilsonImplF> ScaledShamirFermionF;
|
typedef ScaledShamirFermion<WilsonImplF> ScaledShamirFermionF;
|
||||||
typedef ScaledShamirFermion<WilsonImplD> ScaledShamirFermionD;
|
typedef ScaledShamirFermion<WilsonImplD> ScaledShamirFermionD;
|
||||||
|
|
||||||
typedef MobiusZolotarevFermion<WilsonImplD2> MobiusZolotarevFermionD2;
|
//typedef MobiusZolotarevFermion<WilsonImplD2> MobiusZolotarevFermionD2;
|
||||||
typedef MobiusZolotarevFermion<WilsonImplF> MobiusZolotarevFermionF;
|
typedef MobiusZolotarevFermion<WilsonImplF> MobiusZolotarevFermionF;
|
||||||
typedef MobiusZolotarevFermion<WilsonImplD> MobiusZolotarevFermionD;
|
typedef MobiusZolotarevFermion<WilsonImplD> MobiusZolotarevFermionD;
|
||||||
typedef ShamirZolotarevFermion<WilsonImplD2> ShamirZolotarevFermionD2;
|
//typedef ShamirZolotarevFermion<WilsonImplD2> ShamirZolotarevFermionD2;
|
||||||
typedef ShamirZolotarevFermion<WilsonImplF> ShamirZolotarevFermionF;
|
typedef ShamirZolotarevFermion<WilsonImplF> ShamirZolotarevFermionF;
|
||||||
typedef ShamirZolotarevFermion<WilsonImplD> ShamirZolotarevFermionD;
|
typedef ShamirZolotarevFermion<WilsonImplD> ShamirZolotarevFermionD;
|
||||||
|
|
||||||
typedef OverlapWilsonCayleyTanhFermion<WilsonImplD2> OverlapWilsonCayleyTanhFermionD2;
|
//typedef OverlapWilsonCayleyTanhFermion<WilsonImplD2> OverlapWilsonCayleyTanhFermionD2;
|
||||||
typedef OverlapWilsonCayleyTanhFermion<WilsonImplF> OverlapWilsonCayleyTanhFermionF;
|
typedef OverlapWilsonCayleyTanhFermion<WilsonImplF> OverlapWilsonCayleyTanhFermionF;
|
||||||
typedef OverlapWilsonCayleyTanhFermion<WilsonImplD> OverlapWilsonCayleyTanhFermionD;
|
typedef OverlapWilsonCayleyTanhFermion<WilsonImplD> OverlapWilsonCayleyTanhFermionD;
|
||||||
typedef OverlapWilsonCayleyZolotarevFermion<WilsonImplD2> OverlapWilsonCayleyZolotarevFermionD2;
|
//typedef OverlapWilsonCayleyZolotarevFermion<WilsonImplD2> OverlapWilsonCayleyZolotarevFermionD2;
|
||||||
typedef OverlapWilsonCayleyZolotarevFermion<WilsonImplF> OverlapWilsonCayleyZolotarevFermionF;
|
typedef OverlapWilsonCayleyZolotarevFermion<WilsonImplF> OverlapWilsonCayleyZolotarevFermionF;
|
||||||
typedef OverlapWilsonCayleyZolotarevFermion<WilsonImplD> OverlapWilsonCayleyZolotarevFermionD;
|
typedef OverlapWilsonCayleyZolotarevFermion<WilsonImplD> OverlapWilsonCayleyZolotarevFermionD;
|
||||||
|
|
||||||
// Continued fraction
|
// Continued fraction
|
||||||
typedef OverlapWilsonContFracTanhFermion<WilsonImplD2> OverlapWilsonContFracTanhFermionD2;
|
//typedef OverlapWilsonContFracTanhFermion<WilsonImplD2> OverlapWilsonContFracTanhFermionD2;
|
||||||
typedef OverlapWilsonContFracTanhFermion<WilsonImplF> OverlapWilsonContFracTanhFermionF;
|
typedef OverlapWilsonContFracTanhFermion<WilsonImplF> OverlapWilsonContFracTanhFermionF;
|
||||||
typedef OverlapWilsonContFracTanhFermion<WilsonImplD> OverlapWilsonContFracTanhFermionD;
|
typedef OverlapWilsonContFracTanhFermion<WilsonImplD> OverlapWilsonContFracTanhFermionD;
|
||||||
typedef OverlapWilsonContFracZolotarevFermion<WilsonImplD2> OverlapWilsonContFracZolotarevFermionD2;
|
//typedef OverlapWilsonContFracZolotarevFermion<WilsonImplD2> OverlapWilsonContFracZolotarevFermionD2;
|
||||||
typedef OverlapWilsonContFracZolotarevFermion<WilsonImplF> OverlapWilsonContFracZolotarevFermionF;
|
typedef OverlapWilsonContFracZolotarevFermion<WilsonImplF> OverlapWilsonContFracZolotarevFermionF;
|
||||||
typedef OverlapWilsonContFracZolotarevFermion<WilsonImplD> OverlapWilsonContFracZolotarevFermionD;
|
typedef OverlapWilsonContFracZolotarevFermion<WilsonImplD> OverlapWilsonContFracZolotarevFermionD;
|
||||||
|
|
||||||
// Partial fraction
|
// Partial fraction
|
||||||
typedef OverlapWilsonPartialFractionTanhFermion<WilsonImplD2> OverlapWilsonPartialFractionTanhFermionD2;
|
//typedef OverlapWilsonPartialFractionTanhFermion<WilsonImplD2> OverlapWilsonPartialFractionTanhFermionD2;
|
||||||
typedef OverlapWilsonPartialFractionTanhFermion<WilsonImplF> OverlapWilsonPartialFractionTanhFermionF;
|
typedef OverlapWilsonPartialFractionTanhFermion<WilsonImplF> OverlapWilsonPartialFractionTanhFermionF;
|
||||||
typedef OverlapWilsonPartialFractionTanhFermion<WilsonImplD> OverlapWilsonPartialFractionTanhFermionD;
|
typedef OverlapWilsonPartialFractionTanhFermion<WilsonImplD> OverlapWilsonPartialFractionTanhFermionD;
|
||||||
|
|
||||||
typedef OverlapWilsonPartialFractionZolotarevFermion<WilsonImplD2> OverlapWilsonPartialFractionZolotarevFermionD2;
|
//typedef OverlapWilsonPartialFractionZolotarevFermion<WilsonImplD2> OverlapWilsonPartialFractionZolotarevFermionD2;
|
||||||
typedef OverlapWilsonPartialFractionZolotarevFermion<WilsonImplF> OverlapWilsonPartialFractionZolotarevFermionF;
|
typedef OverlapWilsonPartialFractionZolotarevFermion<WilsonImplF> OverlapWilsonPartialFractionZolotarevFermionF;
|
||||||
typedef OverlapWilsonPartialFractionZolotarevFermion<WilsonImplD> OverlapWilsonPartialFractionZolotarevFermionD;
|
typedef OverlapWilsonPartialFractionZolotarevFermion<WilsonImplD> OverlapWilsonPartialFractionZolotarevFermionD;
|
||||||
|
|
||||||
@@ -252,19 +252,19 @@ typedef WilsonFermion<GparityWilsonImplD> GparityWilsonFermionD;
|
|||||||
typedef DomainWallFermion<GparityWilsonImplF> GparityDomainWallFermionF;
|
typedef DomainWallFermion<GparityWilsonImplF> GparityDomainWallFermionF;
|
||||||
typedef DomainWallFermion<GparityWilsonImplD> GparityDomainWallFermionD;
|
typedef DomainWallFermion<GparityWilsonImplD> GparityDomainWallFermionD;
|
||||||
|
|
||||||
typedef DomainWallEOFAFermion<GparityWilsonImplR> GparityDomainWallEOFAFermionD2;
|
//typedef DomainWallEOFAFermion<GparityWilsonImplR> GparityDomainWallEOFAFermionD2;
|
||||||
typedef DomainWallEOFAFermion<GparityWilsonImplF> GparityDomainWallEOFAFermionF;
|
typedef DomainWallEOFAFermion<GparityWilsonImplF> GparityDomainWallEOFAFermionF;
|
||||||
typedef DomainWallEOFAFermion<GparityWilsonImplD> GparityDomainWallEOFAFermionD;
|
typedef DomainWallEOFAFermion<GparityWilsonImplD> GparityDomainWallEOFAFermionD;
|
||||||
|
|
||||||
typedef WilsonTMFermion<GparityWilsonImplR> GparityWilsonTMFermionD2;
|
//typedef WilsonTMFermion<GparityWilsonImplR> GparityWilsonTMFermionD2;
|
||||||
typedef WilsonTMFermion<GparityWilsonImplF> GparityWilsonTMFermionF;
|
typedef WilsonTMFermion<GparityWilsonImplF> GparityWilsonTMFermionF;
|
||||||
typedef WilsonTMFermion<GparityWilsonImplD> GparityWilsonTMFermionD;
|
typedef WilsonTMFermion<GparityWilsonImplD> GparityWilsonTMFermionD;
|
||||||
|
|
||||||
typedef MobiusFermion<GparityWilsonImplR> GparityMobiusFermionD2;
|
//typedef MobiusFermion<GparityWilsonImplR> GparityMobiusFermionD2;
|
||||||
typedef MobiusFermion<GparityWilsonImplF> GparityMobiusFermionF;
|
typedef MobiusFermion<GparityWilsonImplF> GparityMobiusFermionF;
|
||||||
typedef MobiusFermion<GparityWilsonImplD> GparityMobiusFermionD;
|
typedef MobiusFermion<GparityWilsonImplD> GparityMobiusFermionD;
|
||||||
|
|
||||||
typedef MobiusEOFAFermion<GparityWilsonImplR> GparityMobiusEOFAFermionD2;
|
//typedef MobiusEOFAFermion<GparityWilsonImplR> GparityMobiusEOFAFermionD2;
|
||||||
typedef MobiusEOFAFermion<GparityWilsonImplF> GparityMobiusEOFAFermionF;
|
typedef MobiusEOFAFermion<GparityWilsonImplF> GparityMobiusEOFAFermionF;
|
||||||
typedef MobiusEOFAFermion<GparityWilsonImplD> GparityMobiusEOFAFermionD;
|
typedef MobiusEOFAFermion<GparityWilsonImplD> GparityMobiusEOFAFermionD;
|
||||||
|
|
||||||
|
|||||||
@@ -88,51 +88,14 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
// }
|
// }
|
||||||
//////////////////////////////////////////////
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
template <class T> struct SamePrecisionMapper {
|
|
||||||
typedef T HigherPrecVector ;
|
|
||||||
typedef T LowerPrecVector ;
|
|
||||||
};
|
|
||||||
template <class T> struct LowerPrecisionMapper { };
|
|
||||||
template <> struct LowerPrecisionMapper<vRealF> {
|
|
||||||
typedef vRealF HigherPrecVector ;
|
|
||||||
typedef vRealH LowerPrecVector ;
|
|
||||||
};
|
|
||||||
template <> struct LowerPrecisionMapper<vRealD> {
|
|
||||||
typedef vRealD HigherPrecVector ;
|
|
||||||
typedef vRealF LowerPrecVector ;
|
|
||||||
};
|
|
||||||
template <> struct LowerPrecisionMapper<vComplexF> {
|
|
||||||
typedef vComplexF HigherPrecVector ;
|
|
||||||
typedef vComplexH LowerPrecVector ;
|
|
||||||
};
|
|
||||||
template <> struct LowerPrecisionMapper<vComplexD> {
|
|
||||||
typedef vComplexD HigherPrecVector ;
|
|
||||||
typedef vComplexF LowerPrecVector ;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CoeffReal {
|
struct CoeffReal {
|
||||||
public:
|
public:
|
||||||
typedef RealD _Coeff_t;
|
typedef RealD _Coeff_t;
|
||||||
static const int Nhcs = 2;
|
|
||||||
template<class Simd> using PrecisionMapper = SamePrecisionMapper<Simd>;
|
|
||||||
};
|
|
||||||
struct CoeffRealHalfComms {
|
|
||||||
public:
|
|
||||||
typedef RealD _Coeff_t;
|
|
||||||
static const int Nhcs = 1;
|
|
||||||
template<class Simd> using PrecisionMapper = LowerPrecisionMapper<Simd>;
|
|
||||||
};
|
};
|
||||||
struct CoeffComplex {
|
struct CoeffComplex {
|
||||||
public:
|
public:
|
||||||
typedef ComplexD _Coeff_t;
|
typedef ComplexD _Coeff_t;
|
||||||
static const int Nhcs = 2;
|
|
||||||
template<class Simd> using PrecisionMapper = SamePrecisionMapper<Simd>;
|
|
||||||
};
|
|
||||||
struct CoeffComplexHalfComms {
|
|
||||||
public:
|
|
||||||
typedef ComplexD _Coeff_t;
|
|
||||||
static const int Nhcs = 1;
|
|
||||||
template<class Simd> using PrecisionMapper = LowerPrecisionMapper<Simd>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ public:
|
|||||||
|
|
||||||
static const int Dimension = Representation::Dimension;
|
static const int Dimension = Representation::Dimension;
|
||||||
static const bool isFundamental = Representation::isFundamental;
|
static const bool isFundamental = Representation::isFundamental;
|
||||||
static const int Nhcs = Options::Nhcs;
|
|
||||||
static const bool LsVectorised=false;
|
static const bool LsVectorised=false;
|
||||||
static const bool isGparity=true;
|
static const bool isGparity=true;
|
||||||
|
|
||||||
@@ -56,18 +55,15 @@ public:
|
|||||||
INHERIT_GIMPL_TYPES(Gimpl);
|
INHERIT_GIMPL_TYPES(Gimpl);
|
||||||
|
|
||||||
typedef typename Options::_Coeff_t Coeff_t;
|
typedef typename Options::_Coeff_t Coeff_t;
|
||||||
typedef typename Options::template PrecisionMapper<Simd>::LowerPrecVector SimdL;
|
|
||||||
|
|
||||||
template <typename vtype> using iImplSpinor = iVector<iVector<iVector<vtype, Dimension>, Ns>, Ngp>;
|
template <typename vtype> using iImplSpinor = iVector<iVector<iVector<vtype, Dimension>, Ns>, Ngp>;
|
||||||
template <typename vtype> using iImplPropagator = iMatrix<iMatrix<iMatrix<vtype, Dimension>, Ns>, Ngp>;
|
template <typename vtype> using iImplPropagator = iMatrix<iMatrix<iMatrix<vtype, Dimension>, Ns>, Ngp>;
|
||||||
template <typename vtype> using iImplHalfSpinor = iVector<iVector<iVector<vtype, Dimension>, Nhs>, Ngp>;
|
template <typename vtype> using iImplHalfSpinor = iVector<iVector<iVector<vtype, Dimension>, Nhs>, Ngp>;
|
||||||
template <typename vtype> using iImplHalfCommSpinor = iVector<iVector<iVector<vtype, Dimension>, Nhcs>, Ngp>;
|
|
||||||
template <typename vtype> using iImplDoubledGaugeField = iVector<iVector<iScalar<iMatrix<vtype, Dimension> >, Nds>, Ngp>;
|
template <typename vtype> using iImplDoubledGaugeField = iVector<iVector<iScalar<iMatrix<vtype, Dimension> >, Nds>, Ngp>;
|
||||||
|
|
||||||
typedef iImplSpinor<Simd> SiteSpinor;
|
typedef iImplSpinor<Simd> SiteSpinor;
|
||||||
typedef iImplPropagator<Simd> SitePropagator;
|
typedef iImplPropagator<Simd> SitePropagator;
|
||||||
typedef iImplHalfSpinor<Simd> SiteHalfSpinor;
|
typedef iImplHalfSpinor<Simd> SiteHalfSpinor;
|
||||||
typedef iImplHalfCommSpinor<SimdL> SiteHalfCommSpinor;
|
|
||||||
typedef iImplDoubledGaugeField<Simd> SiteDoubledGaugeField;
|
typedef iImplDoubledGaugeField<Simd> SiteDoubledGaugeField;
|
||||||
|
|
||||||
typedef Lattice<SiteSpinor> FermionField;
|
typedef Lattice<SiteSpinor> FermionField;
|
||||||
@@ -75,7 +71,7 @@ public:
|
|||||||
typedef Lattice<SiteDoubledGaugeField> DoubledGaugeField;
|
typedef Lattice<SiteDoubledGaugeField> DoubledGaugeField;
|
||||||
|
|
||||||
typedef GparityWilsonImplParams ImplParams;
|
typedef GparityWilsonImplParams ImplParams;
|
||||||
typedef WilsonCompressor<SiteHalfCommSpinor,SiteHalfSpinor, SiteSpinor> Compressor;
|
typedef WilsonCompressor<SiteHalfSpinor, SiteSpinor> Compressor;
|
||||||
typedef WilsonStencil<SiteSpinor, SiteHalfSpinor, ImplParams> StencilImpl;
|
typedef WilsonStencil<SiteSpinor, SiteHalfSpinor, ImplParams> StencilImpl;
|
||||||
typedef typename StencilImpl::View_type StencilView;
|
typedef typename StencilImpl::View_type StencilView;
|
||||||
|
|
||||||
@@ -409,8 +405,4 @@ typedef GparityWilsonImpl<vComplex , FundamentalRepresentation,CoeffReal> Gparit
|
|||||||
typedef GparityWilsonImpl<vComplexF, FundamentalRepresentation,CoeffReal> GparityWilsonImplF; // Float
|
typedef GparityWilsonImpl<vComplexF, FundamentalRepresentation,CoeffReal> GparityWilsonImplF; // Float
|
||||||
typedef GparityWilsonImpl<vComplexD, FundamentalRepresentation,CoeffReal> GparityWilsonImplD; // Double
|
typedef GparityWilsonImpl<vComplexD, FundamentalRepresentation,CoeffReal> GparityWilsonImplD; // Double
|
||||||
|
|
||||||
//typedef GparityWilsonImpl<vComplex , FundamentalRepresentation,CoeffRealHalfComms> GparityWilsonImplRL; // Real.. whichever prec
|
|
||||||
//typedef GparityWilsonImpl<vComplexF, FundamentalRepresentation,CoeffRealHalfComms> GparityWilsonImplFH; // Float
|
|
||||||
//typedef GparityWilsonImpl<vComplexD, FundamentalRepresentation,CoeffRealHalfComms> GparityWilsonImplDF; // Double
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
NAMESPACE_END(Grid);
|
||||||
|
|||||||
@@ -176,4 +176,13 @@ typedef StaggeredImpl<vComplex, FundamentalRepresentation > StaggeredImplR; /
|
|||||||
typedef StaggeredImpl<vComplexF, FundamentalRepresentation > StaggeredImplF; // Float
|
typedef StaggeredImpl<vComplexF, FundamentalRepresentation > StaggeredImplF; // Float
|
||||||
typedef StaggeredImpl<vComplexD, FundamentalRepresentation > StaggeredImplD; // Double
|
typedef StaggeredImpl<vComplexD, FundamentalRepresentation > StaggeredImplD; // Double
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Lexicographic staggered implementations
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
typedef StaggeredImpl<sComplexF, FundamentalRepresentation > lexStaggeredImplF;
|
||||||
|
typedef StaggeredImpl<sComplexD, FundamentalRepresentation > lexStaggeredImplD;
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// End lexicographic staggered implementations
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
NAMESPACE_END(Grid);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
|
|
||||||
|
|
||||||
//Could make FaceGather a template param, but then behaviour is runtime not compile time
|
//Could make FaceGather a template param, but then behaviour is runtime not compile time
|
||||||
template<class _HCspinor,class _Hspinor,class _Spinor, class projector>
|
template<class _Hspinor,class _Spinor, class projector>
|
||||||
class WilsonCompressorTemplate : public FaceGatherSimple
|
class WilsonCompressorTemplate : public FaceGatherSimple
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -53,13 +53,11 @@ public:
|
|||||||
|
|
||||||
typedef _Spinor SiteSpinor;
|
typedef _Spinor SiteSpinor;
|
||||||
typedef _Hspinor SiteHalfSpinor;
|
typedef _Hspinor SiteHalfSpinor;
|
||||||
typedef _HCspinor SiteHalfCommSpinor;
|
typedef typename SiteHalfSpinor::vector_type vector_type;
|
||||||
typedef typename SiteHalfCommSpinor::vector_type vComplexLow;
|
constexpr static int Nw=sizeof(SiteHalfSpinor)/sizeof(vector_type);
|
||||||
typedef typename SiteHalfSpinor::vector_type vComplexHigh;
|
|
||||||
constexpr static int Nw=sizeof(SiteHalfSpinor)/sizeof(vComplexHigh);
|
|
||||||
|
|
||||||
accelerator_inline int CommDatumSize(void) const {
|
accelerator_inline int CommDatumSize(void) const {
|
||||||
return sizeof(SiteHalfCommSpinor);
|
return sizeof(SiteHalfSpinor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************/
|
/*****************************************************/
|
||||||
@@ -159,7 +157,7 @@ public:
|
|||||||
spProj(result,in); \
|
spProj(result,in); \
|
||||||
} \
|
} \
|
||||||
}; \
|
}; \
|
||||||
template<typename HCS,typename HS,typename S> using Compressor = WilsonCompressorTemplate<HCS,HS,S,Projector>;
|
template<typename HS,typename S> using Compressor = WilsonCompressorTemplate<HS,S,Projector>;
|
||||||
|
|
||||||
DECLARE_PROJ(WilsonXpProjector,WilsonXpCompressor,spProjXp);
|
DECLARE_PROJ(WilsonXpProjector,WilsonXpCompressor,spProjXp);
|
||||||
DECLARE_PROJ(WilsonYpProjector,WilsonYpCompressor,spProjYp);
|
DECLARE_PROJ(WilsonYpProjector,WilsonYpCompressor,spProjYp);
|
||||||
@@ -188,7 +186,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template<typename HCS,typename HS,typename S> using WilsonCompressor = WilsonCompressorTemplate<HCS,HS,S,WilsonProjector>;
|
template<typename HS,typename S> using WilsonCompressor = WilsonCompressorTemplate<HS,S,WilsonProjector>;
|
||||||
|
|
||||||
// Fast comms buffer manipulation which should inline right through (avoid direction
|
// Fast comms buffer manipulation which should inline right through (avoid direction
|
||||||
// dependent logic that prevents inlining
|
// dependent logic that prevents inlining
|
||||||
@@ -237,7 +235,6 @@ public:
|
|||||||
// Use types to select the write direction by directon compressor
|
// Use types to select the write direction by directon compressor
|
||||||
typedef typename compressor::SiteSpinor SiteSpinor;
|
typedef typename compressor::SiteSpinor SiteSpinor;
|
||||||
typedef typename compressor::SiteHalfSpinor SiteHalfSpinor;
|
typedef typename compressor::SiteHalfSpinor SiteHalfSpinor;
|
||||||
typedef typename compressor::SiteHalfCommSpinor SiteHalfCommSpinor;
|
|
||||||
|
|
||||||
this->_grid->StencilBarrier();
|
this->_grid->StencilBarrier();
|
||||||
|
|
||||||
@@ -245,14 +242,14 @@ public:
|
|||||||
|
|
||||||
this->u_comm_offset=0;
|
this->u_comm_offset=0;
|
||||||
|
|
||||||
WilsonXpCompressor<SiteHalfCommSpinor,SiteHalfSpinor,SiteSpinor> XpCompress;
|
WilsonXpCompressor<SiteHalfSpinor,SiteSpinor> XpCompress;
|
||||||
WilsonYpCompressor<SiteHalfCommSpinor,SiteHalfSpinor,SiteSpinor> YpCompress;
|
WilsonYpCompressor<SiteHalfSpinor,SiteSpinor> YpCompress;
|
||||||
WilsonZpCompressor<SiteHalfCommSpinor,SiteHalfSpinor,SiteSpinor> ZpCompress;
|
WilsonZpCompressor<SiteHalfSpinor,SiteSpinor> ZpCompress;
|
||||||
WilsonTpCompressor<SiteHalfCommSpinor,SiteHalfSpinor,SiteSpinor> TpCompress;
|
WilsonTpCompressor<SiteHalfSpinor,SiteSpinor> TpCompress;
|
||||||
WilsonXmCompressor<SiteHalfCommSpinor,SiteHalfSpinor,SiteSpinor> XmCompress;
|
WilsonXmCompressor<SiteHalfSpinor,SiteSpinor> XmCompress;
|
||||||
WilsonYmCompressor<SiteHalfCommSpinor,SiteHalfSpinor,SiteSpinor> YmCompress;
|
WilsonYmCompressor<SiteHalfSpinor,SiteSpinor> YmCompress;
|
||||||
WilsonZmCompressor<SiteHalfCommSpinor,SiteHalfSpinor,SiteSpinor> ZmCompress;
|
WilsonZmCompressor<SiteHalfSpinor,SiteSpinor> ZmCompress;
|
||||||
WilsonTmCompressor<SiteHalfCommSpinor,SiteHalfSpinor,SiteSpinor> TmCompress;
|
WilsonTmCompressor<SiteHalfSpinor,SiteSpinor> TmCompress;
|
||||||
|
|
||||||
int dag = compress.dag;
|
int dag = compress.dag;
|
||||||
int face_idx=0;
|
int face_idx=0;
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ public:
|
|||||||
static const bool isFundamental = Representation::isFundamental;
|
static const bool isFundamental = Representation::isFundamental;
|
||||||
static const bool LsVectorised=false;
|
static const bool LsVectorised=false;
|
||||||
static const bool isGparity=false;
|
static const bool isGparity=false;
|
||||||
static const int Nhcs = Options::Nhcs;
|
|
||||||
|
|
||||||
typedef PeriodicGaugeImpl<GaugeImplTypes<S, Dimension > > Gimpl;
|
typedef PeriodicGaugeImpl<GaugeImplTypes<S, Dimension > > Gimpl;
|
||||||
INHERIT_GIMPL_TYPES(Gimpl);
|
INHERIT_GIMPL_TYPES(Gimpl);
|
||||||
@@ -51,25 +50,22 @@ public:
|
|||||||
constexpr bool is_fundamental() const{return Dimension == Nc ? 1 : 0;}
|
constexpr bool is_fundamental() const{return Dimension == Nc ? 1 : 0;}
|
||||||
|
|
||||||
typedef typename Options::_Coeff_t Coeff_t;
|
typedef typename Options::_Coeff_t Coeff_t;
|
||||||
typedef typename Options::template PrecisionMapper<Simd>::LowerPrecVector SimdL;
|
|
||||||
|
|
||||||
template <typename vtype> using iImplSpinor = iScalar<iVector<iVector<vtype, Dimension>, Ns> >;
|
template <typename vtype> using iImplSpinor = iScalar<iVector<iVector<vtype, Dimension>, Ns> >;
|
||||||
template <typename vtype> using iImplPropagator = iScalar<iMatrix<iMatrix<vtype, Dimension>, Ns> >;
|
template <typename vtype> using iImplPropagator = iScalar<iMatrix<iMatrix<vtype, Dimension>, Ns> >;
|
||||||
template <typename vtype> using iImplHalfSpinor = iScalar<iVector<iVector<vtype, Dimension>, Nhs> >;
|
template <typename vtype> using iImplHalfSpinor = iScalar<iVector<iVector<vtype, Dimension>, Nhs> >;
|
||||||
template <typename vtype> using iImplHalfCommSpinor = iScalar<iVector<iVector<vtype, Dimension>, Nhcs> >;
|
|
||||||
template <typename vtype> using iImplDoubledGaugeField = iVector<iScalar<iMatrix<vtype, Dimension> >, Nds>;
|
template <typename vtype> using iImplDoubledGaugeField = iVector<iScalar<iMatrix<vtype, Dimension> >, Nds>;
|
||||||
|
|
||||||
typedef iImplSpinor<Simd> SiteSpinor;
|
typedef iImplSpinor<Simd> SiteSpinor;
|
||||||
typedef iImplPropagator<Simd> SitePropagator;
|
typedef iImplPropagator<Simd> SitePropagator;
|
||||||
typedef iImplHalfSpinor<Simd> SiteHalfSpinor;
|
typedef iImplHalfSpinor<Simd> SiteHalfSpinor;
|
||||||
typedef iImplHalfCommSpinor<SimdL> SiteHalfCommSpinor;
|
|
||||||
typedef iImplDoubledGaugeField<Simd> SiteDoubledGaugeField;
|
typedef iImplDoubledGaugeField<Simd> SiteDoubledGaugeField;
|
||||||
|
|
||||||
typedef Lattice<SiteSpinor> FermionField;
|
typedef Lattice<SiteSpinor> FermionField;
|
||||||
typedef Lattice<SitePropagator> PropagatorField;
|
typedef Lattice<SitePropagator> PropagatorField;
|
||||||
typedef Lattice<SiteDoubledGaugeField> DoubledGaugeField;
|
typedef Lattice<SiteDoubledGaugeField> DoubledGaugeField;
|
||||||
|
|
||||||
typedef WilsonCompressor<SiteHalfCommSpinor,SiteHalfSpinor, SiteSpinor> Compressor;
|
typedef WilsonCompressor<SiteHalfSpinor, SiteSpinor> Compressor;
|
||||||
typedef WilsonImplParams ImplParams;
|
typedef WilsonImplParams ImplParams;
|
||||||
typedef WilsonStencil<SiteSpinor, SiteHalfSpinor,ImplParams> StencilImpl;
|
typedef WilsonStencil<SiteSpinor, SiteHalfSpinor,ImplParams> StencilImpl;
|
||||||
typedef const typename StencilImpl::View_type StencilView;
|
typedef const typename StencilImpl::View_type StencilView;
|
||||||
@@ -242,12 +238,19 @@ public:
|
|||||||
typedef WilsonImpl<vComplex, FundamentalRepresentation, CoeffReal > WilsonImplR; // Real.. whichever prec
|
typedef WilsonImpl<vComplex, FundamentalRepresentation, CoeffReal > WilsonImplR; // Real.. whichever prec
|
||||||
typedef WilsonImpl<vComplexF, FundamentalRepresentation, CoeffReal > WilsonImplF; // Float
|
typedef WilsonImpl<vComplexF, FundamentalRepresentation, CoeffReal > WilsonImplF; // Float
|
||||||
typedef WilsonImpl<vComplexD, FundamentalRepresentation, CoeffReal > WilsonImplD; // Double
|
typedef WilsonImpl<vComplexD, FundamentalRepresentation, CoeffReal > WilsonImplD; // Double
|
||||||
typedef WilsonImpl<vComplexD2, FundamentalRepresentation, CoeffReal > WilsonImplD2; // Double
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Lexicographic Wilson implementations
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
typedef WilsonImpl<sComplexF, FundamentalRepresentation, CoeffReal > lexWilsonImplF;
|
||||||
|
typedef WilsonImpl<sComplexD, FundamentalRepresentation, CoeffReal > lexWilsonImplD;
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// End lexicographic Wilson implementations
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
typedef WilsonImpl<vComplex, FundamentalRepresentation, CoeffComplex > ZWilsonImplR; // Real.. whichever prec
|
typedef WilsonImpl<vComplex, FundamentalRepresentation, CoeffComplex > ZWilsonImplR; // Real.. whichever prec
|
||||||
typedef WilsonImpl<vComplexF, FundamentalRepresentation, CoeffComplex > ZWilsonImplF; // Float
|
typedef WilsonImpl<vComplexF, FundamentalRepresentation, CoeffComplex > ZWilsonImplF; // Float
|
||||||
typedef WilsonImpl<vComplexD, FundamentalRepresentation, CoeffComplex > ZWilsonImplD; // Double
|
typedef WilsonImpl<vComplexD, FundamentalRepresentation, CoeffComplex > ZWilsonImplD; // Double
|
||||||
typedef WilsonImpl<vComplexD2, FundamentalRepresentation, CoeffComplex > ZWilsonImplD2; // Double
|
|
||||||
|
|
||||||
typedef WilsonImpl<vComplex, AdjointRepresentation, CoeffReal > WilsonAdjImplR; // Real.. whichever prec
|
typedef WilsonImpl<vComplex, AdjointRepresentation, CoeffReal > WilsonAdjImplR; // Real.. whichever prec
|
||||||
typedef WilsonImpl<vComplexF, AdjointRepresentation, CoeffReal > WilsonAdjImplF; // Float
|
typedef WilsonImpl<vComplexF, AdjointRepresentation, CoeffReal > WilsonAdjImplF; // Float
|
||||||
|
|||||||
@@ -684,7 +684,7 @@ void StaggeredKernels<Impl>::DhopSiteAsm(StencilView &st,
|
|||||||
#ifdef STAG_VEC5D
|
#ifdef STAG_VEC5D
|
||||||
// This is the single precision 5th direction vectorised kernel
|
// This is the single precision 5th direction vectorised kernel
|
||||||
#include <Grid/simd/Intel512single.h>
|
#include <Grid/simd/Intel512single.h>
|
||||||
template <> void StaggeredKernels<StaggeredVec5dImplF>::DhopSiteAsm(StencilView &st,
|
template <> inline void StaggeredKernels<StaggeredVec5dImplF>::DhopSiteAsm(StencilView &st,
|
||||||
DoubledGaugeFieldView &U,
|
DoubledGaugeFieldView &U,
|
||||||
DoubledGaugeFieldView &UUU,
|
DoubledGaugeFieldView &UUU,
|
||||||
SiteSpinor *buf, int sF,
|
SiteSpinor *buf, int sF,
|
||||||
@@ -739,7 +739,7 @@ template <> void StaggeredKernels<StaggeredVec5dImplF>::DhopSiteAsm(StencilView
|
|||||||
}
|
}
|
||||||
|
|
||||||
#include <Grid/simd/Intel512double.h>
|
#include <Grid/simd/Intel512double.h>
|
||||||
template <> void StaggeredKernels<StaggeredVec5dImplD>::DhopSiteAsm(StencilView &st,
|
template <> inline void StaggeredKernels<StaggeredVec5dImplD>::DhopSiteAsm(StencilView &st,
|
||||||
DoubledGaugeFieldView &U,
|
DoubledGaugeFieldView &U,
|
||||||
DoubledGaugeFieldView &UUU,
|
DoubledGaugeFieldView &UUU,
|
||||||
SiteSpinor *buf, int sF,
|
SiteSpinor *buf, int sF,
|
||||||
@@ -825,7 +825,7 @@ template <> void StaggeredKernels<StaggeredVec5dImplD>::DhopSiteAsm(StencilView
|
|||||||
// This is the single precision 5th direction vectorised kernel
|
// This is the single precision 5th direction vectorised kernel
|
||||||
|
|
||||||
#include <Grid/simd/Intel512single.h>
|
#include <Grid/simd/Intel512single.h>
|
||||||
template <> void StaggeredKernels<StaggeredImplF>::DhopSiteAsm(StencilView &st,
|
template <> inline void StaggeredKernels<StaggeredImplF>::DhopSiteAsm(StencilView &st,
|
||||||
DoubledGaugeFieldView &U,
|
DoubledGaugeFieldView &U,
|
||||||
DoubledGaugeFieldView &UUU,
|
DoubledGaugeFieldView &UUU,
|
||||||
SiteSpinor *buf, int sF,
|
SiteSpinor *buf, int sF,
|
||||||
@@ -894,7 +894,7 @@ template <> void StaggeredKernels<StaggeredImplF>::DhopSiteAsm(StencilView &st,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#include <Grid/simd/Intel512double.h>
|
#include <Grid/simd/Intel512double.h>
|
||||||
template <> void StaggeredKernels<StaggeredImplD>::DhopSiteAsm(StencilView &st,
|
template <> inline void StaggeredKernels<StaggeredImplD>::DhopSiteAsm(StencilView &st,
|
||||||
DoubledGaugeFieldView &U,
|
DoubledGaugeFieldView &U,
|
||||||
DoubledGaugeFieldView &UUU,
|
DoubledGaugeFieldView &UUU,
|
||||||
SiteSpinor *buf, int sF,
|
SiteSpinor *buf, int sF,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ directory
|
|||||||
#include <Grid/qcd/action/fermion/FermionCore.h>
|
#include <Grid/qcd/action/fermion/FermionCore.h>
|
||||||
#include <Grid/qcd/action/fermion/implementation/StaggeredKernelsImplementation.h>
|
#include <Grid/qcd/action/fermion/implementation/StaggeredKernelsImplementation.h>
|
||||||
#include <Grid/qcd/action/fermion/implementation/StaggeredKernelsHand.h>
|
#include <Grid/qcd/action/fermion/implementation/StaggeredKernelsHand.h>
|
||||||
|
#include <Grid/qcd/action/fermion/implementation/StaggeredKernelsAsm.h>
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
NAMESPACE_BEGIN(Grid);
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,15 @@
|
|||||||
|
|
||||||
STAG_IMPL_LIST=" \
|
STAG_IMPL_LIST=" \
|
||||||
StaggeredImplF \
|
StaggeredImplF \
|
||||||
StaggeredImplD "
|
StaggeredImplD \
|
||||||
|
lexStaggeredImplF \
|
||||||
STAG5_IMPL_LIST=""
|
lexStaggeredImplD "
|
||||||
|
|
||||||
WILSON_IMPL_LIST=" \
|
WILSON_IMPL_LIST=" \
|
||||||
WilsonImplF \
|
WilsonImplF \
|
||||||
WilsonImplD \
|
WilsonImplD \
|
||||||
WilsonImplD2 \
|
lexWilsonImplF \
|
||||||
|
lexWilsonImplD \
|
||||||
SpWilsonImplF \
|
SpWilsonImplF \
|
||||||
SpWilsonImplD \
|
SpWilsonImplD \
|
||||||
WilsonAdjImplF \
|
WilsonAdjImplF \
|
||||||
@@ -32,9 +33,10 @@ COMPACT_WILSON_IMPL_LIST=" \
|
|||||||
DWF_IMPL_LIST=" \
|
DWF_IMPL_LIST=" \
|
||||||
WilsonImplF \
|
WilsonImplF \
|
||||||
WilsonImplD \
|
WilsonImplD \
|
||||||
WilsonImplD2 \
|
lexWilsonImplF \
|
||||||
|
lexWilsonImplD \
|
||||||
ZWilsonImplF \
|
ZWilsonImplF \
|
||||||
ZWilsonImplD2 "
|
ZWilsonImplD "
|
||||||
|
|
||||||
GDWF_IMPL_LIST=" \
|
GDWF_IMPL_LIST=" \
|
||||||
GparityWilsonImplF \
|
GparityWilsonImplF \
|
||||||
@@ -109,8 +111,3 @@ do
|
|||||||
ln -f -s ../$f.cc.master $impl/$f$impl.cc
|
ln -f -s ../$f.cc.master $impl/$f$impl.cc
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
CC_LIST=" \
|
|
||||||
ImprovedStaggeredFermion5DInstantiation \
|
|
||||||
StaggeredKernelsInstantiation "
|
|
||||||
|
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../ImprovedStaggeredFermion5DInstantiation.cc.master
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../ImprovedStaggeredFermionInstantiation.cc.master
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../NaiveStaggeredFermionInstantiation.cc.master
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../StaggeredKernelsInstantiation.cc.master
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#define IMPLEMENTATION lexStaggeredImplD
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../ImprovedStaggeredFermion5DInstantiation.cc.master
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../ImprovedStaggeredFermionInstantiation.cc.master
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../NaiveStaggeredFermionInstantiation.cc.master
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../StaggeredKernelsInstantiation.cc.master
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#define IMPLEMENTATION lexStaggeredImplF
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../CayleyFermion5DInstantiation.cc.master
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../ContinuedFractionFermion5DInstantiation.cc.master
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../DomainWallEOFAFermionInstantiation.cc.master
|
||||||
Grid/qcd/action/fermion/instantiation/lexWilsonImplD/MobiusEOFAFermionInstantiationlexWilsonImplD.cc
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../MobiusEOFAFermionInstantiation.cc.master
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../PartialFractionFermion5DInstantiation.cc.master
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../WilsonCloverFermionInstantiation.cc.master
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../WilsonFermion5DInstantiation.cc.master
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../WilsonFermionInstantiation.cc.master
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../WilsonKernelsInstantiation.cc.master
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../WilsonTMFermionInstantiation.cc.master
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#define IMPLEMENTATION lexWilsonImplD
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../CayleyFermion5DInstantiation.cc.master
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../ContinuedFractionFermion5DInstantiation.cc.master
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../DomainWallEOFAFermionInstantiation.cc.master
|
||||||
Grid/qcd/action/fermion/instantiation/lexWilsonImplF/MobiusEOFAFermionInstantiationlexWilsonImplF.cc
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../MobiusEOFAFermionInstantiation.cc.master
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../PartialFractionFermion5DInstantiation.cc.master
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../WilsonCloverFermionInstantiation.cc.master
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../WilsonFermion5DInstantiation.cc.master
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../WilsonFermionInstantiation.cc.master
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../WilsonKernelsInstantiation.cc.master
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../WilsonTMFermionInstantiation.cc.master
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#define IMPLEMENTATION lexWilsonImplF
|
||||||
@@ -60,8 +60,10 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
typedef typename Impl::SiteField SiteField; \
|
typedef typename Impl::SiteField SiteField; \
|
||||||
typedef typename Impl::Field Field;
|
typedef typename Impl::Field Field;
|
||||||
|
|
||||||
// hardcodes the exponential approximation in the template
|
// Order of the exponential approximation
|
||||||
template <class S, int Nrepresentation = Nc, int Nexp = 12, class Group = SU<Nc> > class GaugeImplTypes {
|
const int NexpDefault = 12;
|
||||||
|
|
||||||
|
template <class S, int Nrepresentation = Nc, int Nexp = NexpDefault, class Group = SU<Nc> > class GaugeImplTypes {
|
||||||
public:
|
public:
|
||||||
typedef S Simd;
|
typedef S Simd;
|
||||||
typedef typename Simd::scalar_type scalar_type;
|
typedef typename Simd::scalar_type scalar_type;
|
||||||
@@ -188,14 +190,29 @@ typedef GaugeImplTypes<vComplex, Nc> GimplTypesR;
|
|||||||
typedef GaugeImplTypes<vComplexF, Nc> GimplTypesF;
|
typedef GaugeImplTypes<vComplexF, Nc> GimplTypesF;
|
||||||
typedef GaugeImplTypes<vComplexD, Nc> GimplTypesD;
|
typedef GaugeImplTypes<vComplexD, Nc> GimplTypesD;
|
||||||
|
|
||||||
typedef GaugeImplTypes<vComplex, Nc, 12, Sp<Nc> > SpGimplTypesR;
|
typedef GaugeImplTypes<vComplex, Nc, NexpDefault, Sp<Nc> > SpGimplTypesR;
|
||||||
typedef GaugeImplTypes<vComplexF, Nc, 12, Sp<Nc> > SpGimplTypesF;
|
typedef GaugeImplTypes<vComplexF, Nc, NexpDefault, Sp<Nc> > SpGimplTypesF;
|
||||||
typedef GaugeImplTypes<vComplexD, Nc, 12, Sp<Nc> > SpGimplTypesD;
|
typedef GaugeImplTypes<vComplexD, Nc, NexpDefault, Sp<Nc> > SpGimplTypesD;
|
||||||
|
|
||||||
typedef GaugeImplTypes<vComplex, SU<Nc>::AdjointDimension> GimplAdjointTypesR;
|
typedef GaugeImplTypes<vComplex, SU<Nc>::AdjointDimension> GimplAdjointTypesR;
|
||||||
typedef GaugeImplTypes<vComplexF, SU<Nc>::AdjointDimension> GimplAdjointTypesF;
|
typedef GaugeImplTypes<vComplexF, SU<Nc>::AdjointDimension> GimplAdjointTypesF;
|
||||||
typedef GaugeImplTypes<vComplexD, SU<Nc>::AdjointDimension> GimplAdjointTypesD;
|
typedef GaugeImplTypes<vComplexD, SU<Nc>::AdjointDimension> GimplAdjointTypesD;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Lexicographic gauge implementation types
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
typedef GaugeImplTypes<sComplexF, Nc> lexGimplTypesF;
|
||||||
|
typedef GaugeImplTypes<sComplexD, Nc> lexGimplTypesD;
|
||||||
|
|
||||||
|
typedef GaugeImplTypes<sComplexF, Nc, NexpDefault, Sp<Nc> > lexSpGimplTypesF;
|
||||||
|
typedef GaugeImplTypes<sComplexD, Nc, NexpDefault, Sp<Nc> > lexSpGimplTypesD;
|
||||||
|
|
||||||
|
typedef GaugeImplTypes<sComplexF, SU<Nc>::AdjointDimension> lexGimplAdjointTypesF;
|
||||||
|
typedef GaugeImplTypes<sComplexD, SU<Nc>::AdjointDimension> lexGimplAdjointTypesD;
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// End lexicographic gauge implementation types
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -197,6 +197,24 @@ typedef PeriodicGaugeImpl<SpGimplTypesR> SpPeriodicGimplR; // Real.. whichever p
|
|||||||
typedef PeriodicGaugeImpl<SpGimplTypesF> SpPeriodicGimplF; // Float
|
typedef PeriodicGaugeImpl<SpGimplTypesF> SpPeriodicGimplF; // Float
|
||||||
typedef PeriodicGaugeImpl<SpGimplTypesD> SpPeriodicGimplD; // Double
|
typedef PeriodicGaugeImpl<SpGimplTypesD> SpPeriodicGimplD; // Double
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Lexicographic gauge implementations
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
typedef PeriodicGaugeImpl<lexGimplTypesF> lexPeriodicGimplF;
|
||||||
|
typedef PeriodicGaugeImpl<lexGimplTypesD> lexPeriodicGimplD;
|
||||||
|
|
||||||
|
typedef PeriodicGaugeImpl<lexGimplAdjointTypesF> lexPeriodicGimplAdjF;
|
||||||
|
typedef PeriodicGaugeImpl<lexGimplAdjointTypesD> lexPeriodicGimplAdjD;
|
||||||
|
|
||||||
|
typedef ConjugateGaugeImpl<lexGimplTypesF> lexConjugateGimplF;
|
||||||
|
typedef ConjugateGaugeImpl<lexGimplTypesD> lexConjugateGimplD;
|
||||||
|
|
||||||
|
typedef PeriodicGaugeImpl<lexSpGimplTypesF> lexSpPeriodicGimplF;
|
||||||
|
typedef PeriodicGaugeImpl<lexSpGimplTypesD> lexSpPeriodicGimplD;
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// End lexicographic gauge implementations
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
NAMESPACE_END(Grid);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
/*************************************************************************************
|
||||||
|
|
||||||
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
|
Source file: ./lib/qcd/action/pseudofermion/TwoFlavourBosonPseudoFermion.h
|
||||||
|
|
||||||
|
Copyright (C) 2026
|
||||||
|
|
||||||
|
Author: Peter Boyle <pboyle@bnl.gov>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
|
*************************************************************************************/
|
||||||
|
/* END LEGAL */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
NAMESPACE_BEGIN(Grid);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Two flavour BOSON (wrong-sign) pseudofermion for any FermionOperator B:
|
||||||
|
//
|
||||||
|
// S2 = chi^dag Bdag B chi = |B chi|^2
|
||||||
|
//
|
||||||
|
// integral ==> det( Bdag B )^-1 = |det B|^-2
|
||||||
|
//
|
||||||
|
// A compensator monomial: supplies an INVERSE determinant with NO solve in
|
||||||
|
// the force or the action -- both are matrix multiplies. The only solve is
|
||||||
|
// the heatbath chi = B^-1 eta, once per trajectory (for B = the
|
||||||
|
// Pauli-Villars operator this is a mass-one solve, trivially cheap).
|
||||||
|
//
|
||||||
|
// Primary use: two instances with B = PV cancel the |det PV|^2 excess of
|
||||||
|
// TwoFlavourPVdagMPseudoFermionAction down to the DWF quotient
|
||||||
|
// |det M|^2/|det PV|^2 (two unsquared instances rather than one squared
|
||||||
|
// kernel: first powers of PV in the force, milder). Being generic in B it
|
||||||
|
// also serves Hasenbusch-chain compensation at intermediate masses, or any
|
||||||
|
// future inverse-det bookkeeping. (Sibling of the domain-decomposed boson
|
||||||
|
// in DomainDecomposedBoundaryTwoFlavourBosonPseudoFermion.h, without the
|
||||||
|
// boundary machinery.)
|
||||||
|
//
|
||||||
|
// Heatbath exact by construction: S2 after refresh = |B B^-1 eta|^2 = |eta|^2.
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<class Impl>
|
||||||
|
class TwoFlavourBosonPseudoFermionAction : public Action<typename Impl::GaugeField> {
|
||||||
|
public:
|
||||||
|
INHERIT_IMPL_TYPES(Impl);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FermionOperator<Impl> & BOp; // the operator whose |det|^-2 is supplied
|
||||||
|
|
||||||
|
LinearFunction<FermionField> &HeatbathSolver; // b -> B^-1 b (heatbath only)
|
||||||
|
|
||||||
|
FermionField Chi; // the pseudo fermion field for this trajectory
|
||||||
|
|
||||||
|
public:
|
||||||
|
TwoFlavourBosonPseudoFermionAction(FermionOperator<Impl> &_BOp,
|
||||||
|
LinearFunction<FermionField> & HS
|
||||||
|
) : BOp(_BOp),
|
||||||
|
HeatbathSolver(HS),
|
||||||
|
Chi(_BOp.FermionGrid())
|
||||||
|
{};
|
||||||
|
|
||||||
|
virtual std::string action_name(){return "TwoFlavourBosonPseudoFermionAction";}
|
||||||
|
|
||||||
|
virtual std::string LogParameters(){
|
||||||
|
std::stringstream sstream;
|
||||||
|
sstream << GridLogMessage << "["<<action_name()<<"] has no parameters" << std::endl;
|
||||||
|
return sstream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void refresh(const GaugeField &U, GridSerialRNG &sRNG, GridParallelRNG& pRNG) {
|
||||||
|
// P(chi) = e^{- chi^dag BdagB chi} ; chi = B^-1 eta ; P(eta) = e^{-eta^dag eta}
|
||||||
|
// e^{-x^2/2 sig^2} => sig^2 = 0.5 ; eta enters with width 1/sqrt(2).
|
||||||
|
RealD scale = std::sqrt(0.5);
|
||||||
|
FermionField eta(BOp.FermionGrid());
|
||||||
|
gaussian(pRNG,eta);
|
||||||
|
eta = eta * scale;
|
||||||
|
refresh(U,eta);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deterministic-noise variant (test hook):
|
||||||
|
// after this, S(U) == norm2(eta) exactly (to solver tolerance).
|
||||||
|
void refresh(const GaugeField &U, const FermionField &eta) {
|
||||||
|
BOp.ImportGauge(U);
|
||||||
|
Chi = Zero();
|
||||||
|
HeatbathSolver(eta,Chi); // Chi = B^-1 eta : the ONLY solve
|
||||||
|
std::cout << GridLogMessage << action_name() << " refresh |Chi|^2 = "<< norm2(Chi)<<std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
// S2 = |B chi|^2 -- matrix multiply only
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
virtual RealD S(const GaugeField &U) {
|
||||||
|
BOp.ImportGauge(U);
|
||||||
|
|
||||||
|
FermionField w(BOp.FermionGrid());
|
||||||
|
BOp.M(Chi,w); // w = B chi
|
||||||
|
RealD action = norm2(w);
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
// dS2 = chi^dag dBdag w + w^dag dB chi , w = B chi
|
||||||
|
// NO solves.
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
virtual void deriv(const GaugeField &U,GaugeField & dSdU) {
|
||||||
|
BOp.ImportGauge(U);
|
||||||
|
|
||||||
|
FermionField w(BOp.FermionGrid());
|
||||||
|
GaugeField force(BOp.GaugeGrid());
|
||||||
|
|
||||||
|
BOp.M(Chi,w); // w = B chi
|
||||||
|
|
||||||
|
BOp.MDeriv(force, Chi, w, DaggerYes); dSdU = force;
|
||||||
|
BOp.MDeriv(force, w, Chi, DaggerNo ); dSdU = dSdU+force;
|
||||||
|
|
||||||
|
dSdU *= -1.0; // Grid action sign convention (cf TwoFlavourRatio.h)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
NAMESPACE_END(Grid);
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
/*************************************************************************************
|
||||||
|
|
||||||
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
|
Source file: ./lib/qcd/action/pseudofermion/TwoFlavourPVdagMPseudoFermion.h
|
||||||
|
|
||||||
|
Copyright (C) 2026
|
||||||
|
|
||||||
|
Author: Peter Boyle <pboyle@bnl.gov>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
|
*************************************************************************************/
|
||||||
|
/* END LEGAL */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
NAMESPACE_BEGIN(Grid);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Two flavour pseudofermion on the COMPOSITE operator F = PVdag M :
|
||||||
|
//
|
||||||
|
// S1 = phi^dag (Fdag F)^-1 phi
|
||||||
|
//
|
||||||
|
// integral ==> det( Mdag PV PVdag M ) = |det M|^2 |det PV|^2
|
||||||
|
//
|
||||||
|
// i.e. the target two-flavour |det M|^2 TIMES an excess |det PV|^2, to be
|
||||||
|
// cancelled by compensator monomials (two TwoFlavourBosonPseudoFermionAction
|
||||||
|
// instances on PV, each contributing |det PV|^-2, net |det PV|^-4; together
|
||||||
|
// with this action's |det PV|^2 the ensemble carries |det M|^2/|det PV|^2 --
|
||||||
|
// the standard DWF quotient).
|
||||||
|
//
|
||||||
|
// Why this shape: F = PVdag M is exactly the operator the non-Hermitian
|
||||||
|
// multigrid coarsens, so its cycles precondition (Fdag F) natively. The
|
||||||
|
// outer solve is CG on a Hermitian positive definite system -- the
|
||||||
|
// non-normality is quarantined inside the preconditioner. One solve per
|
||||||
|
// force evaluation; all other force ingredients are matrix multiplies.
|
||||||
|
//
|
||||||
|
// Heatbath is exact by OPERATOR ALGEBRA (no wall/projection identity):
|
||||||
|
// refresh: phi = Fdag eta ==> S1 = eta^dag F (Fdag F)^-1 Fdag eta
|
||||||
|
// = |eta|^2 (to solver tol)
|
||||||
|
//
|
||||||
|
// Hasenbusch: nothing here requires PVOp to have mass one. Any
|
||||||
|
// (heavier,lighter) pair F(m1,m2) = D^dag(m1) D(m2) works, each rung
|
||||||
|
// coarsenable by the same machinery; compensate intermediate-mass excess
|
||||||
|
// dets with boson monomials on the heavier operator.
|
||||||
|
//
|
||||||
|
// Solver slots map b -> (Fdag F)^-1 b (full grid, zero guess imposed
|
||||||
|
// internally). The class is agnostic to the implementation: plain CG on
|
||||||
|
// the normal equations for testing; sequential MG solves of Fdag and F, or
|
||||||
|
// preconditioned CG with a frozen-cycle G Gdag preconditioner in production.
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<class Impl>
|
||||||
|
class TwoFlavourPVdagMPseudoFermionAction : public Action<typename Impl::GaugeField> {
|
||||||
|
public:
|
||||||
|
INHERIT_IMPL_TYPES(Impl);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FermionOperator<Impl> & PVOp; // the heavier / Pauli-Villars operator
|
||||||
|
FermionOperator<Impl> & MOp; // the lighter operator
|
||||||
|
|
||||||
|
LinearFunction<FermionField> &DerivSolver; // b -> (FdagF)^-1 b, MD tolerance
|
||||||
|
LinearFunction<FermionField> &ActionSolver; // b -> (FdagF)^-1 b, accept/reject tolerance
|
||||||
|
|
||||||
|
FermionField Phi; // the pseudo fermion field for this trajectory
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// F = PVdag M and Fdag = Mdag PV
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void Fapply(const FermionField &in, FermionField &out) {
|
||||||
|
FermionField tmp(MOp.FermionGrid());
|
||||||
|
MOp.M(in,tmp);
|
||||||
|
PVOp.Mdag(tmp,out);
|
||||||
|
}
|
||||||
|
void FdagApply(const FermionField &in, FermionField &out) {
|
||||||
|
FermionField tmp(MOp.FermionGrid());
|
||||||
|
PVOp.M(in,tmp);
|
||||||
|
MOp.Mdag(tmp,out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
TwoFlavourPVdagMPseudoFermionAction(FermionOperator<Impl> &_PVOp,
|
||||||
|
FermionOperator<Impl> &_MOp,
|
||||||
|
LinearFunction<FermionField> & DS,
|
||||||
|
LinearFunction<FermionField> & AS
|
||||||
|
) : PVOp(_PVOp),
|
||||||
|
MOp(_MOp),
|
||||||
|
DerivSolver(DS),
|
||||||
|
ActionSolver(AS),
|
||||||
|
Phi(_MOp.FermionGrid())
|
||||||
|
{};
|
||||||
|
|
||||||
|
virtual std::string action_name(){return "TwoFlavourPVdagMPseudoFermionAction";}
|
||||||
|
|
||||||
|
virtual std::string LogParameters(){
|
||||||
|
std::stringstream sstream;
|
||||||
|
sstream << GridLogMessage << "["<<action_name()<<"] has no parameters" << std::endl;
|
||||||
|
return sstream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void refresh(const GaugeField &U, GridSerialRNG &sRNG, GridParallelRNG& pRNG) {
|
||||||
|
// P(phi) = e^{- phi^dag (FdagF)^-1 phi} ; phi = Fdag eta ; P(eta) = e^{-eta^dag eta}
|
||||||
|
// e^{-x^2/2 sig^2} => sig^2 = 0.5 ; eta enters with width 1/sqrt(2).
|
||||||
|
RealD scale = std::sqrt(0.5);
|
||||||
|
FermionField eta(MOp.FermionGrid());
|
||||||
|
gaussian(pRNG,eta);
|
||||||
|
eta = eta * scale;
|
||||||
|
refresh(U,eta);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deterministic-noise variant (test hook, TwoFlavourEvenOddRatio idiom):
|
||||||
|
// after this, S(U) == norm2(eta) exactly (to solver tolerance).
|
||||||
|
void refresh(const GaugeField &U, const FermionField &eta) {
|
||||||
|
PVOp.ImportGauge(U);
|
||||||
|
MOp.ImportGauge(U);
|
||||||
|
FdagApply(eta,Phi); // NO solve: heatbath is two matmuls
|
||||||
|
std::cout << GridLogMessage << action_name() << " refresh |Phi|^2 = "<< norm2(Phi)<<std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
// S1 = phi^dag (FdagF)^-1 phi
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
virtual RealD S(const GaugeField &U) {
|
||||||
|
PVOp.ImportGauge(U);
|
||||||
|
MOp.ImportGauge(U);
|
||||||
|
|
||||||
|
FermionField X(MOp.FermionGrid());
|
||||||
|
X = Zero();
|
||||||
|
ActionSolver(Phi,X); // X = (FdagF)^-1 phi
|
||||||
|
|
||||||
|
RealD action = real(innerProduct(Phi,X)); // Hermitian positive kernel
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
// dS1 = - X^dag [ dFdag F + Fdag dF ] X , X = (FdagF)^-1 phi, Y = F X
|
||||||
|
//
|
||||||
|
// dF = dPVdag M + PVdag dM ==> with A = M X, B = PV Y :
|
||||||
|
//
|
||||||
|
// dS1 = - X^dag dMdag B - B^dag dM X - A^dag dPV Y - Y^dag dPVdag A
|
||||||
|
//
|
||||||
|
// ONE solve; A,Y,B by matrix multiply (Y = PVdag A reuses A).
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
virtual void deriv(const GaugeField &U,GaugeField & dSdU) {
|
||||||
|
PVOp.ImportGauge(U);
|
||||||
|
MOp.ImportGauge(U);
|
||||||
|
|
||||||
|
FermionField X(MOp.FermionGrid());
|
||||||
|
FermionField Y(MOp.FermionGrid());
|
||||||
|
FermionField A(MOp.FermionGrid());
|
||||||
|
FermionField B(MOp.FermionGrid());
|
||||||
|
|
||||||
|
GaugeField force(MOp.GaugeGrid());
|
||||||
|
|
||||||
|
X = Zero();
|
||||||
|
DerivSolver(Phi,X); // X = (FdagF)^-1 phi
|
||||||
|
MOp.M(X,A); // A = M X
|
||||||
|
PVOp.Mdag(A,Y); // Y = PVdag A = F X
|
||||||
|
PVOp.M(Y,B); // B = PV Y
|
||||||
|
|
||||||
|
// dS1 = -( X^dag dMdag B + B^dag dM X + A^dag dPV Y + Y^dag dPVdag A )
|
||||||
|
MOp.MDeriv (force, X, B, DaggerYes); dSdU = -force;
|
||||||
|
MOp.MDeriv (force, B, X, DaggerNo ); dSdU = dSdU -force;
|
||||||
|
PVOp.MDeriv(force, A, Y, DaggerNo ); dSdU = dSdU -force;
|
||||||
|
PVOp.MDeriv(force, Y, A, DaggerYes); dSdU = dSdU -force;
|
||||||
|
|
||||||
|
dSdU *= -1.0; // Grid action sign convention (cf TwoFlavourRatio.h)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
NAMESPACE_END(Grid);
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
/*************************************************************************************
|
||||||
|
|
||||||
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
|
Source file: ./lib/qcd/action/pseudofermion/TwoFlavourRatio4DPseudoFermion.h
|
||||||
|
|
||||||
|
Copyright (C) 2026
|
||||||
|
|
||||||
|
Author: Peter Boyle <pboyle@bnl.gov>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
|
*************************************************************************************/
|
||||||
|
/* END LEGAL */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
NAMESPACE_BEGIN(Grid);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Two flavour ratio with FOUR dimensional pseudofermion, UNpreconditioned
|
||||||
|
// (full grid) solves.
|
||||||
|
//
|
||||||
|
// Companion to TwoFlavourRatioEO4DPseudoFermion.h but with the solver
|
||||||
|
// plumbing exposed as LinearFunction<FermionField> objects that already
|
||||||
|
// know their operator -- the natural interface for the non-Hermitian
|
||||||
|
// multigrid GCR stack (PVdagM), which solves M and Mdag DIRECTLY rather
|
||||||
|
// than through SchurRedBlack normal equations.
|
||||||
|
//
|
||||||
|
// Why: with 5D pseudofermions the squared-operator formulation hands
|
||||||
|
// normal-equation solvers (MdagM)^-1 phi AND Mdag^-1 phi from ONE Krylov
|
||||||
|
// space; a direct solver must solve twice, halving its per-solve gain.
|
||||||
|
// The 4D pseudofermion action needs one M^-1 and one M^-dag solve per
|
||||||
|
// force evaluation FOR BOTH solver families, so the direct-solver gain
|
||||||
|
// carries through undiluted. In addition phi4 is Ls-agnostic, so the
|
||||||
|
// force can be evaluated with a reduced-Ls operator pair while the
|
||||||
|
// accept/reject uses full Ls (inexact force, exact action).
|
||||||
|
//
|
||||||
|
// Solver slots (all full-grid 5D LinearFunctions, solution overwritten,
|
||||||
|
// zero guess imposed internally):
|
||||||
|
// DerivMinvSolver : x = M^-1 b (DenOp)
|
||||||
|
// DerivMdagInvSolver : x = M^-dag b (DenOp). For G5R5-hermitian
|
||||||
|
// actions this may be implemented by the caller as
|
||||||
|
// G5R5 . DerivMinvSolver . G5R5 -- no adjoint
|
||||||
|
// multigrid needed.
|
||||||
|
// ActionMinvSolver : x = M^-1 b (DenOp, accept/reject tolerance)
|
||||||
|
// HeatbathVinvSolver : x = V^-1 b (NumOp)
|
||||||
|
//
|
||||||
|
// 4D <-> 5D wall maps: the action is S = | P (M^-1 V) Pdag phi4 |^2 where
|
||||||
|
// (P,Pdag) MUST be a mutually adjoint pair for S and deriv to be
|
||||||
|
// consistent. Two candidate conventions, selected by solution_walls:
|
||||||
|
// true : P = P_- psi(0) + P_+ psi(Ls-1) (solution walls, matches
|
||||||
|
// ExportPhysicalFermionSolution) and Pdag its literal adjoint.
|
||||||
|
// false : P = P_+ psi(0) + P_- psi(Ls-1) (source walls, Pdag matches
|
||||||
|
// ImportUnphysicalFermion).
|
||||||
|
// The heatbath is exact iff [P M^-1 V Pdag][P V^-1 M Pdag] = 1 (the 4D
|
||||||
|
// effective-operator composition identity); which convention satisfies it
|
||||||
|
// is settled numerically by the refresh test S == 0.5*|eta4|^2 exactly.
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<class Impl>
|
||||||
|
class TwoFlavourRatio4DPseudoFermionAction : public Action<typename Impl::GaugeField> {
|
||||||
|
public:
|
||||||
|
INHERIT_IMPL_TYPES(Impl);
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef FermionOperator<Impl> FermOp;
|
||||||
|
FermionOperator<Impl> & NumOp;// the basic operator (V)
|
||||||
|
FermionOperator<Impl> & DenOp;// the basic operator (M)
|
||||||
|
|
||||||
|
LinearFunction<FermionField> &DerivMinvSolver;
|
||||||
|
LinearFunction<FermionField> &DerivMdagInvSolver;
|
||||||
|
LinearFunction<FermionField> &ActionMinvSolver;
|
||||||
|
LinearFunction<FermionField> &HeatbathVinvSolver;
|
||||||
|
|
||||||
|
FermionField phi4; // the pseudo fermion field for this trajectory
|
||||||
|
|
||||||
|
int solution_walls; // wall convention for the (P,Pdag) pair; see header
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// The mutually adjoint 4D <-> 5D pair.
|
||||||
|
// Wall4D : q4 = P psi5 (extract)
|
||||||
|
// Wall4DAdj : psi5 = Pdag q4 (insert; literal adjoint of Wall4D)
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void Wall4D(const FermionField &psi5, FermionField &q4)
|
||||||
|
{
|
||||||
|
int Ls = NumOp.FermionGrid()->_fdimensions[0];
|
||||||
|
FermionField tmp(NumOp.FermionGrid());
|
||||||
|
if ( solution_walls ) {
|
||||||
|
// q4 = P_- psi(0) + P_+ psi(Ls-1)
|
||||||
|
axpby_ssp_pminus(tmp, 0., psi5, 1., psi5, 0, 0);
|
||||||
|
axpby_ssp_pplus (tmp, 1., tmp , 1., psi5, 0, Ls-1);
|
||||||
|
} else {
|
||||||
|
// q4 = P_+ psi(0) + P_- psi(Ls-1)
|
||||||
|
axpby_ssp_pplus (tmp, 0., psi5, 1., psi5, 0, 0);
|
||||||
|
axpby_ssp_pminus(tmp, 1., tmp , 1., psi5, 0, Ls-1);
|
||||||
|
}
|
||||||
|
ExtractSlice(q4, tmp, 0, 0);
|
||||||
|
}
|
||||||
|
void Wall4DAdj(const FermionField &q4, FermionField &psi5)
|
||||||
|
{
|
||||||
|
int Ls = NumOp.FermionGrid()->_fdimensions[0];
|
||||||
|
FermionField tmp(NumOp.FermionGrid());
|
||||||
|
tmp = Zero();
|
||||||
|
InsertSlice(q4, tmp, 0 , 0);
|
||||||
|
InsertSlice(q4, tmp, Ls-1, 0);
|
||||||
|
if ( solution_walls ) {
|
||||||
|
// psi(0) = P_- q4 ; psi(Ls-1) = P_+ q4
|
||||||
|
axpby_ssp_pminus(tmp, 0., tmp, 1., tmp, 0 , 0);
|
||||||
|
axpby_ssp_pplus (tmp, 0., tmp, 1., tmp, Ls-1, Ls-1);
|
||||||
|
} else {
|
||||||
|
// psi(0) = P_+ q4 ; psi(Ls-1) = P_- q4
|
||||||
|
axpby_ssp_pplus (tmp, 0., tmp, 1., tmp, 0 , 0);
|
||||||
|
axpby_ssp_pminus(tmp, 0., tmp, 1., tmp, Ls-1, Ls-1);
|
||||||
|
}
|
||||||
|
psi5 = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
TwoFlavourRatio4DPseudoFermionAction(FermionOperator<Impl> &_NumOp,
|
||||||
|
FermionOperator<Impl> &_DenOp,
|
||||||
|
LinearFunction<FermionField> & DMS,
|
||||||
|
LinearFunction<FermionField> & DMDS,
|
||||||
|
LinearFunction<FermionField> & AMS,
|
||||||
|
LinearFunction<FermionField> & HVS,
|
||||||
|
int _solution_walls = 1
|
||||||
|
) : NumOp(_NumOp),
|
||||||
|
DenOp(_DenOp),
|
||||||
|
DerivMinvSolver(DMS),
|
||||||
|
DerivMdagInvSolver(DMDS),
|
||||||
|
ActionMinvSolver(AMS),
|
||||||
|
HeatbathVinvSolver(HVS),
|
||||||
|
phi4(_NumOp.GaugeGrid()),
|
||||||
|
solution_walls(_solution_walls)
|
||||||
|
{};
|
||||||
|
|
||||||
|
virtual std::string action_name(){return "TwoFlavourRatio4DPseudoFermionAction";}
|
||||||
|
|
||||||
|
virtual std::string LogParameters(){
|
||||||
|
std::stringstream sstream;
|
||||||
|
sstream << GridLogMessage << "["<<action_name()<<"] solution_walls " << solution_walls << std::endl;
|
||||||
|
return sstream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void refresh(const GaugeField &U, GridSerialRNG &sRNG, GridParallelRNG& pRNG) {
|
||||||
|
|
||||||
|
// P(phi4) = e^{- phi4^dag Beff^dag Beff phi4} ; Beff = P M^-1 V Pdag
|
||||||
|
//
|
||||||
|
// NumOp == V
|
||||||
|
// DenOp == M
|
||||||
|
//
|
||||||
|
// Take phi4 = P V^-1 M Pdag eta4 ( = Beff^-1 eta4 by the composition
|
||||||
|
// identity; verified numerically by S == 0.5 |eta4|^2 after refresh )
|
||||||
|
//
|
||||||
|
// P(eta) = e^{- eta^dag eta} ; e^{-x^2/2 sig^2} => sig^2 = 0.5
|
||||||
|
// so eta enters with width 1/sqrt(2).
|
||||||
|
//
|
||||||
|
RealD scale = std::sqrt(0.5);
|
||||||
|
|
||||||
|
FermionField eta4(NumOp.GaugeGrid());
|
||||||
|
FermionField eta5(NumOp.FermionGrid());
|
||||||
|
FermionField tmp (NumOp.FermionGrid());
|
||||||
|
FermionField phi5(NumOp.FermionGrid());
|
||||||
|
|
||||||
|
gaussian(pRNG,eta4);
|
||||||
|
|
||||||
|
NumOp.ImportGauge(U);
|
||||||
|
DenOp.ImportGauge(U);
|
||||||
|
|
||||||
|
Wall4DAdj(eta4,eta5); // eta5 = Pdag eta4
|
||||||
|
DenOp.M(eta5,tmp); // tmp = M eta5
|
||||||
|
phi5 = Zero();
|
||||||
|
HeatbathVinvSolver(tmp,phi5); // phi5 = V^-1 M eta5
|
||||||
|
Wall4D(phi5,phi4); // phi4 = P phi5
|
||||||
|
phi4 = phi4*scale;
|
||||||
|
|
||||||
|
std::cout << GridLogMessage << "4d pf (non-EO) refresh "<< norm2(phi4)<<"\n";
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
// S = phi4^dag (Pdag^dag V^dag M^-dag P^dag) (P M^-1 V Pdag) phi4
|
||||||
|
// = | P M^-1 V Pdag phi4 |^2
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
virtual RealD S(const GaugeField &U) {
|
||||||
|
|
||||||
|
NumOp.ImportGauge(U);
|
||||||
|
DenOp.ImportGauge(U);
|
||||||
|
|
||||||
|
FermionField Y4 (NumOp.GaugeGrid());
|
||||||
|
FermionField phi5(NumOp.FermionGrid());
|
||||||
|
FermionField X (NumOp.FermionGrid());
|
||||||
|
FermionField Y (NumOp.FermionGrid());
|
||||||
|
|
||||||
|
Wall4DAdj(phi4,phi5); // phi5 = Pdag phi4
|
||||||
|
NumOp.M(phi5,X); // X = V phi5
|
||||||
|
Y = Zero();
|
||||||
|
ActionMinvSolver(X,Y); // Y = M^-1 V phi5
|
||||||
|
Wall4D(Y,Y4); // Y4 = P Y
|
||||||
|
|
||||||
|
RealD action = norm2(Y4);
|
||||||
|
|
||||||
|
return action;
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
// dS/du = 2 Re [ (M^-dag Pdag w4)^dag dV Pdag phi4 ]
|
||||||
|
// - 2 Re [ (M^-dag Pdag w4)^dag dM (M^-1 V Pdag phi4) ]
|
||||||
|
// with w4 = P M^-1 V Pdag phi4.
|
||||||
|
// Two first-power solves: one M^-1, one M^-dag.
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
virtual void deriv(const GaugeField &U,GaugeField & dSdU) {
|
||||||
|
|
||||||
|
NumOp.ImportGauge(U);
|
||||||
|
DenOp.ImportGauge(U);
|
||||||
|
|
||||||
|
FermionField phi5 (NumOp.FermionGrid());
|
||||||
|
FermionField Vphi (NumOp.FermionGrid());
|
||||||
|
FermionField MinvVphi (NumOp.FermionGrid());
|
||||||
|
FermionField w4 (NumOp.GaugeGrid());
|
||||||
|
FermionField Y (NumOp.FermionGrid());
|
||||||
|
FermionField MdagInvPdagW (NumOp.FermionGrid());
|
||||||
|
|
||||||
|
GaugeField force(NumOp.GaugeGrid());
|
||||||
|
|
||||||
|
Wall4DAdj(phi4,phi5); // phi5 = Pdag phi4
|
||||||
|
NumOp.M(phi5,Vphi); // Vphi = V phi5
|
||||||
|
MinvVphi = Zero();
|
||||||
|
DerivMinvSolver(Vphi,MinvVphi); // MinvVphi = M^-1 V phi5
|
||||||
|
std::cout << GridLogMessage << "4d pf (non-EO) deriv solve "<< norm2(MinvVphi)<<"\n";
|
||||||
|
|
||||||
|
// Project onto the physical 4D subspace and back: Y = Pdag P MinvVphi.
|
||||||
|
// Pdag here MUST be the literal adjoint of the P used in S, else the
|
||||||
|
// force is inconsistent with the action.
|
||||||
|
Wall4D(MinvVphi,w4); // w4 = P MinvVphi
|
||||||
|
Wall4DAdj(w4,Y); // Y = Pdag w4
|
||||||
|
|
||||||
|
MdagInvPdagW = Zero();
|
||||||
|
DerivMdagInvSolver(Y,MdagInvPdagW); // = M^-dag Pdag w4 (adjoint solve)
|
||||||
|
std::cout << GridLogMessage << "4d pf (non-EO) deriv solve dag "<< norm2(MdagInvPdagW)<<"\n";
|
||||||
|
|
||||||
|
// phi^dag (Pdag' Vdag Mdag^-1 P') (dV) Pdag phi + h.c.
|
||||||
|
NumOp.MDeriv(force, MdagInvPdagW, phi5, DaggerNo ); dSdU=force;
|
||||||
|
NumOp.MDeriv(force, phi5, MdagInvPdagW, DaggerYes); dSdU=dSdU+force;
|
||||||
|
|
||||||
|
// - phi^dag ( ... Mdag^-1 ) dM ( M^-1 V ... ) phi + h.c.
|
||||||
|
DenOp.MDeriv(force, MdagInvPdagW, MinvVphi, DaggerNo ); dSdU=dSdU-force;
|
||||||
|
DenOp.MDeriv(force, MinvVphi, MdagInvPdagW, DaggerYes); dSdU=dSdU-force;
|
||||||
|
|
||||||
|
dSdU *= -1.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
NAMESPACE_END(Grid);
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
/*************************************************************************************
|
||||||
|
|
||||||
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
|
Source file: ./lib/qcd/action/pseudofermion/TwoFlavourRatioLeftPrec.h
|
||||||
|
|
||||||
|
Copyright (C) 2026
|
||||||
|
|
||||||
|
Author: Peter Boyle <pboyle@bnl.gov>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
|
*************************************************************************************/
|
||||||
|
/* END LEGAL */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
NAMESPACE_BEGIN(Grid);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Two flavour ratio with LEFT-PRECONDITIONED solves.
|
||||||
|
//
|
||||||
|
// Same action content as TwoFlavourRatio.h:
|
||||||
|
//
|
||||||
|
// S = phi^dag V (Mdag M)^-1 Vdag phi ==> det[ Mdag M / Vdag V ]
|
||||||
|
//
|
||||||
|
// (V = NumOp the heavier / Pauli-Villars operator, M = DenOp the lighter),
|
||||||
|
// but organised around the composite
|
||||||
|
//
|
||||||
|
// F = Vdag M
|
||||||
|
//
|
||||||
|
// which is the 2-hop-coarsenable operator the non-Hermitian multigrid
|
||||||
|
// serves. Solving M X = b as F X = Vdag b is LEFT PRECONDITIONING by
|
||||||
|
// Vdag; the determinant/action layer is the standard quotient, and all
|
||||||
|
// novelty is confined to the solver contract.
|
||||||
|
//
|
||||||
|
// TwoFlavourRatio.h is tied to a normal-equations solver: one (MdagM)^-1
|
||||||
|
// solve, then Y = M X gives Mdag^-1 Vdag phi almost free. The left-
|
||||||
|
// preconditioned idiom is DIFFERENT: the chain
|
||||||
|
//
|
||||||
|
// b = Vdag phi
|
||||||
|
// z : Fdag z = b (adjoint F solve)
|
||||||
|
// Y = V z (= Mdag^-1 Vdag phi -- harvested from solve 1)
|
||||||
|
// s = Vdag Y (= Vdag V z)
|
||||||
|
// X : F X = s (forward F solve; X = (MdagM)^-1 Vdag phi)
|
||||||
|
//
|
||||||
|
// yields Y BEFORE X (so S(U) needs only the adjoint solve), with Y's
|
||||||
|
// accuracy independent of the second solve. Force terms are then the
|
||||||
|
// standard four MDeriv insertions of TwoFlavourRatio.
|
||||||
|
//
|
||||||
|
// Solver slots are LinearFunctions with the F-SOLVE contract (solution
|
||||||
|
// overwritten, zero guess imposed internally):
|
||||||
|
// ForwardSolver(b,x) : F x = b
|
||||||
|
// AdjointSolver(b,z) : Fdag z = b
|
||||||
|
// implemented in production by the multigrid-GCR stack (forward cycle and
|
||||||
|
// adjoint cycle); in tests by CG on the composite normal equations.
|
||||||
|
// HeatbathSolver(b,x) : x = (Vdag V)^-1 b -- heavy operator, plain CG.
|
||||||
|
//
|
||||||
|
// Heatbath is exact by operator algebra: phi = V (VdagV)^-1 Mdag eta
|
||||||
|
// ==> S = | Mdag^-1 Vdag phi |^2 = |eta|^2 (to solver tolerance); the
|
||||||
|
// deterministic refresh(U,eta) hook below is the test point.
|
||||||
|
//
|
||||||
|
// Hasenbusch: nothing requires V to have mass one; any (heavier,lighter)
|
||||||
|
// pair works, F(V,M) = Vdag M coarsenable by the same machinery, rungs'
|
||||||
|
// solves are F-family (mrhs-batchable, mass-shared coarse space).
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<class Impl>
|
||||||
|
class TwoFlavourRatioLeftPrecPseudoFermionAction : public Action<typename Impl::GaugeField> {
|
||||||
|
public:
|
||||||
|
INHERIT_IMPL_TYPES(Impl);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FermionOperator<Impl> & NumOp;// V
|
||||||
|
FermionOperator<Impl> & DenOp;// M
|
||||||
|
|
||||||
|
LinearFunction<FermionField> &DerivForwardSolver; // F x = b, MD tolerance
|
||||||
|
LinearFunction<FermionField> &DerivAdjointSolver; // Fdag z = b, MD tolerance
|
||||||
|
LinearFunction<FermionField> &ActionAdjointSolver; // Fdag z = b, accept/reject tolerance
|
||||||
|
LinearFunction<FermionField> &HeatbathSolver; // (VdagV)^-1 b, heavy op
|
||||||
|
|
||||||
|
FermionField Phi; // the pseudo fermion field for this trajectory
|
||||||
|
|
||||||
|
public:
|
||||||
|
TwoFlavourRatioLeftPrecPseudoFermionAction(FermionOperator<Impl> &_NumOp,
|
||||||
|
FermionOperator<Impl> &_DenOp,
|
||||||
|
LinearFunction<FermionField> & DFS,
|
||||||
|
LinearFunction<FermionField> & DAS,
|
||||||
|
LinearFunction<FermionField> & AAS,
|
||||||
|
LinearFunction<FermionField> & HS
|
||||||
|
) : NumOp(_NumOp),
|
||||||
|
DenOp(_DenOp),
|
||||||
|
DerivForwardSolver(DFS),
|
||||||
|
DerivAdjointSolver(DAS),
|
||||||
|
ActionAdjointSolver(AAS),
|
||||||
|
HeatbathSolver(HS),
|
||||||
|
Phi(_NumOp.FermionGrid())
|
||||||
|
{};
|
||||||
|
|
||||||
|
virtual std::string action_name(){return "TwoFlavourRatioLeftPrecPseudoFermionAction";}
|
||||||
|
|
||||||
|
virtual std::string LogParameters(){
|
||||||
|
std::stringstream sstream;
|
||||||
|
sstream << GridLogMessage << "["<<action_name()<<"] has no parameters" << std::endl;
|
||||||
|
return sstream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void refresh(const GaugeField &U, GridSerialRNG &sRNG, GridParallelRNG& pRNG) {
|
||||||
|
// P(phi) = e^{- phi^dag V (MdagM)^-1 Vdag phi} ; phi = Vdag^-1 Mdag eta
|
||||||
|
// e^{-x^2/2 sig^2} => sig^2 = 0.5 ; eta enters with width 1/sqrt(2).
|
||||||
|
RealD scale = std::sqrt(0.5);
|
||||||
|
FermionField eta(NumOp.FermionGrid());
|
||||||
|
gaussian(pRNG,eta);
|
||||||
|
eta = eta * scale;
|
||||||
|
refresh(U,eta);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deterministic-noise variant (test hook):
|
||||||
|
// after this, S(U) == norm2(eta) exactly (to solver tolerance).
|
||||||
|
void refresh(const GaugeField &U, const FermionField &eta) {
|
||||||
|
NumOp.ImportGauge(U);
|
||||||
|
DenOp.ImportGauge(U);
|
||||||
|
|
||||||
|
FermionField tmp(NumOp.FermionGrid());
|
||||||
|
FermionField w (NumOp.FermionGrid());
|
||||||
|
|
||||||
|
DenOp.Mdag(eta,tmp); // tmp = Mdag eta
|
||||||
|
w = Zero();
|
||||||
|
HeatbathSolver(tmp,w); // w = (VdagV)^-1 Mdag eta
|
||||||
|
NumOp.M(w,Phi); // Phi = V (VdagV)^-1 Mdag eta = Vdag^-1 Mdag eta
|
||||||
|
std::cout << GridLogMessage << action_name() << " refresh |Phi|^2 = "<< norm2(Phi)<<std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
// S = phi^dag V (MdagM)^-1 Vdag phi = | Mdag^-1 Vdag phi |^2
|
||||||
|
// ONE adjoint F solve: Y = V Fdag^-1 Vdag phi = Mdag^-1 Vdag phi
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
virtual RealD S(const GaugeField &U) {
|
||||||
|
NumOp.ImportGauge(U);
|
||||||
|
DenOp.ImportGauge(U);
|
||||||
|
|
||||||
|
FermionField b(NumOp.FermionGrid());
|
||||||
|
FermionField z(NumOp.FermionGrid());
|
||||||
|
FermionField Y(NumOp.FermionGrid());
|
||||||
|
|
||||||
|
NumOp.Mdag(Phi,b); // b = Vdag phi
|
||||||
|
z = Zero();
|
||||||
|
ActionAdjointSolver(b,z); // Fdag z = b
|
||||||
|
NumOp.M(z,Y); // Y = V z = Mdag^-1 Vdag phi
|
||||||
|
|
||||||
|
RealD action = norm2(Y);
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
// dS/du = phi^dag dV (MdagM)^-1 Vdag phi
|
||||||
|
// - phi^dag V (MdagM)^-1 [ Mdag dM + dMdag M ] (MdagM)^-1 Vdag phi
|
||||||
|
// + phi^dag V (MdagM)^-1 dVdag phi
|
||||||
|
// Identical force insertions to TwoFlavourRatio.h; X and Y from the
|
||||||
|
// left-preconditioned chain (Y harvested from the adjoint solve).
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
virtual void deriv(const GaugeField &U,GaugeField & dSdU) {
|
||||||
|
NumOp.ImportGauge(U);
|
||||||
|
DenOp.ImportGauge(U);
|
||||||
|
|
||||||
|
FermionField b(NumOp.FermionGrid());
|
||||||
|
FermionField z(NumOp.FermionGrid());
|
||||||
|
FermionField Y(NumOp.FermionGrid());
|
||||||
|
FermionField s(NumOp.FermionGrid());
|
||||||
|
FermionField X(NumOp.FermionGrid());
|
||||||
|
|
||||||
|
GaugeField force(NumOp.GaugeGrid());
|
||||||
|
|
||||||
|
NumOp.Mdag(Phi,b); // b = Vdag phi
|
||||||
|
z = Zero();
|
||||||
|
DerivAdjointSolver(b,z); // Fdag z = b
|
||||||
|
NumOp.M(z,Y); // Y = V z = Mdag^-1 Vdag phi (solve-1 harvest)
|
||||||
|
NumOp.Mdag(Y,s); // s = Vdag V z
|
||||||
|
X = Zero();
|
||||||
|
DerivForwardSolver(s,X); // F X = s ==> X = (MdagM)^-1 Vdag phi
|
||||||
|
|
||||||
|
// phi^dag V (MdagM)^-1 dVdag phi
|
||||||
|
NumOp.MDeriv(force , X, Phi, DaggerYes); dSdU = force;
|
||||||
|
// phi^dag dV (MdagM)^-1 Vdag phi
|
||||||
|
NumOp.MDeriv(force , Phi, X, DaggerNo ); dSdU = dSdU+force;
|
||||||
|
// - phi^dag V (MdagM)^-1 Mdag dM (MdagM)^-1 Vdag phi
|
||||||
|
// - phi^dag V (MdagM)^-1 dMdag M (MdagM)^-1 Vdag phi
|
||||||
|
DenOp.MDeriv(force, Y, X, DaggerNo ); dSdU = dSdU-force;
|
||||||
|
DenOp.MDeriv(force, X, Y, DaggerYes); dSdU = dSdU-force;
|
||||||
|
|
||||||
|
dSdU *= -1.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
NAMESPACE_END(Grid);
|
||||||
+12
-12
@@ -89,10 +89,8 @@ template <int ncolour, class group_name>
|
|||||||
class GaugeGroup {
|
class GaugeGroup {
|
||||||
public:
|
public:
|
||||||
static const int Dimension = ncolour;
|
static const int Dimension = ncolour;
|
||||||
static const int AdjointDimension =
|
static const int AdjointDimension = compute_adjoint_dimension<group_name>(ncolour);
|
||||||
compute_adjoint_dimension<group_name>(ncolour);
|
static const int AlgebraDimension = compute_adjoint_dimension<group_name>(ncolour);
|
||||||
static const int AlgebraDimension =
|
|
||||||
compute_adjoint_dimension<group_name>(ncolour);
|
|
||||||
|
|
||||||
template <typename vtype>
|
template <typename vtype>
|
||||||
using iSU2Matrix = iScalar<iScalar<iMatrix<vtype, 2> > >;
|
using iSU2Matrix = iScalar<iScalar<iMatrix<vtype, 2> > >;
|
||||||
@@ -101,8 +99,7 @@ class GaugeGroup {
|
|||||||
template <typename vtype>
|
template <typename vtype>
|
||||||
using iAlgebraVector = iScalar<iScalar<iVector<vtype, AdjointDimension> > >;
|
using iAlgebraVector = iScalar<iScalar<iVector<vtype, AdjointDimension> > >;
|
||||||
template <typename vtype>
|
template <typename vtype>
|
||||||
using iSUnAlgebraMatrix =
|
using iSUnAlgebraMatrix = iScalar<iScalar<iMatrix<vtype, AdjointDimension> > >;
|
||||||
iScalar<iScalar<iMatrix<vtype, AdjointDimension> > >;
|
|
||||||
static int su2subgroups(void) { return su2subgroups(group_name()); }
|
static int su2subgroups(void) { return su2subgroups(group_name()); }
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -297,15 +294,17 @@ class GaugeGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template <int N,class vComplex_t>
|
template <int N,class vComplex_t>
|
||||||
static Lattice<iScalar<iScalar<iMatrix<vComplex_t, N> > > > ProjectOnGeneralGroup(const Lattice<iScalar<iScalar<iMatrix<vComplex_t, N> > > > &Umu) {
|
static Lattice<iScalar<iScalar<iMatrix<vComplex_t, N> > > >
|
||||||
|
ProjectOnGeneralGroup(const Lattice<iScalar<iScalar<iMatrix<vComplex_t, N> > > > &Umu)
|
||||||
|
{
|
||||||
return ProjectOnGeneralGroup(Umu, group_name());
|
return ProjectOnGeneralGroup(Umu, group_name());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int N,class vComplex_t> // Projects on SU(N), Sp(2N), with unit determinant, by first projecting on general group and then enforcing unit determinant
|
// Projects on SU(N), Sp(2N), with unit determinant, by first projecting on general group and then enforcing unit determinant
|
||||||
static void ProjectOnSpecialGroup(Lattice<iScalar<iScalar<iMatrix<vComplex_t, N> > > > &Umu) {
|
template <int N,class vComplex_t>
|
||||||
|
static void ProjectOnSpecialGroup(Lattice<iScalar<iScalar<iMatrix<vComplex_t, N> > > > &Umu)
|
||||||
|
{
|
||||||
Umu = ProjectOnGeneralGroup(Umu);
|
Umu = ProjectOnGeneralGroup(Umu);
|
||||||
auto det = Determinant(Umu);
|
auto det = Determinant(Umu);
|
||||||
|
|
||||||
@@ -319,7 +318,8 @@ class GaugeGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <int N,class vComplex_t> // reunitarise, resimplectify... previously ProjectSUn
|
template <int N,class vComplex_t> // reunitarise, resimplectify... previously ProjectSUn
|
||||||
static void ProjectOnSpecialGroup(Lattice<iVector<iScalar<iMatrix<vComplex_t, N> >, Nd> > &U) {
|
static void ProjectOnSpecialGroup(Lattice<iVector<iScalar<iMatrix<vComplex_t, N> >, Nd> > &U)
|
||||||
|
{
|
||||||
// Reunitarise
|
// Reunitarise
|
||||||
for (int mu = 0; mu < Nd; mu++) {
|
for (int mu = 0; mu < Nd; mu++) {
|
||||||
auto Umu = PeekIndex<LorentzIndex>(U, mu);
|
auto Umu = PeekIndex<LorentzIndex>(U, mu);
|
||||||
|
|||||||
@@ -933,6 +933,8 @@ typedef Optimization::Div DivSIMD;
|
|||||||
typedef Optimization::Mult MultSIMD;
|
typedef Optimization::Mult MultSIMD;
|
||||||
typedef Optimization::MultComplex MultComplexSIMD;
|
typedef Optimization::MultComplex MultComplexSIMD;
|
||||||
typedef Optimization::MultAddComplex MultAddComplexSIMD;
|
typedef Optimization::MultAddComplex MultAddComplexSIMD;
|
||||||
|
// Asserts MultAddComplexSIMD and a three argument MultSIMD operator()
|
||||||
|
#define GRID_ARCH_HAS_COMPLEX_MULT_ADD 1
|
||||||
typedef Optimization::MultRealPart MultRealPartSIMD;
|
typedef Optimization::MultRealPart MultRealPartSIMD;
|
||||||
typedef Optimization::MaddRealPart MaddRealPartSIMD;
|
typedef Optimization::MaddRealPart MaddRealPartSIMD;
|
||||||
typedef Optimization::Conj ConjSIMD;
|
typedef Optimization::Conj ConjSIMD;
|
||||||
|
|||||||
@@ -760,6 +760,8 @@ typedef Optimization::Div DivSIMD;
|
|||||||
typedef Optimization::Mult MultSIMD;
|
typedef Optimization::Mult MultSIMD;
|
||||||
typedef Optimization::MultComplex MultComplexSIMD;
|
typedef Optimization::MultComplex MultComplexSIMD;
|
||||||
typedef Optimization::MultAddComplex MultAddComplexSIMD;
|
typedef Optimization::MultAddComplex MultAddComplexSIMD;
|
||||||
|
// Asserts MultAddComplexSIMD and a three argument MultSIMD operator()
|
||||||
|
#define GRID_ARCH_HAS_COMPLEX_MULT_ADD 1
|
||||||
typedef Optimization::MultRealPart MultRealPartSIMD;
|
typedef Optimization::MultRealPart MultRealPartSIMD;
|
||||||
typedef Optimization::MaddRealPart MaddRealPartSIMD;
|
typedef Optimization::MaddRealPart MaddRealPartSIMD;
|
||||||
typedef Optimization::Conj ConjSIMD;
|
typedef Optimization::Conj ConjSIMD;
|
||||||
|
|||||||
@@ -339,54 +339,6 @@ typedef Grid_simd2<complex<double> , vComplexD> vComplexD2;
|
|||||||
typedef Grid_simd2<double , vRealD> vRealD2;
|
typedef Grid_simd2<double , vRealD> vRealD2;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////
|
|
||||||
// Some traits to recognise the types
|
|
||||||
/////////////////////////////////////////
|
|
||||||
template <typename T>
|
|
||||||
struct is_simd : public std::false_type {};
|
|
||||||
template <> struct is_simd<vRealF> : public std::true_type {};
|
|
||||||
template <> struct is_simd<vRealD> : public std::true_type {};
|
|
||||||
template <> struct is_simd<vRealH> : public std::true_type {};
|
|
||||||
template <> struct is_simd<vComplexF> : public std::true_type {};
|
|
||||||
template <> struct is_simd<vComplexD> : public std::true_type {};
|
|
||||||
template <> struct is_simd<vComplexH> : public std::true_type {};
|
|
||||||
template <> struct is_simd<vInteger> : public std::true_type {};
|
|
||||||
template <> struct is_simd<vRealD2> : public std::true_type {};
|
|
||||||
template <> struct is_simd<vComplexD2> : public std::true_type {};
|
|
||||||
|
|
||||||
template <typename T> using IfSimd = Invoke<std::enable_if<is_simd<T>::value, int> >;
|
|
||||||
template <typename T> using IfNotSimd = Invoke<std::enable_if<!is_simd<T>::value, unsigned> >;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////
|
|
||||||
// insert / extract with complex support
|
|
||||||
///////////////////////////////////////////////
|
|
||||||
template <class S, class V>
|
|
||||||
accelerator_inline S getlane(const Grid_simd<S, V> &in,int lane) {
|
|
||||||
return in.getlane(lane);
|
|
||||||
}
|
|
||||||
template <class S, class V>
|
|
||||||
accelerator_inline void putlane(Grid_simd<S, V> &vec,const S &_S, int lane){
|
|
||||||
vec.putlane(_S,lane);
|
|
||||||
}
|
|
||||||
template <class S,IfNotSimd<S> = 0 >
|
|
||||||
accelerator_inline S getlane(const S &in,int lane) {
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
template <class S,IfNotSimd<S> = 0 >
|
|
||||||
accelerator_inline void putlane(S &vec,const S &_S, int lane){
|
|
||||||
vec = _S;
|
|
||||||
}
|
|
||||||
template <class S, class V>
|
|
||||||
accelerator_inline S getlane(const Grid_simd2<S, V> &in,int lane) {
|
|
||||||
return in.getlane(lane);
|
|
||||||
}
|
|
||||||
template <class S, class V>
|
|
||||||
accelerator_inline void putlane(Grid_simd2<S, V> &vec,const S &_S, int lane){
|
|
||||||
vec.putlane(_S,lane);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// General rotate
|
// General rotate
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -42,6 +42,16 @@
|
|||||||
#define GEN_SIMD_WIDTH 16u
|
#define GEN_SIMD_WIDTH 16u
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// ARMv8.3 complex assist (FCMLA). Kept separate from the ACLE
|
||||||
|
// __ARM_FEATURE_COMPLEX: the instructions are also present on A64FX, where
|
||||||
|
// their throughput is 1:4 and this path would be a regression. Revisit the
|
||||||
|
// rule once measured per target.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
#ifdef __ARM_FEATURE_COMPLEX
|
||||||
|
#define GRID_ARM_FEATURE_COMPLEX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "Grid_generic_types.h"
|
#include "Grid_generic_types.h"
|
||||||
#include <arm_neon.h>
|
#include <arm_neon.h>
|
||||||
|
|
||||||
@@ -226,6 +236,22 @@ struct Div{
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct MultComplex{
|
struct MultComplex{
|
||||||
|
#ifdef GRID_ARM_FEATURE_COMPLEX
|
||||||
|
// fcmla #0 : r += (ar,ar)*(br,bi) = ( ar*br, ar*bi)
|
||||||
|
// fcmla #90 : r += (-ai,ai)*(bi,br) = (-ai*bi, ai*br)
|
||||||
|
// Complex float: two products, two instructions
|
||||||
|
inline float32x4_t operator()(float32x4_t a, float32x4_t b){
|
||||||
|
float32x4_t r = vdupq_n_f32(0.0f);
|
||||||
|
r = vcmlaq_f32(r, a, b);
|
||||||
|
return vcmlaq_rot90_f32(r, a, b);
|
||||||
|
}
|
||||||
|
// Complex double
|
||||||
|
inline float64x2_t operator()(float64x2_t a, float64x2_t b){
|
||||||
|
float64x2_t r = vdupq_n_f64(0.0);
|
||||||
|
r = vcmlaq_f64(r, a, b);
|
||||||
|
return vcmlaq_rot90_f64(r, a, b);
|
||||||
|
}
|
||||||
|
#else
|
||||||
// Complex float
|
// Complex float
|
||||||
inline float32x4_t operator()(float32x4_t a, float32x4_t b){
|
inline float32x4_t operator()(float32x4_t a, float32x4_t b){
|
||||||
|
|
||||||
@@ -273,8 +299,23 @@ struct MultComplex{
|
|||||||
// r5 = vmulq_f64(r0, a);
|
// r5 = vmulq_f64(r0, a);
|
||||||
// return vaddq_f64(r4, r5);
|
// return vaddq_f64(r4, r5);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef GRID_ARM_FEATURE_COMPLEX
|
||||||
|
struct MultAddComplex{
|
||||||
|
// Complex a*b+c
|
||||||
|
inline float32x4_t operator()(float32x4_t a, float32x4_t b, float32x4_t c){
|
||||||
|
c = vcmlaq_f32(c, a, b);
|
||||||
|
return vcmlaq_rot90_f32(c, a, b);
|
||||||
|
}
|
||||||
|
inline float64x2_t operator()(float64x2_t a, float64x2_t b, float64x2_t c){
|
||||||
|
c = vcmlaq_f64(c, a, b);
|
||||||
|
return vcmlaq_rot90_f64(c, a, b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
struct Mult{
|
struct Mult{
|
||||||
// Real float
|
// Real float
|
||||||
inline float32x4_t mac(float32x4_t a, float32x4_t b, float32x4_t c){
|
inline float32x4_t mac(float32x4_t a, float32x4_t b, float32x4_t c){
|
||||||
@@ -285,6 +326,13 @@ struct Mult{
|
|||||||
//return vaddq_f64(vmulq_f64(b,c),a);
|
//return vaddq_f64(vmulq_f64(b,c),a);
|
||||||
return vfmaq_f64(a, b, c);
|
return vfmaq_f64(a, b, c);
|
||||||
}
|
}
|
||||||
|
// Real a*b+c
|
||||||
|
inline float32x4_t operator()(float32x4_t a, float32x4_t b, float32x4_t c){
|
||||||
|
return vfmaq_f32(c, a, b);
|
||||||
|
}
|
||||||
|
inline float64x2_t operator()(float64x2_t a, float64x2_t b, float64x2_t c){
|
||||||
|
return vfmaq_f64(c, a, b);
|
||||||
|
}
|
||||||
inline float32x4_t operator()(float32x4_t a, float32x4_t b){
|
inline float32x4_t operator()(float32x4_t a, float32x4_t b){
|
||||||
return vmulq_f32(a,b);
|
return vmulq_f32(a,b);
|
||||||
}
|
}
|
||||||
@@ -585,6 +633,11 @@ typedef Optimization::Sub SubSIMD;
|
|||||||
typedef Optimization::Div DivSIMD;
|
typedef Optimization::Div DivSIMD;
|
||||||
typedef Optimization::Mult MultSIMD;
|
typedef Optimization::Mult MultSIMD;
|
||||||
typedef Optimization::MultComplex MultComplexSIMD;
|
typedef Optimization::MultComplex MultComplexSIMD;
|
||||||
|
#ifdef GRID_ARM_FEATURE_COMPLEX
|
||||||
|
typedef Optimization::MultAddComplex MultAddComplexSIMD;
|
||||||
|
// Asserts MultAddComplexSIMD and a three argument MultSIMD operator()
|
||||||
|
#define GRID_ARCH_HAS_COMPLEX_MULT_ADD 1
|
||||||
|
#endif
|
||||||
typedef Optimization::MultRealPart MultRealPartSIMD;
|
typedef Optimization::MultRealPart MultRealPartSIMD;
|
||||||
typedef Optimization::MaddRealPart MaddRealPartSIMD;
|
typedef Optimization::MaddRealPart MaddRealPartSIMD;
|
||||||
typedef Optimization::Conj ConjSIMD;
|
typedef Optimization::Conj ConjSIMD;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user