mirror of
https://github.com/paboyle/Grid.git
synced 2026-10-30 17:23:19 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bfd641b22 | ||
|
|
be40aaf751 | ||
|
|
e069fd5ed8 |
No files matched your search
@@ -1,54 +0,0 @@
|
|||||||
name: Bug report
|
|
||||||
description: Report a bug.
|
|
||||||
title: "<insert title>"
|
|
||||||
labels: [bug]
|
|
||||||
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: >
|
|
||||||
Thank you for taking the time to file a bug report.
|
|
||||||
Please check that the code is pointing to the HEAD of develop
|
|
||||||
or any commit in master which is tagged with a version number.
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: "Describe the issue:"
|
|
||||||
description: >
|
|
||||||
Describe the issue and any previous attempt to solve it.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: "Code example:"
|
|
||||||
description: >
|
|
||||||
If relevant, show how to reproduce the issue using a minimal working
|
|
||||||
example.
|
|
||||||
placeholder: |
|
|
||||||
<< your code here >>
|
|
||||||
render: shell
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: "Target platform:"
|
|
||||||
description: >
|
|
||||||
Give a description of the target platform (CPU, network, compiler).
|
|
||||||
Please give the full CPU part description, using for example
|
|
||||||
`cat /proc/cpuinfo | grep 'model name' | uniq` (Linux)
|
|
||||||
or `sysctl machdep.cpu.brand_string` (macOS) and the full output
|
|
||||||
the `--version` option of your compiler.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: "Configure options:"
|
|
||||||
description: >
|
|
||||||
Please give the exact configure command used and attach
|
|
||||||
`config.log`, `grid.config.summary` and the output of `make V=1`.
|
|
||||||
render: shell
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
# Doxygen stuff
|
|
||||||
html/*
|
|
||||||
latex/*
|
|
||||||
|
|
||||||
# Compiled Object files #
|
# Compiled Object files #
|
||||||
#########################
|
#########################
|
||||||
*.slo
|
*.slo
|
||||||
@@ -92,7 +88,6 @@ Thumbs.db
|
|||||||
# build directory #
|
# build directory #
|
||||||
###################
|
###################
|
||||||
build*/*
|
build*/*
|
||||||
Documentation/_build
|
|
||||||
|
|
||||||
# IDE related files #
|
# IDE related files #
|
||||||
#####################
|
#####################
|
||||||
@@ -119,4 +114,3 @@ gh-pages/
|
|||||||
#####################
|
#####################
|
||||||
Grid/qcd/spin/gamma-gen/*.h
|
Grid/qcd/spin/gamma-gen/*.h
|
||||||
Grid/qcd/spin/gamma-gen/*.cc
|
Grid/qcd/spin/gamma-gen/*.cc
|
||||||
Grid/util/Version.h
|
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
language: cpp
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- clang
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: osx
|
||||||
|
osx_image: xcode8.3
|
||||||
|
compiler: clang
|
||||||
|
env: PREC=single
|
||||||
|
- os: osx
|
||||||
|
osx_image: xcode8.3
|
||||||
|
compiler: clang
|
||||||
|
env: PREC=double
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- export GRIDDIR=`pwd`
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$CC" == "clang" ]] && [ ! -e clang/bin ]; then wget $CLANG_LINK; tar -xf `basename $CLANG_LINK`; mkdir clang; mv clang+*/* clang/; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$CC" == "clang" ]]; then export PATH="${GRIDDIR}/clang/bin:${PATH}"; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$CC" == "clang" ]]; then export LD_LIBRARY_PATH="${GRIDDIR}/clang/lib:${LD_LIBRARY_PATH}"; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install libmpc openssl; fi
|
||||||
|
|
||||||
|
install:
|
||||||
|
- export CWD=`pwd`
|
||||||
|
- echo $CWD
|
||||||
|
- export CC=$CC$VERSION
|
||||||
|
- export CXX=$CXX$VERSION
|
||||||
|
- echo $PATH
|
||||||
|
- which autoconf
|
||||||
|
- autoconf --version
|
||||||
|
- which automake
|
||||||
|
- automake --version
|
||||||
|
- which $CC
|
||||||
|
- $CC --version
|
||||||
|
- which $CXX
|
||||||
|
- $CXX --version
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export LDFLAGS='-L/usr/local/lib'; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export EXTRACONF='--with-openssl=/usr/local/opt/openssl'; fi
|
||||||
|
|
||||||
|
script:
|
||||||
|
- ./bootstrap.sh
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- mkdir lime
|
||||||
|
- cd lime
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- wget http://usqcd-software.github.io/downloads/c-lime/lime-1.3.2.tar.gz
|
||||||
|
- tar xf lime-1.3.2.tar.gz
|
||||||
|
- cd lime-1.3.2
|
||||||
|
- ./configure --prefix=$CWD/build/lime/install
|
||||||
|
- make -j4
|
||||||
|
- make install
|
||||||
|
- cd $CWD/build
|
||||||
|
- ../configure --enable-precision=$PREC --enable-simd=SSE4 --enable-comms=none --with-lime=$CWD/build/lime/install ${EXTRACONF}
|
||||||
|
- make -j4
|
||||||
|
- ./benchmarks/Benchmark_dwf --threads 1 --debug-signals
|
||||||
|
- make check
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -1,2 +0,0 @@
|
|||||||
|
|
||||||
mpicxx -qmkl=parallel -fsycl BatchBlasBench.cc -o BatchBlasBench -DGRID_SYCL
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
CXX=hipcc
|
|
||||||
MPICXX=mpicxx
|
|
||||||
CXXFLAGS="-fPIC -I{$ROCM_PATH}/include/ -I${MPICH_DIR}/include -L/lib64 -I/opt/cray/pe/mpich/8.1.28/ofi/gnu/12.3/include -DGRID_HIP"
|
|
||||||
LDFLAGS="-L/lib64 -L${MPICH_DIR}/lib -lmpi -L${CRAY_MPICH_ROOTDIR}/gtl/lib -lmpi_gtl_hsa -lamdhip64 -lhipblas -lrocblas -lmpi_gnu_123"
|
|
||||||
hipcc $CXXFLAGS $LDFLAGS BatchBlasBench.cc -o BatchBlasBench
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
|
|
||||||
mpicxx -qmkl=parallel -fsycl BatchBlasBench.cc -o BatchBlasBench -DGRID_SYCL
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
# 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.
|
|
||||||
- **Grids are borrowed, never owned.** `conformable` is pointer identity, so every object that interoperates must hold the *same* `GridCartesian *`; a class that minted its own grid internally could never conform with anything else. Ownership is therefore not available, and lifetime is managed by scope discipline instead of reference counting: whoever creates a grid retains it beyond every object it handed a reference to. Anything *derived* from a grid inherits this — `~PaddedCell` dereferences its `unpadded_grid`, so a `PaddedCell` cannot even be **destroyed** after its parent grid, only used. Where a consumer must let go early, it offers an explicit hand-back (`MultiGeneralCoarsenedOperatorV2::ReleaseGrid()`) to be called *before* the grid is destroyed.
|
|
||||||
|
|
||||||
- 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`).
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
Version : 0.8.0
|
|
||||||
|
|
||||||
- Clang 3.5 and above, ICPC v16 and above, GCC 6.3 and above recommended
|
|
||||||
- MPI and MPI3 comms optimisations for KNL and OPA finished
|
|
||||||
- Half precision comms
|
|
||||||
@@ -30,46 +30,8 @@ directory
|
|||||||
#ifndef DISABLE_WARNINGS_H
|
#ifndef DISABLE_WARNINGS_H
|
||||||
#define DISABLE_WARNINGS_H
|
#define DISABLE_WARNINGS_H
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if defined __GNUC__ && __GNUC__>=6
|
|
||||||
#pragma GCC diagnostic ignored "-Wignored-attributes"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//disables and intel compiler specific warning (in json.hpp)
|
//disables and intel compiler specific warning (in json.hpp)
|
||||||
#ifdef __ICC
|
|
||||||
#pragma warning disable 488
|
#pragma warning disable 488
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __NVCC__
|
|
||||||
//disables nvcc specific warning in json.hpp
|
|
||||||
#pragma clang diagnostic ignored "-Wdeprecated-register"
|
|
||||||
|
|
||||||
#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__
|
|
||||||
//disables nvcc specific warning in json.hpp
|
|
||||||
#pragma nv_diag_suppress unsigned_compare_with_zero
|
|
||||||
#pragma nv_diag_suppress cast_to_qualified_type
|
|
||||||
//disables nvcc specific warning in many files
|
|
||||||
#pragma nv_diag_suppress esa_on_defaulted_function_ignored
|
|
||||||
#pragma nv_diag_suppress declared_but_not_referenced
|
|
||||||
#pragma nv_diag_suppress extra_semicolon
|
|
||||||
#else
|
|
||||||
//disables nvcc specific warning in json.hpp
|
|
||||||
#pragma diag_suppress unsigned_compare_with_zero
|
|
||||||
#pragma diag_suppress cast_to_qualified_type
|
|
||||||
#pragma diag_suppress declared_but_not_referenced
|
|
||||||
//disables nvcc specific warning in many files
|
|
||||||
#pragma diag_suppress esa_on_defaulted_function_ignored
|
|
||||||
#pragma diag_suppress extra_semicolon
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Disable vectorisation in Eigen on the Power8/9 and PowerPC
|
|
||||||
#ifdef __ALTIVEC__
|
|
||||||
#define EIGEN_DONT_VECTORIZE
|
|
||||||
#endif
|
|
||||||
#ifdef __VSX__
|
|
||||||
#define EIGEN_DONT_VECTORIZE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -42,7 +42,6 @@ Author: paboyle <paboyle@ph.ed.ac.uk>
|
|||||||
#include <Grid/GridQCDcore.h>
|
#include <Grid/GridQCDcore.h>
|
||||||
#include <Grid/qcd/action/Action.h>
|
#include <Grid/qcd/action/Action.h>
|
||||||
#include <Grid/qcd/utils/GaugeFix.h>
|
#include <Grid/qcd/utils/GaugeFix.h>
|
||||||
#include <Grid/qcd/utils/CovariantSmearing.h>
|
|
||||||
#include <Grid/qcd/smearing/Smearing.h>
|
#include <Grid/qcd/smearing/Smearing.h>
|
||||||
#include <Grid/parallelIO/MetaData.h>
|
#include <Grid/parallelIO/MetaData.h>
|
||||||
#include <Grid/qcd/hmc/HMC_aggregate.h>
|
#include <Grid/qcd/hmc/HMC_aggregate.h>
|
||||||
|
|||||||
+5
-12
@@ -38,31 +38,24 @@ Author: paboyle <paboyle@ph.ed.ac.uk>
|
|||||||
#ifndef GRID_BASE_H
|
#ifndef GRID_BASE_H
|
||||||
#define GRID_BASE_H
|
#define GRID_BASE_H
|
||||||
|
|
||||||
|
|
||||||
#include <Grid/DisableWarnings.h>
|
|
||||||
#include <Grid/Namespace.h>
|
|
||||||
#include <Grid/GridStd.h>
|
#include <Grid/GridStd.h>
|
||||||
#include <Grid/threads/Pragmas.h>
|
|
||||||
#include <Grid/perfmon/Timer.h>
|
#include <Grid/perfmon/Timer.h>
|
||||||
//#include <Grid/perfmon/PerfCount.h>
|
#include <Grid/perfmon/PerfCount.h>
|
||||||
#include <Grid/util/Util.h>
|
|
||||||
#include <Grid/log/Log.h>
|
#include <Grid/log/Log.h>
|
||||||
#include <Grid/perfmon/Tracing.h>
|
#include <Grid/allocator/AlignedAllocator.h>
|
||||||
#include <Grid/allocator/Allocator.h>
|
|
||||||
#include <Grid/simd/Simd.h>
|
#include <Grid/simd/Simd.h>
|
||||||
#include <Grid/threads/ThreadReduction.h>
|
|
||||||
#include <Grid/serialisation/Serialisation.h>
|
#include <Grid/serialisation/Serialisation.h>
|
||||||
|
#include <Grid/threads/Threads.h>
|
||||||
|
#include <Grid/util/Util.h>
|
||||||
#include <Grid/util/Sha.h>
|
#include <Grid/util/Sha.h>
|
||||||
#include <Grid/communicator/Communicator.h>
|
#include <Grid/communicator/Communicator.h>
|
||||||
#include <Grid/communicator/RingAllReduce.h>
|
|
||||||
#include <Grid/cartesian/Cartesian.h>
|
#include <Grid/cartesian/Cartesian.h>
|
||||||
#include <Grid/tensors/Tensors.h>
|
#include <Grid/tensors/Tensors.h>
|
||||||
#include <Grid/lattice/Lattice.h>
|
#include <Grid/lattice/Lattice.h>
|
||||||
#include <Grid/cshift/Cshift.h>
|
#include <Grid/cshift/Cshift.h>
|
||||||
#include <Grid/stencil/Stencil.h>
|
#include <Grid/stencil/Stencil.h>
|
||||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
|
||||||
#include <Grid/parallelIO/BinaryIO.h>
|
#include <Grid/parallelIO/BinaryIO.h>
|
||||||
#include <Grid/algorithms/Algorithms.h>
|
#include <Grid/algorithms/Algorithms.h>
|
||||||
NAMESPACE_CHECK(GridCore)
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -36,9 +36,7 @@ Author: paboyle <paboyle@ph.ed.ac.uk>
|
|||||||
#include <Grid/GridCore.h>
|
#include <Grid/GridCore.h>
|
||||||
#include <Grid/qcd/QCD.h>
|
#include <Grid/qcd/QCD.h>
|
||||||
#include <Grid/qcd/spin/Spin.h>
|
#include <Grid/qcd/spin/Spin.h>
|
||||||
#include <Grid/qcd/gparity/Gparity.h>
|
|
||||||
#include <Grid/qcd/utils/Utils.h>
|
#include <Grid/qcd/utils/Utils.h>
|
||||||
#include <Grid/qcd/representations/Representations.h>
|
#include <Grid/qcd/representations/Representations.h>
|
||||||
NAMESPACE_CHECK(GridQCDCore);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
+4
-45
@@ -1,70 +1,29 @@
|
|||||||
#ifndef GRID_STD_H
|
#ifndef GRID_STD_H
|
||||||
#define GRID_STD_H
|
#define GRID_STD_H
|
||||||
|
|
||||||
///////////////////
|
|
||||||
// Grid config
|
|
||||||
///////////////////
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
///////////////////
|
///////////////////
|
||||||
// Std C++ dependencies
|
// Std C++ dependencies
|
||||||
///////////////////
|
///////////////////
|
||||||
#define _NBACKTRACE (256)
|
|
||||||
extern void * Grid_backtrace_buffer[_NBACKTRACE];
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <complex>
|
#include <complex>
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <array>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#ifdef HAVE_EXECINFO_H
|
|
||||||
#include <execinfo.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void GridAbort(void);
|
///////////////////
|
||||||
|
// Grid config
|
||||||
|
///////////////////
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
#define ASSLOG(A) ::write(STDERR_FILENO,A,::strlen(A));
|
|
||||||
#ifdef HAVE_EXECINFO_H
|
|
||||||
#define GRID_ASSERT(b) if(!(b)) { \
|
|
||||||
fflush(stdout); \
|
|
||||||
ASSLOG(" GRID_ASSERT failure: "); \
|
|
||||||
ASSLOG(__FILE__); \
|
|
||||||
ASSLOG(" : "); \
|
|
||||||
ASSLOG(#b); \
|
|
||||||
ASSLOG(" : "); \
|
|
||||||
int symbols = backtrace(Grid_backtrace_buffer,_NBACKTRACE); \
|
|
||||||
backtrace_symbols_fd(Grid_backtrace_buffer,symbols,STDERR_FILENO); \
|
|
||||||
GridAbort(); \
|
|
||||||
};
|
|
||||||
#else
|
|
||||||
#define GRID_ASSERT(b) if(!(b)) { \
|
|
||||||
ASSLOG(" GRID_ASSERT failure: "); \
|
|
||||||
ASSLOG(__FILE__); \
|
|
||||||
ASSLOG(" : "); \
|
|
||||||
ASSLOG(#b); \
|
|
||||||
ASSLOG(" : "); \
|
|
||||||
GridAbort(); \
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef TOFU
|
|
||||||
#undef GRID_COMMS_THREADS
|
|
||||||
#endif
|
|
||||||
#endif /* GRID_STD_H */
|
#endif /* GRID_STD_H */
|
||||||
@@ -1,75 +1,14 @@
|
|||||||
#include <Grid/GridCore.h>
|
|
||||||
#pragma once
|
#pragma once
|
||||||
// Force Eigen to use MKL if Grid has been configured with --enable-mkl
|
// Force Eigen to use MKL if Grid has been configured with --enable-mkl
|
||||||
#ifdef USE_MKL
|
#ifdef USE_MKL
|
||||||
#define EIGEN_USE_MKL_ALL
|
#define EIGEN_USE_MKL_ALL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if defined __GNUC__
|
#if defined __GNUC__
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* NVCC save and restore compile environment*/
|
|
||||||
#ifdef __NVCC__
|
|
||||||
#pragma push
|
|
||||||
#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__
|
|
||||||
#pragma nv_diag_suppress code_is_unreachable
|
|
||||||
#else
|
|
||||||
#pragma diag_suppress code_is_unreachable
|
|
||||||
#endif
|
|
||||||
#pragma push_macro("__CUDA_ARCH__")
|
|
||||||
#pragma push_macro("__NVCC__")
|
|
||||||
#pragma push_macro("__CUDACC__")
|
|
||||||
#undef __CUDA_ARCH__
|
|
||||||
#undef __NVCC__
|
|
||||||
#undef __CUDACC__
|
|
||||||
#define __NVCC__REDEFINE__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* SYCL save and restore compile environment*/
|
|
||||||
#ifdef GRID_SYCL
|
|
||||||
#pragma push
|
|
||||||
#pragma push_macro("__SYCL_DEVICE_ONLY__")
|
|
||||||
#undef __SYCL_DEVICE_ONLY__
|
|
||||||
#define EIGEN_DONT_VECTORIZE
|
|
||||||
#undef EIGEN_USE_SYCL
|
|
||||||
#define __SYCL__REDEFINE__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* HIP save and restore compile environment*/
|
|
||||||
#ifdef GRID_HIP
|
|
||||||
#pragma push
|
|
||||||
#pragma push_macro("__HIP_DEVICE_COMPILE__")
|
|
||||||
#endif
|
|
||||||
#define EIGEN_NO_HIP
|
|
||||||
|
|
||||||
#include <Grid/Eigen/Dense>
|
#include <Grid/Eigen/Dense>
|
||||||
#include <Grid/Eigen/unsupported/CXX11/Tensor>
|
|
||||||
|
|
||||||
/* NVCC restore */
|
|
||||||
#ifdef __NVCC__REDEFINE__
|
|
||||||
#pragma pop_macro("__CUDACC__")
|
|
||||||
#pragma pop_macro("__NVCC__")
|
|
||||||
#pragma pop_macro("__CUDA_ARCH__")
|
|
||||||
#pragma pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*SYCL restore*/
|
|
||||||
#ifdef __SYCL__REDEFINE__
|
|
||||||
#pragma pop_macro("__SYCL_DEVICE_ONLY__")
|
|
||||||
#pragma pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*HIP restore*/
|
|
||||||
#ifdef __HIP__REDEFINE__
|
|
||||||
#pragma pop_macro("__HIP_DEVICE_COMPILE__")
|
|
||||||
#pragma pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined __GNUC__
|
#if defined __GNUC__
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#include <Grid/Grid_Eigen_Dense.h>
|
|
||||||
+2
-24
@@ -21,8 +21,7 @@ if BUILD_HDF5
|
|||||||
extra_headers+=serialisation/Hdf5Type.h
|
extra_headers+=serialisation/Hdf5Type.h
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
all: version-cache
|
||||||
all: version-cache Version.h
|
|
||||||
|
|
||||||
version-cache:
|
version-cache:
|
||||||
@if [ `git status --porcelain | grep -v '??' | wc -l` -gt 0 ]; then\
|
@if [ `git status --porcelain | grep -v '??' | wc -l` -gt 0 ]; then\
|
||||||
@@ -43,7 +42,7 @@ version-cache:
|
|||||||
fi;\
|
fi;\
|
||||||
rm -f vertmp
|
rm -f vertmp
|
||||||
|
|
||||||
Version.h: version-cache
|
Version.h:
|
||||||
cp version-cache Version.h
|
cp version-cache Version.h
|
||||||
|
|
||||||
.PHONY: version-cache
|
.PHONY: version-cache
|
||||||
@@ -54,27 +53,6 @@ Version.h: version-cache
|
|||||||
include Make.inc
|
include Make.inc
|
||||||
include Eigen.inc
|
include Eigen.inc
|
||||||
|
|
||||||
if BUILD_FERMION_INSTANTIATIONS
|
|
||||||
extra_sources+=$(WILS_FERMION_FILES)
|
|
||||||
extra_sources+=$(STAG_FERMION_FILES)
|
|
||||||
if BUILD_ZMOBIUS
|
|
||||||
extra_sources+=$(ZWILS_FERMION_FILES)
|
|
||||||
endif
|
|
||||||
if BUILD_GPARITY
|
|
||||||
extra_sources+=$(GP_FERMION_FILES)
|
|
||||||
endif
|
|
||||||
if BUILD_FERMION_REPS
|
|
||||||
extra_sources+=$(ADJ_FERMION_FILES)
|
|
||||||
extra_sources+=$(TWOIND_FERMION_FILES)
|
|
||||||
endif
|
|
||||||
if BUILD_SP
|
|
||||||
extra_sources+=$(SP_FERMION_FILES)
|
|
||||||
if BUILD_FERMION_REPS
|
|
||||||
extra_sources+=$(SP_TWOIND_FERMION_FILES)
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
lib_LIBRARIES = libGrid.a
|
lib_LIBRARIES = libGrid.a
|
||||||
|
|
||||||
CCFILES += $(extra_sources)
|
CCFILES += $(extra_sources)
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/Namespace.h
|
|
||||||
|
|
||||||
Copyright (C) 2016
|
|
||||||
|
|
||||||
Author: Peter Boyle <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 */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
#include <exception>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#define NAMESPACE_BEGIN(A) namespace A {
|
|
||||||
#define NAMESPACE_END(A) }
|
|
||||||
#define GRID_NAMESPACE_BEGIN NAMESPACE_BEGIN(Grid)
|
|
||||||
#define GRID_NAMESPACE_END NAMESPACE_END(Grid)
|
|
||||||
#define NAMESPACE_CHECK(x) struct namespaceTEST##x {}; static_assert(std::is_same<namespaceTEST##x, ::namespaceTEST##x>::value,"Not in :: at" );
|
|
||||||
|
|
||||||
#define EXCEPTION_CHECK_BEGIN(A) try {
|
|
||||||
#define EXCEPTION_CHECK_END(A) } catch ( std::exception e ) { BACKTRACEFP(stderr); std::cerr << __PRETTY_FUNCTION__ << " : " <<__LINE__<< " Caught exception "<<e.what()<<std::endl; throw; }
|
|
||||||
|
|
||||||
@@ -29,58 +29,34 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
#ifndef GRID_ALGORITHMS_H
|
#ifndef GRID_ALGORITHMS_H
|
||||||
#define GRID_ALGORITHMS_H
|
#define GRID_ALGORITHMS_H
|
||||||
|
|
||||||
NAMESPACE_CHECK(blas);
|
|
||||||
#include <Grid/algorithms/blas/BatchedBlas.h>
|
|
||||||
|
|
||||||
NAMESPACE_CHECK(algorithms);
|
|
||||||
#include <Grid/algorithms/SparseMatrix.h>
|
#include <Grid/algorithms/SparseMatrix.h>
|
||||||
#include <Grid/algorithms/LinearOperator.h>
|
#include <Grid/algorithms/LinearOperator.h>
|
||||||
#include <Grid/algorithms/Preconditioner.h>
|
#include <Grid/algorithms/Preconditioner.h>
|
||||||
NAMESPACE_CHECK(SparseMatrix);
|
|
||||||
|
|
||||||
#include <Grid/algorithms/approx/Zolotarev.h>
|
#include <Grid/algorithms/approx/Zolotarev.h>
|
||||||
#include <Grid/algorithms/approx/Chebyshev.h>
|
#include <Grid/algorithms/approx/Chebyshev.h>
|
||||||
#include <Grid/algorithms/approx/JacobiPolynomial.h>
|
|
||||||
#include <Grid/algorithms/approx/Remez.h>
|
#include <Grid/algorithms/approx/Remez.h>
|
||||||
#include <Grid/algorithms/approx/MultiShiftFunction.h>
|
#include <Grid/algorithms/approx/MultiShiftFunction.h>
|
||||||
#include <Grid/algorithms/approx/Forecast.h>
|
#include <Grid/algorithms/approx/Forecast.h>
|
||||||
#include <Grid/algorithms/approx/RemezGeneral.h>
|
|
||||||
#include <Grid/algorithms/approx/ZMobius.h>
|
#include <Grid/algorithms/iterative/Deflation.h>
|
||||||
NAMESPACE_CHECK(approx);
|
|
||||||
#include <Grid/algorithms/deflation/Deflation.h>
|
|
||||||
#include <Grid/algorithms/deflation/MultiRHSBlockProject.h>
|
|
||||||
#include <Grid/algorithms/deflation/MultiRHSDeflation.h>
|
|
||||||
#include <Grid/algorithms/deflation/MultiRHSBlockCGLinalg.h>
|
|
||||||
// Not really deflation, but useful
|
|
||||||
#include <Grid/algorithms/blas/MomentumProject.h>
|
|
||||||
NAMESPACE_CHECK(deflation);
|
|
||||||
#include <Grid/algorithms/iterative/ConjugateGradient.h>
|
#include <Grid/algorithms/iterative/ConjugateGradient.h>
|
||||||
NAMESPACE_CHECK(ConjGrad);
|
|
||||||
#include <Grid/algorithms/iterative/BiCGSTAB.h>
|
|
||||||
NAMESPACE_CHECK(BiCGSTAB);
|
|
||||||
#include <Grid/algorithms/iterative/ConjugateResidual.h>
|
#include <Grid/algorithms/iterative/ConjugateResidual.h>
|
||||||
#include <Grid/algorithms/iterative/NormalEquations.h>
|
#include <Grid/algorithms/iterative/NormalEquations.h>
|
||||||
#include <Grid/algorithms/iterative/SchurRedBlack.h>
|
#include <Grid/algorithms/iterative/SchurRedBlack.h>
|
||||||
#include <Grid/algorithms/iterative/ConjugateGradientMultiShift.h>
|
#include <Grid/algorithms/iterative/ConjugateGradientMultiShift.h>
|
||||||
#include <Grid/algorithms/iterative/ConjugateGradientMixedPrec.h>
|
#include <Grid/algorithms/iterative/ConjugateGradientMixedPrec.h>
|
||||||
#include <Grid/algorithms/iterative/ConjugateGradientMultiShiftMixedPrec.h>
|
|
||||||
#include <Grid/algorithms/iterative/ConjugateGradientMixedPrecBatched.h>
|
|
||||||
#include <Grid/algorithms/iterative/BiCGSTABMixedPrec.h>
|
|
||||||
#include <Grid/algorithms/iterative/BlockConjugateGradient.h>
|
#include <Grid/algorithms/iterative/BlockConjugateGradient.h>
|
||||||
#include <Grid/algorithms/iterative/ConjugateGradientReliableUpdate.h>
|
#include <Grid/algorithms/iterative/ConjugateGradientReliableUpdate.h>
|
||||||
#include <Grid/algorithms/iterative/MinimalResidual.h>
|
|
||||||
#include <Grid/algorithms/iterative/GeneralisedMinimalResidual.h>
|
|
||||||
#include <Grid/algorithms/iterative/CommunicationAvoidingGeneralisedMinimalResidual.h>
|
|
||||||
#include <Grid/algorithms/iterative/FlexibleGeneralisedMinimalResidual.h>
|
|
||||||
#include <Grid/algorithms/iterative/FlexibleCommunicationAvoidingGeneralisedMinimalResidual.h>
|
|
||||||
#include <Grid/algorithms/iterative/MixedPrecisionFlexibleGeneralisedMinimalResidual.h>
|
|
||||||
#include <Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h>
|
#include <Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h>
|
||||||
#include <Grid/algorithms/iterative/PowerMethod.h>
|
#include <Grid/algorithms/CoarsenedMatrix.h>
|
||||||
#include <Grid/algorithms/iterative/AdefGeneric.h>
|
|
||||||
#include <Grid/algorithms/iterative/AdefMrhs.h>
|
|
||||||
NAMESPACE_CHECK(PowerMethod);
|
|
||||||
#include <Grid/algorithms/multigrid/MultiGrid.h>
|
|
||||||
NAMESPACE_CHECK(multigrid);
|
|
||||||
#include <Grid/algorithms/FFT.h>
|
#include <Grid/algorithms/FFT.h>
|
||||||
|
|
||||||
|
|
||||||
|
// EigCg
|
||||||
|
// Pcg
|
||||||
|
// Hdcg
|
||||||
|
// GCR
|
||||||
|
// etc..
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -0,0 +1,480 @@
|
|||||||
|
/*************************************************************************************
|
||||||
|
|
||||||
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
|
Source file: ./lib/algorithms/CoarsenedMatrix.h
|
||||||
|
|
||||||
|
Copyright (C) 2015
|
||||||
|
|
||||||
|
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
||||||
|
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||||
|
Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local>
|
||||||
|
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_ALGORITHM_COARSENED_MATRIX_H
|
||||||
|
#define GRID_ALGORITHM_COARSENED_MATRIX_H
|
||||||
|
|
||||||
|
|
||||||
|
namespace Grid {
|
||||||
|
|
||||||
|
class Geometry {
|
||||||
|
// int dimension;
|
||||||
|
public:
|
||||||
|
int npoint;
|
||||||
|
std::vector<int> directions ;
|
||||||
|
std::vector<int> displacements;
|
||||||
|
|
||||||
|
Geometry(int _d) {
|
||||||
|
|
||||||
|
int base = (_d==5) ? 1:0;
|
||||||
|
|
||||||
|
// make coarse grid stencil for 4d , not 5d
|
||||||
|
if ( _d==5 ) _d=4;
|
||||||
|
|
||||||
|
npoint = 2*_d+1;
|
||||||
|
directions.resize(npoint);
|
||||||
|
displacements.resize(npoint);
|
||||||
|
for(int d=0;d<_d;d++){
|
||||||
|
directions[2*d ] = d+base;
|
||||||
|
directions[2*d+1] = d+base;
|
||||||
|
displacements[2*d ] = +1;
|
||||||
|
displacements[2*d+1] = -1;
|
||||||
|
}
|
||||||
|
directions [2*_d]=0;
|
||||||
|
displacements[2*_d]=0;
|
||||||
|
|
||||||
|
//// report back
|
||||||
|
std::cout<<GridLogMessage<<"directions :";
|
||||||
|
for(int d=0;d<npoint;d++) std::cout<< directions[d]<< " ";
|
||||||
|
std::cout <<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"displacements :";
|
||||||
|
for(int d=0;d<npoint;d++) std::cout<< displacements[d]<< " ";
|
||||||
|
std::cout<<std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Original cleaner code
|
||||||
|
Geometry(int _d) : dimension(_d), npoint(2*_d+1), directions(npoint), displacements(npoint) {
|
||||||
|
for(int d=0;d<dimension;d++){
|
||||||
|
directions[2*d ] = d;
|
||||||
|
directions[2*d+1] = d;
|
||||||
|
displacements[2*d ] = +1;
|
||||||
|
displacements[2*d+1] = -1;
|
||||||
|
}
|
||||||
|
directions [2*dimension]=0;
|
||||||
|
displacements[2*dimension]=0;
|
||||||
|
}
|
||||||
|
std::vector<int> GetDelta(int point) {
|
||||||
|
std::vector<int> delta(dimension,0);
|
||||||
|
delta[directions[point]] = displacements[point];
|
||||||
|
return delta;
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Fobj,class CComplex,int nbasis>
|
||||||
|
class Aggregation {
|
||||||
|
public:
|
||||||
|
typedef iVector<CComplex,nbasis > siteVector;
|
||||||
|
typedef Lattice<siteVector> CoarseVector;
|
||||||
|
typedef Lattice<iMatrix<CComplex,nbasis > > CoarseMatrix;
|
||||||
|
|
||||||
|
typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field
|
||||||
|
typedef Lattice<Fobj > FineField;
|
||||||
|
|
||||||
|
GridBase *CoarseGrid;
|
||||||
|
GridBase *FineGrid;
|
||||||
|
std::vector<Lattice<Fobj> > subspace;
|
||||||
|
int checkerboard;
|
||||||
|
|
||||||
|
Aggregation(GridBase *_CoarseGrid,GridBase *_FineGrid,int _checkerboard) :
|
||||||
|
CoarseGrid(_CoarseGrid),
|
||||||
|
FineGrid(_FineGrid),
|
||||||
|
subspace(nbasis,_FineGrid),
|
||||||
|
checkerboard(_checkerboard)
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
void Orthogonalise(void){
|
||||||
|
CoarseScalar InnerProd(CoarseGrid);
|
||||||
|
std::cout << GridLogMessage <<" Gramm-Schmidt pass 1"<<std::endl;
|
||||||
|
blockOrthogonalise(InnerProd,subspace);
|
||||||
|
std::cout << GridLogMessage <<" Gramm-Schmidt pass 2"<<std::endl;
|
||||||
|
blockOrthogonalise(InnerProd,subspace);
|
||||||
|
// std::cout << GridLogMessage <<" Gramm-Schmidt checking orthogonality"<<std::endl;
|
||||||
|
// CheckOrthogonal();
|
||||||
|
}
|
||||||
|
void CheckOrthogonal(void){
|
||||||
|
CoarseVector iProj(CoarseGrid);
|
||||||
|
CoarseVector eProj(CoarseGrid);
|
||||||
|
for(int i=0;i<nbasis;i++){
|
||||||
|
blockProject(iProj,subspace[i],subspace);
|
||||||
|
eProj=zero;
|
||||||
|
parallel_for(int ss=0;ss<CoarseGrid->oSites();ss++){
|
||||||
|
eProj._odata[ss](i)=CComplex(1.0);
|
||||||
|
}
|
||||||
|
eProj=eProj - iProj;
|
||||||
|
std::cout<<GridLogMessage<<"Orthog check error "<<i<<" " << norm2(eProj)<<std::endl;
|
||||||
|
}
|
||||||
|
std::cout<<GridLogMessage <<"CheckOrthog done"<<std::endl;
|
||||||
|
}
|
||||||
|
void ProjectToSubspace(CoarseVector &CoarseVec,const FineField &FineVec){
|
||||||
|
blockProject(CoarseVec,FineVec,subspace);
|
||||||
|
}
|
||||||
|
void PromoteFromSubspace(const CoarseVector &CoarseVec,FineField &FineVec){
|
||||||
|
FineVec.checkerboard = subspace[0].checkerboard;
|
||||||
|
blockPromote(CoarseVec,FineVec,subspace);
|
||||||
|
}
|
||||||
|
void CreateSubspaceRandom(GridParallelRNG &RNG){
|
||||||
|
for(int i=0;i<nbasis;i++){
|
||||||
|
random(RNG,subspace[i]);
|
||||||
|
std::cout<<GridLogMessage<<" norm subspace["<<i<<"] "<<norm2(subspace[i])<<std::endl;
|
||||||
|
}
|
||||||
|
Orthogonalise();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
virtual void CreateSubspaceLanczos(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,int nn=nbasis)
|
||||||
|
{
|
||||||
|
// Run a Lanczos with sloppy convergence
|
||||||
|
const int Nstop = nn;
|
||||||
|
const int Nk = nn+20;
|
||||||
|
const int Np = nn+20;
|
||||||
|
const int Nm = Nk+Np;
|
||||||
|
const int MaxIt= 10000;
|
||||||
|
RealD resid = 1.0e-3;
|
||||||
|
|
||||||
|
Chebyshev<FineField> Cheb(0.5,64.0,21);
|
||||||
|
ImplicitlyRestartedLanczos<FineField> IRL(hermop,Cheb,Nstop,Nk,Nm,resid,MaxIt);
|
||||||
|
// IRL.lock = 1;
|
||||||
|
|
||||||
|
FineField noise(FineGrid); gaussian(RNG,noise);
|
||||||
|
FineField tmp(FineGrid);
|
||||||
|
std::vector<RealD> eval(Nm);
|
||||||
|
std::vector<FineField> evec(Nm,FineGrid);
|
||||||
|
|
||||||
|
int Nconv;
|
||||||
|
IRL.calc(eval,evec,
|
||||||
|
noise,
|
||||||
|
Nconv);
|
||||||
|
|
||||||
|
// pull back nn vectors
|
||||||
|
for(int b=0;b<nn;b++){
|
||||||
|
|
||||||
|
subspace[b] = evec[b];
|
||||||
|
|
||||||
|
std::cout << GridLogMessage <<"subspace["<<b<<"] = "<<norm2(subspace[b])<<std::endl;
|
||||||
|
|
||||||
|
hermop.Op(subspace[b],tmp);
|
||||||
|
std::cout<<GridLogMessage << "filtered["<<b<<"] <f|MdagM|f> "<<norm2(tmp)<<std::endl;
|
||||||
|
|
||||||
|
noise = tmp - sqrt(eval[b])*subspace[b] ;
|
||||||
|
|
||||||
|
std::cout<<GridLogMessage << " lambda_"<<b<<" = "<< eval[b] <<" ; [ M - Lambda ]_"<<b<<" vec_"<<b<<" = " <<norm2(noise)<<std::endl;
|
||||||
|
|
||||||
|
noise = tmp + eval[b]*subspace[b] ;
|
||||||
|
|
||||||
|
std::cout<<GridLogMessage << " lambda_"<<b<<" = "<< eval[b] <<" ; [ M - Lambda ]_"<<b<<" vec_"<<b<<" = " <<norm2(noise)<<std::endl;
|
||||||
|
|
||||||
|
}
|
||||||
|
Orthogonalise();
|
||||||
|
for(int b=0;b<nn;b++){
|
||||||
|
std::cout << GridLogMessage <<"subspace["<<b<<"] = "<<norm2(subspace[b])<<std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
virtual void CreateSubspace(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,int nn=nbasis) {
|
||||||
|
|
||||||
|
RealD scale;
|
||||||
|
|
||||||
|
ConjugateGradient<FineField> CG(1.0e-2,10000);
|
||||||
|
FineField noise(FineGrid);
|
||||||
|
FineField Mn(FineGrid);
|
||||||
|
|
||||||
|
for(int b=0;b<nn;b++){
|
||||||
|
|
||||||
|
gaussian(RNG,noise);
|
||||||
|
scale = std::pow(norm2(noise),-0.5);
|
||||||
|
noise=noise*scale;
|
||||||
|
|
||||||
|
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "noise ["<<b<<"] <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
||||||
|
|
||||||
|
for(int i=0;i<1;i++){
|
||||||
|
|
||||||
|
CG(hermop,noise,subspace[b]);
|
||||||
|
|
||||||
|
noise = subspace[b];
|
||||||
|
scale = std::pow(norm2(noise),-0.5);
|
||||||
|
noise=noise*scale;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "filtered["<<b<<"] <f|MdagM|f> "<<norm2(Mn)<<std::endl;
|
||||||
|
subspace[b] = noise;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Orthogonalise();
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Fine Object == (per site) type of fine field
|
||||||
|
// nbasis == number of deflation vectors
|
||||||
|
template<class Fobj,class CComplex,int nbasis>
|
||||||
|
class CoarsenedMatrix : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef iVector<CComplex,nbasis > siteVector;
|
||||||
|
typedef Lattice<siteVector> CoarseVector;
|
||||||
|
typedef Lattice<iMatrix<CComplex,nbasis > > CoarseMatrix;
|
||||||
|
|
||||||
|
typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field
|
||||||
|
typedef Lattice<Fobj > FineField;
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
// Data members
|
||||||
|
////////////////////
|
||||||
|
Geometry geom;
|
||||||
|
GridBase * _grid;
|
||||||
|
CartesianStencil<siteVector,siteVector> Stencil;
|
||||||
|
|
||||||
|
std::vector<CoarseMatrix> A;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////
|
||||||
|
// Interface
|
||||||
|
///////////////////////
|
||||||
|
GridBase * Grid(void) { return _grid; }; // this is all the linalg routines need to know
|
||||||
|
|
||||||
|
RealD M (const CoarseVector &in, CoarseVector &out){
|
||||||
|
|
||||||
|
conformable(_grid,in._grid);
|
||||||
|
conformable(in._grid,out._grid);
|
||||||
|
|
||||||
|
SimpleCompressor<siteVector> compressor;
|
||||||
|
Stencil.HaloExchange(in,compressor);
|
||||||
|
|
||||||
|
parallel_for(int ss=0;ss<Grid()->oSites();ss++){
|
||||||
|
siteVector res = zero;
|
||||||
|
siteVector nbr;
|
||||||
|
int ptype;
|
||||||
|
StencilEntry *SE;
|
||||||
|
for(int point=0;point<geom.npoint;point++){
|
||||||
|
|
||||||
|
SE=Stencil.GetEntry(ptype,point,ss);
|
||||||
|
|
||||||
|
if(SE->_is_local&&SE->_permute) {
|
||||||
|
permute(nbr,in._odata[SE->_offset],ptype);
|
||||||
|
} else if(SE->_is_local) {
|
||||||
|
nbr = in._odata[SE->_offset];
|
||||||
|
} else {
|
||||||
|
nbr = Stencil.CommBuf()[SE->_offset];
|
||||||
|
}
|
||||||
|
res = res + A[point]._odata[ss]*nbr;
|
||||||
|
}
|
||||||
|
vstream(out._odata[ss],res);
|
||||||
|
}
|
||||||
|
return norm2(out);
|
||||||
|
};
|
||||||
|
|
||||||
|
RealD Mdag (const CoarseVector &in, CoarseVector &out){
|
||||||
|
return M(in,out);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Defer support for further coarsening for now
|
||||||
|
void Mdiag (const CoarseVector &in, CoarseVector &out){};
|
||||||
|
void Mdir (const CoarseVector &in, CoarseVector &out,int dir, int disp){};
|
||||||
|
|
||||||
|
CoarsenedMatrix(GridCartesian &CoarseGrid) :
|
||||||
|
|
||||||
|
_grid(&CoarseGrid),
|
||||||
|
geom(CoarseGrid._ndimension),
|
||||||
|
Stencil(&CoarseGrid,geom.npoint,Even,geom.directions,geom.displacements),
|
||||||
|
A(geom.npoint,&CoarseGrid)
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
void CoarsenOperator(GridBase *FineGrid,LinearOperatorBase<Lattice<Fobj> > &linop,
|
||||||
|
Aggregation<Fobj,CComplex,nbasis> & Subspace){
|
||||||
|
|
||||||
|
FineField iblock(FineGrid); // contributions from within this block
|
||||||
|
FineField oblock(FineGrid); // contributions from outwith this block
|
||||||
|
|
||||||
|
FineField phi(FineGrid);
|
||||||
|
FineField tmp(FineGrid);
|
||||||
|
FineField zz(FineGrid); zz=zero;
|
||||||
|
FineField Mphi(FineGrid);
|
||||||
|
|
||||||
|
Lattice<iScalar<vInteger> > coor(FineGrid);
|
||||||
|
|
||||||
|
CoarseVector iProj(Grid());
|
||||||
|
CoarseVector oProj(Grid());
|
||||||
|
CoarseScalar InnerProd(Grid());
|
||||||
|
|
||||||
|
// Orthogonalise the subblocks over the basis
|
||||||
|
blockOrthogonalise(InnerProd,Subspace.subspace);
|
||||||
|
|
||||||
|
// Compute the matrix elements of linop between this orthonormal
|
||||||
|
// set of vectors.
|
||||||
|
int self_stencil=-1;
|
||||||
|
for(int p=0;p<geom.npoint;p++){
|
||||||
|
A[p]=zero;
|
||||||
|
if( geom.displacements[p]==0){
|
||||||
|
self_stencil=p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(self_stencil!=-1);
|
||||||
|
|
||||||
|
for(int i=0;i<nbasis;i++){
|
||||||
|
phi=Subspace.subspace[i];
|
||||||
|
|
||||||
|
std::cout<<GridLogMessage<<"("<<i<<").."<<std::endl;
|
||||||
|
|
||||||
|
for(int p=0;p<geom.npoint;p++){
|
||||||
|
|
||||||
|
int dir = geom.directions[p];
|
||||||
|
int disp = geom.displacements[p];
|
||||||
|
|
||||||
|
Integer block=(FineGrid->_rdimensions[dir])/(Grid()->_rdimensions[dir]);
|
||||||
|
|
||||||
|
LatticeCoordinate(coor,dir);
|
||||||
|
|
||||||
|
if ( disp==0 ){
|
||||||
|
linop.OpDiag(phi,Mphi);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
linop.OpDir(phi,Mphi,dir,disp);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// Pick out contributions coming from this cell and neighbour cell
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
if ( disp==0 ) {
|
||||||
|
iblock = Mphi;
|
||||||
|
oblock = zero;
|
||||||
|
} else if ( disp==1 ) {
|
||||||
|
oblock = where(mod(coor,block)==(block-1),Mphi,zz);
|
||||||
|
iblock = where(mod(coor,block)!=(block-1),Mphi,zz);
|
||||||
|
} else if ( disp==-1 ) {
|
||||||
|
oblock = where(mod(coor,block)==(Integer)0,Mphi,zz);
|
||||||
|
iblock = where(mod(coor,block)!=(Integer)0,Mphi,zz);
|
||||||
|
} else {
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Subspace.ProjectToSubspace(iProj,iblock);
|
||||||
|
Subspace.ProjectToSubspace(oProj,oblock);
|
||||||
|
// blockProject(iProj,iblock,Subspace.subspace);
|
||||||
|
// blockProject(oProj,oblock,Subspace.subspace);
|
||||||
|
parallel_for(int ss=0;ss<Grid()->oSites();ss++){
|
||||||
|
for(int j=0;j<nbasis;j++){
|
||||||
|
if( disp!= 0 ) {
|
||||||
|
A[p]._odata[ss](j,i) = oProj._odata[ss](j);
|
||||||
|
}
|
||||||
|
A[self_stencil]._odata[ss](j,i) = A[self_stencil]._odata[ss](j,i) + iProj._odata[ss](j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
///////////////////////////
|
||||||
|
// test code worth preserving in if block
|
||||||
|
///////////////////////////
|
||||||
|
std::cout<<GridLogMessage<< " Computed matrix elements "<< self_stencil <<std::endl;
|
||||||
|
for(int p=0;p<geom.npoint;p++){
|
||||||
|
std::cout<<GridLogMessage<< "A["<<p<<"]" << std::endl;
|
||||||
|
std::cout<<GridLogMessage<< A[p] << std::endl;
|
||||||
|
}
|
||||||
|
std::cout<<GridLogMessage<< " picking by block0 "<< self_stencil <<std::endl;
|
||||||
|
|
||||||
|
phi=Subspace.subspace[0];
|
||||||
|
std::vector<int> bc(FineGrid->_ndimension,0);
|
||||||
|
|
||||||
|
blockPick(Grid(),phi,tmp,bc); // Pick out a block
|
||||||
|
linop.Op(tmp,Mphi); // Apply big dop
|
||||||
|
blockProject(iProj,Mphi,Subspace.subspace); // project it and print it
|
||||||
|
std::cout<<GridLogMessage<< " Computed matrix elements from block zero only "<<std::endl;
|
||||||
|
std::cout<<GridLogMessage<< iProj <<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"Computed Coarse Operator"<<std::endl;
|
||||||
|
#endif
|
||||||
|
// ForceHermitian();
|
||||||
|
AssertHermitian();
|
||||||
|
// ForceDiagonal();
|
||||||
|
}
|
||||||
|
void ForceDiagonal(void) {
|
||||||
|
|
||||||
|
|
||||||
|
std::cout<<GridLogMessage<<"**************************************************"<<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"**** Forcing coarse operator to be diagonal ****"<<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"**************************************************"<<std::endl;
|
||||||
|
for(int p=0;p<8;p++){
|
||||||
|
A[p]=zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
GridParallelRNG RNG(Grid()); RNG.SeedFixedIntegers(std::vector<int>({55,72,19,17,34}));
|
||||||
|
Lattice<iScalar<CComplex> > val(Grid()); random(RNG,val);
|
||||||
|
|
||||||
|
Complex one(1.0);
|
||||||
|
|
||||||
|
iMatrix<CComplex,nbasis> ident; ident=one;
|
||||||
|
|
||||||
|
val = val*adj(val);
|
||||||
|
val = val + 1.0;
|
||||||
|
|
||||||
|
A[8] = val*ident;
|
||||||
|
|
||||||
|
// for(int s=0;s<Grid()->oSites();s++) {
|
||||||
|
// A[8]._odata[s]=val._odata[s];
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
void ForceHermitian(void) {
|
||||||
|
for(int d=0;d<4;d++){
|
||||||
|
int dd=d+1;
|
||||||
|
A[2*d] = adj(Cshift(A[2*d+1],dd,1));
|
||||||
|
}
|
||||||
|
// A[8] = 0.5*(A[8] + adj(A[8]));
|
||||||
|
}
|
||||||
|
void AssertHermitian(void) {
|
||||||
|
CoarseMatrix AA (Grid());
|
||||||
|
CoarseMatrix AAc (Grid());
|
||||||
|
CoarseMatrix Diff (Grid());
|
||||||
|
for(int d=0;d<4;d++){
|
||||||
|
|
||||||
|
int dd=d+1;
|
||||||
|
AAc = Cshift(A[2*d+1],dd,1);
|
||||||
|
AA = A[2*d];
|
||||||
|
|
||||||
|
Diff = AA - adj(AAc);
|
||||||
|
|
||||||
|
std::cout<<GridLogMessage<<"Norm diff dim "<<d<<" "<< norm2(Diff)<<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"Norm dim "<<d<<" "<< norm2(AA)<<std::endl;
|
||||||
|
|
||||||
|
}
|
||||||
|
Diff = A[8] - adj(A[8]);
|
||||||
|
std::cout<<GridLogMessage<<"Norm diff local "<< norm2(Diff)<<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"Norm local "<< norm2(A[8])<<std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
+247
-436
@@ -1,4 +1,5 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
/*************************************************************************************
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
@@ -23,473 +24,283 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#ifndef _GRID_FFT_H_
|
#ifndef _GRID_FFT_H_
|
||||||
#define _GRID_FFT_H_
|
#define _GRID_FFT_H_
|
||||||
|
|
||||||
#ifdef GRID_CUDA
|
|
||||||
#include <cufft.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef GRID_HIP
|
|
||||||
#include <hipfft/hipfft.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(GRID_CUDA) && !defined(GRID_HIP)
|
|
||||||
#ifdef HAVE_FFTW
|
#ifdef HAVE_FFTW
|
||||||
#if defined(USE_MKL) || defined(GRID_SYCL)
|
#ifdef USE_MKL
|
||||||
#include <fftw/fftw3.h>
|
#include <fftw/fftw3.h>
|
||||||
#else
|
#else
|
||||||
#include <fftw3.h>
|
#include <fftw3.h>
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
namespace Grid {
|
||||||
|
|
||||||
|
template<class scalar> struct FFTW { };
|
||||||
|
|
||||||
|
#ifdef HAVE_FFTW
|
||||||
|
template<> struct FFTW<ComplexD> {
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef fftw_complex FFTW_scalar;
|
||||||
|
typedef fftw_plan FFTW_plan;
|
||||||
|
|
||||||
|
static FFTW_plan fftw_plan_many_dft(int rank, const int *n,int howmany,
|
||||||
|
FFTW_scalar *in, const int *inembed,
|
||||||
|
int istride, int idist,
|
||||||
|
FFTW_scalar *out, const int *onembed,
|
||||||
|
int ostride, int odist,
|
||||||
|
int sign, unsigned flags) {
|
||||||
|
return ::fftw_plan_many_dft(rank,n,howmany,in,inembed,istride,idist,out,onembed,ostride,odist,sign,flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fftw_flops(const FFTW_plan p,double *add, double *mul, double *fmas){
|
||||||
|
::fftw_flops(p,add,mul,fmas);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out) {
|
||||||
|
::fftw_execute_dft(p,in,out);
|
||||||
|
}
|
||||||
|
inline static void fftw_destroy_plan(const FFTW_plan p) {
|
||||||
|
::fftw_destroy_plan(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct FFTW<ComplexF> {
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef fftwf_complex FFTW_scalar;
|
||||||
|
typedef fftwf_plan FFTW_plan;
|
||||||
|
|
||||||
|
static FFTW_plan fftw_plan_many_dft(int rank, const int *n,int howmany,
|
||||||
|
FFTW_scalar *in, const int *inembed,
|
||||||
|
int istride, int idist,
|
||||||
|
FFTW_scalar *out, const int *onembed,
|
||||||
|
int ostride, int odist,
|
||||||
|
int sign, unsigned flags) {
|
||||||
|
return ::fftwf_plan_many_dft(rank,n,howmany,in,inembed,istride,idist,out,onembed,ostride,odist,sign,flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fftw_flops(const FFTW_plan p,double *add, double *mul, double *fmas){
|
||||||
|
::fftwf_flops(p,add,mul,fmas);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out) {
|
||||||
|
::fftwf_execute_dft(p,in,out);
|
||||||
|
}
|
||||||
|
inline static void fftw_destroy_plan(const FFTW_plan p) {
|
||||||
|
::fftwf_destroy_plan(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef FFTW_FORWARD
|
#ifndef FFTW_FORWARD
|
||||||
#define FFTW_FORWARD (-1)
|
#define FFTW_FORWARD (-1)
|
||||||
#define FFTW_BACKWARD (+1)
|
#define FFTW_BACKWARD (+1)
|
||||||
#define FFTW_ESTIMATE (0)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<class scalar> struct FFTW {
|
class FFT {
|
||||||
};
|
private:
|
||||||
|
|
||||||
#ifdef GRID_HIP
|
GridCartesian *vgrid;
|
||||||
template<> struct FFTW<ComplexD> {
|
GridCartesian *sgrid;
|
||||||
public:
|
|
||||||
static const int forward=FFTW_FORWARD;
|
|
||||||
static const int backward=FFTW_BACKWARD;
|
|
||||||
typedef hipfftDoubleComplex FFTW_scalar;
|
|
||||||
typedef hipfftHandle FFTW_plan;
|
|
||||||
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
|
||||||
FFTW_scalar *in, int *inembed,
|
|
||||||
int istride, int idist,
|
|
||||||
FFTW_scalar *out, int *onembed,
|
|
||||||
int ostride, int odist,
|
|
||||||
int sign, unsigned flags) {
|
|
||||||
FFTW_plan p;
|
|
||||||
auto rv = hipfftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,HIPFFT_Z2Z,howmany);
|
|
||||||
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
|
||||||
hipfftResult rv;
|
|
||||||
if ( sign == forward ) rv =hipfftExecZ2Z(p,in,out,HIPFFT_FORWARD);
|
|
||||||
else rv =hipfftExecZ2Z(p,in,out,HIPFFT_BACKWARD);
|
|
||||||
accelerator_barrier();
|
|
||||||
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
|
||||||
}
|
|
||||||
inline static void fftw_destroy_plan(const FFTW_plan p) { hipfftDestroy(p); }
|
|
||||||
};
|
|
||||||
template<> struct FFTW<ComplexF> {
|
|
||||||
public:
|
|
||||||
static const int forward=FFTW_FORWARD;
|
|
||||||
static const int backward=FFTW_BACKWARD;
|
|
||||||
typedef hipfftComplex FFTW_scalar;
|
|
||||||
typedef hipfftHandle FFTW_plan;
|
|
||||||
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
|
||||||
FFTW_scalar *in, int *inembed,
|
|
||||||
int istride, int idist,
|
|
||||||
FFTW_scalar *out, int *onembed,
|
|
||||||
int ostride, int odist,
|
|
||||||
int sign, unsigned flags) {
|
|
||||||
FFTW_plan p;
|
|
||||||
auto rv = hipfftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,HIPFFT_C2C,howmany);
|
|
||||||
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
|
|
||||||
hipfftResult rv;
|
|
||||||
if ( sign == forward ) rv =hipfftExecC2C(p,in,out,HIPFFT_FORWARD);
|
|
||||||
else rv =hipfftExecC2C(p,in,out,HIPFFT_BACKWARD);
|
|
||||||
accelerator_barrier();
|
|
||||||
GRID_ASSERT(rv==HIPFFT_SUCCESS);
|
|
||||||
}
|
|
||||||
inline static void fftw_destroy_plan(const FFTW_plan p) { hipfftDestroy(p); }
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef GRID_CUDA
|
int Nd;
|
||||||
template<> struct FFTW<ComplexD> {
|
double flops;
|
||||||
public:
|
double flops_call;
|
||||||
static const int forward=FFTW_FORWARD;
|
uint64_t usec;
|
||||||
static const int backward=FFTW_BACKWARD;
|
|
||||||
typedef cufftDoubleComplex FFTW_scalar;
|
|
||||||
typedef cufftHandle FFTW_plan;
|
|
||||||
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
|
||||||
FFTW_scalar *in, int *inembed,
|
|
||||||
int istride, int idist,
|
|
||||||
FFTW_scalar *out, int *onembed,
|
|
||||||
int ostride, int odist,
|
|
||||||
int sign, unsigned flags) {
|
|
||||||
FFTW_plan p;
|
|
||||||
cufftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,CUFFT_Z2Z,howmany);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
else cufftExecZ2Z(p,in,out,CUFFT_INVERSE);
|
|
||||||
accelerator_barrier();
|
|
||||||
}
|
|
||||||
inline static void fftw_destroy_plan(const FFTW_plan p) { cufftDestroy(p); }
|
|
||||||
};
|
|
||||||
template<> struct FFTW<ComplexF> {
|
|
||||||
public:
|
|
||||||
static const int forward=FFTW_FORWARD;
|
|
||||||
static const int backward=FFTW_BACKWARD;
|
|
||||||
typedef cufftComplex FFTW_scalar;
|
|
||||||
typedef cufftHandle FFTW_plan;
|
|
||||||
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
|
||||||
FFTW_scalar *in, int *inembed,
|
|
||||||
int istride, int idist,
|
|
||||||
FFTW_scalar *out, int *onembed,
|
|
||||||
int ostride, int odist,
|
|
||||||
int sign, unsigned flags) {
|
|
||||||
FFTW_plan p;
|
|
||||||
cufftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,CUFFT_C2C,howmany);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
else cufftExecC2C(p,in,out,CUFFT_INVERSE);
|
|
||||||
accelerator_barrier();
|
|
||||||
}
|
|
||||||
inline static void fftw_destroy_plan(const FFTW_plan p) { cufftDestroy(p); }
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(GRID_CUDA) && !defined(GRID_HIP)
|
std::vector<int> dimensions;
|
||||||
#ifdef HAVE_FFTW
|
std::vector<int> processors;
|
||||||
template<> struct FFTW<ComplexD> {
|
std::vector<int> processor_coor;
|
||||||
public:
|
|
||||||
typedef fftw_complex FFTW_scalar;
|
|
||||||
typedef fftw_plan FFTW_plan;
|
|
||||||
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
|
||||||
FFTW_scalar *in, int *inembed,
|
|
||||||
int istride, int idist,
|
|
||||||
FFTW_scalar *out, int *onembed,
|
|
||||||
int ostride, int odist,
|
|
||||||
int sign, unsigned 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) {
|
|
||||||
::fftw_execute_dft(p,in,out);
|
|
||||||
}
|
|
||||||
inline static void fftw_destroy_plan(const FFTW_plan p) { ::fftw_destroy_plan(p); }
|
|
||||||
};
|
|
||||||
template<> struct FFTW<ComplexF> {
|
|
||||||
public:
|
|
||||||
typedef fftwf_complex FFTW_scalar;
|
|
||||||
typedef fftwf_plan FFTW_plan;
|
|
||||||
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
|
|
||||||
FFTW_scalar *in, int *inembed,
|
|
||||||
int istride, int idist,
|
|
||||||
FFTW_scalar *out, int *onembed,
|
|
||||||
int ostride, int odist,
|
|
||||||
int sign, unsigned 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) {
|
|
||||||
::fftwf_execute_dft(p,in,out);
|
|
||||||
}
|
|
||||||
inline static void fftw_destroy_plan(const FFTW_plan p) { ::fftwf_destroy_plan(p); }
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct FFTbase {
|
public:
|
||||||
double flops;
|
|
||||||
double flops_call;
|
|
||||||
uint64_t usec;
|
|
||||||
GridCartesian *_grid;
|
|
||||||
|
|
||||||
static const int forward = FFTW_FORWARD;
|
static const int forward=FFTW_FORWARD;
|
||||||
static const int backward = FFTW_BACKWARD;
|
static const int backward=FFTW_BACKWARD;
|
||||||
|
|
||||||
double Flops(void) { return flops; }
|
double Flops(void) {return flops;}
|
||||||
double MFlops(void) { return flops / usec; }
|
double MFlops(void) {return flops/usec;}
|
||||||
double USec(void) { return (double)usec; }
|
double USec(void) {return (double)usec;}
|
||||||
|
|
||||||
FFTbase(GridCartesian *grid) : _grid(grid), flops(0), flops_call(0), usec(0) {}
|
FFT ( GridCartesian * grid ) :
|
||||||
};
|
vgrid(grid),
|
||||||
|
Nd(grid->_ndimension),
|
||||||
// Barrel-shift gather, FFT execute, and insert. Called by both FFT and PlannedFFT.
|
dimensions(grid->_fdimensions),
|
||||||
// The caller is responsible for plan acquisition and destruction.
|
processors(grid->_processors),
|
||||||
template<class vobj>
|
processor_coor(grid->_processor_coor)
|
||||||
static void FFT_dim_execute(
|
|
||||||
Lattice<vobj> &result,
|
|
||||||
const Lattice<vobj> &source,
|
|
||||||
int dim, int sign,
|
|
||||||
typename FFTW<typename vobj::scalar_type>::FFTW_plan p,
|
|
||||||
GridCartesian *grid,
|
|
||||||
double &flops, double &flops_call, uint64_t &usec)
|
|
||||||
{
|
|
||||||
typedef typename vobj::scalar_type scalar;
|
|
||||||
typedef typename vobj::scalar_object sobj;
|
|
||||||
typedef typename vobj::scalar_type scalar_type;
|
|
||||||
typedef typename vobj::vector_type vector_type;
|
|
||||||
typedef typename FFTW<scalar>::FFTW_scalar FFTW_scalar;
|
|
||||||
|
|
||||||
const int Ndim = grid->Nd();
|
|
||||||
int L = grid->_ldimensions[dim];
|
|
||||||
int G = grid->_fdimensions[dim];
|
|
||||||
int Ncomp = sizeof(sobj) / sizeof(scalar);
|
|
||||||
int64_t Nlow = 1, 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];
|
|
||||||
int64_t Nperp = Nlow * Nhigh;
|
|
||||||
|
|
||||||
deviceVector<scalar> pgbuf(Nperp * Ncomp * G);
|
|
||||||
scalar *pgbuf_v = &pgbuf[0];
|
|
||||||
int howmany = Ncomp * Nperp;
|
|
||||||
|
|
||||||
scalar div;
|
|
||||||
if (sign == FFTW_BACKWARD) div = 1.0 / G;
|
|
||||||
else if (sign == FFTW_FORWARD) div = 1.0;
|
|
||||||
else GRID_ASSERT(0);
|
|
||||||
|
|
||||||
double t_pencil = 0, t_fft = 0, t_copy = 0, t_shift = 0;
|
|
||||||
double t_total = -usecond();
|
|
||||||
|
|
||||||
result = source;
|
|
||||||
int pc = grid->_processor_coor[dim];
|
|
||||||
|
|
||||||
const Coordinate ldims = grid->_ldimensions;
|
|
||||||
const Coordinate rdims = grid->_rdimensions;
|
|
||||||
const Coordinate sdims = grid->_simd_layout;
|
|
||||||
const Coordinate processors = grid->_processors;
|
|
||||||
|
|
||||||
Coordinate pgdims(Ndim);
|
|
||||||
pgdims[0] = G;
|
|
||||||
for (int d = 0, dd = 1; d < Ndim; d++)
|
|
||||||
if (d != dim) pgdims[dd++] = ldims[d];
|
|
||||||
int64_t pgvol = 1;
|
|
||||||
for (int d = 0; d < Ndim; d++) pgvol *= pgdims[d];
|
|
||||||
|
|
||||||
const int Nsimd = vobj::Nsimd();
|
|
||||||
t_pencil = -usecond();
|
|
||||||
for (int p_idx = 0; p_idx < processors[dim]; p_idx++) {
|
|
||||||
t_copy -= usecond();
|
|
||||||
autoView(r_v, result, AcceleratorRead);
|
|
||||||
accelerator_for(idx, grid->oSites(), vobj::Nsimd(), {
|
|
||||||
#ifdef GRID_SIMT
|
|
||||||
{
|
{
|
||||||
int lane = acceleratorSIMTlane(Nsimd);
|
flops=0;
|
||||||
#else
|
usec =0;
|
||||||
for (int lane = 0; lane < Nsimd; lane++) {
|
std::vector<int> layout(Nd,1);
|
||||||
#endif
|
sgrid = new GridCartesian(dimensions,layout,processors);
|
||||||
Coordinate icoor, ocoor, pgcoor;
|
};
|
||||||
Lexicographic::CoorFromIndex(icoor, lane, sdims);
|
|
||||||
Lexicographic::CoorFromIndex(ocoor, idx, rdims);
|
~FFT ( void) {
|
||||||
pgcoor[0] = ocoor[dim] + icoor[dim]*rdims[dim] + ((pc+p_idx)%processors[dim])*L;
|
delete sgrid;
|
||||||
for (int d = 0, dd = 1; d < Ndim; d++)
|
}
|
||||||
if (d != dim) { pgcoor[dd] = ocoor[d] + icoor[d]*rdims[d]; dd++; }
|
|
||||||
int64_t pgidx;
|
template<class vobj>
|
||||||
Lexicographic::IndexFromCoor(pgcoor, pgidx, pgdims);
|
void FFT_dim_mask(Lattice<vobj> &result,const Lattice<vobj> &source,std::vector<int> mask,int sign){
|
||||||
vector_type *from = (vector_type *)&r_v[idx];
|
|
||||||
scalar_type stmp;
|
conformable(result._grid,vgrid);
|
||||||
for (int w = 0; w < Ncomp; w++) {
|
conformable(source._grid,vgrid);
|
||||||
stmp = getlane(from[w], lane);
|
Lattice<vobj> tmp(vgrid);
|
||||||
pgbuf_v[pgidx + w*pgvol] = stmp;
|
tmp = source;
|
||||||
|
for(int d=0;d<Nd;d++){
|
||||||
|
if( mask[d] ) {
|
||||||
|
FFT_dim(result,tmp,d,sign);
|
||||||
|
tmp=result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#ifdef GRID_SIMT
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class vobj>
|
||||||
|
void FFT_all_dim(Lattice<vobj> &result,const Lattice<vobj> &source,int sign){
|
||||||
|
std::vector<int> mask(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){
|
||||||
|
#ifndef HAVE_FFTW
|
||||||
|
assert(0);
|
||||||
#else
|
#else
|
||||||
}
|
conformable(result._grid,vgrid);
|
||||||
#endif
|
conformable(source._grid,vgrid);
|
||||||
});
|
|
||||||
t_copy += usecond();
|
|
||||||
if (p_idx != processors[dim] - 1) {
|
|
||||||
Lattice<vobj> temp(grid);
|
|
||||||
t_shift -= usecond();
|
|
||||||
temp = Cshift(result, dim, L); result = temp;
|
|
||||||
t_shift += usecond();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t_pencil += usecond();
|
|
||||||
|
|
||||||
FFTW_scalar *in = (FFTW_scalar *)pgbuf_v;
|
int L = vgrid->_ldimensions[dim];
|
||||||
FFTW_scalar *out = (FFTW_scalar *)pgbuf_v;
|
int G = vgrid->_fdimensions[dim];
|
||||||
t_fft = -usecond();
|
|
||||||
FFTW<scalar>::fftw_execute_dft(p, in, out, sign);
|
|
||||||
t_fft += usecond();
|
|
||||||
|
|
||||||
flops_call = 5.0 * howmany * G * log2(G);
|
std::vector<int> layout(Nd,1);
|
||||||
usec = t_fft;
|
std::vector<int> pencil_gd(vgrid->_fdimensions);
|
||||||
flops = flops_call;
|
|
||||||
|
|
||||||
result = Zero();
|
pencil_gd[dim] = G*processors[dim];
|
||||||
double t_insert = -usecond();
|
|
||||||
{
|
// Pencil global vol LxLxGxLxL per node
|
||||||
autoView(r_v, result, AcceleratorWrite);
|
GridCartesian pencil_g(pencil_gd,layout,processors);
|
||||||
accelerator_for(idx, grid->oSites(), Nsimd, {
|
|
||||||
#ifdef GRID_SIMT
|
// Construct pencils
|
||||||
{
|
typedef typename vobj::scalar_object sobj;
|
||||||
int lane = acceleratorSIMTlane(Nsimd);
|
typedef typename sobj::scalar_type scalar;
|
||||||
#else
|
|
||||||
for (int lane = 0; lane < Nsimd; lane++) {
|
Lattice<sobj> pgbuf(&pencil_g);
|
||||||
#endif
|
|
||||||
Coordinate icoor(Ndim), ocoor(Ndim), pgcoor(Ndim);
|
|
||||||
Lexicographic::CoorFromIndex(icoor, lane, sdims);
|
typedef typename FFTW<scalar>::FFTW_scalar FFTW_scalar;
|
||||||
Lexicographic::CoorFromIndex(ocoor, idx, rdims);
|
typedef typename FFTW<scalar>::FFTW_plan FFTW_plan;
|
||||||
pgcoor[0] = ocoor[dim] + icoor[dim]*rdims[dim] + pc*L;
|
|
||||||
for (int d = 0, dd = 1; d < Ndim; d++)
|
int Ncomp = sizeof(sobj)/sizeof(scalar);
|
||||||
if (d != dim) { pgcoor[dd] = ocoor[d] + icoor[d]*rdims[d]; dd++; }
|
int Nlow = 1;
|
||||||
int64_t pgidx;
|
for(int d=0;d<dim;d++){
|
||||||
Lexicographic::IndexFromCoor(pgcoor, pgidx, pgdims);
|
Nlow*=vgrid->_ldimensions[d];
|
||||||
vector_type *to = (vector_type *)&r_v[idx];
|
|
||||||
scalar_type stmp;
|
|
||||||
for (int w = 0; w < Ncomp; w++) {
|
|
||||||
stmp = pgbuf_v[pgidx + w*pgvol];
|
|
||||||
putlane(to[w], stmp, lane);
|
|
||||||
}
|
}
|
||||||
#ifdef GRID_SIMT
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
});
|
|
||||||
}
|
|
||||||
result = result * div;
|
|
||||||
t_insert += usecond();
|
|
||||||
t_total += usecond();
|
|
||||||
|
|
||||||
std::cout << GridLogPerformance << " FFT took " << t_total/1.0e6 << " s" << std::endl;
|
int rank = 1; /* 1d transforms */
|
||||||
std::cout << GridLogPerformance << " FFT pencil " << t_pencil/1.0e6 << " s" << std::endl;
|
int n[] = {G}; /* 1d transforms of length G */
|
||||||
std::cout << GridLogPerformance << " of which copy " << t_copy/1.0e6 << " s" << std::endl;
|
int howmany = Ncomp;
|
||||||
std::cout << GridLogPerformance << " of which shift" << t_shift/1.0e6 << " s" << std::endl;
|
int odist,idist,istride,ostride;
|
||||||
std::cout << GridLogPerformance << " FFT kernels " << t_fft/1.0e6 << " s" << std::endl;
|
idist = odist = 1; /* Distance between consecutive FT's */
|
||||||
std::cout << GridLogPerformance << " FFT insert " << t_insert/1.0e6 << " s" << std::endl;
|
istride = ostride = Ncomp*Nlow; /* distance between two elements in the same FT */
|
||||||
|
int *inembed = n, *onembed = n;
|
||||||
|
|
||||||
|
scalar div;
|
||||||
|
if ( sign == backward ) div = 1.0/G;
|
||||||
|
else if ( sign == forward ) div = 1.0;
|
||||||
|
else assert(0);
|
||||||
|
|
||||||
|
FFTW_plan p;
|
||||||
|
{
|
||||||
|
FFTW_scalar *in = (FFTW_scalar *)&pgbuf._odata[0];
|
||||||
|
FFTW_scalar *out= (FFTW_scalar *)&pgbuf._odata[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::vector<int> lcoor(Nd), gcoor(Nd);
|
||||||
|
result = source;
|
||||||
|
int pc = processor_coor[dim];
|
||||||
|
for(int p=0;p<processors[dim];p++) {
|
||||||
|
PARALLEL_REGION
|
||||||
|
{
|
||||||
|
std::vector<int> cbuf(Nd);
|
||||||
|
sobj s;
|
||||||
|
|
||||||
|
PARALLEL_FOR_LOOP_INTERN
|
||||||
|
for(int idx=0;idx<sgrid->lSites();idx++) {
|
||||||
|
sgrid->LocalIndexToLocalCoor(idx,cbuf);
|
||||||
|
peekLocalSite(s,result,cbuf);
|
||||||
|
cbuf[dim]+=((pc+p) % processors[dim])*L;
|
||||||
|
// cbuf[dim]+=p*L;
|
||||||
|
pokeLocalSite(s,pgbuf,cbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p != processors[dim] - 1)
|
||||||
|
{
|
||||||
|
result = Cshift(result,dim,L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop over orthog coords
|
||||||
|
int NN=pencil_g.lSites();
|
||||||
|
GridStopWatch timer;
|
||||||
|
timer.Start();
|
||||||
|
PARALLEL_REGION
|
||||||
|
{
|
||||||
|
std::vector<int> cbuf(Nd);
|
||||||
|
|
||||||
|
PARALLEL_FOR_LOOP_INTERN
|
||||||
|
for(int idx=0;idx<NN;idx++) {
|
||||||
|
pencil_g.LocalIndexToLocalCoor(idx, cbuf);
|
||||||
|
if ( cbuf[dim] == 0 ) { // restricts loop to plane at lcoor[dim]==0
|
||||||
|
FFTW_scalar *in = (FFTW_scalar *)&pgbuf._odata[idx];
|
||||||
|
FFTW_scalar *out= (FFTW_scalar *)&pgbuf._odata[idx];
|
||||||
|
FFTW<scalar>::fftw_execute_dft(p,in,out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timer.Stop();
|
||||||
|
|
||||||
|
// performance counting
|
||||||
|
double add,mul,fma;
|
||||||
|
FFTW<scalar>::fftw_flops(p,&add,&mul,&fma);
|
||||||
|
flops_call = add+mul+2.0*fma;
|
||||||
|
usec += timer.useconds();
|
||||||
|
flops+= flops_call*NN;
|
||||||
|
|
||||||
|
// writing out result
|
||||||
|
PARALLEL_REGION
|
||||||
|
{
|
||||||
|
std::vector<int> clbuf(Nd), cgbuf(Nd);
|
||||||
|
sobj s;
|
||||||
|
|
||||||
|
PARALLEL_FOR_LOOP_INTERN
|
||||||
|
for(int idx=0;idx<sgrid->lSites();idx++) {
|
||||||
|
sgrid->LocalIndexToLocalCoor(idx,clbuf);
|
||||||
|
cgbuf = clbuf;
|
||||||
|
cgbuf[dim] = clbuf[dim]+L*pc;
|
||||||
|
peekLocalSite(s,pgbuf,cgbuf);
|
||||||
|
pokeLocalSite(s,result,clbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = result*div;
|
||||||
|
|
||||||
|
// destroying plan
|
||||||
|
FFTW<scalar>::fftw_destroy_plan(p);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
+446
-708
File diff suppressed because it is too large.
Load diff
@@ -1,4 +1,4 @@
|
|||||||
/*************************************************************************************
|
/*************************************************************************************
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
@@ -23,30 +23,24 @@ Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
|||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#ifndef GRID_PRECONDITIONER_H
|
#ifndef GRID_PRECONDITIONER_H
|
||||||
#define GRID_PRECONDITIONER_H
|
#define GRID_PRECONDITIONER_H
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
template<class Field> using Preconditioner = LinearFunction<Field> ;
|
template<class Field> class Preconditioner : public LinearFunction<Field> {
|
||||||
|
virtual void operator()(const Field &src, Field & psi)=0;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
template<class Field> class TrivialPrecon : public Preconditioner<Field> {
|
||||||
template<class Field> class Preconditioner : public LinearFunction<Field> {
|
public:
|
||||||
using LinearFunction<Field>::operator();
|
void operator()(const Field &src, Field & psi){
|
||||||
virtual void operator()(const Field &src, Field & psi)=0;
|
psi = src;
|
||||||
};
|
}
|
||||||
*/
|
TrivialPrecon(void){};
|
||||||
|
};
|
||||||
|
|
||||||
template<class Field> class TrivialPrecon : public Preconditioner<Field> {
|
}
|
||||||
public:
|
|
||||||
using Preconditioner<Field>::operator();
|
|
||||||
virtual void operator()(const Field &src, Field & psi){
|
|
||||||
psi = src;
|
|
||||||
}
|
|
||||||
TrivialPrecon(void){};
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/*************************************************************************************
|
/*************************************************************************************
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
@@ -23,64 +23,49 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#ifndef GRID_ALGORITHM_SPARSE_MATRIX_H
|
#ifndef GRID_ALGORITHM_SPARSE_MATRIX_H
|
||||||
#define GRID_ALGORITHM_SPARSE_MATRIX_H
|
#define GRID_ALGORITHM_SPARSE_MATRIX_H
|
||||||
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Interface defining what I expect of a general sparse matrix, such as a Fermion action
|
// Interface defining what I expect of a general sparse matrix, such as a Fermion action
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
template<class Field> class SparseMatrixBase {
|
template<class Field> class SparseMatrixBase {
|
||||||
public:
|
public:
|
||||||
virtual GridBase *Grid(void) =0;
|
virtual GridBase *Grid(void) =0;
|
||||||
// Full checkerboar operations
|
// Full checkerboar operations
|
||||||
virtual void M (const Field &in, Field &out)=0;
|
virtual RealD M (const Field &in, Field &out)=0;
|
||||||
virtual void Mdag (const Field &in, Field &out)=0;
|
virtual RealD Mdag (const Field &in, Field &out)=0;
|
||||||
virtual void MdagM(const Field &in, Field &out) {
|
virtual void MdagM(const Field &in, Field &out,RealD &ni,RealD &no) {
|
||||||
Field tmp (in.Grid());
|
Field tmp (in._grid);
|
||||||
M(in,tmp);
|
ni=M(in,tmp);
|
||||||
Mdag(tmp,out);
|
no=Mdag(tmp,out);
|
||||||
}
|
}
|
||||||
virtual void MMdag(const Field &in, Field &out) {
|
virtual void Mdiag (const Field &in, Field &out)=0;
|
||||||
Field tmp (in.Grid());
|
virtual void Mdir (const Field &in, Field &out,int dir, int disp)=0;
|
||||||
Mdag(in,tmp);
|
};
|
||||||
M(tmp,out);
|
|
||||||
}
|
|
||||||
virtual void Mdiag (const Field &in, Field &out)=0;
|
|
||||||
virtual void Mdir (const Field &in, Field &out,int dir, int disp)=0;
|
|
||||||
virtual void MdirAll (const Field &in, std::vector<Field> &out)=0;
|
|
||||||
virtual ~SparseMatrixBase() {};
|
|
||||||
};
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Interface augmented by a red black sparse matrix, such as a Fermion action
|
// Interface augmented by a red black sparse matrix, such as a Fermion action
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
template<class Field> class CheckerBoardedSparseMatrixBase : public SparseMatrixBase<Field> {
|
template<class Field> class CheckerBoardedSparseMatrixBase : public SparseMatrixBase<Field> {
|
||||||
public:
|
public:
|
||||||
virtual GridBase *RedBlackGrid(void)=0;
|
virtual GridBase *RedBlackGrid(void)=0;
|
||||||
|
// half checkerboard operaions
|
||||||
|
virtual void Meooe (const Field &in, Field &out)=0;
|
||||||
|
virtual void Mooee (const Field &in, Field &out)=0;
|
||||||
|
virtual void MooeeInv (const Field &in, Field &out)=0;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
virtual void MeooeDag (const Field &in, Field &out)=0;
|
||||||
// Query the even even properties to make algorithmic decisions
|
virtual void MooeeDag (const Field &in, Field &out)=0;
|
||||||
//////////////////////////////////////////////////////////////////////
|
virtual void MooeeInvDag (const Field &in, Field &out)=0;
|
||||||
virtual RealD Mass(void) { return 0.0; };
|
|
||||||
virtual int ConstEE(void) { return 1; }; // Disable assumptions unless overridden
|
|
||||||
virtual int isTrivialEE(void) { return 0; }; // by a derived class that knows better
|
|
||||||
|
|
||||||
// half checkerboard operaions
|
};
|
||||||
virtual void Meooe (const Field &in, Field &out)=0;
|
|
||||||
virtual void Mooee (const Field &in, Field &out)=0;
|
|
||||||
virtual void MooeeInv (const Field &in, Field &out)=0;
|
|
||||||
|
|
||||||
virtual void MeooeDag (const Field &in, Field &out)=0;
|
}
|
||||||
virtual void MooeeDag (const Field &in, Field &out)=0;
|
|
||||||
virtual void MooeeInvDag (const Field &in, Field &out)=0;
|
|
||||||
virtual ~CheckerBoardedSparseMatrixBase() {};
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
+307
-346
@@ -1,4 +1,4 @@
|
|||||||
/*************************************************************************************
|
/*************************************************************************************
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
@@ -25,14 +25,14 @@ Author: Christoph Lehner <clehner@bnl.gov>
|
|||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#ifndef GRID_CHEBYSHEV_H
|
#ifndef GRID_CHEBYSHEV_H
|
||||||
#define GRID_CHEBYSHEV_H
|
#define GRID_CHEBYSHEV_H
|
||||||
|
|
||||||
#include <Grid/algorithms/LinearOperator.h>
|
#include <Grid/algorithms/LinearOperator.h>
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
struct ChebyParams : Serializable {
|
struct ChebyParams : Serializable {
|
||||||
GRID_SERIALIZABLE_CLASS_MEMBERS(ChebyParams,
|
GRID_SERIALIZABLE_CLASS_MEMBERS(ChebyParams,
|
||||||
@@ -41,376 +41,337 @@ struct ChebyParams : Serializable {
|
|||||||
int, Npoly);
|
int, Npoly);
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Generic Chebyshev approximations
|
// Generic Chebyshev approximations
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
template<class Field>
|
template<class Field>
|
||||||
class Chebyshev : public OperatorFunction<Field> {
|
class Chebyshev : public OperatorFunction<Field> {
|
||||||
private:
|
private:
|
||||||
using OperatorFunction<Field>::operator();
|
std::vector<RealD> Coeffs;
|
||||||
|
int order;
|
||||||
|
RealD hi;
|
||||||
|
RealD lo;
|
||||||
|
|
||||||
std::vector<RealD> Coeffs;
|
public:
|
||||||
int order;
|
void csv(std::ostream &out){
|
||||||
RealD hi;
|
RealD diff = hi-lo;
|
||||||
RealD lo;
|
RealD delta = (hi-lo)*1.0e-9;
|
||||||
|
for (RealD x=lo; x<hi; x+=delta) {
|
||||||
public:
|
delta*=1.1;
|
||||||
void csv(std::ostream &out){
|
RealD f = approx(x);
|
||||||
RealD diff = hi-lo;
|
out<< x<<" "<<f<<std::endl;
|
||||||
RealD delta = diff*1.0e-9;
|
|
||||||
for (RealD x=lo; x<hi; x+=delta) {
|
|
||||||
delta*=1.02;
|
|
||||||
RealD f = approx(x);
|
|
||||||
out<< x<<" "<<f<<std::endl;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convenience for plotting the approximation
|
|
||||||
void PlotApprox(std::ostream &out) {
|
|
||||||
out<<"Polynomial approx ["<<lo<<","<<hi<<"]"<<std::endl;
|
|
||||||
for(RealD x=lo;x<hi;x+=(hi-lo)/50.0){
|
|
||||||
out <<x<<"\t"<<approx(x)<<std::endl;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Chebyshev(){};
|
|
||||||
Chebyshev(ChebyParams p){ Init(p.alpha,p.beta,p.Npoly);};
|
|
||||||
Chebyshev(RealD _lo,RealD _hi,int _order, RealD (* func)(RealD) ) {Init(_lo,_hi,_order,func);};
|
|
||||||
Chebyshev(RealD _lo,RealD _hi,int _order) {Init(_lo,_hi,_order);};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// c.f. numerical recipes "chebft"/"chebev". This is sec 5.8 "Chebyshev approximation".
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// CJ: the one we need for Lanczos
|
|
||||||
void Init(RealD _lo,RealD _hi,int _order)
|
|
||||||
{
|
|
||||||
lo=_lo;
|
|
||||||
hi=_hi;
|
|
||||||
order=_order;
|
|
||||||
|
|
||||||
if(order < 2) exit(-1);
|
|
||||||
Coeffs.resize(order,0.0);
|
|
||||||
Coeffs[order-1] = 1.0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// PB - more efficient low pass drops high modes above the low as 1/x uses all Chebyshev's.
|
|
||||||
// Similar kick effect below the threshold as Lanczos filter approach
|
|
||||||
void InitLowPass(RealD _lo,RealD _hi,int _order)
|
|
||||||
{
|
|
||||||
lo=_lo;
|
|
||||||
hi=_hi;
|
|
||||||
order=_order;
|
|
||||||
|
|
||||||
if(order < 2) exit(-1);
|
|
||||||
Coeffs.resize(order);
|
|
||||||
for(int j=0;j<order;j++){
|
|
||||||
RealD k=(order-1.0);
|
|
||||||
RealD s=std::cos( j*M_PI*(k+0.5)/order );
|
|
||||||
Coeffs[j] = s * 2.0/order;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
void Init(RealD _lo,RealD _hi,int _order, RealD (* func)(RealD))
|
|
||||||
{
|
|
||||||
lo=_lo;
|
|
||||||
hi=_hi;
|
|
||||||
order=_order;
|
|
||||||
|
|
||||||
if(order < 2) exit(-1);
|
|
||||||
Coeffs.resize(order);
|
|
||||||
for(int j=0;j<order;j++){
|
|
||||||
RealD s=0;
|
|
||||||
for(int k=0;k<order;k++){
|
|
||||||
RealD y=std::cos(M_PI*(k+0.5)/order);
|
|
||||||
RealD x=0.5*(y*(hi-lo)+(hi+lo));
|
|
||||||
RealD f=func(x);
|
|
||||||
s=s+f*std::cos( j*M_PI*(k+0.5)/order );
|
|
||||||
}
|
}
|
||||||
Coeffs[j] = s * 2.0/order;
|
return;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
template<class functor>
|
|
||||||
void Init(RealD _lo,RealD _hi,int _order, functor & func)
|
|
||||||
{
|
|
||||||
lo=_lo;
|
|
||||||
hi=_hi;
|
|
||||||
order=_order;
|
|
||||||
|
|
||||||
if(order < 2) exit(-1);
|
// Convenience for plotting the approximation
|
||||||
Coeffs.resize(order);
|
void PlotApprox(std::ostream &out) {
|
||||||
for(int j=0;j<order;j++){
|
out<<"Polynomial approx ["<<lo<<","<<hi<<"]"<<std::endl;
|
||||||
RealD s=0;
|
for(RealD x=lo;x<hi;x+=(hi-lo)/50.0){
|
||||||
for(int k=0;k<order;k++){
|
out <<x<<"\t"<<approx(x)<<std::endl;
|
||||||
RealD y=std::cos(M_PI*(k+0.5)/order);
|
|
||||||
RealD x=0.5*(y*(hi-lo)+(hi+lo));
|
|
||||||
RealD f=func(x);
|
|
||||||
s=s+f*std::cos( j*M_PI*(k+0.5)/order );
|
|
||||||
}
|
}
|
||||||
Coeffs[j] = s * 2.0/order;
|
};
|
||||||
}
|
|
||||||
};
|
Chebyshev(){};
|
||||||
|
Chebyshev(ChebyParams p){ Init(p.alpha,p.beta,p.Npoly);};
|
||||||
|
Chebyshev(RealD _lo,RealD _hi,int _order, RealD (* func)(RealD) ) {Init(_lo,_hi,_order,func);};
|
||||||
|
Chebyshev(RealD _lo,RealD _hi,int _order) {Init(_lo,_hi,_order);};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// c.f. numerical recipes "chebft"/"chebev". This is sec 5.8 "Chebyshev approximation".
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CJ: the one we need for Lanczos
|
||||||
|
void Init(RealD _lo,RealD _hi,int _order)
|
||||||
|
{
|
||||||
|
lo=_lo;
|
||||||
|
hi=_hi;
|
||||||
|
order=_order;
|
||||||
|
|
||||||
|
if(order < 2) exit(-1);
|
||||||
|
Coeffs.resize(order);
|
||||||
|
Coeffs.assign(0.,order);
|
||||||
|
Coeffs[order-1] = 1.;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Init(RealD _lo,RealD _hi,int _order, RealD (* func)(RealD))
|
||||||
|
{
|
||||||
|
lo=_lo;
|
||||||
|
hi=_hi;
|
||||||
|
order=_order;
|
||||||
|
|
||||||
|
if(order < 2) exit(-1);
|
||||||
|
Coeffs.resize(order);
|
||||||
|
for(int j=0;j<order;j++){
|
||||||
|
RealD s=0;
|
||||||
|
for(int k=0;k<order;k++){
|
||||||
|
RealD y=std::cos(M_PI*(k+0.5)/order);
|
||||||
|
RealD x=0.5*(y*(hi-lo)+(hi+lo));
|
||||||
|
RealD f=func(x);
|
||||||
|
s=s+f*std::cos( j*M_PI*(k+0.5)/order );
|
||||||
|
}
|
||||||
|
Coeffs[j] = s * 2.0/order;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
void JacksonSmooth(void){
|
void JacksonSmooth(void){
|
||||||
RealD M=order;
|
RealD M=order;
|
||||||
RealD alpha = M_PI/(M+2);
|
RealD alpha = M_PI/(M+2);
|
||||||
RealD lmax = std::cos(alpha);
|
RealD lmax = std::cos(alpha);
|
||||||
RealD sumUsq =0;
|
RealD sumUsq =0;
|
||||||
std::vector<RealD> U(M);
|
std::vector<RealD> U(M);
|
||||||
std::vector<RealD> a(M);
|
std::vector<RealD> a(M);
|
||||||
std::vector<RealD> g(M);
|
std::vector<RealD> g(M);
|
||||||
for(int n=0;n<=M;n++){
|
for(int n=0;n<=M;n++){
|
||||||
U[n] = std::sin((n+1)*std::acos(lmax))/std::sin(std::acos(lmax));
|
U[n] = std::sin((n+1)*std::acos(lmax))/std::sin(std::acos(lmax));
|
||||||
sumUsq += U[n]*U[n];
|
sumUsq += U[n]*U[n];
|
||||||
}
|
}
|
||||||
sumUsq = std::sqrt(sumUsq);
|
sumUsq = std::sqrt(sumUsq);
|
||||||
|
|
||||||
for(int i=1;i<=M;i++){
|
for(int i=1;i<=M;i++){
|
||||||
a[i] = U[i]/sumUsq;
|
a[i] = U[i]/sumUsq;
|
||||||
}
|
}
|
||||||
g[0] = 1.0;
|
g[0] = 1.0;
|
||||||
for(int m=1;m<=M;m++){
|
for(int m=1;m<=M;m++){
|
||||||
g[m] = 0;
|
g[m] = 0;
|
||||||
for(int i=0;i<=M-m;i++){
|
for(int i=0;i<=M-m;i++){
|
||||||
g[m]+= a[i]*a[m+i];
|
g[m]+= a[i]*a[m+i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(int m=1;m<=M;m++){
|
||||||
|
Coeffs[m]*=g[m];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(int m=1;m<=M;m++){
|
RealD approx(RealD x) // Convenience for plotting the approximation
|
||||||
Coeffs[m]*=g[m];
|
{
|
||||||
}
|
RealD Tn;
|
||||||
}
|
RealD Tnm;
|
||||||
RealD approx(RealD x) // Convenience for plotting the approximation
|
RealD Tnp;
|
||||||
{
|
|
||||||
RealD Tn;
|
|
||||||
RealD Tnm;
|
|
||||||
RealD Tnp;
|
|
||||||
|
|
||||||
RealD y=( x-0.5*(hi+lo))/(0.5*(hi-lo));
|
RealD y=( x-0.5*(hi+lo))/(0.5*(hi-lo));
|
||||||
|
|
||||||
RealD T0=1;
|
RealD T0=1;
|
||||||
RealD T1=y;
|
RealD T1=y;
|
||||||
|
|
||||||
RealD sum;
|
RealD sum;
|
||||||
sum = 0.5*Coeffs[0]*T0;
|
sum = 0.5*Coeffs[0]*T0;
|
||||||
sum+= Coeffs[1]*T1;
|
sum+= Coeffs[1]*T1;
|
||||||
|
|
||||||
Tn =T1;
|
Tn =T1;
|
||||||
Tnm=T0;
|
Tnm=T0;
|
||||||
for(int i=2;i<order;i++){
|
for(int i=2;i<order;i++){
|
||||||
Tnp=2*y*Tn-Tnm;
|
Tnp=2*y*Tn-Tnm;
|
||||||
Tnm=Tn;
|
Tnm=Tn;
|
||||||
Tn =Tnp;
|
Tn =Tnp;
|
||||||
sum+= Tn*Coeffs[i];
|
sum+= Tn*Coeffs[i];
|
||||||
}
|
}
|
||||||
return sum;
|
return sum;
|
||||||
};
|
};
|
||||||
|
|
||||||
RealD approxD(RealD x)
|
RealD approxD(RealD x)
|
||||||
{
|
{
|
||||||
RealD Un;
|
RealD Un;
|
||||||
RealD Unm;
|
RealD Unm;
|
||||||
RealD Unp;
|
RealD Unp;
|
||||||
|
|
||||||
RealD y=( x-0.5*(hi+lo))/(0.5*(hi-lo));
|
RealD y=( x-0.5*(hi+lo))/(0.5*(hi-lo));
|
||||||
|
|
||||||
RealD U0=1;
|
RealD U0=1;
|
||||||
RealD U1=2*y;
|
RealD U1=2*y;
|
||||||
|
|
||||||
RealD sum;
|
RealD sum;
|
||||||
sum = Coeffs[1]*U0;
|
sum = Coeffs[1]*U0;
|
||||||
sum+= Coeffs[2]*U1*2.0;
|
sum+= Coeffs[2]*U1*2.0;
|
||||||
|
|
||||||
Un =U1;
|
Un =U1;
|
||||||
Unm=U0;
|
Unm=U0;
|
||||||
for(int i=2;i<order-1;i++){
|
for(int i=2;i<order-1;i++){
|
||||||
Unp=2*y*Un-Unm;
|
Unp=2*y*Un-Unm;
|
||||||
Unm=Un;
|
Unm=Un;
|
||||||
Un =Unp;
|
Un =Unp;
|
||||||
sum+= Un*Coeffs[i+1]*(i+1.0);
|
sum+= Un*Coeffs[i+1]*(i+1.0);
|
||||||
}
|
}
|
||||||
return sum/(0.5*(hi-lo));
|
return sum/(0.5*(hi-lo));
|
||||||
};
|
};
|
||||||
|
|
||||||
RealD approxInv(RealD z, RealD x0, int maxiter, RealD resid) {
|
RealD approxInv(RealD z, RealD x0, int maxiter, RealD resid) {
|
||||||
RealD x = x0;
|
RealD x = x0;
|
||||||
RealD eps;
|
RealD eps;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i=0;i<maxiter;i++) {
|
for (i=0;i<maxiter;i++) {
|
||||||
eps = approx(x) - z;
|
eps = approx(x) - z;
|
||||||
if (fabs(eps / z) < resid)
|
if (fabs(eps / z) < resid)
|
||||||
return x;
|
return x;
|
||||||
x = x - eps / approxD(x);
|
x = x - eps / approxD(x);
|
||||||
}
|
|
||||||
|
|
||||||
return std::numeric_limits<double>::quiet_NaN();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement the required interface
|
|
||||||
void operator() (LinearOperatorBase<Field> &Linop, const Field &in, Field &out) {
|
|
||||||
|
|
||||||
GridBase *grid=in.Grid();
|
|
||||||
|
|
||||||
int vol=grid->gSites();
|
|
||||||
typedef typename Field::vector_type vector_type;
|
|
||||||
|
|
||||||
Field T0(grid); T0 = in;
|
|
||||||
Field T1(grid);
|
|
||||||
Field T2(grid);
|
|
||||||
Field y(grid);
|
|
||||||
|
|
||||||
Field *Tnm = &T0;
|
|
||||||
Field *Tn = &T1;
|
|
||||||
Field *Tnp = &T2;
|
|
||||||
|
|
||||||
// Tn=T1 = (xscale M + mscale)in
|
|
||||||
RealD xscale = 2.0/(hi-lo);
|
|
||||||
RealD mscale = -(hi+lo)/(hi-lo);
|
|
||||||
Linop.HermOp(T0,y);
|
|
||||||
grid->Barrier();
|
|
||||||
axpby(T1,xscale,mscale,y,in);
|
|
||||||
grid->Barrier();
|
|
||||||
|
|
||||||
// sum = .5 c[0] T0 + c[1] T1
|
|
||||||
// out = ()*T0 + Coeffs[1]*T1;
|
|
||||||
axpby(out,0.5*Coeffs[0],Coeffs[1],T0,T1);
|
|
||||||
for(int n=2;n<order;n++){
|
|
||||||
|
|
||||||
Linop.HermOp(*Tn,y);
|
|
||||||
axpby(y,xscale,mscale,y,(*Tn));
|
|
||||||
axpby(*Tnp,2.0,-1.0,y,(*Tnm));
|
|
||||||
if ( Coeffs[n] != 0.0) {
|
|
||||||
axpy(out,Coeffs[n],*Tnp,out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cycle pointers to avoid copies
|
return std::numeric_limits<double>::quiet_NaN();
|
||||||
Field *swizzle = Tnm;
|
|
||||||
Tnm =Tn;
|
|
||||||
Tn =Tnp;
|
|
||||||
Tnp =swizzle;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
// Implement the required interface
|
||||||
|
void operator() (LinearOperatorBase<Field> &Linop, const Field &in, Field &out) {
|
||||||
|
|
||||||
|
GridBase *grid=in._grid;
|
||||||
|
|
||||||
|
// std::cout << "Chevyshef(): in._grid="<<in._grid<<std::endl;
|
||||||
|
//std::cout <<" Linop.Grid()="<<Linop.Grid()<<"Linop.RedBlackGrid()="<<Linop.RedBlackGrid()<<std::endl;
|
||||||
|
|
||||||
|
int vol=grid->gSites();
|
||||||
|
|
||||||
|
Field T0(grid); T0 = in;
|
||||||
|
Field T1(grid);
|
||||||
|
Field T2(grid);
|
||||||
|
Field y(grid);
|
||||||
|
|
||||||
|
Field *Tnm = &T0;
|
||||||
|
Field *Tn = &T1;
|
||||||
|
Field *Tnp = &T2;
|
||||||
|
|
||||||
|
// Tn=T1 = (xscale M + mscale)in
|
||||||
|
RealD xscale = 2.0/(hi-lo);
|
||||||
|
RealD mscale = -(hi+lo)/(hi-lo);
|
||||||
|
Linop.HermOp(T0,y);
|
||||||
|
T1=y*xscale+in*mscale;
|
||||||
|
|
||||||
|
// sum = .5 c[0] T0 + c[1] T1
|
||||||
|
out = (0.5*Coeffs[0])*T0 + Coeffs[1]*T1;
|
||||||
|
for(int n=2;n<order;n++){
|
||||||
|
|
||||||
|
Linop.HermOp(*Tn,y);
|
||||||
|
|
||||||
|
y=xscale*y+mscale*(*Tn);
|
||||||
|
|
||||||
|
*Tnp=2.0*y-(*Tnm);
|
||||||
|
|
||||||
|
out=out+Coeffs[n]* (*Tnp);
|
||||||
|
|
||||||
|
// Cycle pointers to avoid copies
|
||||||
|
Field *swizzle = Tnm;
|
||||||
|
Tnm =Tn;
|
||||||
|
Tn =Tnp;
|
||||||
|
Tnp =swizzle;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
template<class Field>
|
template<class Field>
|
||||||
class ChebyshevLanczos : public Chebyshev<Field> {
|
class ChebyshevLanczos : public Chebyshev<Field> {
|
||||||
private:
|
private:
|
||||||
std::vector<RealD> Coeffs;
|
std::vector<RealD> Coeffs;
|
||||||
int order;
|
int order;
|
||||||
RealD alpha;
|
RealD alpha;
|
||||||
RealD beta;
|
RealD beta;
|
||||||
RealD mu;
|
RealD mu;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ChebyshevLanczos(RealD _alpha,RealD _beta,RealD _mu,int _order) :
|
ChebyshevLanczos(RealD _alpha,RealD _beta,RealD _mu,int _order) :
|
||||||
alpha(_alpha),
|
alpha(_alpha),
|
||||||
beta(_beta),
|
beta(_beta),
|
||||||
mu(_mu)
|
mu(_mu)
|
||||||
{
|
{
|
||||||
order=_order;
|
order=_order;
|
||||||
Coeffs.resize(order);
|
Coeffs.resize(order);
|
||||||
for(int i=0;i<_order;i++){
|
for(int i=0;i<_order;i++){
|
||||||
Coeffs[i] = 0.0;
|
Coeffs[i] = 0.0;
|
||||||
|
}
|
||||||
|
Coeffs[order-1]=1.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void csv(std::ostream &out){
|
||||||
|
for (RealD x=-1.2*alpha; x<1.2*alpha; x+=(2.0*alpha)/10000) {
|
||||||
|
RealD f = approx(x);
|
||||||
|
out<< x<<" "<<f<<std::endl;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RealD approx(RealD xx) // Convenience for plotting the approximation
|
||||||
|
{
|
||||||
|
RealD Tn;
|
||||||
|
RealD Tnm;
|
||||||
|
RealD Tnp;
|
||||||
|
Real aa = alpha * alpha;
|
||||||
|
Real bb = beta * beta;
|
||||||
|
|
||||||
|
RealD x = ( 2.0 * (xx-mu)*(xx-mu) - (aa+bb) ) / (aa-bb);
|
||||||
|
|
||||||
|
RealD y= x;
|
||||||
|
|
||||||
|
RealD T0=1;
|
||||||
|
RealD T1=y;
|
||||||
|
|
||||||
|
RealD sum;
|
||||||
|
sum = 0.5*Coeffs[0]*T0;
|
||||||
|
sum+= Coeffs[1]*T1;
|
||||||
|
|
||||||
|
Tn =T1;
|
||||||
|
Tnm=T0;
|
||||||
|
for(int i=2;i<order;i++){
|
||||||
|
Tnp=2*y*Tn-Tnm;
|
||||||
|
Tnm=Tn;
|
||||||
|
Tn =Tnp;
|
||||||
|
sum+= Tn*Coeffs[i];
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
};
|
||||||
|
|
||||||
|
// shift_Multiply in Rudy's code
|
||||||
|
void AminusMuSq(LinearOperatorBase<Field> &Linop, const Field &in, Field &out)
|
||||||
|
{
|
||||||
|
GridBase *grid=in._grid;
|
||||||
|
Field tmp(grid);
|
||||||
|
|
||||||
|
RealD aa= alpha*alpha;
|
||||||
|
RealD bb= beta * beta;
|
||||||
|
|
||||||
|
Linop.HermOp(in,out);
|
||||||
|
out = out - mu*in;
|
||||||
|
|
||||||
|
Linop.HermOp(out,tmp);
|
||||||
|
tmp = tmp - mu * out;
|
||||||
|
|
||||||
|
out = (2.0/ (aa-bb) ) * tmp - ((aa+bb)/(aa-bb))*in;
|
||||||
|
};
|
||||||
|
// Implement the required interface
|
||||||
|
void operator() (LinearOperatorBase<Field> &Linop, const Field &in, Field &out) {
|
||||||
|
|
||||||
|
GridBase *grid=in._grid;
|
||||||
|
|
||||||
|
int vol=grid->gSites();
|
||||||
|
|
||||||
|
Field T0(grid); T0 = in;
|
||||||
|
Field T1(grid);
|
||||||
|
Field T2(grid);
|
||||||
|
Field y(grid);
|
||||||
|
|
||||||
|
Field *Tnm = &T0;
|
||||||
|
Field *Tn = &T1;
|
||||||
|
Field *Tnp = &T2;
|
||||||
|
|
||||||
|
// Tn=T1 = (xscale M )*in
|
||||||
|
AminusMuSq(Linop,T0,T1);
|
||||||
|
|
||||||
|
// sum = .5 c[0] T0 + c[1] T1
|
||||||
|
out = (0.5*Coeffs[0])*T0 + Coeffs[1]*T1;
|
||||||
|
for(int n=2;n<order;n++){
|
||||||
|
|
||||||
|
AminusMuSq(Linop,*Tn,y);
|
||||||
|
|
||||||
|
*Tnp=2.0*y-(*Tnm);
|
||||||
|
|
||||||
|
out=out+Coeffs[n]* (*Tnp);
|
||||||
|
|
||||||
|
// Cycle pointers to avoid copies
|
||||||
|
Field *swizzle = Tnm;
|
||||||
|
Tnm =Tn;
|
||||||
|
Tn =Tnp;
|
||||||
|
Tnp =swizzle;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Coeffs[order-1]=1.0;
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
void csv(std::ostream &out){
|
|
||||||
for (RealD x=-1.2*alpha; x<1.2*alpha; x+=(2.0*alpha)/10000) {
|
|
||||||
RealD f = approx(x);
|
|
||||||
out<< x<<" "<<f<<std::endl;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RealD approx(RealD xx) // Convenience for plotting the approximation
|
|
||||||
{
|
|
||||||
RealD Tn;
|
|
||||||
RealD Tnm;
|
|
||||||
RealD Tnp;
|
|
||||||
Real aa = alpha * alpha;
|
|
||||||
Real bb = beta * beta;
|
|
||||||
|
|
||||||
RealD x = ( 2.0 * (xx-mu)*(xx-mu) - (aa+bb) ) / (aa-bb);
|
|
||||||
|
|
||||||
RealD y= x;
|
|
||||||
|
|
||||||
RealD T0=1;
|
|
||||||
RealD T1=y;
|
|
||||||
|
|
||||||
RealD sum;
|
|
||||||
sum = 0.5*Coeffs[0]*T0;
|
|
||||||
sum+= Coeffs[1]*T1;
|
|
||||||
|
|
||||||
Tn =T1;
|
|
||||||
Tnm=T0;
|
|
||||||
for(int i=2;i<order;i++){
|
|
||||||
Tnp=2*y*Tn-Tnm;
|
|
||||||
Tnm=Tn;
|
|
||||||
Tn =Tnp;
|
|
||||||
sum+= Tn*Coeffs[i];
|
|
||||||
}
|
|
||||||
return sum;
|
|
||||||
};
|
|
||||||
|
|
||||||
// shift_Multiply in Rudy's code
|
|
||||||
void AminusMuSq(LinearOperatorBase<Field> &Linop, const Field &in, Field &out)
|
|
||||||
{
|
|
||||||
GridBase *grid=in.Grid();
|
|
||||||
Field tmp(grid);
|
|
||||||
|
|
||||||
RealD aa= alpha*alpha;
|
|
||||||
RealD bb= beta * beta;
|
|
||||||
|
|
||||||
Linop.HermOp(in,out);
|
|
||||||
out = out - mu*in;
|
|
||||||
|
|
||||||
Linop.HermOp(out,tmp);
|
|
||||||
tmp = tmp - mu * out;
|
|
||||||
|
|
||||||
out = (2.0/ (aa-bb) ) * tmp - ((aa+bb)/(aa-bb))*in;
|
|
||||||
};
|
|
||||||
// Implement the required interface
|
|
||||||
void operator() (LinearOperatorBase<Field> &Linop, const Field &in, Field &out) {
|
|
||||||
|
|
||||||
GridBase *grid=in.Grid();
|
|
||||||
|
|
||||||
int vol=grid->gSites();
|
|
||||||
|
|
||||||
Field T0(grid); T0 = in;
|
|
||||||
Field T1(grid);
|
|
||||||
Field T2(grid);
|
|
||||||
Field y(grid);
|
|
||||||
|
|
||||||
Field *Tnm = &T0;
|
|
||||||
Field *Tn = &T1;
|
|
||||||
Field *Tnp = &T2;
|
|
||||||
|
|
||||||
// Tn=T1 = (xscale M )*in
|
|
||||||
AminusMuSq(Linop,T0,T1);
|
|
||||||
|
|
||||||
// sum = .5 c[0] T0 + c[1] T1
|
|
||||||
out = (0.5*Coeffs[0])*T0 + Coeffs[1]*T1;
|
|
||||||
for(int n=2;n<order;n++){
|
|
||||||
|
|
||||||
AminusMuSq(Linop,*Tn,y);
|
|
||||||
|
|
||||||
*Tnp=2.0*y-(*Tnm);
|
|
||||||
|
|
||||||
out=out+Coeffs[n]* (*Tnp);
|
|
||||||
|
|
||||||
// Cycle pointers to avoid copies
|
|
||||||
Field *swizzle = Tnm;
|
|
||||||
Tnm =Tn;
|
|
||||||
Tn =Tnp;
|
|
||||||
Tnp =swizzle;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
#endif
|
||||||
+114
-114
@@ -26,127 +26,127 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
|
|
||||||
#ifndef INCLUDED_FORECAST_H
|
#ifndef INCLUDED_FORECAST_H
|
||||||
#define INCLUDED_FORECAST_H
|
#define INCLUDED_FORECAST_H
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
// Abstract base class.
|
// Abstract base class.
|
||||||
// Takes a matrix (Mat), a source (phi), and a vector of Fields (chi)
|
// Takes a matrix (Mat), a source (phi), and a vector of Fields (chi)
|
||||||
// and returns a forecasted solution to the system D*psi = phi (psi).
|
// and returns a forecasted solution to the system D*psi = phi (psi).
|
||||||
template<class Matrix, class Field>
|
template<class Matrix, class Field>
|
||||||
class Forecast
|
class Forecast
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Field operator()(Matrix &Mat, const Field& phi, const std::vector<Field>& chi) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Implementation of Brower et al.'s chronological inverter (arXiv:hep-lat/9509012),
|
|
||||||
// used to forecast solutions across poles of the EOFA heatbath.
|
|
||||||
//
|
|
||||||
// Modified from CPS (cps_pp/src/util/dirac_op/d_op_base/comsrc/minresext.C)
|
|
||||||
template<class Matrix, class Field>
|
|
||||||
class ChronoForecast : public Forecast<Matrix,Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Field operator()(Matrix &Mat, const Field& phi, const std::vector<Field>& prev_solns)
|
|
||||||
{
|
{
|
||||||
int degree = prev_solns.size();
|
public:
|
||||||
Field chi(phi); // forecasted solution
|
virtual Field operator()(Matrix &Mat, const Field& phi, const std::vector<Field>& chi) = 0;
|
||||||
|
|
||||||
// Trivial cases
|
|
||||||
if(degree == 0){ chi = Zero(); return chi; }
|
|
||||||
else if(degree == 1){ return prev_solns[0]; }
|
|
||||||
|
|
||||||
// RealD dot;
|
|
||||||
ComplexD xp;
|
|
||||||
Field r(phi); // residual
|
|
||||||
Field Mv(phi);
|
|
||||||
std::vector<Field> v(prev_solns); // orthonormalized previous solutions
|
|
||||||
std::vector<Field> MdagMv(degree,phi);
|
|
||||||
|
|
||||||
// Array to hold the matrix elements
|
|
||||||
std::vector<std::vector<ComplexD>> G(degree, std::vector<ComplexD>(degree));
|
|
||||||
|
|
||||||
// Solution and source vectors
|
|
||||||
std::vector<ComplexD> a(degree);
|
|
||||||
std::vector<ComplexD> b(degree);
|
|
||||||
|
|
||||||
// Orthonormalize the vector basis
|
|
||||||
for(int i=0; i<degree; i++){
|
|
||||||
v[i] *= 1.0/std::sqrt(norm2(v[i]));
|
|
||||||
for(int j=i+1; j<degree; j++){ v[j] -= innerProduct(v[i],v[j]) * v[i]; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform sparse matrix multiplication and construct rhs
|
|
||||||
for(int i=0; i<degree; i++){
|
|
||||||
b[i] = innerProduct(v[i],phi);
|
|
||||||
Mat.M(v[i],Mv);
|
|
||||||
Mat.Mdag(Mv,MdagMv[i]);
|
|
||||||
G[i][i] = innerProduct(v[i],MdagMv[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the matrix
|
|
||||||
for(int j=0; j<degree; j++){
|
|
||||||
for(int k=j+1; k<degree; k++){
|
|
||||||
G[j][k] = innerProduct(v[j],MdagMv[k]);
|
|
||||||
G[k][j] = conjugate(G[j][k]);
|
|
||||||
}}
|
|
||||||
|
|
||||||
// Gauss-Jordan elimination with partial pivoting
|
|
||||||
for(int i=0; i<degree; i++){
|
|
||||||
|
|
||||||
// Perform partial pivoting
|
|
||||||
int k = i;
|
|
||||||
for(int j=i+1; j<degree; j++){ if(abs(G[j][j]) > abs(G[k][k])){ k = j; } }
|
|
||||||
if(k != i){
|
|
||||||
xp = b[k];
|
|
||||||
b[k] = b[i];
|
|
||||||
b[i] = xp;
|
|
||||||
for(int j=0; j<degree; j++){
|
|
||||||
xp = G[k][j];
|
|
||||||
G[k][j] = G[i][j];
|
|
||||||
G[i][j] = xp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert matrix to upper triangular form
|
|
||||||
for(int j=i+1; j<degree; j++){
|
|
||||||
xp = G[j][i]/G[i][i];
|
|
||||||
b[j] -= xp * b[i];
|
|
||||||
for(int k=0; k<degree; k++){ G[j][k] -= xp*G[i][k]; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use Gaussian elimination to solve equations and calculate initial guess
|
|
||||||
chi = Zero();
|
|
||||||
r = phi;
|
|
||||||
for(int i=degree-1; i>=0; i--){
|
|
||||||
a[i] = 0.0;
|
|
||||||
for(int j=i+1; j<degree; j++){ a[i] += G[i][j] * a[j]; }
|
|
||||||
a[i] = (b[i]-a[i])/G[i][i];
|
|
||||||
chi += a[i]*v[i];
|
|
||||||
r -= a[i]*MdagMv[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
RealD true_r(0.0);
|
|
||||||
ComplexD tmp;
|
|
||||||
for(int i=0; i<degree; i++){
|
|
||||||
tmp = -b[i];
|
|
||||||
for(int j=0; j<degree; j++){ tmp += G[i][j]*a[j]; }
|
|
||||||
tmp = conjugate(tmp)*tmp;
|
|
||||||
true_r += std::sqrt(tmp.real());
|
|
||||||
}
|
|
||||||
|
|
||||||
RealD error = std::sqrt(norm2(r)/norm2(phi));
|
|
||||||
std::cout << GridLogMessage << "ChronoForecast: |res|/|src| = " << error << std::endl;
|
|
||||||
|
|
||||||
return chi;
|
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
// Implementation of Brower et al.'s chronological inverter (arXiv:hep-lat/9509012),
|
||||||
|
// used to forecast solutions across poles of the EOFA heatbath.
|
||||||
|
//
|
||||||
|
// Modified from CPS (cps_pp/src/util/dirac_op/d_op_base/comsrc/minresext.C)
|
||||||
|
template<class Matrix, class Field>
|
||||||
|
class ChronoForecast : public Forecast<Matrix,Field>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Field operator()(Matrix &Mat, const Field& phi, const std::vector<Field>& prev_solns)
|
||||||
|
{
|
||||||
|
int degree = prev_solns.size();
|
||||||
|
Field chi(phi); // forecasted solution
|
||||||
|
|
||||||
|
// Trivial cases
|
||||||
|
if(degree == 0){ chi = zero; return chi; }
|
||||||
|
else if(degree == 1){ return prev_solns[0]; }
|
||||||
|
|
||||||
|
RealD dot;
|
||||||
|
ComplexD xp;
|
||||||
|
Field r(phi); // residual
|
||||||
|
Field Mv(phi);
|
||||||
|
std::vector<Field> v(prev_solns); // orthonormalized previous solutions
|
||||||
|
std::vector<Field> MdagMv(degree,phi);
|
||||||
|
|
||||||
|
// Array to hold the matrix elements
|
||||||
|
std::vector<std::vector<ComplexD>> G(degree, std::vector<ComplexD>(degree));
|
||||||
|
|
||||||
|
// Solution and source vectors
|
||||||
|
std::vector<ComplexD> a(degree);
|
||||||
|
std::vector<ComplexD> b(degree);
|
||||||
|
|
||||||
|
// Orthonormalize the vector basis
|
||||||
|
for(int i=0; i<degree; i++){
|
||||||
|
v[i] *= 1.0/std::sqrt(norm2(v[i]));
|
||||||
|
for(int j=i+1; j<degree; j++){ v[j] -= innerProduct(v[i],v[j]) * v[i]; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform sparse matrix multiplication and construct rhs
|
||||||
|
for(int i=0; i<degree; i++){
|
||||||
|
b[i] = innerProduct(v[i],phi);
|
||||||
|
Mat.M(v[i],Mv);
|
||||||
|
Mat.Mdag(Mv,MdagMv[i]);
|
||||||
|
G[i][i] = innerProduct(v[i],MdagMv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the matrix
|
||||||
|
for(int j=0; j<degree; j++){
|
||||||
|
for(int k=j+1; k<degree; k++){
|
||||||
|
G[j][k] = innerProduct(v[j],MdagMv[k]);
|
||||||
|
G[k][j] = std::conj(G[j][k]);
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Gauss-Jordan elimination with partial pivoting
|
||||||
|
for(int i=0; i<degree; i++){
|
||||||
|
|
||||||
|
// Perform partial pivoting
|
||||||
|
int k = i;
|
||||||
|
for(int j=i+1; j<degree; j++){ if(std::abs(G[j][j]) > std::abs(G[k][k])){ k = j; } }
|
||||||
|
if(k != i){
|
||||||
|
xp = b[k];
|
||||||
|
b[k] = b[i];
|
||||||
|
b[i] = xp;
|
||||||
|
for(int j=0; j<degree; j++){
|
||||||
|
xp = G[k][j];
|
||||||
|
G[k][j] = G[i][j];
|
||||||
|
G[i][j] = xp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert matrix to upper triangular form
|
||||||
|
for(int j=i+1; j<degree; j++){
|
||||||
|
xp = G[j][i]/G[i][i];
|
||||||
|
b[j] -= xp * b[i];
|
||||||
|
for(int k=0; k<degree; k++){ G[j][k] -= xp*G[i][k]; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use Gaussian elimination to solve equations and calculate initial guess
|
||||||
|
chi = zero;
|
||||||
|
r = phi;
|
||||||
|
for(int i=degree-1; i>=0; i--){
|
||||||
|
a[i] = 0.0;
|
||||||
|
for(int j=i+1; j<degree; j++){ a[i] += G[i][j] * a[j]; }
|
||||||
|
a[i] = (b[i]-a[i])/G[i][i];
|
||||||
|
chi += a[i]*v[i];
|
||||||
|
r -= a[i]*MdagMv[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
RealD true_r(0.0);
|
||||||
|
ComplexD tmp;
|
||||||
|
for(int i=0; i<degree; i++){
|
||||||
|
tmp = -b[i];
|
||||||
|
for(int j=0; j<degree; j++){ tmp += G[i][j]*a[j]; }
|
||||||
|
tmp = std::conj(tmp)*tmp;
|
||||||
|
true_r += std::sqrt(tmp.real());
|
||||||
|
}
|
||||||
|
|
||||||
|
RealD error = std::sqrt(norm2(r)/norm2(phi));
|
||||||
|
std::cout << GridLogMessage << "ChronoForecast: |res|/|src| = " << error << std::endl;
|
||||||
|
|
||||||
|
return chi;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
#ifndef GRID_JACOBIPOLYNOMIAL_H
|
|
||||||
#define GRID_JACOBIPOLYNOMIAL_H
|
|
||||||
|
|
||||||
#include <Grid/algorithms/LinearOperator.h>
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
template<class Field>
|
|
||||||
class JacobiPolynomial : public OperatorFunction<Field> {
|
|
||||||
private:
|
|
||||||
using OperatorFunction<Field>::operator();
|
|
||||||
|
|
||||||
int order;
|
|
||||||
RealD hi;
|
|
||||||
RealD lo;
|
|
||||||
RealD alpha;
|
|
||||||
RealD beta;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void csv(std::ostream &out){
|
|
||||||
csv(out,lo,hi);
|
|
||||||
}
|
|
||||||
void csv(std::ostream &out,RealD llo,RealD hhi){
|
|
||||||
RealD diff = hhi-llo;
|
|
||||||
RealD delta = diff*1.0e-5;
|
|
||||||
for (RealD x=llo-delta; x<=hhi; x+=delta) {
|
|
||||||
RealD f = approx(x);
|
|
||||||
out<< x<<" "<<f <<std::endl;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JacobiPolynomial(){};
|
|
||||||
JacobiPolynomial(RealD _lo,RealD _hi,int _order,RealD _alpha, RealD _beta)
|
|
||||||
{
|
|
||||||
lo=_lo;
|
|
||||||
hi=_hi;
|
|
||||||
alpha=_alpha;
|
|
||||||
beta=_beta;
|
|
||||||
order=_order;
|
|
||||||
};
|
|
||||||
|
|
||||||
RealD approx(RealD x) // Convenience for plotting the approximation
|
|
||||||
{
|
|
||||||
RealD Tn;
|
|
||||||
RealD Tnm;
|
|
||||||
RealD Tnp;
|
|
||||||
|
|
||||||
RealD y=( x-0.5*(hi+lo))/(0.5*(hi-lo));
|
|
||||||
|
|
||||||
RealD T0=1.0;
|
|
||||||
RealD T1=(alpha-beta)*0.5+(alpha+beta+2.0)*0.5*y;
|
|
||||||
|
|
||||||
Tn =T1;
|
|
||||||
Tnm=T0;
|
|
||||||
for(int n=2;n<=order;n++){
|
|
||||||
RealD cnp = 2.0*n*(n+alpha+beta)*(2.0*n-2.0+alpha+beta);
|
|
||||||
RealD cny = (2.0*n-2.0+alpha+beta)*(2.0*n-1.0+alpha+beta)*(2.0*n+alpha+beta);
|
|
||||||
RealD cn1 = (2.0*n+alpha+beta-1.0)*(alpha*alpha-beta*beta);
|
|
||||||
RealD cnm = - 2.0*(n+alpha-1.0)*(n+beta-1.0)*(2.0*n+alpha+beta);
|
|
||||||
Tnp= ( cny * y *Tn + cn1 * Tn + cnm * Tnm )/ cnp;
|
|
||||||
Tnm=Tn;
|
|
||||||
Tn =Tnp;
|
|
||||||
}
|
|
||||||
return Tnp;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Implement the required interface
|
|
||||||
void operator() (LinearOperatorBase<Field> &Linop, const Field &in, Field &out) {
|
|
||||||
GridBase *grid=in.Grid();
|
|
||||||
|
|
||||||
int vol=grid->gSites();
|
|
||||||
|
|
||||||
Field T0(grid);
|
|
||||||
Field T1(grid);
|
|
||||||
Field T2(grid);
|
|
||||||
Field y(grid);
|
|
||||||
|
|
||||||
|
|
||||||
Field *Tnm = &T0;
|
|
||||||
Field *Tn = &T1;
|
|
||||||
Field *Tnp = &T2;
|
|
||||||
|
|
||||||
// RealD T0=1.0;
|
|
||||||
T0=in;
|
|
||||||
|
|
||||||
// RealD y=( x-0.5*(hi+lo))/(0.5*(hi-lo));
|
|
||||||
// = x * 2/(hi-lo) - (hi+lo)/(hi-lo)
|
|
||||||
Linop.HermOp(T0,y);
|
|
||||||
RealD xscale = 2.0/(hi-lo);
|
|
||||||
RealD mscale = -(hi+lo)/(hi-lo);
|
|
||||||
Linop.HermOp(T0,y);
|
|
||||||
y=y*xscale+in*mscale;
|
|
||||||
|
|
||||||
// RealD T1=(alpha-beta)*0.5+(alpha+beta+2.0)*0.5*y;
|
|
||||||
RealD halfAmB = (alpha-beta)*0.5;
|
|
||||||
RealD halfApBp2= (alpha+beta+2.0)*0.5;
|
|
||||||
T1 = halfAmB * in + halfApBp2*y;
|
|
||||||
|
|
||||||
for(int n=2;n<=order;n++){
|
|
||||||
|
|
||||||
Linop.HermOp(*Tn,y);
|
|
||||||
y=xscale*y+mscale*(*Tn);
|
|
||||||
|
|
||||||
RealD cnp = 2.0*n*(n+alpha+beta)*(2.0*n-2.0+alpha+beta);
|
|
||||||
RealD cny = (2.0*n-2.0+alpha+beta)*(2.0*n-1.0+alpha+beta)*(2.0*n+alpha+beta);
|
|
||||||
RealD cn1 = (2.0*n+alpha+beta-1.0)*(alpha*alpha-beta*beta);
|
|
||||||
RealD cnm = - 2.0*(n+alpha-1.0)*(n+beta-1.0)*(2.0*n+alpha+beta);
|
|
||||||
|
|
||||||
// Tnp= ( cny * y *Tn + cn1 * Tn + cnm * Tnm )/ cnp;
|
|
||||||
cny=cny/cnp;
|
|
||||||
cn1=cn1/cnp;
|
|
||||||
cn1=cn1/cnp;
|
|
||||||
cnm=cnm/cnp;
|
|
||||||
|
|
||||||
*Tnp=cny*y + cn1 *(*Tn) + cnm * (*Tnm);
|
|
||||||
|
|
||||||
// Cycle pointers to avoid copies
|
|
||||||
Field *swizzle = Tnm;
|
|
||||||
Tnm =Tn;
|
|
||||||
Tn =Tnp;
|
|
||||||
Tnp =swizzle;
|
|
||||||
}
|
|
||||||
out=*Tnp;
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
|
||||||
@@ -27,8 +27,7 @@ Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
|||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#include <Grid/GridCore.h>
|
#include <Grid/GridCore.h>
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
double MultiShiftFunction::approx(double x)
|
double MultiShiftFunction::approx(double x)
|
||||||
{
|
{
|
||||||
double a = norm;
|
double a = norm;
|
||||||
@@ -54,4 +53,4 @@ void MultiShiftFunction::csv(std::ostream &out)
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
NAMESPACE_END(Grid);
|
}
|
||||||
@@ -29,7 +29,7 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
#ifndef MULTI_SHIFT_FUNCTION
|
#ifndef MULTI_SHIFT_FUNCTION
|
||||||
#define MULTI_SHIFT_FUNCTION
|
#define MULTI_SHIFT_FUNCTION
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
class MultiShiftFunction {
|
class MultiShiftFunction {
|
||||||
public:
|
public:
|
||||||
@@ -40,7 +40,7 @@ public:
|
|||||||
RealD norm;
|
RealD norm;
|
||||||
RealD lo,hi;
|
RealD lo,hi;
|
||||||
|
|
||||||
MultiShiftFunction(int n,RealD _lo,RealD _hi): poles(n), residues(n), tolerances(n), lo(_lo), hi(_hi) {;};
|
MultiShiftFunction(int n,RealD _lo,RealD _hi): poles(n), residues(n), lo(_lo), hi(_hi) {;};
|
||||||
RealD approx(RealD x);
|
RealD approx(RealD x);
|
||||||
void csv(std::ostream &out);
|
void csv(std::ostream &out);
|
||||||
void gnuplot(std::ostream &out);
|
void gnuplot(std::ostream &out);
|
||||||
@@ -63,5 +63,5 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
NAMESPACE_END(Grid);
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -121,7 +121,7 @@ double AlgRemez::generateApprox(int num_degree, int den_degree,
|
|||||||
// Reallocate arrays, since degree has changed
|
// Reallocate arrays, since degree has changed
|
||||||
if (num_degree != n || den_degree != d) allocate(num_degree,den_degree);
|
if (num_degree != n || den_degree != d) allocate(num_degree,den_degree);
|
||||||
|
|
||||||
GRID_ASSERT(a_len<=SUM_MAX);
|
assert(a_len<=SUM_MAX);
|
||||||
|
|
||||||
step = new bigfloat[num_degree+den_degree+2];
|
step = new bigfloat[num_degree+den_degree+2];
|
||||||
|
|
||||||
@@ -151,9 +151,9 @@ double AlgRemez::generateApprox(int num_degree, int den_degree,
|
|||||||
equations();
|
equations();
|
||||||
if (delta < tolerance) {
|
if (delta < tolerance) {
|
||||||
std::cout<<"Delta too small, try increasing precision\n";
|
std::cout<<"Delta too small, try increasing precision\n";
|
||||||
GRID_ASSERT(0);
|
assert(0);
|
||||||
};
|
};
|
||||||
GRID_ASSERT( delta>= tolerance);
|
assert( delta>= tolerance);
|
||||||
|
|
||||||
search(step);
|
search(step);
|
||||||
}
|
}
|
||||||
@@ -298,7 +298,7 @@ void AlgRemez::stpini(bigfloat *step) {
|
|||||||
// Search for error maxima and minima
|
// Search for error maxima and minima
|
||||||
void AlgRemez::search(bigfloat *step) {
|
void AlgRemez::search(bigfloat *step) {
|
||||||
bigfloat a, q, xm, ym, xn, yn, xx0, xx1;
|
bigfloat a, q, xm, ym, xn, yn, xx0, xx1;
|
||||||
int i, meq, emsign, ensign, steps;
|
int i, j, meq, emsign, ensign, steps;
|
||||||
|
|
||||||
meq = neq + 1;
|
meq = neq + 1;
|
||||||
bigfloat *yy = new bigfloat[meq];
|
bigfloat *yy = new bigfloat[meq];
|
||||||
@@ -306,6 +306,7 @@ void AlgRemez::search(bigfloat *step) {
|
|||||||
bigfloat eclose = 1.0e30;
|
bigfloat eclose = 1.0e30;
|
||||||
bigfloat farther = 0l;
|
bigfloat farther = 0l;
|
||||||
|
|
||||||
|
j = 1;
|
||||||
xx0 = apstrt;
|
xx0 = apstrt;
|
||||||
|
|
||||||
for (i = 0; i < meq; i++) {
|
for (i = 0; i < meq; i++) {
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ class AlgRemez
|
|||||||
virtual ~AlgRemez();
|
virtual ~AlgRemez();
|
||||||
|
|
||||||
int getDegree(void){
|
int getDegree(void){
|
||||||
GRID_ASSERT(n==d);
|
assert(n==d);
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
// Reset the bounds of the approximation
|
// Reset the bounds of the approximation
|
||||||
|
|||||||
@@ -1,473 +0,0 @@
|
|||||||
#include<math.h>
|
|
||||||
#include<stdio.h>
|
|
||||||
#include<stdlib.h>
|
|
||||||
#include<string>
|
|
||||||
#include<iostream>
|
|
||||||
#include<iomanip>
|
|
||||||
#include<cassert>
|
|
||||||
|
|
||||||
#include<Grid/algorithms/approx/RemezGeneral.h>
|
|
||||||
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
AlgRemezGeneral::AlgRemezGeneral(double lower, double upper, long precision,
|
|
||||||
bigfloat (*f)(bigfloat x, void *data), void *data): f(f),
|
|
||||||
data(data),
|
|
||||||
prec(precision),
|
|
||||||
apstrt(lower), apend(upper), apwidt(upper - lower),
|
|
||||||
n(0), d(0), pow_n(0), pow_d(0)
|
|
||||||
{
|
|
||||||
bigfloat::setDefaultPrecision(prec);
|
|
||||||
|
|
||||||
std::cout<<"Approximation bounds are ["<<apstrt<<","<<apend<<"]\n";
|
|
||||||
std::cout<<"Precision of arithmetic is "<<precision<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Determine the properties of the numerator and denominator polynomials
|
|
||||||
void AlgRemezGeneral::setupPolyProperties(int num_degree, int den_degree, PolyType num_type_in, PolyType den_type_in){
|
|
||||||
pow_n = num_degree;
|
|
||||||
pow_d = den_degree;
|
|
||||||
|
|
||||||
if(pow_n % 2 == 0 && num_type_in == PolyType::Odd) GRID_ASSERT(0);
|
|
||||||
if(pow_n % 2 == 1 && num_type_in == PolyType::Even) GRID_ASSERT(0);
|
|
||||||
|
|
||||||
if(pow_d % 2 == 0 && den_type_in == PolyType::Odd) GRID_ASSERT(0);
|
|
||||||
if(pow_d % 2 == 1 && den_type_in == PolyType::Even) GRID_ASSERT(0);
|
|
||||||
|
|
||||||
num_type = num_type_in;
|
|
||||||
den_type = den_type_in;
|
|
||||||
|
|
||||||
num_pows.resize(pow_n+1);
|
|
||||||
den_pows.resize(pow_d+1);
|
|
||||||
|
|
||||||
int n_in = 0;
|
|
||||||
bool odd = num_type == PolyType::Full || num_type == PolyType::Odd;
|
|
||||||
bool even = num_type == PolyType::Full || num_type == PolyType::Even;
|
|
||||||
for(int i=0;i<=pow_n;i++){
|
|
||||||
num_pows[i] = -1;
|
|
||||||
if(i % 2 == 0 && even) num_pows[i] = n_in++;
|
|
||||||
if(i % 2 == 1 && odd) num_pows[i] = n_in++;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << n_in << " terms in numerator" << std::endl;
|
|
||||||
--n_in; //power is 1 less than the number of terms, eg pow=1 a x^1 + b x^0
|
|
||||||
|
|
||||||
int d_in = 0;
|
|
||||||
odd = den_type == PolyType::Full || den_type == PolyType::Odd;
|
|
||||||
even = den_type == PolyType::Full || den_type == PolyType::Even;
|
|
||||||
for(int i=0;i<=pow_d;i++){
|
|
||||||
den_pows[i] = -1;
|
|
||||||
if(i % 2 == 0 && even) den_pows[i] = d_in++;
|
|
||||||
if(i % 2 == 1 && odd) den_pows[i] = d_in++;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << d_in << " terms in denominator" << std::endl;
|
|
||||||
--d_in;
|
|
||||||
|
|
||||||
n = n_in;
|
|
||||||
d = d_in;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Setup algorithm
|
|
||||||
void AlgRemezGeneral::reinitializeAlgorithm(){
|
|
||||||
spread = 1.0e37;
|
|
||||||
iter = 0;
|
|
||||||
|
|
||||||
neq = n + d + 1; //not +2 because highest-power term in denominator is fixed to 1
|
|
||||||
|
|
||||||
param.resize(neq);
|
|
||||||
yy.resize(neq+1);
|
|
||||||
|
|
||||||
//Initialize linear equation temporaries
|
|
||||||
A.resize(neq*neq);
|
|
||||||
B.resize(neq);
|
|
||||||
IPS.resize(neq);
|
|
||||||
|
|
||||||
//Initialize maximum and minimum errors
|
|
||||||
xx.resize(neq+2);
|
|
||||||
mm.resize(neq+1);
|
|
||||||
initialGuess();
|
|
||||||
|
|
||||||
//Initialize search steps
|
|
||||||
step.resize(neq+1);
|
|
||||||
stpini();
|
|
||||||
}
|
|
||||||
|
|
||||||
double AlgRemezGeneral::generateApprox(const int num_degree, const int den_degree,
|
|
||||||
const PolyType num_type_in, const PolyType den_type_in,
|
|
||||||
const double _tolerance, const int report_freq){
|
|
||||||
//Setup the properties of the polynomial
|
|
||||||
setupPolyProperties(num_degree, den_degree, num_type_in, den_type_in);
|
|
||||||
|
|
||||||
//Setup the algorithm
|
|
||||||
reinitializeAlgorithm();
|
|
||||||
|
|
||||||
bigfloat tolerance = _tolerance;
|
|
||||||
|
|
||||||
//Iterate until convergance
|
|
||||||
while (spread > tolerance) {
|
|
||||||
if (iter++ % report_freq==0)
|
|
||||||
std::cout<<"Iteration " <<iter-1<<" spread "<<(double)spread<<" delta "<<(double)delta << std::endl;
|
|
||||||
|
|
||||||
equations();
|
|
||||||
if (delta < tolerance) {
|
|
||||||
std::cout<<"Iteration " << iter-1 << " delta too small (" << delta << "<" << tolerance << "), try increasing precision\n";
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
};
|
|
||||||
GRID_ASSERT( delta>= tolerance );
|
|
||||||
|
|
||||||
search();
|
|
||||||
}
|
|
||||||
|
|
||||||
int sign;
|
|
||||||
double error = (double)getErr(mm[0],&sign);
|
|
||||||
std::cout<<"Converged at "<<iter<<" iterations; error = "<<error<<std::endl;
|
|
||||||
|
|
||||||
// Return the maximum error in the approximation
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Initial values of maximal and minimal errors
|
|
||||||
void AlgRemezGeneral::initialGuess(){
|
|
||||||
// Supply initial guesses for solution points
|
|
||||||
long ncheb = neq; // Degree of Chebyshev error estimate
|
|
||||||
|
|
||||||
// Find ncheb+1 extrema of Chebyshev polynomial
|
|
||||||
bigfloat a = ncheb;
|
|
||||||
bigfloat r;
|
|
||||||
|
|
||||||
mm[0] = apstrt;
|
|
||||||
for (long i = 1; i < ncheb; i++) {
|
|
||||||
r = 0.5 * (1 - cos((M_PI * i)/(double) a));
|
|
||||||
//r *= sqrt_bf(r);
|
|
||||||
r = (exp((double)r)-1.0)/(exp(1.0)-1.0);
|
|
||||||
mm[i] = apstrt + r * apwidt;
|
|
||||||
}
|
|
||||||
mm[ncheb] = apend;
|
|
||||||
|
|
||||||
a = 2.0 * ncheb;
|
|
||||||
for (long i = 0; i <= ncheb; i++) {
|
|
||||||
r = 0.5 * (1 - cos(M_PI * (2*i+1)/(double) a));
|
|
||||||
//r *= sqrt_bf(r); // Squeeze to low end of interval
|
|
||||||
r = (exp((double)r)-1.0)/(exp(1.0)-1.0);
|
|
||||||
xx[i] = apstrt + r * apwidt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialise step sizes
|
|
||||||
void AlgRemezGeneral::stpini(){
|
|
||||||
xx[neq+1] = apend;
|
|
||||||
delta = 0.25;
|
|
||||||
step[0] = xx[0] - apstrt;
|
|
||||||
for (int i = 1; i < neq; i++) step[i] = xx[i] - xx[i-1];
|
|
||||||
step[neq] = step[neq-1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for error maxima and minima
|
|
||||||
void AlgRemezGeneral::search(){
|
|
||||||
bigfloat a, q, xm, ym, xn, yn, xx1;
|
|
||||||
int emsign, ensign, steps;
|
|
||||||
|
|
||||||
int meq = neq + 1;
|
|
||||||
|
|
||||||
bigfloat eclose = 1.0e30;
|
|
||||||
bigfloat farther = 0l;
|
|
||||||
|
|
||||||
bigfloat xx0 = apstrt;
|
|
||||||
|
|
||||||
for (int i = 0; i < meq; i++) {
|
|
||||||
steps = 0;
|
|
||||||
xx1 = xx[i]; // Next zero
|
|
||||||
if (i == meq-1) xx1 = apend;
|
|
||||||
xm = mm[i];
|
|
||||||
ym = getErr(xm,&emsign);
|
|
||||||
q = step[i];
|
|
||||||
xn = xm + q;
|
|
||||||
if (xn < xx0 || xn >= xx1) { // Cannot skip over adjacent boundaries
|
|
||||||
q = -q;
|
|
||||||
xn = xm;
|
|
||||||
yn = ym;
|
|
||||||
ensign = emsign;
|
|
||||||
} else {
|
|
||||||
yn = getErr(xn,&ensign);
|
|
||||||
if (yn < ym) {
|
|
||||||
q = -q;
|
|
||||||
xn = xm;
|
|
||||||
yn = ym;
|
|
||||||
ensign = emsign;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while(yn >= ym) { // March until error becomes smaller.
|
|
||||||
if (++steps > 10)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ym = yn;
|
|
||||||
xm = xn;
|
|
||||||
emsign = ensign;
|
|
||||||
a = xm + q;
|
|
||||||
if (a == xm || a <= xx0 || a >= xx1)
|
|
||||||
break;// Must not skip over the zeros either side.
|
|
||||||
|
|
||||||
xn = a;
|
|
||||||
yn = getErr(xn,&ensign);
|
|
||||||
}
|
|
||||||
|
|
||||||
mm[i] = xm; // Position of maximum
|
|
||||||
yy[i] = ym; // Value of maximum
|
|
||||||
|
|
||||||
if (eclose > ym) eclose = ym;
|
|
||||||
if (farther < ym) farther = ym;
|
|
||||||
|
|
||||||
xx0 = xx1; // Walk to next zero.
|
|
||||||
} // end of search loop
|
|
||||||
|
|
||||||
q = (farther - eclose); // Decrease step size if error spread increased
|
|
||||||
|
|
||||||
if (eclose != 0.0) q /= eclose; // Relative error spread
|
|
||||||
|
|
||||||
if (q >= spread)
|
|
||||||
delta *= 0.5; // Spread is increasing; decrease step size
|
|
||||||
|
|
||||||
spread = q;
|
|
||||||
|
|
||||||
for (int i = 0; i < neq; i++) {
|
|
||||||
q = yy[i+1];
|
|
||||||
if (q != 0.0) q = yy[i] / q - (bigfloat)1l;
|
|
||||||
else q = 0.0625;
|
|
||||||
if (q > (bigfloat)0.25) q = 0.25;
|
|
||||||
q *= mm[i+1] - mm[i];
|
|
||||||
step[i] = q * delta;
|
|
||||||
}
|
|
||||||
step[neq] = step[neq-1];
|
|
||||||
|
|
||||||
for (int i = 0; i < neq; i++) { // Insert new locations for the zeros.
|
|
||||||
xm = xx[i] - step[i];
|
|
||||||
|
|
||||||
if (xm <= apstrt)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (xm >= apend)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (xm <= mm[i])
|
|
||||||
xm = (bigfloat)0.5 * (mm[i] + xx[i]);
|
|
||||||
|
|
||||||
if (xm >= mm[i+1])
|
|
||||||
xm = (bigfloat)0.5 * (mm[i+1] + xx[i]);
|
|
||||||
|
|
||||||
xx[i] = xm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Solve the equations
|
|
||||||
void AlgRemezGeneral::equations(){
|
|
||||||
bigfloat x, y, z;
|
|
||||||
bigfloat *aa;
|
|
||||||
|
|
||||||
for (int i = 0; i < neq; i++) { // set up the equations for solution by simq()
|
|
||||||
int ip = neq * i; // offset to 1st element of this row of matrix
|
|
||||||
x = xx[i]; // the guess for this row
|
|
||||||
y = func(x); // right-hand-side vector
|
|
||||||
|
|
||||||
z = (bigfloat)1l;
|
|
||||||
aa = A.data()+ip;
|
|
||||||
int t = 0;
|
|
||||||
for (int j = 0; j <= pow_n; j++) {
|
|
||||||
if(num_pows[j] != -1){ *aa++ = z; t++; }
|
|
||||||
z *= x;
|
|
||||||
}
|
|
||||||
GRID_ASSERT(t == n+1);
|
|
||||||
|
|
||||||
z = (bigfloat)1l;
|
|
||||||
t = 0;
|
|
||||||
for (int j = 0; j < pow_d; j++) {
|
|
||||||
if(den_pows[j] != -1){ *aa++ = -y * z; t++; }
|
|
||||||
z *= x;
|
|
||||||
}
|
|
||||||
GRID_ASSERT(t == d);
|
|
||||||
|
|
||||||
B[i] = y * z; // Right hand side vector
|
|
||||||
}
|
|
||||||
|
|
||||||
// Solve the simultaneous linear equations.
|
|
||||||
if (simq()){
|
|
||||||
std::cout<<"simq failed\n";
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Evaluate the rational form P(x)/Q(x) using coefficients
|
|
||||||
// from the solution vector param
|
|
||||||
bigfloat AlgRemezGeneral::approx(const bigfloat x) const{
|
|
||||||
// Work backwards toward the constant term.
|
|
||||||
int c = n;
|
|
||||||
bigfloat yn = param[c--]; // Highest order numerator coefficient
|
|
||||||
for (int i = pow_n-1; i >= 0; i--) yn = x * yn + (num_pows[i] != -1 ? param[c--] : bigfloat(0l));
|
|
||||||
|
|
||||||
c = n+d;
|
|
||||||
bigfloat yd = 1l; //Highest degree coefficient is 1.0
|
|
||||||
for (int i = pow_d-1; i >= 0; i--) yd = x * yd + (den_pows[i] != -1 ? param[c--] : bigfloat(0l));
|
|
||||||
|
|
||||||
return(yn/yd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute size and sign of the approximation error at x
|
|
||||||
bigfloat AlgRemezGeneral::getErr(bigfloat x, int *sign) const{
|
|
||||||
bigfloat f = func(x);
|
|
||||||
bigfloat e = approx(x) - f;
|
|
||||||
if (f != 0) e /= f;
|
|
||||||
if (e < (bigfloat)0.0) {
|
|
||||||
*sign = -1;
|
|
||||||
e = -e;
|
|
||||||
}
|
|
||||||
else *sign = 1;
|
|
||||||
|
|
||||||
return(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Solve the system AX=B
|
|
||||||
int AlgRemezGeneral::simq(){
|
|
||||||
|
|
||||||
int ip, ipj, ipk, ipn;
|
|
||||||
int idxpiv;
|
|
||||||
int kp, kp1, kpk, kpn;
|
|
||||||
int nip, nkp;
|
|
||||||
bigfloat em, q, rownrm, big, size, pivot, sum;
|
|
||||||
bigfloat *aa;
|
|
||||||
bigfloat *X = param.data();
|
|
||||||
|
|
||||||
int n = neq;
|
|
||||||
int nm1 = n - 1;
|
|
||||||
// Initialize IPS and X
|
|
||||||
|
|
||||||
int ij = 0;
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
IPS[i] = i;
|
|
||||||
rownrm = 0.0;
|
|
||||||
for(int j = 0; j < n; j++) {
|
|
||||||
q = abs_bf(A[ij]);
|
|
||||||
if(rownrm < q) rownrm = q;
|
|
||||||
++ij;
|
|
||||||
}
|
|
||||||
if (rownrm == (bigfloat)0l) {
|
|
||||||
std::cout<<"simq rownrm=0\n";
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
X[i] = (bigfloat)1.0 / rownrm;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int k = 0; k < nm1; k++) {
|
|
||||||
big = 0.0;
|
|
||||||
idxpiv = 0;
|
|
||||||
|
|
||||||
for (int i = k; i < n; i++) {
|
|
||||||
ip = IPS[i];
|
|
||||||
ipk = n*ip + k;
|
|
||||||
size = abs_bf(A[ipk]) * X[ip];
|
|
||||||
if (size > big) {
|
|
||||||
big = size;
|
|
||||||
idxpiv = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (big == (bigfloat)0l) {
|
|
||||||
std::cout<<"simq big=0\n";
|
|
||||||
return(2);
|
|
||||||
}
|
|
||||||
if (idxpiv != k) {
|
|
||||||
int j = IPS[k];
|
|
||||||
IPS[k] = IPS[idxpiv];
|
|
||||||
IPS[idxpiv] = j;
|
|
||||||
}
|
|
||||||
kp = IPS[k];
|
|
||||||
kpk = n*kp + k;
|
|
||||||
pivot = A[kpk];
|
|
||||||
kp1 = k+1;
|
|
||||||
for (int i = kp1; i < n; i++) {
|
|
||||||
ip = IPS[i];
|
|
||||||
ipk = n*ip + k;
|
|
||||||
em = -A[ipk] / pivot;
|
|
||||||
A[ipk] = -em;
|
|
||||||
nip = n*ip;
|
|
||||||
nkp = n*kp;
|
|
||||||
aa = A.data()+nkp+kp1;
|
|
||||||
for (int j = kp1; j < n; j++) {
|
|
||||||
ipj = nip + j;
|
|
||||||
A[ipj] = A[ipj] + em * *aa++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kpn = n * IPS[n-1] + n - 1; // last element of IPS[n] th row
|
|
||||||
if (A[kpn] == (bigfloat)0l) {
|
|
||||||
std::cout<<"simq A[kpn]=0\n";
|
|
||||||
return(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ip = IPS[0];
|
|
||||||
X[0] = B[ip];
|
|
||||||
for (int i = 1; i < n; i++) {
|
|
||||||
ip = IPS[i];
|
|
||||||
ipj = n * ip;
|
|
||||||
sum = 0.0;
|
|
||||||
for (int j = 0; j < i; j++) {
|
|
||||||
sum += A[ipj] * X[j];
|
|
||||||
++ipj;
|
|
||||||
}
|
|
||||||
X[i] = B[ip] - sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
ipn = n * IPS[n-1] + n - 1;
|
|
||||||
X[n-1] = X[n-1] / A[ipn];
|
|
||||||
|
|
||||||
for (int iback = 1; iback < n; iback++) {
|
|
||||||
//i goes (n-1),...,1
|
|
||||||
int i = nm1 - iback;
|
|
||||||
ip = IPS[i];
|
|
||||||
nip = n*ip;
|
|
||||||
sum = 0.0;
|
|
||||||
aa = A.data()+nip+i+1;
|
|
||||||
for (int j= i + 1; j < n; j++)
|
|
||||||
sum += *aa++ * X[j];
|
|
||||||
X[i] = (X[i] - sum) / A[nip+i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AlgRemezGeneral::csv(std::ostream & os) const{
|
|
||||||
os << "Numerator" << std::endl;
|
|
||||||
for(int i=0;i<=pow_n;i++){
|
|
||||||
os << getCoeffNum(i) << "*x^" << i;
|
|
||||||
if(i!=pow_n) os << " + ";
|
|
||||||
}
|
|
||||||
os << std::endl;
|
|
||||||
|
|
||||||
os << "Denominator" << std::endl;
|
|
||||||
for(int i=0;i<=pow_d;i++){
|
|
||||||
os << getCoeffDen(i) << "*x^" << i;
|
|
||||||
if(i!=pow_d) os << " + ";
|
|
||||||
}
|
|
||||||
os << std::endl;
|
|
||||||
|
|
||||||
//For a true minimax solution the errors should all be equal and the signs should oscillate +-+-+- etc
|
|
||||||
int sign;
|
|
||||||
os << "Errors at maxima: coordinate, error, (sign)" << std::endl;
|
|
||||||
for(int i=0;i<neq+1;i++){
|
|
||||||
os << mm[i] << " " << getErr(mm[i],&sign) << " (" << sign << ")" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
os << "Scan over range:" << std::endl;
|
|
||||||
int npt = 60;
|
|
||||||
bigfloat dlt = (apend - apstrt)/bigfloat(npt-1);
|
|
||||||
|
|
||||||
for (bigfloat x=apstrt; x<=apend; x = x + dlt) {
|
|
||||||
double f = evaluateFunc(x);
|
|
||||||
double r = evaluateApprox(x);
|
|
||||||
os<< x<<","<<r<<","<<f<<","<<r-f<<std::endl;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
/*
|
|
||||||
C.Kelly Jan 2020 based on implementation by M. Clark May 2005
|
|
||||||
|
|
||||||
AlgRemezGeneral is an implementation of the Remez algorithm for approximating an arbitrary function by a rational polynomial
|
|
||||||
It includes optional restriction to odd/even polynomials for the numerator and/or denominator
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef INCLUDED_ALG_REMEZ_GENERAL_H
|
|
||||||
#define INCLUDED_ALG_REMEZ_GENERAL_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <Grid/GridStd.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_LIBGMP
|
|
||||||
#include "bigfloat.h"
|
|
||||||
#else
|
|
||||||
#include "bigfloat_double.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
class AlgRemezGeneral{
|
|
||||||
public:
|
|
||||||
enum PolyType { Even, Odd, Full };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
// In GSL-style, pass the function as a function pointer. Any data required to evaluate the function is passed in as a void pointer
|
|
||||||
bigfloat (*f)(bigfloat x, void *data);
|
|
||||||
void *data;
|
|
||||||
|
|
||||||
// The approximation parameters
|
|
||||||
std::vector<bigfloat> param;
|
|
||||||
bigfloat norm;
|
|
||||||
|
|
||||||
// The number of non-zero terms in the numerator and denominator
|
|
||||||
int n, d;
|
|
||||||
// The numerator and denominator degree (i.e. the largest power)
|
|
||||||
int pow_n, pow_d;
|
|
||||||
|
|
||||||
// Specify if the numerator and/or denominator are odd/even polynomials
|
|
||||||
PolyType num_type;
|
|
||||||
PolyType den_type;
|
|
||||||
std::vector<int> num_pows; //contains the mapping, with -1 if not present
|
|
||||||
std::vector<int> den_pows;
|
|
||||||
|
|
||||||
// The bounds of the approximation
|
|
||||||
bigfloat apstrt, apwidt, apend;
|
|
||||||
|
|
||||||
// Variables used to calculate the approximation
|
|
||||||
int nd1, iter;
|
|
||||||
std::vector<bigfloat> xx;
|
|
||||||
std::vector<bigfloat> mm;
|
|
||||||
std::vector<bigfloat> step;
|
|
||||||
|
|
||||||
bigfloat delta, spread;
|
|
||||||
|
|
||||||
// Variables used in search
|
|
||||||
std::vector<bigfloat> yy;
|
|
||||||
|
|
||||||
// Variables used in solving linear equations
|
|
||||||
std::vector<bigfloat> A;
|
|
||||||
std::vector<bigfloat> B;
|
|
||||||
std::vector<int> IPS;
|
|
||||||
|
|
||||||
// The number of equations we must solve at each iteration (n+d+1)
|
|
||||||
int neq;
|
|
||||||
|
|
||||||
// The precision of the GNU MP library
|
|
||||||
long prec;
|
|
||||||
|
|
||||||
// Initialize member variables associated with the polynomial's properties
|
|
||||||
void setupPolyProperties(int num_degree, int den_degree, PolyType num_type_in, PolyType den_type_in);
|
|
||||||
|
|
||||||
// Initial values of maximal and minmal errors
|
|
||||||
void initialGuess();
|
|
||||||
|
|
||||||
// Initialise step sizes
|
|
||||||
void stpini();
|
|
||||||
|
|
||||||
// Initialize the algorithm
|
|
||||||
void reinitializeAlgorithm();
|
|
||||||
|
|
||||||
// Solve the equations
|
|
||||||
void equations();
|
|
||||||
|
|
||||||
// Search for error maxima and minima
|
|
||||||
void search();
|
|
||||||
|
|
||||||
// Calculate function required for the approximation
|
|
||||||
inline bigfloat func(bigfloat x) const{
|
|
||||||
return f(x, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute size and sign of the approximation error at x
|
|
||||||
bigfloat getErr(bigfloat x, int *sign) const;
|
|
||||||
|
|
||||||
// Solve the system AX=B where X = param
|
|
||||||
int simq();
|
|
||||||
|
|
||||||
// Evaluate the rational form P(x)/Q(x) using coefficients from the solution vector param
|
|
||||||
bigfloat approx(bigfloat x) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
AlgRemezGeneral(double lower, double upper, long prec,
|
|
||||||
bigfloat (*f)(bigfloat x, void *data), void *data);
|
|
||||||
|
|
||||||
inline int getDegree(void) const{
|
|
||||||
GRID_ASSERT(n==d);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
// Reset the bounds of the approximation
|
|
||||||
inline void setBounds(double lower, double upper) {
|
|
||||||
apstrt = lower;
|
|
||||||
apend = upper;
|
|
||||||
apwidt = apend - apstrt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the bounds of the approximation
|
|
||||||
inline void getBounds(double &lower, double &upper) const{
|
|
||||||
lower=(double)apstrt;
|
|
||||||
upper=(double)apend;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the algorithm to generate the rational approximation
|
|
||||||
double generateApprox(int num_degree, int den_degree,
|
|
||||||
PolyType num_type, PolyType den_type,
|
|
||||||
const double tolerance = 1e-15, const int report_freq = 1000);
|
|
||||||
|
|
||||||
inline double generateApprox(int num_degree, int den_degree,
|
|
||||||
const double tolerance = 1e-15, const int report_freq = 1000){
|
|
||||||
return generateApprox(num_degree, den_degree, Full, Full, tolerance, report_freq);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluate the rational form P(x)/Q(x) using coefficients from the
|
|
||||||
// solution vector param
|
|
||||||
inline double evaluateApprox(double x) const{
|
|
||||||
return (double)approx((bigfloat)x);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluate the rational form Q(x)/P(x) using coefficients from the solution vector param
|
|
||||||
inline double evaluateInverseApprox(double x) const{
|
|
||||||
return 1.0/(double)approx((bigfloat)x);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate function required for the approximation
|
|
||||||
inline double evaluateFunc(double x) const{
|
|
||||||
return (double)func((bigfloat)x);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate inverse function required for the approximation
|
|
||||||
inline double evaluateInverseFunc(double x) const{
|
|
||||||
return 1.0/(double)func((bigfloat)x);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump csv of function, approx and error
|
|
||||||
void csv(std::ostream &os = std::cout) const;
|
|
||||||
|
|
||||||
// Get the coefficient of the term x^i in the numerator
|
|
||||||
inline double getCoeffNum(const int i) const{
|
|
||||||
return num_pows[i] == -1 ? 0. : double(param[num_pows[i]]);
|
|
||||||
}
|
|
||||||
// Get the coefficient of the term x^i in the denominator
|
|
||||||
inline double getCoeffDen(const int i) const{
|
|
||||||
if(i == pow_d) return 1.0;
|
|
||||||
else return den_pows[i] == -1 ? 0. : double(param[den_pows[i]+n+1]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/approx/ZMobius.cc
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Christopher Kelly <ckelly@phys.columbia.edu>
|
|
||||||
|
|
||||||
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 */
|
|
||||||
|
|
||||||
#include <Grid/algorithms/approx/ZMobius.h>
|
|
||||||
#include <Grid/algorithms/approx/RemezGeneral.h>
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
NAMESPACE_BEGIN(Approx);
|
|
||||||
|
|
||||||
//Compute the tanh approximation
|
|
||||||
inline double epsilonMobius(const double x, const std::vector<ComplexD> &w){
|
|
||||||
int Ls = w.size();
|
|
||||||
|
|
||||||
ComplexD fxp = 1., fmp = 1.;
|
|
||||||
for(int i=0;i<Ls;i++){
|
|
||||||
fxp = fxp * ( w[i] + x );
|
|
||||||
fmp = fmp * ( w[i] - x );
|
|
||||||
}
|
|
||||||
return ((fxp - fmp)/(fxp + fmp)).real();
|
|
||||||
}
|
|
||||||
inline double epsilonMobius(const double x, const std::vector<RealD> &w){
|
|
||||||
int Ls = w.size();
|
|
||||||
|
|
||||||
double fxp = 1., fmp = 1.;
|
|
||||||
for(int i=0;i<Ls;i++){
|
|
||||||
fxp = fxp * ( w[i] + x );
|
|
||||||
fmp = fmp * ( w[i] - x );
|
|
||||||
}
|
|
||||||
return (fxp - fmp)/(fxp + fmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Compute the tanh approximation in a form suitable for the Remez
|
|
||||||
bigfloat epsilonMobius(bigfloat x, void* data){
|
|
||||||
const std::vector<RealD> &omega = *( (std::vector<RealD> const*)data );
|
|
||||||
bigfloat fxp(1.0);
|
|
||||||
bigfloat fmp(1.0);
|
|
||||||
|
|
||||||
for(int i=0;i<omega.size();i++){
|
|
||||||
fxp = fxp * ( bigfloat(omega[i]) + x);
|
|
||||||
fmp = fmp * ( bigfloat(omega[i]) - x);
|
|
||||||
}
|
|
||||||
return (fxp - fmp)/(fxp + fmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Compute the Zmobius Omega parameters suitable for eigenvalue range -lambda_bound <= lambda <= lambda_bound
|
|
||||||
//Note omega_i = 1/(b_i + c_i) where b_i and c_i are the Mobius parameters
|
|
||||||
void computeZmobiusOmega(std::vector<ComplexD> &omega_out, const int Ls_out,
|
|
||||||
const std::vector<RealD> &omega_in, const int Ls_in,
|
|
||||||
const RealD lambda_bound){
|
|
||||||
GRID_ASSERT(omega_in.size() == Ls_in);
|
|
||||||
omega_out.resize(Ls_out);
|
|
||||||
|
|
||||||
//Use the Remez algorithm to generate the appropriate rational polynomial
|
|
||||||
//For odd polynomial, to satisfy Haar condition must take either positive or negative half of range (cf https://arxiv.org/pdf/0803.0439.pdf page 6)
|
|
||||||
AlgRemezGeneral remez(0, lambda_bound, 64, &epsilonMobius, (void*)&omega_in);
|
|
||||||
remez.generateApprox(Ls_out-1, Ls_out,AlgRemezGeneral::Odd, AlgRemezGeneral::Even, 1e-15, 100);
|
|
||||||
remez.csv(std::cout);
|
|
||||||
|
|
||||||
//The rational approximation has the form [ f(x) - f(-x) ] / [ f(x) + f(-x) ] where f(x) = \Prod_{i=0}^{L_s-1} ( \omega_i + x )
|
|
||||||
//cf https://academiccommons.columbia.edu/doi/10.7916/D8T72HD7 pg 102
|
|
||||||
//omega_i are therefore the negative of the complex roots of f(x)
|
|
||||||
|
|
||||||
//We can find the roots by recognizing that the eigenvalues of a matrix A are the roots of the characteristic polynomial
|
|
||||||
// \rho(\lambda) = det( A - \lambda I ) where I is the unit matrix
|
|
||||||
//The matrix whose characteristic polynomial is an arbitrary monic polynomial a0 + a1 x + a2 x^2 + ... x^n is the companion matrix
|
|
||||||
// A = | 0 1 0 0 0 .... 0 |
|
|
||||||
// | 0 0 1 0 0 .... 0 |
|
|
||||||
// | : : : : : : |
|
|
||||||
// | 0 0 0 0 0 1
|
|
||||||
// | -a0 -a1 -a2 ... ... -an|
|
|
||||||
|
|
||||||
|
|
||||||
//Note the Remez defines the largest power to have unit coefficient
|
|
||||||
std::vector<RealD> coeffs(Ls_out+1);
|
|
||||||
for(int i=0;i<Ls_out+1;i+=2) coeffs[i] = coeffs[i] = remez.getCoeffDen(i); //even powers
|
|
||||||
for(int i=1;i<Ls_out+1;i+=2) coeffs[i] = coeffs[i] = remez.getCoeffNum(i); //odd powers
|
|
||||||
|
|
||||||
std::vector<std::complex<RealD> > roots(Ls_out);
|
|
||||||
|
|
||||||
//Form the companion matrix
|
|
||||||
Eigen::MatrixXd compn(Ls_out,Ls_out);
|
|
||||||
for(int i=0;i<Ls_out-1;i++) compn(i,0) = 0.;
|
|
||||||
compn(Ls_out - 1, 0) = -coeffs[0];
|
|
||||||
|
|
||||||
for(int j=1;j<Ls_out;j++){
|
|
||||||
for(int i=0;i<Ls_out-1;i++) compn(i,j) = i == j-1 ? 1. : 0.;
|
|
||||||
compn(Ls_out - 1, j) = -coeffs[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
//Eigensolve
|
|
||||||
Eigen::EigenSolver<Eigen::MatrixXd> slv(compn, false);
|
|
||||||
|
|
||||||
const auto & ev = slv.eigenvalues();
|
|
||||||
for(int i=0;i<Ls_out;i++)
|
|
||||||
omega_out[i] = -ev(i);
|
|
||||||
|
|
||||||
//Sort ascending (smallest at start of vector!)
|
|
||||||
std::sort(omega_out.begin(), omega_out.end(),
|
|
||||||
[&](const ComplexD &a, const ComplexD &b){ return a.real() < b.real() || (a.real() == b.real() && a.imag() < b.imag()); });
|
|
||||||
|
|
||||||
//McGlynn thesis pg 122 suggest improved iteration counts if magnitude of omega diminishes towards the center of the 5th dimension
|
|
||||||
std::vector<ComplexD> omega_tmp = omega_out;
|
|
||||||
int s_low=0, s_high=Ls_out-1, ss=0;
|
|
||||||
for(int s_from = Ls_out-1; s_from >= 0; s_from--){ //loop from largest omega
|
|
||||||
int s_to;
|
|
||||||
if(ss % 2 == 0){
|
|
||||||
s_to = s_low++;
|
|
||||||
}else{
|
|
||||||
s_to = s_high--;
|
|
||||||
}
|
|
||||||
omega_out[s_to] = omega_tmp[s_from];
|
|
||||||
++ss;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Resulting omega_i:" << std::endl;
|
|
||||||
for(int i=0;i<Ls_out;i++)
|
|
||||||
std::cout << omega_out[i] << std::endl;
|
|
||||||
|
|
||||||
std::cout << "Test result matches the approximate polynomial found by the Remez" << std::endl;
|
|
||||||
std::cout << "<x> <remez approx> <poly approx> <diff poly approx remez approx> <exact> <diff poly approx exact>\n";
|
|
||||||
|
|
||||||
int npt = 60;
|
|
||||||
double dlt = lambda_bound/double(npt-1);
|
|
||||||
|
|
||||||
for (int i =0; i<npt; i++){
|
|
||||||
double x = i*dlt;
|
|
||||||
double r = remez.evaluateApprox(x);
|
|
||||||
double p = epsilonMobius(x, omega_out);
|
|
||||||
double e = epsilonMobius(x, omega_in);
|
|
||||||
|
|
||||||
std::cout << x<< " " << r << " " << p <<" " <<r-p << " " << e << " " << e-p << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//mobius_param = b+c with b-c=1
|
|
||||||
void computeZmobiusOmega(std::vector<ComplexD> &omega_out, const int Ls_out, const RealD mobius_param, const int Ls_in, const RealD lambda_bound){
|
|
||||||
std::vector<RealD> omega_in(Ls_in, 1./mobius_param);
|
|
||||||
computeZmobiusOmega(omega_out, Ls_out, omega_in, Ls_in, lambda_bound);
|
|
||||||
}
|
|
||||||
|
|
||||||
//ZMobius class takes gamma_i = (b+c) omega_i as its input, where b, c are factored out
|
|
||||||
void computeZmobiusGamma(std::vector<ComplexD> &gamma_out,
|
|
||||||
const RealD mobius_param_out, const int Ls_out,
|
|
||||||
const RealD mobius_param_in, const int Ls_in,
|
|
||||||
const RealD lambda_bound){
|
|
||||||
computeZmobiusOmega(gamma_out, Ls_out, mobius_param_in, Ls_in, lambda_bound);
|
|
||||||
for(int i=0;i<Ls_out;i++) gamma_out[i] = gamma_out[i] * mobius_param_out;
|
|
||||||
}
|
|
||||||
//Assumes mobius_param_out == mobius_param_in
|
|
||||||
void computeZmobiusGamma(std::vector<ComplexD> &gamma_out, const int Ls_out, const RealD mobius_param, const int Ls_in, const RealD lambda_bound){
|
|
||||||
computeZmobiusGamma(gamma_out, mobius_param, Ls_out, mobius_param, Ls_in, lambda_bound);
|
|
||||||
}
|
|
||||||
|
|
||||||
NAMESPACE_END(Approx);
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/approx/ZMobius.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Christopher Kelly <ckelly@phys.columbia.edu>
|
|
||||||
|
|
||||||
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_ZMOBIUS_APPROX_H
|
|
||||||
#define GRID_ZMOBIUS_APPROX_H
|
|
||||||
|
|
||||||
#include <Grid/GridCore.h>
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
NAMESPACE_BEGIN(Approx);
|
|
||||||
|
|
||||||
//Compute the Zmobius Omega parameters suitable for eigenvalue range -lambda_bound <= lambda <= lambda_bound
|
|
||||||
//Note omega_i = 1/(b_i + c_i) where b_i and c_i are the Mobius parameters
|
|
||||||
void computeZmobiusOmega(std::vector<ComplexD> &omega_out, const int Ls_out,
|
|
||||||
const std::vector<RealD> &omega_in, const int Ls_in,
|
|
||||||
const RealD lambda_bound);
|
|
||||||
|
|
||||||
//mobius_param = b+c with b-c=1
|
|
||||||
void computeZmobiusOmega(std::vector<ComplexD> &omega_out, const int Ls_out, const RealD mobius_param, const int Ls_in, const RealD lambda_bound);
|
|
||||||
|
|
||||||
//ZMobius class takes gamma_i = (b+c) omega_i as its input, where b, c are factored out
|
|
||||||
void computeZmobiusGamma(std::vector<ComplexD> &gamma_out,
|
|
||||||
const RealD mobius_param_out, const int Ls_out,
|
|
||||||
const RealD mobius_param_in, const int Ls_in,
|
|
||||||
const RealD lambda_bound);
|
|
||||||
|
|
||||||
//Assumes mobius_param_out == mobius_param_in
|
|
||||||
void computeZmobiusGamma(std::vector<ComplexD> &gamma_out, const int Ls_out, const RealD mobius_param, const int Ls_in, const RealD lambda_bound);
|
|
||||||
|
|
||||||
NAMESPACE_END(Approx);
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -58,8 +58,8 @@
|
|||||||
|
|
||||||
/* Compute the partial fraction expansion coefficients (alpha) from the
|
/* Compute the partial fraction expansion coefficients (alpha) from the
|
||||||
* factored form */
|
* factored form */
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
NAMESPACE_BEGIN(Approx);
|
namespace Approx {
|
||||||
|
|
||||||
static void construct_partfrac(izd *z) {
|
static void construct_partfrac(izd *z) {
|
||||||
int dn = z -> dn, dd = z -> dd, type = z -> type;
|
int dn = z -> dn, dd = z -> dd, type = z -> type;
|
||||||
@@ -293,7 +293,7 @@ static void sncndnFK(INTERNAL_PRECISION u, INTERNAL_PRECISION k,
|
|||||||
* Set type = 0 for the Zolotarev approximation, which is zero at x = 0, and
|
* Set type = 0 for the Zolotarev approximation, which is zero at x = 0, and
|
||||||
* type = 1 for the approximation which is infinite at x = 0. */
|
* type = 1 for the approximation which is infinite at x = 0. */
|
||||||
|
|
||||||
zolotarev_data* zolotarev(ZOLO_PRECISION epsilon, int n, int type) {
|
zolotarev_data* zolotarev(PRECISION epsilon, int n, int type) {
|
||||||
INTERNAL_PRECISION A, c, cp, kp, ksq, sn, cn, dn, Kp, Kj, z, z0, t, M, F,
|
INTERNAL_PRECISION A, c, cp, kp, ksq, sn, cn, dn, Kp, Kj, z, z0, t, M, F,
|
||||||
l, invlambda, xi, xisq, *tv, s, opl;
|
l, invlambda, xi, xisq, *tv, s, opl;
|
||||||
int m, czero, ts;
|
int m, czero, ts;
|
||||||
@@ -375,12 +375,12 @@ zolotarev_data* zolotarev(ZOLO_PRECISION epsilon, int n, int type) {
|
|||||||
construct_partfrac(d);
|
construct_partfrac(d);
|
||||||
construct_contfrac(d);
|
construct_contfrac(d);
|
||||||
|
|
||||||
/* Converting everything to ZOLO_PRECISION for external use only */
|
/* Converting everything to PRECISION for external use only */
|
||||||
|
|
||||||
zd = (zolotarev_data*) malloc(sizeof(zolotarev_data));
|
zd = (zolotarev_data*) malloc(sizeof(zolotarev_data));
|
||||||
zd -> A = (ZOLO_PRECISION) d -> A;
|
zd -> A = (PRECISION) d -> A;
|
||||||
zd -> Delta = (ZOLO_PRECISION) d -> Delta;
|
zd -> Delta = (PRECISION) d -> Delta;
|
||||||
zd -> epsilon = (ZOLO_PRECISION) d -> epsilon;
|
zd -> epsilon = (PRECISION) d -> epsilon;
|
||||||
zd -> n = d -> n;
|
zd -> n = d -> n;
|
||||||
zd -> type = d -> type;
|
zd -> type = d -> type;
|
||||||
zd -> dn = d -> dn;
|
zd -> dn = d -> dn;
|
||||||
@@ -390,24 +390,24 @@ zolotarev_data* zolotarev(ZOLO_PRECISION epsilon, int n, int type) {
|
|||||||
zd -> deg_num = d -> deg_num;
|
zd -> deg_num = d -> deg_num;
|
||||||
zd -> deg_denom = d -> deg_denom;
|
zd -> deg_denom = d -> deg_denom;
|
||||||
|
|
||||||
zd -> a = (ZOLO_PRECISION*) malloc(zd -> dn * sizeof(ZOLO_PRECISION));
|
zd -> a = (PRECISION*) malloc(zd -> dn * sizeof(PRECISION));
|
||||||
for (m = 0; m < zd -> dn; m++) zd -> a[m] = (ZOLO_PRECISION) d -> a[m];
|
for (m = 0; m < zd -> dn; m++) zd -> a[m] = (PRECISION) d -> a[m];
|
||||||
free(d -> a);
|
free(d -> a);
|
||||||
|
|
||||||
zd -> ap = (ZOLO_PRECISION*) malloc(zd -> dd * sizeof(ZOLO_PRECISION));
|
zd -> ap = (PRECISION*) malloc(zd -> dd * sizeof(PRECISION));
|
||||||
for (m = 0; m < zd -> dd; m++) zd -> ap[m] = (ZOLO_PRECISION) d -> ap[m];
|
for (m = 0; m < zd -> dd; m++) zd -> ap[m] = (PRECISION) d -> ap[m];
|
||||||
free(d -> ap);
|
free(d -> ap);
|
||||||
|
|
||||||
zd -> alpha = (ZOLO_PRECISION*) malloc(zd -> da * sizeof(ZOLO_PRECISION));
|
zd -> alpha = (PRECISION*) malloc(zd -> da * sizeof(PRECISION));
|
||||||
for (m = 0; m < zd -> da; m++) zd -> alpha[m] = (ZOLO_PRECISION) d -> alpha[m];
|
for (m = 0; m < zd -> da; m++) zd -> alpha[m] = (PRECISION) d -> alpha[m];
|
||||||
free(d -> alpha);
|
free(d -> alpha);
|
||||||
|
|
||||||
zd -> beta = (ZOLO_PRECISION*) malloc(zd -> db * sizeof(ZOLO_PRECISION));
|
zd -> beta = (PRECISION*) malloc(zd -> db * sizeof(PRECISION));
|
||||||
for (m = 0; m < zd -> db; m++) zd -> beta[m] = (ZOLO_PRECISION) d -> beta[m];
|
for (m = 0; m < zd -> db; m++) zd -> beta[m] = (PRECISION) d -> beta[m];
|
||||||
free(d -> beta);
|
free(d -> beta);
|
||||||
|
|
||||||
zd -> gamma = (ZOLO_PRECISION*) malloc(zd -> n * sizeof(ZOLO_PRECISION));
|
zd -> gamma = (PRECISION*) malloc(zd -> n * sizeof(PRECISION));
|
||||||
for (m = 0; m < zd -> n; m++) zd -> gamma[m] = (ZOLO_PRECISION) d -> gamma[m];
|
for (m = 0; m < zd -> n; m++) zd -> gamma[m] = (PRECISION) d -> gamma[m];
|
||||||
free(d -> gamma);
|
free(d -> gamma);
|
||||||
|
|
||||||
free(d);
|
free(d);
|
||||||
@@ -426,7 +426,7 @@ void zolotarev_free(zolotarev_data *zdata)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
zolotarev_data* higham(ZOLO_PRECISION epsilon, int n) {
|
zolotarev_data* higham(PRECISION epsilon, int n) {
|
||||||
INTERNAL_PRECISION A, M, c, cp, z, z0, t, epssq;
|
INTERNAL_PRECISION A, M, c, cp, z, z0, t, epssq;
|
||||||
int m, czero;
|
int m, czero;
|
||||||
zolotarev_data *zd;
|
zolotarev_data *zd;
|
||||||
@@ -481,9 +481,9 @@ zolotarev_data* higham(ZOLO_PRECISION epsilon, int n) {
|
|||||||
/* Converting everything to PRECISION for external use only */
|
/* Converting everything to PRECISION for external use only */
|
||||||
|
|
||||||
zd = (zolotarev_data*) malloc(sizeof(zolotarev_data));
|
zd = (zolotarev_data*) malloc(sizeof(zolotarev_data));
|
||||||
zd -> A = (ZOLO_PRECISION) d -> A;
|
zd -> A = (PRECISION) d -> A;
|
||||||
zd -> Delta = (ZOLO_PRECISION) d -> Delta;
|
zd -> Delta = (PRECISION) d -> Delta;
|
||||||
zd -> epsilon = (ZOLO_PRECISION) d -> epsilon;
|
zd -> epsilon = (PRECISION) d -> epsilon;
|
||||||
zd -> n = d -> n;
|
zd -> n = d -> n;
|
||||||
zd -> type = d -> type;
|
zd -> type = d -> type;
|
||||||
zd -> dn = d -> dn;
|
zd -> dn = d -> dn;
|
||||||
@@ -493,47 +493,45 @@ zolotarev_data* higham(ZOLO_PRECISION epsilon, int n) {
|
|||||||
zd -> deg_num = d -> deg_num;
|
zd -> deg_num = d -> deg_num;
|
||||||
zd -> deg_denom = d -> deg_denom;
|
zd -> deg_denom = d -> deg_denom;
|
||||||
|
|
||||||
zd -> a = (ZOLO_PRECISION*) malloc(zd -> dn * sizeof(ZOLO_PRECISION));
|
zd -> a = (PRECISION*) malloc(zd -> dn * sizeof(PRECISION));
|
||||||
for (m = 0; m < zd -> dn; m++) zd -> a[m] = (ZOLO_PRECISION) d -> a[m];
|
for (m = 0; m < zd -> dn; m++) zd -> a[m] = (PRECISION) d -> a[m];
|
||||||
free(d -> a);
|
free(d -> a);
|
||||||
|
|
||||||
zd -> ap = (ZOLO_PRECISION*) malloc(zd -> dd * sizeof(ZOLO_PRECISION));
|
zd -> ap = (PRECISION*) malloc(zd -> dd * sizeof(PRECISION));
|
||||||
for (m = 0; m < zd -> dd; m++) zd -> ap[m] = (ZOLO_PRECISION) d -> ap[m];
|
for (m = 0; m < zd -> dd; m++) zd -> ap[m] = (PRECISION) d -> ap[m];
|
||||||
free(d -> ap);
|
free(d -> ap);
|
||||||
|
|
||||||
zd -> alpha = (ZOLO_PRECISION*) malloc(zd -> da * sizeof(ZOLO_PRECISION));
|
zd -> alpha = (PRECISION*) malloc(zd -> da * sizeof(PRECISION));
|
||||||
for (m = 0; m < zd -> da; m++) zd -> alpha[m] = (ZOLO_PRECISION) d -> alpha[m];
|
for (m = 0; m < zd -> da; m++) zd -> alpha[m] = (PRECISION) d -> alpha[m];
|
||||||
free(d -> alpha);
|
free(d -> alpha);
|
||||||
|
|
||||||
zd -> beta = (ZOLO_PRECISION*) malloc(zd -> db * sizeof(ZOLO_PRECISION));
|
zd -> beta = (PRECISION*) malloc(zd -> db * sizeof(PRECISION));
|
||||||
for (m = 0; m < zd -> db; m++) zd -> beta[m] = (ZOLO_PRECISION) d -> beta[m];
|
for (m = 0; m < zd -> db; m++) zd -> beta[m] = (PRECISION) d -> beta[m];
|
||||||
free(d -> beta);
|
free(d -> beta);
|
||||||
|
|
||||||
zd -> gamma = (ZOLO_PRECISION*) malloc(zd -> n * sizeof(ZOLO_PRECISION));
|
zd -> gamma = (PRECISION*) malloc(zd -> n * sizeof(PRECISION));
|
||||||
for (m = 0; m < zd -> n; m++) zd -> gamma[m] = (ZOLO_PRECISION) d -> gamma[m];
|
for (m = 0; m < zd -> n; m++) zd -> gamma[m] = (PRECISION) d -> gamma[m];
|
||||||
free(d -> gamma);
|
free(d -> gamma);
|
||||||
|
|
||||||
free(d);
|
free(d);
|
||||||
return zd;
|
return zd;
|
||||||
}
|
}
|
||||||
|
}}
|
||||||
NAMESPACE_END(Approx);
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
#ifdef TEST
|
#ifdef TEST
|
||||||
|
|
||||||
#undef ZERO
|
#undef ZERO
|
||||||
#define ZERO ((ZOLO_PRECISION) 0)
|
#define ZERO ((PRECISION) 0)
|
||||||
#undef ONE
|
#undef ONE
|
||||||
#define ONE ((ZOLO_PRECISION) 1)
|
#define ONE ((PRECISION) 1)
|
||||||
#undef TWO
|
#undef TWO
|
||||||
#define TWO ((ZOLO_PRECISION) 2)
|
#define TWO ((PRECISION) 2)
|
||||||
|
|
||||||
/* Evaluate the rational approximation R(x) using the factored form */
|
/* Evaluate the rational approximation R(x) using the factored form */
|
||||||
|
|
||||||
static ZOLO_PRECISION zolotarev_eval(ZOLO_PRECISION x, zolotarev_data* rdata) {
|
static PRECISION zolotarev_eval(PRECISION x, zolotarev_data* rdata) {
|
||||||
int m;
|
int m;
|
||||||
ZOLO_PRECISION R;
|
PRECISION R;
|
||||||
|
|
||||||
if (rdata -> type == 0) {
|
if (rdata -> type == 0) {
|
||||||
R = rdata -> A * x;
|
R = rdata -> A * x;
|
||||||
@@ -551,9 +549,9 @@ static ZOLO_PRECISION zolotarev_eval(ZOLO_PRECISION x, zolotarev_data* rdata) {
|
|||||||
|
|
||||||
/* Evaluate the rational approximation R(x) using the partial fraction form */
|
/* Evaluate the rational approximation R(x) using the partial fraction form */
|
||||||
|
|
||||||
static ZOLO_PRECISION zolotarev_partfrac_eval(ZOLO_PRECISION x, zolotarev_data* rdata) {
|
static PRECISION zolotarev_partfrac_eval(PRECISION x, zolotarev_data* rdata) {
|
||||||
int m;
|
int m;
|
||||||
ZOLO_PRECISION R = rdata -> alpha[rdata -> da - 1];
|
PRECISION R = rdata -> alpha[rdata -> da - 1];
|
||||||
for (m = 0; m < rdata -> dd; m++)
|
for (m = 0; m < rdata -> dd; m++)
|
||||||
R += rdata -> alpha[m] / (x * x - rdata -> ap[m]);
|
R += rdata -> alpha[m] / (x * x - rdata -> ap[m]);
|
||||||
if (rdata -> type == 1) R += rdata -> alpha[rdata -> dd] / (x * x);
|
if (rdata -> type == 1) R += rdata -> alpha[rdata -> dd] / (x * x);
|
||||||
@@ -568,18 +566,18 @@ static ZOLO_PRECISION zolotarev_partfrac_eval(ZOLO_PRECISION x, zolotarev_data*
|
|||||||
* non-signalling overflow this will work correctly since 1/(1/0) = 1/INF = 0,
|
* non-signalling overflow this will work correctly since 1/(1/0) = 1/INF = 0,
|
||||||
* but with signalling overflow you will get an error message. */
|
* but with signalling overflow you will get an error message. */
|
||||||
|
|
||||||
static ZOLO_PRECISION zolotarev_contfrac_eval(ZOLO_PRECISION x, zolotarev_data* rdata) {
|
static PRECISION zolotarev_contfrac_eval(PRECISION x, zolotarev_data* rdata) {
|
||||||
int m;
|
int m;
|
||||||
ZOLO_PRECISION R = rdata -> beta[0] * x;
|
PRECISION R = rdata -> beta[0] * x;
|
||||||
for (m = 1; m < rdata -> db; m++) R = rdata -> beta[m] * x + ONE / R;
|
for (m = 1; m < rdata -> db; m++) R = rdata -> beta[m] * x + ONE / R;
|
||||||
return R;
|
return R;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Evaluate the rational approximation R(x) using Cayley form */
|
/* Evaluate the rational approximation R(x) using Cayley form */
|
||||||
|
|
||||||
static ZOLO_PRECISION zolotarev_cayley_eval(ZOLO_PRECISION x, zolotarev_data* rdata) {
|
static PRECISION zolotarev_cayley_eval(PRECISION x, zolotarev_data* rdata) {
|
||||||
int m;
|
int m;
|
||||||
ZOLO_PRECISION T;
|
PRECISION T;
|
||||||
|
|
||||||
T = rdata -> type == 0 ? ONE : -ONE;
|
T = rdata -> type == 0 ? ONE : -ONE;
|
||||||
for (m = 0; m < rdata -> n; m++)
|
for (m = 0; m < rdata -> n; m++)
|
||||||
@@ -587,7 +585,6 @@ static ZOLO_PRECISION zolotarev_cayley_eval(ZOLO_PRECISION x, zolotarev_data* rd
|
|||||||
return (ONE - T) / (ONE + T);
|
return (ONE - T) / (ONE + T);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Test program. Apart from printing out the parameters for R(x) it produces
|
/* Test program. Apart from printing out the parameters for R(x) it produces
|
||||||
* the following data files for plotting (unless NPLOT is defined):
|
* the following data files for plotting (unless NPLOT is defined):
|
||||||
*
|
*
|
||||||
@@ -607,7 +604,7 @@ int main(int argc, char** argv) {
|
|||||||
int m, n, plotpts = 5000, type = 0;
|
int m, n, plotpts = 5000, type = 0;
|
||||||
float eps, x, ypferr, ycferr, ycaylerr, maxypferr, maxycferr, maxycaylerr;
|
float eps, x, ypferr, ycferr, ycaylerr, maxypferr, maxycferr, maxycaylerr;
|
||||||
zolotarev_data *rdata;
|
zolotarev_data *rdata;
|
||||||
ZOLO_PRECISION y;
|
PRECISION y;
|
||||||
FILE *plot_function, *plot_error,
|
FILE *plot_function, *plot_error,
|
||||||
*plot_partfrac, *plot_contfrac, *plot_cayley;
|
*plot_partfrac, *plot_contfrac, *plot_cayley;
|
||||||
|
|
||||||
@@ -626,13 +623,13 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rdata = type == 2
|
rdata = type == 2
|
||||||
? higham((ZOLO_PRECISION) eps, n)
|
? higham((PRECISION) eps, n)
|
||||||
: zolotarev((ZOLO_PRECISION) eps, n, type);
|
: zolotarev((PRECISION) eps, n, type);
|
||||||
|
|
||||||
printf("Zolotarev Test: R(epsilon = %g, n = %d, type = %d)\n\t"
|
printf("Zolotarev Test: R(epsilon = %g, n = %d, type = %d)\n\t"
|
||||||
STRINGIFY(VERSION) "\n\t" STRINGIFY(HVERSION)
|
STRINGIFY(VERSION) "\n\t" STRINGIFY(HVERSION)
|
||||||
"\n\tINTERNAL_PRECISION = " STRINGIFY(INTERNAL_PRECISION)
|
"\n\tINTERNAL_PRECISION = " STRINGIFY(INTERNAL_PRECISION)
|
||||||
"\tZOLO_PRECISION = " STRINGIFY(ZOLO_PRECISION)
|
"\tPRECISION = " STRINGIFY(PRECISION)
|
||||||
"\n\n\tRational approximation of degree (%d,%d), %s at x = 0\n"
|
"\n\n\tRational approximation of degree (%d,%d), %s at x = 0\n"
|
||||||
"\tDelta = %g (maximum error)\n\n"
|
"\tDelta = %g (maximum error)\n\n"
|
||||||
"\tA = %g (overall factor)\n",
|
"\tA = %g (overall factor)\n",
|
||||||
@@ -681,15 +678,15 @@ int main(int argc, char** argv) {
|
|||||||
x = 2.4 * (float) m / plotpts - 1.2;
|
x = 2.4 * (float) m / plotpts - 1.2;
|
||||||
if (rdata -> type == 0 || fabs(x) * (float) plotpts > 1.0) {
|
if (rdata -> type == 0 || fabs(x) * (float) plotpts > 1.0) {
|
||||||
/* skip x = 0 for type 1, as R(0) is singular */
|
/* skip x = 0 for type 1, as R(0) is singular */
|
||||||
y = zolotarev_eval((ZOLO_PRECISION) x, rdata);
|
y = zolotarev_eval((PRECISION) x, rdata);
|
||||||
fprintf(plot_function, "%g %g\n", x, (float) y);
|
fprintf(plot_function, "%g %g\n", x, (float) y);
|
||||||
fprintf(plot_error, "%g %g\n",
|
fprintf(plot_error, "%g %g\n",
|
||||||
x, (float)((y - ((x > 0.0 ? ONE : -ONE))) / rdata -> Delta));
|
x, (float)((y - ((x > 0.0 ? ONE : -ONE))) / rdata -> Delta));
|
||||||
ypferr = (float)((zolotarev_partfrac_eval((ZOLO_PRECISION) x, rdata) - y)
|
ypferr = (float)((zolotarev_partfrac_eval((PRECISION) x, rdata) - y)
|
||||||
/ rdata -> Delta);
|
/ rdata -> Delta);
|
||||||
ycferr = (float)((zolotarev_contfrac_eval((ZOLO_PRECISION) x, rdata) - y)
|
ycferr = (float)((zolotarev_contfrac_eval((PRECISION) x, rdata) - y)
|
||||||
/ rdata -> Delta);
|
/ rdata -> Delta);
|
||||||
ycaylerr = (float)((zolotarev_cayley_eval((ZOLO_PRECISION) x, rdata) - y)
|
ycaylerr = (float)((zolotarev_cayley_eval((PRECISION) x, rdata) - y)
|
||||||
/ rdata -> Delta);
|
/ rdata -> Delta);
|
||||||
if (fabs(x) < 1.0 && fabs(x) > rdata -> epsilon) {
|
if (fabs(x) < 1.0 && fabs(x) > rdata -> epsilon) {
|
||||||
maxypferr = MAX(maxypferr, fabs(ypferr));
|
maxypferr = MAX(maxypferr, fabs(ypferr));
|
||||||
@@ -726,5 +723,5 @@ int main(int argc, char** argv) {
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* TEST */
|
|
||||||
|
|
||||||
|
#endif /* TEST */
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
/* -*- Mode: C; comment-column: 22; fill-column: 79; -*- */
|
/* -*- Mode: C; comment-column: 22; fill-column: 79; -*- */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#include <Grid/Namespace.h>
|
namespace Grid {
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Approx {
|
||||||
NAMESPACE_BEGIN(Approx);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define HVERSION Header Time-stamp: <14-OCT-2004 09:26:51.00 adk@MISSCONTRARY>
|
#define HVERSION Header Time-stamp: <14-OCT-2004 09:26:51.00 adk@MISSCONTRARY>
|
||||||
|
|
||||||
|
|
||||||
#ifndef ZOLOTAREV_INTERNAL
|
#ifndef ZOLOTAREV_INTERNAL
|
||||||
#ifndef ZOLO_PRECISION
|
#ifndef PRECISION
|
||||||
#define ZOLO_PRECISION double
|
#define PRECISION double
|
||||||
#endif
|
#endif
|
||||||
#define ZPRECISION ZOLO_PRECISION
|
#define ZPRECISION PRECISION
|
||||||
#define ZOLOTAREV_DATA zolotarev_data
|
#define ZOLOTAREV_DATA zolotarev_data
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -77,13 +77,11 @@ typedef struct {
|
|||||||
* zolotarev_data structure. The arguments must satisfy the constraints that
|
* zolotarev_data structure. The arguments must satisfy the constraints that
|
||||||
* epsilon > 0, n > 0, and type = 0 or 1. */
|
* epsilon > 0, n > 0, and type = 0 or 1. */
|
||||||
|
|
||||||
ZOLOTAREV_DATA* higham(ZOLO_PRECISION epsilon, int n) ;
|
ZOLOTAREV_DATA* higham(PRECISION epsilon, int n) ;
|
||||||
ZOLOTAREV_DATA* zolotarev(ZOLO_PRECISION epsilon, int n, int type);
|
ZOLOTAREV_DATA* zolotarev(PRECISION epsilon, int n, int type);
|
||||||
void zolotarev_free(zolotarev_data *zdata);
|
void zolotarev_free(zolotarev_data *zdata);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
NAMESPACE_END(Approx);
|
}}
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -10,12 +10,10 @@
|
|||||||
#ifndef INCLUDED_BIGFLOAT_H
|
#ifndef INCLUDED_BIGFLOAT_H
|
||||||
#define INCLUDED_BIGFLOAT_H
|
#define INCLUDED_BIGFLOAT_H
|
||||||
|
|
||||||
#define __GMP_WITHIN_CONFIGURE
|
|
||||||
#include <gmp.h>
|
#include <gmp.h>
|
||||||
#include <mpf2mpfr.h>
|
#include <mpf2mpfr.h>
|
||||||
#include <mpfr.h>
|
#include <mpfr.h>
|
||||||
#undef __GMP_WITHIN_CONFIGURE
|
|
||||||
|
|
||||||
class bigfloat {
|
class bigfloat {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -25,10 +25,6 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
|
|
||||||
#ifndef INCLUDED_BIGFLOAT_DOUBLE_H
|
|
||||||
#define INCLUDED_BIGFLOAT_DOUBLE_H
|
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
typedef double mfloat;
|
typedef double mfloat;
|
||||||
@@ -190,6 +186,4 @@ public:
|
|||||||
// friend bigfloat& random(void);
|
// friend bigfloat& random(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: BatchedBlas.h
|
|
||||||
|
|
||||||
Copyright (C) 2023
|
|
||||||
|
|
||||||
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 */
|
|
||||||
#include <Grid/GridCore.h>
|
|
||||||
#include <Grid/algorithms/blas/BatchedBlas.h>
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
gridblasHandle_t GridBLAS::gridblasHandle;
|
|
||||||
int GridBLAS::gridblasInit;
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -1,282 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
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);
|
|
||||||
@@ -1,300 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: MomentumProject.h
|
|
||||||
|
|
||||||
Copyright (C) 2025
|
|
||||||
|
|
||||||
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);
|
|
||||||
/*
|
|
||||||
MultiMomProject
|
|
||||||
|
|
||||||
Import vectors -> nxyz x (ncomponent x nt)
|
|
||||||
Import complex phases -> nmom x nxy
|
|
||||||
|
|
||||||
apply = via (possibly batched) GEMM
|
|
||||||
*/
|
|
||||||
template<class Field, class ComplexField>
|
|
||||||
class MomentumProject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef typename Field::scalar_type scalar;
|
|
||||||
typedef typename Field::scalar_object scalar_object;
|
|
||||||
|
|
||||||
GridBase *grid;
|
|
||||||
uint64_t nmom;
|
|
||||||
uint64_t nxyz;
|
|
||||||
uint64_t nt;
|
|
||||||
uint64_t nbtw;
|
|
||||||
uint64_t words;
|
|
||||||
|
|
||||||
deviceVector<scalar> BLAS_V; //
|
|
||||||
deviceVector<scalar> BLAS_M; //
|
|
||||||
deviceVector<scalar> BLAS_P; //
|
|
||||||
|
|
||||||
MomentumProject(){};
|
|
||||||
~MomentumProject(){ Deallocate(); };
|
|
||||||
|
|
||||||
void Deallocate(void)
|
|
||||||
{
|
|
||||||
grid=nullptr;
|
|
||||||
nmom=0;
|
|
||||||
nxyz=0;
|
|
||||||
nt=0;
|
|
||||||
nbtw=0;
|
|
||||||
words=0;
|
|
||||||
BLAS_V.resize(0);
|
|
||||||
BLAS_M.resize(0);
|
|
||||||
BLAS_P.resize(0);
|
|
||||||
}
|
|
||||||
void Allocate(int _nmom,GridBase *_grid)
|
|
||||||
{
|
|
||||||
grid=_grid;
|
|
||||||
Coordinate ldims = grid->LocalDimensions();
|
|
||||||
|
|
||||||
nmom=_nmom;
|
|
||||||
nt = ldims[grid->Nd()-1];
|
|
||||||
nxyz = grid->lSites()/nt;
|
|
||||||
words = sizeof(scalar_object)/sizeof(scalar);
|
|
||||||
nbtw = nt * words;
|
|
||||||
|
|
||||||
BLAS_V.resize (nxyz * nt * words );
|
|
||||||
BLAS_M.resize (nmom * nxyz );
|
|
||||||
BLAS_P.resize (nmom * nt * words );
|
|
||||||
}
|
|
||||||
void ImportMomenta(const std::vector <ComplexField> &momenta)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(momenta.size()==nmom);
|
|
||||||
// might as well just make the momenta here
|
|
||||||
typedef typename Field::vector_object vobj;
|
|
||||||
|
|
||||||
int nd = grid->_ndimension;
|
|
||||||
|
|
||||||
uint64_t sz = BLAS_M.size();
|
|
||||||
|
|
||||||
GRID_ASSERT(momenta.size()==nmom)
|
|
||||||
GRID_ASSERT(momenta[0].Grid()==grid);
|
|
||||||
GRID_ASSERT(sz = nxyz * nmom);
|
|
||||||
|
|
||||||
Coordinate rdimensions = grid->_rdimensions;
|
|
||||||
Coordinate ldims = grid->LocalDimensions();
|
|
||||||
int64_t osites = grid->oSites();
|
|
||||||
Coordinate simd = grid->_simd_layout;
|
|
||||||
const int Nsimd = vobj::Nsimd();
|
|
||||||
uint64_t lwords = words; // local variable for copy in to GPU
|
|
||||||
int64_t Nxyz = nxyz;
|
|
||||||
auto blasData_p = &BLAS_M[0];
|
|
||||||
for(int m=0;m<momenta.size();m++){
|
|
||||||
|
|
||||||
autoView( Data , momenta[m], AcceleratorRead);
|
|
||||||
auto Data_p = &Data[0];
|
|
||||||
|
|
||||||
accelerator_for(xyz,nxyz,1,{
|
|
||||||
//////////////////////////////////////////
|
|
||||||
// isite -- map lane within buffer to lane within lattice
|
|
||||||
////////////////////////////////////////////
|
|
||||||
Coordinate lcoor(nd,0);
|
|
||||||
Lexicographic::CoorFromIndex(lcoor,xyz,ldims);
|
|
||||||
|
|
||||||
Coordinate icoor(nd);
|
|
||||||
Coordinate ocoor(nd);
|
|
||||||
for (int d = 0; d < nd; d++) {
|
|
||||||
icoor[d] = lcoor[d]/rdimensions[d];
|
|
||||||
ocoor[d] = lcoor[d]%rdimensions[d];
|
|
||||||
}
|
|
||||||
int64_t osite;
|
|
||||||
int64_t isite;
|
|
||||||
Lexicographic::IndexFromCoor(ocoor,osite,rdimensions);
|
|
||||||
Lexicographic::IndexFromCoor(icoor,isite,simd);
|
|
||||||
|
|
||||||
// BLAS_M[nmom][slice_vol]
|
|
||||||
// Fortran Column major BLAS layout is M_xyz,mom
|
|
||||||
scalar data = extractLane(isite,Data[osite]);
|
|
||||||
uint64_t idx = xyz+m*Nxyz;
|
|
||||||
blasData_p[idx] = data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void ImportVector(Field &vec)
|
|
||||||
{
|
|
||||||
typedef typename Field::vector_object vobj;
|
|
||||||
|
|
||||||
int nd = grid->_ndimension;
|
|
||||||
|
|
||||||
uint64_t sz = BLAS_V.size();
|
|
||||||
|
|
||||||
GRID_ASSERT(sz = nxyz * words * nt);
|
|
||||||
|
|
||||||
Coordinate rdimensions = grid->_rdimensions;
|
|
||||||
Coordinate ldims= grid->LocalDimensions();
|
|
||||||
int64_t osites = grid->oSites();
|
|
||||||
Coordinate simd = grid->_simd_layout;
|
|
||||||
const int Nsimd = vobj::Nsimd();
|
|
||||||
uint64_t lwords= words; // local variable for copy in to GPU
|
|
||||||
|
|
||||||
auto blasData_p = &BLAS_V[0];
|
|
||||||
autoView( Data , vec, AcceleratorRead);
|
|
||||||
auto Data_p = &Data[0];
|
|
||||||
|
|
||||||
int64_t nwords = words;// for capture
|
|
||||||
int64_t Nt = nt;// for capture
|
|
||||||
|
|
||||||
accelerator_for(sf,osites,Nsimd,{
|
|
||||||
#ifdef GRID_SIMT
|
|
||||||
{
|
|
||||||
int lane=acceleratorSIMTlane(Nsimd); // buffer lane
|
|
||||||
#else
|
|
||||||
for(int lane=0;lane<Nsimd;lane++) {
|
|
||||||
#endif
|
|
||||||
//////////////////////////////////////////
|
|
||||||
// isite -- map lane within buffer to lane within lattice
|
|
||||||
////////////////////////////////////////////
|
|
||||||
Coordinate lcoor(nd,0);
|
|
||||||
Coordinate icoor(nd);
|
|
||||||
Coordinate ocoor(nd);
|
|
||||||
|
|
||||||
Lexicographic::CoorFromIndex(icoor,lane,simd);
|
|
||||||
Lexicographic::CoorFromIndex(ocoor,sf,rdimensions);
|
|
||||||
|
|
||||||
|
|
||||||
int64_t l_xyz = 0;
|
|
||||||
for (int d = 0; d < nd; d++) {
|
|
||||||
lcoor[d] = rdimensions[d]*icoor[d] + ocoor[d];
|
|
||||||
}
|
|
||||||
uint64_t l_t = lcoor[nd-1];
|
|
||||||
|
|
||||||
Coordinate xyz_coor = lcoor;
|
|
||||||
xyz_coor[nd-1] =0;
|
|
||||||
Lexicographic::IndexFromCoor(xyz_coor,l_xyz,ldims);
|
|
||||||
|
|
||||||
|
|
||||||
scalar_object data = extractLane(lane,Data[sf]);
|
|
||||||
scalar *data_words = (scalar *) &data;
|
|
||||||
for(int w = 0 ; w < nwords; w++) {
|
|
||||||
// BLAS_V[slice_vol][nt][words]
|
|
||||||
// Fortran Column major BLAS layout is V_(t,w)_xyz
|
|
||||||
uint64_t idx = w+l_t*nwords + l_xyz * nwords * Nt;
|
|
||||||
blasData_p[idx] = data_words[w];
|
|
||||||
}
|
|
||||||
#ifdef GRID_SIMT
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
});
|
|
||||||
}
|
|
||||||
void ExportMomentumProjection(std::vector<typename Field::scalar_object> &projection)
|
|
||||||
{
|
|
||||||
projection.resize(nmom*nt);
|
|
||||||
acceleratorCopyFromDevice(&BLAS_P[0],(scalar *)&projection[0],BLAS_P.size()*sizeof(scalar));
|
|
||||||
// Could decide on a layout late?
|
|
||||||
}
|
|
||||||
|
|
||||||
// Row major layout "C" order:
|
|
||||||
// BLAS_V[slice_vol][nt][words]
|
|
||||||
// BLAS_M[nmom][slice_vol]
|
|
||||||
// BLAS_P[nmom][nt][words]
|
|
||||||
//
|
|
||||||
// Fortran Column major BLAS layout is V_(w,t)_xyz
|
|
||||||
// Fortran Column major BLAS layout is M_xyz,mom
|
|
||||||
// Fortran Column major BLAS layout is P_(w,t),mom
|
|
||||||
//
|
|
||||||
// Projected
|
|
||||||
//
|
|
||||||
// P = (V * M)_(w,t),mom
|
|
||||||
//
|
|
||||||
void Project(Field &data,std::vector< typename Field::scalar_object > & projected_gdata)
|
|
||||||
{
|
|
||||||
double t_import=0;
|
|
||||||
double t_export=0;
|
|
||||||
double t_gemm =0;
|
|
||||||
double t_allreduce=0;
|
|
||||||
t_import-=usecond();
|
|
||||||
this->ImportVector(data);
|
|
||||||
|
|
||||||
std::vector< typename Field::scalar_object > projected_planes;
|
|
||||||
|
|
||||||
deviceVector<scalar *> Vd(1);
|
|
||||||
deviceVector<scalar *> Md(1);
|
|
||||||
deviceVector<scalar *> Pd(1);
|
|
||||||
|
|
||||||
scalar * Vh = & BLAS_V[0];
|
|
||||||
scalar * Mh = & BLAS_M[0];
|
|
||||||
scalar * Ph = & BLAS_P[0];
|
|
||||||
|
|
||||||
acceleratorPut(Vd[0],Vh);
|
|
||||||
acceleratorPut(Md[0],Mh);
|
|
||||||
acceleratorPut(Pd[0],Ph);
|
|
||||||
t_import+=usecond();
|
|
||||||
|
|
||||||
GridBLAS BLAS;
|
|
||||||
|
|
||||||
/////////////////////////////////////////
|
|
||||||
// P_im = VMmx . Vxi
|
|
||||||
/////////////////////////////////////////
|
|
||||||
t_gemm-=usecond();
|
|
||||||
BLAS.gemmBatched(GridBLAS_OP_N,GridBLAS_OP_N,
|
|
||||||
words*nt,nmom,nxyz,
|
|
||||||
scalar(1.0),
|
|
||||||
Vd,
|
|
||||||
Md,
|
|
||||||
scalar(0.0), // wipe out result
|
|
||||||
Pd);
|
|
||||||
BLAS.synchronise();
|
|
||||||
t_gemm+=usecond();
|
|
||||||
|
|
||||||
t_export-=usecond();
|
|
||||||
ExportMomentumProjection(projected_planes); // resizes
|
|
||||||
t_export+=usecond();
|
|
||||||
|
|
||||||
/////////////////////////////////
|
|
||||||
// Reduce across MPI ranks
|
|
||||||
/////////////////////////////////
|
|
||||||
int nd = grid->Nd();
|
|
||||||
int gt = grid->GlobalDimensions()[nd-1];
|
|
||||||
int lt = grid->LocalDimensions()[nd-1];
|
|
||||||
projected_gdata.resize(gt*nmom);
|
|
||||||
for(int t=0;t<gt*nmom;t++){ // global Nt array with zeroes for stuff not on this node
|
|
||||||
projected_gdata[t]=Zero();
|
|
||||||
}
|
|
||||||
for(int t=0;t<lt;t++){
|
|
||||||
for(int m=0;m<nmom;m++){
|
|
||||||
int st = grid->LocalStarts()[nd-1];
|
|
||||||
projected_gdata[t+st + gt*m] = projected_planes[t+lt*m];
|
|
||||||
}}
|
|
||||||
t_allreduce-=usecond();
|
|
||||||
grid->GlobalSumVector((scalar *)&projected_gdata[0],gt*nmom*words);
|
|
||||||
t_allreduce+=usecond();
|
|
||||||
|
|
||||||
std::cout << GridLogPerformance<<" MomentumProject t_import "<<t_import<<"us"<<std::endl;
|
|
||||||
std::cout << GridLogPerformance<<" MomentumProject t_export "<<t_export<<"us"<<std::endl;
|
|
||||||
std::cout << GridLogPerformance<<" MomentumProject t_gemm "<<t_gemm<<"us"<<std::endl;
|
|
||||||
std::cout << GridLogPerformance<<" MomentumProject t_reduce "<<t_allreduce<<"us"<<std::endl;
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
@@ -1,376 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: MultiRHSBlockCGLinalg.h
|
|
||||||
|
|
||||||
Copyright (C) 2024
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
||||||
/* Need helper object for BLAS accelerated mrhs blockCG */
|
|
||||||
template<class Field>
|
|
||||||
class MultiRHSBlockCGLinalg
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef typename Field::scalar_type scalar;
|
|
||||||
typedef typename Field::scalar_object scalar_object;
|
|
||||||
typedef typename Field::vector_object vector_object;
|
|
||||||
|
|
||||||
deviceVector<scalar> BLAS_X; // nrhs x vol -- the sources
|
|
||||||
deviceVector<scalar> BLAS_Y; // nrhs x vol -- the result
|
|
||||||
deviceVector<scalar> BLAS_C; // nrhs x nrhs -- the coefficients
|
|
||||||
deviceVector<scalar> BLAS_Cred; // nrhs x nrhs x oSites -- reduction buffer
|
|
||||||
deviceVector<scalar *> Xdip;
|
|
||||||
deviceVector<scalar *> Ydip;
|
|
||||||
deviceVector<scalar *> Cdip;
|
|
||||||
|
|
||||||
MultiRHSBlockCGLinalg() {};
|
|
||||||
~MultiRHSBlockCGLinalg(){ Deallocate(); };
|
|
||||||
|
|
||||||
void Deallocate(void)
|
|
||||||
{
|
|
||||||
Xdip.resize(0);
|
|
||||||
Ydip.resize(0);
|
|
||||||
Cdip.resize(0);
|
|
||||||
BLAS_Cred.resize(0);
|
|
||||||
BLAS_C.resize(0);
|
|
||||||
BLAS_X.resize(0);
|
|
||||||
BLAS_Y.resize(0);
|
|
||||||
}
|
|
||||||
void MaddMatrix(std::vector<Field> &AP, Eigen::MatrixXcd &m , const std::vector<Field> &X,const std::vector<Field> &Y,RealD scale=1.0)
|
|
||||||
{
|
|
||||||
std::vector<Field> Y_copy(AP.size(),AP[0].Grid());
|
|
||||||
for(int r=0;r<AP.size();r++){
|
|
||||||
Y_copy[r] = Y[r];
|
|
||||||
}
|
|
||||||
MulMatrix(AP,m,X);
|
|
||||||
for(int r=0;r<AP.size();r++){
|
|
||||||
AP[r] = scale*AP[r]+Y_copy[r];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void MulMatrix(std::vector<Field> &Y, Eigen::MatrixXcd &m , const std::vector<Field> &X)
|
|
||||||
{
|
|
||||||
typedef typename Field::scalar_type scomplex;
|
|
||||||
GridBase *grid;
|
|
||||||
uint64_t vol;
|
|
||||||
uint64_t words;
|
|
||||||
|
|
||||||
int nrhs = Y.size();
|
|
||||||
grid = X[0].Grid();
|
|
||||||
vol = grid->lSites();
|
|
||||||
words = sizeof(scalar_object)/sizeof(scalar);
|
|
||||||
int64_t vw = vol * words;
|
|
||||||
|
|
||||||
RealD t0 = usecond();
|
|
||||||
BLAS_X.resize(nrhs * vw); // cost free if size doesn't change
|
|
||||||
BLAS_Y.resize(nrhs * vw); // cost free if size doesn't change
|
|
||||||
BLAS_C.resize(nrhs * nrhs);// cost free if size doesn't change
|
|
||||||
RealD t1 = usecond();
|
|
||||||
|
|
||||||
/////////////////////////////////////////////
|
|
||||||
// Copy in the multi-rhs sources
|
|
||||||
/////////////////////////////////////////////
|
|
||||||
for(int r=0;r<nrhs;r++){
|
|
||||||
int64_t offset = r*vw;
|
|
||||||
autoView(x_v,X[r],AcceleratorRead);
|
|
||||||
acceleratorCopyDeviceToDevice(&x_v[0],&BLAS_X[offset],sizeof(scalar_object)*vol);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assumes Eigen storage contiguous
|
|
||||||
acceleratorCopyToDevice(&m(0,0),&BLAS_C[0],BLAS_C.size()*sizeof(scalar));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* in Fortran column major notation (cuBlas order)
|
|
||||||
*
|
|
||||||
* Xxr = [X1(x)][..][Xn(x)]
|
|
||||||
* Yxr = [Y1(x)][..][Ym(x)]
|
|
||||||
* Y = X . C
|
|
||||||
*/
|
|
||||||
deviceVector<scalar *> Xd(1);
|
|
||||||
deviceVector<scalar *> Yd(1);
|
|
||||||
deviceVector<scalar *> Cd(1);
|
|
||||||
|
|
||||||
scalar * Xh = & BLAS_X[0];
|
|
||||||
scalar * Yh = & BLAS_Y[0];
|
|
||||||
scalar * Ch = & BLAS_C[0];
|
|
||||||
|
|
||||||
acceleratorPut(Xd[0],Xh);
|
|
||||||
acceleratorPut(Yd[0],Yh);
|
|
||||||
acceleratorPut(Cd[0],Ch);
|
|
||||||
|
|
||||||
RealD t2 = usecond();
|
|
||||||
GridBLAS BLAS;
|
|
||||||
/////////////////////////////////////////
|
|
||||||
// Y = X*C (transpose?)
|
|
||||||
/////////////////////////////////////////
|
|
||||||
BLAS.gemmBatched(GridBLAS_OP_N,GridBLAS_OP_N,
|
|
||||||
vw,nrhs,nrhs,
|
|
||||||
scalar(1.0),
|
|
||||||
Xd,
|
|
||||||
Cd,
|
|
||||||
scalar(0.0), // wipe out Y
|
|
||||||
Yd);
|
|
||||||
BLAS.synchronise();
|
|
||||||
RealD t3 = usecond();
|
|
||||||
|
|
||||||
// Copy back Y = m X
|
|
||||||
for(int r=0;r<nrhs;r++){
|
|
||||||
int64_t offset = r*vw;
|
|
||||||
autoView(y_v,Y[r],AcceleratorWrite);
|
|
||||||
acceleratorCopyDeviceToDevice(&BLAS_Y[offset],&y_v[0],sizeof(scalar_object)*vol);
|
|
||||||
}
|
|
||||||
RealD t4 = usecond();
|
|
||||||
std::cout <<GridLogPerformance << "MulMatrix alloc took "<< t1-t0<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "MulMatrix preamble took "<< t2-t1<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "MulMatrix blas took "<< t3-t2<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "MulMatrix copy took "<< t4-t3<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "MulMatrix total "<< t4-t0<<" us"<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InnerProductMatrix(Eigen::MatrixXcd &m , const std::vector<Field> &X, const std::vector<Field> &Y)
|
|
||||||
{
|
|
||||||
#if 0
|
|
||||||
int nrhs;
|
|
||||||
GridBase *grid;
|
|
||||||
uint64_t vol;
|
|
||||||
uint64_t words;
|
|
||||||
|
|
||||||
nrhs = X.size();
|
|
||||||
GRID_ASSERT(X.size()==Y.size());
|
|
||||||
conformable(X[0],Y[0]);
|
|
||||||
|
|
||||||
grid = X[0].Grid();
|
|
||||||
vol = grid->lSites();
|
|
||||||
words = sizeof(scalar_object)/sizeof(scalar);
|
|
||||||
int64_t vw = vol * words;
|
|
||||||
|
|
||||||
RealD t0 = usecond();
|
|
||||||
BLAS_X.resize(nrhs * vw); // cost free if size doesn't change
|
|
||||||
BLAS_Y.resize(nrhs * vw); // cost free if size doesn't change
|
|
||||||
BLAS_C.resize(nrhs * nrhs);// cost free if size doesn't change
|
|
||||||
RealD t1 = usecond();
|
|
||||||
|
|
||||||
/////////////////////////////////////////////
|
|
||||||
// Copy in the multi-rhs sources
|
|
||||||
/////////////////////////////////////////////
|
|
||||||
for(int r=0;r<nrhs;r++){
|
|
||||||
int64_t offset = r*vw;
|
|
||||||
autoView(x_v,X[r],AcceleratorRead);
|
|
||||||
acceleratorCopyDeviceToDevice(&x_v[0],&BLAS_X[offset],sizeof(scalar_object)*vol);
|
|
||||||
autoView(y_v,Y[r],AcceleratorRead);
|
|
||||||
acceleratorCopyDeviceToDevice(&y_v[0],&BLAS_Y[offset],sizeof(scalar_object)*vol);
|
|
||||||
}
|
|
||||||
RealD t2 = usecond();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* in Fortran column major notation (cuBlas order)
|
|
||||||
*
|
|
||||||
* Xxr = [X1(x)][..][Xn(x)]
|
|
||||||
*
|
|
||||||
* Yxr = [Y1(x)][..][Ym(x)]
|
|
||||||
*
|
|
||||||
* C_rs = X^dag Y
|
|
||||||
*/
|
|
||||||
deviceVector<scalar *> Xd(1);
|
|
||||||
deviceVector<scalar *> Yd(1);
|
|
||||||
deviceVector<scalar *> Cd(1);
|
|
||||||
|
|
||||||
scalar * Xh = & BLAS_X[0];
|
|
||||||
scalar * Yh = & BLAS_Y[0];
|
|
||||||
scalar * Ch = & BLAS_C[0];
|
|
||||||
|
|
||||||
acceleratorPut(Xd[0],Xh);
|
|
||||||
acceleratorPut(Yd[0],Yh);
|
|
||||||
acceleratorPut(Cd[0],Ch);
|
|
||||||
|
|
||||||
GridBLAS BLAS;
|
|
||||||
|
|
||||||
RealD t3 = usecond();
|
|
||||||
/////////////////////////////////////////
|
|
||||||
// C_rs = X^dag Y
|
|
||||||
/////////////////////////////////////////
|
|
||||||
BLAS.gemmBatched(GridBLAS_OP_C,GridBLAS_OP_N,
|
|
||||||
nrhs,nrhs,vw,
|
|
||||||
ComplexD(1.0),
|
|
||||||
Xd,
|
|
||||||
Yd,
|
|
||||||
ComplexD(0.0), // wipe out C
|
|
||||||
Cd);
|
|
||||||
BLAS.synchronise();
|
|
||||||
RealD t4 = usecond();
|
|
||||||
|
|
||||||
std::vector<scalar> HOST_C(BLAS_C.size()); // nrhs . nrhs -- the coefficients
|
|
||||||
acceleratorCopyFromDevice(&BLAS_C[0],&HOST_C[0],BLAS_C.size()*sizeof(scalar));
|
|
||||||
grid->GlobalSumVector(&HOST_C[0],nrhs*nrhs);
|
|
||||||
|
|
||||||
RealD t5 = usecond();
|
|
||||||
for(int rr=0;rr<nrhs;rr++){
|
|
||||||
for(int r=0;r<nrhs;r++){
|
|
||||||
int off = r+nrhs*rr;
|
|
||||||
m(r,rr)=HOST_C[off];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RealD t6 = usecond();
|
|
||||||
uint64_t M=nrhs;
|
|
||||||
uint64_t N=nrhs;
|
|
||||||
uint64_t K=vw;
|
|
||||||
RealD bytes = 1.0*sizeof(ComplexD)*(M*N*2+N*K+M*K);
|
|
||||||
RealD flops = 8.0*M*N*K;
|
|
||||||
flops = flops/(t4-t3)/1.e3;
|
|
||||||
bytes = bytes/(t4-t3)/1.e3;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix m,n,k "<< M<<","<<N<<","<<K<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix alloc t1 "<< t1-t0<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix cp t2 "<< t2-t1<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix setup t3 "<< t3-t2<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix blas t4 "<< t4-t3<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix blas "<< flops<<" GF/s"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix blas "<< bytes<<" GB/s"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix gsum t5 "<< t5-t4<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix cp t6 "<< t6-t5<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix took "<< t6-t0<<" us"<<std::endl;
|
|
||||||
#else
|
|
||||||
int nrhs;
|
|
||||||
GridBase *grid;
|
|
||||||
uint64_t vol;
|
|
||||||
uint64_t words;
|
|
||||||
|
|
||||||
nrhs = X.size();
|
|
||||||
GRID_ASSERT(X.size()==Y.size());
|
|
||||||
conformable(X[0],Y[0]);
|
|
||||||
|
|
||||||
grid = X[0].Grid();
|
|
||||||
int rd0 = grid->_rdimensions[0] * grid->_rdimensions[1];
|
|
||||||
vol = grid->oSites()/rd0;
|
|
||||||
words = rd0*sizeof(vector_object)/sizeof(scalar);
|
|
||||||
int64_t vw = vol * words;
|
|
||||||
GRID_ASSERT(vw == grid->lSites()*sizeof(scalar_object)/sizeof(scalar));
|
|
||||||
|
|
||||||
RealD t0 = usecond();
|
|
||||||
BLAS_X.resize(nrhs * vw); // cost free if size doesn't change
|
|
||||||
BLAS_Y.resize(nrhs * vw); // cost free if size doesn't change
|
|
||||||
BLAS_Cred.resize(nrhs * nrhs * vol);// cost free if size doesn't change
|
|
||||||
RealD t1 = usecond();
|
|
||||||
|
|
||||||
/////////////////////////////////////////////
|
|
||||||
// Copy in the multi-rhs sources -- layout batched BLAS ready
|
|
||||||
/////////////////////////////////////////////
|
|
||||||
for(int r=0;r<nrhs;r++){
|
|
||||||
autoView(x_v,X[r],AcceleratorRead);
|
|
||||||
autoView(y_v,Y[r],AcceleratorRead);
|
|
||||||
scalar *from_x=(scalar *)&x_v[0];
|
|
||||||
scalar *from_y=(scalar *)&y_v[0];
|
|
||||||
scalar *BX = &BLAS_X[0];
|
|
||||||
scalar *BY = &BLAS_Y[0];
|
|
||||||
accelerator_for(ssw,vw,1,{
|
|
||||||
uint64_t ss=ssw/words;
|
|
||||||
uint64_t w=ssw%words;
|
|
||||||
uint64_t offset = w+r*words+ss*nrhs*words; // [ss][rhs][words]
|
|
||||||
BX[offset] = from_x[ssw];
|
|
||||||
BY[offset] = from_y[ssw];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
RealD t2 = usecond();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* in Fortran column major notation (cuBlas order)
|
|
||||||
*
|
|
||||||
* Xxr = [X1(x)][..][Xn(x)]
|
|
||||||
*
|
|
||||||
* Yxr = [Y1(x)][..][Ym(x)]
|
|
||||||
*
|
|
||||||
* C_rs = X^dag Y
|
|
||||||
*/
|
|
||||||
Xdip.resize(vol);
|
|
||||||
Ydip.resize(vol);
|
|
||||||
Cdip.resize(vol);
|
|
||||||
std::vector<scalar *> Xh(vol);
|
|
||||||
std::vector<scalar *> Yh(vol);
|
|
||||||
std::vector<scalar *> Ch(vol);
|
|
||||||
for(uint64_t ss=0;ss<vol;ss++){
|
|
||||||
|
|
||||||
Xh[ss] = & BLAS_X[ss*nrhs*words];
|
|
||||||
Yh[ss] = & BLAS_Y[ss*nrhs*words];
|
|
||||||
Ch[ss] = & BLAS_Cred[ss*nrhs*nrhs];
|
|
||||||
|
|
||||||
}
|
|
||||||
acceleratorCopyToDevice(&Xh[0],&Xdip[0],vol*sizeof(scalar *));
|
|
||||||
acceleratorCopyToDevice(&Yh[0],&Ydip[0],vol*sizeof(scalar *));
|
|
||||||
acceleratorCopyToDevice(&Ch[0],&Cdip[0],vol*sizeof(scalar *));
|
|
||||||
|
|
||||||
GridBLAS BLAS;
|
|
||||||
|
|
||||||
RealD t3 = usecond();
|
|
||||||
/////////////////////////////////////////
|
|
||||||
// C_rs = X^dag Y
|
|
||||||
/////////////////////////////////////////
|
|
||||||
BLAS.gemmBatched(GridBLAS_OP_C,GridBLAS_OP_N,
|
|
||||||
nrhs,nrhs,words,
|
|
||||||
ComplexD(1.0),
|
|
||||||
Xdip,
|
|
||||||
Ydip,
|
|
||||||
ComplexD(0.0), // wipe out C
|
|
||||||
Cdip);
|
|
||||||
BLAS.synchronise();
|
|
||||||
RealD t4 = usecond();
|
|
||||||
|
|
||||||
std::vector<scalar> HOST_C(BLAS_Cred.size()); // nrhs . nrhs -- the coefficients
|
|
||||||
acceleratorCopyFromDevice(&BLAS_Cred[0],&HOST_C[0],BLAS_Cred.size()*sizeof(scalar));
|
|
||||||
|
|
||||||
RealD t5 = usecond();
|
|
||||||
m = Eigen::MatrixXcd::Zero(nrhs,nrhs);
|
|
||||||
for(int ss=0;ss<vol;ss++){
|
|
||||||
Eigen::Map<Eigen::MatrixXcd> eC((std::complex<double> *)&HOST_C[ss*nrhs*nrhs],nrhs,nrhs);
|
|
||||||
m = m + eC;
|
|
||||||
}
|
|
||||||
RealD t6l = usecond();
|
|
||||||
grid->GlobalSumVector((scalar *) &m(0,0),nrhs*nrhs);
|
|
||||||
RealD t6 = usecond();
|
|
||||||
uint64_t M=nrhs;
|
|
||||||
uint64_t N=nrhs;
|
|
||||||
uint64_t K=vw;
|
|
||||||
RealD xybytes = grid->lSites()*sizeof(scalar_object);
|
|
||||||
RealD bytes = 1.0*sizeof(ComplexD)*(M*N*2+N*K+M*K);
|
|
||||||
RealD flops = 8.0*M*N*K;
|
|
||||||
flops = flops/(t4-t3)/1.e3;
|
|
||||||
bytes = bytes/(t4-t3)/1.e3;
|
|
||||||
xybytes = 4*xybytes/(t2-t1)/1.e3;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix m,n,k "<< M<<","<<N<<","<<K<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix alloc t1 "<< t1-t0<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix cp t2 "<< t2-t1<<" us "<<xybytes<<" GB/s"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix setup t3 "<< t3-t2<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix blas t4 "<< t4-t3<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix blas "<< flops<<" GF/s"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix blas "<< bytes<<" GB/s"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix cp t5 "<< t5-t4<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix lsum t6l "<< t6l-t5<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix gsum t6 "<< t6-t6l<<" us"<<std::endl;
|
|
||||||
std::cout <<GridLogPerformance<< "InnerProductMatrix took "<< t6-t0<<" us"<<std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -1,233 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: MultiRHSDeflation.h
|
|
||||||
|
|
||||||
Copyright (C) 2023
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
||||||
/* Need helper object for BLAS accelerated mrhs projection
|
|
||||||
|
|
||||||
i) MultiRHS Deflation
|
|
||||||
|
|
||||||
Import Evecs -> nev x vol x internal
|
|
||||||
Import vector of Lattice objects -> nrhs x vol x internal
|
|
||||||
=> Cij (nrhs x Nev) via GEMM.
|
|
||||||
=> Guess (nrhs x vol x internal) = C x evecs (via GEMM)
|
|
||||||
Export
|
|
||||||
|
|
||||||
|
|
||||||
ii) MultiRHS block projection
|
|
||||||
|
|
||||||
Import basis -> nblock x nbasis x (block x internal)
|
|
||||||
Import vector of fine lattice objects -> nblock x nrhs x (block x internal)
|
|
||||||
|
|
||||||
=> coarse_(nrhs x nbasis )^block = via batched GEMM
|
|
||||||
|
|
||||||
iii) Alternate interface:
|
|
||||||
Import higher dim Lattice object-> vol x nrhs layout
|
|
||||||
|
|
||||||
*/
|
|
||||||
template<class Field>
|
|
||||||
class MultiRHSDeflation
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef typename Field::scalar_type scalar;
|
|
||||||
typedef typename Field::scalar_object scalar_object;
|
|
||||||
|
|
||||||
int nev;
|
|
||||||
std::vector<RealD> eval;
|
|
||||||
GridBase *grid;
|
|
||||||
uint64_t vol;
|
|
||||||
uint64_t words;
|
|
||||||
|
|
||||||
deviceVector<scalar> BLAS_E; // nev x vol -- the eigenbasis (up to a 1/sqrt(lambda))
|
|
||||||
deviceVector<scalar> BLAS_R; // nrhs x vol -- the sources
|
|
||||||
deviceVector<scalar> BLAS_G; // nrhs x vol -- the guess
|
|
||||||
deviceVector<scalar> BLAS_C; // nrhs x nev -- the coefficients
|
|
||||||
|
|
||||||
MultiRHSDeflation(){};
|
|
||||||
~MultiRHSDeflation(){ Deallocate(); };
|
|
||||||
|
|
||||||
void Deallocate(void)
|
|
||||||
{
|
|
||||||
nev=0;
|
|
||||||
grid=nullptr;
|
|
||||||
vol=0;
|
|
||||||
words=0;
|
|
||||||
BLAS_E.resize(0);
|
|
||||||
BLAS_R.resize(0);
|
|
||||||
BLAS_C.resize(0);
|
|
||||||
BLAS_G.resize(0);
|
|
||||||
}
|
|
||||||
void Allocate(int _nev,GridBase *_grid)
|
|
||||||
{
|
|
||||||
nev=_nev;
|
|
||||||
grid=_grid;
|
|
||||||
vol = grid->lSites();
|
|
||||||
words = sizeof(scalar_object)/sizeof(scalar);
|
|
||||||
eval.resize(nev);
|
|
||||||
BLAS_E.resize (vol * words * nev );
|
|
||||||
std::cout << GridLogMessage << " Allocate for "<<nev<<" eigenvectors and volume "<<vol<<std::endl;
|
|
||||||
}
|
|
||||||
void ImportEigenVector(Field &evec,RealD &_eval, int ev)
|
|
||||||
{
|
|
||||||
// std::cout << " ev " <<ev<<" eval "<<_eval<< std::endl;
|
|
||||||
GRID_ASSERT(ev<eval.size());
|
|
||||||
eval[ev] = _eval;
|
|
||||||
|
|
||||||
int64_t offset = ev*vol*words;
|
|
||||||
autoView(v,evec,AcceleratorRead);
|
|
||||||
acceleratorCopyDeviceToDevice(&v[0],&BLAS_E[offset],sizeof(scalar_object)*vol);
|
|
||||||
|
|
||||||
}
|
|
||||||
void ImportEigenBasis(std::vector<Field> &evec,std::vector<RealD> &_eval)
|
|
||||||
{
|
|
||||||
ImportEigenBasis(evec,_eval,0,evec.size());
|
|
||||||
}
|
|
||||||
// Could use to import a batch of eigenvectors
|
|
||||||
void ImportEigenBasis(std::vector<Field> &evec,std::vector<RealD> &_eval, int _ev0, int _nev)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(_ev0+_nev<=evec.size());
|
|
||||||
|
|
||||||
Allocate(_nev,evec[0].Grid());
|
|
||||||
|
|
||||||
// Imports a sub-batch of eigenvectors, _ev0, ..., _ev0+_nev-1
|
|
||||||
for(int e=0;e<nev;e++){
|
|
||||||
std::cout << "Importing eigenvector "<<e<<" evalue "<<_eval[_ev0+e]<<std::endl;
|
|
||||||
ImportEigenVector(evec[_ev0+e],_eval[_ev0+e],e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void DeflateSources(std::vector<Field> &source,std::vector<Field> & guess)
|
|
||||||
{
|
|
||||||
int nrhs = source.size();
|
|
||||||
GRID_ASSERT(source.size()==guess.size());
|
|
||||||
GRID_ASSERT(grid == guess[0].Grid());
|
|
||||||
conformable(guess[0],source[0]);
|
|
||||||
|
|
||||||
int64_t vw = vol * words;
|
|
||||||
|
|
||||||
RealD t0 = usecond();
|
|
||||||
BLAS_R.resize(nrhs * vw); // cost free if size doesn't change
|
|
||||||
BLAS_G.resize(nrhs * vw); // cost free if size doesn't change
|
|
||||||
BLAS_C.resize(nev * nrhs);// cost free if size doesn't change
|
|
||||||
|
|
||||||
/////////////////////////////////////////////
|
|
||||||
// Copy in the multi-rhs sources
|
|
||||||
/////////////////////////////////////////////
|
|
||||||
// for(int r=0;r<nrhs;r++){
|
|
||||||
// std::cout << " source["<<r<<"] = "<<norm2(source[r])<<std::endl;
|
|
||||||
// }
|
|
||||||
for(int r=0;r<nrhs;r++){
|
|
||||||
int64_t offset = r*vw;
|
|
||||||
autoView(v,source[r],AcceleratorRead);
|
|
||||||
acceleratorCopyDeviceToDevice(&v[0],&BLAS_R[offset],sizeof(scalar_object)*vol);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* in Fortran column major notation (cuBlas order)
|
|
||||||
*
|
|
||||||
* Exe = [e1(x)][..][en(x)]
|
|
||||||
*
|
|
||||||
* Rxr = [r1(x)][..][rm(x)]
|
|
||||||
*
|
|
||||||
* C_er = E^dag R
|
|
||||||
* C_er = C_er / lambda_e
|
|
||||||
* G_xr = Exe Cer
|
|
||||||
*/
|
|
||||||
deviceVector<scalar *> Ed(1);
|
|
||||||
deviceVector<scalar *> Rd(1);
|
|
||||||
deviceVector<scalar *> Cd(1);
|
|
||||||
deviceVector<scalar *> Gd(1);
|
|
||||||
|
|
||||||
scalar * Eh = & BLAS_E[0];
|
|
||||||
scalar * Rh = & BLAS_R[0];
|
|
||||||
scalar * Ch = & BLAS_C[0];
|
|
||||||
scalar * Gh = & BLAS_G[0];
|
|
||||||
|
|
||||||
acceleratorPut(Ed[0],Eh);
|
|
||||||
acceleratorPut(Rd[0],Rh);
|
|
||||||
acceleratorPut(Cd[0],Ch);
|
|
||||||
acceleratorPut(Gd[0],Gh);
|
|
||||||
|
|
||||||
GridBLAS BLAS;
|
|
||||||
|
|
||||||
/////////////////////////////////////////
|
|
||||||
// C_er = E^dag R
|
|
||||||
/////////////////////////////////////////
|
|
||||||
BLAS.gemmBatched(GridBLAS_OP_C,GridBLAS_OP_N,
|
|
||||||
nev,nrhs,vw,
|
|
||||||
scalar(1.0),
|
|
||||||
Ed,
|
|
||||||
Rd,
|
|
||||||
scalar(0.0), // wipe out C
|
|
||||||
Cd);
|
|
||||||
BLAS.synchronise();
|
|
||||||
|
|
||||||
GRID_ASSERT(BLAS_C.size()==nev*nrhs);
|
|
||||||
|
|
||||||
std::vector<scalar> HOST_C(BLAS_C.size()); // nrhs . nev -- the coefficients
|
|
||||||
acceleratorCopyFromDevice(&BLAS_C[0],&HOST_C[0],BLAS_C.size()*sizeof(scalar));
|
|
||||||
grid->GlobalSumVector(&HOST_C[0],nev*nrhs);
|
|
||||||
for(int e=0;e<nev;e++){
|
|
||||||
RealD lam(1.0/eval[e]);
|
|
||||||
for(int r=0;r<nrhs;r++){
|
|
||||||
int off = e+nev*r;
|
|
||||||
HOST_C[off]=HOST_C[off] * lam;
|
|
||||||
// std::cout << "C["<<e<<"]["<<r<<"] ="<<HOST_C[off]<< " eval[e] "<<eval[e] <<std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
acceleratorCopyToDevice(&HOST_C[0],&BLAS_C[0],BLAS_C.size()*sizeof(scalar));
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////
|
|
||||||
// Guess G_xr = Exe Cer
|
|
||||||
/////////////////////////////////////////
|
|
||||||
BLAS.gemmBatched(GridBLAS_OP_N,GridBLAS_OP_N,
|
|
||||||
vw,nrhs,nev,
|
|
||||||
scalar(1.0),
|
|
||||||
Ed, // x . nev
|
|
||||||
Cd, // nev . nrhs
|
|
||||||
scalar(0.0),
|
|
||||||
Gd);
|
|
||||||
BLAS.synchronise();
|
|
||||||
|
|
||||||
///////////////////////////////////////
|
|
||||||
// Copy out the multirhs
|
|
||||||
///////////////////////////////////////
|
|
||||||
for(int r=0;r<nrhs;r++){
|
|
||||||
int64_t offset = r*vw;
|
|
||||||
autoView(v,guess[r],AcceleratorWrite);
|
|
||||||
acceleratorCopyDeviceToDevice(&BLAS_G[offset],&v[0],sizeof(scalar_object)*vol);
|
|
||||||
}
|
|
||||||
RealD t1 = usecond();
|
|
||||||
std::cout << GridLogMessage << "MultiRHSDeflation for "<<nrhs<<" sources with "<<nev<<" eigenvectors took " << (t1-t0)/1e3 <<" ms"<<std::endl;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
@@ -33,134 +33,109 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
* Script A = SolverMatrix
|
* Script A = SolverMatrix
|
||||||
* Script P = Preconditioner
|
* Script P = Preconditioner
|
||||||
*
|
*
|
||||||
|
* Deflation methods considered
|
||||||
|
* -- Solve P A x = P b [ like Luscher ]
|
||||||
|
* DEF-1 M P A x = M P b [i.e. left precon]
|
||||||
|
* DEF-2 P^T M A x = P^T M b
|
||||||
|
* ADEF-1 Preconditioner = M P + Q [ Q + M + M A Q]
|
||||||
|
* ADEF-2 Preconditioner = P^T M + Q
|
||||||
|
* BNN Preconditioner = P^T M P + Q
|
||||||
|
* BNN2 Preconditioner = M P + P^TM +Q - M P A M
|
||||||
|
*
|
||||||
* Implement ADEF-2
|
* Implement ADEF-2
|
||||||
*
|
*
|
||||||
* Vstart = P^Tx + Qb
|
* Vstart = P^Tx + Qb
|
||||||
* M1 = P^TM + Q
|
* M1 = P^TM + Q
|
||||||
* M2=M3=1
|
* M2=M3=1
|
||||||
|
* Vout = x
|
||||||
*/
|
*/
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
|
// abstract base
|
||||||
template<class Field>
|
template<class Field, class CoarseField>
|
||||||
class TwoLevelCG : public LinearFunction<Field>
|
class TwoLevelFlexiblePcg : public LinearFunction<Field>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
int verbose;
|
||||||
RealD Tolerance;
|
RealD Tolerance;
|
||||||
Integer MaxIterations;
|
Integer MaxIterations;
|
||||||
|
const int mmax = 5;
|
||||||
GridBase *grid;
|
GridBase *grid;
|
||||||
|
GridBase *coarsegrid;
|
||||||
|
|
||||||
// Fine operator, Smoother, CoarseSolver
|
LinearOperatorBase<Field> *_Linop
|
||||||
LinearOperatorBase<Field> &_FineLinop;
|
OperatorFunction<Field> *_Smoother,
|
||||||
LinearFunction<Field> &_Smoother;
|
LinearFunction<CoarseField> *_CoarseSolver;
|
||||||
|
|
||||||
GridStopWatch ProjectTimer;
|
// Need somthing that knows how to get from Coarse to fine and back again
|
||||||
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,
|
TwoLevelFlexiblePcg(RealD tol,
|
||||||
Integer maxit,
|
Integer maxit,
|
||||||
LinearOperatorBase<Field> &FineLinop,
|
LinearOperatorBase<Field> *Linop,
|
||||||
LinearFunction<Field> &Smoother,
|
LinearOperatorBase<Field> *SmootherLinop,
|
||||||
GridBase *fine) :
|
OperatorFunction<Field> *Smoother,
|
||||||
|
OperatorFunction<CoarseField> CoarseLinop
|
||||||
|
) :
|
||||||
Tolerance(tol),
|
Tolerance(tol),
|
||||||
MaxIterations(maxit),
|
MaxIterations(maxit),
|
||||||
_FineLinop(FineLinop),
|
_Linop(Linop),
|
||||||
_Smoother(Smoother)
|
_PreconditionerLinop(PrecLinop),
|
||||||
|
_Preconditioner(Preconditioner)
|
||||||
{
|
{
|
||||||
grid = fine;
|
verbose=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual void operator() (const Field &src, Field &x)
|
// The Pcg routine is common to all, but the various matrices differ from derived
|
||||||
{
|
// implementation to derived implmentation
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg starting single RHS"<<std::endl;
|
void operator() (const Field &src, Field &psi){
|
||||||
|
void operator() (const Field &src, Field &psi){
|
||||||
|
|
||||||
|
psi.checkerboard = src.checkerboard;
|
||||||
|
grid = src._grid;
|
||||||
|
|
||||||
RealD f;
|
RealD f;
|
||||||
RealD rtzp,rtz,a,d,b;
|
RealD rtzp,rtz,a,d,b;
|
||||||
RealD rptzp;
|
RealD rptzp;
|
||||||
|
RealD tn;
|
||||||
|
RealD guess = norm2(psi);
|
||||||
|
RealD ssq = norm2(src);
|
||||||
|
RealD rsq = ssq*Tolerance*Tolerance;
|
||||||
|
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
// Set up history vectors
|
// Set up history vectors
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
int mmax = 5;
|
std::vector<Field> p (mmax,grid);
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg allocating"<<std::endl;
|
|
||||||
std::vector<Field> p(mmax,grid);
|
|
||||||
std::vector<Field> mmp(mmax,grid);
|
std::vector<Field> mmp(mmax,grid);
|
||||||
std::vector<RealD> pAp(mmax);
|
std::vector<RealD> pAp(mmax);
|
||||||
Field z(grid);
|
|
||||||
|
Field x (grid); x = psi;
|
||||||
|
Field z (grid);
|
||||||
Field tmp(grid);
|
Field tmp(grid);
|
||||||
Field mp (grid);
|
Field r (grid);
|
||||||
Field r (grid);
|
Field mu (grid);
|
||||||
Field mu (grid);
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg allocated"<<std::endl;
|
|
||||||
//Initial residual computation & set up
|
|
||||||
RealD guess = norm2(x);
|
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg guess nrm "<<guess<<std::endl;
|
|
||||||
RealD src_nrm = norm2(src);
|
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg src nrm "<<src_nrm<<std::endl;
|
|
||||||
|
|
||||||
if ( src_nrm == 0.0 ) {
|
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg given trivial source norm "<<src_nrm<<std::endl;
|
|
||||||
x=Zero();
|
|
||||||
}
|
|
||||||
RealD tn;
|
|
||||||
|
|
||||||
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();
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
// x0 = Vstart -- possibly modify guess
|
// x0 = Vstart -- possibly modify guess
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
x=src;
|
||||||
Vstart(x,src);
|
Vstart(x,src);
|
||||||
|
|
||||||
// r0 = b -A x0
|
// r0 = b -A x0
|
||||||
_FineLinop.HermOp(x,mmp[0]);
|
HermOp(x,mmp); // Shouldn't this be something else?
|
||||||
axpy (r, -1.0,mmp[0], src); // Recomputes r=src-Ax0
|
axpy (r, -1.0,mmp[0], src); // Recomputes r=src-Ax0
|
||||||
{
|
|
||||||
double n1 = norm2(x);
|
|
||||||
double n2 = norm2(mmp[0]);
|
|
||||||
double n3 = norm2(r);
|
|
||||||
std::cout<<GridLogMessage<<"x,vstart,r = "<<n1<<" "<<n2<<" "<<n3<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
// Compute z = M1 x
|
// Compute z = M1 x
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
PcgM1(r,z);
|
M1(r,z,tmp,mp,SmootherMirs);
|
||||||
rtzp =real(innerProduct(r,z));
|
rtzp =real(innerProduct(r,z));
|
||||||
|
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
// Solve for Mss mu = P A z and set p = z-mu
|
// Solve for Mss mu = P A z and set p = z-mu
|
||||||
// Def2 p = 1 - Q Az = Pright z
|
// Def2: p = 1 - Q Az = Pright z
|
||||||
// Other algos M2 is trivial
|
// Other algos M2 is trivial
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
PcgM2(z,p[0]);
|
M2(z,p[0]);
|
||||||
|
|
||||||
RealD ssq = norm2(src);
|
|
||||||
RealD rsq = ssq*Tolerance*Tolerance;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<<"HDCG: k=0 residual "<<rtzp<<" rsq "<<rsq<<"\n";
|
|
||||||
|
|
||||||
Field pp(grid);
|
|
||||||
|
|
||||||
for (int k=0;k<=MaxIterations;k++){
|
for (int k=0;k<=MaxIterations;k++){
|
||||||
|
|
||||||
@@ -168,53 +143,31 @@ class TwoLevelCG : public LinearFunction<Field>
|
|||||||
int peri_kp = (k+1) % mmax;
|
int peri_kp = (k+1) % mmax;
|
||||||
|
|
||||||
rtz=rtzp;
|
rtz=rtzp;
|
||||||
M3Timer.Start();
|
d= M3(p[peri_k],mp,mmp[peri_k],tmp);
|
||||||
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);
|
M1(r,z,tmp,mp);
|
||||||
|
|
||||||
{
|
|
||||||
RealD n1,n2;
|
|
||||||
n1=norm2(r);
|
|
||||||
n2=norm2(z);
|
|
||||||
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";
|
|
||||||
|
|
||||||
// PcgM2(z,p[0]);
|
M2(z,mu); // ADEF-2 this is identity. Axpy possible to eliminate
|
||||||
PcgM2(z,mu); // ADEF-2 this is identity. Axpy possible to eliminate
|
|
||||||
|
|
||||||
p[peri_kp]=mu;
|
p[peri_kp]=p[peri_k];
|
||||||
|
|
||||||
// Standard search direction p -> z + b p
|
// Standard search direction p -> z + b p ; b =
|
||||||
b = (rtzp)/rtz;
|
b = (rtzp)/rtz;
|
||||||
|
|
||||||
int northog;
|
int northog;
|
||||||
// k=zero <=> peri_kp=1; northog = 1
|
|
||||||
// k=1 <=> peri_kp=2; northog = 2
|
|
||||||
// ... ... ...
|
|
||||||
// k=mmax-2<=> peri_kp=mmax-1; northog = mmax-1
|
|
||||||
// k=mmax-1<=> peri_kp=0; northog = 1
|
|
||||||
|
|
||||||
// northog = (peri_kp==0)?1:peri_kp; // This is the fCG(mmax) algorithm
|
// northog = (peri_kp==0)?1:peri_kp; // This is the fCG(mmax) algorithm
|
||||||
northog = (k>mmax-1)?(mmax-1):k; // This is the fCG-Tr(mmax-1) algorithm
|
northog = (k>mmax-1)?(mmax-1):k; // This is the fCG-Tr(mmax-1) algorithm
|
||||||
|
|
||||||
std::cout<<GridLogMessage<<"HDCG::fPcg iteration "<<k<<" : orthogonalising to last "<<northog<<" vectors\n";
|
|
||||||
for(int back=0; back < northog; back++){
|
for(int back=0; back < northog; back++){
|
||||||
int peri_back = (k-back)%mmax;
|
int peri_back = (k-back)%mmax;
|
||||||
RealD pbApk= real(innerProduct(mmp[peri_back],p[peri_kp]));
|
RealD pbApk= real(innerProduct(mmp[peri_back],p[peri_kp]));
|
||||||
@@ -223,335 +176,75 @@ class TwoLevelCG : public LinearFunction<Field>
|
|||||||
}
|
}
|
||||||
|
|
||||||
RealD rrn=sqrt(rn/ssq);
|
RealD rrn=sqrt(rn/ssq);
|
||||||
RealD rtn=sqrt(rtz/ssq);
|
std::cout<<GridLogMessage<<"TwoLevelfPcg: k= "<<k<<" residual = "<<rrn<<std::endl;
|
||||||
RealD rtnp=sqrt(rtzp/ssq);
|
|
||||||
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: fPcg k= "<<k<<" residual = "<<rrn<<"\n";
|
|
||||||
|
|
||||||
// Stopping condition
|
// Stopping condition
|
||||||
if ( rn <= rsq ) {
|
if ( rn <= rsq ) {
|
||||||
|
|
||||||
HDCGTimer.Stop();
|
HermOp(x,mmp); // Shouldn't this be something else?
|
||||||
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]);
|
|
||||||
axpy(tmp,-1.0,src,mmp[0]);
|
axpy(tmp,-1.0,src,mmp[0]);
|
||||||
|
|
||||||
RealD mmpnorm = sqrt(norm2(mmp[0]));
|
RealD psinorm = sqrt(norm2(x));
|
||||||
RealD xnorm = sqrt(norm2(x));
|
RealD srcnorm = sqrt(norm2(src));
|
||||||
RealD srcnorm = sqrt(norm2(src));
|
RealD tmpnorm = sqrt(norm2(tmp));
|
||||||
RealD tmpnorm = sqrt(norm2(tmp));
|
RealD true_residual = tmpnorm/srcnorm;
|
||||||
RealD true_residual = tmpnorm/srcnorm;
|
std::cout<<GridLogMessage<<"TwoLevelfPcg: true residual is "<<true_residual<<std::endl;
|
||||||
std::cout<<GridLogMessage
|
std::cout<<GridLogMessage<<"TwoLevelfPcg: target residual was"<<Tolerance<<std::endl;
|
||||||
<<"HDCG: true residual is "<<true_residual
|
return k;
|
||||||
<<" solution "<<xnorm
|
|
||||||
<<" source "<<srcnorm
|
|
||||||
<<" mmp "<<mmpnorm
|
|
||||||
<<std::endl;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
HDCGTimer.Stop();
|
// Non-convergence
|
||||||
std::cout<<GridLogMessage<<"HDCG: not converged "<<HDCGTimer.Elapsed()<<std::endl;
|
assert(0);
|
||||||
RealD xnorm = sqrt(norm2(x));
|
|
||||||
RealD srcnorm = sqrt(norm2(src));
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: non-converged solution "<<xnorm<<" source "<<srcnorm<<std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
virtual void operator() (std::vector<Field> &src, std::vector<Field> &x)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<<"HDCG: mrhs fPcg starting"<<std::endl;
|
|
||||||
src[0].Grid()->Barrier();
|
|
||||||
int nrhs = src.size();
|
|
||||||
std::vector<RealD> f(nrhs);
|
|
||||||
std::vector<RealD> rtzp(nrhs);
|
|
||||||
std::vector<RealD> rtz(nrhs);
|
|
||||||
std::vector<RealD> a(nrhs);
|
|
||||||
std::vector<RealD> d(nrhs);
|
|
||||||
std::vector<RealD> b(nrhs);
|
|
||||||
std::vector<RealD> rptzp(nrhs);
|
|
||||||
/////////////////////////////
|
|
||||||
// Set up history vectors
|
|
||||||
/////////////////////////////
|
|
||||||
int mmax = 3;
|
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg allocating"<<std::endl;
|
|
||||||
src[0].Grid()->Barrier();
|
|
||||||
std::vector<std::vector<Field> > p(nrhs); for(int r=0;r<nrhs;r++) p[r].resize(mmax,grid);
|
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg allocated p"<<std::endl;
|
|
||||||
src[0].Grid()->Barrier();
|
|
||||||
std::vector<std::vector<Field> > mmp(nrhs); for(int r=0;r<nrhs;r++) mmp[r].resize(mmax,grid);
|
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg allocated mmp"<<std::endl;
|
|
||||||
src[0].Grid()->Barrier();
|
|
||||||
std::vector<std::vector<RealD> > pAp(nrhs); for(int r=0;r<nrhs;r++) pAp[r].resize(mmax);
|
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg allocated pAp"<<std::endl;
|
|
||||||
src[0].Grid()->Barrier();
|
|
||||||
std::vector<Field> z(nrhs,grid);
|
|
||||||
std::vector<Field> mp (nrhs,grid);
|
|
||||||
std::vector<Field> r (nrhs,grid);
|
|
||||||
std::vector<Field> mu (nrhs,grid);
|
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg allocated z,mp,r,mu"<<std::endl;
|
|
||||||
src[0].Grid()->Barrier();
|
|
||||||
|
|
||||||
//Initial residual computation & set up
|
|
||||||
std::vector<RealD> src_nrm(nrhs);
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
|
||||||
src_nrm[rhs]=norm2(src[rhs]);
|
|
||||||
GRID_ASSERT(src_nrm[rhs]!=0.0);
|
|
||||||
}
|
|
||||||
std::vector<RealD> tn(nrhs);
|
|
||||||
|
|
||||||
GridStopWatch HDCGTimer;
|
|
||||||
HDCGTimer.Start();
|
|
||||||
//////////////////////////
|
|
||||||
// x0 = Vstart -- possibly modify guess
|
|
||||||
//////////////////////////
|
|
||||||
Vstart(x,src);
|
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
|
||||||
// r0 = b -A x0
|
|
||||||
_FineLinop.HermOp(x[rhs],mmp[rhs][0]);
|
|
||||||
axpy (r[rhs], -1.0,mmp[rhs][0], src[rhs]); // Recomputes r=src-Ax0
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////
|
|
||||||
// Compute z = M1 x
|
|
||||||
//////////////////////////////////
|
|
||||||
// This needs a multiRHS version for acceleration
|
|
||||||
PcgM1(r,z);
|
|
||||||
|
|
||||||
std::vector<RealD> ssq(nrhs);
|
|
||||||
std::vector<RealD> rsq(nrhs);
|
|
||||||
std::vector<Field> pp(nrhs,grid);
|
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
|
||||||
rtzp[rhs] =real(innerProduct(r[rhs],z[rhs]));
|
|
||||||
p[rhs][0]=z[rhs];
|
|
||||||
ssq[rhs]=norm2(src[rhs]);
|
|
||||||
rsq[rhs]= ssq[rhs]*Tolerance*Tolerance;
|
|
||||||
std::cout << GridLogMessage<<"mrhs HDCG: "<<rhs<<" k=0 residual "<<rtzp[rhs]<<" rsq "<<rsq[rhs]<<"\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<RealD> rn(nrhs);
|
|
||||||
for (int k=0;k<=MaxIterations;k++){
|
|
||||||
|
|
||||||
int peri_k = k % mmax;
|
|
||||||
int peri_kp = (k+1) % mmax;
|
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
|
||||||
rtz[rhs]=rtzp[rhs];
|
|
||||||
d[rhs]= PcgM3(p[rhs][peri_k],mmp[rhs][peri_k]);
|
|
||||||
a[rhs] = rtz[rhs]/d[rhs];
|
|
||||||
|
|
||||||
// Memorise this
|
|
||||||
pAp[rhs][peri_k] = d[rhs];
|
|
||||||
|
|
||||||
axpy(x[rhs],a[rhs],p[rhs][peri_k],x[rhs]);
|
|
||||||
rn[rhs] = axpy_norm(r[rhs],-a[rhs],mmp[rhs][peri_k],r[rhs]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute z = M x (for *all* RHS)
|
|
||||||
PcgM1(r,z);
|
|
||||||
std::cout << GridLogMessage<<"HDCG::fPcg M1 complete"<<std::endl;
|
|
||||||
grid->Barrier();
|
|
||||||
|
|
||||||
RealD max_rn=0.0;
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
|
||||||
|
|
||||||
rtzp[rhs] =real(innerProduct(r[rhs],z[rhs]));
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<<"HDCG::fPcg rhs"<<rhs<<" iteration "<<k<<" : inner rtzp "<<rtzp[rhs]<<"\n";
|
|
||||||
|
|
||||||
mu[rhs]=z[rhs];
|
|
||||||
|
|
||||||
p[rhs][peri_kp]=mu[rhs];
|
|
||||||
|
|
||||||
// Standard search direction p == z + b p
|
|
||||||
b[rhs] = (rtzp[rhs])/rtz[rhs];
|
|
||||||
|
|
||||||
int northog = (k>mmax-1)?(mmax-1):k; // This is the fCG-Tr(mmax-1) algorithm
|
|
||||||
std::cout<<GridLogMessage<<"HDCG::fPcg iteration "<<k<<" : orthogonalising to last "<<northog<<" vectors\n";
|
|
||||||
for(int back=0; back < northog; back++){
|
|
||||||
int peri_back = (k-back)%mmax;
|
|
||||||
RealD pbApk= real(innerProduct(mmp[rhs][peri_back],p[rhs][peri_kp]));
|
|
||||||
RealD beta = -pbApk/pAp[rhs][peri_back];
|
|
||||||
axpy(p[rhs][peri_kp],beta,p[rhs][peri_back],p[rhs][peri_kp]);
|
|
||||||
}
|
|
||||||
|
|
||||||
RealD rrn=sqrt(rn[rhs]/ssq[rhs]);
|
|
||||||
RealD rtn=sqrt(rtz[rhs]/ssq[rhs]);
|
|
||||||
RealD rtnp=sqrt(rtzp[rhs]/ssq[rhs]);
|
|
||||||
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: rhs "<<rhs<<"fPcg k= "<<k<<" residual = "<<rrn<<"\n";
|
|
||||||
if ( rrn > max_rn ) max_rn = rrn;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stopping condition based on worst case
|
|
||||||
if ( max_rn <= Tolerance ) {
|
|
||||||
|
|
||||||
HDCGTimer.Stop();
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg converged in "<<k<<" iterations and "<<HDCGTimer.Elapsed()<<std::endl;;
|
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
|
||||||
_FineLinop.HermOp(x[rhs],mmp[rhs][0]);
|
|
||||||
Field tmp(grid);
|
|
||||||
axpy(tmp,-1.0,src[rhs],mmp[rhs][0]);
|
|
||||||
|
|
||||||
RealD mmpnorm = sqrt(norm2(mmp[rhs][0]));
|
|
||||||
RealD xnorm = sqrt(norm2(x[rhs]));
|
|
||||||
RealD srcnorm = sqrt(norm2(src[rhs]));
|
|
||||||
RealD tmpnorm = sqrt(norm2(tmp));
|
|
||||||
RealD true_residual = tmpnorm/srcnorm;
|
|
||||||
std::cout<<GridLogMessage
|
|
||||||
<<"HDCG: true residual ["<<rhs<<"] is "<<true_residual
|
|
||||||
<<" solution "<<xnorm
|
|
||||||
<<" source "<<srcnorm
|
|
||||||
<<" mmp "<<mmpnorm
|
|
||||||
<<std::endl;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
HDCGTimer.Stop();
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: not converged "<<HDCGTimer.Elapsed()<<std::endl;
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
|
||||||
RealD xnorm = sqrt(norm2(x[rhs]));
|
|
||||||
RealD srcnorm = sqrt(norm2(src[rhs]));
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: non-converged solution "<<xnorm<<" source "<<srcnorm<<std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual void PcgM1(std::vector<Field> & in,std::vector<Field> & out)
|
virtual void M(Field & in,Field & out,Field & tmp) {
|
||||||
{
|
|
||||||
std::cout << "PcgM1 default (cheat) mrhs version"<<std::endl;
|
|
||||||
for(int rhs=0;rhs<in.size();rhs++){
|
|
||||||
this->PcgM1(in[rhs],out[rhs]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtual void PcgM1(Field & in, Field & out) =0;
|
|
||||||
virtual void Vstart(std::vector<Field> & x,std::vector<Field> & src)
|
|
||||||
{
|
|
||||||
std::cout << "Vstart default (cheat) mrhs version"<<std::endl;
|
|
||||||
for(int rhs=0;rhs<x.size();rhs++){
|
|
||||||
this->Vstart(x[rhs],src[rhs]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtual void Vstart(Field & x,const Field & src)=0;
|
|
||||||
|
|
||||||
virtual void PcgM2(const Field & in, Field & out) {
|
|
||||||
out=in;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual RealD PcgM3(const Field & p, Field & mmp){
|
virtual void M1(Field & in, Field & out) {// the smoother
|
||||||
RealD dd;
|
|
||||||
_FineLinop.HermOp(p,mmp);
|
|
||||||
ComplexD dot = innerProduct(p,mmp);
|
|
||||||
dd=real(dot);
|
|
||||||
return dd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// Only Def1 has non-trivial Vout.
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Field, class CoarseField, class Aggregation>
|
|
||||||
class TwoLevelADEF2 : public TwoLevelCG<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Need something that knows how to get from Coarse to fine and back again
|
|
||||||
// void ProjectToSubspace(CoarseVector &CoarseVec,const FineField &FineVec){
|
|
||||||
// void PromoteFromSubspace(const CoarseVector &CoarseVec,FineField &FineVec){
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////
|
|
||||||
GridBase *coarsegrid;
|
|
||||||
Aggregation &_Aggregates;
|
|
||||||
LinearFunction<CoarseField> &_CoarseSolver;
|
|
||||||
LinearFunction<CoarseField> &_CoarseSolverPrecise;
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// more most opertor functions
|
|
||||||
TwoLevelADEF2(RealD tol,
|
|
||||||
Integer maxit,
|
|
||||||
LinearOperatorBase<Field> &FineLinop,
|
|
||||||
LinearFunction<Field> &Smoother,
|
|
||||||
LinearFunction<CoarseField> &CoarseSolver,
|
|
||||||
LinearFunction<CoarseField> &CoarseSolverPrecise,
|
|
||||||
Aggregation &Aggregates
|
|
||||||
) :
|
|
||||||
TwoLevelCG<Field>(tol,maxit,FineLinop,Smoother,Aggregates.FineGrid),
|
|
||||||
_CoarseSolver(CoarseSolver),
|
|
||||||
_CoarseSolverPrecise(CoarseSolverPrecise),
|
|
||||||
_Aggregates(Aggregates)
|
|
||||||
{
|
|
||||||
coarsegrid = Aggregates.CoarseGrid;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual void PcgM1(Field & in, Field & out)
|
|
||||||
{
|
|
||||||
GRID_TRACE("MultiGridPreconditioner ");
|
|
||||||
// [PTM+Q] in = [1 - Q A] M in + Q in = Min + Q [ in -A Min]
|
// [PTM+Q] in = [1 - Q A] M in + Q in = Min + Q [ in -A Min]
|
||||||
|
Field tmp(grid);
|
||||||
|
Field Min(grid);
|
||||||
|
|
||||||
Field tmp(this->grid);
|
PcgM(in,Min); // Smoother call
|
||||||
Field Min(this->grid);
|
|
||||||
CoarseField PleftProj(this->coarsegrid);
|
|
||||||
CoarseField PleftMss_proj(this->coarsegrid);
|
|
||||||
|
|
||||||
this->SmoothTimer.Start();
|
HermOp(Min,out);
|
||||||
this->_Smoother(in,Min);
|
|
||||||
this->SmoothTimer.Stop();
|
|
||||||
this->SmoothCalls++;
|
|
||||||
|
|
||||||
this->MatrixTimer.Start();
|
|
||||||
this->_FineLinop.HermOp(Min,out);
|
|
||||||
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
|
||||||
|
|
||||||
this->ProjectTimer.Start();
|
ProjectToSubspace(tmp,PleftProj);
|
||||||
this->_Aggregates.ProjectToSubspace(PleftProj,tmp);
|
ApplyInverse(PleftProj,PleftMss_proj); // Ass^{-1} [in - A Min]_s
|
||||||
this->ProjectTimer.Stop();
|
PromoteFromSubspace(PleftMss_proj,tmp);// tmp = Q[in - A Min]
|
||||||
this->ProjectCalls++;
|
|
||||||
this->CoarseTimer.Start();
|
|
||||||
this->_CoarseSolver(PleftProj,PleftMss_proj); // Ass^{-1} [in - A Min]_s
|
|
||||||
this->CoarseTimer.Stop();
|
|
||||||
this->CoarseCalls++;
|
|
||||||
this->PromoteTimer.Start();
|
|
||||||
this->_Aggregates.PromoteFromSubspace(PleftMss_proj,tmp);// tmp = Q[in - A Min]
|
|
||||||
this->PromoteTimer.Stop();
|
|
||||||
this->PromoteCalls++;
|
|
||||||
|
|
||||||
axpy(out,1.0,Min,tmp); // Min+tmp
|
axpy(out,1.0,Min,tmp); // Min+tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Vstart(Field & x,const Field & src)
|
virtual void M2(const Field & in, Field & out) {
|
||||||
{
|
out=in;
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg Vstart "<<std::endl;
|
// Must override for Def2 only
|
||||||
|
// case PcgDef2:
|
||||||
|
// Pright(in,out);
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual RealD M3(const Field & p, Field & mmp){
|
||||||
|
double d,dd;
|
||||||
|
HermOpAndNorm(p,mmp,d,dd);
|
||||||
|
return dd;
|
||||||
|
// Must override for Def1 only
|
||||||
|
// case PcgDef1:
|
||||||
|
// d=linop_d->Mprec(p,mmp,tmp,0,1);// Dag no
|
||||||
|
// linop_d->Mprec(mmp,mp,tmp,1);// Dag yes
|
||||||
|
// Pleft(mp,mmp);
|
||||||
|
// d=real(linop_d->inner(p,mmp));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void VstartDef2(Field & xconst Field & src){
|
||||||
|
//case PcgDef2:
|
||||||
|
//case PcgAdef2:
|
||||||
|
//case PcgAdef2f:
|
||||||
|
//case PcgV11f:
|
||||||
///////////////////////////////////
|
///////////////////////////////////
|
||||||
// Choose x_0 such that
|
// Choose x_0 such that
|
||||||
// x_0 = guess + (A_ss^inv) r_s = guess + Ass_inv [src -Aguess]
|
// x_0 = guess + (A_ss^inv) r_s = guess + Ass_inv [src -Aguess]
|
||||||
@@ -563,78 +256,142 @@ class TwoLevelADEF2 : public TwoLevelCG<Field>
|
|||||||
// = src_s - (A guess)_s - src_s + (A guess)_s
|
// = src_s - (A guess)_s - src_s + (A guess)_s
|
||||||
// = 0
|
// = 0
|
||||||
///////////////////////////////////
|
///////////////////////////////////
|
||||||
Field r(this->grid);
|
Field r(grid);
|
||||||
Field mmp(this->grid);
|
Field mmp(grid);
|
||||||
CoarseField PleftProj(this->coarsegrid);
|
|
||||||
CoarseField PleftMss_proj(this->coarsegrid);
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg Vstart projecting "<<std::endl;
|
HermOp(x,mmp);
|
||||||
this->_Aggregates.ProjectToSubspace(PleftProj,src);
|
axpy (r, -1.0, mmp, src); // r_{-1} = src - A x
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg Vstart coarse solve "<<std::endl;
|
ProjectToSubspace(r,PleftProj);
|
||||||
this->_CoarseSolverPrecise(PleftProj,PleftMss_proj); // Ass^{-1} r_s
|
ApplyInverseCG(PleftProj,PleftMss_proj); // Ass^{-1} r_s
|
||||||
std::cout << GridLogMessage<<"HDCG: fPcg Vstart promote "<<std::endl;
|
PromoteFromSubspace(PleftMss_proj,mmp);
|
||||||
this->_Aggregates.PromoteFromSubspace(PleftMss_proj,x);
|
x=x+mmp;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
virtual void Vstart(Field & x,const Field & src){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
// Only Def1 has non-trivial Vout. Override in Def1
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
virtual void Vout (Field & in, Field & out,Field & src){
|
||||||
|
out = in;
|
||||||
|
//case PcgDef1:
|
||||||
|
// //Qb + PT x
|
||||||
|
// ProjectToSubspace(src,PleftProj);
|
||||||
|
// ApplyInverse(PleftProj,PleftMss_proj); // Ass^{-1} r_s
|
||||||
|
// PromoteFromSubspace(PleftMss_proj,tmp);
|
||||||
|
//
|
||||||
|
// Pright(in,out);
|
||||||
|
//
|
||||||
|
// linop_d->axpy(out,tmp,out,1.0);
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Pright and Pleft are common to all implementations
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
virtual void Pright(Field & in,Field & out){
|
||||||
|
// P_R = [ 1 0 ]
|
||||||
|
// [ -Mss^-1 Msb 0 ]
|
||||||
|
Field in_sbar(grid);
|
||||||
|
|
||||||
|
ProjectToSubspace(in,PleftProj);
|
||||||
|
PromoteFromSubspace(PleftProj,out);
|
||||||
|
axpy(in_sbar,-1.0,out,in); // in_sbar = in - in_s
|
||||||
|
|
||||||
|
HermOp(in_sbar,out);
|
||||||
|
ProjectToSubspace(out,PleftProj); // Mssbar in_sbar (project)
|
||||||
|
|
||||||
|
ApplyInverse (PleftProj,PleftMss_proj); // Mss^{-1} Mssbar
|
||||||
|
PromoteFromSubspace(PleftMss_proj,out); //
|
||||||
|
|
||||||
|
axpy(out,-1.0,out,in_sbar); // in_sbar - Mss^{-1} Mssbar in_sbar
|
||||||
|
}
|
||||||
|
virtual void Pleft (Field & in,Field & out){
|
||||||
|
// P_L = [ 1 -Mbs Mss^-1]
|
||||||
|
// [ 0 0 ]
|
||||||
|
Field in_sbar(grid);
|
||||||
|
Field tmp2(grid);
|
||||||
|
Field Mtmp(grid);
|
||||||
|
|
||||||
|
ProjectToSubspace(in,PleftProj);
|
||||||
|
PromoteFromSubspace(PleftProj,out);
|
||||||
|
axpy(in_sbar,-1.0,out,in); // in_sbar = in - in_s
|
||||||
|
|
||||||
|
ApplyInverse(PleftProj,PleftMss_proj); // Mss^{-1} in_s
|
||||||
|
PromoteFromSubspace(PleftMss_proj,out);
|
||||||
|
|
||||||
|
HermOp(out,Mtmp);
|
||||||
|
|
||||||
|
ProjectToSubspace(Mtmp,PleftProj); // Msbar s Mss^{-1}
|
||||||
|
PromoteFromSubspace(PleftProj,tmp2);
|
||||||
|
|
||||||
|
axpy(out,-1.0,tmp2,Mtmp);
|
||||||
|
axpy(out,-1.0,out,in_sbar); // in_sbar - Msbars Mss^{-1} in_s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<class Field>
|
template<class Field>
|
||||||
class TwoLevelADEF1defl : public TwoLevelCG<Field>
|
class TwoLevelFlexiblePcgADef2 : public TwoLevelFlexiblePcg<Field> {
|
||||||
{
|
public:
|
||||||
public:
|
virtual void M(Field & in,Field & out,Field & tmp){
|
||||||
const std::vector<Field> &evec;
|
|
||||||
const std::vector<RealD> &eval;
|
|
||||||
|
|
||||||
TwoLevelADEF1defl(RealD tol,
|
|
||||||
Integer maxit,
|
|
||||||
LinearOperatorBase<Field> &FineLinop,
|
|
||||||
LinearFunction<Field> &Smoother,
|
|
||||||
std::vector<Field> &_evec,
|
|
||||||
std::vector<RealD> &_eval) :
|
|
||||||
TwoLevelCG<Field>(tol,maxit,FineLinop,Smoother,_evec[0].Grid()),
|
|
||||||
evec(_evec),
|
|
||||||
eval(_eval)
|
|
||||||
{};
|
|
||||||
|
|
||||||
// Can just inherit existing M2
|
|
||||||
// Can just inherit existing M3
|
|
||||||
|
|
||||||
// Simple vstart - do nothing
|
|
||||||
virtual void Vstart(Field & x,const Field & src){
|
|
||||||
x=src; // Could apply Q
|
|
||||||
};
|
|
||||||
|
|
||||||
// Override PcgM1
|
|
||||||
virtual void PcgM1(Field & in, Field & out)
|
|
||||||
{
|
|
||||||
GRID_TRACE("EvecPreconditioner ");
|
|
||||||
int N=evec.size();
|
|
||||||
Field Pin(this->grid);
|
|
||||||
Field Qin(this->grid);
|
|
||||||
|
|
||||||
//MP + Q = M(1-AQ) + Q = M
|
|
||||||
// // If we are eigenvector deflating in coarse space
|
|
||||||
// // Q = Sum_i |phi_i> 1/lambda_i <phi_i|
|
|
||||||
// // A Q = Sum_i |phi_i> <phi_i|
|
|
||||||
// // M(1-AQ) = M(1-proj) + Q
|
|
||||||
Qin.Checkerboard()=in.Checkerboard();
|
|
||||||
Qin = Zero();
|
|
||||||
Pin = in;
|
|
||||||
for (int i=0;i<N;i++) {
|
|
||||||
const Field& tmp = evec[i];
|
|
||||||
auto ip = TensorRemove(innerProduct(tmp,in));
|
|
||||||
axpy(Qin, ip / eval[i],tmp,Qin);
|
|
||||||
axpy(Pin, -ip ,tmp,Pin);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->_Smoother(Pin,out);
|
|
||||||
|
|
||||||
out = out + Qin;
|
|
||||||
}
|
}
|
||||||
};
|
virtual void M1(Field & in, Field & out,Field & tmp,Field & mp){
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
}
|
||||||
|
virtual void M2(Field & in, Field & out){
|
||||||
|
|
||||||
|
}
|
||||||
|
virtual RealD M3(Field & p, Field & mp,Field & mmp, Field & tmp){
|
||||||
|
|
||||||
|
}
|
||||||
|
virtual void Vstart(Field & in, Field & src, Field & r, Field & mp, Field & mmp, Field & tmp){
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
template<class Field>
|
||||||
|
class TwoLevelFlexiblePcgAD : public TwoLevelFlexiblePcg<Field> {
|
||||||
|
public:
|
||||||
|
virtual void M(Field & in,Field & out,Field & tmp);
|
||||||
|
virtual void M1(Field & in, Field & out,Field & tmp,Field & mp);
|
||||||
|
virtual void M2(Field & in, Field & out);
|
||||||
|
virtual RealD M3(Field & p, Field & mp,Field & mmp, Field & tmp);
|
||||||
|
virtual void Vstart(Field & in, Field & src, Field & r, Field & mp, Field & mmp, Field & tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Field>
|
||||||
|
class TwoLevelFlexiblePcgDef1 : public TwoLevelFlexiblePcg<Field> {
|
||||||
|
public:
|
||||||
|
virtual void M(Field & in,Field & out,Field & tmp);
|
||||||
|
virtual void M1(Field & in, Field & out,Field & tmp,Field & mp);
|
||||||
|
virtual void M2(Field & in, Field & out);
|
||||||
|
virtual RealD M3(Field & p, Field & mp,Field & mmp, Field & tmp);
|
||||||
|
virtual void Vstart(Field & in, Field & src, Field & r, Field & mp, Field & mmp, Field & tmp);
|
||||||
|
virtual void Vout (Field & in, Field & out,Field & src,Field & tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Field>
|
||||||
|
class TwoLevelFlexiblePcgDef2 : public TwoLevelFlexiblePcg<Field> {
|
||||||
|
public:
|
||||||
|
virtual void M(Field & in,Field & out,Field & tmp);
|
||||||
|
virtual void M1(Field & in, Field & out,Field & tmp,Field & mp);
|
||||||
|
virtual void M2(Field & in, Field & out);
|
||||||
|
virtual RealD M3(Field & p, Field & mp,Field & mmp, Field & tmp);
|
||||||
|
virtual void Vstart(Field & in, Field & src, Field & r, Field & mp, Field & mmp, Field & tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Field>
|
||||||
|
class TwoLevelFlexiblePcgV11: public TwoLevelFlexiblePcg<Field> {
|
||||||
|
public:
|
||||||
|
virtual void M(Field & in,Field & out,Field & tmp);
|
||||||
|
virtual void M1(Field & in, Field & out,Field & tmp,Field & mp);
|
||||||
|
virtual void M2(Field & in, Field & out);
|
||||||
|
virtual RealD M3(Field & p, Field & mp,Field & mmp, Field & tmp);
|
||||||
|
virtual void Vstart(Field & in, Field & src, Field & r, Field & mp, Field & mmp, Field & tmp);
|
||||||
|
}
|
||||||
|
*/
|
||||||
#endif
|
#endif
|
||||||
@@ -1,734 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/AdefGeneric.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Peter Boyle <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 */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compared to Tang-2009: P=Pleft. P^T = PRight Q=MssInv.
|
|
||||||
* Script A = SolverMatrix
|
|
||||||
* Script P = Preconditioner
|
|
||||||
*
|
|
||||||
* Implement ADEF-2
|
|
||||||
*
|
|
||||||
* Vstart = P^Tx + Qb
|
|
||||||
* M1 = P^TM + Q
|
|
||||||
* M2=M3=1
|
|
||||||
*/
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
|
|
||||||
template<class Field>
|
|
||||||
class TwoLevelCGmrhs
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
RealD Tolerance;
|
|
||||||
Integer MaxIterations;
|
|
||||||
GridBase *grid;
|
|
||||||
|
|
||||||
// Fine operator, Smoother, CoarseSolver
|
|
||||||
LinearOperatorBase<Field> &_FineLinop;
|
|
||||||
LinearFunction<Field> &_Smoother;
|
|
||||||
MultiRHSBlockCGLinalg<Field> _BlockCGLinalg;
|
|
||||||
|
|
||||||
GridStopWatch ProjectTimer;
|
|
||||||
GridStopWatch PromoteTimer;
|
|
||||||
GridStopWatch DeflateTimer;
|
|
||||||
GridStopWatch CoarseTimer;
|
|
||||||
GridStopWatch FineTimer;
|
|
||||||
GridStopWatch SmoothTimer;
|
|
||||||
GridStopWatch InsertTimer;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Field rrr;
|
|
||||||
Field sss;
|
|
||||||
Field qqq;
|
|
||||||
Field zzz;
|
|
||||||
*/
|
|
||||||
// more most opertor functions
|
|
||||||
TwoLevelCGmrhs(RealD tol,
|
|
||||||
Integer maxit,
|
|
||||||
LinearOperatorBase<Field> &FineLinop,
|
|
||||||
LinearFunction<Field> &Smoother,
|
|
||||||
GridBase *fine) :
|
|
||||||
Tolerance(tol),
|
|
||||||
MaxIterations(maxit),
|
|
||||||
_FineLinop(FineLinop),
|
|
||||||
_Smoother(Smoother)
|
|
||||||
/*
|
|
||||||
rrr(fine),
|
|
||||||
sss(fine),
|
|
||||||
qqq(fine),
|
|
||||||
zzz(fine)
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
grid = fine;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Vector case
|
|
||||||
virtual void operator() (std::vector<Field> &src, std::vector<Field> &x)
|
|
||||||
{
|
|
||||||
SolveSingleSystem(src,x);
|
|
||||||
// SolvePrecBlockCG(src,x);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Thin QR factorisation (google it)
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//Dimensions
|
|
||||||
// R_{ferm x Nblock} = Q_{ferm x Nblock} x C_{Nblock x Nblock} -> ferm x Nblock
|
|
||||||
//
|
|
||||||
// Rdag R = m_rr = Herm = L L^dag <-- Cholesky decomposition (LLT routine in Eigen)
|
|
||||||
//
|
|
||||||
// Q C = R => Q = R C^{-1}
|
|
||||||
//
|
|
||||||
// Want Ident = Q^dag Q = C^{-dag} R^dag R C^{-1} = C^{-dag} L L^dag C^{-1} = 1_{Nblock x Nblock}
|
|
||||||
//
|
|
||||||
// Set C = L^{dag}, and then Q^dag Q = ident
|
|
||||||
//
|
|
||||||
// Checks:
|
|
||||||
// Cdag C = Rdag R ; passes.
|
|
||||||
// QdagQ = 1 ; passes
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void ThinQRfact (Eigen::MatrixXcd &m_zz,
|
|
||||||
Eigen::MatrixXcd &C,
|
|
||||||
Eigen::MatrixXcd &Cinv,
|
|
||||||
std::vector<Field> & Q,
|
|
||||||
std::vector<Field> & MQ,
|
|
||||||
const std::vector<Field> & Z,
|
|
||||||
const std::vector<Field> & MZ)
|
|
||||||
{
|
|
||||||
RealD t0=usecond();
|
|
||||||
_BlockCGLinalg.InnerProductMatrix(m_zz,MZ,Z);
|
|
||||||
RealD t1=usecond();
|
|
||||||
|
|
||||||
m_zz = 0.5*(m_zz+m_zz.adjoint());
|
|
||||||
|
|
||||||
Eigen::MatrixXcd L = m_zz.llt().matrixL();
|
|
||||||
|
|
||||||
C = L.adjoint();
|
|
||||||
Cinv = C.inverse();
|
|
||||||
|
|
||||||
RealD t3=usecond();
|
|
||||||
_BlockCGLinalg.MulMatrix( Q,Cinv,Z);
|
|
||||||
_BlockCGLinalg.MulMatrix(MQ,Cinv,MZ);
|
|
||||||
RealD t4=usecond();
|
|
||||||
std::cout << " ThinQRfact IP :"<< t1-t0<<" us"<<std::endl;
|
|
||||||
std::cout << " ThinQRfact Eigen :"<< t3-t1<<" us"<<std::endl;
|
|
||||||
std::cout << " ThinQRfact MulMat:"<< t4-t3<<" us"<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void SolvePrecBlockCG (std::vector<Field> &src, std::vector<Field> &X)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<<"HDCG: mrhs fPrecBlockcg starting"<<std::endl;
|
|
||||||
src[0].Grid()->Barrier();
|
|
||||||
int nrhs = src.size();
|
|
||||||
// std::vector<RealD> f(nrhs);
|
|
||||||
// std::vector<RealD> rtzp(nrhs);
|
|
||||||
// std::vector<RealD> rtz(nrhs);
|
|
||||||
// std::vector<RealD> a(nrhs);
|
|
||||||
// std::vector<RealD> d(nrhs);
|
|
||||||
// std::vector<RealD> b(nrhs);
|
|
||||||
// std::vector<RealD> rptzp(nrhs);
|
|
||||||
|
|
||||||
////////////////////////////////////////////
|
|
||||||
//Initial residual computation & set up
|
|
||||||
////////////////////////////////////////////
|
|
||||||
std::vector<RealD> ssq(nrhs);
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
|
||||||
ssq[rhs]=norm2(src[rhs]); GRID_ASSERT(ssq[rhs]!=0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////
|
|
||||||
// Fields -- eliminate duplicates between fPcg and block cg
|
|
||||||
///////////////////////////
|
|
||||||
std::vector<Field> Mtmp(nrhs,grid);
|
|
||||||
std::vector<Field> tmp(nrhs,grid);
|
|
||||||
std::vector<Field> Z(nrhs,grid); // Rename Z to R
|
|
||||||
std::vector<Field> MZ(nrhs,grid); // Rename MZ to Z
|
|
||||||
std::vector<Field> Q(nrhs,grid); //
|
|
||||||
std::vector<Field> MQ(nrhs,grid); // Rename to P
|
|
||||||
std::vector<Field> D(nrhs,grid);
|
|
||||||
std::vector<Field> AD(nrhs,grid);
|
|
||||||
|
|
||||||
/************************************************************************
|
|
||||||
* Preconditioned Block conjugate gradient rQ
|
|
||||||
* Generalise Sebastien Birk Thesis, after Dubrulle 2001.
|
|
||||||
* Introduce preconditioning following Saad Ch9
|
|
||||||
************************************************************************
|
|
||||||
* Dimensions:
|
|
||||||
*
|
|
||||||
* X,B etc... ==(Nferm x nrhs)
|
|
||||||
* Matrix A==(Nferm x Nferm)
|
|
||||||
*
|
|
||||||
* Nferm = Nspin x Ncolour x Ncomplex x Nlattice_site
|
|
||||||
* QC => Thin QR factorisation (google it)
|
|
||||||
*
|
|
||||||
* R = B-AX
|
|
||||||
* Z = Mi R
|
|
||||||
* QC = Z
|
|
||||||
* D = Q
|
|
||||||
* for k:
|
|
||||||
* R = AD
|
|
||||||
* Z = Mi R
|
|
||||||
* M = [D^dag R]^{-1}
|
|
||||||
* X = X + D M C
|
|
||||||
* QS = Q - Z.M
|
|
||||||
* D = Q + D S^dag
|
|
||||||
* C = S C
|
|
||||||
*/
|
|
||||||
Eigen::MatrixXcd m_DZ = Eigen::MatrixXcd::Identity(nrhs,nrhs);
|
|
||||||
Eigen::MatrixXcd m_M = Eigen::MatrixXcd::Identity(nrhs,nrhs);
|
|
||||||
Eigen::MatrixXcd m_zz = Eigen::MatrixXcd::Zero(nrhs,nrhs);
|
|
||||||
Eigen::MatrixXcd m_rr = Eigen::MatrixXcd::Zero(nrhs,nrhs);
|
|
||||||
|
|
||||||
Eigen::MatrixXcd m_C = Eigen::MatrixXcd::Zero(nrhs,nrhs);
|
|
||||||
Eigen::MatrixXcd m_Cinv = Eigen::MatrixXcd::Zero(nrhs,nrhs);
|
|
||||||
Eigen::MatrixXcd m_S = Eigen::MatrixXcd::Zero(nrhs,nrhs);
|
|
||||||
Eigen::MatrixXcd m_Sinv = Eigen::MatrixXcd::Zero(nrhs,nrhs);
|
|
||||||
|
|
||||||
Eigen::MatrixXcd m_tmp = Eigen::MatrixXcd::Identity(nrhs,nrhs);
|
|
||||||
Eigen::MatrixXcd m_tmp1 = Eigen::MatrixXcd::Identity(nrhs,nrhs);
|
|
||||||
|
|
||||||
GridStopWatch HDCGTimer;
|
|
||||||
|
|
||||||
//////////////////////////
|
|
||||||
// x0 = Vstart -- possibly modify guess
|
|
||||||
//////////////////////////
|
|
||||||
Vstart(X,src);
|
|
||||||
|
|
||||||
//////////////////////////
|
|
||||||
// R = B-AX
|
|
||||||
//////////////////////////
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
|
||||||
// r0 = b -A x0
|
|
||||||
_FineLinop.HermOp(X[rhs],tmp[rhs]);
|
|
||||||
axpy (Z[rhs], -1.0,tmp[rhs], src[rhs]); // Computes R=Z=src - A X0
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////
|
|
||||||
// Compute MZ = M1 Z = M1 B - M1 A x0
|
|
||||||
//////////////////////////////////
|
|
||||||
PcgM1(Z,MZ);
|
|
||||||
|
|
||||||
//////////////////////////////////
|
|
||||||
// QC = Z
|
|
||||||
//////////////////////////////////
|
|
||||||
ThinQRfact (m_zz, m_C, m_Cinv, Q, MQ, Z, MZ);
|
|
||||||
|
|
||||||
//////////////////////////////////
|
|
||||||
// D=MQ
|
|
||||||
//////////////////////////////////
|
|
||||||
for(int b=0;b<nrhs;b++) D[b]=MQ[b]; // LLT rotation of the MZ basis of search dirs
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<<"PrecBlockCGrQ vec computed initial residual and QR fact " <<std::endl;
|
|
||||||
|
|
||||||
ProjectTimer.Reset();
|
|
||||||
PromoteTimer.Reset();
|
|
||||||
DeflateTimer.Reset();
|
|
||||||
CoarseTimer.Reset();
|
|
||||||
SmoothTimer.Reset();
|
|
||||||
FineTimer.Reset();
|
|
||||||
InsertTimer.Reset();
|
|
||||||
|
|
||||||
GridStopWatch M1Timer;
|
|
||||||
GridStopWatch M2Timer;
|
|
||||||
GridStopWatch M3Timer;
|
|
||||||
GridStopWatch LinalgTimer;
|
|
||||||
GridStopWatch InnerProdTimer;
|
|
||||||
|
|
||||||
HDCGTimer.Start();
|
|
||||||
|
|
||||||
std::vector<RealD> rn(nrhs);
|
|
||||||
for (int k=0;k<=MaxIterations;k++){
|
|
||||||
|
|
||||||
////////////////////
|
|
||||||
// Z = AD
|
|
||||||
////////////////////
|
|
||||||
M3Timer.Start();
|
|
||||||
for(int b=0;b<nrhs;b++) _FineLinop.HermOp(D[b], Z[b]);
|
|
||||||
M3Timer.Stop();
|
|
||||||
|
|
||||||
////////////////////
|
|
||||||
// MZ = M1 Z <==== the Multigrid preconditioner
|
|
||||||
////////////////////
|
|
||||||
M1Timer.Start();
|
|
||||||
PcgM1(Z,MZ);
|
|
||||||
M1Timer.Stop();
|
|
||||||
|
|
||||||
FineTimer.Start();
|
|
||||||
////////////////////
|
|
||||||
// M = [D^dag Z]^{-1} = (<Ddag MZ>_M)^{-1} inner prod, generalising Saad derivation of Precon CG
|
|
||||||
////////////////////
|
|
||||||
InnerProdTimer.Start();
|
|
||||||
_BlockCGLinalg.InnerProductMatrix(m_DZ,D,Z);
|
|
||||||
InnerProdTimer.Stop();
|
|
||||||
m_M = m_DZ.inverse();
|
|
||||||
|
|
||||||
///////////////////////////
|
|
||||||
// X = X + D MC
|
|
||||||
///////////////////////////
|
|
||||||
m_tmp = m_M * m_C;
|
|
||||||
LinalgTimer.Start();
|
|
||||||
_BlockCGLinalg.MaddMatrix(X,m_tmp, D,X); // D are the search directions and X takes the updates
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
///////////////////////////
|
|
||||||
// QS = Q - M Z
|
|
||||||
// (MQ) S = MQ - M (M1Z)
|
|
||||||
///////////////////////////
|
|
||||||
LinalgTimer.Start();
|
|
||||||
_BlockCGLinalg.MaddMatrix(tmp ,m_M, Z, Q,-1.0);
|
|
||||||
_BlockCGLinalg.MaddMatrix(Mtmp,m_M,MZ,MQ,-1.0);
|
|
||||||
ThinQRfact (m_zz, m_S, m_Sinv, Q, MQ, tmp, Mtmp);
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
////////////////////////////
|
|
||||||
// D = MQ + D S^dag
|
|
||||||
////////////////////////////
|
|
||||||
m_tmp = m_S.adjoint();
|
|
||||||
LinalgTimer.Start();
|
|
||||||
_BlockCGLinalg.MaddMatrix(D,m_tmp,D,MQ);
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
////////////////////////////
|
|
||||||
// C = S C
|
|
||||||
////////////////////////////
|
|
||||||
m_C = m_S*m_C;
|
|
||||||
|
|
||||||
////////////////////////////
|
|
||||||
// convergence monitor
|
|
||||||
////////////////////////////
|
|
||||||
m_rr = m_C.adjoint() * m_C;
|
|
||||||
|
|
||||||
FineTimer.Stop();
|
|
||||||
|
|
||||||
RealD max_resid=0;
|
|
||||||
RealD rrsum=0;
|
|
||||||
RealD sssum=0;
|
|
||||||
RealD rr;
|
|
||||||
|
|
||||||
for(int b=0;b<nrhs;b++) {
|
|
||||||
rrsum+=real(m_rr(b,b));
|
|
||||||
sssum+=ssq[b];
|
|
||||||
rr = real(m_rr(b,b))/ssq[b];
|
|
||||||
if ( rr > max_resid ) max_resid = rr;
|
|
||||||
}
|
|
||||||
std::cout << GridLogMessage <<
|
|
||||||
"\t Prec BlockCGrQ Iteration "<<k<<" ave resid "<< std::sqrt(rrsum/sssum) << " max "<< std::sqrt(max_resid) <<std::endl;
|
|
||||||
|
|
||||||
|
|
||||||
if ( max_resid < Tolerance*Tolerance ) {
|
|
||||||
|
|
||||||
HDCGTimer.Stop();
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ converged in "<<k<<" iterations and "<<HDCGTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Linalg "<<LinalgTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : fine H "<<M3Timer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : prec M1 "<<M1Timer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"**** M1 breakdown:"<<std::endl;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Project "<<ProjectTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Promote "<<PromoteTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Deflate "<<DeflateTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Coarse "<<CoarseTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Fine "<<FineTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Smooth "<<SmoothTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs PrecBlockCGrQ : Insert "<<InsertTimer.Elapsed()<<std::endl;;
|
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
|
||||||
|
|
||||||
_FineLinop.HermOp(X[rhs],tmp[rhs]);
|
|
||||||
|
|
||||||
Field mytmp(grid);
|
|
||||||
axpy(mytmp,-1.0,src[rhs],tmp[rhs]);
|
|
||||||
|
|
||||||
RealD xnorm = sqrt(norm2(X[rhs]));
|
|
||||||
RealD srcnorm = sqrt(norm2(src[rhs]));
|
|
||||||
RealD tmpnorm = sqrt(norm2(mytmp));
|
|
||||||
RealD true_residual = tmpnorm/srcnorm;
|
|
||||||
std::cout<<GridLogMessage
|
|
||||||
<<"HDCG: true residual ["<<rhs<<"] is "<<true_residual
|
|
||||||
<<" solution "<<xnorm
|
|
||||||
<<" source "<<srcnorm
|
|
||||||
<<std::endl;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
HDCGTimer.Stop();
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: PrecBlockCGrQ not converged "<<HDCGTimer.Elapsed()<<std::endl;
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void SolveSingleSystem (std::vector<Field> &src, std::vector<Field> &x)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<<"HDCG: mrhs fPcg starting"<<std::endl;
|
|
||||||
src[0].Grid()->Barrier();
|
|
||||||
int nrhs = src.size();
|
|
||||||
std::vector<RealD> f(nrhs);
|
|
||||||
std::vector<RealD> rtzp(nrhs);
|
|
||||||
std::vector<RealD> rtz(nrhs);
|
|
||||||
std::vector<RealD> a(nrhs);
|
|
||||||
std::vector<RealD> d(nrhs);
|
|
||||||
std::vector<RealD> b(nrhs);
|
|
||||||
std::vector<RealD> rptzp(nrhs);
|
|
||||||
/////////////////////////////
|
|
||||||
// Set up history vectors
|
|
||||||
/////////////////////////////
|
|
||||||
int mmax = 3;
|
|
||||||
|
|
||||||
std::vector<std::vector<Field> > p(nrhs); for(int r=0;r<nrhs;r++) p[r].resize(mmax,grid);
|
|
||||||
std::vector<std::vector<Field> > mmp(nrhs); for(int r=0;r<nrhs;r++) mmp[r].resize(mmax,grid);
|
|
||||||
std::vector<std::vector<RealD> > pAp(nrhs); for(int r=0;r<nrhs;r++) pAp[r].resize(mmax);
|
|
||||||
|
|
||||||
std::vector<Field> z(nrhs,grid);
|
|
||||||
std::vector<Field> mp (nrhs,grid);
|
|
||||||
std::vector<Field> r (nrhs,grid);
|
|
||||||
std::vector<Field> mu (nrhs,grid);
|
|
||||||
|
|
||||||
//Initial residual computation & set up
|
|
||||||
std::vector<RealD> src_nrm(nrhs);
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
|
||||||
src_nrm[rhs]=norm2(src[rhs]);
|
|
||||||
GRID_ASSERT(src_nrm[rhs]!=0.0);
|
|
||||||
}
|
|
||||||
std::vector<RealD> tn(nrhs);
|
|
||||||
|
|
||||||
GridStopWatch HDCGTimer;
|
|
||||||
//////////////////////////
|
|
||||||
// x0 = Vstart -- possibly modify guess
|
|
||||||
//////////////////////////
|
|
||||||
Vstart(x,src);
|
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
|
||||||
// r0 = b -A x0
|
|
||||||
_FineLinop.HermOp(x[rhs],mmp[rhs][0]);
|
|
||||||
axpy (r[rhs], -1.0,mmp[rhs][0], src[rhs]); // Recomputes r=src-Ax0
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////
|
|
||||||
// Compute z = M1 x
|
|
||||||
//////////////////////////////////
|
|
||||||
// This needs a multiRHS version for acceleration
|
|
||||||
PcgM1(r,z);
|
|
||||||
|
|
||||||
std::vector<RealD> ssq(nrhs);
|
|
||||||
std::vector<RealD> rsq(nrhs);
|
|
||||||
std::vector<Field> pp(nrhs,grid);
|
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
|
||||||
rtzp[rhs] =real(innerProduct(r[rhs],z[rhs]));
|
|
||||||
p[rhs][0]=z[rhs];
|
|
||||||
ssq[rhs]=norm2(src[rhs]);
|
|
||||||
rsq[rhs]= ssq[rhs]*Tolerance*Tolerance;
|
|
||||||
// std::cout << GridLogMessage<<"mrhs HDCG: "<<rhs<<" k=0 residual "<<rtzp[rhs]<<" rsq "<<rsq[rhs]<<"\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
ProjectTimer.Reset();
|
|
||||||
PromoteTimer.Reset();
|
|
||||||
DeflateTimer.Reset();
|
|
||||||
CoarseTimer.Reset();
|
|
||||||
SmoothTimer.Reset();
|
|
||||||
FineTimer.Reset();
|
|
||||||
InsertTimer.Reset();
|
|
||||||
|
|
||||||
GridStopWatch M1Timer;
|
|
||||||
GridStopWatch M2Timer;
|
|
||||||
GridStopWatch M3Timer;
|
|
||||||
GridStopWatch LinalgTimer;
|
|
||||||
|
|
||||||
HDCGTimer.Start();
|
|
||||||
|
|
||||||
std::vector<RealD> rn(nrhs);
|
|
||||||
for (int k=0;k<=MaxIterations;k++){
|
|
||||||
|
|
||||||
int peri_k = k % mmax;
|
|
||||||
int peri_kp = (k+1) % mmax;
|
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
|
||||||
rtz[rhs]=rtzp[rhs];
|
|
||||||
M3Timer.Start();
|
|
||||||
d[rhs]= PcgM3(p[rhs][peri_k],mmp[rhs][peri_k]);
|
|
||||||
M3Timer.Stop();
|
|
||||||
a[rhs] = rtz[rhs]/d[rhs];
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
// Memorise this
|
|
||||||
pAp[rhs][peri_k] = d[rhs];
|
|
||||||
|
|
||||||
axpy(x[rhs],a[rhs],p[rhs][peri_k],x[rhs]);
|
|
||||||
rn[rhs] = axpy_norm(r[rhs],-a[rhs],mmp[rhs][peri_k],r[rhs]);
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute z = M x (for *all* RHS)
|
|
||||||
M1Timer.Start();
|
|
||||||
PcgM1(r,z);
|
|
||||||
M1Timer.Stop();
|
|
||||||
|
|
||||||
RealD max_rn=0.0;
|
|
||||||
LinalgTimer.Start();
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
|
||||||
|
|
||||||
rtzp[rhs] =real(innerProduct(r[rhs],z[rhs]));
|
|
||||||
|
|
||||||
// std::cout << GridLogMessage<<"HDCG::fPcg rhs"<<rhs<<" iteration "<<k<<" : inner rtzp "<<rtzp[rhs]<<"\n";
|
|
||||||
mu[rhs]=z[rhs];
|
|
||||||
|
|
||||||
p[rhs][peri_kp]=mu[rhs];
|
|
||||||
|
|
||||||
// Standard search direction p == z + b p
|
|
||||||
b[rhs] = (rtzp[rhs])/rtz[rhs];
|
|
||||||
|
|
||||||
int northog = (k>mmax-1)?(mmax-1):k; // This is the fCG-Tr(mmax-1) algorithm
|
|
||||||
for(int back=0; back < northog; back++){
|
|
||||||
int peri_back = (k-back)%mmax;
|
|
||||||
RealD pbApk= real(innerProduct(mmp[rhs][peri_back],p[rhs][peri_kp]));
|
|
||||||
RealD beta = -pbApk/pAp[rhs][peri_back];
|
|
||||||
axpy(p[rhs][peri_kp],beta,p[rhs][peri_back],p[rhs][peri_kp]);
|
|
||||||
}
|
|
||||||
|
|
||||||
RealD rrn=sqrt(rn[rhs]/ssq[rhs]);
|
|
||||||
RealD rtn=sqrt(rtz[rhs]/ssq[rhs]);
|
|
||||||
RealD rtnp=sqrt(rtzp[rhs]/ssq[rhs]);
|
|
||||||
|
|
||||||
std::cout<<GridLogMessage<<"HDCG:fPcg rhs "<<rhs<<" k= "<<k<<" residual = "<<rrn<<"\n";
|
|
||||||
if ( rrn > max_rn ) max_rn = rrn;
|
|
||||||
}
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
// Stopping condition based on worst case
|
|
||||||
if ( max_rn <= Tolerance ) {
|
|
||||||
|
|
||||||
HDCGTimer.Stop();
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg converged in "<<k<<" iterations and "<<HDCGTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Linalg "<<LinalgTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : fine M3 "<<M3Timer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : prec M1 "<<M1Timer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"**** M1 breakdown:"<<std::endl;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Project "<<ProjectTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Promote "<<PromoteTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Deflate "<<DeflateTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Coarse "<<CoarseTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Fine "<<FineTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Smooth "<<SmoothTimer.Elapsed()<<std::endl;;
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: mrhs fPcg : Insert "<<InsertTimer.Elapsed()<<std::endl;;
|
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
|
||||||
_FineLinop.HermOp(x[rhs],mmp[rhs][0]);
|
|
||||||
Field tmp(grid);
|
|
||||||
axpy(tmp,-1.0,src[rhs],mmp[rhs][0]);
|
|
||||||
|
|
||||||
RealD mmpnorm = sqrt(norm2(mmp[rhs][0]));
|
|
||||||
RealD xnorm = sqrt(norm2(x[rhs]));
|
|
||||||
RealD srcnorm = sqrt(norm2(src[rhs]));
|
|
||||||
RealD tmpnorm = sqrt(norm2(tmp));
|
|
||||||
RealD true_residual = tmpnorm/srcnorm;
|
|
||||||
std::cout<<GridLogMessage
|
|
||||||
<<"HDCG: true residual ["<<rhs<<"] is "<<true_residual
|
|
||||||
<<" solution "<<xnorm
|
|
||||||
<<" source "<<srcnorm
|
|
||||||
<<" mmp "<<mmpnorm
|
|
||||||
<<std::endl;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
HDCGTimer.Stop();
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: not converged "<<HDCGTimer.Elapsed()<<std::endl;
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++){
|
|
||||||
RealD xnorm = sqrt(norm2(x[rhs]));
|
|
||||||
RealD srcnorm = sqrt(norm2(src[rhs]));
|
|
||||||
std::cout<<GridLogMessage<<"HDCG: non-converged solution "<<xnorm<<" source "<<srcnorm<<std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
virtual void PcgM1(std::vector<Field> & in,std::vector<Field> & out) = 0;
|
|
||||||
virtual void Vstart(std::vector<Field> & x,std::vector<Field> & src) = 0;
|
|
||||||
virtual void PcgM2(const Field & in, Field & out) {
|
|
||||||
out=in;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual RealD PcgM3(const Field & p, Field & mmp){
|
|
||||||
RealD dd;
|
|
||||||
_FineLinop.HermOp(p,mmp);
|
|
||||||
ComplexD dot = innerProduct(p,mmp);
|
|
||||||
dd=real(dot);
|
|
||||||
return dd;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Field, class CoarseField>
|
|
||||||
class TwoLevelADEF2mrhs : public TwoLevelCGmrhs<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
GridBase *coarsegrid;
|
|
||||||
GridBase *coarsegridmrhs;
|
|
||||||
LinearFunction<CoarseField> &_CoarseSolverMrhs;
|
|
||||||
LinearFunction<CoarseField> &_CoarseSolverPreciseMrhs;
|
|
||||||
MultiRHSBlockProject<Field> &_Projector;
|
|
||||||
MultiRHSDeflation<CoarseField> &_Deflator;
|
|
||||||
|
|
||||||
|
|
||||||
TwoLevelADEF2mrhs(RealD tol,
|
|
||||||
Integer maxit,
|
|
||||||
LinearOperatorBase<Field> &FineLinop,
|
|
||||||
LinearFunction<Field> &Smoother,
|
|
||||||
LinearFunction<CoarseField> &CoarseSolverMrhs,
|
|
||||||
LinearFunction<CoarseField> &CoarseSolverPreciseMrhs,
|
|
||||||
MultiRHSBlockProject<Field> &Projector,
|
|
||||||
MultiRHSDeflation<CoarseField> &Deflator,
|
|
||||||
GridBase *_coarsemrhsgrid) :
|
|
||||||
TwoLevelCGmrhs<Field>(tol, maxit,FineLinop,Smoother,Projector.fine_grid),
|
|
||||||
_CoarseSolverMrhs(CoarseSolverMrhs),
|
|
||||||
_CoarseSolverPreciseMrhs(CoarseSolverPreciseMrhs),
|
|
||||||
_Projector(Projector),
|
|
||||||
_Deflator(Deflator)
|
|
||||||
{
|
|
||||||
coarsegrid = Projector.coarse_grid;
|
|
||||||
coarsegridmrhs = _coarsemrhsgrid;// Thi could be in projector
|
|
||||||
};
|
|
||||||
|
|
||||||
// Override Vstart
|
|
||||||
virtual void Vstart(std::vector<Field> & x,std::vector<Field> & src)
|
|
||||||
{
|
|
||||||
int nrhs=x.size();
|
|
||||||
///////////////////////////////////
|
|
||||||
// Choose x_0 such that
|
|
||||||
// x_0 = guess + (A_ss^inv) r_s = guess + Ass_inv [src -Aguess]
|
|
||||||
// = [1 - Ass_inv A] Guess + Assinv src
|
|
||||||
// = P^T guess + Assinv src
|
|
||||||
// = Vstart [Tang notation]
|
|
||||||
// This gives:
|
|
||||||
// W^T (src - A x_0) = src_s - A guess_s - r_s
|
|
||||||
// = src_s - (A guess)_s - src_s + (A guess)_s
|
|
||||||
// = 0
|
|
||||||
///////////////////////////////////
|
|
||||||
std::vector<CoarseField> PleftProj(nrhs,this->coarsegrid);
|
|
||||||
std::vector<CoarseField> PleftMss_proj(nrhs,this->coarsegrid);
|
|
||||||
CoarseField PleftProjMrhs(this->coarsegridmrhs);
|
|
||||||
CoarseField PleftMss_projMrhs(this->coarsegridmrhs);
|
|
||||||
|
|
||||||
this->_Projector.blockProject(src,PleftProj);
|
|
||||||
this->_Deflator.DeflateSources(PleftProj,PleftMss_proj);
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
|
||||||
InsertSliceFast(PleftProj[rhs],PleftProjMrhs,rhs,0);
|
|
||||||
InsertSliceFast(PleftMss_proj[rhs],PleftMss_projMrhs,rhs,0); // the guess
|
|
||||||
}
|
|
||||||
|
|
||||||
this->_CoarseSolverPreciseMrhs(PleftProjMrhs,PleftMss_projMrhs); // Ass^{-1} r_s
|
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
|
||||||
ExtractSliceFast(PleftMss_proj[rhs],PleftMss_projMrhs,rhs,0);
|
|
||||||
}
|
|
||||||
this->_Projector.blockPromote(x,PleftMss_proj);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void PcgM1(std::vector<Field> & in,std::vector<Field> & out){
|
|
||||||
|
|
||||||
int nrhs=in.size();
|
|
||||||
|
|
||||||
// [PTM+Q] in = [1 - Q A] M in + Q in = Min + Q [ in -A Min]
|
|
||||||
std::vector<Field> tmp(nrhs,this->grid);
|
|
||||||
std::vector<Field> Min(nrhs,this->grid);
|
|
||||||
|
|
||||||
std::vector<CoarseField> PleftProj(nrhs,this->coarsegrid);
|
|
||||||
std::vector<CoarseField> PleftMss_proj(nrhs,this->coarsegrid);
|
|
||||||
|
|
||||||
CoarseField PleftProjMrhs(this->coarsegridmrhs);
|
|
||||||
CoarseField PleftMss_projMrhs(this->coarsegridmrhs);
|
|
||||||
|
|
||||||
// this->rrr=in[0];
|
|
||||||
|
|
||||||
#undef SMOOTHER_BLOCK_SOLVE
|
|
||||||
#if SMOOTHER_BLOCK_SOLVE
|
|
||||||
this->SmoothTimer.Start();
|
|
||||||
this->_Smoother(in,Min);
|
|
||||||
this->SmoothTimer.Stop();
|
|
||||||
#else
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
|
||||||
this->SmoothTimer.Start();
|
|
||||||
this->_Smoother(in[rhs],Min[rhs]);
|
|
||||||
this->SmoothTimer.Stop();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// this->sss=Min[0];
|
|
||||||
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
|
||||||
|
|
||||||
this->FineTimer.Start();
|
|
||||||
this->_FineLinop.HermOp(Min[rhs],out[rhs]);
|
|
||||||
axpy(tmp[rhs],-1.0,out[rhs],in[rhs]); // resid = in - A Min
|
|
||||||
this->FineTimer.Stop();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
this->ProjectTimer.Start();
|
|
||||||
this->_Projector.blockProject(tmp,PleftProj);
|
|
||||||
this->ProjectTimer.Stop();
|
|
||||||
this->DeflateTimer.Start();
|
|
||||||
this->_Deflator.DeflateSources(PleftProj,PleftMss_proj);
|
|
||||||
this->DeflateTimer.Stop();
|
|
||||||
this->InsertTimer.Start();
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
|
||||||
InsertSliceFast(PleftProj[rhs],PleftProjMrhs,rhs,0);
|
|
||||||
InsertSliceFast(PleftMss_proj[rhs],PleftMss_projMrhs,rhs,0); // the guess
|
|
||||||
}
|
|
||||||
this->InsertTimer.Stop();
|
|
||||||
|
|
||||||
this->CoarseTimer.Start();
|
|
||||||
this->_CoarseSolverMrhs(PleftProjMrhs,PleftMss_projMrhs); // Ass^{-1} [in - A Min]_s
|
|
||||||
this->CoarseTimer.Stop();
|
|
||||||
|
|
||||||
this->InsertTimer.Start();
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
|
||||||
ExtractSliceFast(PleftMss_proj[rhs],PleftMss_projMrhs,rhs,0);
|
|
||||||
}
|
|
||||||
this->InsertTimer.Stop();
|
|
||||||
this->PromoteTimer.Start();
|
|
||||||
this->_Projector.blockPromote(tmp,PleftMss_proj);// tmp= Q[in - A Min]
|
|
||||||
this->PromoteTimer.Stop();
|
|
||||||
this->FineTimer.Start();
|
|
||||||
// this->qqq=tmp[0];
|
|
||||||
for(int rhs=0;rhs<nrhs;rhs++) {
|
|
||||||
axpy(out[rhs],1.0,Min[rhs],tmp[rhs]); // Min+tmp
|
|
||||||
}
|
|
||||||
// this->zzz=out[0];
|
|
||||||
this->FineTimer.Stop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,234 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/BiCGSTAB.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>
|
|
||||||
Author: juettner <juettner@soton.ac.uk>
|
|
||||||
Author: David Murphy <djmurphy@mit.edu>
|
|
||||||
|
|
||||||
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_BICGSTAB_H
|
|
||||||
#define GRID_BICGSTAB_H
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
// Base classes for iterative processes based on operators
|
|
||||||
// single input vec, single output vec.
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
template <class Field>
|
|
||||||
class BiCGSTAB : public OperatorFunction<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using OperatorFunction<Field>::operator();
|
|
||||||
|
|
||||||
bool ErrorOnNoConverge; // throw an GRID_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
|
|
||||||
|
|
||||||
BiCGSTAB(RealD tol, Integer maxit, bool err_on_no_conv = true) :
|
|
||||||
Tolerance(tol), MaxIterations(maxit), ErrorOnNoConverge(err_on_no_conv){};
|
|
||||||
|
|
||||||
void operator()(LinearOperatorBase<Field>& Linop, const Field& src, Field& psi)
|
|
||||||
{
|
|
||||||
psi.Checkerboard() = src.Checkerboard();
|
|
||||||
conformable(psi, src);
|
|
||||||
|
|
||||||
RealD cp(0), rho(1), rho_prev(0), alpha(1), beta(0), omega(1);
|
|
||||||
RealD a(0), bo(0), b(0), ssq(0);
|
|
||||||
|
|
||||||
Field p(src);
|
|
||||||
Field r(src);
|
|
||||||
Field rhat(src);
|
|
||||||
Field v(src);
|
|
||||||
Field s(src);
|
|
||||||
Field t(src);
|
|
||||||
Field h(src);
|
|
||||||
|
|
||||||
v = Zero();
|
|
||||||
p = Zero();
|
|
||||||
|
|
||||||
// Initial residual computation & set up
|
|
||||||
RealD guess = norm2(psi);
|
|
||||||
GRID_ASSERT(std::isnan(guess) == 0);
|
|
||||||
|
|
||||||
Linop.Op(psi, v);
|
|
||||||
b = norm2(v);
|
|
||||||
|
|
||||||
r = src - v;
|
|
||||||
rhat = r;
|
|
||||||
a = norm2(r);
|
|
||||||
ssq = norm2(src);
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << std::setprecision(8) << "BiCGSTAB: guess " << guess << std::endl;
|
|
||||||
std::cout << GridLogIterative << std::setprecision(8) << "BiCGSTAB: src " << ssq << std::endl;
|
|
||||||
std::cout << GridLogIterative << std::setprecision(8) << "BiCGSTAB: mp " << b << std::endl;
|
|
||||||
std::cout << GridLogIterative << std::setprecision(8) << "BiCGSTAB: r " << a << std::endl;
|
|
||||||
|
|
||||||
RealD rsq = Tolerance * Tolerance * ssq;
|
|
||||||
|
|
||||||
// Check if guess is really REALLY good :)
|
|
||||||
if(a <= rsq){ return; }
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << std::setprecision(8) << "BiCGSTAB: k=0 residual " << a << " target " << rsq << std::endl;
|
|
||||||
|
|
||||||
GridStopWatch LinalgTimer;
|
|
||||||
GridStopWatch InnerTimer;
|
|
||||||
GridStopWatch AxpyNormTimer;
|
|
||||||
GridStopWatch LinearCombTimer;
|
|
||||||
GridStopWatch MatrixTimer;
|
|
||||||
GridStopWatch SolverTimer;
|
|
||||||
|
|
||||||
SolverTimer.Start();
|
|
||||||
int k;
|
|
||||||
for (k = 1; k <= MaxIterations; k++)
|
|
||||||
{
|
|
||||||
rho_prev = rho;
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
InnerTimer.Start();
|
|
||||||
ComplexD Crho = innerProduct(rhat,r);
|
|
||||||
InnerTimer.Stop();
|
|
||||||
rho = Crho.real();
|
|
||||||
|
|
||||||
beta = (rho / rho_prev) * (alpha / omega);
|
|
||||||
|
|
||||||
LinearCombTimer.Start();
|
|
||||||
bo = beta * omega;
|
|
||||||
{
|
|
||||||
autoView( p_v , p, AcceleratorWrite);
|
|
||||||
autoView( r_v , r, AcceleratorRead);
|
|
||||||
autoView( v_v , v, AcceleratorRead);
|
|
||||||
accelerator_for(ss, p_v.size(), Field::vector_object::Nsimd(),{
|
|
||||||
coalescedWrite(p_v[ss], beta*p_v(ss) - bo*v_v(ss) + r_v(ss));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
LinearCombTimer.Stop();
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
MatrixTimer.Start();
|
|
||||||
Linop.Op(p,v);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
InnerTimer.Start();
|
|
||||||
ComplexD Calpha = innerProduct(rhat,v);
|
|
||||||
InnerTimer.Stop();
|
|
||||||
alpha = rho / Calpha.real();
|
|
||||||
|
|
||||||
LinearCombTimer.Start();
|
|
||||||
{
|
|
||||||
autoView( p_v , p, AcceleratorRead);
|
|
||||||
autoView( r_v , r, AcceleratorRead);
|
|
||||||
autoView( v_v , v, AcceleratorRead);
|
|
||||||
autoView( psi_v,psi, AcceleratorRead);
|
|
||||||
autoView( h_v , h, AcceleratorWrite);
|
|
||||||
autoView( s_v , s, AcceleratorWrite);
|
|
||||||
accelerator_for(ss, h_v.size(), Field::vector_object::Nsimd(),{
|
|
||||||
coalescedWrite(h_v[ss], alpha*p_v(ss) + psi_v(ss));
|
|
||||||
});
|
|
||||||
accelerator_for(ss, s_v.size(), Field::vector_object::Nsimd(),{
|
|
||||||
coalescedWrite(s_v[ss], -alpha*v_v(ss) + r_v(ss));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
LinearCombTimer.Stop();
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
MatrixTimer.Start();
|
|
||||||
Linop.Op(s,t);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
InnerTimer.Start();
|
|
||||||
ComplexD Comega = innerProduct(t,s);
|
|
||||||
InnerTimer.Stop();
|
|
||||||
omega = Comega.real() / norm2(t);
|
|
||||||
|
|
||||||
LinearCombTimer.Start();
|
|
||||||
{
|
|
||||||
autoView( psi_v,psi, AcceleratorWrite);
|
|
||||||
autoView( r_v , r, AcceleratorWrite);
|
|
||||||
autoView( h_v , h, AcceleratorRead);
|
|
||||||
autoView( s_v , s, AcceleratorRead);
|
|
||||||
autoView( t_v , t, AcceleratorRead);
|
|
||||||
accelerator_for(ss, psi_v.size(), Field::vector_object::Nsimd(),{
|
|
||||||
coalescedWrite(psi_v[ss], h_v(ss) + omega * s_v(ss));
|
|
||||||
coalescedWrite(r_v[ss], -omega * t_v(ss) + s_v(ss));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
LinearCombTimer.Stop();
|
|
||||||
|
|
||||||
cp = norm2(r);
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << "BiCGSTAB: Iteration " << k << " residual " << sqrt(cp/ssq) << " target " << Tolerance << std::endl;
|
|
||||||
|
|
||||||
// Stopping condition
|
|
||||||
if(cp <= rsq)
|
|
||||||
{
|
|
||||||
SolverTimer.Stop();
|
|
||||||
Linop.Op(psi, v);
|
|
||||||
p = v - src;
|
|
||||||
|
|
||||||
RealD srcnorm = sqrt(norm2(src));
|
|
||||||
RealD resnorm = sqrt(norm2(p));
|
|
||||||
RealD true_residual = resnorm / srcnorm;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "BiCGSTAB Converged on iteration " << k << std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tComputed residual " << sqrt(cp/ssq) << std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tTrue residual " << true_residual << std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tTarget " << Tolerance << std::endl;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "Time breakdown " << std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tElapsed " << SolverTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tLinalg " << LinalgTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tInner " << InnerTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tAxpyNorm " << AxpyNormTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tLinearComb " << LinearCombTimer.Elapsed() << std::endl;
|
|
||||||
|
|
||||||
if(ErrorOnNoConverge){ GRID_ASSERT(true_residual / Tolerance < 10000.0); }
|
|
||||||
|
|
||||||
IterationsToComplete = k;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "BiCGSTAB did NOT converge" << std::endl;
|
|
||||||
|
|
||||||
if(ErrorOnNoConverge){ GRID_ASSERT(0); }
|
|
||||||
IterationsToComplete = k;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/BiCGSTABMixedPrec.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Christopher Kelly <ckelly@phys.columbia.edu>
|
|
||||||
Author: David Murphy <djmurphy@mit.edu>
|
|
||||||
|
|
||||||
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_BICGSTAB_MIXED_PREC_H
|
|
||||||
#define GRID_BICGSTAB_MIXED_PREC_H
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
// Mixed precision restarted defect correction BiCGSTAB
|
|
||||||
template<class FieldD, class FieldF, typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0, typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
|
|
||||||
class MixedPrecisionBiCGSTAB : public LinearFunction<FieldD>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using LinearFunction<FieldD>::operator();
|
|
||||||
RealD Tolerance;
|
|
||||||
RealD InnerTolerance; // Initial tolerance for inner CG. Defaults to Tolerance but can be changed
|
|
||||||
Integer MaxInnerIterations;
|
|
||||||
Integer MaxOuterIterations;
|
|
||||||
GridBase* SinglePrecGrid; // Grid for single-precision fields
|
|
||||||
RealD OuterLoopNormMult; // Stop the outer loop and move to a final double prec solve when the residual is OuterLoopNormMult * Tolerance
|
|
||||||
LinearOperatorBase<FieldF> &Linop_f;
|
|
||||||
LinearOperatorBase<FieldD> &Linop_d;
|
|
||||||
|
|
||||||
Integer TotalInnerIterations; //Number of inner CG iterations
|
|
||||||
Integer TotalOuterIterations; //Number of restarts
|
|
||||||
Integer TotalFinalStepIterations; //Number of CG iterations in final patch-up step
|
|
||||||
|
|
||||||
//Option to speed up *inner single precision* solves using a LinearFunction that produces a guess
|
|
||||||
LinearFunction<FieldF> *guesser;
|
|
||||||
|
|
||||||
MixedPrecisionBiCGSTAB(RealD tol, Integer maxinnerit, Integer maxouterit, GridBase* _sp_grid,
|
|
||||||
LinearOperatorBase<FieldF>& _Linop_f, LinearOperatorBase<FieldD>& _Linop_d) :
|
|
||||||
Linop_f(_Linop_f), Linop_d(_Linop_d), Tolerance(tol), InnerTolerance(tol), MaxInnerIterations(maxinnerit),
|
|
||||||
MaxOuterIterations(maxouterit), SinglePrecGrid(_sp_grid), OuterLoopNormMult(100.), guesser(NULL) {};
|
|
||||||
|
|
||||||
void useGuesser(LinearFunction<FieldF>& g){
|
|
||||||
guesser = &g;
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator() (const FieldD& src_d_in, FieldD& sol_d)
|
|
||||||
{
|
|
||||||
TotalInnerIterations = 0;
|
|
||||||
|
|
||||||
GridStopWatch TotalTimer;
|
|
||||||
TotalTimer.Start();
|
|
||||||
|
|
||||||
int cb = src_d_in.Checkerboard();
|
|
||||||
sol_d.Checkerboard() = cb;
|
|
||||||
|
|
||||||
RealD src_norm = norm2(src_d_in);
|
|
||||||
RealD stop = src_norm * Tolerance*Tolerance;
|
|
||||||
|
|
||||||
GridBase* DoublePrecGrid = src_d_in.Grid();
|
|
||||||
FieldD tmp_d(DoublePrecGrid);
|
|
||||||
tmp_d.Checkerboard() = cb;
|
|
||||||
|
|
||||||
FieldD tmp2_d(DoublePrecGrid);
|
|
||||||
tmp2_d.Checkerboard() = cb;
|
|
||||||
|
|
||||||
FieldD src_d(DoublePrecGrid);
|
|
||||||
src_d = src_d_in; //source for next inner iteration, computed from residual during operation
|
|
||||||
|
|
||||||
RealD inner_tol = InnerTolerance;
|
|
||||||
|
|
||||||
FieldF src_f(SinglePrecGrid);
|
|
||||||
src_f.Checkerboard() = cb;
|
|
||||||
|
|
||||||
FieldF sol_f(SinglePrecGrid);
|
|
||||||
sol_f.Checkerboard() = cb;
|
|
||||||
|
|
||||||
BiCGSTAB<FieldF> CG_f(inner_tol, MaxInnerIterations);
|
|
||||||
CG_f.ErrorOnNoConverge = false;
|
|
||||||
|
|
||||||
GridStopWatch InnerCGtimer;
|
|
||||||
|
|
||||||
GridStopWatch PrecChangeTimer;
|
|
||||||
|
|
||||||
Integer &outer_iter = TotalOuterIterations; //so it will be equal to the final iteration count
|
|
||||||
|
|
||||||
for(outer_iter = 0; outer_iter < MaxOuterIterations; outer_iter++)
|
|
||||||
{
|
|
||||||
// Compute double precision rsd and also new RHS vector.
|
|
||||||
Linop_d.Op(sol_d, tmp_d);
|
|
||||||
RealD norm = axpy_norm(src_d, -1., tmp_d, src_d_in); //src_d is residual vector
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "MixedPrecisionBiCGSTAB: Outer iteration " << outer_iter << " residual " << norm << " target " << stop << std::endl;
|
|
||||||
|
|
||||||
if(norm < OuterLoopNormMult * stop){
|
|
||||||
std::cout << GridLogMessage << "MixedPrecisionBiCGSTAB: Outer iteration converged on iteration " << outer_iter << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
while(norm * inner_tol * inner_tol < stop){ inner_tol *= 2; } // inner_tol = sqrt(stop/norm) ??
|
|
||||||
|
|
||||||
PrecChangeTimer.Start();
|
|
||||||
precisionChange(src_f, src_d);
|
|
||||||
PrecChangeTimer.Stop();
|
|
||||||
|
|
||||||
sol_f = Zero();
|
|
||||||
|
|
||||||
//Optionally improve inner solver guess (eg using known eigenvectors)
|
|
||||||
if(guesser != NULL){ (*guesser)(src_f, sol_f); }
|
|
||||||
|
|
||||||
//Inner CG
|
|
||||||
CG_f.Tolerance = inner_tol;
|
|
||||||
InnerCGtimer.Start();
|
|
||||||
CG_f(Linop_f, src_f, sol_f);
|
|
||||||
InnerCGtimer.Stop();
|
|
||||||
TotalInnerIterations += CG_f.IterationsToComplete;
|
|
||||||
|
|
||||||
//Convert sol back to double and add to double prec solution
|
|
||||||
PrecChangeTimer.Start();
|
|
||||||
precisionChange(tmp_d, sol_f);
|
|
||||||
PrecChangeTimer.Stop();
|
|
||||||
|
|
||||||
axpy(sol_d, 1.0, tmp_d, sol_d);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Final trial CG
|
|
||||||
std::cout << GridLogMessage << "MixedPrecisionBiCGSTAB: Starting final patch-up double-precision solve" << std::endl;
|
|
||||||
|
|
||||||
BiCGSTAB<FieldD> CG_d(Tolerance, MaxInnerIterations);
|
|
||||||
CG_d(Linop_d, src_d_in, sol_d);
|
|
||||||
TotalFinalStepIterations = CG_d.IterationsToComplete;
|
|
||||||
|
|
||||||
TotalTimer.Stop();
|
|
||||||
std::cout << GridLogMessage << "MixedPrecisionBiCGSTAB: Inner CG iterations " << TotalInnerIterations << " Restarts " << TotalOuterIterations << " Final CG iterations " << TotalFinalStepIterations << std::endl;
|
|
||||||
std::cout << GridLogMessage << "MixedPrecisionBiCGSTAB: Total time " << TotalTimer.Elapsed() << " Precision change " << PrecChangeTimer.Elapsed() << " Inner CG total " << InnerCGtimer.Elapsed() << std::endl;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -27,63 +27,13 @@ See the full license in the file "LICENSE" in the top level distribution
|
|||||||
directory
|
directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#pragma once
|
#ifndef GRID_BLOCK_CONJUGATE_GRADIENT_H
|
||||||
|
#define GRID_BLOCK_CONJUGATE_GRADIENT_H
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
template<class Field>
|
|
||||||
void InnerProductMatrix(Eigen::MatrixXcd &m , const std::vector<Field> &X, const std::vector<Field> &Y){
|
|
||||||
typedef typename Field::scalar_type scomplex;
|
|
||||||
int Nblock = X.size();
|
|
||||||
for(int b=0;b<Nblock;b++){
|
|
||||||
for(int bp=0;bp<Nblock;bp++) {
|
|
||||||
m(b,bp) = innerProduct(X[b],Y[bp]);
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
template<class Field>
|
|
||||||
void MaddMatrix(std::vector<Field> &AP, Eigen::MatrixXcd &m , const std::vector<Field> &X,const std::vector<Field> &Y,RealD scale=1.0){
|
|
||||||
// Should make this cache friendly with site outermost, parallel_for
|
|
||||||
// Deal with case AP aliases with either Y or X
|
|
||||||
//
|
|
||||||
//Could pack "X" and "AP" into a Nblock x Volume dense array.
|
|
||||||
// AP(Nrhs x vol) = Y(Nrhs x vol) + scale * m(nrhs x nrhs) * X(nrhs*vol)
|
|
||||||
typedef typename Field::scalar_type scomplex;
|
|
||||||
int Nblock = AP.size();
|
|
||||||
std::vector<Field> tmp(Nblock,X[0]);
|
|
||||||
for(int b=0;b<Nblock;b++){
|
|
||||||
tmp[b] = Y[b];
|
|
||||||
for(int bp=0;bp<Nblock;bp++) {
|
|
||||||
tmp[b] = tmp[b] +scomplex(scale*m(bp,b))*X[bp];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(int b=0;b<Nblock;b++){
|
|
||||||
AP[b] = tmp[b];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template<class Field>
|
|
||||||
void MulMatrix(std::vector<Field> &AP, Eigen::MatrixXcd &m , const std::vector<Field> &X){
|
|
||||||
// Should make this cache friendly with site outermost, parallel_for
|
|
||||||
typedef typename Field::scalar_type scomplex;
|
|
||||||
int Nblock = AP.size();
|
|
||||||
for(int b=0;b<Nblock;b++){
|
|
||||||
AP[b] = Zero();
|
|
||||||
for(int bp=0;bp<Nblock;bp++) {
|
|
||||||
AP[b] += scomplex(m(bp,b))*X[bp];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template<class Field>
|
|
||||||
double normv(const std::vector<Field> &P){
|
|
||||||
int Nblock = P.size();
|
|
||||||
double nn = 0.0;
|
|
||||||
for(int b=0;b<Nblock;b++) {
|
|
||||||
nn+=norm2(P[b]);
|
|
||||||
}
|
|
||||||
return nn;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum BlockCGtype { BlockCG, BlockCGrQ, CGmultiRHS, BlockCGVec, BlockCGrQVec };
|
namespace Grid {
|
||||||
|
|
||||||
|
enum BlockCGtype { BlockCG, BlockCGrQ, CGmultiRHS };
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// Block conjugate gradient. Dimension zero should be the block direction
|
// Block conjugate gradient. Dimension zero should be the block direction
|
||||||
@@ -92,27 +42,33 @@ template <class Field>
|
|||||||
class BlockConjugateGradient : public OperatorFunction<Field> {
|
class BlockConjugateGradient : public OperatorFunction<Field> {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
||||||
typedef typename Field::scalar_type scomplex;
|
typedef typename Field::scalar_type scomplex;
|
||||||
|
|
||||||
int blockDim ;
|
int blockDim ;
|
||||||
int Nblock;
|
int Nblock;
|
||||||
|
|
||||||
BlockCGtype CGtype;
|
BlockCGtype CGtype;
|
||||||
bool ErrorOnNoConverge; // throw an GRID_ASSERT when the CG fails to converge.
|
bool ErrorOnNoConverge; // throw an assert when the CG fails to converge.
|
||||||
// Defaults true.
|
// Defaults true.
|
||||||
RealD Tolerance;
|
RealD Tolerance;
|
||||||
Integer MaxIterations;
|
Integer MaxIterations;
|
||||||
Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion
|
Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion
|
||||||
Integer PrintInterval; //GridLogMessages or Iterative
|
|
||||||
RealD TrueResidual;
|
|
||||||
|
|
||||||
BlockConjugateGradient(BlockCGtype cgtype,int _Orthog,RealD tol, Integer maxit, bool err_on_no_conv = true)
|
BlockConjugateGradient(BlockCGtype cgtype,int _Orthog,RealD tol, Integer maxit, bool err_on_no_conv = true)
|
||||||
: Tolerance(tol), CGtype(cgtype), blockDim(_Orthog), MaxIterations(maxit), ErrorOnNoConverge(err_on_no_conv),PrintInterval(100)
|
: Tolerance(tol), CGtype(cgtype), blockDim(_Orthog), MaxIterations(maxit), ErrorOnNoConverge(err_on_no_conv)
|
||||||
{};
|
{};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Thin QR factorisation (google it)
|
// Thin QR factorisation (google it)
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void ThinQRfact (Eigen::MatrixXcd &m_rr,
|
||||||
|
Eigen::MatrixXcd &C,
|
||||||
|
Eigen::MatrixXcd &Cinv,
|
||||||
|
Field & Q,
|
||||||
|
const Field & R)
|
||||||
|
{
|
||||||
|
int Orthog = blockDim; // First dimension is block dim; this is an assumption
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
//Dimensions
|
//Dimensions
|
||||||
// R_{ferm x Nblock} = Q_{ferm x Nblock} x C_{Nblock x Nblock} -> ferm x Nblock
|
// R_{ferm x Nblock} = Q_{ferm x Nblock} x C_{Nblock x Nblock} -> ferm x Nblock
|
||||||
@@ -129,29 +85,22 @@ class BlockConjugateGradient : public OperatorFunction<Field> {
|
|||||||
// Cdag C = Rdag R ; passes.
|
// Cdag C = Rdag R ; passes.
|
||||||
// QdagQ = 1 ; passes
|
// QdagQ = 1 ; passes
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
void ThinQRfact (Eigen::MatrixXcd &m_rr,
|
|
||||||
Eigen::MatrixXcd &C,
|
|
||||||
Eigen::MatrixXcd &Cinv,
|
|
||||||
Field & Q,
|
|
||||||
const Field & R)
|
|
||||||
{
|
|
||||||
int Orthog = blockDim; // First dimension is block dim; this is an assumption
|
|
||||||
sliceInnerProductMatrix(m_rr,R,R,Orthog);
|
sliceInnerProductMatrix(m_rr,R,R,Orthog);
|
||||||
|
|
||||||
// Force manifest hermitian to avoid rounding related
|
// Force manifest hermitian to avoid rounding related
|
||||||
/*
|
|
||||||
int rank=m_rr.rows();
|
|
||||||
for(int r=0;r<rank;r++){
|
|
||||||
for(int s=0;s<rank;s++){
|
|
||||||
std::cout << "QR m_rr["<<r<<","<<s<<"] "<<m_rr(r,s)<<std::endl;
|
|
||||||
}}
|
|
||||||
*/
|
|
||||||
m_rr = 0.5*(m_rr+m_rr.adjoint());
|
m_rr = 0.5*(m_rr+m_rr.adjoint());
|
||||||
|
|
||||||
Eigen::MatrixXcd L = m_rr.llt().matrixL();
|
#if 0
|
||||||
|
std::cout << " Calling Cholesky ldlt on m_rr " << m_rr <<std::endl;
|
||||||
|
Eigen::MatrixXcd L_ldlt = m_rr.ldlt().matrixL();
|
||||||
|
std::cout << " Called Cholesky ldlt on m_rr " << L_ldlt <<std::endl;
|
||||||
|
auto D_ldlt = m_rr.ldlt().vectorD();
|
||||||
|
std::cout << " Called Cholesky ldlt on m_rr " << D_ldlt <<std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
// ComplexD det = L.determinant();
|
// std::cout << " Calling Cholesky llt on m_rr " <<std::endl;
|
||||||
// std::cout << " Det m_rr "<<det<<std::endl;
|
Eigen::MatrixXcd L = m_rr.llt().matrixL();
|
||||||
|
// std::cout << " Called Cholesky llt on m_rr " << L <<std::endl;
|
||||||
C = L.adjoint();
|
C = L.adjoint();
|
||||||
Cinv = C.inverse();
|
Cinv = C.inverse();
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -163,34 +112,6 @@ void ThinQRfact (Eigen::MatrixXcd &m_rr,
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
sliceMulMatrix(Q,Cinv,R,Orthog);
|
sliceMulMatrix(Q,Cinv,R,Orthog);
|
||||||
}
|
}
|
||||||
// see comments above
|
|
||||||
void ThinQRfact (Eigen::MatrixXcd &m_rr,
|
|
||||||
Eigen::MatrixXcd &C,
|
|
||||||
Eigen::MatrixXcd &Cinv,
|
|
||||||
std::vector<Field> & Q,
|
|
||||||
const std::vector<Field> & R)
|
|
||||||
{
|
|
||||||
InnerProductMatrix(m_rr,R,R);
|
|
||||||
/*
|
|
||||||
int rank=m_rr.rows();
|
|
||||||
for(int r=0;r<rank;r++){
|
|
||||||
for(int s=0;s<rank;s++){
|
|
||||||
std::cout << "QRvec m_rr["<<r<<","<<s<<"] "<<m_rr(r,s)<<std::endl;
|
|
||||||
}}
|
|
||||||
*/
|
|
||||||
m_rr = 0.5*(m_rr+m_rr.adjoint());
|
|
||||||
|
|
||||||
Eigen::MatrixXcd L = m_rr.llt().matrixL();
|
|
||||||
|
|
||||||
// ComplexD det = L.determinant();
|
|
||||||
// std::cout << " Det m_rr "<<det<<std::endl;
|
|
||||||
|
|
||||||
C = L.adjoint();
|
|
||||||
Cinv = C.inverse();
|
|
||||||
|
|
||||||
MulMatrix(Q,Cinv,R);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Call one of several implementations
|
// Call one of several implementations
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -198,18 +119,12 @@ void operator()(LinearOperatorBase<Field> &Linop, const Field &Src, Field &Psi)
|
|||||||
{
|
{
|
||||||
if ( CGtype == BlockCGrQ ) {
|
if ( CGtype == BlockCGrQ ) {
|
||||||
BlockCGrQsolve(Linop,Src,Psi);
|
BlockCGrQsolve(Linop,Src,Psi);
|
||||||
|
} else if (CGtype == BlockCG ) {
|
||||||
|
BlockCGsolve(Linop,Src,Psi);
|
||||||
} else if (CGtype == CGmultiRHS ) {
|
} else if (CGtype == CGmultiRHS ) {
|
||||||
CGmultiRHSsolve(Linop,Src,Psi);
|
CGmultiRHSsolve(Linop,Src,Psi);
|
||||||
} else {
|
} else {
|
||||||
GRID_ASSERT(0);
|
assert(0);
|
||||||
}
|
|
||||||
}
|
|
||||||
virtual void operator()(LinearOperatorBase<Field> &Linop, const std::vector<Field> &Src, std::vector<Field> &Psi)
|
|
||||||
{
|
|
||||||
if ( CGtype == BlockCGrQVec ) {
|
|
||||||
BlockCGrQsolveVec(Linop,Src,Psi);
|
|
||||||
} else {
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,12 +138,11 @@ virtual void operator()(LinearOperatorBase<Field> &Linop, const std::vector<Fiel
|
|||||||
void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
||||||
{
|
{
|
||||||
int Orthog = blockDim; // First dimension is block dim; this is an assumption
|
int Orthog = blockDim; // First dimension is block dim; this is an assumption
|
||||||
Nblock = B.Grid()->_fdimensions[Orthog];
|
Nblock = B._grid->_fdimensions[Orthog];
|
||||||
/* FAKE */
|
|
||||||
Nblock=8;
|
|
||||||
std::cout<<GridLogMessage<<" Block Conjugate Gradient : Orthog "<<Orthog<<" Nblock "<<Nblock<<std::endl;
|
std::cout<<GridLogMessage<<" Block Conjugate Gradient : Orthog "<<Orthog<<" Nblock "<<Nblock<<std::endl;
|
||||||
|
|
||||||
X.Checkerboard() = B.Checkerboard();
|
X.checkerboard = B.checkerboard;
|
||||||
conformable(X, B);
|
conformable(X, B);
|
||||||
|
|
||||||
Field tmp(B);
|
Field tmp(B);
|
||||||
@@ -256,13 +170,12 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
|||||||
sliceNorm(ssq,B,Orthog);
|
sliceNorm(ssq,B,Orthog);
|
||||||
RealD sssum=0;
|
RealD sssum=0;
|
||||||
for(int b=0;b<Nblock;b++) sssum+=ssq[b];
|
for(int b=0;b<Nblock;b++) sssum+=ssq[b];
|
||||||
for(int b=0;b<Nblock;b++) std::cout << "src["<<b<<"]" << ssq[b] <<std::endl;
|
|
||||||
|
|
||||||
sliceNorm(residuals,B,Orthog);
|
sliceNorm(residuals,B,Orthog);
|
||||||
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); }
|
for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
|
||||||
|
|
||||||
sliceNorm(residuals,X,Orthog);
|
sliceNorm(residuals,X,Orthog);
|
||||||
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); }
|
for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
|
||||||
|
|
||||||
/************************************************************************
|
/************************************************************************
|
||||||
* Block conjugate gradient rQ (Sebastien Birk Thesis, after Dubrulle 2001)
|
* Block conjugate gradient rQ (Sebastien Birk Thesis, after Dubrulle 2001)
|
||||||
@@ -289,13 +202,15 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
|||||||
std::cout << GridLogMessage<<"BlockCGrQ algorithm initialisation " <<std::endl;
|
std::cout << GridLogMessage<<"BlockCGrQ algorithm initialisation " <<std::endl;
|
||||||
|
|
||||||
//1. QC = R = B-AX, D = Q ; QC => Thin QR factorisation (google it)
|
//1. QC = R = B-AX, D = Q ; QC => Thin QR factorisation (google it)
|
||||||
|
|
||||||
Linop.HermOp(X, AD);
|
Linop.HermOp(X, AD);
|
||||||
tmp = B - AD;
|
tmp = B - AD;
|
||||||
|
//std::cout << GridLogMessage << " initial tmp " << norm2(tmp)<< std::endl;
|
||||||
sliceNorm(residuals,tmp,Orthog);
|
|
||||||
for(int b=0;b<Nblock;b++) std::cout << "res["<<b<<"]" << residuals[b] <<std::endl;
|
|
||||||
|
|
||||||
ThinQRfact (m_rr, m_C, m_Cinv, Q, tmp);
|
ThinQRfact (m_rr, m_C, m_Cinv, Q, tmp);
|
||||||
|
//std::cout << GridLogMessage << " initial Q " << norm2(Q)<< std::endl;
|
||||||
|
//std::cout << GridLogMessage << " m_rr " << m_rr<<std::endl;
|
||||||
|
//std::cout << GridLogMessage << " m_C " << m_C<<std::endl;
|
||||||
|
//std::cout << GridLogMessage << " m_Cinv " << m_Cinv<<std::endl;
|
||||||
D=Q;
|
D=Q;
|
||||||
|
|
||||||
std::cout << GridLogMessage<<"BlockCGrQ computed initial residual and QR fact " <<std::endl;
|
std::cout << GridLogMessage<<"BlockCGrQ computed initial residual and QR fact " <<std::endl;
|
||||||
@@ -310,8 +225,6 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
|||||||
GridStopWatch SolverTimer;
|
GridStopWatch SolverTimer;
|
||||||
SolverTimer.Start();
|
SolverTimer.Start();
|
||||||
|
|
||||||
RealD max_resid=0;
|
|
||||||
|
|
||||||
int k;
|
int k;
|
||||||
for (k = 1; k <= MaxIterations; k++) {
|
for (k = 1; k <= MaxIterations; k++) {
|
||||||
|
|
||||||
@@ -319,12 +232,14 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
|||||||
MatrixTimer.Start();
|
MatrixTimer.Start();
|
||||||
Linop.HermOp(D, Z);
|
Linop.HermOp(D, Z);
|
||||||
MatrixTimer.Stop();
|
MatrixTimer.Stop();
|
||||||
|
//std::cout << GridLogMessage << " norm2 Z " <<norm2(Z)<<std::endl;
|
||||||
|
|
||||||
//4. M = [D^dag Z]^{-1}
|
//4. M = [D^dag Z]^{-1}
|
||||||
sliceInnerTimer.Start();
|
sliceInnerTimer.Start();
|
||||||
sliceInnerProductMatrix(m_DZ,D,Z,Orthog);
|
sliceInnerProductMatrix(m_DZ,D,Z,Orthog);
|
||||||
sliceInnerTimer.Stop();
|
sliceInnerTimer.Stop();
|
||||||
m_M = m_DZ.inverse();
|
m_M = m_DZ.inverse();
|
||||||
|
//std::cout << GridLogMessage << " m_DZ " <<m_DZ<<std::endl;
|
||||||
|
|
||||||
//5. X = X + D MC
|
//5. X = X + D MC
|
||||||
m_tmp = m_M * m_C;
|
m_tmp = m_M * m_C;
|
||||||
@@ -342,7 +257,6 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
|||||||
|
|
||||||
//7. D = Q + D S^dag
|
//7. D = Q + D S^dag
|
||||||
m_tmp = m_S.adjoint();
|
m_tmp = m_S.adjoint();
|
||||||
|
|
||||||
sliceMaddTimer.Start();
|
sliceMaddTimer.Start();
|
||||||
sliceMaddMatrix(D,m_tmp,D,Q,Orthog);
|
sliceMaddMatrix(D,m_tmp,D,Q,Orthog);
|
||||||
sliceMaddTimer.Stop();
|
sliceMaddTimer.Stop();
|
||||||
@@ -356,7 +270,7 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
|||||||
*/
|
*/
|
||||||
m_rr = m_C.adjoint() * m_C;
|
m_rr = m_C.adjoint() * m_C;
|
||||||
|
|
||||||
max_resid=0;
|
RealD max_resid=0;
|
||||||
RealD rrsum=0;
|
RealD rrsum=0;
|
||||||
RealD rr;
|
RealD rr;
|
||||||
|
|
||||||
@@ -383,8 +297,7 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
|||||||
|
|
||||||
Linop.HermOp(X, AD);
|
Linop.HermOp(X, AD);
|
||||||
AD = AD-B;
|
AD = AD-B;
|
||||||
TrueResidual = std::sqrt(norm2(AD)/norm2(B));
|
std::cout << GridLogMessage <<"\t True residual is " << std::sqrt(norm2(AD)/norm2(B)) <<std::endl;
|
||||||
std::cout << GridLogMessage <<"\tTrue residual is " << TrueResidual <<std::endl;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "Time Breakdown "<<std::endl;
|
std::cout << GridLogMessage << "Time Breakdown "<<std::endl;
|
||||||
std::cout << GridLogMessage << "\tElapsed " << SolverTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tElapsed " << SolverTimer.Elapsed() <<std::endl;
|
||||||
@@ -398,11 +311,155 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
std::cout << GridLogMessage << "BlockConjugateGradient(rQ) did NOT converge" << std::endl;
|
||||||
|
|
||||||
std::cout << GridLogMessage << "BlockConjugateGradient(rQ) did NOT converge "<<k<<" / "<<MaxIterations
|
if (ErrorOnNoConverge) assert(0);
|
||||||
<<" residual "<< std::sqrt(max_resid)<< std::endl;
|
IterationsToComplete = k;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Block conjugate gradient; Original O'Leary Dimension zero should be the block direction
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
void BlockCGsolve(LinearOperatorBase<Field> &Linop, const Field &Src, Field &Psi)
|
||||||
|
{
|
||||||
|
int Orthog = blockDim; // First dimension is block dim; this is an assumption
|
||||||
|
Nblock = Src._grid->_fdimensions[Orthog];
|
||||||
|
|
||||||
if (ErrorOnNoConverge) GRID_ASSERT(0);
|
std::cout<<GridLogMessage<<" Block Conjugate Gradient : Orthog "<<Orthog<<" Nblock "<<Nblock<<std::endl;
|
||||||
|
|
||||||
|
Psi.checkerboard = Src.checkerboard;
|
||||||
|
conformable(Psi, Src);
|
||||||
|
|
||||||
|
Field P(Src);
|
||||||
|
Field AP(Src);
|
||||||
|
Field R(Src);
|
||||||
|
|
||||||
|
Eigen::MatrixXcd m_pAp = Eigen::MatrixXcd::Identity(Nblock,Nblock);
|
||||||
|
Eigen::MatrixXcd m_pAp_inv= Eigen::MatrixXcd::Identity(Nblock,Nblock);
|
||||||
|
Eigen::MatrixXcd m_rr = Eigen::MatrixXcd::Zero(Nblock,Nblock);
|
||||||
|
Eigen::MatrixXcd m_rr_inv = Eigen::MatrixXcd::Zero(Nblock,Nblock);
|
||||||
|
|
||||||
|
Eigen::MatrixXcd m_alpha = Eigen::MatrixXcd::Zero(Nblock,Nblock);
|
||||||
|
Eigen::MatrixXcd m_beta = Eigen::MatrixXcd::Zero(Nblock,Nblock);
|
||||||
|
|
||||||
|
// Initial residual computation & set up
|
||||||
|
std::vector<RealD> residuals(Nblock);
|
||||||
|
std::vector<RealD> ssq(Nblock);
|
||||||
|
|
||||||
|
sliceNorm(ssq,Src,Orthog);
|
||||||
|
RealD sssum=0;
|
||||||
|
for(int b=0;b<Nblock;b++) sssum+=ssq[b];
|
||||||
|
|
||||||
|
sliceNorm(residuals,Src,Orthog);
|
||||||
|
for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
|
||||||
|
|
||||||
|
sliceNorm(residuals,Psi,Orthog);
|
||||||
|
for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
|
||||||
|
|
||||||
|
// Initial search dir is guess
|
||||||
|
Linop.HermOp(Psi, AP);
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* Block conjugate gradient (Stephen Pickles, thesis 1995, pp 71, O Leary 1980)
|
||||||
|
************************************************************************
|
||||||
|
* O'Leary : R = B - A X
|
||||||
|
* O'Leary : P = M R ; preconditioner M = 1
|
||||||
|
* O'Leary : alpha = PAP^{-1} RMR
|
||||||
|
* O'Leary : beta = RMR^{-1}_old RMR_new
|
||||||
|
* O'Leary : X=X+Palpha
|
||||||
|
* O'Leary : R_new=R_old-AP alpha
|
||||||
|
* O'Leary : P=MR_new+P beta
|
||||||
|
*/
|
||||||
|
|
||||||
|
R = Src - AP;
|
||||||
|
P = R;
|
||||||
|
sliceInnerProductMatrix(m_rr,R,R,Orthog);
|
||||||
|
|
||||||
|
GridStopWatch sliceInnerTimer;
|
||||||
|
GridStopWatch sliceMaddTimer;
|
||||||
|
GridStopWatch MatrixTimer;
|
||||||
|
GridStopWatch SolverTimer;
|
||||||
|
SolverTimer.Start();
|
||||||
|
|
||||||
|
int k;
|
||||||
|
for (k = 1; k <= MaxIterations; k++) {
|
||||||
|
|
||||||
|
RealD rrsum=0;
|
||||||
|
for(int b=0;b<Nblock;b++) rrsum+=real(m_rr(b,b));
|
||||||
|
|
||||||
|
std::cout << GridLogIterative << "\titeration "<<k<<" rr_sum "<<rrsum<<" ssq_sum "<< sssum
|
||||||
|
<<" / "<<std::sqrt(rrsum/sssum) <<std::endl;
|
||||||
|
|
||||||
|
MatrixTimer.Start();
|
||||||
|
Linop.HermOp(P, AP);
|
||||||
|
MatrixTimer.Stop();
|
||||||
|
|
||||||
|
// Alpha
|
||||||
|
sliceInnerTimer.Start();
|
||||||
|
sliceInnerProductMatrix(m_pAp,P,AP,Orthog);
|
||||||
|
sliceInnerTimer.Stop();
|
||||||
|
m_pAp_inv = m_pAp.inverse();
|
||||||
|
m_alpha = m_pAp_inv * m_rr ;
|
||||||
|
|
||||||
|
// Psi, R update
|
||||||
|
sliceMaddTimer.Start();
|
||||||
|
sliceMaddMatrix(Psi,m_alpha, P,Psi,Orthog); // add alpha * P to psi
|
||||||
|
sliceMaddMatrix(R ,m_alpha,AP, R,Orthog,-1.0);// sub alpha * AP to resid
|
||||||
|
sliceMaddTimer.Stop();
|
||||||
|
|
||||||
|
// Beta
|
||||||
|
m_rr_inv = m_rr.inverse();
|
||||||
|
sliceInnerTimer.Start();
|
||||||
|
sliceInnerProductMatrix(m_rr,R,R,Orthog);
|
||||||
|
sliceInnerTimer.Stop();
|
||||||
|
m_beta = m_rr_inv *m_rr;
|
||||||
|
|
||||||
|
// Search update
|
||||||
|
sliceMaddTimer.Start();
|
||||||
|
sliceMaddMatrix(AP,m_beta,P,R,Orthog);
|
||||||
|
sliceMaddTimer.Stop();
|
||||||
|
P= AP;
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* convergence monitor
|
||||||
|
*********************
|
||||||
|
*/
|
||||||
|
RealD max_resid=0;
|
||||||
|
RealD rr;
|
||||||
|
for(int b=0;b<Nblock;b++){
|
||||||
|
rr = real(m_rr(b,b))/ssq[b];
|
||||||
|
if ( rr > max_resid ) max_resid = rr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( max_resid < Tolerance*Tolerance ) {
|
||||||
|
|
||||||
|
SolverTimer.Stop();
|
||||||
|
|
||||||
|
std::cout << GridLogMessage<<"BlockCG converged in "<<k<<" iterations"<<std::endl;
|
||||||
|
for(int b=0;b<Nblock;b++){
|
||||||
|
std::cout << GridLogMessage<< "\t\tblock "<<b<<" computed resid "
|
||||||
|
<< std::sqrt(real(m_rr(b,b))/ssq[b])<<std::endl;
|
||||||
|
}
|
||||||
|
std::cout << GridLogMessage<<"\tMax residual is "<<std::sqrt(max_resid)<<std::endl;
|
||||||
|
|
||||||
|
Linop.HermOp(Psi, AP);
|
||||||
|
AP = AP-Src;
|
||||||
|
std::cout << GridLogMessage <<"\t True residual is " << std::sqrt(norm2(AP)/norm2(Src)) <<std::endl;
|
||||||
|
|
||||||
|
std::cout << GridLogMessage << "Time Breakdown "<<std::endl;
|
||||||
|
std::cout << GridLogMessage << "\tElapsed " << SolverTimer.Elapsed() <<std::endl;
|
||||||
|
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||||
|
std::cout << GridLogMessage << "\tInnerProd " << sliceInnerTimer.Elapsed() <<std::endl;
|
||||||
|
std::cout << GridLogMessage << "\tMaddMatrix " << sliceMaddTimer.Elapsed() <<std::endl;
|
||||||
|
|
||||||
|
IterationsToComplete = k;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
std::cout << GridLogMessage << "BlockConjugateGradient did NOT converge" << std::endl;
|
||||||
|
|
||||||
|
if (ErrorOnNoConverge) assert(0);
|
||||||
IterationsToComplete = k;
|
IterationsToComplete = k;
|
||||||
}
|
}
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
@@ -412,11 +469,11 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
|
|||||||
void CGmultiRHSsolve(LinearOperatorBase<Field> &Linop, const Field &Src, Field &Psi)
|
void CGmultiRHSsolve(LinearOperatorBase<Field> &Linop, const Field &Src, Field &Psi)
|
||||||
{
|
{
|
||||||
int Orthog = blockDim; // First dimension is block dim
|
int Orthog = blockDim; // First dimension is block dim
|
||||||
Nblock = Src.Grid()->_fdimensions[Orthog];
|
Nblock = Src._grid->_fdimensions[Orthog];
|
||||||
|
|
||||||
std::cout<<GridLogMessage<<"MultiRHS Conjugate Gradient : Orthog "<<Orthog<<" Nblock "<<Nblock<<std::endl;
|
std::cout<<GridLogMessage<<"MultiRHS Conjugate Gradient : Orthog "<<Orthog<<" Nblock "<<Nblock<<std::endl;
|
||||||
|
|
||||||
Psi.Checkerboard() = Src.Checkerboard();
|
Psi.checkerboard = Src.checkerboard;
|
||||||
conformable(Psi, Src);
|
conformable(Psi, Src);
|
||||||
|
|
||||||
Field P(Src);
|
Field P(Src);
|
||||||
@@ -438,10 +495,10 @@ void CGmultiRHSsolve(LinearOperatorBase<Field> &Linop, const Field &Src, Field &
|
|||||||
for(int b=0;b<Nblock;b++) sssum+=ssq[b];
|
for(int b=0;b<Nblock;b++) sssum+=ssq[b];
|
||||||
|
|
||||||
sliceNorm(residuals,Src,Orthog);
|
sliceNorm(residuals,Src,Orthog);
|
||||||
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); }
|
for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
|
||||||
|
|
||||||
sliceNorm(residuals,Psi,Orthog);
|
sliceNorm(residuals,Psi,Orthog);
|
||||||
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); }
|
for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
|
||||||
|
|
||||||
// Initial search dir is guess
|
// Initial search dir is guess
|
||||||
Linop.HermOp(Psi, AP);
|
Linop.HermOp(Psi, AP);
|
||||||
@@ -522,8 +579,7 @@ void CGmultiRHSsolve(LinearOperatorBase<Field> &Linop, const Field &Src, Field &
|
|||||||
|
|
||||||
Linop.HermOp(Psi, AP);
|
Linop.HermOp(Psi, AP);
|
||||||
AP = AP-Src;
|
AP = AP-Src;
|
||||||
TrueResidual = std::sqrt(norm2(AP)/norm2(Src));
|
std::cout <<GridLogMessage << "\tTrue residual is " << std::sqrt(norm2(AP)/norm2(Src)) <<std::endl;
|
||||||
std::cout <<GridLogMessage << "\tTrue residual is " << TrueResidual <<std::endl;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "Time Breakdown "<<std::endl;
|
std::cout << GridLogMessage << "Time Breakdown "<<std::endl;
|
||||||
std::cout << GridLogMessage << "\tElapsed " << SolverTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tElapsed " << SolverTimer.Elapsed() <<std::endl;
|
||||||
@@ -540,202 +596,11 @@ void CGmultiRHSsolve(LinearOperatorBase<Field> &Linop, const Field &Src, Field &
|
|||||||
}
|
}
|
||||||
std::cout << GridLogMessage << "MultiRHSConjugateGradient did NOT converge" << std::endl;
|
std::cout << GridLogMessage << "MultiRHSConjugateGradient did NOT converge" << std::endl;
|
||||||
|
|
||||||
if (ErrorOnNoConverge) GRID_ASSERT(0);
|
if (ErrorOnNoConverge) assert(0);
|
||||||
IterationsToComplete = k;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
// BlockCGrQvec implementation:
|
|
||||||
//--------------------------
|
|
||||||
// X is guess/Solution
|
|
||||||
// B is RHS
|
|
||||||
// Solve A X_i = B_i ; i refers to Nblock index
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
void BlockCGrQsolveVec(LinearOperatorBase<Field> &Linop, const std::vector<Field> &B, std::vector<Field> &X)
|
|
||||||
{
|
|
||||||
Nblock = B.size();
|
|
||||||
GRID_ASSERT(Nblock == X.size());
|
|
||||||
|
|
||||||
std::cout<<GridLogMessage<<" Block Conjugate Gradient Vec rQ : Nblock "<<Nblock<<std::endl;
|
|
||||||
|
|
||||||
for(int b=0;b<Nblock;b++){
|
|
||||||
X[b].Checkerboard() = B[b].Checkerboard();
|
|
||||||
conformable(X[b], B[b]);
|
|
||||||
conformable(X[b], X[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Field Fake(B[0]);
|
|
||||||
|
|
||||||
std::vector<Field> tmp(Nblock,Fake);
|
|
||||||
std::vector<Field> Q(Nblock,Fake);
|
|
||||||
std::vector<Field> D(Nblock,Fake);
|
|
||||||
std::vector<Field> Z(Nblock,Fake);
|
|
||||||
std::vector<Field> AD(Nblock,Fake);
|
|
||||||
|
|
||||||
Eigen::MatrixXcd m_DZ = Eigen::MatrixXcd::Identity(Nblock,Nblock);
|
|
||||||
Eigen::MatrixXcd m_M = Eigen::MatrixXcd::Identity(Nblock,Nblock);
|
|
||||||
Eigen::MatrixXcd m_rr = Eigen::MatrixXcd::Zero(Nblock,Nblock);
|
|
||||||
|
|
||||||
Eigen::MatrixXcd m_C = Eigen::MatrixXcd::Zero(Nblock,Nblock);
|
|
||||||
Eigen::MatrixXcd m_Cinv = Eigen::MatrixXcd::Zero(Nblock,Nblock);
|
|
||||||
Eigen::MatrixXcd m_S = Eigen::MatrixXcd::Zero(Nblock,Nblock);
|
|
||||||
Eigen::MatrixXcd m_Sinv = Eigen::MatrixXcd::Zero(Nblock,Nblock);
|
|
||||||
|
|
||||||
Eigen::MatrixXcd m_tmp = Eigen::MatrixXcd::Identity(Nblock,Nblock);
|
|
||||||
Eigen::MatrixXcd m_tmp1 = Eigen::MatrixXcd::Identity(Nblock,Nblock);
|
|
||||||
|
|
||||||
// Initial residual computation & set up
|
|
||||||
std::vector<RealD> residuals(Nblock);
|
|
||||||
std::vector<RealD> ssq(Nblock);
|
|
||||||
|
|
||||||
RealD sssum=0;
|
|
||||||
for(int b=0;b<Nblock;b++){ ssq[b] = norm2(B[b]);}
|
|
||||||
for(int b=0;b<Nblock;b++){ std::cout << "ssq["<<b<<"] "<<ssq[b]<<std::endl;}
|
|
||||||
for(int b=0;b<Nblock;b++) sssum+=ssq[b];
|
|
||||||
|
|
||||||
for(int b=0;b<Nblock;b++){ residuals[b] = norm2(B[b]);}
|
|
||||||
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); }
|
|
||||||
|
|
||||||
for(int b=0;b<Nblock;b++){ residuals[b] = norm2(X[b]);}
|
|
||||||
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); }
|
|
||||||
|
|
||||||
/************************************************************************
|
|
||||||
* Block conjugate gradient rQ (Sebastien Birk Thesis, after Dubrulle 2001)
|
|
||||||
************************************************************************
|
|
||||||
* Dimensions:
|
|
||||||
*
|
|
||||||
* X,B==(Nferm x Nblock)
|
|
||||||
* A==(Nferm x Nferm)
|
|
||||||
*
|
|
||||||
* Nferm = Nspin x Ncolour x Ncomplex x Nlattice_site
|
|
||||||
*
|
|
||||||
* QC = R = B-AX, D = Q ; QC => Thin QR factorisation (google it)
|
|
||||||
* for k:
|
|
||||||
* Z = AD
|
|
||||||
* M = [D^dag Z]^{-1}
|
|
||||||
* X = X + D MC
|
|
||||||
* QS = Q - ZM
|
|
||||||
* D = Q + D S^dag
|
|
||||||
* C = S C
|
|
||||||
*/
|
|
||||||
///////////////////////////////////////
|
|
||||||
// Initial block: initial search dir is guess
|
|
||||||
///////////////////////////////////////
|
|
||||||
std::cout << GridLogMessage<<"BlockCGrQvec algorithm initialisation " <<std::endl;
|
|
||||||
|
|
||||||
//1. QC = R = B-AX, D = Q ; QC => Thin QR factorisation (google it)
|
|
||||||
for(int b=0;b<Nblock;b++) {
|
|
||||||
Linop.HermOp(X[b], AD[b]);
|
|
||||||
tmp[b] = B[b] - AD[b];
|
|
||||||
std::cout << "r0["<<b<<"] "<<norm2(tmp[b])<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
ThinQRfact (m_rr, m_C, m_Cinv, Q, tmp);
|
|
||||||
|
|
||||||
for(int b=0;b<Nblock;b++) D[b]=Q[b];
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<<"BlockCGrQ vec computed initial residual and QR fact " <<std::endl;
|
|
||||||
|
|
||||||
///////////////////////////////////////
|
|
||||||
// Timers
|
|
||||||
///////////////////////////////////////
|
|
||||||
GridStopWatch sliceInnerTimer;
|
|
||||||
GridStopWatch sliceMaddTimer;
|
|
||||||
GridStopWatch QRTimer;
|
|
||||||
GridStopWatch MatrixTimer;
|
|
||||||
GridStopWatch SolverTimer;
|
|
||||||
SolverTimer.Start();
|
|
||||||
|
|
||||||
int k;
|
|
||||||
for (k = 1; k <= MaxIterations; k++) {
|
|
||||||
|
|
||||||
//3. Z = AD
|
|
||||||
MatrixTimer.Start();
|
|
||||||
for(int b=0;b<Nblock;b++) Linop.HermOp(D[b], Z[b]);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
//4. M = [D^dag Z]^{-1}
|
|
||||||
sliceInnerTimer.Start();
|
|
||||||
InnerProductMatrix(m_DZ,D,Z);
|
|
||||||
sliceInnerTimer.Stop();
|
|
||||||
m_M = m_DZ.inverse();
|
|
||||||
|
|
||||||
//5. X = X + D MC
|
|
||||||
m_tmp = m_M * m_C;
|
|
||||||
sliceMaddTimer.Start();
|
|
||||||
MaddMatrix(X,m_tmp, D,X);
|
|
||||||
sliceMaddTimer.Stop();
|
|
||||||
|
|
||||||
//6. QS = Q - ZM
|
|
||||||
sliceMaddTimer.Start();
|
|
||||||
MaddMatrix(tmp,m_M,Z,Q,-1.0);
|
|
||||||
sliceMaddTimer.Stop();
|
|
||||||
QRTimer.Start();
|
|
||||||
ThinQRfact (m_rr, m_S, m_Sinv, Q, tmp);
|
|
||||||
QRTimer.Stop();
|
|
||||||
|
|
||||||
//7. D = Q + D S^dag
|
|
||||||
m_tmp = m_S.adjoint();
|
|
||||||
sliceMaddTimer.Start();
|
|
||||||
MaddMatrix(D,m_tmp,D,Q);
|
|
||||||
sliceMaddTimer.Stop();
|
|
||||||
|
|
||||||
//8. C = S C
|
|
||||||
m_C = m_S*m_C;
|
|
||||||
|
|
||||||
/*********************
|
|
||||||
* convergence monitor
|
|
||||||
*********************
|
|
||||||
*/
|
|
||||||
m_rr = m_C.adjoint() * m_C;
|
|
||||||
|
|
||||||
RealD max_resid=0;
|
|
||||||
RealD rrsum=0;
|
|
||||||
RealD rr;
|
|
||||||
|
|
||||||
for(int b=0;b<Nblock;b++) {
|
|
||||||
rrsum+=real(m_rr(b,b));
|
|
||||||
rr = real(m_rr(b,b))/ssq[b];
|
|
||||||
if ( rr > max_resid ) max_resid = rr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << "\t Block Iteration "<<k<<" ave resid "<< std::sqrt(rrsum/sssum) << " max "<< std::sqrt(max_resid) <<std::endl;
|
|
||||||
|
|
||||||
if ( max_resid < Tolerance*Tolerance ) {
|
|
||||||
|
|
||||||
SolverTimer.Stop();
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<<"BlockCGrQ converged in "<<k<<" iterations"<<std::endl;
|
|
||||||
|
|
||||||
for(int b=0;b<Nblock;b++){
|
|
||||||
std::cout << GridLogMessage<< "\t\tblock "<<b<<" computed resid "<< std::sqrt(real(m_rr(b,b))/ssq[b])<<std::endl;
|
|
||||||
}
|
|
||||||
std::cout << GridLogMessage<<"\tMax residual is "<<std::sqrt(max_resid)<<std::endl;
|
|
||||||
|
|
||||||
for(int b=0;b<Nblock;b++) Linop.HermOp(X[b], AD[b]);
|
|
||||||
for(int b=0;b<Nblock;b++) AD[b] = AD[b]-B[b];
|
|
||||||
TrueResidual = std::sqrt(normv(AD)/normv(B));
|
|
||||||
std::cout << GridLogMessage << "\tTrue residual is " << TrueResidual <<std::endl;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "Time Breakdown "<<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tElapsed " << SolverTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tInnerProd " << sliceInnerTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tMaddMatrix " << sliceMaddTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tThinQRfact " << QRTimer.Elapsed() <<std::endl;
|
|
||||||
|
|
||||||
IterationsToComplete = k;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
std::cout << GridLogMessage << "BlockConjugateGradient(rQ) did NOT converge" << std::endl;
|
|
||||||
|
|
||||||
if (ErrorOnNoConverge) GRID_ASSERT(0);
|
|
||||||
IterationsToComplete = k;
|
IterationsToComplete = k;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
}
|
||||||
|
#endif
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/CommunicationAvoidingGeneralisedMinimalResidual.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Daniel Richtmann <daniel.richtmann@ur.de>
|
|
||||||
|
|
||||||
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_COMMUNICATION_AVOIDING_GENERALISED_MINIMAL_RESIDUAL_H
|
|
||||||
#define GRID_COMMUNICATION_AVOIDING_GENERALISED_MINIMAL_RESIDUAL_H
|
|
||||||
|
|
||||||
namespace Grid {
|
|
||||||
|
|
||||||
template<class Field>
|
|
||||||
class CommunicationAvoidingGeneralisedMinimalResidual : public OperatorFunction<Field> {
|
|
||||||
public:
|
|
||||||
using OperatorFunction<Field>::operator();
|
|
||||||
|
|
||||||
bool ErrorOnNoConverge; // Throw an GRID_ASSERT when CAGMRES fails to converge,
|
|
||||||
// defaults to true
|
|
||||||
|
|
||||||
RealD Tolerance;
|
|
||||||
|
|
||||||
Integer MaxIterations;
|
|
||||||
Integer RestartLength;
|
|
||||||
Integer MaxNumberOfRestarts;
|
|
||||||
Integer IterationCount; // Number of iterations the CAGMRES took to finish,
|
|
||||||
// filled in upon completion
|
|
||||||
|
|
||||||
GridStopWatch MatrixTimer;
|
|
||||||
GridStopWatch LinalgTimer;
|
|
||||||
GridStopWatch QrTimer;
|
|
||||||
GridStopWatch CompSolutionTimer;
|
|
||||||
|
|
||||||
Eigen::MatrixXcd H;
|
|
||||||
|
|
||||||
std::vector<ComplexD> y;
|
|
||||||
std::vector<ComplexD> gamma;
|
|
||||||
std::vector<ComplexD> c;
|
|
||||||
std::vector<ComplexD> s;
|
|
||||||
|
|
||||||
CommunicationAvoidingGeneralisedMinimalResidual(RealD tol,
|
|
||||||
Integer maxit,
|
|
||||||
Integer restart_length,
|
|
||||||
bool err_on_no_conv = true)
|
|
||||||
: Tolerance(tol)
|
|
||||||
, MaxIterations(maxit)
|
|
||||||
, RestartLength(restart_length)
|
|
||||||
, MaxNumberOfRestarts(MaxIterations/RestartLength + ((MaxIterations%RestartLength == 0) ? 0 : 1))
|
|
||||||
, ErrorOnNoConverge(err_on_no_conv)
|
|
||||||
, H(Eigen::MatrixXcd::Zero(RestartLength, RestartLength + 1)) // sizes taken from DD-αAMG code base
|
|
||||||
, y(RestartLength + 1, 0.)
|
|
||||||
, gamma(RestartLength + 1, 0.)
|
|
||||||
, c(RestartLength + 1, 0.)
|
|
||||||
, s(RestartLength + 1, 0.) {};
|
|
||||||
|
|
||||||
void operator()(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi) {
|
|
||||||
|
|
||||||
std::cout << GridLogWarning << "This algorithm currently doesn't differ from regular GMRES" << std::endl;
|
|
||||||
|
|
||||||
psi.Checkerboard() = src.Checkerboard();
|
|
||||||
conformable(psi, src);
|
|
||||||
|
|
||||||
RealD guess = norm2(psi);
|
|
||||||
GRID_ASSERT(std::isnan(guess) == 0);
|
|
||||||
|
|
||||||
RealD cp;
|
|
||||||
RealD ssq = norm2(src);
|
|
||||||
RealD rsq = Tolerance * Tolerance * ssq;
|
|
||||||
|
|
||||||
Field r(src.Grid());
|
|
||||||
|
|
||||||
std::cout << std::setprecision(4) << std::scientific;
|
|
||||||
std::cout << GridLogIterative << "CommunicationAvoidingGeneralisedMinimalResidual: guess " << guess << std::endl;
|
|
||||||
std::cout << GridLogIterative << "CommunicationAvoidingGeneralisedMinimalResidual: src " << ssq << std::endl;
|
|
||||||
|
|
||||||
MatrixTimer.Reset();
|
|
||||||
LinalgTimer.Reset();
|
|
||||||
QrTimer.Reset();
|
|
||||||
CompSolutionTimer.Reset();
|
|
||||||
|
|
||||||
GridStopWatch SolverTimer;
|
|
||||||
SolverTimer.Start();
|
|
||||||
|
|
||||||
IterationCount = 0;
|
|
||||||
|
|
||||||
for (int k=0; k<MaxNumberOfRestarts; k++) {
|
|
||||||
|
|
||||||
cp = outerLoopBody(LinOp, src, psi, rsq);
|
|
||||||
|
|
||||||
// Stopping condition
|
|
||||||
if (cp <= rsq) {
|
|
||||||
|
|
||||||
SolverTimer.Stop();
|
|
||||||
|
|
||||||
LinOp.Op(psi,r);
|
|
||||||
axpy(r,-1.0,src,r);
|
|
||||||
|
|
||||||
RealD srcnorm = sqrt(ssq);
|
|
||||||
RealD resnorm = sqrt(norm2(r));
|
|
||||||
RealD true_residual = resnorm / srcnorm;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "CommunicationAvoidingGeneralisedMinimalResidual: Converged on iteration " << IterationCount
|
|
||||||
<< " computed residual " << sqrt(cp / ssq)
|
|
||||||
<< " true residual " << true_residual
|
|
||||||
<< " target " << Tolerance << std::endl;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "CAGMRES Time elapsed: Total " << SolverTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "CAGMRES Time elapsed: Matrix " << MatrixTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "CAGMRES Time elapsed: Linalg " << LinalgTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "CAGMRES Time elapsed: QR " << QrTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "CAGMRES Time elapsed: CompSol " << CompSolutionTimer.Elapsed() << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "CommunicationAvoidingGeneralisedMinimalResidual did NOT converge" << std::endl;
|
|
||||||
|
|
||||||
if (ErrorOnNoConverge)
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) {
|
|
||||||
|
|
||||||
RealD cp = 0;
|
|
||||||
|
|
||||||
Field w(src.Grid());
|
|
||||||
Field r(src.Grid());
|
|
||||||
|
|
||||||
// this should probably be made a class member so that it is only allocated once, not in every restart
|
|
||||||
std::vector<Field> v(RestartLength + 1, src.Grid()); for (auto &elem : v) elem = Zero();
|
|
||||||
|
|
||||||
MatrixTimer.Start();
|
|
||||||
LinOp.Op(psi, w);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
r = src - w;
|
|
||||||
|
|
||||||
gamma[0] = sqrt(norm2(r));
|
|
||||||
|
|
||||||
ComplexD scale = 1.0/gamma[0];
|
|
||||||
v[0] = scale * r;
|
|
||||||
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
for (int i=0; i<RestartLength; i++) {
|
|
||||||
|
|
||||||
IterationCount++;
|
|
||||||
|
|
||||||
arnoldiStep(LinOp, v, w, i);
|
|
||||||
|
|
||||||
qrUpdate(i);
|
|
||||||
|
|
||||||
cp = norm(gamma[i+1]);
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << "CommunicationAvoidingGeneralisedMinimalResidual: Iteration " << IterationCount
|
|
||||||
<< " residual " << cp << " target " << rsq << std::endl;
|
|
||||||
|
|
||||||
if ((i == RestartLength - 1) || (IterationCount == MaxIterations) || (cp <= rsq)) {
|
|
||||||
|
|
||||||
computeSolution(v, psi, i);
|
|
||||||
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GRID_ASSERT(0); // Never reached
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void arnoldiStep(LinearOperatorBase<Field> &LinOp, std::vector<Field> &v, Field &w, int iter) {
|
|
||||||
|
|
||||||
MatrixTimer.Start();
|
|
||||||
LinOp.Op(v[iter], w);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
for (int i = 0; i <= iter; ++i) {
|
|
||||||
H(iter, i) = innerProduct(v[i], w);
|
|
||||||
w = w - ComplexD(H(iter, i)) * v[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
H(iter, iter + 1) = sqrt(norm2(w));
|
|
||||||
v[iter + 1] = ComplexD(1. / H(iter, iter + 1)) * w;
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void qrUpdate(int iter) {
|
|
||||||
|
|
||||||
QrTimer.Start();
|
|
||||||
for (int i = 0; i < iter ; ++i) {
|
|
||||||
auto tmp = -s[i] * ComplexD(H(iter, i)) + c[i] * ComplexD(H(iter, i + 1));
|
|
||||||
H(iter, i) = conjugate(c[i]) * ComplexD(H(iter, i)) + conjugate(s[i]) * ComplexD(H(iter, i + 1));
|
|
||||||
H(iter, i + 1) = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute new Givens Rotation
|
|
||||||
auto nu = sqrt(std::norm(H(iter, iter)) + std::norm(H(iter, iter + 1)));
|
|
||||||
c[iter] = H(iter, iter) / nu;
|
|
||||||
s[iter] = H(iter, iter + 1) / nu;
|
|
||||||
|
|
||||||
// Apply new Givens rotation
|
|
||||||
H(iter, iter) = nu;
|
|
||||||
H(iter, iter + 1) = 0.;
|
|
||||||
|
|
||||||
gamma[iter + 1] = -s[iter] * gamma[iter];
|
|
||||||
gamma[iter] = conjugate(c[iter]) * gamma[iter];
|
|
||||||
QrTimer.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void computeSolution(std::vector<Field> const &v, Field &psi, int iter) {
|
|
||||||
|
|
||||||
CompSolutionTimer.Start();
|
|
||||||
for (int i = iter; i >= 0; i--) {
|
|
||||||
y[i] = gamma[i];
|
|
||||||
for (int k = i + 1; k <= iter; k++)
|
|
||||||
y[i] = y[i] - ComplexD(H(k, i)) * y[k];
|
|
||||||
y[i] = y[i] / ComplexD(H(i, i));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i <= iter; i++)
|
|
||||||
psi = psi + v[i] * y[i];
|
|
||||||
CompSolutionTimer.Stop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -27,95 +27,56 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
See the full license in the file "LICENSE" in the top level distribution
|
See the full license in the file "LICENSE" in the top level distribution
|
||||||
directory
|
directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#ifndef GRID_CONJUGATE_GRADIENT_H
|
#ifndef GRID_CONJUGATE_GRADIENT_H
|
||||||
#define GRID_CONJUGATE_GRADIENT_H
|
#define GRID_CONJUGATE_GRADIENT_H
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////
|
||||||
// Base classes for iterative processes based on operators
|
// Base classes for iterative processes based on operators
|
||||||
// single input vec, single output vec.
|
// single input vec, single output vec.
|
||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
template <class Field>
|
template <class Field>
|
||||||
class ConjugateGradient : public OperatorFunction<Field> {
|
class ConjugateGradient : public OperatorFunction<Field> {
|
||||||
public:
|
public:
|
||||||
|
bool ErrorOnNoConverge; // throw an assert when the CG fails to converge.
|
||||||
using OperatorFunction<Field>::operator();
|
|
||||||
|
|
||||||
bool ErrorOnNoConverge; // throw an GRID_ASSERT when the CG fails to converge.
|
|
||||||
// Defaults true.
|
// Defaults true.
|
||||||
RealD Tolerance;
|
RealD Tolerance;
|
||||||
Integer MaxIterations;
|
Integer MaxIterations;
|
||||||
Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion
|
Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion
|
||||||
RealD TrueResidual;
|
|
||||||
|
|
||||||
ConjugateGradient(RealD tol, Integer maxit, bool err_on_no_conv = true)
|
ConjugateGradient(RealD tol, Integer maxit, bool err_on_no_conv = true)
|
||||||
: Tolerance(tol),
|
: Tolerance(tol),
|
||||||
MaxIterations(maxit),
|
MaxIterations(maxit),
|
||||||
ErrorOnNoConverge(err_on_no_conv)
|
ErrorOnNoConverge(err_on_no_conv){};
|
||||||
{};
|
|
||||||
|
|
||||||
virtual void LogIteration(int k,RealD a,RealD b){
|
void operator()(LinearOperatorBase<Field> &Linop, const Field &src, Field &psi) {
|
||||||
// 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("ConjugateGradient");
|
|
||||||
GridStopWatch PreambleTimer;
|
|
||||||
GridStopWatch ConstructTimer;
|
|
||||||
GridStopWatch NormTimer;
|
|
||||||
GridStopWatch AssignTimer;
|
|
||||||
PreambleTimer.Start();
|
|
||||||
psi.Checkerboard() = src.Checkerboard();
|
|
||||||
|
|
||||||
|
psi.checkerboard = src.checkerboard;
|
||||||
conformable(psi, src);
|
conformable(psi, src);
|
||||||
|
|
||||||
RealD cp, c, a, d, b, ssq, qq;
|
RealD cp, c, a, d, b, ssq, qq, b_pred;
|
||||||
//RealD b_pred;
|
|
||||||
|
|
||||||
// Was doing copies
|
Field p(src);
|
||||||
ConstructTimer.Start();
|
Field mmp(src);
|
||||||
Field p (src.Grid());
|
Field r(src);
|
||||||
Field mmp(src.Grid());
|
|
||||||
Field r (src.Grid());
|
|
||||||
ConstructTimer.Stop();
|
|
||||||
|
|
||||||
// Initial residual computation & set up
|
// Initial residual computation & set up
|
||||||
NormTimer.Start();
|
|
||||||
ssq = norm2(src);
|
|
||||||
RealD guess = norm2(psi);
|
RealD guess = norm2(psi);
|
||||||
NormTimer.Stop();
|
assert(std::isnan(guess) == 0);
|
||||||
GRID_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;
|
|
||||||
p = r;
|
|
||||||
a = norm2(p);
|
|
||||||
}
|
|
||||||
cp = a;
|
|
||||||
AssignTimer.Stop();
|
|
||||||
|
|
||||||
// Handle trivial case of zero src
|
|
||||||
if (ssq == 0.){
|
Linop.HermOpAndNorm(psi, mmp, d, b);
|
||||||
psi = Zero();
|
|
||||||
IterationsToComplete = 1;
|
r = src - mmp;
|
||||||
TrueResidual = 0.;
|
p = r;
|
||||||
return;
|
|
||||||
}
|
a = norm2(p);
|
||||||
|
cp = a;
|
||||||
|
ssq = norm2(src);
|
||||||
|
|
||||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: guess " << guess << std::endl;
|
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: src " << ssq << std::endl;
|
||||||
@@ -128,16 +89,12 @@ public:
|
|||||||
|
|
||||||
// Check if guess is really REALLY good :)
|
// Check if guess is really REALLY good :)
|
||||||
if (cp <= rsq) {
|
if (cp <= rsq) {
|
||||||
TrueResidual = std::sqrt(a/ssq);
|
|
||||||
std::cout << GridLogMessage << "ConjugateGradient guess is converged already " << std::endl;
|
|
||||||
IterationsToComplete = 0;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << GridLogIterative << std::setprecision(8)
|
std::cout << GridLogIterative << std::setprecision(8)
|
||||||
<< "ConjugateGradient: k=0 residual " << cp << " target " << rsq << std::endl;
|
<< "ConjugateGradient: k=0 residual " << cp << " target " << rsq << std::endl;
|
||||||
|
|
||||||
PreambleTimer.Stop();
|
|
||||||
GridStopWatch LinalgTimer;
|
GridStopWatch LinalgTimer;
|
||||||
GridStopWatch InnerTimer;
|
GridStopWatch InnerTimer;
|
||||||
GridStopWatch AxpyNormTimer;
|
GridStopWatch AxpyNormTimer;
|
||||||
@@ -145,13 +102,9 @@ public:
|
|||||||
GridStopWatch MatrixTimer;
|
GridStopWatch MatrixTimer;
|
||||||
GridStopWatch SolverTimer;
|
GridStopWatch SolverTimer;
|
||||||
|
|
||||||
RealD usecs = -usecond();
|
|
||||||
SolverTimer.Start();
|
SolverTimer.Start();
|
||||||
int k;
|
int k;
|
||||||
for (k = 1; k <= MaxIterations; k++) {
|
for (k = 1; k <= MaxIterations*1000; k++) {
|
||||||
|
|
||||||
GridStopWatch IterationTimer;
|
|
||||||
IterationTimer.Start();
|
|
||||||
c = cp;
|
c = cp;
|
||||||
|
|
||||||
MatrixTimer.Start();
|
MatrixTimer.Start();
|
||||||
@@ -172,206 +125,53 @@ public:
|
|||||||
b = cp / c;
|
b = cp / c;
|
||||||
|
|
||||||
LinearCombTimer.Start();
|
LinearCombTimer.Start();
|
||||||
{
|
parallel_for(int ss=0;ss<src._grid->oSites();ss++){
|
||||||
autoView( psi_v , psi, AcceleratorWrite);
|
vstream(psi[ss], a * p[ss] + psi[ss]);
|
||||||
autoView( p_v , p, AcceleratorWrite);
|
vstream(p [ss], b * p[ss] + r[ss]);
|
||||||
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();
|
LinearCombTimer.Stop();
|
||||||
LinalgTimer.Stop();
|
LinalgTimer.Stop();
|
||||||
LogIteration(k,a,b);
|
|
||||||
|
|
||||||
IterationTimer.Stop();
|
std::cout << GridLogIterative << "ConjugateGradient: Iteration " << k
|
||||||
if ( (k % 500) == 0 ) {
|
<< " residual " << cp << " target " << rsq << std::endl;
|
||||||
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
|
// Stopping condition
|
||||||
if (cp <= rsq) {
|
if (cp <= rsq) {
|
||||||
usecs +=usecond();
|
|
||||||
SolverTimer.Stop();
|
SolverTimer.Stop();
|
||||||
Linop.HermOpAndNorm(psi, mmp, d, qq);
|
Linop.HermOpAndNorm(psi, mmp, d, qq);
|
||||||
p = mmp - src;
|
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;
|
|
||||||
|
|
||||||
// std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl;
|
RealD srcnorm = sqrt(norm2(src));
|
||||||
std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl;
|
RealD resnorm = sqrt(norm2(p));
|
||||||
std::cout << GridLogMessage << "\tConstruct " << ConstructTimer.Elapsed() <<std::endl;
|
RealD true_residual = resnorm / srcnorm;
|
||||||
std::cout << GridLogMessage << "\tNorm " << NormTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tAssign " << AssignTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "ConjugateGradient Converged on iteration " << k << std::endl;
|
||||||
std::cout << GridLogMessage << "\tSolver " << SolverTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tComputed residual " << sqrt(cp / ssq)<<std::endl;
|
||||||
std::cout << GridLogMessage << "Solver breakdown "<<std::endl;
|
std::cout << GridLogMessage << "\tTrue residual " << true_residual<<std::endl;
|
||||||
|
std::cout << GridLogMessage << "\tTarget " << Tolerance << std::endl;
|
||||||
|
|
||||||
|
std::cout << GridLogMessage << "Time breakdown "<<std::endl;
|
||||||
|
std::cout << GridLogMessage << "\tElapsed " << SolverTimer.Elapsed() <<std::endl;
|
||||||
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||||
std::cout << GridLogMessage << "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
||||||
std::cout << GridLogMessage << "\t\tInner " << InnerTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tInner " << InnerTimer.Elapsed() <<std::endl;
|
||||||
std::cout << GridLogMessage << "\t\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
|
||||||
std::cout << GridLogMessage << "\t\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
|
||||||
|
|
||||||
std::cout << GridLogDebug << "\tMobius flop rate " << DwfFlops/ usecs<< " Gflops " <<std::endl;
|
if (ErrorOnNoConverge) assert(true_residual / Tolerance < 10000.0);
|
||||||
|
|
||||||
if (ErrorOnNoConverge) GRID_ASSERT(true_residual / Tolerance < 10000.0);
|
|
||||||
|
|
||||||
IterationsToComplete = k;
|
IterationsToComplete = k;
|
||||||
TrueResidual = true_residual;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Failed. Calculate true residual before giving up
|
std::cout << GridLogMessage << "ConjugateGradient did NOT converge"
|
||||||
// Linop.HermOpAndNorm(psi, mmp, d, qq);
|
<< std::endl;
|
||||||
// p = mmp - src;
|
|
||||||
//TrueResidual = sqrt(norm2(p)/ssq);
|
|
||||||
// TrueResidual = 1;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "ConjugateGradient did NOT converge "<<k<<" / "<< MaxIterations
|
if (ErrorOnNoConverge) assert(0);
|
||||||
<<" 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) GRID_ASSERT(0);
|
|
||||||
IterationsToComplete = k;
|
IterationsToComplete = k;
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
template <class Field>
|
|
||||||
class ConjugateGradientPolynomial : public ConjugateGradient<Field> {
|
|
||||||
public:
|
|
||||||
// Optionally record the CG polynomial
|
|
||||||
std::vector<double> ak;
|
|
||||||
std::vector<double> bk;
|
|
||||||
std::vector<double> poly_p;
|
|
||||||
std::vector<double> poly_r;
|
|
||||||
std::vector<double> poly_Ap;
|
|
||||||
std::vector<double> polynomial;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ConjugateGradientPolynomial(RealD tol, Integer maxit, bool err_on_no_conv = true)
|
|
||||||
: ConjugateGradient<Field>(tol,maxit,err_on_no_conv)
|
|
||||||
{ };
|
|
||||||
void PolyHermOp(LinearOperatorBase<Field> &Linop, 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;
|
|
||||||
Linop.HermOp(tmp,AtoN);
|
|
||||||
psi = psi + polynomial[n]*AtoN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void CGsequenceHermOp(LinearOperatorBase<Field> &Linop, const Field &src, Field &x)
|
|
||||||
{
|
|
||||||
Field Ap(src.Grid());
|
|
||||||
Field r(src.Grid());
|
|
||||||
Field p(src.Grid());
|
|
||||||
p=src;
|
|
||||||
r=src;
|
|
||||||
x=Zero();
|
|
||||||
x.Checkerboard()=src.Checkerboard();
|
|
||||||
for(int k=0;k<ak.size();k++){
|
|
||||||
x = x + ak[k]*p;
|
|
||||||
Linop.HermOp(p,Ap);
|
|
||||||
r = r - ak[k] * Ap;
|
|
||||||
p = r + bk[k] * p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Solve(LinearOperatorBase<Field> &Linop, const Field &src, Field &psi)
|
|
||||||
{
|
|
||||||
psi=Zero();
|
|
||||||
this->operator ()(Linop,src,psi);
|
|
||||||
}
|
|
||||||
virtual void LogBegin(void)
|
|
||||||
{
|
|
||||||
std::cout << "ConjugageGradientPolynomial::LogBegin() "<<std::endl;
|
|
||||||
ak.resize(0);
|
|
||||||
bk.resize(0);
|
|
||||||
polynomial.resize(0);
|
|
||||||
poly_Ap.resize(0);
|
|
||||||
poly_Ap.resize(0);
|
|
||||||
poly_p.resize(1);
|
|
||||||
poly_r.resize(1);
|
|
||||||
poly_p[0]=1.0;
|
|
||||||
poly_r[0]=1.0;
|
|
||||||
};
|
|
||||||
virtual void LogIteration(int k,RealD a,RealD b)
|
|
||||||
{
|
|
||||||
// With zero guess,
|
|
||||||
// p = r = src
|
|
||||||
//
|
|
||||||
// iterate:
|
|
||||||
// x = x + a p
|
|
||||||
// r = r - a A p
|
|
||||||
// p = r + b p
|
|
||||||
//
|
|
||||||
// [0]
|
|
||||||
// r = x
|
|
||||||
// p = x
|
|
||||||
// Ap=0
|
|
||||||
//
|
|
||||||
// [1]
|
|
||||||
// Ap = A x + 0 ==> shift poly P right by 1 and add 0.
|
|
||||||
// x = x + a p ==> add polynomials term by term
|
|
||||||
// r = r - a A p ==> add polynomials term by term
|
|
||||||
// p = r + b p ==> add polynomials term by term
|
|
||||||
//
|
|
||||||
std::cout << "ConjugageGradientPolynomial::LogIteration() "<<k<<std::endl;
|
|
||||||
ak.push_back(a);
|
|
||||||
bk.push_back(b);
|
|
||||||
// Ap= right_shift(p)
|
|
||||||
poly_Ap.resize(k+1);
|
|
||||||
poly_Ap[0]=0.0;
|
|
||||||
for(int i=0;i<k;i++){
|
|
||||||
poly_Ap[i+1]=poly_p[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// x = x + a p
|
|
||||||
polynomial.resize(k);
|
|
||||||
polynomial[k-1]=0.0;
|
|
||||||
for(int i=0;i<k;i++){
|
|
||||||
polynomial[i] = polynomial[i] + a * poly_p[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// r = r - a Ap
|
|
||||||
// p = r + b p
|
|
||||||
poly_r.resize(k+1);
|
|
||||||
poly_p.resize(k+1);
|
|
||||||
poly_r[k] = poly_p[k] = 0.0;
|
|
||||||
for(int i=0;i<k+1;i++){
|
|
||||||
poly_r[i] = poly_r[i] - a * poly_Ap[i];
|
|
||||||
poly_p[i] = poly_r[i] + b * poly_p[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/*************************************************************************************
|
/*************************************************************************************
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
@@ -23,20 +23,17 @@ Author: Christopher Kelly <ckelly@phys.columbia.edu>
|
|||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#ifndef GRID_CONJUGATE_GRADIENT_MIXED_PREC_H
|
#ifndef GRID_CONJUGATE_GRADIENT_MIXED_PREC_H
|
||||||
#define GRID_CONJUGATE_GRADIENT_MIXED_PREC_H
|
#define GRID_CONJUGATE_GRADIENT_MIXED_PREC_H
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
//Mixed precision restarted defect correction CG
|
//Mixed precision restarted defect correction CG
|
||||||
template<class FieldD,class FieldF,
|
template<class FieldD,class FieldF, typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0,typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
|
||||||
typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0,
|
|
||||||
typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
|
|
||||||
class MixedPrecisionConjugateGradient : public LinearFunction<FieldD> {
|
class MixedPrecisionConjugateGradient : public LinearFunction<FieldD> {
|
||||||
public:
|
public:
|
||||||
using LinearFunction<FieldD>::operator();
|
|
||||||
RealD Tolerance;
|
RealD Tolerance;
|
||||||
RealD InnerTolerance; //Initial tolerance for inner CG. Defaults to Tolerance but can be changed
|
RealD InnerTolerance; //Initial tolerance for inner CG. Defaults to Tolerance but can be changed
|
||||||
Integer MaxInnerIterations;
|
Integer MaxInnerIterations;
|
||||||
@@ -49,17 +46,11 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
Integer TotalInnerIterations; //Number of inner CG iterations
|
Integer TotalInnerIterations; //Number of inner CG iterations
|
||||||
Integer TotalOuterIterations; //Number of restarts
|
Integer TotalOuterIterations; //Number of restarts
|
||||||
Integer TotalFinalStepIterations; //Number of CG iterations in final patch-up step
|
Integer TotalFinalStepIterations; //Number of CG iterations in final patch-up step
|
||||||
RealD TrueResidual;
|
|
||||||
|
|
||||||
//Option to speed up *inner single precision* solves using a LinearFunction that produces a guess
|
//Option to speed up *inner single precision* solves using a LinearFunction that produces a guess
|
||||||
LinearFunction<FieldF> *guesser;
|
LinearFunction<FieldF> *guesser;
|
||||||
|
|
||||||
MixedPrecisionConjugateGradient(RealD tol,
|
MixedPrecisionConjugateGradient(RealD tol, Integer maxinnerit, Integer maxouterit, GridBase* _sp_grid, LinearOperatorBase<FieldF> &_Linop_f, LinearOperatorBase<FieldD> &_Linop_d) :
|
||||||
Integer maxinnerit,
|
|
||||||
Integer maxouterit,
|
|
||||||
GridBase* _sp_grid,
|
|
||||||
LinearOperatorBase<FieldF> &_Linop_f,
|
|
||||||
LinearOperatorBase<FieldD> &_Linop_d) :
|
|
||||||
Linop_f(_Linop_f), Linop_d(_Linop_d),
|
Linop_f(_Linop_f), Linop_d(_Linop_d),
|
||||||
Tolerance(tol), InnerTolerance(tol), MaxInnerIterations(maxinnerit), MaxOuterIterations(maxouterit), SinglePrecGrid(_sp_grid),
|
Tolerance(tol), InnerTolerance(tol), MaxInnerIterations(maxinnerit), MaxOuterIterations(maxouterit), SinglePrecGrid(_sp_grid),
|
||||||
OuterLoopNormMult(100.), guesser(NULL){ };
|
OuterLoopNormMult(100.), guesser(NULL){ };
|
||||||
@@ -68,103 +59,96 @@ NAMESPACE_BEGIN(Grid);
|
|||||||
guesser = &g;
|
guesser = &g;
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator() (const FieldD &src_d_in, FieldD &sol_d){
|
void operator() (const FieldD &src_d_in, FieldD &sol_d){
|
||||||
std::cout << GridLogMessage << "MixedPrecisionConjugateGradient: Starting mixed precision CG with outer tolerance " << Tolerance << " and inner tolerance " << InnerTolerance << std::endl;
|
TotalInnerIterations = 0;
|
||||||
TotalInnerIterations = 0;
|
|
||||||
|
|
||||||
GridStopWatch TotalTimer;
|
GridStopWatch TotalTimer;
|
||||||
TotalTimer.Start();
|
TotalTimer.Start();
|
||||||
|
|
||||||
int cb = src_d_in.Checkerboard();
|
int cb = src_d_in.checkerboard;
|
||||||
sol_d.Checkerboard() = cb;
|
sol_d.checkerboard = cb;
|
||||||
|
|
||||||
RealD src_norm = norm2(src_d_in);
|
RealD src_norm = norm2(src_d_in);
|
||||||
RealD stop = src_norm * Tolerance*Tolerance;
|
RealD stop = src_norm * Tolerance*Tolerance;
|
||||||
|
|
||||||
GridBase* DoublePrecGrid = src_d_in.Grid();
|
GridBase* DoublePrecGrid = src_d_in._grid;
|
||||||
FieldD tmp_d(DoublePrecGrid);
|
FieldD tmp_d(DoublePrecGrid);
|
||||||
tmp_d.Checkerboard() = cb;
|
tmp_d.checkerboard = cb;
|
||||||
|
|
||||||
FieldD tmp2_d(DoublePrecGrid);
|
FieldD tmp2_d(DoublePrecGrid);
|
||||||
tmp2_d.Checkerboard() = cb;
|
tmp2_d.checkerboard = cb;
|
||||||
|
|
||||||
FieldD src_d(DoublePrecGrid);
|
FieldD src_d(DoublePrecGrid);
|
||||||
src_d = src_d_in; //source for next inner iteration, computed from residual during operation
|
src_d = src_d_in; //source for next inner iteration, computed from residual during operation
|
||||||
|
|
||||||
RealD inner_tol = InnerTolerance;
|
RealD inner_tol = InnerTolerance;
|
||||||
|
|
||||||
FieldF src_f(SinglePrecGrid);
|
FieldF src_f(SinglePrecGrid);
|
||||||
src_f.Checkerboard() = cb;
|
src_f.checkerboard = cb;
|
||||||
|
|
||||||
FieldF sol_f(SinglePrecGrid);
|
FieldF sol_f(SinglePrecGrid);
|
||||||
sol_f.Checkerboard() = cb;
|
sol_f.checkerboard = cb;
|
||||||
|
|
||||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Starting initial inner CG with tolerance " << inner_tol << std::endl;
|
ConjugateGradient<FieldF> CG_f(inner_tol, MaxInnerIterations);
|
||||||
ConjugateGradient<FieldF> CG_f(inner_tol, MaxInnerIterations);
|
CG_f.ErrorOnNoConverge = false;
|
||||||
CG_f.ErrorOnNoConverge = false;
|
|
||||||
|
|
||||||
GridStopWatch InnerCGtimer;
|
GridStopWatch InnerCGtimer;
|
||||||
|
|
||||||
GridStopWatch PrecChangeTimer;
|
GridStopWatch PrecChangeTimer;
|
||||||
|
|
||||||
Integer &outer_iter = TotalOuterIterations; //so it will be equal to the final iteration count
|
Integer &outer_iter = TotalOuterIterations; //so it will be equal to the final iteration count
|
||||||
|
|
||||||
precisionChangeWorkspace pc_wk_sp_to_dp(DoublePrecGrid, SinglePrecGrid);
|
for(outer_iter = 0; outer_iter < MaxOuterIterations; outer_iter++){
|
||||||
precisionChangeWorkspace pc_wk_dp_to_sp(SinglePrecGrid, DoublePrecGrid);
|
//Compute double precision rsd and also new RHS vector.
|
||||||
|
Linop_d.HermOp(sol_d, tmp_d);
|
||||||
|
RealD norm = axpy_norm(src_d, -1., tmp_d, src_d_in); //src_d is residual vector
|
||||||
|
|
||||||
for(outer_iter = 0; outer_iter < MaxOuterIterations; outer_iter++){
|
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Outer iteration " <<outer_iter<<" residual "<< norm<< " target "<< stop<<std::endl;
|
||||||
//Compute double precision rsd and also new RHS vector.
|
|
||||||
Linop_d.HermOp(sol_d, tmp_d);
|
|
||||||
RealD norm = axpy_norm(src_d, -1., tmp_d, src_d_in); //src_d is residual vector
|
|
||||||
std::cout<<GridLogMessage<<" rsd norm "<<norm<<std::endl;
|
|
||||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Outer iteration " <<outer_iter<<" residual "<< norm<< " target "<< stop<<std::endl;
|
|
||||||
|
|
||||||
if(norm < OuterLoopNormMult * stop){
|
if(norm < OuterLoopNormMult * stop){
|
||||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Outer iteration converged on iteration " <<outer_iter <<std::endl;
|
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Outer iteration converged on iteration " <<outer_iter <<std::endl;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
while(norm * inner_tol * inner_tol < stop) inner_tol *= 2; // inner_tol = sqrt(stop/norm) ??
|
||||||
|
|
||||||
|
PrecChangeTimer.Start();
|
||||||
|
precisionChange(src_f, src_d);
|
||||||
|
PrecChangeTimer.Stop();
|
||||||
|
|
||||||
|
zeroit(sol_f);
|
||||||
|
|
||||||
|
//Optionally improve inner solver guess (eg using known eigenvectors)
|
||||||
|
if(guesser != NULL)
|
||||||
|
(*guesser)(src_f, sol_f);
|
||||||
|
|
||||||
|
//Inner CG
|
||||||
|
CG_f.Tolerance = inner_tol;
|
||||||
|
InnerCGtimer.Start();
|
||||||
|
CG_f(Linop_f, src_f, sol_f);
|
||||||
|
InnerCGtimer.Stop();
|
||||||
|
TotalInnerIterations += CG_f.IterationsToComplete;
|
||||||
|
|
||||||
|
//Convert sol back to double and add to double prec solution
|
||||||
|
PrecChangeTimer.Start();
|
||||||
|
precisionChange(tmp_d, sol_f);
|
||||||
|
PrecChangeTimer.Stop();
|
||||||
|
|
||||||
|
axpy(sol_d, 1.0, tmp_d, sol_d);
|
||||||
}
|
}
|
||||||
while(norm * inner_tol * inner_tol < stop*1.01) inner_tol *= 2; // inner_tol = sqrt(stop/norm) ??
|
|
||||||
|
|
||||||
PrecChangeTimer.Start();
|
//Final trial CG
|
||||||
precisionChange(src_f, src_d, pc_wk_dp_to_sp);
|
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Starting final patch-up double-precision solve"<<std::endl;
|
||||||
PrecChangeTimer.Stop();
|
|
||||||
|
|
||||||
sol_f = Zero();
|
ConjugateGradient<FieldD> CG_d(Tolerance, MaxInnerIterations);
|
||||||
|
CG_d(Linop_d, src_d_in, sol_d);
|
||||||
|
TotalFinalStepIterations = CG_d.IterationsToComplete;
|
||||||
|
|
||||||
//Optionally improve inner solver guess (eg using known eigenvectors)
|
TotalTimer.Stop();
|
||||||
if(guesser != NULL)
|
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Inner CG iterations " << TotalInnerIterations << " Restarts " << TotalOuterIterations << " Final CG iterations " << TotalFinalStepIterations << std::endl;
|
||||||
(*guesser)(src_f, sol_f);
|
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Total time " << TotalTimer.Elapsed() << " Precision change " << PrecChangeTimer.Elapsed() << " Inner CG total " << InnerCGtimer.Elapsed() << std::endl;
|
||||||
|
|
||||||
//Inner CG
|
|
||||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Outer iteration " << outer_iter << " starting inner CG with tolerance " << inner_tol << std::endl;
|
|
||||||
CG_f.Tolerance = inner_tol;
|
|
||||||
InnerCGtimer.Start();
|
|
||||||
CG_f(Linop_f, src_f, sol_f);
|
|
||||||
InnerCGtimer.Stop();
|
|
||||||
TotalInnerIterations += CG_f.IterationsToComplete;
|
|
||||||
|
|
||||||
//Convert sol back to double and add to double prec solution
|
|
||||||
PrecChangeTimer.Start();
|
|
||||||
precisionChange(tmp_d, sol_f, pc_wk_sp_to_dp);
|
|
||||||
PrecChangeTimer.Stop();
|
|
||||||
|
|
||||||
axpy(sol_d, 1.0, tmp_d, sol_d);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//Final trial CG
|
}
|
||||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Starting final patch-up double-precision solve"<<std::endl;
|
|
||||||
|
|
||||||
ConjugateGradient<FieldD> CG_d(Tolerance, MaxInnerIterations);
|
|
||||||
CG_d(Linop_d, src_d_in, sol_d);
|
|
||||||
TotalFinalStepIterations = CG_d.IterationsToComplete;
|
|
||||||
TrueResidual = CG_d.TrueResidual;
|
|
||||||
|
|
||||||
TotalTimer.Stop();
|
|
||||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Inner CG iterations " << TotalInnerIterations << " Restarts " << TotalOuterIterations << " Final CG iterations " << TotalFinalStepIterations << std::endl;
|
|
||||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradient: Total time " << TotalTimer.Elapsed() << " Precision change " << PrecChangeTimer.Elapsed() << " Inner CG total " << InnerCGtimer.Elapsed() << std::endl;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,213 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/ConjugateGradientMixedPrecBatched.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Raoul Hodgson <raoul.hodgson@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_MIXED_PREC_BATCHED_H
|
|
||||||
#define GRID_CONJUGATE_GRADIENT_MIXED_PREC_BATCHED_H
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
//Mixed precision restarted defect correction CG
|
|
||||||
template<class FieldD,class FieldF,
|
|
||||||
typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0,
|
|
||||||
typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
|
|
||||||
class MixedPrecisionConjugateGradientBatched : public LinearFunction<FieldD> {
|
|
||||||
public:
|
|
||||||
using LinearFunction<FieldD>::operator();
|
|
||||||
RealD Tolerance;
|
|
||||||
RealD InnerTolerance; //Initial tolerance for inner CG. Defaults to Tolerance but can be changed
|
|
||||||
Integer MaxInnerIterations;
|
|
||||||
Integer MaxOuterIterations;
|
|
||||||
Integer MaxPatchupIterations;
|
|
||||||
GridBase* SinglePrecGrid; //Grid for single-precision fields
|
|
||||||
RealD OuterLoopNormMult; //Stop the outer loop and move to a final double prec solve when the residual is OuterLoopNormMult * Tolerance
|
|
||||||
LinearOperatorBase<FieldF> &Linop_f;
|
|
||||||
LinearOperatorBase<FieldD> &Linop_d;
|
|
||||||
|
|
||||||
//Option to speed up *inner single precision* solves using a LinearFunction that produces a guess
|
|
||||||
LinearFunction<FieldF> *guesser;
|
|
||||||
bool updateResidual;
|
|
||||||
|
|
||||||
MixedPrecisionConjugateGradientBatched(RealD tol,
|
|
||||||
Integer maxinnerit,
|
|
||||||
Integer maxouterit,
|
|
||||||
Integer maxpatchit,
|
|
||||||
GridBase* _sp_grid,
|
|
||||||
LinearOperatorBase<FieldF> &_Linop_f,
|
|
||||||
LinearOperatorBase<FieldD> &_Linop_d,
|
|
||||||
bool _updateResidual=true) :
|
|
||||||
Linop_f(_Linop_f), Linop_d(_Linop_d),
|
|
||||||
Tolerance(tol), InnerTolerance(tol), MaxInnerIterations(maxinnerit), MaxOuterIterations(maxouterit), MaxPatchupIterations(maxpatchit), SinglePrecGrid(_sp_grid),
|
|
||||||
OuterLoopNormMult(100.), guesser(NULL), updateResidual(_updateResidual) { };
|
|
||||||
|
|
||||||
void useGuesser(LinearFunction<FieldF> &g){
|
|
||||||
guesser = &g;
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator() (const FieldD &src_d_in, FieldD &sol_d){
|
|
||||||
std::vector<FieldD> srcs_d_in{src_d_in};
|
|
||||||
std::vector<FieldD> sols_d{sol_d};
|
|
||||||
|
|
||||||
(*this)(srcs_d_in,sols_d);
|
|
||||||
|
|
||||||
sol_d = sols_d[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator() (const std::vector<FieldD> &src_d_in, std::vector<FieldD> &sol_d){
|
|
||||||
GRID_ASSERT(src_d_in.size() == sol_d.size());
|
|
||||||
int NBatch = src_d_in.size();
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "NBatch = " << NBatch << std::endl;
|
|
||||||
|
|
||||||
Integer TotalOuterIterations = 0; //Number of restarts
|
|
||||||
std::vector<Integer> TotalInnerIterations(NBatch,0); //Number of inner CG iterations
|
|
||||||
std::vector<Integer> TotalFinalStepIterations(NBatch,0); //Number of CG iterations in final patch-up step
|
|
||||||
|
|
||||||
GridStopWatch TotalTimer;
|
|
||||||
TotalTimer.Start();
|
|
||||||
|
|
||||||
GridStopWatch InnerCGtimer;
|
|
||||||
GridStopWatch PrecChangeTimer;
|
|
||||||
|
|
||||||
int cb = src_d_in[0].Checkerboard();
|
|
||||||
|
|
||||||
std::vector<RealD> src_norm;
|
|
||||||
std::vector<RealD> norm;
|
|
||||||
std::vector<RealD> stop;
|
|
||||||
|
|
||||||
GridBase* DoublePrecGrid = src_d_in[0].Grid();
|
|
||||||
FieldD tmp_d(DoublePrecGrid);
|
|
||||||
tmp_d.Checkerboard() = cb;
|
|
||||||
|
|
||||||
FieldD tmp2_d(DoublePrecGrid);
|
|
||||||
tmp2_d.Checkerboard() = cb;
|
|
||||||
|
|
||||||
std::vector<FieldD> src_d;
|
|
||||||
std::vector<FieldF> src_f;
|
|
||||||
std::vector<FieldF> sol_f;
|
|
||||||
|
|
||||||
for (int i=0; i<NBatch; i++) {
|
|
||||||
sol_d[i].Checkerboard() = cb;
|
|
||||||
|
|
||||||
src_norm.push_back(norm2(src_d_in[i]));
|
|
||||||
norm.push_back(0.);
|
|
||||||
stop.push_back(src_norm[i] * Tolerance*Tolerance);
|
|
||||||
|
|
||||||
src_d.push_back(src_d_in[i]); //source for next inner iteration, computed from residual during operation
|
|
||||||
|
|
||||||
src_f.push_back(SinglePrecGrid);
|
|
||||||
src_f[i].Checkerboard() = cb;
|
|
||||||
|
|
||||||
sol_f.push_back(SinglePrecGrid);
|
|
||||||
sol_f[i].Checkerboard() = cb;
|
|
||||||
}
|
|
||||||
|
|
||||||
RealD inner_tol = InnerTolerance;
|
|
||||||
|
|
||||||
ConjugateGradient<FieldF> CG_f(inner_tol, MaxInnerIterations);
|
|
||||||
CG_f.ErrorOnNoConverge = false;
|
|
||||||
|
|
||||||
Integer &outer_iter = TotalOuterIterations; //so it will be equal to the final iteration count
|
|
||||||
|
|
||||||
for(outer_iter = 0; outer_iter < MaxOuterIterations; outer_iter++){
|
|
||||||
std::cout << GridLogMessage << std::endl;
|
|
||||||
std::cout << GridLogMessage << "Outer iteration " << outer_iter << std::endl;
|
|
||||||
|
|
||||||
bool allConverged = true;
|
|
||||||
|
|
||||||
for (int i=0; i<NBatch; i++) {
|
|
||||||
//Compute double precision rsd and also new RHS vector.
|
|
||||||
Linop_d.HermOp(sol_d[i], tmp_d);
|
|
||||||
norm[i] = axpy_norm(src_d[i], -1., tmp_d, src_d_in[i]); //src_d is residual vector
|
|
||||||
|
|
||||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Outer iteration " << outer_iter <<" solve " << i << " residual "<< norm[i] << " target "<< stop[i] <<std::endl;
|
|
||||||
|
|
||||||
PrecChangeTimer.Start();
|
|
||||||
precisionChange(src_f[i], src_d[i]);
|
|
||||||
PrecChangeTimer.Stop();
|
|
||||||
|
|
||||||
sol_f[i] = Zero();
|
|
||||||
|
|
||||||
if(norm[i] > OuterLoopNormMult * stop[i]) {
|
|
||||||
allConverged = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (allConverged) break;
|
|
||||||
|
|
||||||
if (updateResidual) {
|
|
||||||
RealD normMax = *std::max_element(std::begin(norm), std::end(norm));
|
|
||||||
RealD stopMax = *std::max_element(std::begin(stop), std::end(stop));
|
|
||||||
while( normMax * inner_tol * inner_tol < stopMax) inner_tol *= 2; // inner_tol = sqrt(stop/norm) ??
|
|
||||||
CG_f.Tolerance = inner_tol;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Optionally improve inner solver guess (eg using known eigenvectors)
|
|
||||||
if(guesser != NULL) {
|
|
||||||
(*guesser)(src_f, sol_f);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0; i<NBatch; i++) {
|
|
||||||
//Inner CG
|
|
||||||
InnerCGtimer.Start();
|
|
||||||
CG_f(Linop_f, src_f[i], sol_f[i]);
|
|
||||||
InnerCGtimer.Stop();
|
|
||||||
TotalInnerIterations[i] += CG_f.IterationsToComplete;
|
|
||||||
|
|
||||||
//Convert sol back to double and add to double prec solution
|
|
||||||
PrecChangeTimer.Start();
|
|
||||||
precisionChange(tmp_d, sol_f[i]);
|
|
||||||
PrecChangeTimer.Stop();
|
|
||||||
|
|
||||||
axpy(sol_d[i], 1.0, tmp_d, sol_d[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Final trial CG
|
|
||||||
std::cout << GridLogMessage << std::endl;
|
|
||||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Starting final patch-up double-precision solve"<<std::endl;
|
|
||||||
|
|
||||||
for (int i=0; i<NBatch; i++) {
|
|
||||||
ConjugateGradient<FieldD> CG_d(Tolerance, MaxPatchupIterations);
|
|
||||||
CG_d(Linop_d, src_d_in[i], sol_d[i]);
|
|
||||||
TotalFinalStepIterations[i] += CG_d.IterationsToComplete;
|
|
||||||
}
|
|
||||||
|
|
||||||
TotalTimer.Stop();
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << std::endl;
|
|
||||||
for (int i=0; i<NBatch; i++) {
|
|
||||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: solve " << i << " Inner CG iterations " << TotalInnerIterations[i] << " Restarts " << TotalOuterIterations << " Final CG iterations " << TotalFinalStepIterations[i] << std::endl;
|
|
||||||
}
|
|
||||||
std::cout << GridLogMessage << std::endl;
|
|
||||||
std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Total time " << TotalTimer.Elapsed() << " Precision change " << PrecChangeTimer.Elapsed() << " Inner CG total " << InnerCGtimer.Elapsed() << std::endl;
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/*************************************************************************************
|
/*************************************************************************************
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
@@ -24,168 +24,146 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#ifndef GRID_CONJUGATE_MULTI_SHIFT_GRADIENT_H
|
#ifndef GRID_CONJUGATE_MULTI_SHIFT_GRADIENT_H
|
||||||
#define GRID_CONJUGATE_MULTI_SHIFT_GRADIENT_H
|
#define GRID_CONJUGATE_MULTI_SHIFT_GRADIENT_H
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////
|
||||||
// Base classes for iterative processes based on operators
|
// Base classes for iterative processes based on operators
|
||||||
// single input vec, single output vec.
|
// single input vec, single output vec.
|
||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template<class Field>
|
template<class Field>
|
||||||
class ConjugateGradientMultiShift : public OperatorMultiFunction<Field>,
|
class ConjugateGradientMultiShift : public OperatorMultiFunction<Field>,
|
||||||
public OperatorFunction<Field>
|
public OperatorFunction<Field>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
RealD Tolerance;
|
||||||
|
Integer MaxIterations;
|
||||||
|
Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion
|
||||||
|
int verbose;
|
||||||
|
MultiShiftFunction shifts;
|
||||||
|
|
||||||
using OperatorFunction<Field>::operator();
|
ConjugateGradientMultiShift(Integer maxit,MultiShiftFunction &_shifts) :
|
||||||
|
MaxIterations(maxit),
|
||||||
|
shifts(_shifts)
|
||||||
|
{
|
||||||
|
verbose=1;
|
||||||
|
}
|
||||||
|
|
||||||
// RealD Tolerance;
|
void operator() (LinearOperatorBase<Field> &Linop, const Field &src, Field &psi)
|
||||||
Integer MaxIterations;
|
{
|
||||||
Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion
|
GridBase *grid = src._grid;
|
||||||
std::vector<int> IterationsToCompleteShift; // Iterations for this shift
|
int nshift = shifts.order;
|
||||||
int verbose;
|
std::vector<Field> results(nshift,grid);
|
||||||
MultiShiftFunction shifts;
|
(*this)(Linop,src,results,psi);
|
||||||
std::vector<RealD> TrueResidualShift;
|
}
|
||||||
|
void operator() (LinearOperatorBase<Field> &Linop, const Field &src, std::vector<Field> &results, Field &psi)
|
||||||
|
{
|
||||||
|
int nshift = shifts.order;
|
||||||
|
|
||||||
ConjugateGradientMultiShift(Integer maxit, const MultiShiftFunction &_shifts) :
|
(*this)(Linop,src,results);
|
||||||
MaxIterations(maxit),
|
|
||||||
shifts(_shifts)
|
psi = shifts.norm*src;
|
||||||
{
|
for(int i=0;i<nshift;i++){
|
||||||
verbose=1;
|
psi = psi + shifts.residues[i]*results[i];
|
||||||
IterationsToCompleteShift.resize(_shifts.order);
|
|
||||||
TrueResidualShift.resize(_shifts.order);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator() (LinearOperatorBase<Field> &Linop, const Field &src, Field &psi)
|
return;
|
||||||
{
|
}
|
||||||
GridBase *grid = src.Grid();
|
|
||||||
int nshift = shifts.order;
|
|
||||||
std::vector<Field> results(nshift,grid);
|
|
||||||
(*this)(Linop,src,results,psi);
|
|
||||||
}
|
|
||||||
void operator() (LinearOperatorBase<Field> &Linop, const Field &src, std::vector<Field> &results, Field &psi)
|
|
||||||
{
|
|
||||||
int nshift = shifts.order;
|
|
||||||
|
|
||||||
(*this)(Linop,src,results);
|
void operator() (LinearOperatorBase<Field> &Linop, const Field &src, std::vector<Field> &psi)
|
||||||
|
{
|
||||||
|
|
||||||
psi = shifts.norm*src;
|
GridBase *grid = src._grid;
|
||||||
for(int i=0;i<nshift;i++){
|
|
||||||
psi = psi + shifts.residues[i]*results[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// Convenience references to the info stored in "MultiShiftFunction"
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
int nshift = shifts.order;
|
||||||
|
|
||||||
|
std::vector<RealD> &mass(shifts.poles); // Make references to array in "shifts"
|
||||||
|
std::vector<RealD> &mresidual(shifts.tolerances);
|
||||||
|
std::vector<RealD> alpha(nshift,1.0);
|
||||||
|
std::vector<Field> ps(nshift,grid);// Search directions
|
||||||
|
|
||||||
|
assert(psi.size()==nshift);
|
||||||
|
assert(mass.size()==nshift);
|
||||||
|
assert(mresidual.size()==nshift);
|
||||||
|
|
||||||
|
// dynamic sized arrays on stack; 2d is a pain with vector
|
||||||
|
RealD bs[nshift];
|
||||||
|
RealD rsq[nshift];
|
||||||
|
RealD z[nshift][2];
|
||||||
|
int converged[nshift];
|
||||||
|
|
||||||
|
const int primary =0;
|
||||||
|
|
||||||
|
//Primary shift fields CG iteration
|
||||||
|
RealD a,b,c,d;
|
||||||
|
RealD cp,bp,qq; //prev
|
||||||
|
|
||||||
|
// Matrix mult fields
|
||||||
|
Field r(grid);
|
||||||
|
Field p(grid);
|
||||||
|
Field tmp(grid);
|
||||||
|
Field mmp(grid);
|
||||||
|
|
||||||
|
// Check lightest mass
|
||||||
|
for(int s=0;s<nshift;s++){
|
||||||
|
assert( mass[s]>= mass[primary] );
|
||||||
|
converged[s]=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator() (LinearOperatorBase<Field> &Linop, const Field &src, std::vector<Field> &psi)
|
// Wire guess to zero
|
||||||
{
|
// Residuals "r" are src
|
||||||
GRID_TRACE("ConjugateGradientMultiShift");
|
// First search direction "p" is also src
|
||||||
|
cp = norm2(src);
|
||||||
|
for(int s=0;s<nshift;s++){
|
||||||
|
rsq[s] = cp * mresidual[s] * mresidual[s];
|
||||||
|
std::cout<<GridLogMessage<<"ConjugateGradientMultiShift: shift "<<s
|
||||||
|
<<" target resid "<<rsq[s]<<std::endl;
|
||||||
|
ps[s] = src;
|
||||||
|
}
|
||||||
|
// r and p for primary
|
||||||
|
r=src;
|
||||||
|
p=src;
|
||||||
|
|
||||||
GridBase *grid = src.Grid();
|
//MdagM+m[0]
|
||||||
|
Linop.HermOpAndNorm(p,mmp,d,qq);
|
||||||
|
axpy(mmp,mass[0],p,mmp);
|
||||||
|
RealD rn = norm2(p);
|
||||||
|
d += rn*mass[0];
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
// have verified that inner product of
|
||||||
// Convenience references to the info stored in "MultiShiftFunction"
|
// p and mmp is equal to d after this since
|
||||||
////////////////////////////////////////////////////////////////////////
|
// the d computation is tricky
|
||||||
int nshift = shifts.order;
|
// qq = real(innerProduct(p,mmp));
|
||||||
|
// std::cout<<GridLogMessage << "debug equal ? qq "<<qq<<" d "<< d<<std::endl;
|
||||||
|
|
||||||
std::vector<RealD> &mass(shifts.poles); // Make references to array in "shifts"
|
b = -cp /d;
|
||||||
std::vector<RealD> &mresidual(shifts.tolerances);
|
|
||||||
std::vector<RealD> alpha(nshift,1.0);
|
|
||||||
std::vector<Field> ps(nshift,grid);// Search directions
|
|
||||||
|
|
||||||
GRID_ASSERT(psi.size()==nshift);
|
// Set up the various shift variables
|
||||||
GRID_ASSERT(mass.size()==nshift);
|
int iz=0;
|
||||||
GRID_ASSERT(mresidual.size()==nshift);
|
z[0][1-iz] = 1.0;
|
||||||
|
z[0][iz] = 1.0;
|
||||||
|
bs[0] = b;
|
||||||
|
for(int s=1;s<nshift;s++){
|
||||||
|
z[s][1-iz] = 1.0;
|
||||||
|
z[s][iz] = 1.0/( 1.0 - b*(mass[s]-mass[0]));
|
||||||
|
bs[s] = b*z[s][iz];
|
||||||
|
}
|
||||||
|
|
||||||
// remove dynamic sized arrays on stack; 2d is a pain with vector
|
// r += b[0] A.p[0]
|
||||||
std::vector<RealD> bs(nshift);
|
// c= norm(r)
|
||||||
std::vector<RealD> rsq(nshift);
|
c=axpy_norm(r,b,mmp,r);
|
||||||
std::vector<std::array<RealD,2> > z(nshift);
|
|
||||||
std::vector<int> converged(nshift);
|
|
||||||
|
|
||||||
const int primary =0;
|
|
||||||
|
|
||||||
//Primary shift fields CG iteration
|
|
||||||
RealD a,b,c,d;
|
|
||||||
RealD cp,bp,qq; //prev
|
|
||||||
|
|
||||||
// Matrix mult fields
|
|
||||||
Field r(grid);
|
|
||||||
Field p(grid);
|
|
||||||
Field tmp(grid);
|
|
||||||
Field mmp(grid);
|
|
||||||
|
|
||||||
// Check lightest mass
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
GRID_ASSERT( mass[s]>= mass[primary] );
|
|
||||||
converged[s]=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wire guess to zero
|
|
||||||
// Residuals "r" are src
|
|
||||||
// First search direction "p" is also src
|
|
||||||
cp = norm2(src);
|
|
||||||
|
|
||||||
// Handle trivial case of zero src.
|
|
||||||
if( cp == 0. ){
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
psi[s] = Zero();
|
|
||||||
IterationsToCompleteShift[s] = 1;
|
|
||||||
TrueResidualShift[s] = 0.;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
rsq[s] = cp * mresidual[s] * mresidual[s];
|
|
||||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShift: shift "<<s
|
|
||||||
<<" target resid^2 "<<rsq[s]<<std::endl;
|
|
||||||
ps[s] = src;
|
|
||||||
}
|
|
||||||
// r and p for primary
|
|
||||||
r=src;
|
|
||||||
p=src;
|
|
||||||
|
|
||||||
//MdagM+m[0]
|
|
||||||
Linop.HermOpAndNorm(p,mmp,d,qq);
|
|
||||||
axpy(mmp,mass[0],p,mmp);
|
|
||||||
RealD rn = norm2(p);
|
|
||||||
d += rn*mass[0];
|
|
||||||
|
|
||||||
// have verified that inner product of
|
|
||||||
// p and mmp is equal to d after this since
|
|
||||||
// the d computation is tricky
|
|
||||||
// qq = real(innerProduct(p,mmp));
|
|
||||||
// std::cout<<GridLogMessage << "debug equal ? qq "<<qq<<" d "<< d<<std::endl;
|
|
||||||
|
|
||||||
b = -cp /d;
|
|
||||||
|
|
||||||
// Set up the various shift variables
|
|
||||||
int iz=0;
|
|
||||||
z[0][1-iz] = 1.0;
|
|
||||||
z[0][iz] = 1.0;
|
|
||||||
bs[0] = b;
|
|
||||||
for(int s=1;s<nshift;s++){
|
|
||||||
z[s][1-iz] = 1.0;
|
|
||||||
z[s][iz] = 1.0/( 1.0 - b*(mass[s]-mass[0]));
|
|
||||||
bs[s] = b*z[s][iz];
|
|
||||||
}
|
|
||||||
|
|
||||||
// r += b[0] A.p[0]
|
|
||||||
// c= norm(r)
|
|
||||||
c=axpy_norm(r,b,mmp,r);
|
|
||||||
|
|
||||||
for(int s=0;s<nshift;s++) {
|
|
||||||
axpby(psi[s],0.,-bs[s]*alpha[s],src,src);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << "ConjugateGradientMultiShift: initial rn (|src|^2) =" << rn << " qq (|MdagM src|^2) =" << qq << " d ( dot(src, [MdagM + m_0]src) ) =" << d << " c=" << c << std::endl;
|
|
||||||
|
|
||||||
|
for(int s=0;s<nshift;s++) {
|
||||||
|
axpby(psi[s],0.,-bs[s]*alpha[s],src,src);
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
// Timers
|
// Timers
|
||||||
@@ -197,37 +175,37 @@ public:
|
|||||||
GridStopWatch SolverTimer;
|
GridStopWatch SolverTimer;
|
||||||
SolverTimer.Start();
|
SolverTimer.Start();
|
||||||
|
|
||||||
// Iteration loop
|
// Iteration loop
|
||||||
int k;
|
int k;
|
||||||
|
|
||||||
for (k=1;k<=MaxIterations;k++){
|
for (k=1;k<=MaxIterations;k++){
|
||||||
|
|
||||||
a = c /cp;
|
a = c /cp;
|
||||||
AXPYTimer.Start();
|
AXPYTimer.Start();
|
||||||
axpy(p,a,p,r);
|
axpy(p,a,p,r);
|
||||||
AXPYTimer.Stop();
|
AXPYTimer.Stop();
|
||||||
|
|
||||||
// Note to self - direction ps is iterated seperately
|
// Note to self - direction ps is iterated seperately
|
||||||
// for each shift. Does not appear to have any scope
|
// for each shift. Does not appear to have any scope
|
||||||
// for avoiding linear algebra in "single" case.
|
// for avoiding linear algebra in "single" case.
|
||||||
//
|
//
|
||||||
// However SAME r is used. Could load "r" and update
|
// However SAME r is used. Could load "r" and update
|
||||||
// ALL ps[s]. 2/3 Bandwidth saving
|
// ALL ps[s]. 2/3 Bandwidth saving
|
||||||
// New Kernel: Load r, vector of coeffs, vector of pointers ps
|
// New Kernel: Load r, vector of coeffs, vector of pointers ps
|
||||||
AXPYTimer.Start();
|
AXPYTimer.Start();
|
||||||
for(int s=0;s<nshift;s++){
|
for(int s=0;s<nshift;s++){
|
||||||
if ( ! converged[s] ) {
|
if ( ! converged[s] ) {
|
||||||
if (s==0){
|
if (s==0){
|
||||||
axpy(ps[s],a,ps[s],r);
|
axpy(ps[s],a,ps[s],r);
|
||||||
} else{
|
} else{
|
||||||
RealD as =a *z[s][iz]*bs[s] /(z[s][1-iz]*b);
|
RealD as =a *z[s][iz]*bs[s] /(z[s][1-iz]*b);
|
||||||
axpby(ps[s],z[s][iz],as,r,ps[s]);
|
axpby(ps[s],z[s][iz],as,r,ps[s]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
AXPYTimer.Stop();
|
AXPYTimer.Stop();
|
||||||
|
|
||||||
cp=c;
|
cp=c;
|
||||||
MatrixTimer.Start();
|
MatrixTimer.Start();
|
||||||
//Linop.HermOpAndNorm(p,mmp,d,qq); // d is used
|
//Linop.HermOpAndNorm(p,mmp,d,qq); // d is used
|
||||||
// The below is faster on KNL
|
// The below is faster on KNL
|
||||||
@@ -237,110 +215,108 @@ public:
|
|||||||
MatrixTimer.Stop();
|
MatrixTimer.Stop();
|
||||||
|
|
||||||
AXPYTimer.Start();
|
AXPYTimer.Start();
|
||||||
axpy(mmp,mass[0],p,mmp);
|
axpy(mmp,mass[0],p,mmp);
|
||||||
AXPYTimer.Stop();
|
AXPYTimer.Stop();
|
||||||
RealD rn = norm2(p);
|
RealD rn = norm2(p);
|
||||||
d += rn*mass[0];
|
d += rn*mass[0];
|
||||||
|
|
||||||
bp=b;
|
bp=b;
|
||||||
b=-cp/d;
|
b=-cp/d;
|
||||||
|
|
||||||
AXPYTimer.Start();
|
AXPYTimer.Start();
|
||||||
c=axpy_norm(r,b,mmp,r);
|
c=axpy_norm(r,b,mmp,r);
|
||||||
AXPYTimer.Stop();
|
AXPYTimer.Stop();
|
||||||
|
|
||||||
// Toggle the recurrence history
|
// Toggle the recurrence history
|
||||||
bs[0] = b;
|
bs[0] = b;
|
||||||
iz = 1-iz;
|
iz = 1-iz;
|
||||||
ShiftTimer.Start();
|
ShiftTimer.Start();
|
||||||
for(int s=1;s<nshift;s++){
|
for(int s=1;s<nshift;s++){
|
||||||
if((!converged[s])){
|
if((!converged[s])){
|
||||||
RealD z0 = z[s][1-iz];
|
RealD z0 = z[s][1-iz];
|
||||||
RealD z1 = z[s][iz];
|
RealD z1 = z[s][iz];
|
||||||
z[s][iz] = z0*z1*bp
|
z[s][iz] = z0*z1*bp
|
||||||
/ (b*a*(z1-z0) + z1*bp*(1- (mass[s]-mass[0])*b));
|
/ (b*a*(z1-z0) + z1*bp*(1- (mass[s]-mass[0])*b));
|
||||||
bs[s] = b*z[s][iz]/z0; // NB sign rel to Mike
|
bs[s] = b*z[s][iz]/z0; // NB sign rel to Mike
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ShiftTimer.Stop();
|
ShiftTimer.Stop();
|
||||||
|
|
||||||
for(int s=0;s<nshift;s++){
|
for(int s=0;s<nshift;s++){
|
||||||
int ss = s;
|
int ss = s;
|
||||||
// Scope for optimisation here in case of "single".
|
// Scope for optimisation here in case of "single".
|
||||||
// Could load psi[0] and pull all ps[s] in.
|
// Could load psi[0] and pull all ps[s] in.
|
||||||
// if ( single ) ss=primary;
|
// if ( single ) ss=primary;
|
||||||
// Bandwith saving in single case is Ls * 3 -> 2+Ls, so ~ 3x saving
|
// Bandwith saving in single case is Ls * 3 -> 2+Ls, so ~ 3x saving
|
||||||
// Pipelined CG gain:
|
// Pipelined CG gain:
|
||||||
//
|
//
|
||||||
// New Kernel: Load r, vector of coeffs, vector of pointers ps
|
// New Kernel: Load r, vector of coeffs, vector of pointers ps
|
||||||
// New Kernel: Load psi[0], vector of coeffs, vector of pointers ps
|
// New Kernel: Load psi[0], vector of coeffs, vector of pointers ps
|
||||||
// If can predict the coefficient bs then we can fuse these and avoid write reread cyce
|
// If can predict the coefficient bs then we can fuse these and avoid write reread cyce
|
||||||
// on ps[s].
|
// on ps[s].
|
||||||
// Before: 3 x npole + 3 x npole
|
// Before: 3 x npole + 3 x npole
|
||||||
// After : 2 x npole (ps[s]) => 3x speed up of multishift CG.
|
// After : 2 x npole (ps[s]) => 3x speed up of multishift CG.
|
||||||
|
|
||||||
if( (!converged[s]) ) {
|
if( (!converged[s]) ) {
|
||||||
axpy(psi[ss],-bs[s]*alpha[s],ps[s],psi[ss]);
|
axpy(psi[ss],-bs[s]*alpha[s],ps[s],psi[ss]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Convergence checks
|
// Convergence checks
|
||||||
int all_converged = 1;
|
int all_converged = 1;
|
||||||
for(int s=0;s<nshift;s++){
|
for(int s=0;s<nshift;s++){
|
||||||
|
|
||||||
if ( (!converged[s]) ){
|
if ( (!converged[s]) ){
|
||||||
IterationsToCompleteShift[s] = k;
|
|
||||||
|
|
||||||
RealD css = c * z[s][iz]* z[s][iz];
|
RealD css = c * z[s][iz]* z[s][iz];
|
||||||
|
|
||||||
if(css<rsq[s]){
|
|
||||||
if ( ! converged[s] )
|
|
||||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShift k="<<k<<" Shift "<<s<<" has converged"<<std::endl;
|
|
||||||
converged[s]=1;
|
|
||||||
} else {
|
|
||||||
all_converged=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if(css<rsq[s]){
|
||||||
|
if ( ! converged[s] )
|
||||||
|
std::cout<<GridLogMessage<<"ConjugateGradientMultiShift k="<<k<<" Shift "<<s<<" has converged"<<std::endl;
|
||||||
|
converged[s]=1;
|
||||||
|
} else {
|
||||||
|
all_converged=0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if ( all_converged ){
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( all_converged ){
|
||||||
|
|
||||||
SolverTimer.Stop();
|
SolverTimer.Stop();
|
||||||
|
|
||||||
|
|
||||||
std::cout<<GridLogMessage<< "CGMultiShift: All shifts have converged iteration "<<k<<std::endl;
|
std::cout<<GridLogMessage<< "CGMultiShift: All shifts have converged iteration "<<k<<std::endl;
|
||||||
std::cout<<GridLogMessage<< "CGMultiShift: Checking solutions"<<std::endl;
|
std::cout<<GridLogMessage<< "CGMultiShift: Checking solutions"<<std::endl;
|
||||||
|
|
||||||
// Check answers
|
// Check answers
|
||||||
for(int s=0; s < nshift; s++) {
|
for(int s=0; s < nshift; s++) {
|
||||||
Linop.HermOpAndNorm(psi[s],mmp,d,qq);
|
Linop.HermOpAndNorm(psi[s],mmp,d,qq);
|
||||||
axpy(tmp,mass[s],psi[s],mmp);
|
axpy(tmp,mass[s],psi[s],mmp);
|
||||||
axpy(r,-alpha[s],src,tmp);
|
axpy(r,-alpha[s],src,tmp);
|
||||||
RealD rn = norm2(r);
|
RealD rn = norm2(r);
|
||||||
RealD cn = norm2(src);
|
RealD cn = norm2(src);
|
||||||
TrueResidualShift[s] = std::sqrt(rn/cn);
|
std::cout<<GridLogMessage<<"CGMultiShift: shift["<<s<<"] true residual "<<std::sqrt(rn/cn)<<std::endl;
|
||||||
std::cout<<GridLogMessage<<"CGMultiShift: shift["<<s<<"] true residual "<< TrueResidualShift[s] <<std::endl;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "Time Breakdown "<<std::endl;
|
std::cout << GridLogMessage << "Time Breakdown "<<std::endl;
|
||||||
std::cout << GridLogMessage << "\tElapsed " << SolverTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tElapsed " << SolverTimer.Elapsed() <<std::endl;
|
||||||
std::cout << GridLogMessage << "\tAXPY " << AXPYTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tAXPY " << AXPYTimer.Elapsed() <<std::endl;
|
||||||
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tMarix " << MatrixTimer.Elapsed() <<std::endl;
|
||||||
std::cout << GridLogMessage << "\tShift " << ShiftTimer.Elapsed() <<std::endl;
|
std::cout << GridLogMessage << "\tShift " << ShiftTimer.Elapsed() <<std::endl;
|
||||||
|
|
||||||
IterationsToComplete = k;
|
IterationsToComplete = k;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
// ugly hack
|
|
||||||
std::cout<<GridLogMessage<<"CG multi shift did not converge"<<std::endl;
|
|
||||||
// GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
NAMESPACE_END(Grid);
|
}
|
||||||
|
// ugly hack
|
||||||
|
std::cout<<GridLogMessage<<"CG multi shift did not converge"<<std::endl;
|
||||||
|
// assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1,373 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/ConjugateGradientMultiShift.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
|
||||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|
||||||
Author: Christopher Kelly <ckelly@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);
|
|
||||||
|
|
||||||
//CK 2020: A variant of the multi-shift conjugate gradient with the matrix multiplication in single precision.
|
|
||||||
//The residual is stored in single precision, but the search directions and solution are stored in double precision.
|
|
||||||
//Every update_freq iterations the residual is corrected in double precision.
|
|
||||||
//For safety the a final regular CG is applied to clean up if necessary
|
|
||||||
|
|
||||||
//PB Pure single, then double fixup
|
|
||||||
|
|
||||||
template<class FieldD, class FieldF,
|
|
||||||
typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0,
|
|
||||||
typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
|
|
||||||
class ConjugateGradientMultiShiftMixedPrecCleanup : public OperatorMultiFunction<FieldD>,
|
|
||||||
public OperatorFunction<FieldD>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
using OperatorFunction<FieldD>::operator();
|
|
||||||
|
|
||||||
RealD Tolerance;
|
|
||||||
Integer MaxIterationsMshift;
|
|
||||||
Integer MaxIterations;
|
|
||||||
Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion
|
|
||||||
std::vector<int> IterationsToCompleteShift; // Iterations for this shift
|
|
||||||
int verbose;
|
|
||||||
MultiShiftFunction shifts;
|
|
||||||
std::vector<RealD> TrueResidualShift;
|
|
||||||
|
|
||||||
int ReliableUpdateFreq; //number of iterations between reliable updates
|
|
||||||
|
|
||||||
GridBase* SinglePrecGrid; //Grid for single-precision fields
|
|
||||||
LinearOperatorBase<FieldF> &Linop_f; //single precision
|
|
||||||
|
|
||||||
ConjugateGradientMultiShiftMixedPrecCleanup(Integer maxit, const MultiShiftFunction &_shifts,
|
|
||||||
GridBase* _SinglePrecGrid, LinearOperatorBase<FieldF> &_Linop_f,
|
|
||||||
int _ReliableUpdateFreq) :
|
|
||||||
MaxIterationsMshift(maxit), shifts(_shifts), SinglePrecGrid(_SinglePrecGrid), Linop_f(_Linop_f), ReliableUpdateFreq(_ReliableUpdateFreq),
|
|
||||||
MaxIterations(20000)
|
|
||||||
{
|
|
||||||
verbose=1;
|
|
||||||
IterationsToCompleteShift.resize(_shifts.order);
|
|
||||||
TrueResidualShift.resize(_shifts.order);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator() (LinearOperatorBase<FieldD> &Linop, const FieldD &src, FieldD &psi)
|
|
||||||
{
|
|
||||||
GridBase *grid = src.Grid();
|
|
||||||
int nshift = shifts.order;
|
|
||||||
std::vector<FieldD> results(nshift,grid);
|
|
||||||
(*this)(Linop,src,results,psi);
|
|
||||||
}
|
|
||||||
void operator() (LinearOperatorBase<FieldD> &Linop, const FieldD &src, std::vector<FieldD> &results, FieldD &psi)
|
|
||||||
{
|
|
||||||
int nshift = shifts.order;
|
|
||||||
|
|
||||||
(*this)(Linop,src,results);
|
|
||||||
|
|
||||||
psi = shifts.norm*src;
|
|
||||||
for(int i=0;i<nshift;i++){
|
|
||||||
psi = psi + shifts.residues[i]*results[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator() (LinearOperatorBase<FieldD> &Linop_d, const FieldD &src_d, std::vector<FieldD> &psi_d)
|
|
||||||
{
|
|
||||||
GRID_TRACE("ConjugateGradientMultiShiftMixedPrecCleanup");
|
|
||||||
GridBase *DoublePrecGrid = src_d.Grid();
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
// Convenience references to the info stored in "MultiShiftFunction"
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
int nshift = shifts.order;
|
|
||||||
|
|
||||||
std::vector<RealD> &mass(shifts.poles); // Make references to array in "shifts"
|
|
||||||
std::vector<RealD> &mresidual(shifts.tolerances);
|
|
||||||
std::vector<RealD> alpha(nshift,1.0);
|
|
||||||
|
|
||||||
//Double precision search directions
|
|
||||||
FieldD p_d(DoublePrecGrid);
|
|
||||||
std::vector<FieldF> ps_f (nshift, SinglePrecGrid);// Search directions (single precision)
|
|
||||||
std::vector<FieldF> psi_f(nshift, SinglePrecGrid);// solutions (single precision)
|
|
||||||
|
|
||||||
FieldD tmp_d(DoublePrecGrid);
|
|
||||||
FieldD r_d(DoublePrecGrid);
|
|
||||||
FieldF r_f(SinglePrecGrid);
|
|
||||||
FieldD mmp_d(DoublePrecGrid);
|
|
||||||
|
|
||||||
GRID_ASSERT(psi_d.size()==nshift);
|
|
||||||
GRID_ASSERT(mass.size()==nshift);
|
|
||||||
GRID_ASSERT(mresidual.size()==nshift);
|
|
||||||
|
|
||||||
// dynamic sized arrays on stack; 2d is a pain with vector
|
|
||||||
std::vector<RealD> bs(nshift);
|
|
||||||
std::vector<RealD> rsq(nshift);
|
|
||||||
std::vector<RealD> rsqf(nshift);
|
|
||||||
std::vector<std::array<RealD,2> > z(nshift);
|
|
||||||
std::vector<int> converged(nshift);
|
|
||||||
|
|
||||||
const int primary =0;
|
|
||||||
|
|
||||||
//Primary shift fields CG iteration
|
|
||||||
RealD a,b,c,d;
|
|
||||||
RealD cp,bp,qq; //prev
|
|
||||||
|
|
||||||
// Matrix mult fields
|
|
||||||
FieldF p_f(SinglePrecGrid);
|
|
||||||
FieldF mmp_f(SinglePrecGrid);
|
|
||||||
|
|
||||||
// Check lightest mass
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
GRID_ASSERT( mass[s]>= mass[primary] );
|
|
||||||
converged[s]=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wire guess to zero
|
|
||||||
// Residuals "r" are src
|
|
||||||
// First search direction "p" is also src
|
|
||||||
cp = norm2(src_d);
|
|
||||||
|
|
||||||
// Handle trivial case of zero src.
|
|
||||||
if( cp == 0. ){
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
psi_d[s] = Zero();
|
|
||||||
psi_f[s] = Zero();
|
|
||||||
IterationsToCompleteShift[s] = 1;
|
|
||||||
TrueResidualShift[s] = 0.;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
rsq[s] = cp * mresidual[s] * mresidual[s];
|
|
||||||
rsqf[s] =rsq[s];
|
|
||||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrecCleanup: shift "<< s <<" target resid "<<rsq[s]<<std::endl;
|
|
||||||
// ps_d[s] = src_d;
|
|
||||||
precisionChange(ps_f[s],src_d);
|
|
||||||
}
|
|
||||||
// r and p for primary
|
|
||||||
p_d = src_d; //primary copy --- make this a reference to ps_d to save axpys
|
|
||||||
r_d = p_d;
|
|
||||||
|
|
||||||
//MdagM+m[0]
|
|
||||||
precisionChange(p_f,p_d);
|
|
||||||
Linop_f.HermOpAndNorm(p_f,mmp_f,d,qq); // mmp = MdagM p d=real(dot(p, mmp)), qq=norm2(mmp)
|
|
||||||
precisionChange(tmp_d,mmp_f);
|
|
||||||
Linop_d.HermOpAndNorm(p_d,mmp_d,d,qq); // mmp = MdagM p d=real(dot(p, mmp)), qq=norm2(mmp)
|
|
||||||
tmp_d = tmp_d - mmp_d;
|
|
||||||
std::cout << " Testing operators match "<<norm2(mmp_d)<<" f "<<norm2(mmp_f)<<" diff "<< norm2(tmp_d)<<std::endl;
|
|
||||||
// GRID_ASSERT(norm2(tmp_d)< 1.0e-4);
|
|
||||||
|
|
||||||
axpy(mmp_d,mass[0],p_d,mmp_d);
|
|
||||||
RealD rn = norm2(p_d);
|
|
||||||
d += rn*mass[0];
|
|
||||||
|
|
||||||
b = -cp /d;
|
|
||||||
|
|
||||||
// Set up the various shift variables
|
|
||||||
int iz=0;
|
|
||||||
z[0][1-iz] = 1.0;
|
|
||||||
z[0][iz] = 1.0;
|
|
||||||
bs[0] = b;
|
|
||||||
for(int s=1;s<nshift;s++){
|
|
||||||
z[s][1-iz] = 1.0;
|
|
||||||
z[s][iz] = 1.0/( 1.0 - b*(mass[s]-mass[0]));
|
|
||||||
bs[s] = b*z[s][iz];
|
|
||||||
}
|
|
||||||
|
|
||||||
// r += b[0] A.p[0]
|
|
||||||
// c= norm(r)
|
|
||||||
c=axpy_norm(r_d,b,mmp_d,r_d);
|
|
||||||
|
|
||||||
for(int s=0;s<nshift;s++) {
|
|
||||||
axpby(psi_d[s],0.,-bs[s]*alpha[s],src_d,src_d);
|
|
||||||
precisionChange(psi_f[s],psi_d[s]);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////
|
|
||||||
// Timers
|
|
||||||
///////////////////////////////////////
|
|
||||||
GridStopWatch AXPYTimer, ShiftTimer, QRTimer, MatrixTimer, SolverTimer, PrecChangeTimer, CleanupTimer;
|
|
||||||
|
|
||||||
SolverTimer.Start();
|
|
||||||
|
|
||||||
// Iteration loop
|
|
||||||
int k;
|
|
||||||
|
|
||||||
for (k=1;k<=MaxIterationsMshift;k++){
|
|
||||||
|
|
||||||
a = c /cp;
|
|
||||||
AXPYTimer.Start();
|
|
||||||
axpy(p_d,a,p_d,r_d);
|
|
||||||
AXPYTimer.Stop();
|
|
||||||
|
|
||||||
PrecChangeTimer.Start();
|
|
||||||
precisionChange(r_f, r_d);
|
|
||||||
PrecChangeTimer.Stop();
|
|
||||||
|
|
||||||
AXPYTimer.Start();
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
if ( ! converged[s] ) {
|
|
||||||
if (s==0){
|
|
||||||
axpy(ps_f[s],a,ps_f[s],r_f);
|
|
||||||
} else{
|
|
||||||
RealD as =a *z[s][iz]*bs[s] /(z[s][1-iz]*b);
|
|
||||||
axpby(ps_f[s],z[s][iz],as,r_f,ps_f[s]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AXPYTimer.Stop();
|
|
||||||
|
|
||||||
cp=c;
|
|
||||||
PrecChangeTimer.Start();
|
|
||||||
precisionChange(p_f, p_d); //get back single prec search direction for linop
|
|
||||||
PrecChangeTimer.Stop();
|
|
||||||
MatrixTimer.Start();
|
|
||||||
Linop_f.HermOp(p_f,mmp_f);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
PrecChangeTimer.Start();
|
|
||||||
precisionChange(mmp_d, mmp_f); // From Float to Double
|
|
||||||
PrecChangeTimer.Stop();
|
|
||||||
|
|
||||||
d=real(innerProduct(p_d,mmp_d));
|
|
||||||
axpy(mmp_d,mass[0],p_d,mmp_d);
|
|
||||||
RealD rn = norm2(p_d);
|
|
||||||
d += rn*mass[0];
|
|
||||||
|
|
||||||
bp=b;
|
|
||||||
b=-cp/d;
|
|
||||||
|
|
||||||
// Toggle the recurrence history
|
|
||||||
bs[0] = b;
|
|
||||||
iz = 1-iz;
|
|
||||||
ShiftTimer.Start();
|
|
||||||
for(int s=1;s<nshift;s++){
|
|
||||||
if((!converged[s])){
|
|
||||||
RealD z0 = z[s][1-iz];
|
|
||||||
RealD z1 = z[s][iz];
|
|
||||||
z[s][iz] = z0*z1*bp
|
|
||||||
/ (b*a*(z1-z0) + z1*bp*(1- (mass[s]-mass[0])*b));
|
|
||||||
bs[s] = b*z[s][iz]/z0; // NB sign rel to Mike
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ShiftTimer.Stop();
|
|
||||||
|
|
||||||
//Update single precision solutions
|
|
||||||
AXPYTimer.Start();
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
int ss = s;
|
|
||||||
if( (!converged[s]) ) {
|
|
||||||
axpy(psi_f[ss],-bs[s]*alpha[s],ps_f[s],psi_f[ss]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c = axpy_norm(r_d,b,mmp_d,r_d);
|
|
||||||
AXPYTimer.Stop();
|
|
||||||
|
|
||||||
// Convergence checks
|
|
||||||
int all_converged = 1;
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
|
|
||||||
if ( (!converged[s]) ){
|
|
||||||
IterationsToCompleteShift[s] = k;
|
|
||||||
|
|
||||||
RealD css = c * z[s][iz]* z[s][iz];
|
|
||||||
|
|
||||||
if(css<rsqf[s]){
|
|
||||||
if ( ! converged[s] )
|
|
||||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrecCleanup k="<<k<<" Shift "<<s<<" has converged"<<std::endl;
|
|
||||||
converged[s]=1;
|
|
||||||
} else {
|
|
||||||
all_converged=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( all_converged || k == MaxIterationsMshift-1){
|
|
||||||
|
|
||||||
SolverTimer.Stop();
|
|
||||||
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
precisionChange(psi_d[s],psi_f[s]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if ( all_converged ){
|
|
||||||
std::cout<<GridLogMessage<< "ConjugateGradientMultiShiftMixedPrecCleanup: All shifts have converged iteration "<<k<<std::endl;
|
|
||||||
std::cout<<GridLogMessage<< "ConjugateGradientMultiShiftMixedPrecCleanup: Checking solutions"<<std::endl;
|
|
||||||
} else {
|
|
||||||
std::cout<<GridLogMessage<< "ConjugateGradientMultiShiftMixedPrecCleanup: Not all shifts have converged iteration "<<k<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check answers
|
|
||||||
for(int s=0; s < nshift; s++) {
|
|
||||||
Linop_d.HermOpAndNorm(psi_d[s],mmp_d,d,qq);
|
|
||||||
axpy(tmp_d,mass[s],psi_d[s],mmp_d);
|
|
||||||
axpy(r_d,-alpha[s],src_d,tmp_d);
|
|
||||||
RealD rn = norm2(r_d);
|
|
||||||
RealD cn = norm2(src_d);
|
|
||||||
TrueResidualShift[s] = std::sqrt(rn/cn);
|
|
||||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrecCleanup: shift["<<s<<"] true residual "<< TrueResidualShift[s] << " target " << mresidual[s] << std::endl;
|
|
||||||
|
|
||||||
//If we have not reached the desired tolerance, do a (mixed precision) CG cleanup
|
|
||||||
if(rn >= rsq[s]){
|
|
||||||
CleanupTimer.Start();
|
|
||||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrecCleanup: performing cleanup step for shift " << s << std::endl;
|
|
||||||
|
|
||||||
//Setup linear operators for final cleanup
|
|
||||||
ConjugateGradientMultiShiftMixedPrecSupport::ShiftedLinop<FieldD> Linop_shift_d(Linop_d, mass[s]);
|
|
||||||
ConjugateGradientMultiShiftMixedPrecSupport::ShiftedLinop<FieldF> Linop_shift_f(Linop_f, mass[s]);
|
|
||||||
|
|
||||||
MixedPrecisionConjugateGradient<FieldD,FieldF> cg(mresidual[s], MaxIterations, MaxIterations, SinglePrecGrid, Linop_shift_f, Linop_shift_d);
|
|
||||||
cg(src_d, psi_d[s]);
|
|
||||||
|
|
||||||
TrueResidualShift[s] = cg.TrueResidual;
|
|
||||||
CleanupTimer.Stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "ConjugateGradientMultiShiftMixedPrecCleanup: Time Breakdown for body"<<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tSolver " << SolverTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\t\tAXPY " << AXPYTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\t\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\t\tShift " << ShiftTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\t\tPrecision Change " << PrecChangeTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tFinal Cleanup " << CleanupTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tSolver+Cleanup " << SolverTimer.Elapsed() + CleanupTimer.Elapsed() << std::endl;
|
|
||||||
|
|
||||||
IterationsToComplete = k;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
std::cout<<GridLogMessage<<"CG multi shift did not converge"<<std::endl;
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
@@ -1,416 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/ConjugateGradientMultiShift.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
|
||||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|
||||||
Author: Christopher Kelly <ckelly@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_CONJUGATE_GRADIENT_MULTI_SHIFT_MIXEDPREC_H
|
|
||||||
#define GRID_CONJUGATE_GRADIENT_MULTI_SHIFT_MIXEDPREC_H
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
//CK 2020: A variant of the multi-shift conjugate gradient with the matrix multiplication in single precision.
|
|
||||||
//The residual is stored in single precision, but the search directions and solution are stored in double precision.
|
|
||||||
//Every update_freq iterations the residual is corrected in double precision.
|
|
||||||
|
|
||||||
//For safety the a final regular CG is applied to clean up if necessary
|
|
||||||
|
|
||||||
//Linop to add shift to input linop, used in cleanup CG
|
|
||||||
namespace ConjugateGradientMultiShiftMixedPrecSupport{
|
|
||||||
template<typename Field>
|
|
||||||
class ShiftedLinop: public LinearOperatorBase<Field>{
|
|
||||||
public:
|
|
||||||
LinearOperatorBase<Field> &linop_base;
|
|
||||||
RealD shift;
|
|
||||||
|
|
||||||
ShiftedLinop(LinearOperatorBase<Field> &_linop_base, RealD _shift): linop_base(_linop_base), shift(_shift){}
|
|
||||||
|
|
||||||
void OpDiag (const Field &in, Field &out){ GRID_ASSERT(0); }
|
|
||||||
void OpDir (const Field &in, Field &out,int dir,int disp){ GRID_ASSERT(0); }
|
|
||||||
void OpDirAll (const Field &in, std::vector<Field> &out){ GRID_ASSERT(0); }
|
|
||||||
|
|
||||||
void Op (const Field &in, Field &out){ GRID_ASSERT(0); }
|
|
||||||
void AdjOp (const Field &in, Field &out){ GRID_ASSERT(0); }
|
|
||||||
|
|
||||||
void HermOp(const Field &in, Field &out){
|
|
||||||
linop_base.HermOp(in, out);
|
|
||||||
axpy(out, shift, in, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){
|
|
||||||
HermOp(in,out);
|
|
||||||
ComplexD dot = innerProduct(in,out);
|
|
||||||
n1=real(dot);
|
|
||||||
n2=norm2(out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template<class FieldD, class FieldF,
|
|
||||||
typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0,
|
|
||||||
typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
|
|
||||||
class ConjugateGradientMultiShiftMixedPrec : public OperatorMultiFunction<FieldD>,
|
|
||||||
public OperatorFunction<FieldD>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
using OperatorFunction<FieldD>::operator();
|
|
||||||
|
|
||||||
RealD Tolerance;
|
|
||||||
Integer MaxIterationsMshift;
|
|
||||||
Integer MaxIterations;
|
|
||||||
Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion
|
|
||||||
std::vector<int> IterationsToCompleteShift; // Iterations for this shift
|
|
||||||
int verbose;
|
|
||||||
MultiShiftFunction shifts;
|
|
||||||
std::vector<RealD> TrueResidualShift;
|
|
||||||
|
|
||||||
int ReliableUpdateFreq; //number of iterations between reliable updates
|
|
||||||
|
|
||||||
GridBase* SinglePrecGrid; //Grid for single-precision fields
|
|
||||||
LinearOperatorBase<FieldF> &Linop_f; //single precision
|
|
||||||
|
|
||||||
ConjugateGradientMultiShiftMixedPrec(Integer maxit, const MultiShiftFunction &_shifts,
|
|
||||||
GridBase* _SinglePrecGrid, LinearOperatorBase<FieldF> &_Linop_f,
|
|
||||||
int _ReliableUpdateFreq) :
|
|
||||||
MaxIterationsMshift(maxit), shifts(_shifts), SinglePrecGrid(_SinglePrecGrid), Linop_f(_Linop_f), ReliableUpdateFreq(_ReliableUpdateFreq),
|
|
||||||
MaxIterations(20000)
|
|
||||||
{
|
|
||||||
verbose=1;
|
|
||||||
IterationsToCompleteShift.resize(_shifts.order);
|
|
||||||
TrueResidualShift.resize(_shifts.order);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator() (LinearOperatorBase<FieldD> &Linop, const FieldD &src, FieldD &psi)
|
|
||||||
{
|
|
||||||
GridBase *grid = src.Grid();
|
|
||||||
int nshift = shifts.order;
|
|
||||||
std::vector<FieldD> results(nshift,grid);
|
|
||||||
(*this)(Linop,src,results,psi);
|
|
||||||
}
|
|
||||||
void operator() (LinearOperatorBase<FieldD> &Linop, const FieldD &src, std::vector<FieldD> &results, FieldD &psi)
|
|
||||||
{
|
|
||||||
int nshift = shifts.order;
|
|
||||||
|
|
||||||
(*this)(Linop,src,results);
|
|
||||||
|
|
||||||
psi = shifts.norm*src;
|
|
||||||
for(int i=0;i<nshift;i++){
|
|
||||||
psi = psi + shifts.residues[i]*results[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator() (LinearOperatorBase<FieldD> &Linop_d, const FieldD &src_d, std::vector<FieldD> &psi_d)
|
|
||||||
{
|
|
||||||
GRID_TRACE("ConjugateGradientMultiShiftMixedPrec");
|
|
||||||
GridBase *DoublePrecGrid = src_d.Grid();
|
|
||||||
|
|
||||||
precisionChangeWorkspace pc_wk_s_to_d(DoublePrecGrid,SinglePrecGrid);
|
|
||||||
precisionChangeWorkspace pc_wk_d_to_s(SinglePrecGrid,DoublePrecGrid);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
// Convenience references to the info stored in "MultiShiftFunction"
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
int nshift = shifts.order;
|
|
||||||
|
|
||||||
std::vector<RealD> &mass(shifts.poles); // Make references to array in "shifts"
|
|
||||||
std::vector<RealD> &mresidual(shifts.tolerances);
|
|
||||||
std::vector<RealD> alpha(nshift,1.0);
|
|
||||||
|
|
||||||
//Double precision search directions
|
|
||||||
FieldD p_d(DoublePrecGrid);
|
|
||||||
std::vector<FieldD> ps_d(nshift, DoublePrecGrid);// Search directions (double precision)
|
|
||||||
|
|
||||||
FieldD tmp_d(DoublePrecGrid);
|
|
||||||
FieldD r_d(DoublePrecGrid);
|
|
||||||
FieldD mmp_d(DoublePrecGrid);
|
|
||||||
|
|
||||||
GRID_ASSERT(psi_d.size()==nshift);
|
|
||||||
GRID_ASSERT(mass.size()==nshift);
|
|
||||||
GRID_ASSERT(mresidual.size()==nshift);
|
|
||||||
|
|
||||||
// dynamic sized arrays on stack; 2d is a pain with vector
|
|
||||||
std::vector<RealD> bs(nshift);
|
|
||||||
std::vector<RealD> rsq(nshift);
|
|
||||||
std::vector<RealD> rsqf(nshift);
|
|
||||||
std::vector<std::array<RealD,2> > z(nshift);
|
|
||||||
std::vector<int> converged(nshift);
|
|
||||||
|
|
||||||
const int primary =0;
|
|
||||||
|
|
||||||
//Primary shift fields CG iteration
|
|
||||||
RealD a,b,c,d;
|
|
||||||
RealD cp,bp,qq; //prev
|
|
||||||
|
|
||||||
// Matrix mult fields
|
|
||||||
FieldF p_f(SinglePrecGrid);
|
|
||||||
FieldF mmp_f(SinglePrecGrid);
|
|
||||||
|
|
||||||
// Check lightest mass
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
GRID_ASSERT( mass[s]>= mass[primary] );
|
|
||||||
converged[s]=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wire guess to zero
|
|
||||||
// Residuals "r" are src
|
|
||||||
// First search direction "p" is also src
|
|
||||||
cp = norm2(src_d);
|
|
||||||
|
|
||||||
// Handle trivial case of zero src.
|
|
||||||
if( cp == 0. ){
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
psi_d[s] = Zero();
|
|
||||||
IterationsToCompleteShift[s] = 1;
|
|
||||||
TrueResidualShift[s] = 0.;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
rsq[s] = cp * mresidual[s] * mresidual[s];
|
|
||||||
rsqf[s] =rsq[s];
|
|
||||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrec: shift "<< s <<" target resid "<<rsq[s]<<std::endl;
|
|
||||||
ps_d[s] = src_d;
|
|
||||||
}
|
|
||||||
// r and p for primary
|
|
||||||
p_d = src_d; //primary copy --- make this a reference to ps_d to save axpys
|
|
||||||
r_d = p_d;
|
|
||||||
|
|
||||||
//MdagM+m[0]
|
|
||||||
precisionChange(p_f, p_d, pc_wk_d_to_s);
|
|
||||||
|
|
||||||
Linop_f.HermOpAndNorm(p_f,mmp_f,d,qq); // mmp = MdagM p d=real(dot(p, mmp)), qq=norm2(mmp)
|
|
||||||
precisionChange(tmp_d, mmp_f, pc_wk_s_to_d);
|
|
||||||
Linop_d.HermOpAndNorm(p_d,mmp_d,d,qq); // mmp = MdagM p d=real(dot(p, mmp)), qq=norm2(mmp)
|
|
||||||
tmp_d = tmp_d - mmp_d;
|
|
||||||
std::cout << " Testing operators match "<<norm2(mmp_d)<<" f "<<norm2(mmp_f)<<" diff "<< norm2(tmp_d)<<std::endl;
|
|
||||||
GRID_ASSERT(norm2(tmp_d)< 1.0);
|
|
||||||
|
|
||||||
axpy(mmp_d,mass[0],p_d,mmp_d);
|
|
||||||
RealD rn = norm2(p_d);
|
|
||||||
d += rn*mass[0];
|
|
||||||
|
|
||||||
b = -cp /d;
|
|
||||||
|
|
||||||
// Set up the various shift variables
|
|
||||||
int iz=0;
|
|
||||||
z[0][1-iz] = 1.0;
|
|
||||||
z[0][iz] = 1.0;
|
|
||||||
bs[0] = b;
|
|
||||||
for(int s=1;s<nshift;s++){
|
|
||||||
z[s][1-iz] = 1.0;
|
|
||||||
z[s][iz] = 1.0/( 1.0 - b*(mass[s]-mass[0]));
|
|
||||||
bs[s] = b*z[s][iz];
|
|
||||||
}
|
|
||||||
|
|
||||||
// r += b[0] A.p[0]
|
|
||||||
// c= norm(r)
|
|
||||||
c=axpy_norm(r_d,b,mmp_d,r_d);
|
|
||||||
|
|
||||||
for(int s=0;s<nshift;s++) {
|
|
||||||
axpby(psi_d[s],0.,-bs[s]*alpha[s],src_d,src_d);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////
|
|
||||||
// Timers
|
|
||||||
///////////////////////////////////////
|
|
||||||
GridStopWatch AXPYTimer, ShiftTimer, QRTimer, MatrixTimer, SolverTimer, PrecChangeTimer, CleanupTimer;
|
|
||||||
|
|
||||||
SolverTimer.Start();
|
|
||||||
|
|
||||||
// Iteration loop
|
|
||||||
int k;
|
|
||||||
|
|
||||||
for (k=1;k<=MaxIterationsMshift;k++){
|
|
||||||
|
|
||||||
a = c /cp;
|
|
||||||
AXPYTimer.Start();
|
|
||||||
axpy(p_d,a,p_d,r_d);
|
|
||||||
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
if ( ! converged[s] ) {
|
|
||||||
if (s==0){
|
|
||||||
axpy(ps_d[s],a,ps_d[s],r_d);
|
|
||||||
} else{
|
|
||||||
RealD as =a *z[s][iz]*bs[s] /(z[s][1-iz]*b);
|
|
||||||
axpby(ps_d[s],z[s][iz],as,r_d,ps_d[s]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AXPYTimer.Stop();
|
|
||||||
|
|
||||||
PrecChangeTimer.Start();
|
|
||||||
precisionChange(p_f, p_d, pc_wk_d_to_s); //get back single prec search direction for linop
|
|
||||||
PrecChangeTimer.Stop();
|
|
||||||
|
|
||||||
cp=c;
|
|
||||||
MatrixTimer.Start();
|
|
||||||
Linop_f.HermOp(p_f,mmp_f);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
PrecChangeTimer.Start();
|
|
||||||
precisionChange(mmp_d, mmp_f, pc_wk_s_to_d); // From Float to Double
|
|
||||||
PrecChangeTimer.Stop();
|
|
||||||
|
|
||||||
AXPYTimer.Start();
|
|
||||||
d=real(innerProduct(p_d,mmp_d));
|
|
||||||
axpy(mmp_d,mass[0],p_d,mmp_d);
|
|
||||||
AXPYTimer.Stop();
|
|
||||||
RealD rn = norm2(p_d);
|
|
||||||
d += rn*mass[0];
|
|
||||||
|
|
||||||
bp=b;
|
|
||||||
b=-cp/d;
|
|
||||||
|
|
||||||
// Toggle the recurrence history
|
|
||||||
bs[0] = b;
|
|
||||||
iz = 1-iz;
|
|
||||||
ShiftTimer.Start();
|
|
||||||
for(int s=1;s<nshift;s++){
|
|
||||||
if((!converged[s])){
|
|
||||||
RealD z0 = z[s][1-iz];
|
|
||||||
RealD z1 = z[s][iz];
|
|
||||||
z[s][iz] = z0*z1*bp
|
|
||||||
/ (b*a*(z1-z0) + z1*bp*(1- (mass[s]-mass[0])*b));
|
|
||||||
bs[s] = b*z[s][iz]/z0; // NB sign rel to Mike
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ShiftTimer.Stop();
|
|
||||||
|
|
||||||
//Update double precision solutions
|
|
||||||
AXPYTimer.Start();
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
int ss = s;
|
|
||||||
if( (!converged[s]) ) {
|
|
||||||
axpy(psi_d[ss],-bs[s]*alpha[s],ps_d[s],psi_d[ss]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Perform reliable update if necessary; otherwise update residual from single-prec mmp
|
|
||||||
c = axpy_norm(r_d,b,mmp_d,r_d);
|
|
||||||
|
|
||||||
AXPYTimer.Stop();
|
|
||||||
|
|
||||||
if(k % ReliableUpdateFreq == 0){
|
|
||||||
RealD c_old = c;
|
|
||||||
//Replace r with true residual
|
|
||||||
MatrixTimer.Start();
|
|
||||||
Linop_d.HermOp(psi_d[0],mmp_d);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
AXPYTimer.Start();
|
|
||||||
axpy(mmp_d,mass[0],psi_d[0],mmp_d);
|
|
||||||
|
|
||||||
c = axpy_norm(r_d, -1.0, mmp_d, src_d);
|
|
||||||
AXPYTimer.Stop();
|
|
||||||
|
|
||||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrec k="<<k<< ", replaced |r|^2 = "<<c_old <<" with |r|^2 = "<<c<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convergence checks
|
|
||||||
int all_converged = 1;
|
|
||||||
for(int s=0;s<nshift;s++){
|
|
||||||
|
|
||||||
if ( (!converged[s]) ){
|
|
||||||
IterationsToCompleteShift[s] = k;
|
|
||||||
|
|
||||||
RealD css = c * z[s][iz]* z[s][iz];
|
|
||||||
|
|
||||||
if(css<rsqf[s]){
|
|
||||||
if ( ! converged[s] )
|
|
||||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrec k="<<k<<" Shift "<<s<<" has converged"<<std::endl;
|
|
||||||
converged[s]=1;
|
|
||||||
} else {
|
|
||||||
all_converged=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( all_converged || k == MaxIterationsMshift-1){
|
|
||||||
|
|
||||||
SolverTimer.Stop();
|
|
||||||
|
|
||||||
if ( all_converged ){
|
|
||||||
std::cout<<GridLogMessage<< "ConjugateGradientMultiShiftMixedPrec: All shifts have converged iteration "<<k<<std::endl;
|
|
||||||
std::cout<<GridLogMessage<< "ConjugateGradientMultiShiftMixedPrec: Checking solutions"<<std::endl;
|
|
||||||
} else {
|
|
||||||
std::cout<<GridLogMessage<< "ConjugateGradientMultiShiftMixedPrec: Not all shifts have converged iteration "<<k<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check answers
|
|
||||||
for(int s=0; s < nshift; s++) {
|
|
||||||
Linop_d.HermOpAndNorm(psi_d[s],mmp_d,d,qq);
|
|
||||||
axpy(tmp_d,mass[s],psi_d[s],mmp_d);
|
|
||||||
axpy(r_d,-alpha[s],src_d,tmp_d);
|
|
||||||
RealD rn = norm2(r_d);
|
|
||||||
RealD cn = norm2(src_d);
|
|
||||||
TrueResidualShift[s] = std::sqrt(rn/cn);
|
|
||||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrec: shift["<<s<<"] true residual "<< TrueResidualShift[s] << " target " << mresidual[s] << std::endl;
|
|
||||||
|
|
||||||
//If we have not reached the desired tolerance, do a (mixed precision) CG cleanup
|
|
||||||
if(rn >= rsq[s]){
|
|
||||||
CleanupTimer.Start();
|
|
||||||
std::cout<<GridLogMessage<<"ConjugateGradientMultiShiftMixedPrec: performing cleanup step for shift " << s << std::endl;
|
|
||||||
|
|
||||||
//Setup linear operators for final cleanup
|
|
||||||
ConjugateGradientMultiShiftMixedPrecSupport::ShiftedLinop<FieldD> Linop_shift_d(Linop_d, mass[s]);
|
|
||||||
ConjugateGradientMultiShiftMixedPrecSupport::ShiftedLinop<FieldF> Linop_shift_f(Linop_f, mass[s]);
|
|
||||||
|
|
||||||
MixedPrecisionConjugateGradient<FieldD,FieldF> cg(mresidual[s], MaxIterations, MaxIterations, SinglePrecGrid, Linop_shift_f, Linop_shift_d);
|
|
||||||
cg(src_d, psi_d[s]);
|
|
||||||
|
|
||||||
TrueResidualShift[s] = cg.TrueResidual;
|
|
||||||
CleanupTimer.Stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "ConjugateGradientMultiShiftMixedPrec: Time Breakdown for body"<<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tSolver " << SolverTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\t\tAXPY " << AXPYTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\t\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\t\tShift " << ShiftTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\t\tPrecision Change " << PrecChangeTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tFinal Cleanup " << CleanupTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tSolver+Cleanup " << SolverTimer.Elapsed() + CleanupTimer.Elapsed() << std::endl;
|
|
||||||
|
|
||||||
IterationsToComplete = k;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
std::cout<<GridLogMessage<<"CG multi shift did not converge"<<std::endl;
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/*************************************************************************************
|
/*************************************************************************************
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
@@ -23,255 +23,234 @@ Author: Christopher Kelly <ckelly@phys.columbia.edu>
|
|||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#ifndef GRID_CONJUGATE_GRADIENT_RELIABLE_UPDATE_H
|
#ifndef GRID_CONJUGATE_GRADIENT_RELIABLE_UPDATE_H
|
||||||
#define GRID_CONJUGATE_GRADIENT_RELIABLE_UPDATE_H
|
#define GRID_CONJUGATE_GRADIENT_RELIABLE_UPDATE_H
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
template<class FieldD,class FieldF,
|
template<class FieldD,class FieldF, typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0,typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
|
||||||
typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0,
|
class ConjugateGradientReliableUpdate : public LinearFunction<FieldD> {
|
||||||
typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
|
public:
|
||||||
class ConjugateGradientReliableUpdate : public LinearFunction<FieldD> {
|
bool ErrorOnNoConverge; // throw an assert when the CG fails to converge.
|
||||||
public:
|
// Defaults true.
|
||||||
bool ErrorOnNoConverge; // throw an GRID_ASSERT when the CG fails to converge.
|
RealD Tolerance;
|
||||||
// Defaults true.
|
Integer MaxIterations;
|
||||||
RealD Tolerance;
|
Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion
|
||||||
Integer MaxIterations;
|
Integer ReliableUpdatesPerformed;
|
||||||
Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion
|
|
||||||
Integer ReliableUpdatesPerformed;
|
|
||||||
|
|
||||||
bool DoFinalCleanup; //Final DP cleanup, defaults to true
|
bool DoFinalCleanup; //Final DP cleanup, defaults to true
|
||||||
Integer IterationsToCleanup; //Final DP cleanup step iterations
|
Integer IterationsToCleanup; //Final DP cleanup step iterations
|
||||||
|
|
||||||
LinearOperatorBase<FieldF> &Linop_f;
|
LinearOperatorBase<FieldF> &Linop_f;
|
||||||
LinearOperatorBase<FieldD> &Linop_d;
|
LinearOperatorBase<FieldD> &Linop_d;
|
||||||
GridBase* SinglePrecGrid;
|
GridBase* SinglePrecGrid;
|
||||||
RealD Delta; //reliable update parameter. A reliable update is performed when the residual drops by a factor of Delta relative to its value at the last update
|
RealD Delta; //reliable update parameter
|
||||||
|
|
||||||
//Optional ability to switch to a different linear operator once the tolerance reaches a certain point. Useful for single/half -> single/single
|
//Optional ability to switch to a different linear operator once the tolerance reaches a certain point. Useful for single/half -> single/single
|
||||||
LinearOperatorBase<FieldF> *Linop_fallback;
|
LinearOperatorBase<FieldF> *Linop_fallback;
|
||||||
RealD fallback_transition_tol;
|
RealD fallback_transition_tol;
|
||||||
|
|
||||||
|
|
||||||
ConjugateGradientReliableUpdate(RealD tol, Integer maxit, RealD _delta, GridBase* _sp_grid, LinearOperatorBase<FieldF> &_Linop_f, LinearOperatorBase<FieldD> &_Linop_d, bool err_on_no_conv = true)
|
ConjugateGradientReliableUpdate(RealD tol, Integer maxit, RealD _delta, GridBase* _sp_grid, LinearOperatorBase<FieldF> &_Linop_f, LinearOperatorBase<FieldD> &_Linop_d, bool err_on_no_conv = true)
|
||||||
: Tolerance(tol),
|
: Tolerance(tol),
|
||||||
MaxIterations(maxit),
|
MaxIterations(maxit),
|
||||||
Delta(_delta),
|
Delta(_delta),
|
||||||
Linop_f(_Linop_f),
|
Linop_f(_Linop_f),
|
||||||
Linop_d(_Linop_d),
|
Linop_d(_Linop_d),
|
||||||
SinglePrecGrid(_sp_grid),
|
SinglePrecGrid(_sp_grid),
|
||||||
ErrorOnNoConverge(err_on_no_conv),
|
ErrorOnNoConverge(err_on_no_conv),
|
||||||
DoFinalCleanup(true),
|
DoFinalCleanup(true),
|
||||||
Linop_fallback(NULL)
|
Linop_fallback(NULL)
|
||||||
{
|
{};
|
||||||
GRID_ASSERT(Delta > 0. && Delta < 1. && "Expect 0 < Delta < 1");
|
|
||||||
};
|
|
||||||
|
|
||||||
void setFallbackLinop(LinearOperatorBase<FieldF> &_Linop_fallback, const RealD _fallback_transition_tol){
|
void setFallbackLinop(LinearOperatorBase<FieldF> &_Linop_fallback, const RealD _fallback_transition_tol){
|
||||||
Linop_fallback = &_Linop_fallback;
|
Linop_fallback = &_Linop_fallback;
|
||||||
fallback_transition_tol = _fallback_transition_tol;
|
fallback_transition_tol = _fallback_transition_tol;
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(const FieldD &src, FieldD &psi) {
|
|
||||||
GRID_TRACE("ConjugateGradientReliableUpdate");
|
|
||||||
LinearOperatorBase<FieldF> *Linop_f_use = &Linop_f;
|
|
||||||
bool using_fallback = false;
|
|
||||||
|
|
||||||
psi.Checkerboard() = src.Checkerboard();
|
|
||||||
conformable(psi, src);
|
|
||||||
|
|
||||||
RealD cp, c, a, d, b, ssq, qq, b_pred;
|
|
||||||
|
|
||||||
FieldD p(src);
|
|
||||||
FieldD mmp(src);
|
|
||||||
FieldD r(src);
|
|
||||||
|
|
||||||
// Initial residual computation & set up
|
|
||||||
RealD guess = norm2(psi);
|
|
||||||
GRID_ASSERT(std::isnan(guess) == 0);
|
|
||||||
|
|
||||||
Linop_d.HermOpAndNorm(psi, mmp, d, b);
|
|
||||||
|
|
||||||
r = src - mmp;
|
|
||||||
p = r;
|
|
||||||
|
|
||||||
a = norm2(p);
|
|
||||||
cp = a;
|
|
||||||
ssq = norm2(src);
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << std::setprecision(4) << "ConjugateGradientReliableUpdate: guess " << guess << std::endl;
|
|
||||||
std::cout << GridLogIterative << std::setprecision(4) << "ConjugateGradientReliableUpdate: src " << ssq << std::endl;
|
|
||||||
std::cout << GridLogIterative << std::setprecision(4) << "ConjugateGradientReliableUpdate: mp " << d << std::endl;
|
|
||||||
std::cout << GridLogIterative << std::setprecision(4) << "ConjugateGradientReliableUpdate: mmp " << b << std::endl;
|
|
||||||
std::cout << GridLogIterative << std::setprecision(4) << "ConjugateGradientReliableUpdate: cp,r " << cp << std::endl;
|
|
||||||
std::cout << GridLogIterative << std::setprecision(4) << "ConjugateGradientReliableUpdate: p " << a << std::endl;
|
|
||||||
|
|
||||||
RealD rsq = Tolerance * Tolerance * ssq;
|
|
||||||
|
|
||||||
// Check if guess is really REALLY good :)
|
|
||||||
if (cp <= rsq) {
|
|
||||||
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate guess was REALLY good\n";
|
|
||||||
std::cout << GridLogMessage << "\tComputed residual " << std::sqrt(cp / ssq)<<std::endl;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Single prec initialization
|
void operator()(const FieldD &src, FieldD &psi) {
|
||||||
precisionChangeWorkspace pc_wk_sp_to_dp(src.Grid(), SinglePrecGrid);
|
LinearOperatorBase<FieldF> *Linop_f_use = &Linop_f;
|
||||||
precisionChangeWorkspace pc_wk_dp_to_sp(SinglePrecGrid, src.Grid());
|
bool using_fallback = false;
|
||||||
|
|
||||||
FieldF r_f(SinglePrecGrid);
|
psi.checkerboard = src.checkerboard;
|
||||||
r_f.Checkerboard() = r.Checkerboard();
|
conformable(psi, src);
|
||||||
precisionChange(r_f, r, pc_wk_dp_to_sp);
|
|
||||||
|
|
||||||
FieldF psi_f(r_f);
|
RealD cp, c, a, d, b, ssq, qq, b_pred;
|
||||||
psi_f = Zero();
|
|
||||||
|
|
||||||
FieldF p_f(r_f);
|
FieldD p(src);
|
||||||
FieldF mmp_f(r_f);
|
FieldD mmp(src);
|
||||||
|
FieldD r(src);
|
||||||
|
|
||||||
RealD MaxResidSinceLastRelUp = cp; //initial residual
|
// Initial residual computation & set up
|
||||||
|
RealD guess = norm2(psi);
|
||||||
|
assert(std::isnan(guess) == 0);
|
||||||
|
|
||||||
std::cout << GridLogIterative << std::setprecision(4)
|
Linop_d.HermOpAndNorm(psi, mmp, d, b);
|
||||||
<< "ConjugateGradient: k=0 residual " << cp << " target " << rsq << std::endl;
|
|
||||||
|
|
||||||
GridStopWatch LinalgTimer;
|
r = src - mmp;
|
||||||
GridStopWatch MatrixTimer;
|
p = r;
|
||||||
GridStopWatch SolverTimer;
|
|
||||||
GridStopWatch PrecChangeTimer;
|
|
||||||
|
|
||||||
SolverTimer.Start();
|
a = norm2(p);
|
||||||
int k = 0;
|
cp = a;
|
||||||
int l = 0;
|
ssq = norm2(src);
|
||||||
|
|
||||||
for (k = 1; k <= MaxIterations; k++) {
|
std::cout << GridLogIterative << std::setprecision(4) << "ConjugateGradientReliableUpdate: guess " << guess << std::endl;
|
||||||
c = cp;
|
std::cout << GridLogIterative << std::setprecision(4) << "ConjugateGradientReliableUpdate: src " << ssq << std::endl;
|
||||||
|
std::cout << GridLogIterative << std::setprecision(4) << "ConjugateGradientReliableUpdate: mp " << d << std::endl;
|
||||||
|
std::cout << GridLogIterative << std::setprecision(4) << "ConjugateGradientReliableUpdate: mmp " << b << std::endl;
|
||||||
|
std::cout << GridLogIterative << std::setprecision(4) << "ConjugateGradientReliableUpdate: cp,r " << cp << std::endl;
|
||||||
|
std::cout << GridLogIterative << std::setprecision(4) << "ConjugateGradientReliableUpdate: p " << a << std::endl;
|
||||||
|
|
||||||
MatrixTimer.Start();
|
RealD rsq = Tolerance * Tolerance * ssq;
|
||||||
Linop_f_use->HermOpAndNorm(p_f, mmp_f, d, qq);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
// Check if guess is really REALLY good :)
|
||||||
|
|
||||||
a = c / d;
|
|
||||||
b_pred = a * (a * qq - d) / c;
|
|
||||||
|
|
||||||
cp = axpy_norm(r_f, -a, mmp_f, r_f);
|
|
||||||
b = cp / c;
|
|
||||||
|
|
||||||
// Fuse these loops ; should be really easy
|
|
||||||
psi_f = a * p_f + psi_f;
|
|
||||||
//p_f = p_f * b + r_f;
|
|
||||||
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << "ConjugateGradientReliableUpdate: Iteration " << k
|
|
||||||
<< " residual " << cp << " target " << rsq << std::endl;
|
|
||||||
std::cout << GridLogDebug << "a = "<< a << " b_pred = "<< b_pred << " b = "<< b << std::endl;
|
|
||||||
std::cout << GridLogDebug << "qq = "<< qq << " d = "<< d << " c = "<< c << std::endl;
|
|
||||||
|
|
||||||
if(cp > MaxResidSinceLastRelUp){
|
|
||||||
std::cout << GridLogIterative << "ConjugateGradientReliableUpdate: updating MaxResidSinceLastRelUp : " << MaxResidSinceLastRelUp << " -> " << cp << std::endl;
|
|
||||||
MaxResidSinceLastRelUp = cp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stopping condition
|
|
||||||
if (cp <= rsq) {
|
if (cp <= rsq) {
|
||||||
//Although not written in the paper, I assume that I have to add on the final solution
|
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate guess was REALLY good\n";
|
||||||
PrecChangeTimer.Start();
|
std::cout << GridLogMessage << "\tComputed residual " << sqrt(cp / ssq)<<std::endl;
|
||||||
precisionChange(mmp, psi_f, pc_wk_sp_to_dp);
|
|
||||||
PrecChangeTimer.Stop();
|
|
||||||
psi = psi + mmp;
|
|
||||||
|
|
||||||
|
|
||||||
SolverTimer.Stop();
|
|
||||||
Linop_d.HermOpAndNorm(psi, mmp, d, qq);
|
|
||||||
p = mmp - src;
|
|
||||||
|
|
||||||
RealD srcnorm = std::sqrt(norm2(src));
|
|
||||||
RealD resnorm = std::sqrt(norm2(p));
|
|
||||||
RealD true_residual = resnorm / srcnorm;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate Converged on iteration " << k << " after " << l << " reliable updates" << std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tComputed residual " << std::sqrt(cp / ssq)<<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tTrue residual " << true_residual<<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tTarget " << Tolerance << std::endl;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "Time breakdown "<<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tElapsed " << SolverTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tPrecChange " << PrecChangeTimer.Elapsed() <<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tPrecChange avg time " << PrecChangeTimer.Elapsed()/(2*l+1) <<std::endl;
|
|
||||||
|
|
||||||
|
|
||||||
IterationsToComplete = k;
|
|
||||||
ReliableUpdatesPerformed = l;
|
|
||||||
|
|
||||||
if(DoFinalCleanup){
|
|
||||||
//Do a final CG to cleanup
|
|
||||||
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate performing final cleanup.\n";
|
|
||||||
ConjugateGradient<FieldD> CG(Tolerance,MaxIterations);
|
|
||||||
CG.ErrorOnNoConverge = ErrorOnNoConverge;
|
|
||||||
CG(Linop_d,src,psi);
|
|
||||||
IterationsToCleanup = CG.IterationsToComplete;
|
|
||||||
}
|
|
||||||
else if (ErrorOnNoConverge) GRID_ASSERT(true_residual / Tolerance < 10000.0);
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate complete.\n";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if(cp < Delta * MaxResidSinceLastRelUp) { //reliable update
|
|
||||||
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate "
|
//Single prec initialization
|
||||||
<< cp << "(residual) < " << Delta << "(Delta) * " << MaxResidSinceLastRelUp << "(MaxResidSinceLastRelUp) on iteration " << k << " : performing reliable update\n";
|
FieldF r_f(SinglePrecGrid);
|
||||||
PrecChangeTimer.Start();
|
r_f.checkerboard = r.checkerboard;
|
||||||
precisionChange(mmp, psi_f, pc_wk_sp_to_dp);
|
precisionChange(r_f, r);
|
||||||
PrecChangeTimer.Stop();
|
|
||||||
psi = psi + mmp;
|
FieldF psi_f(r_f);
|
||||||
|
psi_f = zero;
|
||||||
|
|
||||||
|
FieldF p_f(r_f);
|
||||||
|
FieldF mmp_f(r_f);
|
||||||
|
|
||||||
|
RealD MaxResidSinceLastRelUp = cp; //initial residual
|
||||||
|
|
||||||
|
std::cout << GridLogIterative << std::setprecision(4)
|
||||||
|
<< "ConjugateGradient: k=0 residual " << cp << " target " << rsq << std::endl;
|
||||||
|
|
||||||
|
GridStopWatch LinalgTimer;
|
||||||
|
GridStopWatch MatrixTimer;
|
||||||
|
GridStopWatch SolverTimer;
|
||||||
|
|
||||||
|
SolverTimer.Start();
|
||||||
|
int k = 0;
|
||||||
|
int l = 0;
|
||||||
|
|
||||||
|
for (k = 1; k <= MaxIterations; k++) {
|
||||||
|
c = cp;
|
||||||
|
|
||||||
MatrixTimer.Start();
|
MatrixTimer.Start();
|
||||||
Linop_d.HermOpAndNorm(psi, mmp, d, qq);
|
Linop_f_use->HermOpAndNorm(p_f, mmp_f, d, qq);
|
||||||
MatrixTimer.Stop();
|
MatrixTimer.Stop();
|
||||||
|
|
||||||
r = src - mmp;
|
LinalgTimer.Start();
|
||||||
|
|
||||||
psi_f = Zero();
|
a = c / d;
|
||||||
PrecChangeTimer.Start();
|
b_pred = a * (a * qq - d) / c;
|
||||||
precisionChange(r_f, r, pc_wk_dp_to_sp);
|
|
||||||
PrecChangeTimer.Stop();
|
|
||||||
cp = norm2(r);
|
|
||||||
MaxResidSinceLastRelUp = cp;
|
|
||||||
|
|
||||||
b = cp/c;
|
cp = axpy_norm(r_f, -a, mmp_f, r_f);
|
||||||
|
b = cp / c;
|
||||||
|
|
||||||
|
// Fuse these loops ; should be really easy
|
||||||
|
psi_f = a * p_f + psi_f;
|
||||||
|
//p_f = p_f * b + r_f;
|
||||||
|
|
||||||
|
LinalgTimer.Stop();
|
||||||
|
|
||||||
|
std::cout << GridLogIterative << "ConjugateGradientReliableUpdate: Iteration " << k
|
||||||
|
<< " residual " << cp << " target " << rsq << std::endl;
|
||||||
|
std::cout << GridLogDebug << "a = "<< a << " b_pred = "<< b_pred << " b = "<< b << std::endl;
|
||||||
|
std::cout << GridLogDebug << "qq = "<< qq << " d = "<< d << " c = "<< c << std::endl;
|
||||||
|
|
||||||
|
if(cp > MaxResidSinceLastRelUp){
|
||||||
|
std::cout << GridLogIterative << "ConjugateGradientReliableUpdate: updating MaxResidSinceLastRelUp : " << MaxResidSinceLastRelUp << " -> " << cp << std::endl;
|
||||||
|
MaxResidSinceLastRelUp = cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stopping condition
|
||||||
|
if (cp <= rsq) {
|
||||||
|
//Although not written in the paper, I assume that I have to add on the final solution
|
||||||
|
precisionChange(mmp, psi_f);
|
||||||
|
psi = psi + mmp;
|
||||||
|
|
||||||
|
|
||||||
|
SolverTimer.Stop();
|
||||||
|
Linop_d.HermOpAndNorm(psi, mmp, d, qq);
|
||||||
|
p = mmp - src;
|
||||||
|
|
||||||
|
RealD srcnorm = sqrt(norm2(src));
|
||||||
|
RealD resnorm = sqrt(norm2(p));
|
||||||
|
RealD true_residual = resnorm / srcnorm;
|
||||||
|
|
||||||
|
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate Converged on iteration " << k << " after " << l << " reliable updates" << std::endl;
|
||||||
|
std::cout << GridLogMessage << "\tComputed residual " << sqrt(cp / ssq)<<std::endl;
|
||||||
|
std::cout << GridLogMessage << "\tTrue residual " << true_residual<<std::endl;
|
||||||
|
std::cout << GridLogMessage << "\tTarget " << Tolerance << std::endl;
|
||||||
|
|
||||||
|
std::cout << GridLogMessage << "Time breakdown "<<std::endl;
|
||||||
|
std::cout << GridLogMessage << "\tElapsed " << SolverTimer.Elapsed() <<std::endl;
|
||||||
|
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||||
|
std::cout << GridLogMessage << "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
||||||
|
|
||||||
|
IterationsToComplete = k;
|
||||||
|
ReliableUpdatesPerformed = l;
|
||||||
|
|
||||||
|
if(DoFinalCleanup){
|
||||||
|
//Do a final CG to cleanup
|
||||||
|
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate performing final cleanup.\n";
|
||||||
|
ConjugateGradient<FieldD> CG(Tolerance,MaxIterations);
|
||||||
|
CG.ErrorOnNoConverge = ErrorOnNoConverge;
|
||||||
|
CG(Linop_d,src,psi);
|
||||||
|
IterationsToCleanup = CG.IterationsToComplete;
|
||||||
|
}
|
||||||
|
else if (ErrorOnNoConverge) assert(true_residual / Tolerance < 10000.0);
|
||||||
|
|
||||||
|
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate complete.\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(cp < Delta * MaxResidSinceLastRelUp) { //reliable update
|
||||||
|
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate "
|
||||||
|
<< cp << "(residual) < " << Delta << "(Delta) * " << MaxResidSinceLastRelUp << "(MaxResidSinceLastRelUp) on iteration " << k << " : performing reliable update\n";
|
||||||
|
precisionChange(mmp, psi_f);
|
||||||
|
psi = psi + mmp;
|
||||||
|
|
||||||
|
Linop_d.HermOpAndNorm(psi, mmp, d, qq);
|
||||||
|
r = src - mmp;
|
||||||
|
|
||||||
|
psi_f = zero;
|
||||||
|
precisionChange(r_f, r);
|
||||||
|
cp = norm2(r);
|
||||||
|
MaxResidSinceLastRelUp = cp;
|
||||||
|
|
||||||
|
b = cp/c;
|
||||||
|
|
||||||
|
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate new residual " << cp << std::endl;
|
||||||
|
|
||||||
|
l = l+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_f = p_f * b + r_f; //update search vector after reliable update appears to help convergence
|
||||||
|
|
||||||
|
if(!using_fallback && Linop_fallback != NULL && cp < fallback_transition_tol){
|
||||||
|
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate switching to fallback linear operator on iteration " << k << " at residual " << cp << std::endl;
|
||||||
|
Linop_f_use = Linop_fallback;
|
||||||
|
using_fallback = true;
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate new residual " << cp << std::endl;
|
|
||||||
|
|
||||||
l = l+1;
|
|
||||||
}
|
}
|
||||||
|
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate did NOT converge"
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
p_f = p_f * b + r_f; //update search vector after reliable update appears to help convergence
|
if (ErrorOnNoConverge) assert(0);
|
||||||
|
IterationsToComplete = k;
|
||||||
if(!using_fallback && Linop_fallback != NULL && cp < fallback_transition_tol){
|
ReliableUpdatesPerformed = l;
|
||||||
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate switching to fallback linear operator on iteration " << k << " at residual " << cp << std::endl;
|
|
||||||
Linop_f_use = Linop_fallback;
|
|
||||||
using_fallback = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate did NOT converge"
|
};
|
||||||
<< std::endl;
|
|
||||||
|
|
||||||
if (ErrorOnNoConverge) GRID_ASSERT(0);
|
|
||||||
IterationsToComplete = k;
|
|
||||||
ReliableUpdatesPerformed = l;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/*************************************************************************************
|
/*************************************************************************************
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
@@ -24,90 +24,88 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#ifndef GRID_CONJUGATE_RESIDUAL_H
|
#ifndef GRID_CONJUGATE_RESIDUAL_H
|
||||||
#define GRID_CONJUGATE_RESIDUAL_H
|
#define GRID_CONJUGATE_RESIDUAL_H
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////
|
||||||
// Base classes for iterative processes based on operators
|
// Base classes for iterative processes based on operators
|
||||||
// single input vec, single output vec.
|
// single input vec, single output vec.
|
||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template<class Field>
|
template<class Field>
|
||||||
class ConjugateResidual : public OperatorFunction<Field> {
|
class ConjugateResidual : public OperatorFunction<Field> {
|
||||||
public:
|
public:
|
||||||
using OperatorFunction<Field>::operator();
|
RealD Tolerance;
|
||||||
|
Integer MaxIterations;
|
||||||
|
int verbose;
|
||||||
|
|
||||||
RealD Tolerance;
|
ConjugateResidual(RealD tol,Integer maxit) : Tolerance(tol), MaxIterations(maxit) {
|
||||||
Integer MaxIterations;
|
verbose=0;
|
||||||
int verbose;
|
};
|
||||||
|
|
||||||
ConjugateResidual(RealD tol,Integer maxit) : Tolerance(tol), MaxIterations(maxit) {
|
void operator() (LinearOperatorBase<Field> &Linop,const Field &src, Field &psi){
|
||||||
verbose=0;
|
|
||||||
};
|
|
||||||
|
|
||||||
void operator() (LinearOperatorBase<Field> &Linop,const Field &src, Field &psi){
|
RealD a, b, c, d;
|
||||||
|
RealD cp, ssq,rsq;
|
||||||
|
|
||||||
RealD a, b; // c, d;
|
RealD rAr, rAAr, rArp;
|
||||||
RealD cp, ssq,rsq;
|
RealD pAp, pAAp;
|
||||||
|
|
||||||
RealD rAr, rAAr, rArp;
|
GridBase *grid = src._grid;
|
||||||
RealD pAp, pAAp;
|
psi=zero;
|
||||||
|
Field r(grid), p(grid), Ap(grid), Ar(grid);
|
||||||
|
|
||||||
GridBase *grid = src.Grid();
|
r=src;
|
||||||
psi=Zero();
|
p=src;
|
||||||
Field r(grid), p(grid), Ap(grid), Ar(grid);
|
|
||||||
|
|
||||||
r=src;
|
|
||||||
p=src;
|
|
||||||
|
|
||||||
Linop.HermOpAndNorm(p,Ap,pAp,pAAp);
|
|
||||||
Linop.HermOpAndNorm(r,Ar,rAr,rAAr);
|
|
||||||
|
|
||||||
cp =norm2(r);
|
|
||||||
ssq=norm2(src);
|
|
||||||
rsq=Tolerance*Tolerance*ssq;
|
|
||||||
|
|
||||||
if (verbose) std::cout<<GridLogMessage<<"ConjugateResidual: iteration " <<0<<" residual "<<cp<< " target"<< rsq<<std::endl;
|
|
||||||
|
|
||||||
for(int k=1;k<MaxIterations;k++){
|
|
||||||
|
|
||||||
a = rAr/pAAp;
|
|
||||||
|
|
||||||
axpy(psi,a,p,psi);
|
|
||||||
|
|
||||||
cp = axpy_norm(r,-a,Ap,r);
|
|
||||||
|
|
||||||
rArp=rAr;
|
|
||||||
|
|
||||||
|
Linop.HermOpAndNorm(p,Ap,pAp,pAAp);
|
||||||
Linop.HermOpAndNorm(r,Ar,rAr,rAAr);
|
Linop.HermOpAndNorm(r,Ar,rAr,rAAr);
|
||||||
|
|
||||||
b =rAr/rArp;
|
cp =norm2(r);
|
||||||
|
ssq=norm2(src);
|
||||||
|
rsq=Tolerance*Tolerance*ssq;
|
||||||
|
|
||||||
axpy(p,b,p,r);
|
if (verbose) std::cout<<GridLogMessage<<"ConjugateResidual: iteration " <<0<<" residual "<<cp<< " target"<< rsq<<std::endl;
|
||||||
pAAp=axpy_norm(Ap,b,Ap,Ar);
|
|
||||||
|
|
||||||
if(verbose) std::cout<<GridLogMessage<<"ConjugateResidual: iteration " <<k<<" residual "<<cp<< " target"<< rsq<<std::endl;
|
for(int k=1;k<MaxIterations;k++){
|
||||||
|
|
||||||
|
a = rAr/pAAp;
|
||||||
|
|
||||||
|
axpy(psi,a,p,psi);
|
||||||
|
|
||||||
|
cp = axpy_norm(r,-a,Ap,r);
|
||||||
|
|
||||||
|
rArp=rAr;
|
||||||
|
|
||||||
|
Linop.HermOpAndNorm(r,Ar,rAr,rAAr);
|
||||||
|
|
||||||
|
b =rAr/rArp;
|
||||||
|
|
||||||
|
axpy(p,b,p,r);
|
||||||
|
pAAp=axpy_norm(Ap,b,Ap,Ar);
|
||||||
|
|
||||||
|
if(verbose) std::cout<<GridLogMessage<<"ConjugateResidual: iteration " <<k<<" residual "<<cp<< " target"<< rsq<<std::endl;
|
||||||
|
|
||||||
|
if(cp<rsq) {
|
||||||
|
Linop.HermOp(psi,Ap);
|
||||||
|
axpy(r,-1.0,src,Ap);
|
||||||
|
RealD true_resid = norm2(r)/ssq;
|
||||||
|
std::cout<<GridLogMessage<<"ConjugateResidual: Converged on iteration " <<k
|
||||||
|
<< " computed residual "<<sqrt(cp/ssq)
|
||||||
|
<< " true residual "<<sqrt(true_resid)
|
||||||
|
<< " target " <<Tolerance <<std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(cp<rsq) {
|
|
||||||
Linop.HermOp(psi,Ap);
|
|
||||||
axpy(r,-1.0,src,Ap);
|
|
||||||
RealD true_resid = norm2(r)/ssq;
|
|
||||||
std::cout<<GridLogMessage<<"ConjugateResidual: Converged on iteration " <<k
|
|
||||||
<< " computed residual "<<std::sqrt(cp/ssq)
|
|
||||||
<< " true residual "<<std::sqrt(true_resid)
|
|
||||||
<< " target " <<Tolerance <<std::endl;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cout<<GridLogMessage<<"ConjugateResidual did NOT converge"<<std::endl;
|
||||||
|
assert(0);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
std::cout<<GridLogMessage<<"ConjugateResidual did NOT converge"<<std::endl;
|
}
|
||||||
GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
#endif
|
||||||
@@ -33,19 +33,12 @@ namespace Grid {
|
|||||||
template<class Field>
|
template<class Field>
|
||||||
class ZeroGuesser: public LinearFunction<Field> {
|
class ZeroGuesser: public LinearFunction<Field> {
|
||||||
public:
|
public:
|
||||||
using LinearFunction<Field>::operator();
|
virtual void operator()(const Field &src, Field &guess) { guess = zero; };
|
||||||
virtual void operator()(const Field &src, Field &guess) { guess = Zero(); };
|
|
||||||
};
|
|
||||||
template<class Field>
|
|
||||||
class DoNothingGuesser: public LinearFunction<Field> {
|
|
||||||
public:
|
|
||||||
using LinearFunction<Field>::operator();
|
|
||||||
virtual void operator()(const Field &src, Field &guess) { };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class Field>
|
template<class Field>
|
||||||
class SourceGuesser: public LinearFunction<Field> {
|
class SourceGuesser: public LinearFunction<Field> {
|
||||||
public:
|
public:
|
||||||
using LinearFunction<Field>::operator();
|
|
||||||
virtual void operator()(const Field &src, Field &guess) { guess = src; };
|
virtual void operator()(const Field &src, Field &guess) { guess = src; };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -57,29 +50,20 @@ class DeflatedGuesser: public LinearFunction<Field> {
|
|||||||
private:
|
private:
|
||||||
const std::vector<Field> &evec;
|
const std::vector<Field> &evec;
|
||||||
const std::vector<RealD> &eval;
|
const std::vector<RealD> &eval;
|
||||||
const unsigned int N;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using LinearFunction<Field>::operator();
|
|
||||||
|
|
||||||
DeflatedGuesser(const std::vector<Field> & _evec,const std::vector<RealD> & _eval)
|
DeflatedGuesser(const std::vector<Field> & _evec,const std::vector<RealD> & _eval) : evec(_evec), eval(_eval) {};
|
||||||
: DeflatedGuesser(_evec, _eval, _evec.size())
|
|
||||||
{}
|
|
||||||
|
|
||||||
DeflatedGuesser(const std::vector<Field> & _evec, const std::vector<RealD> & _eval, const unsigned int _N)
|
|
||||||
: evec(_evec), eval(_eval), N(_N)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(evec.size()==eval.size());
|
|
||||||
GRID_ASSERT(N <= evec.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void operator()(const Field &src,Field &guess) {
|
virtual void operator()(const Field &src,Field &guess) {
|
||||||
guess = Zero();
|
guess = zero;
|
||||||
|
assert(evec.size()==eval.size());
|
||||||
|
auto N = evec.size();
|
||||||
for (int i=0;i<N;i++) {
|
for (int i=0;i<N;i++) {
|
||||||
const Field& tmp = evec[i];
|
const Field& tmp = evec[i];
|
||||||
axpy(guess,TensorRemove(innerProduct(tmp,src)) / eval[i],tmp,guess);
|
axpy(guess,TensorRemove(innerProduct(tmp,src)) / eval[i],tmp,guess);
|
||||||
}
|
}
|
||||||
guess.Checkerboard() = src.Checkerboard();
|
guess.checkerboard = src.checkerboard;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -91,7 +75,6 @@ private:
|
|||||||
const std::vector<RealD> &eval_coarse;
|
const std::vector<RealD> &eval_coarse;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
using LinearFunction<FineField>::operator();
|
|
||||||
LocalCoherenceDeflatedGuesser(const std::vector<FineField> &_subspace,
|
LocalCoherenceDeflatedGuesser(const std::vector<FineField> &_subspace,
|
||||||
const std::vector<CoarseField> &_evec_coarse,
|
const std::vector<CoarseField> &_evec_coarse,
|
||||||
const std::vector<RealD> &_eval_coarse)
|
const std::vector<RealD> &_eval_coarse)
|
||||||
@@ -103,52 +86,17 @@ public:
|
|||||||
|
|
||||||
void operator()(const FineField &src,FineField &guess) {
|
void operator()(const FineField &src,FineField &guess) {
|
||||||
int N = (int)evec_coarse.size();
|
int N = (int)evec_coarse.size();
|
||||||
CoarseField src_coarse(evec_coarse[0].Grid());
|
CoarseField src_coarse(evec_coarse[0]._grid);
|
||||||
CoarseField guess_coarse(evec_coarse[0].Grid()); guess_coarse = Zero();
|
CoarseField guess_coarse(evec_coarse[0]._grid); guess_coarse = zero;
|
||||||
blockProject(src_coarse,src,subspace);
|
blockProject(src_coarse,src,subspace);
|
||||||
for (int i=0;i<N;i++) {
|
for (int i=0;i<N;i++) {
|
||||||
const CoarseField & tmp = evec_coarse[i];
|
const CoarseField & tmp = evec_coarse[i];
|
||||||
axpy(guess_coarse,TensorRemove(innerProduct(tmp,src_coarse)) / eval_coarse[i],tmp,guess_coarse);
|
axpy(guess_coarse,TensorRemove(innerProduct(tmp,src_coarse)) / eval_coarse[i],tmp,guess_coarse);
|
||||||
}
|
}
|
||||||
blockPromote(guess_coarse,guess,subspace);
|
blockPromote(guess_coarse,guess,subspace);
|
||||||
guess.Checkerboard() = src.Checkerboard();
|
guess.checkerboard = src.checkerboard;
|
||||||
};
|
|
||||||
|
|
||||||
void operator()(const std::vector<FineField> &src,std::vector<FineField> &guess) {
|
|
||||||
int Nevec = (int)evec_coarse.size();
|
|
||||||
int Nsrc = (int)src.size();
|
|
||||||
// make temp variables
|
|
||||||
std::vector<CoarseField> src_coarse(Nsrc,evec_coarse[0].Grid());
|
|
||||||
std::vector<CoarseField> guess_coarse(Nsrc,evec_coarse[0].Grid());
|
|
||||||
//Preporcessing
|
|
||||||
std::cout << GridLogMessage << "Start BlockProject for loop" << std::endl;
|
|
||||||
for (int j=0;j<Nsrc;j++)
|
|
||||||
{
|
|
||||||
guess_coarse[j] = Zero();
|
|
||||||
std::cout << GridLogMessage << "BlockProject iter: " << j << std::endl;
|
|
||||||
blockProject(src_coarse[j],src[j],subspace);
|
|
||||||
}
|
|
||||||
//deflation set up for eigen vector batchsize 1 and source batch size equal number of sources
|
|
||||||
std::cout << GridLogMessage << "Start ProjectAccum for loop" << std::endl;
|
|
||||||
for (int i=0;i<Nevec;i++)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage << "ProjectAccum Nvec: " << i << std::endl;
|
|
||||||
const CoarseField & tmp = evec_coarse[i];
|
|
||||||
for (int j=0;j<Nsrc;j++)
|
|
||||||
{
|
|
||||||
axpy(guess_coarse[j],TensorRemove(innerProduct(tmp,src_coarse[j])) / eval_coarse[i],tmp,guess_coarse[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//postprocessing
|
|
||||||
std::cout << GridLogMessage << "Start BlockPromote for loop" << std::endl;
|
|
||||||
for (int j=0;j<Nsrc;j++) {
|
|
||||||
std::cout << GridLogMessage << "BlockProject iter: " << j << std::endl;
|
|
||||||
blockPromote(guess_coarse[j],guess[j],subspace);
|
|
||||||
guess[j].Checkerboard() = src[j].Checkerboard();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,258 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/FlexibleCommunicationAvoidingGeneralisedMinimalResidual.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Daniel Richtmann <daniel.richtmann@ur.de>
|
|
||||||
|
|
||||||
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_FLEXIBLE_COMMUNICATION_AVOIDING_GENERALISED_MINIMAL_RESIDUAL_H
|
|
||||||
#define GRID_FLEXIBLE_COMMUNICATION_AVOIDING_GENERALISED_MINIMAL_RESIDUAL_H
|
|
||||||
|
|
||||||
namespace Grid {
|
|
||||||
|
|
||||||
template<class Field>
|
|
||||||
class FlexibleCommunicationAvoidingGeneralisedMinimalResidual : public OperatorFunction<Field> {
|
|
||||||
public:
|
|
||||||
using OperatorFunction<Field>::operator();
|
|
||||||
|
|
||||||
bool ErrorOnNoConverge; // Throw an GRID_ASSERT when FCAGMRES fails to converge,
|
|
||||||
// defaults to true
|
|
||||||
|
|
||||||
RealD Tolerance;
|
|
||||||
|
|
||||||
Integer MaxIterations;
|
|
||||||
Integer RestartLength;
|
|
||||||
Integer MaxNumberOfRestarts;
|
|
||||||
Integer IterationCount; // Number of iterations the FCAGMRES took to finish,
|
|
||||||
// filled in upon completion
|
|
||||||
|
|
||||||
GridStopWatch MatrixTimer;
|
|
||||||
GridStopWatch PrecTimer;
|
|
||||||
GridStopWatch LinalgTimer;
|
|
||||||
GridStopWatch QrTimer;
|
|
||||||
GridStopWatch CompSolutionTimer;
|
|
||||||
|
|
||||||
Eigen::MatrixXcd H;
|
|
||||||
|
|
||||||
std::vector<ComplexD> y;
|
|
||||||
std::vector<ComplexD> gamma;
|
|
||||||
std::vector<ComplexD> c;
|
|
||||||
std::vector<ComplexD> s;
|
|
||||||
|
|
||||||
LinearFunction<Field> &Preconditioner;
|
|
||||||
|
|
||||||
FlexibleCommunicationAvoidingGeneralisedMinimalResidual(RealD tol,
|
|
||||||
Integer maxit,
|
|
||||||
LinearFunction<Field> &Prec,
|
|
||||||
Integer restart_length,
|
|
||||||
bool err_on_no_conv = true)
|
|
||||||
: Tolerance(tol)
|
|
||||||
, MaxIterations(maxit)
|
|
||||||
, RestartLength(restart_length)
|
|
||||||
, MaxNumberOfRestarts(MaxIterations/RestartLength + ((MaxIterations%RestartLength == 0) ? 0 : 1))
|
|
||||||
, ErrorOnNoConverge(err_on_no_conv)
|
|
||||||
, H(Eigen::MatrixXcd::Zero(RestartLength, RestartLength + 1)) // sizes taken from DD-αAMG code base
|
|
||||||
, y(RestartLength + 1, 0.)
|
|
||||||
, gamma(RestartLength + 1, 0.)
|
|
||||||
, c(RestartLength + 1, 0.)
|
|
||||||
, s(RestartLength + 1, 0.)
|
|
||||||
, Preconditioner(Prec) {};
|
|
||||||
|
|
||||||
void operator()(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi) {
|
|
||||||
|
|
||||||
std::cout << GridLogWarning << "This algorithm currently doesn't differ from regular FGMRES" << std::endl;
|
|
||||||
|
|
||||||
psi.Checkerboard() = src.Checkerboard();
|
|
||||||
conformable(psi, src);
|
|
||||||
|
|
||||||
RealD guess = norm2(psi);
|
|
||||||
GRID_ASSERT(std::isnan(guess) == 0);
|
|
||||||
|
|
||||||
RealD cp;
|
|
||||||
RealD ssq = norm2(src);
|
|
||||||
RealD rsq = Tolerance * Tolerance * ssq;
|
|
||||||
|
|
||||||
Field r(src.Grid());
|
|
||||||
|
|
||||||
std::cout << std::setprecision(4) << std::scientific;
|
|
||||||
std::cout << GridLogIterative << "FlexibleCommunicationAvoidingGeneralisedMinimalResidual: guess " << guess << std::endl;
|
|
||||||
std::cout << GridLogIterative << "FlexibleCommunicationAvoidingGeneralisedMinimalResidual: src " << ssq << std::endl;
|
|
||||||
|
|
||||||
PrecTimer.Reset();
|
|
||||||
MatrixTimer.Reset();
|
|
||||||
LinalgTimer.Reset();
|
|
||||||
QrTimer.Reset();
|
|
||||||
CompSolutionTimer.Reset();
|
|
||||||
|
|
||||||
GridStopWatch SolverTimer;
|
|
||||||
SolverTimer.Start();
|
|
||||||
|
|
||||||
IterationCount = 0;
|
|
||||||
|
|
||||||
for (int k=0; k<MaxNumberOfRestarts; k++) {
|
|
||||||
|
|
||||||
cp = outerLoopBody(LinOp, src, psi, rsq);
|
|
||||||
|
|
||||||
// Stopping condition
|
|
||||||
if (cp <= rsq) {
|
|
||||||
|
|
||||||
SolverTimer.Stop();
|
|
||||||
|
|
||||||
LinOp.Op(psi,r);
|
|
||||||
axpy(r,-1.0,src,r);
|
|
||||||
|
|
||||||
RealD srcnorm = sqrt(ssq);
|
|
||||||
RealD resnorm = sqrt(norm2(r));
|
|
||||||
RealD true_residual = resnorm / srcnorm;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "FlexibleCommunicationAvoidingGeneralisedMinimalResidual: Converged on iteration " << IterationCount
|
|
||||||
<< " computed residual " << sqrt(cp / ssq)
|
|
||||||
<< " true residual " << true_residual
|
|
||||||
<< " target " << Tolerance << std::endl;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "FCAGMRES Time elapsed: Total " << SolverTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "FCAGMRES Time elapsed: Precon " << PrecTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "FCAGMRES Time elapsed: Matrix " << MatrixTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "FCAGMRES Time elapsed: Linalg " << LinalgTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "FCAGMRES Time elapsed: QR " << QrTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "FCAGMRES Time elapsed: CompSol " << CompSolutionTimer.Elapsed() << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "FlexibleCommunicationAvoidingGeneralisedMinimalResidual did NOT converge" << std::endl;
|
|
||||||
|
|
||||||
if (ErrorOnNoConverge)
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) {
|
|
||||||
|
|
||||||
RealD cp = 0;
|
|
||||||
|
|
||||||
Field w(src.Grid());
|
|
||||||
Field r(src.Grid());
|
|
||||||
|
|
||||||
// these should probably be made class members so that they are only allocated once, not in every restart
|
|
||||||
std::vector<Field> v(RestartLength + 1, src.Grid()); for (auto &elem : v) elem = Zero();
|
|
||||||
std::vector<Field> z(RestartLength + 1, src.Grid()); for (auto &elem : z) elem = Zero();
|
|
||||||
|
|
||||||
MatrixTimer.Start();
|
|
||||||
LinOp.Op(psi, w);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
r = src - w;
|
|
||||||
|
|
||||||
gamma[0] = sqrt(norm2(r));
|
|
||||||
|
|
||||||
v[0] = (1. / gamma[0]) * r;
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
for (int i=0; i<RestartLength; i++) {
|
|
||||||
|
|
||||||
IterationCount++;
|
|
||||||
|
|
||||||
arnoldiStep(LinOp, v, z, w, i);
|
|
||||||
|
|
||||||
qrUpdate(i);
|
|
||||||
|
|
||||||
cp = norm(gamma[i+1]);
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << "FlexibleCommunicationAvoidingGeneralisedMinimalResidual: Iteration " << IterationCount
|
|
||||||
<< " residual " << cp << " target " << rsq << std::endl;
|
|
||||||
|
|
||||||
if ((i == RestartLength - 1) || (IterationCount == MaxIterations) || (cp <= rsq)) {
|
|
||||||
|
|
||||||
computeSolution(z, psi, i);
|
|
||||||
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GRID_ASSERT(0); // Never reached
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void arnoldiStep(LinearOperatorBase<Field> &LinOp, std::vector<Field> &v, std::vector<Field> &z, Field &w, int iter) {
|
|
||||||
|
|
||||||
PrecTimer.Start();
|
|
||||||
Preconditioner(v[iter], z[iter]);
|
|
||||||
PrecTimer.Stop();
|
|
||||||
|
|
||||||
MatrixTimer.Start();
|
|
||||||
LinOp.Op(z[iter], w);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
for (int i = 0; i <= iter; ++i) {
|
|
||||||
H(iter, i) = innerProduct(v[i], w);
|
|
||||||
w = w - ComplexD(H(iter, i)) * v[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
H(iter, iter + 1) = sqrt(norm2(w));
|
|
||||||
v[iter + 1] = ComplexD(1. / H(iter, iter + 1)) * w;
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void qrUpdate(int iter) {
|
|
||||||
|
|
||||||
QrTimer.Start();
|
|
||||||
for (int i = 0; i < iter ; ++i) {
|
|
||||||
auto tmp = -s[i] * ComplexD(H(iter, i)) + c[i] * ComplexD(H(iter, i + 1));
|
|
||||||
H(iter, i) = conjugate(c[i]) * ComplexD(H(iter, i)) + conjugate(s[i]) * ComplexD(H(iter, i + 1));
|
|
||||||
H(iter, i + 1) = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute new Givens Rotation
|
|
||||||
auto nu = sqrt(std::norm(H(iter, iter)) + std::norm(H(iter, iter + 1)));
|
|
||||||
c[iter] = H(iter, iter) / nu;
|
|
||||||
s[iter] = H(iter, iter + 1) / nu;
|
|
||||||
|
|
||||||
// Apply new Givens rotation
|
|
||||||
H(iter, iter) = nu;
|
|
||||||
H(iter, iter + 1) = 0.;
|
|
||||||
|
|
||||||
gamma[iter + 1] = -s[iter] * gamma[iter];
|
|
||||||
gamma[iter] = conjugate(c[iter]) * gamma[iter];
|
|
||||||
QrTimer.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void computeSolution(std::vector<Field> const &z, Field &psi, int iter) {
|
|
||||||
|
|
||||||
CompSolutionTimer.Start();
|
|
||||||
for (int i = iter; i >= 0; i--) {
|
|
||||||
y[i] = gamma[i];
|
|
||||||
for (int k = i + 1; k <= iter; k++)
|
|
||||||
y[i] = y[i] - ComplexD(H(k, i)) * y[k];
|
|
||||||
y[i] = y[i] / ComplexD(H(i, i));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i <= iter; i++)
|
|
||||||
psi = psi + z[i] * y[i];
|
|
||||||
CompSolutionTimer.Stop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/FlexibleGeneralisedMinimalResidual.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Daniel Richtmann <daniel.richtmann@ur.de>
|
|
||||||
|
|
||||||
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_FLEXIBLE_GENERALISED_MINIMAL_RESIDUAL_H
|
|
||||||
#define GRID_FLEXIBLE_GENERALISED_MINIMAL_RESIDUAL_H
|
|
||||||
|
|
||||||
namespace Grid {
|
|
||||||
|
|
||||||
template<class Field>
|
|
||||||
class FlexibleGeneralisedMinimalResidual : public OperatorFunction<Field> {
|
|
||||||
public:
|
|
||||||
using OperatorFunction<Field>::operator();
|
|
||||||
|
|
||||||
bool ErrorOnNoConverge; // Throw an GRID_ASSERT when FGMRES fails to converge,
|
|
||||||
// defaults to true
|
|
||||||
|
|
||||||
RealD Tolerance;
|
|
||||||
|
|
||||||
Integer MaxIterations;
|
|
||||||
Integer RestartLength;
|
|
||||||
Integer MaxNumberOfRestarts;
|
|
||||||
Integer IterationCount; // Number of iterations the FGMRES took to finish,
|
|
||||||
// filled in upon completion
|
|
||||||
|
|
||||||
GridStopWatch MatrixTimer;
|
|
||||||
GridStopWatch PrecTimer;
|
|
||||||
GridStopWatch LinalgTimer;
|
|
||||||
GridStopWatch QrTimer;
|
|
||||||
GridStopWatch CompSolutionTimer;
|
|
||||||
|
|
||||||
Eigen::MatrixXcd H;
|
|
||||||
|
|
||||||
std::vector<ComplexD> y;
|
|
||||||
std::vector<ComplexD> gamma;
|
|
||||||
std::vector<ComplexD> c;
|
|
||||||
std::vector<ComplexD> s;
|
|
||||||
|
|
||||||
LinearFunction<Field> &Preconditioner;
|
|
||||||
|
|
||||||
FlexibleGeneralisedMinimalResidual(RealD tol,
|
|
||||||
Integer maxit,
|
|
||||||
LinearFunction<Field> &Prec,
|
|
||||||
Integer restart_length,
|
|
||||||
bool err_on_no_conv = true)
|
|
||||||
: Tolerance(tol)
|
|
||||||
, MaxIterations(maxit)
|
|
||||||
, RestartLength(restart_length)
|
|
||||||
, MaxNumberOfRestarts(MaxIterations/RestartLength + ((MaxIterations%RestartLength == 0) ? 0 : 1))
|
|
||||||
, ErrorOnNoConverge(err_on_no_conv)
|
|
||||||
, H(Eigen::MatrixXcd::Zero(RestartLength, RestartLength + 1)) // sizes taken from DD-αAMG code base
|
|
||||||
, y(RestartLength + 1, 0.)
|
|
||||||
, gamma(RestartLength + 1, 0.)
|
|
||||||
, c(RestartLength + 1, 0.)
|
|
||||||
, s(RestartLength + 1, 0.)
|
|
||||||
, Preconditioner(Prec) {};
|
|
||||||
|
|
||||||
void operator()(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi) {
|
|
||||||
|
|
||||||
psi.Checkerboard() = src.Checkerboard();
|
|
||||||
conformable(psi, src);
|
|
||||||
|
|
||||||
RealD guess = norm2(psi);
|
|
||||||
GRID_ASSERT(std::isnan(guess) == 0);
|
|
||||||
|
|
||||||
RealD cp;
|
|
||||||
RealD ssq = norm2(src);
|
|
||||||
RealD rsq = Tolerance * Tolerance * ssq;
|
|
||||||
|
|
||||||
Field r(src.Grid());
|
|
||||||
|
|
||||||
std::cout << std::setprecision(4) << std::scientific;
|
|
||||||
std::cout << GridLogIterative << "FlexibleGeneralisedMinimalResidual: guess " << guess << std::endl;
|
|
||||||
std::cout << GridLogIterative << "FlexibleGeneralisedMinimalResidual: src " << ssq << std::endl;
|
|
||||||
|
|
||||||
PrecTimer.Reset();
|
|
||||||
MatrixTimer.Reset();
|
|
||||||
LinalgTimer.Reset();
|
|
||||||
QrTimer.Reset();
|
|
||||||
CompSolutionTimer.Reset();
|
|
||||||
|
|
||||||
GridStopWatch SolverTimer;
|
|
||||||
SolverTimer.Start();
|
|
||||||
|
|
||||||
IterationCount = 0;
|
|
||||||
|
|
||||||
for (int k=0; k<MaxNumberOfRestarts; k++) {
|
|
||||||
|
|
||||||
cp = outerLoopBody(LinOp, src, psi, rsq);
|
|
||||||
|
|
||||||
// Stopping condition
|
|
||||||
if (cp <= rsq) {
|
|
||||||
|
|
||||||
SolverTimer.Stop();
|
|
||||||
|
|
||||||
LinOp.Op(psi,r);
|
|
||||||
axpy(r,-1.0,src,r);
|
|
||||||
|
|
||||||
RealD srcnorm = sqrt(ssq);
|
|
||||||
RealD resnorm = sqrt(norm2(r));
|
|
||||||
RealD true_residual = resnorm / srcnorm;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "FlexibleGeneralisedMinimalResidual: Converged on iteration " << IterationCount
|
|
||||||
<< " computed residual " << sqrt(cp / ssq)
|
|
||||||
<< " true residual " << true_residual
|
|
||||||
<< " target " << Tolerance << std::endl;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "FGMRES Time elapsed: Total " << SolverTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "FGMRES Time elapsed: Precon " << PrecTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "FGMRES Time elapsed: Matrix " << MatrixTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "FGMRES Time elapsed: Linalg " << LinalgTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "FGMRES Time elapsed: QR " << QrTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "FGMRES Time elapsed: CompSol " << CompSolutionTimer.Elapsed() << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "FlexibleGeneralisedMinimalResidual did NOT converge" << std::endl;
|
|
||||||
|
|
||||||
if (ErrorOnNoConverge)
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) {
|
|
||||||
|
|
||||||
RealD cp = 0;
|
|
||||||
|
|
||||||
Field w(src.Grid());
|
|
||||||
Field r(src.Grid());
|
|
||||||
|
|
||||||
// these should probably be made class members so that they are only allocated once, not in every restart
|
|
||||||
std::vector<Field> v(RestartLength + 1, src.Grid()); for (auto &elem : v) elem = Zero();
|
|
||||||
std::vector<Field> z(RestartLength + 1, src.Grid()); for (auto &elem : z) elem = Zero();
|
|
||||||
|
|
||||||
MatrixTimer.Start();
|
|
||||||
LinOp.Op(psi, w);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
r = src - w;
|
|
||||||
|
|
||||||
gamma[0] = sqrt(norm2(r));
|
|
||||||
|
|
||||||
v[0] = (1. / gamma[0]) * r;
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
for (int i=0; i<RestartLength; i++) {
|
|
||||||
|
|
||||||
IterationCount++;
|
|
||||||
|
|
||||||
arnoldiStep(LinOp, v, z, w, i);
|
|
||||||
|
|
||||||
qrUpdate(i);
|
|
||||||
|
|
||||||
cp = norm(gamma[i+1]);
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << "FlexibleGeneralisedMinimalResidual: Iteration " << IterationCount
|
|
||||||
<< " residual " << cp << " target " << rsq << std::endl;
|
|
||||||
|
|
||||||
if ((i == RestartLength - 1) || (IterationCount == MaxIterations) || (cp <= rsq)) {
|
|
||||||
|
|
||||||
computeSolution(z, psi, i);
|
|
||||||
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GRID_ASSERT(0); // Never reached
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void arnoldiStep(LinearOperatorBase<Field> &LinOp, std::vector<Field> &v, std::vector<Field> &z, Field &w, int iter) {
|
|
||||||
|
|
||||||
PrecTimer.Start();
|
|
||||||
Preconditioner(v[iter], z[iter]);
|
|
||||||
PrecTimer.Stop();
|
|
||||||
|
|
||||||
MatrixTimer.Start();
|
|
||||||
LinOp.Op(z[iter], w);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
for (int i = 0; i <= iter; ++i) {
|
|
||||||
H(iter, i) = innerProduct(v[i], w);
|
|
||||||
w = w - ComplexD(H(iter, i)) * v[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
H(iter, iter + 1) = sqrt(norm2(w));
|
|
||||||
v[iter + 1] = ComplexD(1. / H(iter, iter + 1)) * w;
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void qrUpdate(int iter) {
|
|
||||||
|
|
||||||
QrTimer.Start();
|
|
||||||
for (int i = 0; i < iter ; ++i) {
|
|
||||||
auto tmp = -s[i] * ComplexD(H(iter, i)) + c[i] * ComplexD(H(iter, i + 1));
|
|
||||||
H(iter, i) = conjugate(c[i]) * ComplexD(H(iter, i)) + conjugate(s[i]) * ComplexD(H(iter, i + 1));
|
|
||||||
H(iter, i + 1) = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute new Givens Rotation
|
|
||||||
auto nu = sqrt(std::norm(H(iter, iter)) + std::norm(H(iter, iter + 1)));
|
|
||||||
c[iter] = H(iter, iter) / nu;
|
|
||||||
s[iter] = H(iter, iter + 1) / nu;
|
|
||||||
|
|
||||||
// Apply new Givens rotation
|
|
||||||
H(iter, iter) = nu;
|
|
||||||
H(iter, iter + 1) = 0.;
|
|
||||||
|
|
||||||
gamma[iter + 1] = -s[iter] * gamma[iter];
|
|
||||||
gamma[iter] = conjugate(c[iter]) * gamma[iter];
|
|
||||||
QrTimer.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void computeSolution(std::vector<Field> const &z, Field &psi, int iter) {
|
|
||||||
|
|
||||||
CompSolutionTimer.Start();
|
|
||||||
for (int i = iter; i >= 0; i--) {
|
|
||||||
y[i] = gamma[i];
|
|
||||||
for (int k = i + 1; k <= iter; k++)
|
|
||||||
y[i] = y[i] - ComplexD(H(k, i)) * y[k];
|
|
||||||
y[i] = y[i] / ComplexD(H(i, i));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i <= iter; i++)
|
|
||||||
psi = psi + z[i] * y[i];
|
|
||||||
CompSolutionTimer.Stop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,244 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/GeneralisedMinimalResidual.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Daniel Richtmann <daniel.richtmann@ur.de>
|
|
||||||
|
|
||||||
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_GENERALISED_MINIMAL_RESIDUAL_H
|
|
||||||
#define GRID_GENERALISED_MINIMAL_RESIDUAL_H
|
|
||||||
|
|
||||||
namespace Grid {
|
|
||||||
|
|
||||||
template<class Field>
|
|
||||||
class GeneralisedMinimalResidual : public OperatorFunction<Field> {
|
|
||||||
public:
|
|
||||||
using OperatorFunction<Field>::operator();
|
|
||||||
|
|
||||||
bool ErrorOnNoConverge; // Throw an GRID_ASSERT when GMRES fails to converge,
|
|
||||||
// defaults to true
|
|
||||||
|
|
||||||
RealD Tolerance;
|
|
||||||
|
|
||||||
Integer MaxIterations;
|
|
||||||
Integer RestartLength;
|
|
||||||
Integer MaxNumberOfRestarts;
|
|
||||||
Integer IterationCount; // Number of iterations the GMRES took to finish,
|
|
||||||
// filled in upon completion
|
|
||||||
|
|
||||||
GridStopWatch MatrixTimer;
|
|
||||||
GridStopWatch LinalgTimer;
|
|
||||||
GridStopWatch QrTimer;
|
|
||||||
GridStopWatch CompSolutionTimer;
|
|
||||||
|
|
||||||
Eigen::MatrixXcd H;
|
|
||||||
|
|
||||||
std::vector<ComplexD> y;
|
|
||||||
std::vector<ComplexD> gamma;
|
|
||||||
std::vector<ComplexD> c;
|
|
||||||
std::vector<ComplexD> s;
|
|
||||||
|
|
||||||
GeneralisedMinimalResidual(RealD tol,
|
|
||||||
Integer maxit,
|
|
||||||
Integer restart_length,
|
|
||||||
bool err_on_no_conv = true)
|
|
||||||
: Tolerance(tol)
|
|
||||||
, MaxIterations(maxit)
|
|
||||||
, RestartLength(restart_length)
|
|
||||||
, MaxNumberOfRestarts(MaxIterations/RestartLength + ((MaxIterations%RestartLength == 0) ? 0 : 1))
|
|
||||||
, ErrorOnNoConverge(err_on_no_conv)
|
|
||||||
, H(Eigen::MatrixXcd::Zero(RestartLength, RestartLength + 1)) // sizes taken from DD-αAMG code base
|
|
||||||
, y(RestartLength + 1, 0.)
|
|
||||||
, gamma(RestartLength + 1, 0.)
|
|
||||||
, c(RestartLength + 1, 0.)
|
|
||||||
, s(RestartLength + 1, 0.) {};
|
|
||||||
|
|
||||||
void operator()(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi) {
|
|
||||||
|
|
||||||
psi.Checkerboard() = src.Checkerboard();
|
|
||||||
conformable(psi, src);
|
|
||||||
|
|
||||||
RealD guess = norm2(psi);
|
|
||||||
GRID_ASSERT(std::isnan(guess) == 0);
|
|
||||||
|
|
||||||
RealD cp;
|
|
||||||
RealD ssq = norm2(src);
|
|
||||||
RealD rsq = Tolerance * Tolerance * ssq;
|
|
||||||
|
|
||||||
Field r(src.Grid());
|
|
||||||
|
|
||||||
std::cout << std::setprecision(4) << std::scientific;
|
|
||||||
std::cout << GridLogIterative << "GeneralisedMinimalResidual: guess " << guess << std::endl;
|
|
||||||
std::cout << GridLogIterative << "GeneralisedMinimalResidual: src " << ssq << std::endl;
|
|
||||||
|
|
||||||
MatrixTimer.Reset();
|
|
||||||
LinalgTimer.Reset();
|
|
||||||
QrTimer.Reset();
|
|
||||||
CompSolutionTimer.Reset();
|
|
||||||
|
|
||||||
GridStopWatch SolverTimer;
|
|
||||||
SolverTimer.Start();
|
|
||||||
|
|
||||||
IterationCount = 0;
|
|
||||||
|
|
||||||
for (int k=0; k<MaxNumberOfRestarts; k++) {
|
|
||||||
|
|
||||||
cp = outerLoopBody(LinOp, src, psi, rsq);
|
|
||||||
|
|
||||||
// Stopping condition
|
|
||||||
if (cp <= rsq) {
|
|
||||||
|
|
||||||
SolverTimer.Stop();
|
|
||||||
|
|
||||||
LinOp.Op(psi,r);
|
|
||||||
axpy(r,-1.0,src,r);
|
|
||||||
|
|
||||||
RealD srcnorm = sqrt(ssq);
|
|
||||||
RealD resnorm = sqrt(norm2(r));
|
|
||||||
RealD true_residual = resnorm / srcnorm;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "GeneralisedMinimalResidual: Converged on iteration " << IterationCount
|
|
||||||
<< " computed residual " << sqrt(cp / ssq)
|
|
||||||
<< " true residual " << true_residual
|
|
||||||
<< " target " << Tolerance << std::endl;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "GMRES Time elapsed: Total " << SolverTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "GMRES Time elapsed: Matrix " << MatrixTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "GMRES Time elapsed: Linalg " << LinalgTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "GMRES Time elapsed: QR " << QrTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "GMRES Time elapsed: CompSol " << CompSolutionTimer.Elapsed() << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "GeneralisedMinimalResidual did NOT converge" << std::endl;
|
|
||||||
|
|
||||||
if (ErrorOnNoConverge)
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) {
|
|
||||||
|
|
||||||
RealD cp = 0;
|
|
||||||
|
|
||||||
Field w(src.Grid());
|
|
||||||
Field r(src.Grid());
|
|
||||||
|
|
||||||
// this should probably be made a class member so that it is only allocated once, not in every restart
|
|
||||||
std::vector<Field> v(RestartLength + 1, src.Grid()); for (auto &elem : v) elem = Zero();
|
|
||||||
|
|
||||||
MatrixTimer.Start();
|
|
||||||
LinOp.Op(psi, w);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
r = src - w;
|
|
||||||
|
|
||||||
gamma[0] = sqrt(norm2(r));
|
|
||||||
|
|
||||||
v[0] = (1. / gamma[0]) * r;
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
for (int i=0; i<RestartLength; i++) {
|
|
||||||
|
|
||||||
IterationCount++;
|
|
||||||
|
|
||||||
arnoldiStep(LinOp, v, w, i);
|
|
||||||
|
|
||||||
qrUpdate(i);
|
|
||||||
|
|
||||||
cp = norm(gamma[i+1]);
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << "GeneralisedMinimalResidual: Iteration " << IterationCount
|
|
||||||
<< " residual " << cp << " target " << rsq << std::endl;
|
|
||||||
|
|
||||||
if ((i == RestartLength - 1) || (IterationCount == MaxIterations) || (cp <= rsq)) {
|
|
||||||
|
|
||||||
computeSolution(v, psi, i);
|
|
||||||
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GRID_ASSERT(0); // Never reached
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void arnoldiStep(LinearOperatorBase<Field> &LinOp, std::vector<Field> &v, Field &w, int iter) {
|
|
||||||
|
|
||||||
MatrixTimer.Start();
|
|
||||||
LinOp.Op(v[iter], w);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
for (int i = 0; i <= iter; ++i) {
|
|
||||||
H(iter, i) = innerProduct(v[i], w);
|
|
||||||
w = w - ComplexD(H(iter, i)) * v[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
H(iter, iter + 1) = sqrt(norm2(w));
|
|
||||||
v[iter + 1] = ComplexD(1. / H(iter, iter + 1)) * w;
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void qrUpdate(int iter) {
|
|
||||||
|
|
||||||
QrTimer.Start();
|
|
||||||
for (int i = 0; i < iter ; ++i) {
|
|
||||||
auto tmp = -s[i] * ComplexD(H(iter, i)) + c[i] * ComplexD(H(iter, i + 1));
|
|
||||||
H(iter, i) = conjugate(c[i]) * ComplexD(H(iter, i)) + conjugate(s[i]) * ComplexD(H(iter, i + 1));
|
|
||||||
H(iter, i + 1) = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute new Givens Rotation
|
|
||||||
auto nu = sqrt(std::norm(H(iter, iter)) + std::norm(H(iter, iter + 1)));
|
|
||||||
c[iter] = H(iter, iter) / nu;
|
|
||||||
s[iter] = H(iter, iter + 1) / nu;
|
|
||||||
|
|
||||||
// Apply new Givens rotation
|
|
||||||
H(iter, iter) = nu;
|
|
||||||
H(iter, iter + 1) = 0.;
|
|
||||||
|
|
||||||
gamma[iter + 1] = -s[iter] * gamma[iter];
|
|
||||||
gamma[iter] = conjugate(c[iter]) * gamma[iter];
|
|
||||||
QrTimer.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void computeSolution(std::vector<Field> const &v, Field &psi, int iter) {
|
|
||||||
|
|
||||||
CompSolutionTimer.Start();
|
|
||||||
for (int i = iter; i >= 0; i--) {
|
|
||||||
y[i] = gamma[i];
|
|
||||||
for (int k = i + 1; k <= iter; k++)
|
|
||||||
y[i] = y[i] - ComplexD(H(k, i)) * y[k];
|
|
||||||
y[i] = y[i] / ComplexD(H(i, i));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i <= iter; i++)
|
|
||||||
psi = psi + v[i] * y[i];
|
|
||||||
CompSolutionTimer.Stop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -35,7 +35,120 @@ Author: Christoph Lehner <clehner@bnl.gov>
|
|||||||
//#include <zlib.h>
|
//#include <zlib.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
// Move following 100 LOC to lattice/Lattice_basis.h
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
template<class Field>
|
||||||
|
void basisOrthogonalize(std::vector<Field> &basis,Field &w,int k)
|
||||||
|
{
|
||||||
|
for(int j=0; j<k; ++j){
|
||||||
|
auto ip = innerProduct(basis[j],w);
|
||||||
|
w = w - ip*basis[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Field>
|
||||||
|
void basisRotate(std::vector<Field> &basis,Eigen::MatrixXd& Qt,int j0, int j1, int k0,int k1,int Nm)
|
||||||
|
{
|
||||||
|
typedef typename Field::vector_object vobj;
|
||||||
|
GridBase* grid = basis[0]._grid;
|
||||||
|
|
||||||
|
parallel_region
|
||||||
|
{
|
||||||
|
|
||||||
|
std::vector < vobj , commAllocator<vobj> > B(Nm); // Thread private
|
||||||
|
|
||||||
|
parallel_for_internal(int ss=0;ss < grid->oSites();ss++){
|
||||||
|
for(int j=j0; j<j1; ++j) B[j]=0.;
|
||||||
|
|
||||||
|
for(int j=j0; j<j1; ++j){
|
||||||
|
for(int k=k0; k<k1; ++k){
|
||||||
|
B[j] +=Qt(j,k) * basis[k]._odata[ss];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(int j=j0; j<j1; ++j){
|
||||||
|
basis[j]._odata[ss] = B[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract a single rotated vector
|
||||||
|
template<class Field>
|
||||||
|
void basisRotateJ(Field &result,std::vector<Field> &basis,Eigen::MatrixXd& Qt,int j, int k0,int k1,int Nm)
|
||||||
|
{
|
||||||
|
typedef typename Field::vector_object vobj;
|
||||||
|
GridBase* grid = basis[0]._grid;
|
||||||
|
|
||||||
|
result.checkerboard = basis[0].checkerboard;
|
||||||
|
parallel_for(int ss=0;ss < grid->oSites();ss++){
|
||||||
|
vobj B = zero;
|
||||||
|
for(int k=k0; k<k1; ++k){
|
||||||
|
B +=Qt(j,k) * basis[k]._odata[ss];
|
||||||
|
}
|
||||||
|
result._odata[ss] = B;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Field>
|
||||||
|
void basisReorderInPlace(std::vector<Field> &_v,std::vector<RealD>& sort_vals, std::vector<int>& idx)
|
||||||
|
{
|
||||||
|
int vlen = idx.size();
|
||||||
|
|
||||||
|
assert(vlen>=1);
|
||||||
|
assert(vlen<=sort_vals.size());
|
||||||
|
assert(vlen<=_v.size());
|
||||||
|
|
||||||
|
for (size_t i=0;i<vlen;i++) {
|
||||||
|
|
||||||
|
if (idx[i] != i) {
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
// idx[i] is a table of desired sources giving a permutation.
|
||||||
|
// Swap v[i] with v[idx[i]].
|
||||||
|
// Find j>i for which _vnew[j] = _vold[i],
|
||||||
|
// track the move idx[j] => idx[i]
|
||||||
|
// track the move idx[i] => i
|
||||||
|
//////////////////////////////////////
|
||||||
|
size_t j;
|
||||||
|
for (j=i;j<idx.size();j++)
|
||||||
|
if (idx[j]==i)
|
||||||
|
break;
|
||||||
|
|
||||||
|
assert(idx[i] > i); assert(j!=idx.size()); assert(idx[j]==i);
|
||||||
|
|
||||||
|
std::swap(_v[i]._odata,_v[idx[i]]._odata); // should use vector move constructor, no data copy
|
||||||
|
std::swap(sort_vals[i],sort_vals[idx[i]]);
|
||||||
|
|
||||||
|
idx[j] = idx[i];
|
||||||
|
idx[i] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::vector<int> basisSortGetIndex(std::vector<RealD>& sort_vals)
|
||||||
|
{
|
||||||
|
std::vector<int> idx(sort_vals.size());
|
||||||
|
std::iota(idx.begin(), idx.end(), 0);
|
||||||
|
|
||||||
|
// sort indexes based on comparing values in v
|
||||||
|
std::sort(idx.begin(), idx.end(), [&sort_vals](int i1, int i2) {
|
||||||
|
return ::fabs(sort_vals[i1]) < ::fabs(sort_vals[i2]);
|
||||||
|
});
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Field>
|
||||||
|
void basisSortInPlace(std::vector<Field> & _v,std::vector<RealD>& sort_vals, bool reverse)
|
||||||
|
{
|
||||||
|
std::vector<int> idx = basisSortGetIndex(sort_vals);
|
||||||
|
if (reverse)
|
||||||
|
std::reverse(idx.begin(), idx.end());
|
||||||
|
|
||||||
|
basisReorderInPlace(_v,sort_vals,idx);
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////
|
||||||
// Implicitly restarted lanczos
|
// Implicitly restarted lanczos
|
||||||
@@ -79,16 +192,14 @@ template<class Field> class ImplicitlyRestartedLanczosHermOpTester : public Imp
|
|||||||
RealD vv = norm2(v) / ::pow(evalMaxApprox,2.0);
|
RealD vv = norm2(v) / ::pow(evalMaxApprox,2.0);
|
||||||
|
|
||||||
std::cout.precision(13);
|
std::cout.precision(13);
|
||||||
|
|
||||||
int conv=0;
|
|
||||||
if( (vv<eresid*eresid) ) conv = 1;
|
|
||||||
|
|
||||||
std::cout<<GridLogIRL << "[" << std::setw(3)<<j<<"] "
|
std::cout<<GridLogIRL << "[" << std::setw(3)<<j<<"] "
|
||||||
<<"eval = "<<std::setw(25)<< eval << " (" << eval_poly << ")"
|
<<"eval = "<<std::setw(25)<< eval << " (" << eval_poly << ")"
|
||||||
<<" |H B[i] - eval[i]B[i]|^2 / evalMaxApprox^2 " << std::setw(25) << vv
|
<<" |H B[i] - eval[i]B[i]|^2 / evalMaxApprox^2 " << std::setw(25) << vv
|
||||||
<<" target " << eresid*eresid << " conv " <<conv
|
|
||||||
<<std::endl;
|
<<std::endl;
|
||||||
|
|
||||||
|
int conv=0;
|
||||||
|
if( (vv<eresid*eresid) ) conv = 1;
|
||||||
|
|
||||||
return conv;
|
return conv;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -148,7 +259,7 @@ public:
|
|||||||
RealD _eresid, // resid in lmdue deficit
|
RealD _eresid, // resid in lmdue deficit
|
||||||
int _MaxIter, // Max iterations
|
int _MaxIter, // Max iterations
|
||||||
RealD _betastp=0.0, // if beta(k) < betastp: converged
|
RealD _betastp=0.0, // if beta(k) < betastp: converged
|
||||||
int _MinRestart=0, int _orth_period = 1,
|
int _MinRestart=1, int _orth_period = 1,
|
||||||
IRLdiagonalisation _diagonalisation= IRLdiagonaliseWithEigen) :
|
IRLdiagonalisation _diagonalisation= IRLdiagonaliseWithEigen) :
|
||||||
SimpleTester(HermOp), _PolyOp(PolyOp), _HermOp(HermOp), _Tester(Tester),
|
SimpleTester(HermOp), _PolyOp(PolyOp), _HermOp(HermOp), _Tester(Tester),
|
||||||
Nstop(_Nstop) , Nk(_Nk), Nm(_Nm),
|
Nstop(_Nstop) , Nk(_Nk), Nm(_Nm),
|
||||||
@@ -164,7 +275,7 @@ public:
|
|||||||
RealD _eresid, // resid in lmdue deficit
|
RealD _eresid, // resid in lmdue deficit
|
||||||
int _MaxIter, // Max iterations
|
int _MaxIter, // Max iterations
|
||||||
RealD _betastp=0.0, // if beta(k) < betastp: converged
|
RealD _betastp=0.0, // if beta(k) < betastp: converged
|
||||||
int _MinRestart=0, int _orth_period = 1,
|
int _MinRestart=1, int _orth_period = 1,
|
||||||
IRLdiagonalisation _diagonalisation= IRLdiagonaliseWithEigen) :
|
IRLdiagonalisation _diagonalisation= IRLdiagonaliseWithEigen) :
|
||||||
SimpleTester(HermOp), _PolyOp(PolyOp), _HermOp(HermOp), _Tester(SimpleTester),
|
SimpleTester(HermOp), _PolyOp(PolyOp), _HermOp(HermOp), _Tester(SimpleTester),
|
||||||
Nstop(_Nstop) , Nk(_Nk), Nm(_Nm),
|
Nstop(_Nstop) , Nk(_Nk), Nm(_Nm),
|
||||||
@@ -178,7 +289,7 @@ public:
|
|||||||
template<typename T> static RealD normalise(T& v)
|
template<typename T> static RealD normalise(T& v)
|
||||||
{
|
{
|
||||||
RealD nn = norm2(v);
|
RealD nn = norm2(v);
|
||||||
nn = std::sqrt(nn);
|
nn = sqrt(nn);
|
||||||
v = v * (1.0/nn);
|
v = v * (1.0/nn);
|
||||||
return nn;
|
return nn;
|
||||||
}
|
}
|
||||||
@@ -210,10 +321,10 @@ until convergence
|
|||||||
*/
|
*/
|
||||||
void calc(std::vector<RealD>& eval, std::vector<Field>& evec, const Field& src, int& Nconv, bool reverse=false)
|
void calc(std::vector<RealD>& eval, std::vector<Field>& evec, const Field& src, int& Nconv, bool reverse=false)
|
||||||
{
|
{
|
||||||
GridBase *grid = src.Grid();
|
GridBase *grid = src._grid;
|
||||||
GRID_ASSERT(grid == evec[0].Grid());
|
assert(grid == evec[0]._grid);
|
||||||
|
|
||||||
// GridLogIRL.TimingMode(1);
|
GridLogIRL.TimingMode(1);
|
||||||
std::cout << GridLogIRL <<"**************************************************************************"<< std::endl;
|
std::cout << GridLogIRL <<"**************************************************************************"<< std::endl;
|
||||||
std::cout << GridLogIRL <<" ImplicitlyRestartedLanczos::calc() starting iteration 0 / "<< MaxIter<< std::endl;
|
std::cout << GridLogIRL <<" ImplicitlyRestartedLanczos::calc() starting iteration 0 / "<< MaxIter<< std::endl;
|
||||||
std::cout << GridLogIRL <<"**************************************************************************"<< std::endl;
|
std::cout << GridLogIRL <<"**************************************************************************"<< std::endl;
|
||||||
@@ -231,32 +342,27 @@ until convergence
|
|||||||
}
|
}
|
||||||
std::cout << GridLogIRL <<"**************************************************************************"<< std::endl;
|
std::cout << GridLogIRL <<"**************************************************************************"<< std::endl;
|
||||||
|
|
||||||
GRID_ASSERT(Nm <= evec.size() && Nm <= eval.size());
|
assert(Nm <= evec.size() && Nm <= eval.size());
|
||||||
|
|
||||||
// quickly get an idea of the largest eigenvalue to more properly normalize the residuum
|
// quickly get an idea of the largest eigenvalue to more properly normalize the residuum
|
||||||
RealD evalMaxApprox = 0.0;
|
RealD evalMaxApprox = 0.0;
|
||||||
{
|
{
|
||||||
auto src_n = src;
|
auto src_n = src;
|
||||||
auto tmp = src;
|
auto tmp = src;
|
||||||
std::cout << GridLogIRL << " IRL source norm " << norm2(src) << std::endl;
|
|
||||||
const int _MAX_ITER_IRL_MEVAPP_ = 50;
|
const int _MAX_ITER_IRL_MEVAPP_ = 50;
|
||||||
for (int i=0;i<_MAX_ITER_IRL_MEVAPP_;i++) {
|
for (int i=0;i<_MAX_ITER_IRL_MEVAPP_;i++) {
|
||||||
normalise(src_n);
|
normalise(src_n);
|
||||||
_HermOp(src_n,tmp);
|
_HermOp(src_n,tmp);
|
||||||
// std::cout << GridLogMessage<< tmp<<std::endl; exit(0);
|
RealD vnum = real(innerProduct(src_n,tmp)); // HermOp.
|
||||||
// std::cout << GridLogIRL << " _HermOp " << norm2(tmp) << std::endl;
|
|
||||||
// RealD vnum = real(innerProduct(src_n,tmp)); // HermOp.
|
|
||||||
RealD vnum = real(innerProduct(tmp,tmp)); // HermOp^2.
|
|
||||||
RealD vden = norm2(src_n);
|
RealD vden = norm2(src_n);
|
||||||
RealD na = std::sqrt(vnum/vden);
|
RealD na = vnum/vden;
|
||||||
if (fabs(evalMaxApprox/na - 1.0) < 0.0001)
|
if (fabs(evalMaxApprox/na - 1.0) < 0.05)
|
||||||
i=_MAX_ITER_IRL_MEVAPP_;
|
i=_MAX_ITER_IRL_MEVAPP_;
|
||||||
evalMaxApprox = na;
|
evalMaxApprox = na;
|
||||||
std::cout << GridLogIRL << " Approximation of largest eigenvalue: " << evalMaxApprox << std::endl;
|
std::cout << GridLogIRL << " Approximation of largest eigenvalue: " << evalMaxApprox << std::endl;
|
||||||
src_n = tmp;
|
src_n = tmp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::cout << GridLogIRL << " Final evalMaxApprox " << evalMaxApprox << std::endl;
|
|
||||||
|
|
||||||
std::vector<RealD> lme(Nm);
|
std::vector<RealD> lme(Nm);
|
||||||
std::vector<RealD> lme2(Nm);
|
std::vector<RealD> lme2(Nm);
|
||||||
@@ -337,10 +443,10 @@ until convergence
|
|||||||
}
|
}
|
||||||
std::cout<<GridLogIRL <<"QR decomposed "<<std::endl;
|
std::cout<<GridLogIRL <<"QR decomposed "<<std::endl;
|
||||||
|
|
||||||
GRID_ASSERT(k2<Nm); GRID_ASSERT(k2<Nm); GRID_ASSERT(k1>0);
|
assert(k2<Nm); assert(k2<Nm); assert(k1>0);
|
||||||
|
|
||||||
basisRotate(evec,Qt,k1-1,k2+1,0,Nm,Nm); /// big constraint on the basis
|
basisRotate(evec,Qt,k1-1,k2+1,0,Nm,Nm); /// big constraint on the basis
|
||||||
std::cout<<GridLogIRL <<"basisRotated by Qt *"<<k1-1<<","<<k2+1<<")"<<std::endl;
|
std::cout<<GridLogIRL <<"basisRotated by Qt"<<std::endl;
|
||||||
|
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
// Compressed vector f and beta(k2)
|
// Compressed vector f and beta(k2)
|
||||||
@@ -348,7 +454,7 @@ until convergence
|
|||||||
f *= Qt(k2-1,Nm-1);
|
f *= Qt(k2-1,Nm-1);
|
||||||
f += lme[k2-1] * evec[k2];
|
f += lme[k2-1] * evec[k2];
|
||||||
beta_k = norm2(f);
|
beta_k = norm2(f);
|
||||||
beta_k = std::sqrt(beta_k);
|
beta_k = sqrt(beta_k);
|
||||||
std::cout<<GridLogIRL<<" beta(k) = "<<beta_k<<std::endl;
|
std::cout<<GridLogIRL<<" beta(k) = "<<beta_k<<std::endl;
|
||||||
|
|
||||||
RealD betar = 1.0/beta_k;
|
RealD betar = 1.0/beta_k;
|
||||||
@@ -371,7 +477,7 @@ until convergence
|
|||||||
|
|
||||||
std::cout << GridLogIRL << "Test convergence: rotate subset of vectors to test convergence " << std::endl;
|
std::cout << GridLogIRL << "Test convergence: rotate subset of vectors to test convergence " << std::endl;
|
||||||
|
|
||||||
Field B(grid); B.Checkerboard() = evec[0].Checkerboard();
|
Field B(grid); B.checkerboard = evec[0].checkerboard;
|
||||||
|
|
||||||
// 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;
|
||||||
@@ -409,7 +515,7 @@ until convergence
|
|||||||
|
|
||||||
converged:
|
converged:
|
||||||
{
|
{
|
||||||
Field B(grid); B.Checkerboard() = evec[0].Checkerboard();
|
Field B(grid); B.checkerboard = evec[0].checkerboard;
|
||||||
basisRotate(evec,Qt,0,Nk,0,Nk,Nm);
|
basisRotate(evec,Qt,0,Nk,0,Nk,Nm);
|
||||||
std::cout << GridLogIRL << " Rotated basis"<<std::endl;
|
std::cout << GridLogIRL << " Rotated basis"<<std::endl;
|
||||||
Nconv=0;
|
Nconv=0;
|
||||||
@@ -423,15 +529,14 @@ until convergence
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( Nconv < Nstop ) {
|
if ( Nconv < Nstop )
|
||||||
std::cout << GridLogIRL << "Nconv ("<<Nconv<<") < Nstop ("<<Nstop<<")"<<std::endl;
|
std::cout << GridLogIRL << "Nconv ("<<Nconv<<") < Nstop ("<<Nstop<<")"<<std::endl;
|
||||||
std::cout << GridLogIRL << "returning Nstop vectors, the last "<< Nstop-Nconv << "of which might meet convergence criterion only approximately" <<std::endl;
|
|
||||||
}
|
|
||||||
eval=eval2;
|
eval=eval2;
|
||||||
|
|
||||||
//Keep only converged
|
//Keep only converged
|
||||||
eval.resize(Nstop);// was Nconv
|
eval.resize(Nconv);// Nstop?
|
||||||
evec.resize(Nstop,grid);// was Nconv
|
evec.resize(Nconv,grid);// Nstop?
|
||||||
basisSortInPlace(evec,eval,reverse);
|
basisSortInPlace(evec,eval,reverse);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -449,11 +554,11 @@ until convergence
|
|||||||
/* Saad PP. 195
|
/* Saad PP. 195
|
||||||
1. Choose an initial vector v1 of 2-norm unity. Set β1 ≡ 0, v0 ≡ 0
|
1. Choose an initial vector v1 of 2-norm unity. Set β1 ≡ 0, v0 ≡ 0
|
||||||
2. For k = 1,2,...,m Do:
|
2. For k = 1,2,...,m Do:
|
||||||
3. wk:=Avk - b_k v_{k-1}
|
3. wk:=Avk−βkv_{k−1}
|
||||||
4. ak:=(wk,vk) //
|
4. αk:=(wk,vk) //
|
||||||
5. wk:=wk-akvk // wk orthog vk
|
5. wk:=wk−αkvk // wk orthog vk
|
||||||
6. bk+1 := ||wk||_2. If b_k+1 = 0 then Stop
|
6. βk+1 := ∥wk∥2. If βk+1 = 0 then Stop
|
||||||
7. vk+1 := wk/b_k+1
|
7. vk+1 := wk/βk+1
|
||||||
8. EndDo
|
8. EndDo
|
||||||
*/
|
*/
|
||||||
void step(std::vector<RealD>& lmd,
|
void step(std::vector<RealD>& lmd,
|
||||||
@@ -461,41 +566,38 @@ until convergence
|
|||||||
std::vector<Field>& evec,
|
std::vector<Field>& evec,
|
||||||
Field& w,int Nm,int k)
|
Field& w,int Nm,int k)
|
||||||
{
|
{
|
||||||
std::cout<<GridLogDebug << "Lanczos step " <<k<<std::endl;
|
|
||||||
const RealD tiny = 1.0e-20;
|
const RealD tiny = 1.0e-20;
|
||||||
GRID_ASSERT( k< Nm );
|
assert( k< Nm );
|
||||||
|
|
||||||
GridStopWatch gsw_op,gsw_o;
|
GridStopWatch gsw_op,gsw_o;
|
||||||
|
|
||||||
Field& evec_k = evec[k];
|
Field& evec_k = evec[k];
|
||||||
|
|
||||||
_PolyOp(evec_k,w); std::cout<<GridLogDebug << "PolyOp" <<std::endl;
|
_PolyOp(evec_k,w); std::cout<<GridLogIRL << "PolyOp" <<std::endl;
|
||||||
|
|
||||||
if(k>0) w -= lme[k-1] * evec[k-1];
|
if(k>0) w -= lme[k-1] * evec[k-1];
|
||||||
|
|
||||||
ComplexD zalph = innerProduct(evec_k,w);
|
ComplexD zalph = innerProduct(evec_k,w); // 4. αk:=(wk,vk)
|
||||||
RealD alph = real(zalph);
|
RealD alph = real(zalph);
|
||||||
|
|
||||||
w = w - alph * evec_k;
|
w = w - alph * evec_k;// 5. wk:=wk−αkvk
|
||||||
|
|
||||||
RealD beta = normalise(w);
|
RealD beta = normalise(w); // 6. βk+1 := ∥wk∥2. If βk+1 = 0 then Stop
|
||||||
|
// 7. vk+1 := wk/βk+1
|
||||||
|
|
||||||
lmd[k] = alph;
|
lmd[k] = alph;
|
||||||
lme[k] = beta;
|
lme[k] = beta;
|
||||||
|
|
||||||
if ( (k>0) && ( (k % orth_period) == 0 )) {
|
if (k>0 && k % orth_period == 0) {
|
||||||
std::cout<<GridLogDebug << "Orthogonalising " <<k<<std::endl;
|
|
||||||
orthogonalize(w,evec,k); // orthonormalise
|
orthogonalize(w,evec,k); // orthonormalise
|
||||||
std::cout<<GridLogDebug << "Orthogonalised " <<k<<std::endl;
|
std::cout<<GridLogIRL << "Orthogonalised " <<std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(k < Nm-1) evec[k+1] = w;
|
if(k < Nm-1) evec[k+1] = w;
|
||||||
|
|
||||||
std::cout<<GridLogIRL << "Lanczos step alpha[" << k << "] = " << zalph << " beta[" << k << "] = "<<beta<<std::endl;
|
std::cout<<GridLogIRL << "alpha[" << k << "] = " << zalph << " beta[" << k << "] = "<<beta<<std::endl;
|
||||||
if ( beta < tiny )
|
if ( beta < tiny )
|
||||||
std::cout<<GridLogIRL << " beta is tiny "<<beta<<std::endl;
|
std::cout<<GridLogIRL << " beta is tiny "<<beta<<std::endl;
|
||||||
|
|
||||||
std::cout<<GridLogDebug << "Lanczos step complete " <<k<<std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void diagonalize_Eigen(std::vector<RealD>& lmd, std::vector<RealD>& lme,
|
void diagonalize_Eigen(std::vector<RealD>& lmd, std::vector<RealD>& lme,
|
||||||
@@ -597,7 +699,7 @@ until convergence
|
|||||||
} else if ( diagonalisation == IRLdiagonaliseWithEigen ) {
|
} else if ( diagonalisation == IRLdiagonaliseWithEigen ) {
|
||||||
diagonalize_Eigen(lmd,lme,Nk,Nm,Qt,grid);
|
diagonalize_Eigen(lmd,lme,Nk,Nm,Qt,grid);
|
||||||
} else {
|
} else {
|
||||||
GRID_ASSERT(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -687,7 +789,7 @@ void diagonalize_lapack(std::vector<RealD>& lmd,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
GRID_ASSERT(0);
|
assert(0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,7 +807,7 @@ void diagonalize_QR(std::vector<RealD>& lmd, std::vector<RealD>& lme,
|
|||||||
|
|
||||||
// determination of 2x2 leading submatrix
|
// determination of 2x2 leading submatrix
|
||||||
RealD dsub = lmd[kmax-1]-lmd[kmax-2];
|
RealD dsub = lmd[kmax-1]-lmd[kmax-2];
|
||||||
RealD dd = std::sqrt(dsub*dsub + 4.0*lme[kmax-2]*lme[kmax-2]);
|
RealD dd = sqrt(dsub*dsub + 4.0*lme[kmax-2]*lme[kmax-2]);
|
||||||
RealD Dsh = 0.5*(lmd[kmax-2]+lmd[kmax-1] +dd*(dsub/fabs(dsub)));
|
RealD Dsh = 0.5*(lmd[kmax-2]+lmd[kmax-1] +dd*(dsub/fabs(dsub)));
|
||||||
// (Dsh: shift)
|
// (Dsh: shift)
|
||||||
|
|
||||||
@@ -736,6 +838,5 @@ void diagonalize_QR(std::vector<RealD>& lmd, std::vector<RealD>& lme,
|
|||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/*************************************************************************************
|
/*************************************************************************************
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
@@ -24,15 +24,16 @@ Author: paboyle <paboyle@ph.ed.ac.uk>
|
|||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#ifndef GRID_LOCAL_COHERENCE_IRL_H
|
#ifndef GRID_LOCAL_COHERENCE_IRL_H
|
||||||
#define GRID_LOCAL_COHERENCE_IRL_H
|
#define GRID_LOCAL_COHERENCE_IRL_H
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
|
|
||||||
struct LanczosParams : Serializable {
|
struct LanczosParams : Serializable {
|
||||||
public:
|
public:
|
||||||
GRID_SERIALIZABLE_CLASS_MEMBERS(LanczosParams,
|
GRID_SERIALIZABLE_CLASS_MEMBERS(LanczosParams,
|
||||||
ChebyParams, Cheby,/*Chebyshev*/
|
ChebyParams, Cheby,/*Chebyshev*/
|
||||||
int, Nstop, /*Vecs in Lanczos must converge Nstop < Nk < Nm*/
|
int, Nstop, /*Vecs in Lanczos must converge Nstop < Nk < Nm*/
|
||||||
@@ -44,9 +45,8 @@ public:
|
|||||||
int, MinRes); // Must restart
|
int, MinRes); // Must restart
|
||||||
};
|
};
|
||||||
|
|
||||||
//This class is the input parameter class for some testing programs
|
|
||||||
struct LocalCoherenceLanczosParams : Serializable {
|
struct LocalCoherenceLanczosParams : Serializable {
|
||||||
public:
|
public:
|
||||||
GRID_SERIALIZABLE_CLASS_MEMBERS(LocalCoherenceLanczosParams,
|
GRID_SERIALIZABLE_CLASS_MEMBERS(LocalCoherenceLanczosParams,
|
||||||
bool, saveEvecs,
|
bool, saveEvecs,
|
||||||
bool, doFine,
|
bool, doFine,
|
||||||
@@ -59,7 +59,7 @@ public:
|
|||||||
RealD , coarse_relax_tol,
|
RealD , coarse_relax_tol,
|
||||||
std::vector<int>, blockSize,
|
std::vector<int>, blockSize,
|
||||||
std::string, config,
|
std::string, config,
|
||||||
std::vector < ComplexD >, omega,
|
std::vector < std::complex<double> >, omega,
|
||||||
RealD, mass,
|
RealD, mass,
|
||||||
RealD, M5);
|
RealD, M5);
|
||||||
};
|
};
|
||||||
@@ -68,7 +68,6 @@ public:
|
|||||||
template<class Fobj,class CComplex,int nbasis>
|
template<class Fobj,class CComplex,int nbasis>
|
||||||
class ProjectedHermOp : public LinearFunction<Lattice<iVector<CComplex,nbasis > > > {
|
class ProjectedHermOp : public LinearFunction<Lattice<iVector<CComplex,nbasis > > > {
|
||||||
public:
|
public:
|
||||||
using LinearFunction<Lattice<iVector<CComplex,nbasis > > >::operator();
|
|
||||||
typedef iVector<CComplex,nbasis > CoarseSiteVector;
|
typedef iVector<CComplex,nbasis > CoarseSiteVector;
|
||||||
typedef Lattice<CoarseSiteVector> CoarseField;
|
typedef Lattice<CoarseSiteVector> CoarseField;
|
||||||
typedef Lattice<CComplex> CoarseScalar; // used for inner products on fine field
|
typedef Lattice<CComplex> CoarseScalar; // used for inner products on fine field
|
||||||
@@ -80,18 +79,18 @@ public:
|
|||||||
ProjectedHermOp(LinearOperatorBase<FineField>& linop, std::vector<FineField> & _subspace) :
|
ProjectedHermOp(LinearOperatorBase<FineField>& linop, std::vector<FineField> & _subspace) :
|
||||||
_Linop(linop), subspace(_subspace)
|
_Linop(linop), subspace(_subspace)
|
||||||
{
|
{
|
||||||
GRID_ASSERT(subspace.size() >0);
|
assert(subspace.size() >0);
|
||||||
};
|
};
|
||||||
|
|
||||||
void operator()(const CoarseField& in, CoarseField& out) {
|
void operator()(const CoarseField& in, CoarseField& out) {
|
||||||
GridBase *FineGrid = subspace[0].Grid();
|
GridBase *FineGrid = subspace[0]._grid;
|
||||||
int checkerboard = subspace[0].Checkerboard();
|
int checkerboard = subspace[0].checkerboard;
|
||||||
|
|
||||||
FineField fin (FineGrid); fin.Checkerboard()= checkerboard;
|
FineField fin (FineGrid); fin.checkerboard= checkerboard;
|
||||||
FineField fout(FineGrid); fout.Checkerboard() = checkerboard;
|
FineField fout(FineGrid); fout.checkerboard = checkerboard;
|
||||||
|
|
||||||
blockPromote(in,fin,subspace); std::cout<<GridLogIRL<<"ProjectedHermop : Promote to fine"<<std::endl;
|
blockPromote(in,fin,subspace); std::cout<<GridLogIRL<<"ProjectedHermop : Promote to fine"<<std::endl;
|
||||||
_Linop.HermOp(fin,fout); std::cout<<GridLogIRL<<"ProjectedHermop : HermOp (fine) "<<std::endl;
|
_Linop.HermOp(fin,fout); std::cout<<GridLogIRL<<"ProjectedHermop : HermOp (fine) "<<std::endl;
|
||||||
blockProject(out,fout,subspace); std::cout<<GridLogIRL<<"ProjectedHermop : Project to coarse "<<std::endl;
|
blockProject(out,fout,subspace); std::cout<<GridLogIRL<<"ProjectedHermop : Project to coarse "<<std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -99,7 +98,6 @@ public:
|
|||||||
template<class Fobj,class CComplex,int nbasis>
|
template<class Fobj,class CComplex,int nbasis>
|
||||||
class ProjectedFunctionHermOp : public LinearFunction<Lattice<iVector<CComplex,nbasis > > > {
|
class ProjectedFunctionHermOp : public LinearFunction<Lattice<iVector<CComplex,nbasis > > > {
|
||||||
public:
|
public:
|
||||||
using LinearFunction<Lattice<iVector<CComplex,nbasis > > >::operator();
|
|
||||||
typedef iVector<CComplex,nbasis > CoarseSiteVector;
|
typedef iVector<CComplex,nbasis > CoarseSiteVector;
|
||||||
typedef Lattice<CoarseSiteVector> CoarseField;
|
typedef Lattice<CoarseSiteVector> CoarseField;
|
||||||
typedef Lattice<CComplex> CoarseScalar; // used for inner products on fine field
|
typedef Lattice<CComplex> CoarseScalar; // used for inner products on fine field
|
||||||
@@ -120,11 +118,11 @@ public:
|
|||||||
|
|
||||||
void operator()(const CoarseField& in, CoarseField& out) {
|
void operator()(const CoarseField& in, CoarseField& out) {
|
||||||
|
|
||||||
GridBase *FineGrid = subspace[0].Grid();
|
GridBase *FineGrid = subspace[0]._grid;
|
||||||
int checkerboard = subspace[0].Checkerboard();
|
int checkerboard = subspace[0].checkerboard;
|
||||||
|
|
||||||
FineField fin (FineGrid); fin.Checkerboard() =checkerboard;
|
FineField fin (FineGrid); fin.checkerboard =checkerboard;
|
||||||
FineField fout(FineGrid);fout.Checkerboard() =checkerboard;
|
FineField fout(FineGrid);fout.checkerboard =checkerboard;
|
||||||
|
|
||||||
blockPromote(in,fin,subspace); std::cout<<GridLogIRL<<"ProjectedFunctionHermop : Promote to fine"<<std::endl;
|
blockPromote(in,fin,subspace); std::cout<<GridLogIRL<<"ProjectedFunctionHermop : Promote to fine"<<std::endl;
|
||||||
_poly(_Linop,fin,fout); std::cout<<GridLogIRL<<"ProjectedFunctionHermop : Poly "<<std::endl;
|
_poly(_Linop,fin,fout); std::cout<<GridLogIRL<<"ProjectedFunctionHermop : Poly "<<std::endl;
|
||||||
@@ -135,7 +133,7 @@ public:
|
|||||||
template<class Fobj,class CComplex,int nbasis>
|
template<class Fobj,class CComplex,int nbasis>
|
||||||
class ImplicitlyRestartedLanczosSmoothedTester : public ImplicitlyRestartedLanczosTester<Lattice<iVector<CComplex,nbasis > > >
|
class ImplicitlyRestartedLanczosSmoothedTester : public ImplicitlyRestartedLanczosTester<Lattice<iVector<CComplex,nbasis > > >
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef iVector<CComplex,nbasis > CoarseSiteVector;
|
typedef iVector<CComplex,nbasis > CoarseSiteVector;
|
||||||
typedef Lattice<CoarseSiteVector> CoarseField;
|
typedef Lattice<CoarseSiteVector> CoarseField;
|
||||||
typedef Lattice<CComplex> CoarseScalar; // used for inner products on fine field
|
typedef Lattice<CComplex> CoarseScalar; // used for inner products on fine field
|
||||||
@@ -144,26 +142,18 @@ public:
|
|||||||
LinearFunction<CoarseField> & _Poly;
|
LinearFunction<CoarseField> & _Poly;
|
||||||
OperatorFunction<FineField> & _smoother;
|
OperatorFunction<FineField> & _smoother;
|
||||||
LinearOperatorBase<FineField> &_Linop;
|
LinearOperatorBase<FineField> &_Linop;
|
||||||
RealD _coarse_relax_tol;
|
RealD _coarse_relax_tol;
|
||||||
std::vector<FineField> &_subspace;
|
std::vector<FineField> &_subspace;
|
||||||
|
|
||||||
int _largestEvalIdxForReport; //The convergence of the LCL is based on the evals of the coarse grid operator, not those of the underlying fine grid operator
|
|
||||||
//As a result we do not know what the eval range of the fine operator is until the very end, making tuning the Cheby bounds very difficult
|
|
||||||
//To work around this issue, every restart we separately reconstruct the fine operator eval for the lowest and highest evec and print these
|
|
||||||
//out alongside the evals of the coarse operator. To do so we need to know the index of the largest eval (i.e. Nstop-1)
|
|
||||||
//NOTE: If largestEvalIdxForReport=-1 (default) then this is not performed
|
|
||||||
|
|
||||||
ImplicitlyRestartedLanczosSmoothedTester(LinearFunction<CoarseField> &Poly,
|
ImplicitlyRestartedLanczosSmoothedTester(LinearFunction<CoarseField> &Poly,
|
||||||
OperatorFunction<FineField> &smoother,
|
OperatorFunction<FineField> &smoother,
|
||||||
LinearOperatorBase<FineField> &Linop,
|
LinearOperatorBase<FineField> &Linop,
|
||||||
std::vector<FineField> &subspace,
|
std::vector<FineField> &subspace,
|
||||||
RealD coarse_relax_tol=5.0e3,
|
RealD coarse_relax_tol=5.0e3)
|
||||||
int largestEvalIdxForReport=-1)
|
|
||||||
: _smoother(smoother), _Linop(Linop), _Poly(Poly), _subspace(subspace),
|
: _smoother(smoother), _Linop(Linop), _Poly(Poly), _subspace(subspace),
|
||||||
_coarse_relax_tol(coarse_relax_tol), _largestEvalIdxForReport(largestEvalIdxForReport)
|
_coarse_relax_tol(coarse_relax_tol)
|
||||||
{ };
|
{ };
|
||||||
|
|
||||||
//evalMaxApprox: approximation of largest eval of the fine Chebyshev operator (suitably wrapped by block projection)
|
|
||||||
int TestConvergence(int j,RealD eresid,CoarseField &B, RealD &eval,RealD evalMaxApprox)
|
int TestConvergence(int j,RealD eresid,CoarseField &B, RealD &eval,RealD evalMaxApprox)
|
||||||
{
|
{
|
||||||
CoarseField v(B);
|
CoarseField v(B);
|
||||||
@@ -186,30 +176,16 @@ public:
|
|||||||
<<" |H B[i] - eval[i]B[i]|^2 / evalMaxApprox^2 " << std::setw(25) << vv
|
<<" |H B[i] - eval[i]B[i]|^2 / evalMaxApprox^2 " << std::setw(25) << vv
|
||||||
<<std::endl;
|
<<std::endl;
|
||||||
|
|
||||||
if(_largestEvalIdxForReport != -1 && (j==0 || j==_largestEvalIdxForReport)){
|
|
||||||
std::cout<<GridLogIRL << "Estimating true eval of fine grid operator for eval idx " << j << std::endl;
|
|
||||||
RealD tmp_eval;
|
|
||||||
ReconstructEval(j,eresid,B,tmp_eval,1.0); //don't use evalMaxApprox of coarse operator! (cf below)
|
|
||||||
}
|
|
||||||
|
|
||||||
int conv=0;
|
int conv=0;
|
||||||
if( (vv<eresid*eresid) ) conv = 1;
|
if( (vv<eresid*eresid) ) conv = 1;
|
||||||
return conv;
|
return conv;
|
||||||
}
|
}
|
||||||
|
|
||||||
//This function is called at the end of the coarse grid Lanczos. It promotes the coarse eigenvector 'B' to the fine grid,
|
|
||||||
//applies a smoother to the result then computes the computes the *fine grid* eigenvalue (output as 'eval').
|
|
||||||
|
|
||||||
//evalMaxApprox should be the approximation of the largest eval of the fine Hermop. However when this function is called by IRL it actually passes the largest eval of the *Chebyshev* operator (as this is the max approx used for the TestConvergence above)
|
|
||||||
//As the largest eval of the Chebyshev is typically several orders of magnitude larger this makes the convergence test pass even when it should not.
|
|
||||||
//We therefore ignore evalMaxApprox here and use a value of 1.0 (note this value is already used by TestCoarse)
|
|
||||||
int ReconstructEval(int j,RealD eresid,CoarseField &B, RealD &eval,RealD evalMaxApprox)
|
int ReconstructEval(int j,RealD eresid,CoarseField &B, RealD &eval,RealD evalMaxApprox)
|
||||||
{
|
{
|
||||||
evalMaxApprox = 1.0; //cf above
|
GridBase *FineGrid = _subspace[0]._grid;
|
||||||
GridBase *FineGrid = _subspace[0].Grid();
|
int checkerboard = _subspace[0].checkerboard;
|
||||||
int checkerboard = _subspace[0].Checkerboard();
|
FineField fB(FineGrid);fB.checkerboard =checkerboard;
|
||||||
FineField fB(FineGrid);fB.Checkerboard() =checkerboard;
|
FineField fv(FineGrid);fv.checkerboard =checkerboard;
|
||||||
FineField fv(FineGrid);fv.Checkerboard() =checkerboard;
|
|
||||||
|
|
||||||
blockPromote(B,fv,_subspace);
|
blockPromote(B,fv,_subspace);
|
||||||
|
|
||||||
@@ -224,13 +200,13 @@ public:
|
|||||||
eval = vnum/vden;
|
eval = vnum/vden;
|
||||||
fv -= eval*fB;
|
fv -= eval*fB;
|
||||||
RealD vv = norm2(fv) / ::pow(evalMaxApprox,2.0);
|
RealD vv = norm2(fv) / ::pow(evalMaxApprox,2.0);
|
||||||
if ( j > nbasis ) eresid = eresid*_coarse_relax_tol;
|
|
||||||
|
|
||||||
std::cout.precision(13);
|
std::cout.precision(13);
|
||||||
std::cout<<GridLogIRL << "[" << std::setw(3)<<j<<"] "
|
std::cout<<GridLogIRL << "[" << std::setw(3)<<j<<"] "
|
||||||
<<"eval = "<<std::setw(25)<< eval << " (" << eval_poly << ")"
|
<<"eval = "<<std::setw(25)<< eval << " (" << eval_poly << ")"
|
||||||
<<" |H B[i] - eval[i]B[i]|^2 / evalMaxApprox^2 " << std::setw(25) << vv << " target " << eresid*eresid
|
<<" |H B[i] - eval[i]B[i]|^2 / evalMaxApprox^2 " << std::setw(25) << vv
|
||||||
<<std::endl;
|
<<std::endl;
|
||||||
|
if ( j > nbasis ) eresid = eresid*_coarse_relax_tol;
|
||||||
if( (vv<eresid*eresid) ) return 1;
|
if( (vv<eresid*eresid) ) return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -308,10 +284,6 @@ public:
|
|||||||
evals_coarse.resize(0);
|
evals_coarse.resize(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
//The block inner product is the inner product on the fine grid locally summed over the blocks
|
|
||||||
//to give a Lattice<Scalar> on the coarse grid. This function orthnormalizes the fine-grid subspace
|
|
||||||
//vectors under the block inner product. This step must be performed after computing the fine grid
|
|
||||||
//eigenvectors and before computing the coarse grid eigenvectors.
|
|
||||||
void Orthogonalise(void ) {
|
void Orthogonalise(void ) {
|
||||||
CoarseScalar InnerProd(_CoarseGrid);
|
CoarseScalar InnerProd(_CoarseGrid);
|
||||||
std::cout << GridLogMessage <<" Gramm-Schmidt pass 1"<<std::endl;
|
std::cout << GridLogMessage <<" Gramm-Schmidt pass 1"<<std::endl;
|
||||||
@@ -333,11 +305,11 @@ public:
|
|||||||
int Nk = nbasis;
|
int Nk = nbasis;
|
||||||
subspace.resize(Nk,_FineGrid);
|
subspace.resize(Nk,_FineGrid);
|
||||||
subspace[0]=1.0;
|
subspace[0]=1.0;
|
||||||
subspace[0].Checkerboard()=_checkerboard;
|
subspace[0].checkerboard=_checkerboard;
|
||||||
normalise(subspace[0]);
|
normalise(subspace[0]);
|
||||||
PlainHermOp<FineField> Op(_FineOp);
|
PlainHermOp<FineField> Op(_FineOp);
|
||||||
for(int k=1;k<Nk;k++){
|
for(int k=1;k<Nk;k++){
|
||||||
subspace[k].Checkerboard()=_checkerboard;
|
subspace[k].checkerboard=_checkerboard;
|
||||||
Op(subspace[k-1],subspace[k]);
|
Op(subspace[k-1],subspace[k]);
|
||||||
normalise(subspace[k]);
|
normalise(subspace[k]);
|
||||||
}
|
}
|
||||||
@@ -346,21 +318,19 @@ public:
|
|||||||
|
|
||||||
void testFine(RealD resid)
|
void testFine(RealD resid)
|
||||||
{
|
{
|
||||||
GRID_ASSERT(evals_fine.size() == nbasis);
|
assert(evals_fine.size() == nbasis);
|
||||||
GRID_ASSERT(subspace.size() == nbasis);
|
assert(subspace.size() == nbasis);
|
||||||
PlainHermOp<FineField> Op(_FineOp);
|
PlainHermOp<FineField> Op(_FineOp);
|
||||||
ImplicitlyRestartedLanczosHermOpTester<FineField> SimpleTester(Op);
|
ImplicitlyRestartedLanczosHermOpTester<FineField> SimpleTester(Op);
|
||||||
for(int k=0;k<nbasis;k++){
|
for(int k=0;k<nbasis;k++){
|
||||||
GRID_ASSERT(SimpleTester.ReconstructEval(k,resid,subspace[k],evals_fine[k],1.0)==1);
|
assert(SimpleTester.ReconstructEval(k,resid,subspace[k],evals_fine[k],1.0)==1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//While this method serves to check the coarse eigenvectors, it also recomputes the eigenvalues from the smoothed reconstructed eigenvectors
|
|
||||||
//hence the smoother can be tuned after running the coarse Lanczos by using a different smoother here
|
|
||||||
void testCoarse(RealD resid,ChebyParams cheby_smooth,RealD relax)
|
void testCoarse(RealD resid,ChebyParams cheby_smooth,RealD relax)
|
||||||
{
|
{
|
||||||
GRID_ASSERT(evals_fine.size() == nbasis);
|
assert(evals_fine.size() == nbasis);
|
||||||
GRID_ASSERT(subspace.size() == nbasis);
|
assert(subspace.size() == nbasis);
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// create a smoother and see if we can get a cheap convergence test and smooth inside the IRL
|
// create a smoother and see if we can get a cheap convergence test and smooth inside the IRL
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -380,7 +350,7 @@ public:
|
|||||||
void calcFine(ChebyParams cheby_parms,int Nstop,int Nk,int Nm,RealD resid,
|
void calcFine(ChebyParams cheby_parms,int Nstop,int Nk,int Nm,RealD resid,
|
||||||
RealD MaxIt, RealD betastp, int MinRes)
|
RealD MaxIt, RealD betastp, int MinRes)
|
||||||
{
|
{
|
||||||
GRID_ASSERT(nbasis<=Nm);
|
assert(nbasis<=Nm);
|
||||||
Chebyshev<FineField> Cheby(cheby_parms);
|
Chebyshev<FineField> Cheby(cheby_parms);
|
||||||
FunctionHermOp<FineField> ChebyOp(Cheby,_FineOp);
|
FunctionHermOp<FineField> ChebyOp(Cheby,_FineOp);
|
||||||
PlainHermOp<FineField> Op(_FineOp);
|
PlainHermOp<FineField> Op(_FineOp);
|
||||||
@@ -390,65 +360,47 @@ public:
|
|||||||
|
|
||||||
ImplicitlyRestartedLanczos<FineField> IRL(ChebyOp,Op,Nstop,Nk,Nm,resid,MaxIt,betastp,MinRes);
|
ImplicitlyRestartedLanczos<FineField> IRL(ChebyOp,Op,Nstop,Nk,Nm,resid,MaxIt,betastp,MinRes);
|
||||||
|
|
||||||
FineField src(_FineGrid);
|
FineField src(_FineGrid); src=1.0; src.checkerboard = _checkerboard;
|
||||||
typedef typename FineField::scalar_type Scalar;
|
|
||||||
// src=1.0;
|
|
||||||
src=Scalar(1.0);
|
|
||||||
src.Checkerboard() = _checkerboard;
|
|
||||||
|
|
||||||
int Nconv;
|
int Nconv;
|
||||||
IRL.calc(evals_fine,subspace,src,Nconv,false);
|
IRL.calc(evals_fine,subspace,src,Nconv,false);
|
||||||
|
|
||||||
// Shrink down to number saved
|
// Shrink down to number saved
|
||||||
GRID_ASSERT(Nstop>=nbasis);
|
assert(Nstop>=nbasis);
|
||||||
GRID_ASSERT(Nconv>=nbasis);
|
assert(Nconv>=nbasis);
|
||||||
evals_fine.resize(nbasis);
|
evals_fine.resize(nbasis);
|
||||||
subspace.resize(nbasis,_FineGrid);
|
subspace.resize(nbasis,_FineGrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//cheby_op: Parameters of the fine grid Chebyshev polynomial used for the Lanczos acceleration
|
|
||||||
//cheby_smooth: Parameters of a separate Chebyshev polynomial used after the Lanczos has completed to smooth out high frequency noise in the reconstructed fine grid eigenvectors prior to computing the eigenvalue
|
|
||||||
//relax: Reconstructed eigenvectors (post smoothing) are naturally not as precise as true eigenvectors. This factor acts as a multiplier on the stopping condition when determining whether the results satisfy the user provided stopping condition
|
|
||||||
void calcCoarse(ChebyParams cheby_op,ChebyParams cheby_smooth,RealD relax,
|
void calcCoarse(ChebyParams cheby_op,ChebyParams cheby_smooth,RealD relax,
|
||||||
int Nstop, int Nk, int Nm,RealD resid,
|
int Nstop, int Nk, int Nm,RealD resid,
|
||||||
RealD MaxIt, RealD betastp, int MinRes)
|
RealD MaxIt, RealD betastp, int MinRes)
|
||||||
{
|
{
|
||||||
Chebyshev<FineField> Cheby(cheby_op); //Chebyshev of fine operator on fine grid
|
Chebyshev<FineField> Cheby(cheby_op);
|
||||||
ProjectedHermOp<Fobj,CComplex,nbasis> Op(_FineOp,subspace); //Fine operator on coarse grid with intermediate fine grid conversion
|
ProjectedHermOp<Fobj,CComplex,nbasis> Op(_FineOp,subspace);
|
||||||
ProjectedFunctionHermOp<Fobj,CComplex,nbasis> ChebyOp (Cheby,_FineOp,subspace); //Chebyshev of fine operator on coarse grid with intermediate fine grid conversion
|
ProjectedFunctionHermOp<Fobj,CComplex,nbasis> ChebyOp (Cheby,_FineOp,subspace);
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// create a smoother and see if we can get a cheap convergence test and smooth inside the IRL
|
// create a smoother and see if we can get a cheap convergence test and smooth inside the IRL
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
Chebyshev<FineField> ChebySmooth(cheby_smooth); //lower order Chebyshev of fine operator on fine grid used to smooth regenerated eigenvectors
|
Chebyshev<FineField> ChebySmooth(cheby_smooth);
|
||||||
ImplicitlyRestartedLanczosSmoothedTester<Fobj,CComplex,nbasis> ChebySmoothTester(ChebyOp,ChebySmooth,_FineOp,subspace,relax,Nstop-1);
|
ImplicitlyRestartedLanczosSmoothedTester<Fobj,CComplex,nbasis> ChebySmoothTester(ChebyOp,ChebySmooth,_FineOp,subspace,relax);
|
||||||
|
|
||||||
evals_coarse.resize(Nm);
|
evals_coarse.resize(Nm);
|
||||||
evec_coarse.resize(Nm,_CoarseGrid);
|
evec_coarse.resize(Nm,_CoarseGrid);
|
||||||
|
|
||||||
CoarseField src(_CoarseGrid); src=1.0;
|
CoarseField src(_CoarseGrid); src=1.0;
|
||||||
|
|
||||||
//Note the "tester" here is also responsible for generating the fine grid eigenvalues which are output into the "evals_coarse" array
|
|
||||||
ImplicitlyRestartedLanczos<CoarseField> IRL(ChebyOp,ChebyOp,ChebySmoothTester,Nstop,Nk,Nm,resid,MaxIt,betastp,MinRes);
|
ImplicitlyRestartedLanczos<CoarseField> IRL(ChebyOp,ChebyOp,ChebySmoothTester,Nstop,Nk,Nm,resid,MaxIt,betastp,MinRes);
|
||||||
int Nconv=0;
|
int Nconv=0;
|
||||||
IRL.calc(evals_coarse,evec_coarse,src,Nconv,false);
|
IRL.calc(evals_coarse,evec_coarse,src,Nconv,false);
|
||||||
GRID_ASSERT(Nconv>=Nstop);
|
assert(Nconv>=Nstop);
|
||||||
evals_coarse.resize(Nstop);
|
evals_coarse.resize(Nstop);
|
||||||
evec_coarse.resize (Nstop,_CoarseGrid);
|
evec_coarse.resize (Nstop,_CoarseGrid);
|
||||||
for (int i=0;i<Nstop;i++){
|
for (int i=0;i<Nstop;i++){
|
||||||
std::cout << i << " Coarse eval = " << evals_coarse[i] << std::endl;
|
std::cout << i << " Coarse eval = " << evals_coarse[i] << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Get the fine eigenvector 'i' by reconstruction
|
|
||||||
void getFineEvecEval(FineField &evec, RealD &eval, const int i) const{
|
|
||||||
blockPromote(evec_coarse[i],evec,subspace);
|
|
||||||
eval = evals_coarse[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/MinimalResidual.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Daniel Richtmann <daniel.richtmann@ur.de>
|
|
||||||
|
|
||||||
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_MINIMAL_RESIDUAL_H
|
|
||||||
#define GRID_MINIMAL_RESIDUAL_H
|
|
||||||
|
|
||||||
namespace Grid {
|
|
||||||
|
|
||||||
template<class Field> class MinimalResidual : public OperatorFunction<Field> {
|
|
||||||
public:
|
|
||||||
using OperatorFunction<Field>::operator();
|
|
||||||
|
|
||||||
bool ErrorOnNoConverge; // throw an GRID_ASSERT when the MR fails to converge.
|
|
||||||
// Defaults true.
|
|
||||||
RealD Tolerance;
|
|
||||||
Integer MaxIterations;
|
|
||||||
RealD overRelaxParam;
|
|
||||||
Integer IterationsToComplete; // Number of iterations the MR took to finish.
|
|
||||||
// Filled in upon completion
|
|
||||||
|
|
||||||
MinimalResidual(RealD tol, Integer maxit, Real ovrelparam = 1.0, bool err_on_no_conv = true)
|
|
||||||
: Tolerance(tol), MaxIterations(maxit), overRelaxParam(ovrelparam), ErrorOnNoConverge(err_on_no_conv){};
|
|
||||||
|
|
||||||
void operator()(LinearOperatorBase<Field> &Linop, const Field &src, Field &psi) {
|
|
||||||
|
|
||||||
psi.Checkerboard() = src.Checkerboard();
|
|
||||||
conformable(psi, src);
|
|
||||||
|
|
||||||
ComplexD a, c;
|
|
||||||
RealD d;
|
|
||||||
|
|
||||||
Field Mr(src);
|
|
||||||
Field r(src);
|
|
||||||
|
|
||||||
// Initial residual computation & set up
|
|
||||||
RealD guess = norm2(psi);
|
|
||||||
GRID_ASSERT(std::isnan(guess) == 0);
|
|
||||||
|
|
||||||
RealD ssq = norm2(src);
|
|
||||||
RealD rsq = Tolerance * Tolerance * ssq;
|
|
||||||
|
|
||||||
Linop.Op(psi, Mr);
|
|
||||||
|
|
||||||
r = src - Mr;
|
|
||||||
|
|
||||||
RealD cp = norm2(r);
|
|
||||||
|
|
||||||
std::cout << std::setprecision(4) << std::scientific;
|
|
||||||
std::cout << GridLogIterative << "MinimalResidual: guess " << guess << std::endl;
|
|
||||||
std::cout << GridLogIterative << "MinimalResidual: src " << ssq << std::endl;
|
|
||||||
std::cout << GridLogIterative << "MinimalResidual: cp,r " << cp << std::endl;
|
|
||||||
|
|
||||||
if (cp <= rsq) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << "MinimalResidual: k=0 residual " << cp << " target " << rsq << std::endl;
|
|
||||||
|
|
||||||
GridStopWatch LinalgTimer;
|
|
||||||
GridStopWatch MatrixTimer;
|
|
||||||
GridStopWatch SolverTimer;
|
|
||||||
|
|
||||||
SolverTimer.Start();
|
|
||||||
int k;
|
|
||||||
for (k = 1; k <= MaxIterations; k++) {
|
|
||||||
|
|
||||||
MatrixTimer.Start();
|
|
||||||
Linop.Op(r, Mr);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
|
|
||||||
c = innerProduct(Mr, r);
|
|
||||||
|
|
||||||
d = norm2(Mr);
|
|
||||||
|
|
||||||
a = c / d;
|
|
||||||
|
|
||||||
a = a * overRelaxParam;
|
|
||||||
|
|
||||||
psi = psi + r * a;
|
|
||||||
|
|
||||||
r = r - Mr * a;
|
|
||||||
|
|
||||||
cp = norm2(r);
|
|
||||||
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << "MinimalResidual: Iteration " << k
|
|
||||||
<< " residual " << cp << " target " << rsq << std::endl;
|
|
||||||
std::cout << GridLogDebug << "a = " << a << " c = " << c << " d = " << d << std::endl;
|
|
||||||
|
|
||||||
// Stopping condition
|
|
||||||
if (cp <= rsq) {
|
|
||||||
SolverTimer.Stop();
|
|
||||||
|
|
||||||
Linop.Op(psi, Mr);
|
|
||||||
r = src - Mr;
|
|
||||||
|
|
||||||
RealD srcnorm = sqrt(ssq);
|
|
||||||
RealD resnorm = sqrt(norm2(r));
|
|
||||||
RealD true_residual = resnorm / srcnorm;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "MinimalResidual Converged on iteration " << k
|
|
||||||
<< " computed residual " << sqrt(cp / ssq)
|
|
||||||
<< " true residual " << true_residual
|
|
||||||
<< " target " << Tolerance << std::endl;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "MR Time elapsed: Total " << SolverTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "MR Time elapsed: Matrix " << MatrixTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "MR Time elapsed: Linalg " << LinalgTimer.Elapsed() << std::endl;
|
|
||||||
|
|
||||||
if (ErrorOnNoConverge)
|
|
||||||
GRID_ASSERT(true_residual / Tolerance < 10000.0);
|
|
||||||
|
|
||||||
IterationsToComplete = k;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "MinimalResidual did NOT converge"
|
|
||||||
<< std::endl;
|
|
||||||
|
|
||||||
if (ErrorOnNoConverge)
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
|
|
||||||
IterationsToComplete = k;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace Grid
|
|
||||||
#endif
|
|
||||||
@@ -1,276 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/MixedPrecisionFlexibleGeneralisedMinimalResidual.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Daniel Richtmann <daniel.richtmann@ur.de>
|
|
||||||
|
|
||||||
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_MIXED_PRECISION_FLEXIBLE_GENERALISED_MINIMAL_RESIDUAL_H
|
|
||||||
#define GRID_MIXED_PRECISION_FLEXIBLE_GENERALISED_MINIMAL_RESIDUAL_H
|
|
||||||
|
|
||||||
namespace Grid {
|
|
||||||
|
|
||||||
template<class FieldD, class FieldF, typename std::enable_if<getPrecision<FieldD>::value == 2, int>::type = 0, typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
|
|
||||||
class MixedPrecisionFlexibleGeneralisedMinimalResidual : public OperatorFunction<FieldD> {
|
|
||||||
public:
|
|
||||||
|
|
||||||
using OperatorFunction<FieldD>::operator();
|
|
||||||
|
|
||||||
bool ErrorOnNoConverge; // Throw an GRID_ASSERT when MPFGMRES fails to converge,
|
|
||||||
// defaults to true
|
|
||||||
|
|
||||||
RealD Tolerance;
|
|
||||||
|
|
||||||
Integer MaxIterations;
|
|
||||||
Integer RestartLength;
|
|
||||||
Integer MaxNumberOfRestarts;
|
|
||||||
Integer IterationCount; // Number of iterations the MPFGMRES took to finish,
|
|
||||||
// filled in upon completion
|
|
||||||
|
|
||||||
GridStopWatch MatrixTimer;
|
|
||||||
GridStopWatch PrecTimer;
|
|
||||||
GridStopWatch LinalgTimer;
|
|
||||||
GridStopWatch QrTimer;
|
|
||||||
GridStopWatch CompSolutionTimer;
|
|
||||||
GridStopWatch ChangePrecTimer;
|
|
||||||
|
|
||||||
Eigen::MatrixXcd H;
|
|
||||||
|
|
||||||
std::vector<ComplexD> y;
|
|
||||||
std::vector<ComplexD> gamma;
|
|
||||||
std::vector<ComplexD> c;
|
|
||||||
std::vector<ComplexD> s;
|
|
||||||
|
|
||||||
GridBase* SinglePrecGrid;
|
|
||||||
|
|
||||||
LinearFunction<FieldF> &Preconditioner;
|
|
||||||
|
|
||||||
MixedPrecisionFlexibleGeneralisedMinimalResidual(RealD tol,
|
|
||||||
Integer maxit,
|
|
||||||
GridBase * sp_grid,
|
|
||||||
LinearFunction<FieldF> &Prec,
|
|
||||||
Integer restart_length,
|
|
||||||
bool err_on_no_conv = true)
|
|
||||||
: Tolerance(tol)
|
|
||||||
, MaxIterations(maxit)
|
|
||||||
, RestartLength(restart_length)
|
|
||||||
, MaxNumberOfRestarts(MaxIterations/RestartLength + ((MaxIterations%RestartLength == 0) ? 0 : 1))
|
|
||||||
, ErrorOnNoConverge(err_on_no_conv)
|
|
||||||
, H(Eigen::MatrixXcd::Zero(RestartLength, RestartLength + 1)) // sizes taken from DD-αAMG code base
|
|
||||||
, y(RestartLength + 1, 0.)
|
|
||||||
, gamma(RestartLength + 1, 0.)
|
|
||||||
, c(RestartLength + 1, 0.)
|
|
||||||
, s(RestartLength + 1, 0.)
|
|
||||||
, SinglePrecGrid(sp_grid)
|
|
||||||
, Preconditioner(Prec) {};
|
|
||||||
|
|
||||||
void operator()(LinearOperatorBase<FieldD> &LinOp, const FieldD &src, FieldD &psi) {
|
|
||||||
|
|
||||||
psi.Checkerboard() = src.Checkerboard();
|
|
||||||
conformable(psi, src);
|
|
||||||
|
|
||||||
RealD guess = norm2(psi);
|
|
||||||
GRID_ASSERT(std::isnan(guess) == 0);
|
|
||||||
|
|
||||||
RealD cp;
|
|
||||||
RealD ssq = norm2(src);
|
|
||||||
RealD rsq = Tolerance * Tolerance * ssq;
|
|
||||||
|
|
||||||
FieldD r(src.Grid());
|
|
||||||
|
|
||||||
std::cout << std::setprecision(4) << std::scientific;
|
|
||||||
std::cout << GridLogIterative << "MPFGMRES: guess " << guess << std::endl;
|
|
||||||
std::cout << GridLogIterative << "MPFGMRES: src " << ssq << std::endl;
|
|
||||||
|
|
||||||
PrecTimer.Reset();
|
|
||||||
MatrixTimer.Reset();
|
|
||||||
LinalgTimer.Reset();
|
|
||||||
QrTimer.Reset();
|
|
||||||
CompSolutionTimer.Reset();
|
|
||||||
ChangePrecTimer.Reset();
|
|
||||||
|
|
||||||
GridStopWatch SolverTimer;
|
|
||||||
SolverTimer.Start();
|
|
||||||
|
|
||||||
IterationCount = 0;
|
|
||||||
|
|
||||||
for (int k=0; k<MaxNumberOfRestarts; k++) {
|
|
||||||
|
|
||||||
cp = outerLoopBody(LinOp, src, psi, rsq);
|
|
||||||
|
|
||||||
// Stopping condition
|
|
||||||
if (cp <= rsq) {
|
|
||||||
|
|
||||||
SolverTimer.Stop();
|
|
||||||
|
|
||||||
LinOp.Op(psi,r);
|
|
||||||
axpy(r,-1.0,src,r);
|
|
||||||
|
|
||||||
RealD srcnorm = sqrt(ssq);
|
|
||||||
RealD resnorm = sqrt(norm2(r));
|
|
||||||
RealD true_residual = resnorm / srcnorm;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "MPFGMRES: Converged on iteration " << IterationCount
|
|
||||||
<< " computed residual " << sqrt(cp / ssq)
|
|
||||||
<< " true residual " << true_residual
|
|
||||||
<< " target " << Tolerance << std::endl;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "MPFGMRES Time elapsed: Total " << SolverTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "MPFGMRES Time elapsed: Precon " << PrecTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "MPFGMRES Time elapsed: Matrix " << MatrixTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "MPFGMRES Time elapsed: Linalg " << LinalgTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "MPFGMRES Time elapsed: QR " << QrTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "MPFGMRES Time elapsed: CompSol " << CompSolutionTimer.Elapsed() << std::endl;
|
|
||||||
std::cout << GridLogMessage << "MPFGMRES Time elapsed: PrecChange " << ChangePrecTimer.Elapsed() << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "MPFGMRES did NOT converge" << std::endl;
|
|
||||||
|
|
||||||
if (ErrorOnNoConverge)
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
RealD outerLoopBody(LinearOperatorBase<FieldD> &LinOp, const FieldD &src, FieldD &psi, RealD rsq) {
|
|
||||||
|
|
||||||
RealD cp = 0;
|
|
||||||
|
|
||||||
FieldD w(src.Grid());
|
|
||||||
FieldD r(src.Grid());
|
|
||||||
|
|
||||||
// these should probably be made class members so that they are only allocated once, not in every restart
|
|
||||||
std::vector<FieldD> v(RestartLength + 1, src.Grid()); for (auto &elem : v) elem = Zero();
|
|
||||||
std::vector<FieldD> z(RestartLength + 1, src.Grid()); for (auto &elem : z) elem = Zero();
|
|
||||||
|
|
||||||
MatrixTimer.Start();
|
|
||||||
LinOp.Op(psi, w);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
r = src - w;
|
|
||||||
|
|
||||||
gamma[0] = sqrt(norm2(r));
|
|
||||||
|
|
||||||
v[0] = (1. / gamma[0]) * r;
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
for (int i=0; i<RestartLength; i++) {
|
|
||||||
|
|
||||||
IterationCount++;
|
|
||||||
|
|
||||||
arnoldiStep(LinOp, v, z, w, i);
|
|
||||||
|
|
||||||
qrUpdate(i);
|
|
||||||
|
|
||||||
cp = norm(gamma[i+1]);
|
|
||||||
|
|
||||||
std::cout << GridLogIterative << "MPFGMRES: Iteration " << IterationCount
|
|
||||||
<< " residual " << cp << " target " << rsq << std::endl;
|
|
||||||
|
|
||||||
if ((i == RestartLength - 1) || (IterationCount == MaxIterations) || (cp <= rsq)) {
|
|
||||||
|
|
||||||
computeSolution(z, psi, i);
|
|
||||||
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GRID_ASSERT(0); // Never reached
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void arnoldiStep(LinearOperatorBase<FieldD> &LinOp, std::vector<FieldD> &v, std::vector<FieldD> &z, FieldD &w, int iter) {
|
|
||||||
|
|
||||||
FieldF v_f(SinglePrecGrid);
|
|
||||||
FieldF z_f(SinglePrecGrid);
|
|
||||||
|
|
||||||
ChangePrecTimer.Start();
|
|
||||||
precisionChange(v_f, v[iter]);
|
|
||||||
precisionChange(z_f, z[iter]);
|
|
||||||
ChangePrecTimer.Stop();
|
|
||||||
|
|
||||||
PrecTimer.Start();
|
|
||||||
Preconditioner(v_f, z_f);
|
|
||||||
PrecTimer.Stop();
|
|
||||||
|
|
||||||
ChangePrecTimer.Start();
|
|
||||||
precisionChange(z[iter], z_f);
|
|
||||||
ChangePrecTimer.Stop();
|
|
||||||
|
|
||||||
MatrixTimer.Start();
|
|
||||||
LinOp.Op(z[iter], w);
|
|
||||||
MatrixTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
for (int i = 0; i <= iter; ++i) {
|
|
||||||
H(iter, i) = innerProduct(v[i], w);
|
|
||||||
w = w - ComplexD(H(iter, i)) * v[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
H(iter, iter + 1) = sqrt(norm2(w));
|
|
||||||
v[iter + 1] = ComplexD(1. / H(iter, iter + 1)) * w;
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void qrUpdate(int iter) {
|
|
||||||
|
|
||||||
QrTimer.Start();
|
|
||||||
for (int i = 0; i < iter ; ++i) {
|
|
||||||
auto tmp = -s[i] * ComplexD(H(iter, i)) + c[i] * ComplexD(H(iter, i + 1));
|
|
||||||
H(iter, i) = conjugate(c[i]) * ComplexD(H(iter, i)) + conjugate(s[i]) * ComplexD(H(iter, i + 1));
|
|
||||||
H(iter, i + 1) = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute new Givens Rotation
|
|
||||||
auto nu = sqrt(std::norm(H(iter, iter)) + std::norm(H(iter, iter + 1)));
|
|
||||||
c[iter] = H(iter, iter) / nu;
|
|
||||||
s[iter] = H(iter, iter + 1) / nu;
|
|
||||||
|
|
||||||
// Apply new Givens rotation
|
|
||||||
H(iter, iter) = nu;
|
|
||||||
H(iter, iter + 1) = 0.;
|
|
||||||
|
|
||||||
gamma[iter + 1] = -s[iter] * gamma[iter];
|
|
||||||
gamma[iter] = conjugate(c[iter]) * gamma[iter];
|
|
||||||
QrTimer.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void computeSolution(std::vector<FieldD> const &z, FieldD &psi, int iter) {
|
|
||||||
|
|
||||||
CompSolutionTimer.Start();
|
|
||||||
for (int i = iter; i >= 0; i--) {
|
|
||||||
y[i] = gamma[i];
|
|
||||||
for (int k = i + 1; k <= iter; k++)
|
|
||||||
y[i] = y[i] - ComplexD(H(k, i)) * y[k];
|
|
||||||
y[i] = y[i] / ComplexD(H(i, i));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i <= iter; i++)
|
|
||||||
psi = psi + z[i] * y[i];
|
|
||||||
CompSolutionTimer.Stop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/*************************************************************************************
|
/*************************************************************************************
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
@@ -23,116 +23,38 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#ifndef GRID_NORMAL_EQUATIONS_H
|
#ifndef GRID_NORMAL_EQUATIONS_H
|
||||||
#define GRID_NORMAL_EQUATIONS_H
|
#define GRID_NORMAL_EQUATIONS_H
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Take a matrix and form an NE solver calling a Herm solver
|
// Take a matrix and form an NE solver calling a Herm solver
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
template<class Field> class NormalEquations : public LinearFunction<Field>{
|
template<class Field> class NormalEquations : public OperatorFunction<Field>{
|
||||||
private:
|
private:
|
||||||
SparseMatrixBase<Field> & _Matrix;
|
SparseMatrixBase<Field> & _Matrix;
|
||||||
OperatorFunction<Field> & _HermitianSolver;
|
OperatorFunction<Field> & _HermitianSolver;
|
||||||
LinearFunction<Field> & _Guess;
|
|
||||||
public:
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
public:
|
||||||
// Wrap the usual normal equations trick
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
NormalEquations(SparseMatrixBase<Field> &Matrix, OperatorFunction<Field> &HermitianSolver,
|
|
||||||
LinearFunction<Field> &Guess)
|
|
||||||
: _Matrix(Matrix), _HermitianSolver(HermitianSolver), _Guess(Guess) {};
|
|
||||||
|
|
||||||
void operator() (const Field &in, Field &out){
|
/////////////////////////////////////////////////////
|
||||||
|
// Wrap the usual normal equations trick
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
NormalEquations(SparseMatrixBase<Field> &Matrix, OperatorFunction<Field> &HermitianSolver)
|
||||||
|
: _Matrix(Matrix), _HermitianSolver(HermitianSolver) {};
|
||||||
|
|
||||||
Field src(in.Grid());
|
void operator() (const Field &in, Field &out){
|
||||||
Field tmp(in.Grid());
|
|
||||||
|
|
||||||
MdagMLinearOperator<SparseMatrixBase<Field>,Field> MdagMOp(_Matrix);
|
Field src(in._grid);
|
||||||
_Matrix.Mdag(in,src);
|
|
||||||
_Guess(src,out);
|
|
||||||
_HermitianSolver(MdagMOp,src,out); // Mdag M out = Mdag in
|
|
||||||
|
|
||||||
}
|
_Matrix.Mdag(in,src);
|
||||||
};
|
_HermitianSolver(src,out); // Mdag M out = Mdag in
|
||||||
|
|
||||||
template<class Field> class NormalResidual : public LinearFunction<Field>{
|
}
|
||||||
private:
|
};
|
||||||
SparseMatrixBase<Field> & _Matrix;
|
|
||||||
OperatorFunction<Field> & _HermitianSolver;
|
|
||||||
LinearFunction<Field> & _Guess;
|
|
||||||
public:
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
}
|
||||||
// Wrap the usual normal equations trick
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
NormalResidual(SparseMatrixBase<Field> &Matrix, OperatorFunction<Field> &HermitianSolver,
|
|
||||||
LinearFunction<Field> &Guess)
|
|
||||||
: _Matrix(Matrix), _HermitianSolver(HermitianSolver), _Guess(Guess) {};
|
|
||||||
|
|
||||||
void operator() (const Field &in, Field &out){
|
|
||||||
|
|
||||||
Field res(in.Grid());
|
|
||||||
Field tmp(in.Grid());
|
|
||||||
|
|
||||||
MMdagLinearOperator<SparseMatrixBase<Field>,Field> MMdagOp(_Matrix);
|
|
||||||
_Guess(in,res);
|
|
||||||
_HermitianSolver(MMdagOp,in,res); // M Mdag res = in ;
|
|
||||||
_Matrix.Mdag(res,out); // out = Mdag res
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Field> class HPDSolver : public LinearFunction<Field> {
|
|
||||||
private:
|
|
||||||
LinearOperatorBase<Field> & _Matrix;
|
|
||||||
OperatorFunction<Field> & _HermitianSolver;
|
|
||||||
LinearFunction<Field> & _Guess;
|
|
||||||
public:
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// Wrap the usual normal equations trick
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
HPDSolver(LinearOperatorBase<Field> &Matrix,
|
|
||||||
OperatorFunction<Field> &HermitianSolver,
|
|
||||||
LinearFunction<Field> &Guess)
|
|
||||||
: _Matrix(Matrix), _HermitianSolver(HermitianSolver), _Guess(Guess) {};
|
|
||||||
|
|
||||||
void operator() (const Field &in, Field &out){
|
|
||||||
|
|
||||||
_Guess(in,out);
|
|
||||||
_HermitianSolver(_Matrix,in,out); //M out = in
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template<class Field> class MdagMSolver : public LinearFunction<Field> {
|
|
||||||
private:
|
|
||||||
SparseMatrixBase<Field> & _Matrix;
|
|
||||||
OperatorFunction<Field> & _HermitianSolver;
|
|
||||||
LinearFunction<Field> & _Guess;
|
|
||||||
public:
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// Wrap the usual normal equations trick
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
MdagMSolver(SparseMatrixBase<Field> &Matrix, OperatorFunction<Field> &HermitianSolver,
|
|
||||||
LinearFunction<Field> &Guess)
|
|
||||||
: _Matrix(Matrix), _HermitianSolver(HermitianSolver), _Guess(Guess) {};
|
|
||||||
|
|
||||||
void operator() (const Field &in, Field &out){
|
|
||||||
|
|
||||||
MdagMLinearOperator<SparseMatrixBase<Field>,Field> MdagMOp(_Matrix);
|
|
||||||
_Guess(in,out);
|
|
||||||
|
|
||||||
_HermitianSolver(MdagMOp,in,out); // Mdag M out = Mdag in
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
namespace Grid {
|
|
||||||
template<class Field> class PowerMethod
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
template<typename T> static RealD normalise(T& v)
|
|
||||||
{
|
|
||||||
RealD nn = norm2(v);
|
|
||||||
nn = sqrt(nn);
|
|
||||||
v = v * (1.0/nn);
|
|
||||||
return nn;
|
|
||||||
}
|
|
||||||
|
|
||||||
RealD operator()(LinearOperatorBase<Field> &HermOp, const Field &src)
|
|
||||||
{
|
|
||||||
GridBase *grid = src.Grid();
|
|
||||||
|
|
||||||
// quickly get an idea of the largest eigenvalue to more properly normalize the residuum
|
|
||||||
RealD evalMaxApprox = 0.0;
|
|
||||||
auto src_n = src;
|
|
||||||
auto tmp = src;
|
|
||||||
const int _MAX_ITER_EST_ = 200;
|
|
||||||
|
|
||||||
for (int i=0;i<_MAX_ITER_EST_;i++) {
|
|
||||||
|
|
||||||
normalise(src_n);
|
|
||||||
HermOp.HermOp(src_n,tmp);
|
|
||||||
RealD vnum = real(innerProduct(src_n,tmp)); // HermOp.
|
|
||||||
RealD vden = norm2(src_n);
|
|
||||||
RealD na = vnum/vden;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "PowerMethod: Current approximation of largest eigenvalue " << na << std::endl;
|
|
||||||
|
|
||||||
// if ( (fabs(evalMaxApprox/na - 1.0) < 0.0001) || (i==_MAX_ITER_EST_-1) ) {
|
|
||||||
// evalMaxApprox = na;
|
|
||||||
// return evalMaxApprox;
|
|
||||||
// }
|
|
||||||
evalMaxApprox = na;
|
|
||||||
src_n = tmp;
|
|
||||||
}
|
|
||||||
std::cout << GridLogMessage << " Approximation of largest eigenvalue: " << evalMaxApprox << std::endl;
|
|
||||||
return evalMaxApprox;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
namespace Grid {
|
|
||||||
|
|
||||||
class Band
|
|
||||||
{
|
|
||||||
RealD lo, hi;
|
|
||||||
public:
|
|
||||||
Band(RealD _lo,RealD _hi)
|
|
||||||
{
|
|
||||||
lo=_lo;
|
|
||||||
hi=_hi;
|
|
||||||
}
|
|
||||||
RealD operator() (RealD x){
|
|
||||||
if ( x>lo && x<hi ){
|
|
||||||
return 1.0;
|
|
||||||
} else {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class PowerSpectrum
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
template<typename T> static RealD normalise(T& v)
|
|
||||||
{
|
|
||||||
RealD nn = norm2(v);
|
|
||||||
nn = sqrt(nn);
|
|
||||||
v = v * (1.0/nn);
|
|
||||||
return nn;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<RealD> ranges;
|
|
||||||
std::vector<int> order;
|
|
||||||
|
|
||||||
PowerSpectrum( std::vector<RealD> &bins, std::vector<int> &_order ) : ranges(bins), order(_order) { };
|
|
||||||
|
|
||||||
template<class Field>
|
|
||||||
RealD operator()(LinearOperatorBase<Field> &HermOp, const Field &src)
|
|
||||||
{
|
|
||||||
GridBase *grid = src.Grid();
|
|
||||||
int N=ranges.size();
|
|
||||||
RealD hi = ranges[N-1];
|
|
||||||
|
|
||||||
RealD lo_band = 0.0;
|
|
||||||
RealD hi_band;
|
|
||||||
RealD nn=norm2(src);
|
|
||||||
RealD ss=0.0;
|
|
||||||
|
|
||||||
Field tmp = src;
|
|
||||||
|
|
||||||
for(int b=0;b<N;b++){
|
|
||||||
hi_band = ranges[b];
|
|
||||||
Band Notch(lo_band,hi_band);
|
|
||||||
|
|
||||||
Chebyshev<Field> polynomial;
|
|
||||||
polynomial.Init(0.0,hi,order[b],Notch);
|
|
||||||
polynomial.JacksonSmooth();
|
|
||||||
|
|
||||||
polynomial(HermOp,src,tmp) ;
|
|
||||||
|
|
||||||
RealD p=norm2(tmp);
|
|
||||||
ss=ss+p;
|
|
||||||
std::cout << GridLogMessage << " PowerSpectrum Band["<<lo_band<<","<<hi_band<<"] power "<<norm2(tmp)/nn<<std::endl;
|
|
||||||
|
|
||||||
lo_band=hi_band;
|
|
||||||
}
|
|
||||||
std::cout << GridLogMessage << " PowerSpectrum total power "<<ss/nn<<std::endl;
|
|
||||||
std::cout << GridLogMessage << " PowerSpectrum total power (unnormalised) "<<nn<<std::endl;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/*************************************************************************************
|
/*************************************************************************************
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
@@ -23,97 +23,97 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#ifndef GRID_PREC_CONJUGATE_RESIDUAL_H
|
#ifndef GRID_PREC_CONJUGATE_RESIDUAL_H
|
||||||
#define GRID_PREC_CONJUGATE_RESIDUAL_H
|
#define GRID_PREC_CONJUGATE_RESIDUAL_H
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////
|
||||||
// Base classes for iterative processes based on operators
|
// Base classes for iterative processes based on operators
|
||||||
// single input vec, single output vec.
|
// single input vec, single output vec.
|
||||||
/////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template<class Field>
|
template<class Field>
|
||||||
class PrecConjugateResidual : public OperatorFunction<Field> {
|
class PrecConjugateResidual : public OperatorFunction<Field> {
|
||||||
public:
|
public:
|
||||||
RealD Tolerance;
|
RealD Tolerance;
|
||||||
Integer MaxIterations;
|
Integer MaxIterations;
|
||||||
int verbose;
|
int verbose;
|
||||||
LinearFunction<Field> &Preconditioner;
|
LinearFunction<Field> &Preconditioner;
|
||||||
|
|
||||||
PrecConjugateResidual(RealD tol,Integer maxit,LinearFunction<Field> &Prec) : Tolerance(tol), MaxIterations(maxit), Preconditioner(Prec)
|
PrecConjugateResidual(RealD tol,Integer maxit,LinearFunction<Field> &Prec) : Tolerance(tol), MaxIterations(maxit), Preconditioner(Prec)
|
||||||
{
|
{
|
||||||
verbose=1;
|
verbose=1;
|
||||||
};
|
};
|
||||||
|
|
||||||
void operator() (LinearOperatorBase<Field> &Linop,const Field &src, Field &psi){
|
void operator() (LinearOperatorBase<Field> &Linop,const Field &src, Field &psi){
|
||||||
|
|
||||||
RealD a, b, c, d;
|
RealD a, b, c, d;
|
||||||
RealD cp, ssq,rsq;
|
RealD cp, ssq,rsq;
|
||||||
|
|
||||||
RealD rAr, rAAr, rArp;
|
RealD rAr, rAAr, rArp;
|
||||||
RealD pAp, pAAp;
|
RealD pAp, pAAp;
|
||||||
|
|
||||||
GridBase *grid = src.Grid();
|
GridBase *grid = src._grid;
|
||||||
Field r(grid), p(grid), Ap(grid), Ar(grid), z(grid);
|
Field r(grid), p(grid), Ap(grid), Ar(grid), z(grid);
|
||||||
|
|
||||||
psi=zero;
|
psi=zero;
|
||||||
r = src;
|
r = src;
|
||||||
Preconditioner(r,p);
|
Preconditioner(r,p);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Linop.HermOpAndNorm(p,Ap,pAp,pAAp);
|
Linop.HermOpAndNorm(p,Ap,pAp,pAAp);
|
||||||
Ar=Ap;
|
Ar=Ap;
|
||||||
rAr=pAp;
|
rAr=pAp;
|
||||||
rAAr=pAAp;
|
rAAr=pAAp;
|
||||||
|
|
||||||
cp =norm2(r);
|
cp =norm2(r);
|
||||||
ssq=norm2(src);
|
ssq=norm2(src);
|
||||||
rsq=Tolerance*Tolerance*ssq;
|
rsq=Tolerance*Tolerance*ssq;
|
||||||
|
|
||||||
if (verbose) std::cout<<GridLogMessage<<"PrecConjugateResidual: iteration " <<0<<" residual "<<cp<< " target"<< rsq<<std::endl;
|
if (verbose) std::cout<<GridLogMessage<<"PrecConjugateResidual: iteration " <<0<<" residual "<<cp<< " target"<< rsq<<std::endl;
|
||||||
|
|
||||||
for(int k=0;k<MaxIterations;k++){
|
for(int k=0;k<MaxIterations;k++){
|
||||||
|
|
||||||
|
|
||||||
Preconditioner(Ap,z);
|
Preconditioner(Ap,z);
|
||||||
RealD rq= real(innerProduct(Ap,z));
|
RealD rq= real(innerProduct(Ap,z));
|
||||||
|
|
||||||
a = rAr/rq;
|
a = rAr/rq;
|
||||||
|
|
||||||
axpy(psi,a,p,psi);
|
axpy(psi,a,p,psi);
|
||||||
cp = axpy_norm(r,-a,z,r);
|
cp = axpy_norm(r,-a,z,r);
|
||||||
|
|
||||||
rArp=rAr;
|
rArp=rAr;
|
||||||
|
|
||||||
Linop.HermOpAndNorm(r,Ar,rAr,rAAr);
|
Linop.HermOpAndNorm(r,Ar,rAr,rAAr);
|
||||||
|
|
||||||
b =rAr/rArp;
|
b =rAr/rArp;
|
||||||
|
|
||||||
axpy(p,b,p,r);
|
axpy(p,b,p,r);
|
||||||
pAAp=axpy_norm(Ap,b,Ap,Ar);
|
pAAp=axpy_norm(Ap,b,Ap,Ar);
|
||||||
|
|
||||||
if(verbose) std::cout<<GridLogMessage<<"PrecConjugateResidual: iteration " <<k<<" residual "<<cp<< " target"<< rsq<<std::endl;
|
if(verbose) std::cout<<GridLogMessage<<"PrecConjugateResidual: iteration " <<k<<" residual "<<cp<< " target"<< rsq<<std::endl;
|
||||||
|
|
||||||
|
if(cp<rsq) {
|
||||||
|
Linop.HermOp(psi,Ap);
|
||||||
|
axpy(r,-1.0,src,Ap);
|
||||||
|
RealD true_resid = norm2(r)/ssq;
|
||||||
|
std::cout<<GridLogMessage<<"PrecConjugateResidual: Converged on iteration " <<k
|
||||||
|
<< " computed residual "<<sqrt(cp/ssq)
|
||||||
|
<< " true residual "<<sqrt(true_resid)
|
||||||
|
<< " target " <<Tolerance <<std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(cp<rsq) {
|
|
||||||
Linop.HermOp(psi,Ap);
|
|
||||||
axpy(r,-1.0,src,Ap);
|
|
||||||
RealD true_resid = norm2(r)/ssq;
|
|
||||||
std::cout<<GridLogMessage<<"PrecConjugateResidual: Converged on iteration " <<k
|
|
||||||
<< " computed residual "<<sqrt(cp/ssq)
|
|
||||||
<< " true residual "<<sqrt(true_resid)
|
|
||||||
<< " target " <<Tolerance <<std::endl;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cout<<GridLogMessage<<"PrecConjugateResidual did NOT converge"<<std::endl;
|
||||||
|
assert(0);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
std::cout<<GridLogMessage<<"PrecConjugateResidual did NOT converge"<<std::endl;
|
}
|
||||||
GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/*************************************************************************************
|
/*************************************************************************************
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
Grid physics library, www.github.com/paboyle/Grid
|
||||||
|
|
||||||
@@ -24,8 +24,8 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#ifndef GRID_PREC_GCR_H
|
#ifndef GRID_PREC_GCR_H
|
||||||
#define GRID_PREC_GCR_H
|
#define GRID_PREC_GCR_H
|
||||||
|
|
||||||
@@ -36,205 +36,195 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
//NB. Likely not original reference since they are focussing on a preconditioner variant.
|
//NB. Likely not original reference since they are focussing on a preconditioner variant.
|
||||||
// but VPGCR was nicely written up in their paper
|
// but VPGCR was nicely written up in their paper
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
#define GCRLogLevel std::cout << GridLogMessage <<std::string(level,'\t')<< " Level "<<level<<" "
|
template<class Field>
|
||||||
|
class PrecGeneralisedConjugateResidual : public OperatorFunction<Field> {
|
||||||
|
public:
|
||||||
|
RealD Tolerance;
|
||||||
|
Integer MaxIterations;
|
||||||
|
int verbose;
|
||||||
|
int mmax;
|
||||||
|
int nstep;
|
||||||
|
int steps;
|
||||||
|
GridStopWatch PrecTimer;
|
||||||
|
GridStopWatch MatTimer;
|
||||||
|
GridStopWatch LinalgTimer;
|
||||||
|
|
||||||
template<class Field>
|
LinearFunction<Field> &Preconditioner;
|
||||||
class PrecGeneralisedConjugateResidual : public LinearFunction<Field> {
|
|
||||||
public:
|
|
||||||
using LinearFunction<Field>::operator();
|
|
||||||
RealD Tolerance;
|
|
||||||
Integer MaxIterations;
|
|
||||||
int verbose;
|
|
||||||
int mmax;
|
|
||||||
int nstep;
|
|
||||||
int steps;
|
|
||||||
int level;
|
|
||||||
GridStopWatch PrecTimer;
|
|
||||||
GridStopWatch MatTimer;
|
|
||||||
GridStopWatch LinalgTimer;
|
|
||||||
|
|
||||||
LinearFunction<Field> &Preconditioner;
|
PrecGeneralisedConjugateResidual(RealD tol,Integer maxit,LinearFunction<Field> &Prec,int _mmax,int _nstep) :
|
||||||
LinearOperatorBase<Field> &Linop;
|
Tolerance(tol),
|
||||||
|
MaxIterations(maxit),
|
||||||
|
Preconditioner(Prec),
|
||||||
|
mmax(_mmax),
|
||||||
|
nstep(_nstep)
|
||||||
|
{
|
||||||
|
verbose=1;
|
||||||
|
};
|
||||||
|
|
||||||
void Level(int lv) { level=lv; };
|
void operator() (LinearOperatorBase<Field> &Linop,const Field &src, Field &psi){
|
||||||
|
|
||||||
PrecGeneralisedConjugateResidual(RealD tol,Integer maxit,LinearOperatorBase<Field> &_Linop,LinearFunction<Field> &Prec,int _mmax,int _nstep) :
|
psi=zero;
|
||||||
Tolerance(tol),
|
RealD cp, ssq,rsq;
|
||||||
MaxIterations(maxit),
|
ssq=norm2(src);
|
||||||
Linop(_Linop),
|
rsq=Tolerance*Tolerance*ssq;
|
||||||
Preconditioner(Prec),
|
|
||||||
mmax(_mmax),
|
|
||||||
nstep(_nstep)
|
|
||||||
{
|
|
||||||
level=1;
|
|
||||||
verbose=1;
|
|
||||||
};
|
|
||||||
|
|
||||||
void operator() (const Field &src, Field &psi){
|
Field r(src._grid);
|
||||||
|
|
||||||
psi=Zero();
|
PrecTimer.Reset();
|
||||||
RealD cp, ssq,rsq;
|
MatTimer.Reset();
|
||||||
ssq=norm2(src);
|
LinalgTimer.Reset();
|
||||||
rsq=Tolerance*Tolerance*ssq;
|
|
||||||
|
|
||||||
Field r(src.Grid());
|
GridStopWatch SolverTimer;
|
||||||
|
SolverTimer.Start();
|
||||||
|
|
||||||
PrecTimer.Reset();
|
steps=0;
|
||||||
MatTimer.Reset();
|
for(int k=0;k<MaxIterations;k++){
|
||||||
LinalgTimer.Reset();
|
|
||||||
|
|
||||||
GridStopWatch SolverTimer;
|
cp=GCRnStep(Linop,src,psi,rsq);
|
||||||
SolverTimer.Start();
|
|
||||||
|
|
||||||
steps=0;
|
std::cout<<GridLogMessage<<"VPGCR("<<mmax<<","<<nstep<<") "<< steps <<" steps cp = "<<cp<<std::endl;
|
||||||
for(int k=0;k<MaxIterations;k++){
|
|
||||||
|
|
||||||
cp=GCRnStep(src,psi,rsq);
|
if(cp<rsq) {
|
||||||
|
|
||||||
GCRLogLevel <<"PGCR("<<mmax<<","<<nstep<<") "<< steps <<" steps cp = "<<cp<<" target "<<rsq <<std::endl;
|
SolverTimer.Stop();
|
||||||
|
|
||||||
if(cp<rsq) {
|
Linop.HermOp(psi,r);
|
||||||
|
axpy(r,-1.0,src,r);
|
||||||
|
RealD tr = norm2(r);
|
||||||
|
std::cout<<GridLogMessage<<"PrecGeneralisedConjugateResidual: Converged on iteration " <<steps
|
||||||
|
<< " computed residual "<<sqrt(cp/ssq)
|
||||||
|
<< " true residual " <<sqrt(tr/ssq)
|
||||||
|
<< " target " <<Tolerance <<std::endl;
|
||||||
|
|
||||||
SolverTimer.Stop();
|
std::cout<<GridLogMessage<<"VPGCR Time elapsed: Total "<< SolverTimer.Elapsed() <<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"VPGCR Time elapsed: Precon "<< PrecTimer.Elapsed() <<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"VPGCR Time elapsed: Matrix "<< MatTimer.Elapsed() <<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<"VPGCR Time elapsed: Linalg "<< LinalgTimer.Elapsed() <<std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Linop.HermOp(psi,r);
|
|
||||||
axpy(r,-1.0,src,r);
|
|
||||||
RealD tr = norm2(r);
|
|
||||||
GCRLogLevel<<"PGCR: Converged on iteration " <<steps
|
|
||||||
<< " computed residual "<<sqrt(cp/ssq)
|
|
||||||
<< " true residual " <<sqrt(tr/ssq)
|
|
||||||
<< " target " <<Tolerance <<std::endl;
|
|
||||||
|
|
||||||
GCRLogLevel<<"PGCR Time elapsed: Total "<< SolverTimer.Elapsed() <<std::endl;
|
|
||||||
/*
|
|
||||||
GCRLogLevel<<"PGCR Time elapsed: Precon "<< PrecTimer.Elapsed() <<std::endl;
|
|
||||||
GCRLogLevel<<"PGCR Time elapsed: Matrix "<< MatTimer.Elapsed() <<std::endl;
|
|
||||||
GCRLogLevel<<"PGCR Time elapsed: Linalg "<< LinalgTimer.Elapsed() <<std::endl;
|
|
||||||
*/
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
std::cout<<GridLogMessage<<"Variable Preconditioned GCR did not converge"<<std::endl;
|
||||||
|
assert(0);
|
||||||
}
|
}
|
||||||
GCRLogLevel<<"Variable Preconditioned GCR did not converge"<<std::endl;
|
|
||||||
// GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
RealD GCRnStep(const Field &src, Field &psi,RealD rsq){
|
RealD GCRnStep(LinearOperatorBase<Field> &Linop,const Field &src, Field &psi,RealD rsq){
|
||||||
|
|
||||||
RealD cp;
|
RealD cp;
|
||||||
RealD a, b;
|
RealD a, b, c, d;
|
||||||
RealD zAz, zAAz;
|
RealD zAz, zAAz;
|
||||||
RealD rq;
|
RealD rAq, rq;
|
||||||
|
|
||||||
GridBase *grid = src.Grid();
|
GridBase *grid = src._grid;
|
||||||
|
|
||||||
Field r(grid);
|
Field r(grid);
|
||||||
Field z(grid);
|
Field z(grid);
|
||||||
Field tmp(grid);
|
Field tmp(grid);
|
||||||
Field ttmp(grid);
|
Field ttmp(grid);
|
||||||
Field Az(grid);
|
Field Az(grid);
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
// history for flexible orthog
|
// history for flexible orthog
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
std::vector<Field> q(mmax,grid);
|
std::vector<Field> q(mmax,grid);
|
||||||
std::vector<Field> p(mmax,grid);
|
std::vector<Field> p(mmax,grid);
|
||||||
std::vector<RealD> qq(mmax);
|
std::vector<RealD> qq(mmax);
|
||||||
|
|
||||||
GCRLogLevel<< "PGCR nStep("<<nstep<<")"<<std::endl;
|
|
||||||
|
|
||||||
//////////////////////////////////
|
|
||||||
// initial guess x0 is taken as nonzero.
|
|
||||||
// r0=src-A x0 = src
|
|
||||||
//////////////////////////////////
|
|
||||||
MatTimer.Start();
|
|
||||||
Linop.HermOpAndNorm(psi,Az,zAz,zAAz);
|
|
||||||
MatTimer.Stop();
|
|
||||||
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
r=src-Az;
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
GCRLogLevel<< "PGCR true residual r = src - A psi "<<norm2(r) <<std::endl;
|
|
||||||
|
|
||||||
/////////////////////
|
|
||||||
// p = Prec(r)
|
|
||||||
/////////////////////
|
|
||||||
|
|
||||||
PrecTimer.Start();
|
|
||||||
Preconditioner(r,z);
|
|
||||||
PrecTimer.Stop();
|
|
||||||
|
|
||||||
MatTimer.Start();
|
|
||||||
Linop.HermOpAndNorm(z,Az,zAz,zAAz);
|
|
||||||
MatTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
|
|
||||||
//p[0],q[0],qq[0]
|
|
||||||
p[0]= z;
|
|
||||||
q[0]= Az;
|
|
||||||
qq[0]= zAAz;
|
|
||||||
|
|
||||||
cp =norm2(r);
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
for(int k=0;k<nstep;k++){
|
|
||||||
|
|
||||||
steps++;
|
|
||||||
|
|
||||||
int kp = k+1;
|
|
||||||
int peri_k = k %mmax;
|
|
||||||
int peri_kp= kp%mmax;
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
rq= real(innerProduct(r,q[peri_k])); // what if rAr not real?
|
|
||||||
a = rq/qq[peri_k];
|
|
||||||
|
|
||||||
axpy(psi,a,p[peri_k],psi);
|
|
||||||
|
|
||||||
cp = axpy_norm(r,-a,q[peri_k],r);
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
|
|
||||||
GCRLogLevel<< "PGCR step["<<steps<<"] resid " << cp << " target " <<rsq<<std::endl;
|
|
||||||
|
|
||||||
if((k==nstep-1)||(cp<rsq)){
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
// initial guess x0 is taken as nonzero.
|
||||||
|
// r0=src-A x0 = src
|
||||||
|
//////////////////////////////////
|
||||||
|
MatTimer.Start();
|
||||||
|
Linop.HermOpAndNorm(psi,Az,zAz,zAAz);
|
||||||
|
MatTimer.Stop();
|
||||||
|
r=src-Az;
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
// p = Prec(r)
|
||||||
|
/////////////////////
|
||||||
PrecTimer.Start();
|
PrecTimer.Start();
|
||||||
Preconditioner(r,z);// solve Az = r
|
Preconditioner(r,z);
|
||||||
PrecTimer.Stop();
|
PrecTimer.Stop();
|
||||||
|
|
||||||
|
MatTimer.Start();
|
||||||
|
Linop.HermOp(z,tmp);
|
||||||
|
MatTimer.Stop();
|
||||||
|
|
||||||
|
ttmp=tmp;
|
||||||
|
tmp=tmp-r;
|
||||||
|
|
||||||
|
/*
|
||||||
|
std::cout<<GridLogMessage<<r<<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<z<<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<ttmp<<std::endl;
|
||||||
|
std::cout<<GridLogMessage<<tmp<<std::endl;
|
||||||
|
*/
|
||||||
|
|
||||||
MatTimer.Start();
|
MatTimer.Start();
|
||||||
Linop.HermOpAndNorm(z,Az,zAz,zAAz);
|
Linop.HermOpAndNorm(z,Az,zAz,zAAz);
|
||||||
MatTimer.Stop();
|
MatTimer.Stop();
|
||||||
|
|
||||||
LinalgTimer.Start();
|
//p[0],q[0],qq[0]
|
||||||
|
p[0]= z;
|
||||||
|
q[0]= Az;
|
||||||
|
qq[0]= zAAz;
|
||||||
|
|
||||||
q[peri_kp]=Az;
|
cp =norm2(r);
|
||||||
p[peri_kp]=z;
|
|
||||||
|
|
||||||
int northog = ((kp)>(mmax-1))?(mmax-1):(kp); // if more than mmax done, we orthog all mmax history.
|
for(int k=0;k<nstep;k++){
|
||||||
for(int back=0;back<northog;back++){
|
|
||||||
|
|
||||||
int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0);
|
steps++;
|
||||||
|
|
||||||
|
int kp = k+1;
|
||||||
|
int peri_k = k %mmax;
|
||||||
|
int peri_kp= kp%mmax;
|
||||||
|
|
||||||
|
rq= real(innerProduct(r,q[peri_k])); // what if rAr not real?
|
||||||
|
a = rq/qq[peri_k];
|
||||||
|
|
||||||
|
axpy(psi,a,p[peri_k],psi);
|
||||||
|
|
||||||
|
cp = axpy_norm(r,-a,q[peri_k],r);
|
||||||
|
|
||||||
|
if((k==nstep-1)||(cp<rsq)){
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout<<GridLogMessage<< " VPGCR_step["<<steps<<"] resid " <<sqrt(cp/rsq)<<std::endl;
|
||||||
|
|
||||||
|
PrecTimer.Start();
|
||||||
|
Preconditioner(r,z);// solve Az = r
|
||||||
|
PrecTimer.Stop();
|
||||||
|
|
||||||
|
MatTimer.Start();
|
||||||
|
Linop.HermOpAndNorm(z,Az,zAz,zAAz);
|
||||||
|
Linop.HermOp(z,tmp);
|
||||||
|
MatTimer.Stop();
|
||||||
|
tmp=tmp-r;
|
||||||
|
std::cout<<GridLogMessage<< " Preconditioner resid " <<sqrt(norm2(tmp)/norm2(r))<<std::endl;
|
||||||
|
|
||||||
|
q[peri_kp]=Az;
|
||||||
|
p[peri_kp]=z;
|
||||||
|
|
||||||
|
int northog = ((kp)>(mmax-1))?(mmax-1):(kp); // if more than mmax done, we orthog all mmax history.
|
||||||
|
for(int back=0;back<northog;back++){
|
||||||
|
|
||||||
|
int peri_back=(k-back)%mmax; assert((k-back)>=0);
|
||||||
|
|
||||||
|
b=-real(innerProduct(q[peri_back],Az))/qq[peri_back];
|
||||||
|
p[peri_kp]=p[peri_kp]+b*p[peri_back];
|
||||||
|
q[peri_kp]=q[peri_kp]+b*q[peri_back];
|
||||||
|
|
||||||
|
}
|
||||||
|
qq[peri_kp]=norm2(q[peri_kp]); // could use axpy_norm
|
||||||
|
|
||||||
b=-real(innerProduct(q[peri_back],Az))/qq[peri_back];
|
|
||||||
p[peri_kp]=p[peri_kp]+b*p[peri_back];
|
|
||||||
q[peri_kp]=q[peri_kp]+b*q[peri_back];
|
|
||||||
|
|
||||||
}
|
}
|
||||||
qq[peri_kp]=norm2(q[peri_kp]); // could use axpy_norm
|
assert(0); // never reached
|
||||||
LinalgTimer.Stop();
|
return cp;
|
||||||
}
|
}
|
||||||
GRID_ASSERT(0); // never reached
|
};
|
||||||
return cp;
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#undef GCRLogLevel
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,296 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/iterative/PrecGeneralisedConjugateResidual.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
|
||||||
Author: Peter Boyle <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_PREC_GCR_NON_HERM_H
|
|
||||||
#define GRID_PREC_GCR_NON_HERM_H
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//VPGCR Abe and Zhang, 2005.
|
|
||||||
//INTERNATIONAL JOURNAL OF NUMERICAL ANALYSIS AND MODELING
|
|
||||||
//Computing and Information Volume 2, Number 2, Pages 147-161
|
|
||||||
//NB. Likely not original reference since they are focussing on a preconditioner variant.
|
|
||||||
// but VPGCR was nicely written up in their paper
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
#define GCRLogLevel std::cout << GridLogMessage <<std::string(level,'\t')<< name<<" "
|
|
||||||
|
|
||||||
template<class Field>
|
|
||||||
class PrecGeneralisedConjugateResidualNonHermitian : public LinearFunction<Field> {
|
|
||||||
public:
|
|
||||||
using LinearFunction<Field>::operator();
|
|
||||||
RealD Tolerance;
|
|
||||||
RealD SSQ;
|
|
||||||
Integer MaxIterations;
|
|
||||||
int verbose;
|
|
||||||
int mmax;
|
|
||||||
int nstep;
|
|
||||||
int steps;
|
|
||||||
int level;
|
|
||||||
GridStopWatch PrecTimer;
|
|
||||||
GridStopWatch MatTimer;
|
|
||||||
GridStopWatch LinalgTimer;
|
|
||||||
std::string name;
|
|
||||||
int ZeroGuess = 0; // caller contract: guess is always zero => first-cycle r0 = src, skip the apply
|
|
||||||
// persistent GCR history (see GCRnStep)
|
|
||||||
GridBase *hist_grid = nullptr;
|
|
||||||
std::vector<Field> q;
|
|
||||||
std::vector<Field> p;
|
|
||||||
std::vector<RealD> qq;
|
|
||||||
int FirstCycle = 0;
|
|
||||||
|
|
||||||
LinearFunction<Field> &Preconditioner;
|
|
||||||
LinearOperatorBase<Field> &Linop;
|
|
||||||
|
|
||||||
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; };
|
|
||||||
// Coefficient logging: one line per step with the step length a_k and the
|
|
||||||
// orthogonalisation coefficients b_{k,j}. These are the data from which a
|
|
||||||
// FIXED polynomial smoother can be harvested: if they are stable from call
|
|
||||||
// to call, the adaptive GCR can be replaced by a stationary p(A) with the
|
|
||||||
// same applies and no reductions. Off by default; boss rank prints.
|
|
||||||
int LogCoeffs = 0;
|
|
||||||
void LogCoefficients(int l) { LogCoeffs = l; };
|
|
||||||
|
|
||||||
PrecGeneralisedConjugateResidualNonHermitian(RealD tol,Integer maxit,LinearOperatorBase<Field> &_Linop,LinearFunction<Field> &Prec,int _mmax,int _nstep) :
|
|
||||||
Tolerance(tol),
|
|
||||||
MaxIterations(maxit),
|
|
||||||
Linop(_Linop),
|
|
||||||
Preconditioner(Prec),
|
|
||||||
mmax(_mmax),
|
|
||||||
nstep(_nstep)
|
|
||||||
{
|
|
||||||
Level(1);
|
|
||||||
verbose=1;
|
|
||||||
};
|
|
||||||
|
|
||||||
void operator() (const Field &src, Field &psi){
|
|
||||||
|
|
||||||
// psi=Zero();
|
|
||||||
RealD cp, ssq,rsq;
|
|
||||||
ssq=norm2(src);
|
|
||||||
SSQ=ssq;
|
|
||||||
rsq=Tolerance*Tolerance*ssq;
|
|
||||||
|
|
||||||
Field r(src.Grid());
|
|
||||||
|
|
||||||
PrecTimer.Reset();
|
|
||||||
MatTimer.Reset();
|
|
||||||
LinalgTimer.Reset();
|
|
||||||
|
|
||||||
GridStopWatch SolverTimer;
|
|
||||||
SolverTimer.Start();
|
|
||||||
|
|
||||||
steps=0;
|
|
||||||
FirstCycle=1;
|
|
||||||
for(int k=0;k<MaxIterations;k++){
|
|
||||||
|
|
||||||
cp=GCRnStep(src,psi,rsq);
|
|
||||||
|
|
||||||
GCRLogLevel <<"PGCR("<<mmax<<","<<nstep<<") "<< steps <<" steps cp = "<<sqrt(cp/ssq)<<" target "<<sqrt(rsq/ssq) <<std::endl;
|
|
||||||
|
|
||||||
if(cp<rsq) {
|
|
||||||
|
|
||||||
SolverTimer.Stop();
|
|
||||||
|
|
||||||
Linop.Op(psi,r);
|
|
||||||
axpy(r,-1.0,src,r);
|
|
||||||
RealD tr = norm2(r);
|
|
||||||
GCRLogLevel<<"PGCR: Converged on iteration " <<steps
|
|
||||||
<< " computed residual "<<sqrt(cp/ssq)
|
|
||||||
<< " true residual " <<sqrt(tr/ssq)
|
|
||||||
<< " target " <<Tolerance <<std::endl;
|
|
||||||
|
|
||||||
GCRLogLevel<<"PGCR Time elapsed: Total "<< SolverTimer.Elapsed() <<std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
GCRLogLevel<<"Variable Preconditioned GCR did not converge"<<std::endl;
|
|
||||||
// GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
RealD GCRnStep(const Field &src, Field &psi,RealD rsq){
|
|
||||||
|
|
||||||
RealD cp;
|
|
||||||
ComplexD a, b;
|
|
||||||
ComplexD rq;
|
|
||||||
|
|
||||||
GridBase *grid = src.Grid();
|
|
||||||
|
|
||||||
// Only r and one scratch for the restart residual; the new p/q directions
|
|
||||||
// are produced directly in their persistent history slots.
|
|
||||||
Field r(grid);
|
|
||||||
Field Az(grid);
|
|
||||||
|
|
||||||
////////////////////////////////
|
|
||||||
// history for flexible orthog
|
|
||||||
////////////////////////////////
|
|
||||||
// History arrays are PERSISTENT across calls (allocated once per grid,
|
|
||||||
// re-made only if the grid or mmax changes). The per-call form
|
|
||||||
// std::vector<Field>(mmax,grid) built a temporary and copy-constructed it
|
|
||||||
// mmax times on every restart cycle -- measured ~5 ms per fine-smoother
|
|
||||||
// call. Safe: every entry is written before it is read within a cycle
|
|
||||||
// (q[kp],p[kp] assigned before the northog loop can reach them), so no
|
|
||||||
// stale content is ever consumed.
|
|
||||||
if ( hist_grid != grid || (int)q.size() != mmax ) {
|
|
||||||
q.clear(); p.clear();
|
|
||||||
q.reserve(mmax); p.reserve(mmax);
|
|
||||||
for(int i=0;i<mmax;i++){ q.emplace_back(grid); p.emplace_back(grid); }
|
|
||||||
qq.assign(mmax, 0.0);
|
|
||||||
hist_grid = grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
GCRLogLevel<< "PGCR nStep("<<nstep<<")"<<std::endl;
|
|
||||||
|
|
||||||
//////////////////////////////////
|
|
||||||
// r0 = src - A x0. ZeroGuess: on the first cycle x0==0 by caller
|
|
||||||
// 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();
|
|
||||||
Linop.Op(psi,Az);
|
|
||||||
MatTimer.Stop();
|
|
||||||
LinalgTimer.Start();
|
|
||||||
r=src-Az;
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
}
|
|
||||||
FirstCycle=0;
|
|
||||||
|
|
||||||
/////////////////////
|
|
||||||
// p = Prec(r)
|
|
||||||
/////////////////////
|
|
||||||
|
|
||||||
// p[0] = Prec(r), q[0] = A p[0], written straight into the history slots
|
|
||||||
PrecTimer.Start();
|
|
||||||
Preconditioner(r,p[0]);
|
|
||||||
PrecTimer.Stop();
|
|
||||||
|
|
||||||
MatTimer.Start();
|
|
||||||
Linop.Op(p[0],q[0]);
|
|
||||||
MatTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
|
|
||||||
qq[0]= norm2(q[0]);
|
|
||||||
|
|
||||||
cp =norm2(r);
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
GCRLogLevel<< "PGCR true residual "<< sqrt(cp/SSQ) <<std::endl;
|
|
||||||
|
|
||||||
for(int k=0;k<nstep;k++){
|
|
||||||
GRID_TRACE("PGCR_step");
|
|
||||||
steps++;
|
|
||||||
|
|
||||||
int kp = k+1;
|
|
||||||
int peri_k = k %mmax;
|
|
||||||
int peri_kp= kp%mmax;
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
rq= innerProduct(q[peri_k],r); // what if rAr not real?
|
|
||||||
a = rq/qq[peri_k];
|
|
||||||
|
|
||||||
axpy(psi,a,p[peri_k],psi);
|
|
||||||
|
|
||||||
cp = axpy_norm(r,-a,q[peri_k],r);
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
if ( LogCoeffs ) {
|
|
||||||
GCRLogLevel<<"coeff["<<k<<"] a=("<<real(a)<<","<<imag(a)<<")"
|
|
||||||
<<" |r|/|r0|="<<sqrt(cp/SSQ)<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
GCRLogLevel<< "PGCR step["<<steps<<"] resid " << sqrt(cp/SSQ)<<std::endl;
|
|
||||||
|
|
||||||
if((k==nstep-1)||(cp<rsq)){
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// New direction written straight into its history slot: p = Prec(r), q = A p.
|
|
||||||
PrecTimer.Start();
|
|
||||||
Preconditioner(r,p[peri_kp]);
|
|
||||||
PrecTimer.Stop();
|
|
||||||
|
|
||||||
MatTimer.Start();
|
|
||||||
Linop.Op(p[peri_kp],q[peri_kp]);
|
|
||||||
MatTimer.Stop();
|
|
||||||
|
|
||||||
LinalgTimer.Start();
|
|
||||||
|
|
||||||
int northog = ((kp)>(mmax-1))?(mmax-1):(kp); // if more than mmax done, we orthog all mmax history.
|
|
||||||
std::ostringstream bs;
|
|
||||||
|
|
||||||
// Classical Gram-Schmidt: every coefficient is taken against the
|
|
||||||
// UN-updated new q (so all northog inner products are independent and
|
|
||||||
// batchable), then the window is applied. The coefficient is complex:
|
|
||||||
// for a non-Hermitian operator <q_j,Aq> is complex and keeping only the
|
|
||||||
// real part left the q's non-orthogonal.
|
|
||||||
// Batched: one fused kernel + one reduction for all coefficients,
|
|
||||||
// one fused pass per update (independent of mmax).
|
|
||||||
std::vector<const Field*> qwin(northog), pwin(northog);
|
|
||||||
for(int back=0;back<northog;back++){
|
|
||||||
int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0);
|
|
||||||
GRID_ASSERT(peri_back!=peri_kp);
|
|
||||||
qwin[back] = &q[peri_back];
|
|
||||||
pwin[back] = &p[peri_back];
|
|
||||||
}
|
|
||||||
std::vector<ComplexD> bcoef;
|
|
||||||
innerProductMulti(bcoef,qwin,q[peri_kp]);
|
|
||||||
for(int back=0;back<northog;back++){
|
|
||||||
int peri_back=(k-back)%mmax;
|
|
||||||
bcoef[back] = -bcoef[back]/qq[peri_back];
|
|
||||||
if ( LogCoeffs ) bs<<" b["<<back<<"]="<<bcoef[back];
|
|
||||||
}
|
|
||||||
if ( northog ) {
|
|
||||||
axpyMulti(p[peri_kp],bcoef,pwin);
|
|
||||||
qq[peri_kp]=axpyMultiNorm(q[peri_kp],bcoef,qwin);
|
|
||||||
} else {
|
|
||||||
qq[peri_kp]=norm2(q[peri_kp]);
|
|
||||||
}
|
|
||||||
if ( LogCoeffs && northog ) {
|
|
||||||
GCRLogLevel<<"coeff["<<k<<"]"<<bs.str()<<std::endl;
|
|
||||||
}
|
|
||||||
qq[peri_kp]=norm2(q[peri_kp]); // could use axpy_norm
|
|
||||||
LinalgTimer.Stop();
|
|
||||||
}
|
|
||||||
GRID_ASSERT(0); // never reached
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
#undef GCRLogLevel
|
|
||||||
#endif
|
|
||||||
@@ -1,371 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithmsf/iterative/QuasiMinimalResidual.h
|
|
||||||
|
|
||||||
Copyright (C) 2019
|
|
||||||
|
|
||||||
Author: Peter Boyle <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 */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
template<class Field>
|
|
||||||
RealD innerG5ProductReal(Field &l, Field &r)
|
|
||||||
{
|
|
||||||
Gamma G5(Gamma::Algebra::Gamma5);
|
|
||||||
Field tmp(l.Grid());
|
|
||||||
// tmp = G5*r;
|
|
||||||
G5R5(tmp,r);
|
|
||||||
ComplexD ip =innerProduct(l,tmp);
|
|
||||||
std::cout << "innerProductRealG5R5 "<<ip<<std::endl;
|
|
||||||
return ip.real();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Field>
|
|
||||||
class QuasiMinimalResidual : public OperatorFunction<Field> {
|
|
||||||
public:
|
|
||||||
using OperatorFunction<Field>::operator();
|
|
||||||
|
|
||||||
bool ErrorOnNoConverge;
|
|
||||||
RealD Tolerance;
|
|
||||||
Integer MaxIterations;
|
|
||||||
Integer IterationCount;
|
|
||||||
|
|
||||||
QuasiMinimalResidual(RealD tol,
|
|
||||||
Integer maxit,
|
|
||||||
bool err_on_no_conv = true)
|
|
||||||
: Tolerance(tol)
|
|
||||||
, MaxIterations(maxit)
|
|
||||||
, ErrorOnNoConverge(err_on_no_conv)
|
|
||||||
{};
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
void operator()(LinearOperatorBase<Field> &LinOp, const Field &b, Field &x)
|
|
||||||
{
|
|
||||||
RealD resid;
|
|
||||||
IterationCount=0;
|
|
||||||
|
|
||||||
RealD rho, rho_1, xi, gamma, gamma_1, theta, theta_1;
|
|
||||||
RealD eta, delta, ep, beta;
|
|
||||||
|
|
||||||
GridBase *Grid = b.Grid();
|
|
||||||
Field r(Grid), d(Grid), s(Grid);
|
|
||||||
Field v(Grid), w(Grid), y(Grid), z(Grid);
|
|
||||||
Field v_tld(Grid), w_tld(Grid), y_tld(Grid), z_tld(Grid);
|
|
||||||
Field p(Grid), q(Grid), p_tld(Grid);
|
|
||||||
|
|
||||||
Real normb = norm2(b);
|
|
||||||
|
|
||||||
LinOp.Op(x,r); r = b - r;
|
|
||||||
|
|
||||||
GRID_ASSERT(normb> 0.0);
|
|
||||||
|
|
||||||
resid = norm2(r)/normb;
|
|
||||||
if (resid <= Tolerance) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
v_tld = r;
|
|
||||||
y = v_tld;
|
|
||||||
rho = norm2(y);
|
|
||||||
|
|
||||||
// Take Gamma5 conjugate
|
|
||||||
// Gamma G5(Gamma::Algebra::Gamma5);
|
|
||||||
// G5R5(w_tld,r);
|
|
||||||
// w_tld = G5* v_tld;
|
|
||||||
w_tld=v_tld;
|
|
||||||
z = w_tld;
|
|
||||||
xi = norm2(z);
|
|
||||||
|
|
||||||
gamma = 1.0;
|
|
||||||
eta = -1.0;
|
|
||||||
theta = 0.0;
|
|
||||||
|
|
||||||
for (int i = 1; i <= MaxIterations; i++) {
|
|
||||||
|
|
||||||
// Breakdown tests
|
|
||||||
GRID_ASSERT( rho != 0.0);
|
|
||||||
GRID_ASSERT( xi != 0.0);
|
|
||||||
|
|
||||||
v = (1. / rho) * v_tld;
|
|
||||||
y = (1. / rho) * y;
|
|
||||||
|
|
||||||
w = (1. / xi) * w_tld;
|
|
||||||
z = (1. / xi) * z;
|
|
||||||
|
|
||||||
ComplexD Zdelta = innerProduct(z, y); // Complex?
|
|
||||||
std::cout << "Zdelta "<<Zdelta<<std::endl;
|
|
||||||
delta = Zdelta.real();
|
|
||||||
|
|
||||||
y_tld = y;
|
|
||||||
z_tld = z;
|
|
||||||
|
|
||||||
if (i > 1) {
|
|
||||||
p = y_tld - (xi * delta / ep) * p;
|
|
||||||
q = z_tld - (rho * delta / ep) * q;
|
|
||||||
} else {
|
|
||||||
p = y_tld;
|
|
||||||
q = z_tld;
|
|
||||||
}
|
|
||||||
|
|
||||||
LinOp.Op(p,p_tld); // p_tld = A * p;
|
|
||||||
ComplexD Zep = innerProduct(q, p_tld);
|
|
||||||
ep=Zep.real();
|
|
||||||
std::cout << "Zep "<<Zep <<std::endl;
|
|
||||||
// Complex Audit
|
|
||||||
GRID_ASSERT(abs(ep)>0);
|
|
||||||
|
|
||||||
beta = ep / delta;
|
|
||||||
GRID_ASSERT(abs(beta)>0);
|
|
||||||
|
|
||||||
v_tld = p_tld - beta * v;
|
|
||||||
y = v_tld;
|
|
||||||
|
|
||||||
rho_1 = rho;
|
|
||||||
rho = norm2(y);
|
|
||||||
LinOp.AdjOp(q,w_tld);
|
|
||||||
w_tld = w_tld - beta * w;
|
|
||||||
z = w_tld;
|
|
||||||
|
|
||||||
xi = norm2(z);
|
|
||||||
|
|
||||||
gamma_1 = gamma;
|
|
||||||
theta_1 = theta;
|
|
||||||
|
|
||||||
theta = rho / (gamma_1 * beta);
|
|
||||||
gamma = 1.0 / sqrt(1.0 + theta * theta);
|
|
||||||
std::cout << "theta "<<theta<<std::endl;
|
|
||||||
std::cout << "gamma "<<gamma<<std::endl;
|
|
||||||
|
|
||||||
GRID_ASSERT(abs(gamma)> 0.0);
|
|
||||||
|
|
||||||
eta = -eta * rho_1 * gamma* gamma / (beta * gamma_1 * gamma_1);
|
|
||||||
|
|
||||||
if (i > 1) {
|
|
||||||
d = eta * p + (theta_1 * theta_1 * gamma * gamma) * d;
|
|
||||||
s = eta * p_tld + (theta_1 * theta_1 * gamma * gamma) * s;
|
|
||||||
} else {
|
|
||||||
d = eta * p;
|
|
||||||
s = eta * p_tld;
|
|
||||||
}
|
|
||||||
|
|
||||||
x =x+d; // update approximation vector
|
|
||||||
r =r-s; // compute residual
|
|
||||||
|
|
||||||
if ((resid = norm2(r) / normb) <= Tolerance) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::cout << "Iteration "<<i<<" resid " << resid<<std::endl;
|
|
||||||
}
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
return; // no convergence
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// QMRg5 SMP thesis
|
|
||||||
void operator()(LinearOperatorBase<Field> &LinOp, const Field &b, Field &x)
|
|
||||||
{
|
|
||||||
// Real scalars
|
|
||||||
GridBase *grid = b.Grid();
|
|
||||||
|
|
||||||
Field r(grid);
|
|
||||||
Field p_m(grid), p_m_minus_1(grid), p_m_minus_2(grid);
|
|
||||||
Field v_m(grid), v_m_minus_1(grid), v_m_plus_1(grid);
|
|
||||||
Field tmp(grid);
|
|
||||||
|
|
||||||
RealD w;
|
|
||||||
RealD z1, z2;
|
|
||||||
RealD delta_m, delta_m_minus_1;
|
|
||||||
RealD c_m_plus_1, c_m, c_m_minus_1;
|
|
||||||
RealD s_m_plus_1, s_m, s_m_minus_1;
|
|
||||||
RealD alpha, beta, gamma, epsilon;
|
|
||||||
RealD mu, nu, rho, theta, xi, chi;
|
|
||||||
RealD mod2r, mod2b;
|
|
||||||
RealD tau2, target2;
|
|
||||||
|
|
||||||
mod2b=norm2(b);
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
// Initial residual
|
|
||||||
/////////////////////////
|
|
||||||
LinOp.Op(x,tmp);
|
|
||||||
r = b - tmp;
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
// \mu = \rho = |r_0|
|
|
||||||
/////////////////////////
|
|
||||||
mod2r = norm2(r);
|
|
||||||
rho = sqrt( mod2r);
|
|
||||||
mu=rho;
|
|
||||||
|
|
||||||
std::cout << "QuasiMinimalResidual rho "<< rho<<std::endl;
|
|
||||||
/////////////////////////
|
|
||||||
// Zero negative history
|
|
||||||
/////////////////////////
|
|
||||||
v_m_plus_1 = Zero();
|
|
||||||
v_m_minus_1 = Zero();
|
|
||||||
p_m_minus_1 = Zero();
|
|
||||||
p_m_minus_2 = Zero();
|
|
||||||
|
|
||||||
// v0
|
|
||||||
v_m = (1.0/rho)*r;
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
// Initial coeffs
|
|
||||||
/////////////////////////
|
|
||||||
delta_m_minus_1 = 1.0;
|
|
||||||
c_m_minus_1 = 1.0;
|
|
||||||
c_m = 1.0;
|
|
||||||
s_m_minus_1 = 0.0;
|
|
||||||
s_m = 0.0;
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
// Set up convergence check
|
|
||||||
/////////////////////////
|
|
||||||
tau2 = mod2r;
|
|
||||||
target2 = mod2b * Tolerance*Tolerance;
|
|
||||||
|
|
||||||
for(int iter = 0 ; iter < MaxIterations; iter++){
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
// \delta_m = (v_m, \gamma_5 v_m)
|
|
||||||
/////////////////////////
|
|
||||||
delta_m = innerG5ProductReal(v_m,v_m);
|
|
||||||
std::cout << "QuasiMinimalResidual delta_m "<< delta_m<<std::endl;
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
// tmp = A v_m
|
|
||||||
/////////////////////////
|
|
||||||
LinOp.Op(v_m,tmp);
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
// \alpha = (v_m, \gamma_5 temp) / \delta_m
|
|
||||||
/////////////////////////
|
|
||||||
alpha = innerG5ProductReal(v_m,tmp);
|
|
||||||
alpha = alpha/delta_m ;
|
|
||||||
std::cout << "QuasiMinimalResidual alpha "<< alpha<<std::endl;
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
// \beta = \rho \delta_m / \delta_{m-1}
|
|
||||||
/////////////////////////
|
|
||||||
beta = rho * delta_m / delta_m_minus_1;
|
|
||||||
std::cout << "QuasiMinimalResidual beta "<< beta<<std::endl;
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
// \tilde{v}_{m+1} = temp - \alpha v_m - \beta v_{m-1}
|
|
||||||
/////////////////////////
|
|
||||||
v_m_plus_1 = tmp - alpha*v_m - beta*v_m_minus_1;
|
|
||||||
|
|
||||||
///////////////////////////////
|
|
||||||
// \rho = || \tilde{v}_{m+1} ||
|
|
||||||
///////////////////////////////
|
|
||||||
rho = sqrt( norm2(v_m_plus_1) );
|
|
||||||
std::cout << "QuasiMinimalResidual rho "<< rho<<std::endl;
|
|
||||||
|
|
||||||
///////////////////////////////
|
|
||||||
// v_{m+1} = \tilde{v}_{m+1}
|
|
||||||
///////////////////////////////
|
|
||||||
v_m_plus_1 = (1.0 / rho) * v_m_plus_1;
|
|
||||||
|
|
||||||
////////////////////////////////
|
|
||||||
// QMR recurrence coefficients.
|
|
||||||
////////////////////////////////
|
|
||||||
theta = s_m_minus_1 * beta;
|
|
||||||
gamma = c_m_minus_1 * beta;
|
|
||||||
epsilon = c_m * gamma + s_m * alpha;
|
|
||||||
xi = -s_m * gamma + c_m * alpha;
|
|
||||||
nu = sqrt( xi*xi + rho*rho );
|
|
||||||
c_m_plus_1 = fabs(xi) / nu;
|
|
||||||
if ( xi == 0.0 ) {
|
|
||||||
s_m_plus_1 = 1.0;
|
|
||||||
} else {
|
|
||||||
s_m_plus_1 = c_m_plus_1 * rho / xi;
|
|
||||||
}
|
|
||||||
chi = c_m_plus_1 * xi + s_m_plus_1 * rho;
|
|
||||||
|
|
||||||
std::cout << "QuasiMinimalResidual coeffs "<< theta <<" "<<gamma<<" "<< epsilon<<" "<< xi<<" "<< nu<<std::endl;
|
|
||||||
std::cout << "QuasiMinimalResidual coeffs "<< chi <<std::endl;
|
|
||||||
|
|
||||||
////////////////////////////////
|
|
||||||
//p_m=(v_m - \epsilon p_{m-1} - \theta p_{m-2}) / \chi
|
|
||||||
////////////////////////////////
|
|
||||||
p_m = (1.0/chi) * v_m - (epsilon/chi) * p_m_minus_1 - (theta/chi) * p_m_minus_2;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
// \psi = \psi + c_{m+1} \mu p_m
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
x = x + ( c_m_plus_1 * mu ) * p_m;
|
|
||||||
|
|
||||||
////////////////////////////////////////
|
|
||||||
//
|
|
||||||
////////////////////////////////////////
|
|
||||||
mu = -s_m_plus_1 * mu;
|
|
||||||
delta_m_minus_1 = delta_m;
|
|
||||||
c_m_minus_1 = c_m;
|
|
||||||
c_m = c_m_plus_1;
|
|
||||||
s_m_minus_1 = s_m;
|
|
||||||
s_m = s_m_plus_1;
|
|
||||||
|
|
||||||
////////////////////////////////////
|
|
||||||
// Could use pointer swizzle games.
|
|
||||||
////////////////////////////////////
|
|
||||||
v_m_minus_1 = v_m;
|
|
||||||
v_m = v_m_plus_1;
|
|
||||||
p_m_minus_2 = p_m_minus_1;
|
|
||||||
p_m_minus_1 = p_m;
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////
|
|
||||||
// Convergence checks
|
|
||||||
/////////////////////////////////////
|
|
||||||
z1 = RealD(iter+1.0);
|
|
||||||
z2 = z1 + 1.0;
|
|
||||||
tau2 = tau2 *( z2 / z1 ) * s_m * s_m;
|
|
||||||
std::cout << " QuasiMinimumResidual iteration "<< iter<<std::endl;
|
|
||||||
std::cout << " QuasiMinimumResidual tau bound "<< tau2<<std::endl;
|
|
||||||
|
|
||||||
// Compute true residual
|
|
||||||
mod2r = tau2;
|
|
||||||
if ( 1 || (tau2 < (100.0 * target2)) ) {
|
|
||||||
LinOp.Op(x,tmp);
|
|
||||||
r = b - tmp;
|
|
||||||
mod2r = norm2(r);
|
|
||||||
std::cout << " QuasiMinimumResidual true residual is "<< mod2r<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if ( mod2r < target2 ) {
|
|
||||||
|
|
||||||
std::cout << " QuasiMinimumResidual has converged"<<std::endl;
|
|
||||||
return;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
+59
-7
@@ -27,7 +27,51 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
namespace QCD {
|
||||||
|
|
||||||
|
|
||||||
|
template<class Field>
|
||||||
|
class PauliVillarsSolverUnprec
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConjugateGradient<Field> & CG;
|
||||||
|
PauliVillarsSolverUnprec( ConjugateGradient<Field> &_CG) : CG(_CG){};
|
||||||
|
|
||||||
|
template<class Matrix>
|
||||||
|
void operator() (Matrix &_Matrix,const Field &src,Field &sol)
|
||||||
|
{
|
||||||
|
RealD m = _Matrix.Mass();
|
||||||
|
Field A (_Matrix.FermionGrid());
|
||||||
|
|
||||||
|
MdagMLinearOperator<Matrix,Field> HermOp(_Matrix);
|
||||||
|
|
||||||
|
_Matrix.SetMass(1.0);
|
||||||
|
_Matrix.Mdag(src,A);
|
||||||
|
CG(HermOp,A,sol);
|
||||||
|
_Matrix.SetMass(m);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Field>
|
||||||
|
class PauliVillarsSolverRBprec
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConjugateGradient<Field> & CG;
|
||||||
|
PauliVillarsSolverRBprec( ConjugateGradient<Field> &_CG) : CG(_CG){};
|
||||||
|
|
||||||
|
template<class Matrix>
|
||||||
|
void operator() (Matrix &_Matrix,const Field &src,Field &sol)
|
||||||
|
{
|
||||||
|
RealD m = _Matrix.Mass();
|
||||||
|
Field A (_Matrix.FermionGrid());
|
||||||
|
|
||||||
|
_Matrix.SetMass(1.0);
|
||||||
|
SchurRedBlackDiagMooeeSolve<Field> SchurSolver(CG);
|
||||||
|
SchurSolver(_Matrix,src,sol);
|
||||||
|
_Matrix.SetMass(m);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
template<class Field,class PVinverter> class Reconstruct5DfromPhysical {
|
template<class Field,class PVinverter> class Reconstruct5DfromPhysical {
|
||||||
private:
|
private:
|
||||||
@@ -41,12 +85,20 @@ template<class Field,class PVinverter> class Reconstruct5DfromPhysical {
|
|||||||
// of the Mobius exact AMA corrections.
|
// of the Mobius exact AMA corrections.
|
||||||
//
|
//
|
||||||
// TODO : understand absence of contact term in eqns in Hantao's thesis
|
// TODO : understand absence of contact term in eqns in Hantao's thesis
|
||||||
// sol4 is contact term subtracted, but thesis & Brower's paper suggests not.
|
// sol4 is contact term subtracted.
|
||||||
//
|
//
|
||||||
// Step 1: Localise PV inverse in a routine. [DONE]
|
// Options
|
||||||
|
// a) Defect correction approach:
|
||||||
|
// 1) Compute defect from current soln (initially guess).
|
||||||
|
// This is ...... outerToInner check !!!!
|
||||||
|
// 2) Deflated Zmobius solve to get 4d soln
|
||||||
|
// Ensure deflation is working
|
||||||
|
// 3) Refine 5d Outer using the inner 4d delta soln
|
||||||
|
//
|
||||||
|
// Step 1: localise PV inverse in a routine. [DONE]
|
||||||
// Step 2: Schur based PV inverse [DONE]
|
// Step 2: Schur based PV inverse [DONE]
|
||||||
// Step 3: Fourier accelerated PV inverse [DONE]
|
// Step 3: Fourier accelerated PV inverse
|
||||||
//
|
// Step 4:
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
Reconstruct5DfromPhysical(PVinverter &_PauliVillarsSolver)
|
Reconstruct5DfromPhysical(PVinverter &_PauliVillarsSolver)
|
||||||
@@ -130,5 +182,5 @@ template<class Field,class PVinverter> class Reconstruct5DfromPhysical {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
}
|
||||||
|
}
|
||||||
@@ -87,25 +87,228 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
namespace Grid {
|
namespace Grid {
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Use base class to share code
|
// Take a matrix and form a Red Black solver calling a Herm solver
|
||||||
|
// Use of RB info prevents making SchurRedBlackSolve conform to standard interface
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Now make the norm reflect extra factor of Mee
|
||||||
|
template<class Field> class SchurRedBlackStaggeredSolve {
|
||||||
|
private:
|
||||||
|
OperatorFunction<Field> & _HermitianRBSolver;
|
||||||
|
int CBfactorise;
|
||||||
|
bool subGuess;
|
||||||
|
public:
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
// Wrap the usual normal equations Schur trick
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
SchurRedBlackStaggeredSolve(OperatorFunction<Field> &HermitianRBSolver, const bool initSubGuess = false) :
|
||||||
|
_HermitianRBSolver(HermitianRBSolver)
|
||||||
|
{
|
||||||
|
CBfactorise=0;
|
||||||
|
subtractGuess(initSubGuess);
|
||||||
|
};
|
||||||
|
void subtractGuess(const bool initSubGuess)
|
||||||
|
{
|
||||||
|
subGuess = initSubGuess;
|
||||||
|
}
|
||||||
|
bool isSubtractGuess(void)
|
||||||
|
{
|
||||||
|
return subGuess;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Matrix>
|
||||||
|
void operator() (Matrix & _Matrix,const Field &in, Field &out){
|
||||||
|
ZeroGuesser<Field> guess;
|
||||||
|
(*this)(_Matrix,in,out,guess);
|
||||||
|
}
|
||||||
|
template<class Matrix, class Guesser>
|
||||||
|
void operator() (Matrix & _Matrix,const Field &in, Field &out, Guesser &guess){
|
||||||
|
|
||||||
|
// FIXME CGdiagonalMee not implemented virtual function
|
||||||
|
// FIXME use CBfactorise to control schur decomp
|
||||||
|
GridBase *grid = _Matrix.RedBlackGrid();
|
||||||
|
GridBase *fgrid= _Matrix.Grid();
|
||||||
|
|
||||||
|
SchurStaggeredOperator<Matrix,Field> _HermOpEO(_Matrix);
|
||||||
|
|
||||||
|
Field src_e(grid);
|
||||||
|
Field src_o(grid);
|
||||||
|
Field sol_e(grid);
|
||||||
|
Field sol_o(grid);
|
||||||
|
Field tmp(grid);
|
||||||
|
Field Mtmp(grid);
|
||||||
|
Field resid(fgrid);
|
||||||
|
|
||||||
|
std::cout << GridLogMessage << " SchurRedBlackStaggeredSolve " <<std::endl;
|
||||||
|
pickCheckerboard(Even,src_e,in);
|
||||||
|
pickCheckerboard(Odd ,src_o,in);
|
||||||
|
pickCheckerboard(Even,sol_e,out);
|
||||||
|
pickCheckerboard(Odd ,sol_o,out);
|
||||||
|
std::cout << GridLogMessage << " SchurRedBlackStaggeredSolve checkerboards picked" <<std::endl;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
// src_o = (source_o - Moe MeeInv source_e)
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
_Matrix.MooeeInv(src_e,tmp); assert( tmp.checkerboard ==Even);
|
||||||
|
_Matrix.Meooe (tmp,Mtmp); assert( Mtmp.checkerboard ==Odd);
|
||||||
|
tmp=src_o-Mtmp; assert( tmp.checkerboard ==Odd);
|
||||||
|
|
||||||
|
//src_o = tmp; assert(src_o.checkerboard ==Odd);
|
||||||
|
_Matrix.Mooee(tmp,src_o); // Extra factor of "m" in source from dumb choice of matrix norm.
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
// Call the red-black solver
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
std::cout<<GridLogMessage << "SchurRedBlackStaggeredSolver calling the Mpc solver" <<std::endl;
|
||||||
|
guess(src_o, sol_o);
|
||||||
|
Mtmp = sol_o;
|
||||||
|
_HermitianRBSolver(_HermOpEO,src_o,sol_o); assert(sol_o.checkerboard==Odd);
|
||||||
|
std::cout<<GridLogMessage << "SchurRedBlackStaggeredSolver called the Mpc solver" <<std::endl;
|
||||||
|
// Fionn A2A boolean behavioural control
|
||||||
|
if (subGuess) sol_o = sol_o-Mtmp;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
_Matrix.Meooe(sol_o,tmp); assert( tmp.checkerboard ==Even);
|
||||||
|
src_e = src_e-tmp; assert( src_e.checkerboard ==Even);
|
||||||
|
_Matrix.MooeeInv(src_e,sol_e); assert( sol_e.checkerboard ==Even);
|
||||||
|
|
||||||
|
std::cout<<GridLogMessage << "SchurRedBlackStaggeredSolver reconstructed other CB" <<std::endl;
|
||||||
|
setCheckerboard(out,sol_e); assert( sol_e.checkerboard ==Even);
|
||||||
|
setCheckerboard(out,sol_o); assert( sol_o.checkerboard ==Odd );
|
||||||
|
std::cout<<GridLogMessage << "SchurRedBlackStaggeredSolver inserted solution" <<std::endl;
|
||||||
|
|
||||||
|
// Verify the unprec residual
|
||||||
|
if ( ! subGuess ) {
|
||||||
|
_Matrix.M(out,resid);
|
||||||
|
resid = resid-in;
|
||||||
|
RealD ns = norm2(in);
|
||||||
|
RealD nr = norm2(resid);
|
||||||
|
std::cout<<GridLogMessage << "SchurRedBlackStaggered solver true unprec resid "<< std::sqrt(nr/ns) <<" nr "<< nr <<" ns "<<ns << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << GridLogMessage << "Guess subtracted after solve." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template<class Field> using SchurRedBlackStagSolve = SchurRedBlackStaggeredSolve<Field>;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Take a matrix and form a Red Black solver calling a Herm solver
|
// Take a matrix and form a Red Black solver calling a Herm solver
|
||||||
// Use of RB info prevents making SchurRedBlackSolve conform to standard interface
|
// Use of RB info prevents making SchurRedBlackSolve conform to standard interface
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
template<class Field> class SchurRedBlackBase {
|
template<class Field> class SchurRedBlackDiagMooeeSolve {
|
||||||
protected:
|
private:
|
||||||
typedef CheckerBoardedSparseMatrixBase<Field> Matrix;
|
|
||||||
OperatorFunction<Field> & _HermitianRBSolver;
|
OperatorFunction<Field> & _HermitianRBSolver;
|
||||||
int CBfactorise;
|
int CBfactorise;
|
||||||
bool subGuess;
|
bool subGuess;
|
||||||
bool useSolnAsInitGuess; // if true user-supplied solution vector is used as initial guess for solver
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
SchurRedBlackBase(OperatorFunction<Field> &HermitianRBSolver, const bool initSubGuess = false,
|
/////////////////////////////////////////////////////
|
||||||
const bool _solnAsInitGuess = false) :
|
// Wrap the usual normal equations Schur trick
|
||||||
_HermitianRBSolver(HermitianRBSolver),
|
/////////////////////////////////////////////////////
|
||||||
useSolnAsInitGuess(_solnAsInitGuess)
|
SchurRedBlackDiagMooeeSolve(OperatorFunction<Field> &HermitianRBSolver,int cb=0, const bool initSubGuess = false) : _HermitianRBSolver(HermitianRBSolver)
|
||||||
|
{
|
||||||
|
CBfactorise=cb;
|
||||||
|
subtractGuess(initSubGuess);
|
||||||
|
};
|
||||||
|
void subtractGuess(const bool initSubGuess)
|
||||||
|
{
|
||||||
|
subGuess = initSubGuess;
|
||||||
|
}
|
||||||
|
bool isSubtractGuess(void)
|
||||||
|
{
|
||||||
|
return subGuess;
|
||||||
|
}
|
||||||
|
template<class Matrix>
|
||||||
|
void operator() (Matrix & _Matrix,const Field &in, Field &out){
|
||||||
|
ZeroGuesser<Field> guess;
|
||||||
|
(*this)(_Matrix,in,out,guess);
|
||||||
|
}
|
||||||
|
template<class Matrix, class Guesser>
|
||||||
|
void operator() (Matrix & _Matrix,const Field &in, Field &out,Guesser &guess){
|
||||||
|
|
||||||
|
// FIXME CGdiagonalMee not implemented virtual function
|
||||||
|
// FIXME use CBfactorise to control schur decomp
|
||||||
|
GridBase *grid = _Matrix.RedBlackGrid();
|
||||||
|
GridBase *fgrid= _Matrix.Grid();
|
||||||
|
|
||||||
|
SchurDiagMooeeOperator<Matrix,Field> _HermOpEO(_Matrix);
|
||||||
|
|
||||||
|
Field src_e(grid);
|
||||||
|
Field src_o(grid);
|
||||||
|
Field sol_e(grid);
|
||||||
|
Field sol_o(grid);
|
||||||
|
Field tmp(grid);
|
||||||
|
Field Mtmp(grid);
|
||||||
|
Field resid(fgrid);
|
||||||
|
|
||||||
|
pickCheckerboard(Even,src_e,in);
|
||||||
|
pickCheckerboard(Odd ,src_o,in);
|
||||||
|
pickCheckerboard(Even,sol_e,out);
|
||||||
|
pickCheckerboard(Odd ,sol_o,out);
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
// src_o = Mdag * (source_o - Moe MeeInv source_e)
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
_Matrix.MooeeInv(src_e,tmp); assert( tmp.checkerboard ==Even);
|
||||||
|
_Matrix.Meooe (tmp,Mtmp); assert( Mtmp.checkerboard ==Odd);
|
||||||
|
tmp=src_o-Mtmp; assert( tmp.checkerboard ==Odd);
|
||||||
|
|
||||||
|
// get the right MpcDag
|
||||||
|
_HermOpEO.MpcDag(tmp,src_o); assert(src_o.checkerboard ==Odd);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
// Call the red-black solver
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
std::cout<<GridLogMessage << "SchurRedBlack solver calling the MpcDagMp solver" <<std::endl;
|
||||||
|
guess(src_o,sol_o);
|
||||||
|
Mtmp = sol_o;
|
||||||
|
_HermitianRBSolver(_HermOpEO,src_o,sol_o); assert(sol_o.checkerboard==Odd);
|
||||||
|
// Fionn A2A boolean behavioural control
|
||||||
|
if (subGuess) sol_o = sol_o-Mtmp;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
_Matrix.Meooe(sol_o,tmp); assert( tmp.checkerboard ==Even);
|
||||||
|
src_e = src_e-tmp; assert( src_e.checkerboard ==Even);
|
||||||
|
_Matrix.MooeeInv(src_e,sol_e); assert( sol_e.checkerboard ==Even);
|
||||||
|
|
||||||
|
setCheckerboard(out,sol_e); assert( sol_e.checkerboard ==Even);
|
||||||
|
setCheckerboard(out,sol_o); assert( sol_o.checkerboard ==Odd );
|
||||||
|
|
||||||
|
// Verify the unprec residual
|
||||||
|
if ( ! subGuess ) {
|
||||||
|
_Matrix.M(out,resid);
|
||||||
|
resid = resid-in;
|
||||||
|
RealD ns = norm2(in);
|
||||||
|
RealD nr = norm2(resid);
|
||||||
|
|
||||||
|
std::cout<<GridLogMessage << "SchurRedBlackDiagMooee solver true unprec resid "<< std::sqrt(nr/ns) <<" nr "<< nr <<" ns "<<ns << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << GridLogMessage << "Guess subtracted after solve." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Take a matrix and form a Red Black solver calling a Herm solver
|
||||||
|
// Use of RB info prevents making SchurRedBlackSolve conform to standard interface
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<class Field> class SchurRedBlackDiagTwoSolve {
|
||||||
|
private:
|
||||||
|
OperatorFunction<Field> & _HermitianRBSolver;
|
||||||
|
int CBfactorise;
|
||||||
|
bool subGuess;
|
||||||
|
public:
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
// Wrap the usual normal equations Schur trick
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
SchurRedBlackDiagTwoSolve(OperatorFunction<Field> &HermitianRBSolver, const bool initSubGuess = false) :
|
||||||
|
_HermitianRBSolver(HermitianRBSolver)
|
||||||
{
|
{
|
||||||
CBfactorise = 0;
|
CBfactorise = 0;
|
||||||
subtractGuess(initSubGuess);
|
subtractGuess(initSubGuess);
|
||||||
@@ -119,120 +322,12 @@ namespace Grid {
|
|||||||
return subGuess;
|
return subGuess;
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
template<class Matrix>
|
||||||
// Shared code
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
void operator() (Matrix & _Matrix,const Field &in, Field &out){
|
void operator() (Matrix & _Matrix,const Field &in, Field &out){
|
||||||
ZeroGuesser<Field> guess;
|
ZeroGuesser<Field> guess;
|
||||||
(*this)(_Matrix,in,out,guess);
|
(*this)(_Matrix,in,out,guess);
|
||||||
}
|
}
|
||||||
void operator()(Matrix &_Matrix, const std::vector<Field> &in, std::vector<Field> &out)
|
template<class Matrix,class Guesser>
|
||||||
{
|
|
||||||
ZeroGuesser<Field> guess;
|
|
||||||
(*this)(_Matrix,in,out,guess);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RedBlackSource(Matrix &_Matrix, const std::vector<Field> &in, std::vector<Field> &src_o)
|
|
||||||
{
|
|
||||||
GridBase *grid = _Matrix.RedBlackGrid();
|
|
||||||
Field tmp(grid);
|
|
||||||
int nblock = in.size();
|
|
||||||
for(int b=0;b<nblock;b++){
|
|
||||||
RedBlackSource(_Matrix,in[b],tmp,src_o[b]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// James can write his own deflated guesser
|
|
||||||
// with optimised code for the inner products
|
|
||||||
// RedBlackSolveSplitGrid();
|
|
||||||
// RedBlackSolve(_Matrix,src_o,sol_o);
|
|
||||||
|
|
||||||
void RedBlackSolution(Matrix &_Matrix, const std::vector<Field> &in, const std::vector<Field> &sol_o, std::vector<Field> &out)
|
|
||||||
{
|
|
||||||
GridBase *grid = _Matrix.RedBlackGrid();
|
|
||||||
Field tmp(grid);
|
|
||||||
int nblock = in.size();
|
|
||||||
for(int b=0;b<nblock;b++) {
|
|
||||||
pickCheckerboard(Even,tmp,in[b]);
|
|
||||||
RedBlackSolution(_Matrix,sol_o[b],tmp,out[b]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Guesser>
|
|
||||||
void operator()(Matrix &_Matrix, const std::vector<Field> &in, std::vector<Field> &out,Guesser &guess)
|
|
||||||
{
|
|
||||||
GridBase *grid = _Matrix.RedBlackGrid();
|
|
||||||
GridBase *fgrid= _Matrix.Grid();
|
|
||||||
int nblock = in.size();
|
|
||||||
|
|
||||||
std::vector<Field> src_o(nblock,grid);
|
|
||||||
std::vector<Field> sol_o(nblock,grid);
|
|
||||||
|
|
||||||
std::vector<Field> guess_save;
|
|
||||||
|
|
||||||
Field resid(fgrid);
|
|
||||||
Field tmp(grid);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
|
||||||
// Prepare RedBlack source
|
|
||||||
////////////////////////////////////////////////
|
|
||||||
RedBlackSource(_Matrix,in,src_o);
|
|
||||||
// for(int b=0;b<nblock;b++){
|
|
||||||
// RedBlackSource(_Matrix,in[b],tmp,src_o[b]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
|
||||||
// Make the guesses
|
|
||||||
////////////////////////////////////////////////
|
|
||||||
if ( subGuess ) guess_save.resize(nblock,grid);
|
|
||||||
|
|
||||||
|
|
||||||
if(useSolnAsInitGuess) {
|
|
||||||
for(int b=0;b<nblock;b++){
|
|
||||||
pickCheckerboard(Odd, sol_o[b], out[b]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
guess(src_o, sol_o);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( subGuess ) {
|
|
||||||
for(int b=0;b<nblock;b++){
|
|
||||||
guess_save[b] = sol_o[b];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//////////////////////////////////////////////////////////////
|
|
||||||
// Call the block solver
|
|
||||||
//////////////////////////////////////////////////////////////
|
|
||||||
std::cout<<GridLogMessage << "SchurRedBlackBase calling the solver for "<<nblock<<" RHS" <<std::endl;
|
|
||||||
RedBlackSolve(_Matrix,src_o,sol_o);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
|
||||||
// A2A boolean behavioural control & reconstruct other checkerboard
|
|
||||||
////////////////////////////////////////////////
|
|
||||||
for(int b=0;b<nblock;b++) {
|
|
||||||
|
|
||||||
if (subGuess) sol_o[b] = sol_o[b] - guess_save[b];
|
|
||||||
|
|
||||||
///////// Needs even source //////////////
|
|
||||||
pickCheckerboard(Even,tmp,in[b]);
|
|
||||||
RedBlackSolution(_Matrix,sol_o[b],tmp,out[b]);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
// Check unprec residual if possible
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
if ( ! subGuess ) {
|
|
||||||
_Matrix.M(out[b],resid);
|
|
||||||
resid = resid-in[b];
|
|
||||||
RealD ns = norm2(in[b]);
|
|
||||||
RealD nr = norm2(resid);
|
|
||||||
|
|
||||||
std::cout<<GridLogMessage<< "SchurRedBlackBase solver true unprec resid["<<b<<"] "<<std::sqrt(nr/ns) << std::endl;
|
|
||||||
} else {
|
|
||||||
std::cout<<GridLogMessage<< "SchurRedBlackBase Guess subtracted after solve["<<b<<"] " << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template<class Guesser>
|
|
||||||
void operator() (Matrix & _Matrix,const Field &in, Field &out,Guesser &guess){
|
void operator() (Matrix & _Matrix,const Field &in, Field &out,Guesser &guess){
|
||||||
|
|
||||||
// FIXME CGdiagonalMee not implemented virtual function
|
// FIXME CGdiagonalMee not implemented virtual function
|
||||||
@@ -240,42 +335,52 @@ namespace Grid {
|
|||||||
GridBase *grid = _Matrix.RedBlackGrid();
|
GridBase *grid = _Matrix.RedBlackGrid();
|
||||||
GridBase *fgrid= _Matrix.Grid();
|
GridBase *fgrid= _Matrix.Grid();
|
||||||
|
|
||||||
Field resid(fgrid);
|
SchurDiagTwoOperator<Matrix,Field> _HermOpEO(_Matrix);
|
||||||
Field src_o(grid);
|
|
||||||
Field src_e(grid);
|
Field src_e(grid);
|
||||||
|
Field src_o(grid);
|
||||||
|
Field sol_e(grid);
|
||||||
Field sol_o(grid);
|
Field sol_o(grid);
|
||||||
|
Field tmp(grid);
|
||||||
|
Field Mtmp(grid);
|
||||||
|
Field resid(fgrid);
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
pickCheckerboard(Even,src_e,in);
|
||||||
// RedBlack source
|
pickCheckerboard(Odd ,src_o,in);
|
||||||
////////////////////////////////////////////////
|
pickCheckerboard(Even,sol_e,out);
|
||||||
RedBlackSource(_Matrix,in,src_e,src_o);
|
pickCheckerboard(Odd ,sol_o,out);
|
||||||
|
|
||||||
////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
// Construct the guess
|
// src_o = Mdag * (source_o - Moe MeeInv source_e)
|
||||||
////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
if(useSolnAsInitGuess) {
|
_Matrix.MooeeInv(src_e,tmp); assert( tmp.checkerboard ==Even);
|
||||||
pickCheckerboard(Odd, sol_o, out);
|
_Matrix.Meooe (tmp,Mtmp); assert( Mtmp.checkerboard ==Odd);
|
||||||
} else {
|
tmp=src_o-Mtmp; assert( tmp.checkerboard ==Odd);
|
||||||
guess(src_o,sol_o);
|
|
||||||
}
|
|
||||||
|
|
||||||
Field guess_save(grid);
|
// get the right MpcDag
|
||||||
guess_save = sol_o;
|
_HermOpEO.MpcDag(tmp,src_o); assert(src_o.checkerboard ==Odd);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
// Call the red-black solver
|
// Call the red-black solver
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
RedBlackSolve(_Matrix,src_o,sol_o);
|
std::cout<<GridLogMessage << "SchurRedBlack solver calling the MpcDagMp solver" <<std::endl;
|
||||||
|
// _HermitianRBSolver(_HermOpEO,src_o,sol_o); assert(sol_o.checkerboard==Odd);
|
||||||
////////////////////////////////////////////////
|
guess(src_o,tmp);
|
||||||
|
Mtmp = tmp;
|
||||||
|
_HermitianRBSolver(_HermOpEO,src_o,tmp); assert(tmp.checkerboard==Odd);
|
||||||
// Fionn A2A boolean behavioural control
|
// Fionn A2A boolean behavioural control
|
||||||
////////////////////////////////////////////////
|
if (subGuess) tmp = tmp-Mtmp;
|
||||||
if (subGuess) sol_o= sol_o-guess_save;
|
_Matrix.MooeeInv(tmp,sol_o); assert( sol_o.checkerboard ==Odd);
|
||||||
|
|
||||||
///////////////////////////////////////////////////
|
///////////////////////////////////////////////////
|
||||||
// RedBlack solution needs the even source
|
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
||||||
///////////////////////////////////////////////////
|
///////////////////////////////////////////////////
|
||||||
RedBlackSolution(_Matrix,sol_o,src_e,out);
|
_Matrix.Meooe(sol_o,tmp); assert( tmp.checkerboard ==Even);
|
||||||
|
src_e = src_e-tmp; assert( src_e.checkerboard ==Even);
|
||||||
|
_Matrix.MooeeInv(src_e,sol_e); assert( sol_e.checkerboard ==Even);
|
||||||
|
|
||||||
|
setCheckerboard(out,sol_e); assert( sol_e.checkerboard ==Even);
|
||||||
|
setCheckerboard(out,sol_o); assert( sol_o.checkerboard ==Odd );
|
||||||
|
|
||||||
// Verify the unprec residual
|
// Verify the unprec residual
|
||||||
if ( ! subGuess ) {
|
if ( ! subGuess ) {
|
||||||
@@ -284,449 +389,115 @@ namespace Grid {
|
|||||||
RealD ns = norm2(in);
|
RealD ns = norm2(in);
|
||||||
RealD nr = norm2(resid);
|
RealD nr = norm2(resid);
|
||||||
|
|
||||||
std::cout<<GridLogMessage << "SchurRedBlackBase solver true unprec resid "<< std::sqrt(nr/ns) << std::endl;
|
std::cout<<GridLogMessage << "SchurRedBlackDiagTwo solver true unprec resid "<< std::sqrt(nr/ns) <<" nr "<< nr <<" ns "<<ns << std::endl;
|
||||||
} else {
|
} else {
|
||||||
std::cout << GridLogMessage << "SchurRedBlackBase Guess subtracted after solve." << std::endl;
|
std::cout << GridLogMessage << "Guess subtracted after solve." << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
// Override in derived.
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
virtual void RedBlackSource (Matrix & _Matrix,const Field &src, Field &src_e,Field &src_o) =0;
|
|
||||||
virtual void RedBlackSolution(Matrix & _Matrix,const Field &sol_o, const Field &src_e,Field &sol) =0;
|
|
||||||
virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o) =0;
|
|
||||||
virtual void RedBlackSolve (Matrix & _Matrix,const std::vector<Field> &src_o, std::vector<Field> &sol_o)=0;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
template<class Field> class SchurRedBlackStaggeredSolve : public SchurRedBlackBase<Field> {
|
// Take a matrix and form a Red Black solver calling a Herm solver
|
||||||
|
// Use of RB info prevents making SchurRedBlackSolve conform to standard interface
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<class Field> class SchurRedBlackDiagTwoMixed {
|
||||||
|
private:
|
||||||
|
LinearFunction<Field> & _HermitianRBSolver;
|
||||||
|
int CBfactorise;
|
||||||
|
bool subGuess;
|
||||||
public:
|
public:
|
||||||
typedef CheckerBoardedSparseMatrixBase<Field> Matrix;
|
|
||||||
|
|
||||||
SchurRedBlackStaggeredSolve(OperatorFunction<Field> &HermitianRBSolver, const bool initSubGuess = false,
|
|
||||||
const bool _solnAsInitGuess = false)
|
|
||||||
: SchurRedBlackBase<Field> (HermitianRBSolver,initSubGuess,_solnAsInitGuess)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
// Override RedBlack specialisation
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
virtual void RedBlackSource(Matrix & _Matrix,const Field &src, Field &src_e,Field &src_o)
|
|
||||||
{
|
|
||||||
GridBase *grid = _Matrix.RedBlackGrid();
|
|
||||||
GridBase *fgrid= _Matrix.Grid();
|
|
||||||
|
|
||||||
Field tmp(grid);
|
|
||||||
Field Mtmp(grid);
|
|
||||||
|
|
||||||
pickCheckerboard(Even,src_e,src);
|
|
||||||
pickCheckerboard(Odd ,src_o,src);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// src_o = (source_o - Moe MeeInv source_e)
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
_Matrix.MooeeInv(src_e,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
|
||||||
_Matrix.Meooe (tmp,Mtmp); GRID_ASSERT( Mtmp.Checkerboard() ==Odd);
|
|
||||||
tmp=src_o-Mtmp; GRID_ASSERT( tmp.Checkerboard() ==Odd);
|
|
||||||
|
|
||||||
_Matrix.Mooee(tmp,src_o); // Extra factor of "m" in source from dumb choice of matrix norm.
|
|
||||||
}
|
|
||||||
virtual void RedBlackSolution(Matrix & _Matrix,const Field &sol_o, const Field &src_e_c,Field &sol)
|
|
||||||
{
|
|
||||||
GridBase *grid = _Matrix.RedBlackGrid();
|
|
||||||
GridBase *fgrid= _Matrix.Grid();
|
|
||||||
|
|
||||||
Field tmp(grid);
|
|
||||||
Field sol_e(grid);
|
|
||||||
Field src_e(grid);
|
|
||||||
|
|
||||||
src_e = src_e_c; // Const correctness
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
_Matrix.Meooe(sol_o,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
|
||||||
src_e = src_e-tmp; GRID_ASSERT( src_e.Checkerboard() ==Even);
|
|
||||||
_Matrix.MooeeInv(src_e,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
|
||||||
|
|
||||||
setCheckerboard(sol,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
|
||||||
setCheckerboard(sol,sol_o); GRID_ASSERT( sol_o.Checkerboard() ==Odd );
|
|
||||||
}
|
|
||||||
virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o)
|
|
||||||
{
|
|
||||||
SchurStaggeredOperator<Matrix,Field> _HermOpEO(_Matrix);
|
|
||||||
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o); GRID_ASSERT(sol_o.Checkerboard()==Odd);
|
|
||||||
};
|
|
||||||
virtual void RedBlackSolve (Matrix & _Matrix,const std::vector<Field> &src_o, std::vector<Field> &sol_o)
|
|
||||||
{
|
|
||||||
SchurStaggeredOperator<Matrix,Field> _HermOpEO(_Matrix);
|
|
||||||
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template<class Field> using SchurRedBlackStagSolve = SchurRedBlackStaggeredSolve<Field>;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Site diagonal has Mooee on it.
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
template<class Field> class SchurRedBlackDiagMooeeSolve : public SchurRedBlackBase<Field> {
|
|
||||||
public:
|
|
||||||
typedef CheckerBoardedSparseMatrixBase<Field> Matrix;
|
|
||||||
|
|
||||||
SchurRedBlackDiagMooeeSolve(OperatorFunction<Field> &HermitianRBSolver, const bool initSubGuess = false,
|
|
||||||
const bool _solnAsInitGuess = false)
|
|
||||||
: SchurRedBlackBase<Field> (HermitianRBSolver,initSubGuess,_solnAsInitGuess) {};
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
// Override RedBlack specialisation
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
virtual void RedBlackSource(Matrix & _Matrix,const Field &src, Field &src_e,Field &src_o)
|
|
||||||
{
|
|
||||||
GridBase *grid = _Matrix.RedBlackGrid();
|
|
||||||
GridBase *fgrid= _Matrix.Grid();
|
|
||||||
|
|
||||||
Field tmp(grid);
|
|
||||||
Field Mtmp(grid);
|
|
||||||
|
|
||||||
pickCheckerboard(Even,src_e,src);
|
|
||||||
pickCheckerboard(Odd ,src_o,src);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// src_o = Mdag * (source_o - Moe MeeInv source_e)
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
_Matrix.MooeeInv(src_e,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
|
||||||
_Matrix.Meooe (tmp,Mtmp); GRID_ASSERT( Mtmp.Checkerboard() ==Odd);
|
|
||||||
tmp=src_o-Mtmp; GRID_ASSERT( tmp.Checkerboard() ==Odd);
|
|
||||||
|
|
||||||
// get the right MpcDag
|
|
||||||
SchurDiagMooeeOperator<Matrix,Field> _HermOpEO(_Matrix);
|
|
||||||
_HermOpEO.MpcDag(tmp,src_o); GRID_ASSERT(src_o.Checkerboard() ==Odd);
|
|
||||||
|
|
||||||
}
|
|
||||||
virtual void RedBlackSolution(Matrix & _Matrix,const Field &sol_o, const Field &src_e,Field &sol)
|
|
||||||
{
|
|
||||||
GridBase *grid = _Matrix.RedBlackGrid();
|
|
||||||
GridBase *fgrid= _Matrix.Grid();
|
|
||||||
|
|
||||||
Field tmp(grid);
|
|
||||||
Field sol_e(grid);
|
|
||||||
Field src_e_i(grid);
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
_Matrix.Meooe(sol_o,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
|
||||||
src_e_i = src_e-tmp; GRID_ASSERT( src_e_i.Checkerboard() ==Even);
|
|
||||||
_Matrix.MooeeInv(src_e_i,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
|
||||||
|
|
||||||
setCheckerboard(sol,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
|
||||||
setCheckerboard(sol,sol_o); GRID_ASSERT( sol_o.Checkerboard() ==Odd );
|
|
||||||
}
|
|
||||||
virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o)
|
|
||||||
{
|
|
||||||
SchurDiagMooeeOperator<Matrix,Field> _HermOpEO(_Matrix);
|
|
||||||
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o); GRID_ASSERT(sol_o.Checkerboard()==Odd);
|
|
||||||
};
|
|
||||||
virtual void RedBlackSolve (Matrix & _Matrix,const std::vector<Field> &src_o, std::vector<Field> &sol_o)
|
|
||||||
{
|
|
||||||
SchurDiagMooeeOperator<Matrix,Field> _HermOpEO(_Matrix);
|
|
||||||
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Field> class NonHermitianSchurRedBlackDiagMooeeSolve : public SchurRedBlackBase<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef CheckerBoardedSparseMatrixBase<Field> Matrix;
|
|
||||||
|
|
||||||
NonHermitianSchurRedBlackDiagMooeeSolve(OperatorFunction<Field>& RBSolver, const bool initSubGuess = false,
|
|
||||||
const bool _solnAsInitGuess = false)
|
|
||||||
: SchurRedBlackBase<Field>(RBSolver, initSubGuess, _solnAsInitGuess) {};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
// Override RedBlack specialisation
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
virtual void RedBlackSource(Matrix& _Matrix, const Field& src, Field& src_e, Field& src_o)
|
|
||||||
{
|
|
||||||
GridBase* grid = _Matrix.RedBlackGrid();
|
|
||||||
GridBase* fgrid = _Matrix.Grid();
|
|
||||||
|
|
||||||
Field tmp(grid);
|
|
||||||
Field Mtmp(grid);
|
|
||||||
|
|
||||||
pickCheckerboard(Even, src_e, src);
|
|
||||||
pickCheckerboard(Odd , src_o, src);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// src_o = Mdag * (source_o - Moe MeeInv source_e)
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
_Matrix.MooeeInv(src_e, tmp); GRID_ASSERT( tmp.Checkerboard() == Even );
|
|
||||||
_Matrix.Meooe (tmp, Mtmp); GRID_ASSERT( Mtmp.Checkerboard() == Odd );
|
|
||||||
src_o -= Mtmp; GRID_ASSERT( src_o.Checkerboard() == Odd );
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void RedBlackSolution(Matrix& _Matrix, const Field& sol_o, const Field& src_e, Field& sol)
|
|
||||||
{
|
|
||||||
GridBase* grid = _Matrix.RedBlackGrid();
|
|
||||||
GridBase* fgrid = _Matrix.Grid();
|
|
||||||
|
|
||||||
Field tmp(grid);
|
|
||||||
Field sol_e(grid);
|
|
||||||
Field src_e_i(grid);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
_Matrix.Meooe(sol_o, tmp); GRID_ASSERT( tmp.Checkerboard() == Even );
|
|
||||||
src_e_i = src_e - tmp; GRID_ASSERT( src_e_i.Checkerboard() == Even );
|
|
||||||
_Matrix.MooeeInv(src_e_i, sol_e); GRID_ASSERT( sol_e.Checkerboard() == Even );
|
|
||||||
|
|
||||||
setCheckerboard(sol, sol_e); GRID_ASSERT( sol_e.Checkerboard() == Even );
|
|
||||||
setCheckerboard(sol, sol_o); GRID_ASSERT( sol_o.Checkerboard() == Odd );
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void RedBlackSolve(Matrix& _Matrix, const Field& src_o, Field& sol_o)
|
|
||||||
{
|
|
||||||
NonHermitianSchurDiagMooeeOperator<Matrix,Field> _OpEO(_Matrix);
|
|
||||||
this->_HermitianRBSolver(_OpEO, src_o, sol_o); GRID_ASSERT(sol_o.Checkerboard() == Odd);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void RedBlackSolve(Matrix& _Matrix, const std::vector<Field>& src_o, std::vector<Field>& sol_o)
|
|
||||||
{
|
|
||||||
NonHermitianSchurDiagMooeeOperator<Matrix,Field> _OpEO(_Matrix);
|
|
||||||
this->_HermitianRBSolver(_OpEO, src_o, sol_o);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Site diagonal is identity, left preconditioned by Mee^inv
|
|
||||||
// ( 1 - Mee^inv Meo Moo^inv Moe ) phi = Mee_inv ( Mee - Meo Moo^inv Moe Mee^inv ) phi = Mee_inv eta
|
|
||||||
//
|
|
||||||
// Solve:
|
|
||||||
// ( 1 - Mee^inv Meo Moo^inv Moe )^dag ( 1 - Mee^inv Meo Moo^inv Moe ) phi = ( 1 - Mee^inv Meo Moo^inv Moe )^dag Mee_inv eta
|
|
||||||
//
|
|
||||||
// Old notation e<->o
|
|
||||||
//
|
|
||||||
// Left precon by Moo^-1
|
|
||||||
// b) (Doo^{dag} M_oo^-dag) (Moo^-1 Doo) psi_o = [ (D_oo)^dag M_oo^-dag ] Moo^-1 L^{-1} eta_o
|
|
||||||
// eta_o' = (D_oo)^dag M_oo^-dag Moo^-1 (eta_o - Moe Mee^{-1} eta_e)
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
template<class Field> class SchurRedBlackDiagOneSolve : public SchurRedBlackBase<Field> {
|
|
||||||
public:
|
|
||||||
typedef CheckerBoardedSparseMatrixBase<Field> Matrix;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
// Wrap the usual normal equations Schur trick
|
// Wrap the usual normal equations Schur trick
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
SchurRedBlackDiagOneSolve(OperatorFunction<Field> &HermitianRBSolver, const bool initSubGuess = false,
|
SchurRedBlackDiagTwoMixed(LinearFunction<Field> &HermitianRBSolver, const bool initSubGuess = false) :
|
||||||
const bool _solnAsInitGuess = false)
|
_HermitianRBSolver(HermitianRBSolver)
|
||||||
: SchurRedBlackBase<Field>(HermitianRBSolver,initSubGuess,_solnAsInitGuess) {};
|
|
||||||
|
|
||||||
virtual void RedBlackSource(Matrix & _Matrix,const Field &src, Field &src_e,Field &src_o)
|
|
||||||
{
|
{
|
||||||
GridBase *grid = _Matrix.RedBlackGrid();
|
CBfactorise=0;
|
||||||
GridBase *fgrid= _Matrix.Grid();
|
subtractGuess(initSubGuess);
|
||||||
|
};
|
||||||
SchurDiagOneOperator<Matrix,Field> _HermOpEO(_Matrix);
|
void subtractGuess(const bool initSubGuess)
|
||||||
|
{
|
||||||
Field tmp(grid);
|
subGuess = initSubGuess;
|
||||||
Field Mtmp(grid);
|
}
|
||||||
|
bool isSubtractGuess(void)
|
||||||
pickCheckerboard(Even,src_e,src);
|
{
|
||||||
pickCheckerboard(Odd ,src_o,src);
|
return subGuess;
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// src_o = Mpcdag *MooeeInv * (source_o - Moe MeeInv source_e)
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
_Matrix.MooeeInv(src_e,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
|
||||||
_Matrix.Meooe (tmp,Mtmp); GRID_ASSERT( Mtmp.Checkerboard() ==Odd);
|
|
||||||
Mtmp=src_o-Mtmp;
|
|
||||||
_Matrix.MooeeInv(Mtmp,tmp); GRID_ASSERT( tmp.Checkerboard() ==Odd);
|
|
||||||
|
|
||||||
// get the right MpcDag
|
|
||||||
_HermOpEO.MpcDag(tmp,src_o); GRID_ASSERT(src_o.Checkerboard() ==Odd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void RedBlackSolution(Matrix & _Matrix,const Field &sol_o, const Field &src_e,Field &sol)
|
template<class Matrix>
|
||||||
{
|
void operator() (Matrix & _Matrix,const Field &in, Field &out){
|
||||||
GridBase *grid = _Matrix.RedBlackGrid();
|
ZeroGuesser<Field> guess;
|
||||||
GridBase *fgrid= _Matrix.Grid();
|
(*this)(_Matrix,in,out,guess);
|
||||||
|
|
||||||
Field tmp(grid);
|
|
||||||
Field sol_e(grid);
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
_Matrix.Meooe(sol_o,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
|
||||||
tmp = src_e-tmp; GRID_ASSERT( src_e.Checkerboard() ==Even);
|
|
||||||
_Matrix.MooeeInv(tmp,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
|
||||||
|
|
||||||
setCheckerboard(sol,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
|
||||||
setCheckerboard(sol,sol_o); GRID_ASSERT( sol_o.Checkerboard() ==Odd );
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o)
|
|
||||||
{
|
|
||||||
SchurDiagOneOperator<Matrix,Field> _HermOpEO(_Matrix);
|
|
||||||
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o);
|
|
||||||
};
|
|
||||||
virtual void RedBlackSolve (Matrix & _Matrix,const std::vector<Field> &src_o, std::vector<Field> &sol_o)
|
|
||||||
{
|
|
||||||
SchurDiagOneOperator<Matrix,Field> _HermOpEO(_Matrix);
|
|
||||||
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o);
|
|
||||||
}
|
}
|
||||||
};
|
template<class Matrix, class Guesser>
|
||||||
|
void operator() (Matrix & _Matrix,const Field &in, Field &out,Guesser &guess){
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
// FIXME CGdiagonalMee not implemented virtual function
|
||||||
// Site diagonal is identity, right preconditioned by Mee^inv
|
// FIXME use CBfactorise to control schur decomp
|
||||||
// ( 1 - Meo Moo^inv Moe Mee^inv ) phi =( 1 - Meo Moo^inv Moe Mee^inv ) Mee psi = = eta = eta
|
|
||||||
//=> psi = MeeInv phi
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
template<class Field> class SchurRedBlackDiagTwoSolve : public SchurRedBlackBase<Field> {
|
|
||||||
public:
|
|
||||||
typedef CheckerBoardedSparseMatrixBase<Field> Matrix;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// Wrap the usual normal equations Schur trick
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
SchurRedBlackDiagTwoSolve(OperatorFunction<Field> &HermitianRBSolver, const bool initSubGuess = false,
|
|
||||||
const bool _solnAsInitGuess = false)
|
|
||||||
: SchurRedBlackBase<Field>(HermitianRBSolver,initSubGuess,_solnAsInitGuess) {};
|
|
||||||
|
|
||||||
virtual void RedBlackSource(Matrix & _Matrix,const Field &src, Field &src_e,Field &src_o)
|
|
||||||
{
|
|
||||||
GridBase *grid = _Matrix.RedBlackGrid();
|
GridBase *grid = _Matrix.RedBlackGrid();
|
||||||
GridBase *fgrid= _Matrix.Grid();
|
GridBase *fgrid= _Matrix.Grid();
|
||||||
|
|
||||||
SchurDiagTwoOperator<Matrix,Field> _HermOpEO(_Matrix);
|
SchurDiagTwoOperator<Matrix,Field> _HermOpEO(_Matrix);
|
||||||
|
|
||||||
|
Field src_e(grid);
|
||||||
|
Field src_o(grid);
|
||||||
|
Field sol_e(grid);
|
||||||
|
Field sol_o(grid);
|
||||||
Field tmp(grid);
|
Field tmp(grid);
|
||||||
Field Mtmp(grid);
|
Field Mtmp(grid);
|
||||||
|
Field resid(fgrid);
|
||||||
|
|
||||||
pickCheckerboard(Even,src_e,src);
|
pickCheckerboard(Even,src_e,in);
|
||||||
pickCheckerboard(Odd ,src_o,src);
|
pickCheckerboard(Odd ,src_o,in);
|
||||||
|
pickCheckerboard(Even,sol_e,out);
|
||||||
|
pickCheckerboard(Odd ,sol_o,out);
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
// src_o = Mdag * (source_o - Moe MeeInv source_e)
|
// src_o = Mdag * (source_o - Moe MeeInv source_e)
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
_Matrix.MooeeInv(src_e,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
_Matrix.MooeeInv(src_e,tmp); assert( tmp.checkerboard ==Even);
|
||||||
_Matrix.Meooe (tmp,Mtmp); GRID_ASSERT( Mtmp.Checkerboard() ==Odd);
|
_Matrix.Meooe (tmp,Mtmp); assert( Mtmp.checkerboard ==Odd);
|
||||||
tmp=src_o-Mtmp; GRID_ASSERT( tmp.Checkerboard() ==Odd);
|
tmp=src_o-Mtmp; assert( tmp.checkerboard ==Odd);
|
||||||
|
|
||||||
// get the right MpcDag
|
// get the right MpcDag
|
||||||
_HermOpEO.MpcDag(tmp,src_o); GRID_ASSERT(src_o.Checkerboard() ==Odd);
|
_HermOpEO.MpcDag(tmp,src_o); assert(src_o.checkerboard ==Odd);
|
||||||
}
|
|
||||||
|
|
||||||
virtual void RedBlackSolution(Matrix & _Matrix,const Field &sol_o, const Field &src_e,Field &sol)
|
//////////////////////////////////////////////////////////////
|
||||||
{
|
// Call the red-black solver
|
||||||
GridBase *grid = _Matrix.RedBlackGrid();
|
//////////////////////////////////////////////////////////////
|
||||||
GridBase *fgrid= _Matrix.Grid();
|
std::cout<<GridLogMessage << "SchurRedBlack solver calling the MpcDagMp solver" <<std::endl;
|
||||||
|
// _HermitianRBSolver(_HermOpEO,src_o,sol_o); assert(sol_o.checkerboard==Odd);
|
||||||
Field sol_o_i(grid);
|
// _HermitianRBSolver(_HermOpEO,src_o,tmp); assert(tmp.checkerboard==Odd);
|
||||||
Field tmp(grid);
|
guess(src_o,tmp);
|
||||||
Field sol_e(grid);
|
Mtmp = tmp;
|
||||||
|
_HermitianRBSolver(_HermOpEO,src_o,tmp); assert(tmp.checkerboard==Odd);
|
||||||
////////////////////////////////////////////////
|
// Fionn A2A boolean behavioural control
|
||||||
// MooeeInv due to pecond
|
if (subGuess) tmp = tmp-Mtmp;
|
||||||
////////////////////////////////////////////////
|
_Matrix.MooeeInv(tmp,sol_o); assert( sol_o.checkerboard ==Odd);
|
||||||
_Matrix.MooeeInv(sol_o,tmp);
|
|
||||||
sol_o_i = tmp;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////
|
///////////////////////////////////////////////////
|
||||||
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
||||||
///////////////////////////////////////////////////
|
///////////////////////////////////////////////////
|
||||||
_Matrix.Meooe(sol_o_i,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even);
|
_Matrix.Meooe(sol_o,tmp); assert( tmp.checkerboard ==Even);
|
||||||
tmp = src_e-tmp; GRID_ASSERT( src_e.Checkerboard() ==Even);
|
src_e = src_e-tmp; assert( src_e.checkerboard ==Even);
|
||||||
_Matrix.MooeeInv(tmp,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
_Matrix.MooeeInv(src_e,sol_e); assert( sol_e.checkerboard ==Even);
|
||||||
|
|
||||||
setCheckerboard(sol,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even);
|
setCheckerboard(out,sol_e); assert( sol_e.checkerboard ==Even);
|
||||||
setCheckerboard(sol,sol_o_i); GRID_ASSERT( sol_o_i.Checkerboard() ==Odd );
|
setCheckerboard(out,sol_o); assert( sol_o.checkerboard ==Odd );
|
||||||
};
|
|
||||||
|
|
||||||
virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o)
|
// Verify the unprec residual
|
||||||
{
|
if ( ! subGuess ) {
|
||||||
SchurDiagTwoOperator<Matrix,Field> _HermOpEO(_Matrix);
|
_Matrix.M(out,resid);
|
||||||
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o);
|
resid = resid-in;
|
||||||
};
|
RealD ns = norm2(in);
|
||||||
virtual void RedBlackSolve (Matrix & _Matrix,const std::vector<Field> &src_o, std::vector<Field> &sol_o)
|
RealD nr = norm2(resid);
|
||||||
{
|
|
||||||
SchurDiagTwoOperator<Matrix,Field> _HermOpEO(_Matrix);
|
std::cout << GridLogMessage << "SchurRedBlackDiagTwo solver true unprec resid " << std::sqrt(nr / ns) << " nr " << nr << " ns " << ns << std::endl;
|
||||||
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o);
|
} else {
|
||||||
|
std::cout << GridLogMessage << "Guess subtracted after solve." << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class Field> class NonHermitianSchurRedBlackDiagTwoSolve : public SchurRedBlackBase<Field>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef CheckerBoardedSparseMatrixBase<Field> Matrix;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// Wrap the usual normal equations Schur trick
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
NonHermitianSchurRedBlackDiagTwoSolve(OperatorFunction<Field>& RBSolver, const bool initSubGuess = false,
|
|
||||||
const bool _solnAsInitGuess = false)
|
|
||||||
: SchurRedBlackBase<Field>(RBSolver, initSubGuess, _solnAsInitGuess) {};
|
|
||||||
|
|
||||||
virtual void RedBlackSource(Matrix& _Matrix, const Field& src, Field& src_e, Field& src_o)
|
|
||||||
{
|
|
||||||
GridBase* grid = _Matrix.RedBlackGrid();
|
|
||||||
GridBase* fgrid = _Matrix.Grid();
|
|
||||||
|
|
||||||
Field tmp(grid);
|
|
||||||
Field Mtmp(grid);
|
|
||||||
|
|
||||||
pickCheckerboard(Even, src_e, src);
|
|
||||||
pickCheckerboard(Odd , src_o, src);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// src_o = Mdag * (source_o - Moe MeeInv source_e)
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
_Matrix.MooeeInv(src_e, tmp); GRID_ASSERT( tmp.Checkerboard() == Even );
|
|
||||||
_Matrix.Meooe (tmp, Mtmp); GRID_ASSERT( Mtmp.Checkerboard() == Odd );
|
|
||||||
src_o -= Mtmp; GRID_ASSERT( src_o.Checkerboard() == Odd );
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void RedBlackSolution(Matrix& _Matrix, const Field& sol_o, const Field& src_e, Field& sol)
|
|
||||||
{
|
|
||||||
GridBase* grid = _Matrix.RedBlackGrid();
|
|
||||||
GridBase* fgrid = _Matrix.Grid();
|
|
||||||
|
|
||||||
Field sol_o_i(grid);
|
|
||||||
Field tmp(grid);
|
|
||||||
Field sol_e(grid);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
|
||||||
// MooeeInv due to pecond
|
|
||||||
////////////////////////////////////////////////
|
|
||||||
_Matrix.MooeeInv(sol_o, tmp);
|
|
||||||
sol_o_i = tmp;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
_Matrix.Meooe(sol_o_i, tmp); GRID_ASSERT( tmp.Checkerboard() == Even );
|
|
||||||
tmp = src_e - tmp; GRID_ASSERT( src_e.Checkerboard() == Even );
|
|
||||||
_Matrix.MooeeInv(tmp, sol_e); GRID_ASSERT( sol_e.Checkerboard() == Even );
|
|
||||||
|
|
||||||
setCheckerboard(sol, sol_e); GRID_ASSERT( sol_e.Checkerboard() == Even );
|
|
||||||
setCheckerboard(sol, sol_o_i); GRID_ASSERT( sol_o_i.Checkerboard() == Odd );
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual void RedBlackSolve(Matrix& _Matrix, const Field& src_o, Field& sol_o)
|
|
||||||
{
|
|
||||||
NonHermitianSchurDiagTwoOperator<Matrix,Field> _OpEO(_Matrix);
|
|
||||||
this->_HermitianRBSolver(_OpEO, src_o, sol_o);
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual void RedBlackSolve(Matrix& _Matrix, const std::vector<Field>& src_o, std::vector<Field>& sol_o)
|
|
||||||
{
|
|
||||||
NonHermitianSchurDiagTwoOperator<Matrix,Field> _OpEO(_Matrix);
|
|
||||||
this->_HermitianRBSolver(_OpEO, src_o, sol_o);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,631 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/Aggregates.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
|
||||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|
||||||
Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local>
|
|
||||||
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 */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
inline RealD AggregatePowerLaw(RealD x)
|
|
||||||
{
|
|
||||||
// return std::pow(x,-4);
|
|
||||||
// return std::pow(x,-3);
|
|
||||||
return std::pow(x,-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Fobj,class CComplex,int nbasis>
|
|
||||||
class Aggregation {
|
|
||||||
public:
|
|
||||||
constexpr int Nbasis(void) { return nbasis; };
|
|
||||||
|
|
||||||
typedef iVector<CComplex,nbasis > siteVector;
|
|
||||||
typedef Lattice<siteVector> CoarseVector;
|
|
||||||
typedef Lattice<iMatrix<CComplex,nbasis > > CoarseMatrix;
|
|
||||||
|
|
||||||
typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field
|
|
||||||
typedef Lattice<Fobj > FineField;
|
|
||||||
|
|
||||||
GridBase *CoarseGrid;
|
|
||||||
GridBase *FineGrid;
|
|
||||||
std::vector<Lattice<Fobj> > subspace;
|
|
||||||
int checkerboard;
|
|
||||||
int Checkerboard(void){return checkerboard;}
|
|
||||||
Aggregation(GridBase *_CoarseGrid,GridBase *_FineGrid,int _checkerboard) :
|
|
||||||
CoarseGrid(_CoarseGrid),
|
|
||||||
FineGrid(_FineGrid),
|
|
||||||
subspace(nbasis,_FineGrid),
|
|
||||||
checkerboard(_checkerboard)
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
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){
|
|
||||||
CoarseScalar InnerProd(CoarseGrid);
|
|
||||||
// std::cout << GridLogMessage <<" Block Gramm-Schmidt pass 1"<<std::endl;
|
|
||||||
blockOrthogonalise(InnerProd,subspace);
|
|
||||||
}
|
|
||||||
void ProjectToSubspace(CoarseVector &CoarseVec,const FineField &FineVec){
|
|
||||||
blockProject(CoarseVec,FineVec,subspace);
|
|
||||||
}
|
|
||||||
void PromoteFromSubspace(const CoarseVector &CoarseVec,FineField &FineVec){
|
|
||||||
FineVec.Checkerboard() = subspace[0].Checkerboard();
|
|
||||||
blockPromote(CoarseVec,FineVec,subspace);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void CreateSubspaceRandom(GridParallelRNG &RNG) {
|
|
||||||
int nn=nbasis;
|
|
||||||
RealD scale;
|
|
||||||
FineField noise(FineGrid);
|
|
||||||
for(int b=0;b<nn;b++){
|
|
||||||
subspace[b] = Zero();
|
|
||||||
gaussian(RNG,noise);
|
|
||||||
scale = std::pow(norm2(noise),-0.5);
|
|
||||||
noise=noise*scale;
|
|
||||||
subspace[b] = noise;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtual void CreateSubspace(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,int nn=nbasis)
|
|
||||||
{
|
|
||||||
|
|
||||||
RealD scale;
|
|
||||||
|
|
||||||
ConjugateGradient<FineField> CG(1.0e-4,2000,false);
|
|
||||||
FineField noise(FineGrid);
|
|
||||||
FineField Mn(FineGrid);
|
|
||||||
|
|
||||||
for(int b=0;b<nn;b++){
|
|
||||||
|
|
||||||
subspace[b] = Zero();
|
|
||||||
gaussian(RNG,noise);
|
|
||||||
scale = std::pow(norm2(noise),-0.5);
|
|
||||||
noise=noise*scale;
|
|
||||||
|
|
||||||
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "noise ["<<b<<"] <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
|
||||||
|
|
||||||
for(int i=0;i<2;i++){
|
|
||||||
|
|
||||||
CG(hermop,noise,subspace[b]);
|
|
||||||
|
|
||||||
noise = subspace[b];
|
|
||||||
scale = std::pow(norm2(noise),-0.5);
|
|
||||||
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;
|
|
||||||
subspace[b] = noise;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void CreateSubspaceGCR(GridParallelRNG &RNG,LinearOperatorBase<FineField> &DiracOp,int nn=nbasis)
|
|
||||||
{
|
|
||||||
RealD scale;
|
|
||||||
|
|
||||||
TrivialPrecon<FineField> simple_fine;
|
|
||||||
// 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,30,DiracOp,simple_fine,12,12);
|
|
||||||
// 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 src(FineGrid);
|
|
||||||
FineField guess(FineGrid);
|
|
||||||
FineField Mn(FineGrid);
|
|
||||||
|
|
||||||
for(int b=0;b<nn;b++){
|
|
||||||
|
|
||||||
subspace[b] = Zero();
|
|
||||||
gaussian(RNG,noise);
|
|
||||||
scale = std::pow(norm2(noise),-0.5);
|
|
||||||
noise=noise*scale;
|
|
||||||
|
|
||||||
DiracOp.Op(noise,Mn); std::cout<<GridLogMessage << "noise ["<<b<<"] <n|Op|n> "<<innerProduct(noise,Mn)<<std::endl;
|
|
||||||
|
|
||||||
for(int i=0;i<3;i++){
|
|
||||||
// void operator() (const Field &src, Field &psi){
|
|
||||||
#if 1
|
|
||||||
if (i==0)std::cout << GridLogMessage << " inverting on noise "<<std::endl;
|
|
||||||
src = noise;
|
|
||||||
guess=Zero();
|
|
||||||
GCR(src,guess);
|
|
||||||
subspace[b] = guess;
|
|
||||||
#else
|
|
||||||
if (i==0)std::cout << GridLogMessage << " inverting on zero "<<std::endl;
|
|
||||||
src=Zero();
|
|
||||||
guess = noise;
|
|
||||||
GCR(src,guess);
|
|
||||||
subspace[b] = guess;
|
|
||||||
#endif
|
|
||||||
noise = subspace[b];
|
|
||||||
scale = std::pow(norm2(noise),-0.5);
|
|
||||||
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;
|
|
||||||
subspace[b] = noise;
|
|
||||||
|
|
||||||
}
|
|
||||||
GlobalOrthonormalise();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// World of possibilities here. But have tried quite a lot of experiments (250+ jobs run on Summit)
|
|
||||||
// and this is the best I found
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
virtual void CreateSubspaceChebyshev(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,
|
|
||||||
int nn,
|
|
||||||
double hi,
|
|
||||||
double lo,
|
|
||||||
int orderfilter,
|
|
||||||
int ordermin,
|
|
||||||
int orderstep,
|
|
||||||
double filterlo
|
|
||||||
) {
|
|
||||||
|
|
||||||
RealD scale;
|
|
||||||
|
|
||||||
FineField noise(FineGrid);
|
|
||||||
FineField Mn(FineGrid);
|
|
||||||
FineField tmp(FineGrid);
|
|
||||||
|
|
||||||
// New normalised noise
|
|
||||||
gaussian(RNG,noise);
|
|
||||||
scale = std::pow(norm2(noise),-0.5);
|
|
||||||
noise=noise*scale;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<<" Chebyshev subspace pass-1 : ord "<<orderfilter<<" ["<<lo<<","<<hi<<"]"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<" Chebyshev subspace pass-2 : nbasis"<<nn<<" min "
|
|
||||||
<<ordermin<<" step "<<orderstep
|
|
||||||
<<" lo"<<filterlo<<std::endl;
|
|
||||||
|
|
||||||
// Initial matrix element
|
|
||||||
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "noise <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
|
||||||
|
|
||||||
int b =0;
|
|
||||||
{
|
|
||||||
ComplexD ip;
|
|
||||||
// Filter
|
|
||||||
Chebyshev<FineField> Cheb(lo,hi,orderfilter);
|
|
||||||
Cheb(hermop,noise,Mn);
|
|
||||||
// normalise
|
|
||||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
|
||||||
subspace[b] = Mn;
|
|
||||||
|
|
||||||
hermop.Op(Mn,tmp);
|
|
||||||
ip= innerProduct(Mn,tmp);
|
|
||||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|Op|n> "<<norm2(tmp)<<" "<<ip<<std::endl;
|
|
||||||
|
|
||||||
hermop.AdjOp(Mn,tmp);
|
|
||||||
ip = innerProduct(Mn,tmp);
|
|
||||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|AdjOp|n> "<<norm2(tmp)<<" "<<ip<<std::endl;
|
|
||||||
b++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a full sequence of Chebyshevs
|
|
||||||
{
|
|
||||||
lo=filterlo;
|
|
||||||
noise=Mn;
|
|
||||||
|
|
||||||
FineField T0(FineGrid); T0 = noise;
|
|
||||||
FineField T1(FineGrid);
|
|
||||||
FineField T2(FineGrid);
|
|
||||||
FineField y(FineGrid);
|
|
||||||
|
|
||||||
FineField *Tnm = &T0;
|
|
||||||
FineField *Tn = &T1;
|
|
||||||
FineField *Tnp = &T2;
|
|
||||||
|
|
||||||
// Tn=T1 = (xscale M + mscale)in
|
|
||||||
RealD xscale = 2.0/(hi-lo);
|
|
||||||
RealD mscale = -(hi+lo)/(hi-lo);
|
|
||||||
hermop.HermOp(T0,y);
|
|
||||||
T1=y*xscale+noise*mscale;
|
|
||||||
|
|
||||||
for(int n=2;n<=ordermin+orderstep*(nn-2);n++){
|
|
||||||
|
|
||||||
hermop.HermOp(*Tn,y);
|
|
||||||
|
|
||||||
autoView( y_v , y, AcceleratorWrite);
|
|
||||||
autoView( Tn_v , (*Tn), AcceleratorWrite);
|
|
||||||
autoView( Tnp_v , (*Tnp), AcceleratorWrite);
|
|
||||||
autoView( Tnm_v , (*Tnm), AcceleratorWrite);
|
|
||||||
const int Nsimd = CComplex::Nsimd();
|
|
||||||
accelerator_for(ss, FineGrid->oSites(), Nsimd, {
|
|
||||||
coalescedWrite(y_v[ss],xscale*y_v(ss)+mscale*Tn_v(ss));
|
|
||||||
coalescedWrite(Tnp_v[ss],2.0*y_v(ss)-Tnm_v(ss));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Possible more fine grained control is needed than a linear sweep,
|
|
||||||
// but huge productivity gain if this is simple algorithm and not a tunable
|
|
||||||
int m =1;
|
|
||||||
if ( n>=ordermin ) m=n-ordermin;
|
|
||||||
if ( (m%orderstep)==0 ) {
|
|
||||||
Mn=*Tnp;
|
|
||||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
|
||||||
subspace[b] = Mn;
|
|
||||||
|
|
||||||
|
|
||||||
ComplexD ip;
|
|
||||||
|
|
||||||
hermop.Op(Mn,tmp);
|
|
||||||
ip= innerProduct(Mn,tmp);
|
|
||||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|Op|n> "<<norm2(tmp)<<" "<<ip<<std::endl;
|
|
||||||
|
|
||||||
hermop.AdjOp(Mn,tmp);
|
|
||||||
ip = innerProduct(Mn,tmp);
|
|
||||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|AdjOp|n> "<<norm2(tmp)<<" "<<ip<<std::endl;
|
|
||||||
|
|
||||||
b++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cycle pointers to avoid copies
|
|
||||||
FineField *swizzle = Tnm;
|
|
||||||
Tnm =Tn;
|
|
||||||
Tn =Tnp;
|
|
||||||
Tnp =swizzle;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GRID_ASSERT(b==nn);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
virtual void CreateSubspacePolyCheby(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,
|
|
||||||
int nn,
|
|
||||||
double hi,
|
|
||||||
double lo1,
|
|
||||||
int orderfilter,
|
|
||||||
double lo2,
|
|
||||||
int orderstep)
|
|
||||||
{
|
|
||||||
RealD scale;
|
|
||||||
|
|
||||||
FineField noise(FineGrid);
|
|
||||||
FineField Mn(FineGrid);
|
|
||||||
FineField tmp(FineGrid);
|
|
||||||
|
|
||||||
// New normalised noise
|
|
||||||
gaussian(RNG,noise);
|
|
||||||
scale = std::pow(norm2(noise),-0.5);
|
|
||||||
noise=noise*scale;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<<" CreateSubspacePolyCheby "<<std::endl;
|
|
||||||
// Initial matrix element
|
|
||||||
hermop.Op(noise,Mn);
|
|
||||||
std::cout<<GridLogMessage << "noise <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
|
||||||
|
|
||||||
int b =0;
|
|
||||||
{
|
|
||||||
// Filter
|
|
||||||
std::cout << GridLogMessage << "Cheby "<<lo1<<","<<hi<<" "<<orderstep<<std::endl;
|
|
||||||
Chebyshev<FineField> Cheb(lo1,hi,orderfilter);
|
|
||||||
Cheb(hermop,noise,Mn);
|
|
||||||
// normalise
|
|
||||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
|
||||||
subspace[b] = Mn;
|
|
||||||
hermop.Op(Mn,tmp);
|
|
||||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
|
||||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|n> "<<norm2(Mn)<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a full sequence of Chebyshevs
|
|
||||||
for(int n=1;n<nn;n++){
|
|
||||||
std::cout << GridLogMessage << "Cheby "<<lo2<<","<<hi<<" "<<orderstep<<std::endl;
|
|
||||||
Chebyshev<FineField> Cheb(lo2,hi,orderstep);
|
|
||||||
Cheb(hermop,subspace[n-1],Mn);
|
|
||||||
|
|
||||||
for(int m=0;m<n;m++){
|
|
||||||
ComplexD c = innerProduct(subspace[m],Mn);
|
|
||||||
Mn = Mn - c*subspace[m];
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalise
|
|
||||||
scale = std::pow(norm2(Mn),-0.5);
|
|
||||||
Mn=Mn*scale;
|
|
||||||
|
|
||||||
subspace[n]=Mn;
|
|
||||||
|
|
||||||
hermop.Op(Mn,tmp);
|
|
||||||
std::cout<<GridLogMessage << "filt ["<<n<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
|
||||||
std::cout<<GridLogMessage << "filt ["<<n<<"] <n|n> "<<norm2(Mn)<<std::endl;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void CreateSubspaceChebyshev(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,
|
|
||||||
int nn,
|
|
||||||
double hi,
|
|
||||||
double lo,
|
|
||||||
int orderfilter
|
|
||||||
) {
|
|
||||||
|
|
||||||
RealD scale;
|
|
||||||
|
|
||||||
FineField noise(FineGrid);
|
|
||||||
FineField Mn(FineGrid);
|
|
||||||
FineField tmp(FineGrid);
|
|
||||||
|
|
||||||
// New normalised noise
|
|
||||||
std::cout << GridLogMessage<<" Chebyshev subspace pure noise : ord "<<orderfilter<<" ["<<lo<<","<<hi<<"]"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<" Chebyshev subspace pure noise : nbasis "<<nn<<std::endl;
|
|
||||||
|
|
||||||
|
|
||||||
for(int b =0;b<nbasis;b++)
|
|
||||||
{
|
|
||||||
gaussian(RNG,noise);
|
|
||||||
scale = std::pow(norm2(noise),-0.5);
|
|
||||||
noise=noise*scale;
|
|
||||||
|
|
||||||
// Initial matrix element
|
|
||||||
hermop.Op(noise,Mn);
|
|
||||||
if(b==0) std::cout<<GridLogMessage << "noise <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
|
||||||
|
|
||||||
// Filter
|
|
||||||
Chebyshev<FineField> Cheb(lo,hi,orderfilter);
|
|
||||||
Cheb(hermop,noise,Mn);
|
|
||||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
|
||||||
|
|
||||||
// Refine
|
|
||||||
Chebyshev<FineField> PowerLaw(lo,hi,1000,AggregatePowerLaw);
|
|
||||||
noise = Mn;
|
|
||||||
PowerLaw(hermop,noise,Mn);
|
|
||||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
|
||||||
|
|
||||||
// normalise
|
|
||||||
subspace[b] = Mn;
|
|
||||||
hermop.Op(Mn,tmp);
|
|
||||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void CreateSubspaceChebyshevPowerLaw(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,
|
|
||||||
int nn,
|
|
||||||
double hi,
|
|
||||||
int orderfilter
|
|
||||||
) {
|
|
||||||
|
|
||||||
RealD scale;
|
|
||||||
|
|
||||||
FineField noise(FineGrid);
|
|
||||||
FineField Mn(FineGrid);
|
|
||||||
FineField tmp(FineGrid);
|
|
||||||
|
|
||||||
// New normalised noise
|
|
||||||
std::cout << GridLogMessage<<" Chebyshev subspace pure noise : ord "<<orderfilter<<" [0,"<<hi<<"]"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<" Chebyshev subspace pure noise : nbasis "<<nn<<std::endl;
|
|
||||||
|
|
||||||
for(int b =0;b<nbasis;b++)
|
|
||||||
{
|
|
||||||
gaussian(RNG,noise);
|
|
||||||
scale = std::pow(norm2(noise),-0.5);
|
|
||||||
noise=noise*scale;
|
|
||||||
|
|
||||||
// Initial matrix element
|
|
||||||
hermop.Op(noise,Mn);
|
|
||||||
if(b==0) std::cout<<GridLogMessage << "noise <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
|
||||||
// Filter
|
|
||||||
Chebyshev<FineField> Cheb(0.0,hi,orderfilter,AggregatePowerLaw);
|
|
||||||
Cheb(hermop,noise,Mn);
|
|
||||||
// normalise
|
|
||||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
|
||||||
subspace[b] = Mn;
|
|
||||||
hermop.Op(Mn,tmp);
|
|
||||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
virtual void CreateSubspaceChebyshevNew(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,
|
|
||||||
double hi
|
|
||||||
) {
|
|
||||||
|
|
||||||
RealD scale;
|
|
||||||
|
|
||||||
FineField noise(FineGrid);
|
|
||||||
FineField Mn(FineGrid);
|
|
||||||
FineField tmp(FineGrid);
|
|
||||||
|
|
||||||
// New normalised noise
|
|
||||||
for(int b =0;b<nbasis;b++)
|
|
||||||
{
|
|
||||||
gaussian(RNG,noise);
|
|
||||||
scale = std::pow(norm2(noise),-0.5);
|
|
||||||
noise=noise*scale;
|
|
||||||
|
|
||||||
// Initial matrix element
|
|
||||||
hermop.Op(noise,Mn);
|
|
||||||
if(b==0) std::cout<<GridLogMessage << "noise <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
|
||||||
// Filter
|
|
||||||
//#opt2(x) = acheb(x,3,90,300)* acheb(x,1,90,50) * acheb(x,0.5,90,200) * acheb(x,0.05,90,400) * acheb(x,0.01,90,1500)
|
|
||||||
/*266
|
|
||||||
Chebyshev<FineField> Cheb1(3.0,hi,300);
|
|
||||||
Chebyshev<FineField> Cheb2(1.0,hi,50);
|
|
||||||
Chebyshev<FineField> Cheb3(0.5,hi,300);
|
|
||||||
Chebyshev<FineField> Cheb4(0.05,hi,500);
|
|
||||||
Chebyshev<FineField> Cheb5(0.01,hi,2000);
|
|
||||||
*/
|
|
||||||
/* 242 */
|
|
||||||
/*
|
|
||||||
Chebyshev<FineField> Cheb3(0.1,hi,300);
|
|
||||||
Chebyshev<FineField> Cheb2(0.02,hi,1000);
|
|
||||||
Chebyshev<FineField> Cheb1(0.003,hi,2000);
|
|
||||||
8?
|
|
||||||
*/
|
|
||||||
/* How many??
|
|
||||||
*/
|
|
||||||
Chebyshev<FineField> Cheb2(0.001,hi,2500); // 169 iters on HDCG after refine
|
|
||||||
Chebyshev<FineField> Cheb1(0.02,hi,600);
|
|
||||||
|
|
||||||
// Chebyshev<FineField> Cheb2(0.001,hi,1500);
|
|
||||||
// Chebyshev<FineField> Cheb1(0.02,hi,600);
|
|
||||||
Cheb1(hermop,noise,Mn); scale = std::pow(norm2(Mn),-0.5); noise=Mn*scale;
|
|
||||||
hermop.Op(noise,tmp); std::cout<<GridLogMessage << "Cheb1 <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
|
||||||
Cheb2(hermop,noise,Mn); scale = std::pow(norm2(Mn),-0.5); noise=Mn*scale;
|
|
||||||
hermop.Op(noise,tmp); std::cout<<GridLogMessage << "Cheb2 <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
|
||||||
// Cheb3(hermop,noise,Mn); scale = std::pow(norm2(Mn),-0.5); noise=Mn*scale;
|
|
||||||
// hermop.Op(noise,tmp); std::cout<<GridLogMessage << "Cheb3 <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
|
||||||
// Cheb4(hermop,noise,Mn); scale = std::pow(norm2(Mn),-0.5); noise=Mn*scale;
|
|
||||||
// hermop.Op(noise,tmp); std::cout<<GridLogMessage << "Cheb4 <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
|
||||||
// Cheb5(hermop,noise,Mn); scale = std::pow(norm2(Mn),-0.5); noise=Mn*scale;
|
|
||||||
// hermop.Op(noise,tmp); std::cout<<GridLogMessage << "Cheb5 <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
|
||||||
subspace[b] = noise;
|
|
||||||
hermop.Op(subspace[b],tmp);
|
|
||||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<< " norm " << norm2(noise)<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void CreateSubspaceMultishift(GridParallelRNG &RNG,LinearOperatorBase<FineField> &hermop,
|
|
||||||
double Lo,double tol,int maxit)
|
|
||||||
{
|
|
||||||
|
|
||||||
RealD scale;
|
|
||||||
|
|
||||||
FineField noise(FineGrid);
|
|
||||||
FineField Mn(FineGrid);
|
|
||||||
FineField tmp(FineGrid);
|
|
||||||
|
|
||||||
// New normalised noise
|
|
||||||
std::cout << GridLogMessage<<" Multishift subspace : Lo "<<Lo<<std::endl;
|
|
||||||
|
|
||||||
// Filter
|
|
||||||
// [ 1/6(x+Lo) - 1/2(x+2Lo) + 1/2(x+3Lo) -1/6(x+4Lo) = Lo^3 /[ (x+1Lo)(x+2Lo)(x+3Lo)(x+4Lo) ]
|
|
||||||
//
|
|
||||||
// 1/(x+Lo) - 1/(x+2 Lo)
|
|
||||||
double epsilon = Lo/3;
|
|
||||||
std::vector<RealD> alpha({1.0/6.0,-1.0/2.0,1.0/2.0,-1.0/6.0});
|
|
||||||
std::vector<RealD> shifts({Lo,Lo+epsilon,Lo+2*epsilon,Lo+3*epsilon});
|
|
||||||
std::vector<RealD> tols({tol,tol,tol,tol});
|
|
||||||
std::cout << "sizes "<<alpha.size()<<" "<<shifts.size()<<" "<<tols.size()<<std::endl;
|
|
||||||
|
|
||||||
MultiShiftFunction msf(4,0.0,95.0);
|
|
||||||
std::cout << "msf constructed "<<std::endl;
|
|
||||||
msf.poles=shifts;
|
|
||||||
msf.residues=alpha;
|
|
||||||
msf.tolerances=tols;
|
|
||||||
msf.norm=0.0;
|
|
||||||
msf.order=alpha.size();
|
|
||||||
ConjugateGradientMultiShift<FineField> MSCG(maxit,msf);
|
|
||||||
|
|
||||||
for(int b =0;b<nbasis;b++)
|
|
||||||
{
|
|
||||||
gaussian(RNG,noise);
|
|
||||||
scale = std::pow(norm2(noise),-0.5);
|
|
||||||
noise=noise*scale;
|
|
||||||
|
|
||||||
// Initial matrix element
|
|
||||||
hermop.Op(noise,Mn);
|
|
||||||
if(b==0) std::cout<<GridLogMessage << "noise <n|MdagM|n> "<<norm2(Mn)<<std::endl;
|
|
||||||
|
|
||||||
MSCG(hermop,noise,Mn);
|
|
||||||
scale = std::pow(norm2(Mn),-0.5); Mn=Mn*scale;
|
|
||||||
subspace[b] = Mn;
|
|
||||||
hermop.Op(Mn,tmp);
|
|
||||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
virtual void RefineSubspace(LinearOperatorBase<FineField> &hermop,
|
|
||||||
double Lo,double tol,int maxit)
|
|
||||||
{
|
|
||||||
FineField tmp(FineGrid);
|
|
||||||
for(int b =0;b<nbasis;b++)
|
|
||||||
{
|
|
||||||
ConjugateGradient<FineField> CGsloppy(tol,maxit,false);
|
|
||||||
ShiftedHermOpLinearOperator<FineField> ShiftedFineHermOp(hermop,Lo);
|
|
||||||
tmp=Zero();
|
|
||||||
CGsloppy(hermop,subspace[b],tmp);
|
|
||||||
RealD scale = std::pow(norm2(tmp),-0.5); tmp=tmp*scale;
|
|
||||||
subspace[b]=tmp;
|
|
||||||
hermop.Op(subspace[b],tmp);
|
|
||||||
std::cout<<GridLogMessage << "filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtual void RefineSubspaceHDCG(LinearOperatorBase<FineField> &hermop,
|
|
||||||
TwoLevelADEF2mrhs<FineField,CoarseVector> & theHDCG,
|
|
||||||
int nrhs)
|
|
||||||
{
|
|
||||||
std::vector<FineField> src_mrhs(nrhs,FineGrid);
|
|
||||||
std::vector<FineField> res_mrhs(nrhs,FineGrid);
|
|
||||||
FineField tmp(FineGrid);
|
|
||||||
for(int b =0;b<nbasis;b+=nrhs)
|
|
||||||
{
|
|
||||||
tmp = subspace[b];
|
|
||||||
RealD scale = std::pow(norm2(tmp),-0.5); tmp=tmp*scale;
|
|
||||||
subspace[b] =tmp;
|
|
||||||
hermop.Op(subspace[b],tmp);
|
|
||||||
std::cout<<GridLogMessage << "before filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
|
||||||
|
|
||||||
for(int r=0;r<MIN(nbasis-b,nrhs);r++){
|
|
||||||
src_mrhs[r] = subspace[b+r];
|
|
||||||
}
|
|
||||||
for(int r=0;r<nrhs;r++){
|
|
||||||
res_mrhs[r] = Zero();
|
|
||||||
}
|
|
||||||
theHDCG(src_mrhs,res_mrhs);
|
|
||||||
|
|
||||||
for(int r=0;r<MIN(nbasis-b,nrhs);r++){
|
|
||||||
tmp = res_mrhs[r];
|
|
||||||
RealD scale = std::pow(norm2(tmp),-0.5); tmp=tmp*scale;
|
|
||||||
subspace[b+r]=tmp;
|
|
||||||
}
|
|
||||||
hermop.Op(subspace[b],tmp);
|
|
||||||
std::cout<<GridLogMessage << "after filt ["<<b<<"] <n|MdagM|n> "<<norm2(tmp)<<std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
@@ -1,215 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./Grid/algorithms/multigrid/BlockCyclic.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.
|
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution
|
|
||||||
directory
|
|
||||||
*************************************************************************************/
|
|
||||||
/* END LEGAL */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// BlockCyclicLayout: the index arithmetic of a 2D block-cyclic distribution
|
|
||||||
// of an N x N matrix over a Pr x Pc logical process grid with block size nb.
|
|
||||||
//
|
|
||||||
// This is stage 1 of the 2D distributed dense inverse
|
|
||||||
// (documentation/DistributedDenseInverse2D.tex). It is deliberately
|
|
||||||
// COMMUNICATOR-FREE: every mapping is a static pure function of
|
|
||||||
// (N, nb, Pr, Pc), so the whole layout is exhaustively unit-testable on one
|
|
||||||
// rank with no MPI in the loop (Test_blockcyclic). A thin instance layer
|
|
||||||
// binds a world rank to a grid coordinate and caches local extents.
|
|
||||||
//
|
|
||||||
// Conventions (fixed here, relied on by every later stage):
|
|
||||||
//
|
|
||||||
// * Global block b of a dimension with Pg processes is owned by process
|
|
||||||
// coordinate b % Pg (ScaLAPACK csrc=0), and is that process's local
|
|
||||||
// block b / Pg.
|
|
||||||
// * Rank <-> grid coordinate is ROW MAJOR over the process grid:
|
|
||||||
// rank = p*Pc + q , p = rank/Pc , q = rank%Pc .
|
|
||||||
// The eventual ring transport must construct its neighbour tables with
|
|
||||||
// the same convention.
|
|
||||||
// * Local storage is COLUMN MAJOR with ld = mloc, matching BlockRows:
|
|
||||||
// local element (i,j) lives at data[i + j*mloc].
|
|
||||||
// * The trailing partial block (N % nb != 0) belongs to the owner of the
|
|
||||||
// last full-size block position; only that one block is short.
|
|
||||||
//
|
|
||||||
// Element (gi,gj) therefore lives on grid coordinate
|
|
||||||
// ( (gi/nb) % Pr , (gj/nb) % Pc )
|
|
||||||
// at local coordinate
|
|
||||||
// ( ((gi/nb)/Pr)*nb + gi%nb , ((gj/nb)/Pc)*nb + gj%nb ).
|
|
||||||
//
|
|
||||||
// Everything here is host-side integer arithmetic; nothing allocates.
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class BlockCyclicLayout
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Closest-to-square factorisation Pr*Pc == P with Pr <= Pc.
|
|
||||||
// For fixed P the per-rank SUMMA volume N^2 (1/Pr + 1/Pc) is minimised
|
|
||||||
// at the most square grid. P=288 -> 16 x 18.
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
static void ChooseProcessGrid(int P, int &Pr, int &Pc)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(P >= 1);
|
|
||||||
Pr = 1;
|
|
||||||
for(int r=1; (int64_t)r*r <= (int64_t)P; r++)
|
|
||||||
if ( P % r == 0 ) Pr = r;
|
|
||||||
Pc = P / Pr;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Number of rows (or columns) of a dimension of global extent N, block nb,
|
|
||||||
// owned by process coordinate p of Pg. ScaLAPACK "numroc", csrc=0.
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
static int64_t NumLocal(int64_t N, int64_t nb, int p, int Pg)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(N >= 0);
|
|
||||||
GRID_ASSERT(nb >= 1);
|
|
||||||
GRID_ASSERT(p >= 0);
|
|
||||||
GRID_ASSERT(p < Pg);
|
|
||||||
int64_t nblocks = N / nb; // full blocks
|
|
||||||
int64_t extra = N % nb; // trailing partial block
|
|
||||||
int64_t full = nblocks / Pg; // full blocks everyone owns
|
|
||||||
int64_t rem = nblocks % Pg; // coords [0,rem) own one more
|
|
||||||
int64_t n = full*nb;
|
|
||||||
if ( p < (int)rem ) n += nb; // an extra full block
|
|
||||||
if ( p == (int)rem ) n += extra; // the partial block, if any
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Global index -> (owner coordinate, local index) in one dimension.
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
static void GlobalToLocal(int64_t g, int64_t nb, int Pg,
|
|
||||||
int &owner, int64_t &loc)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(g >= 0);
|
|
||||||
int64_t b = g / nb; // global block
|
|
||||||
owner = (int)(b % Pg);
|
|
||||||
loc = (b / Pg)*nb + (g % nb);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// (process coordinate, local index) -> global index in one dimension.
|
|
||||||
// Inverse of GlobalToLocal on the owned set.
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
static int64_t LocalToGlobal(int64_t l, int64_t nb, int p, int Pg)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(l >= 0);
|
|
||||||
int64_t lb = l / nb; // local block
|
|
||||||
int64_t b = lb*Pg + p; // global block
|
|
||||||
return b*nb + (l % nb);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Instance layer: bind a rank of a Pr x Pc grid.
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
int64_t N; // global matrix dimension (square)
|
|
||||||
int64_t nb; // block size
|
|
||||||
int Pr, Pc; // process grid
|
|
||||||
int me; // world rank within the grid, row major
|
|
||||||
int prow, pcol;// my grid coordinate
|
|
||||||
int64_t mloc,nloc; // my local extents; storage column major, ld = mloc
|
|
||||||
|
|
||||||
BlockCyclicLayout(int64_t N_, int64_t nb_, int Pr_, int Pc_, int me_)
|
|
||||||
{
|
|
||||||
N = N_;
|
|
||||||
nb = nb_;
|
|
||||||
Pr = Pr_;
|
|
||||||
Pc = Pc_;
|
|
||||||
me = me_;
|
|
||||||
GRID_ASSERT( N >= 0 );
|
|
||||||
GRID_ASSERT( nb >= 1 );
|
|
||||||
GRID_ASSERT( Pr >= 1 );
|
|
||||||
GRID_ASSERT( Pc >= 1 );
|
|
||||||
GRID_ASSERT( me >= 0 );
|
|
||||||
GRID_ASSERT( me < Pr*Pc );
|
|
||||||
prow = me / Pc; // ROW MAJOR rank convention
|
|
||||||
pcol = me % Pc;
|
|
||||||
mloc = NumLocal(N, nb, prow, Pr);
|
|
||||||
nloc = NumLocal(N, nb, pcol, Pc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Owning rank of global element (gi,gj), row-major rank convention.
|
|
||||||
int OwnerRank(int64_t gi, int64_t gj) const
|
|
||||||
{
|
|
||||||
int pr,pc; int64_t li,lj;
|
|
||||||
GlobalToLocal(gi, nb, Pr, pr, li);
|
|
||||||
GlobalToLocal(gj, nb, Pc, pc, lj);
|
|
||||||
return pr*Pc + pc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// My local storage offset of global element (gi,gj).
|
|
||||||
// The caller must know I own it; asserted, not assumed.
|
|
||||||
int64_t LocalOffset(int64_t gi, int64_t gj) const
|
|
||||||
{
|
|
||||||
int pr,pc; int64_t li,lj;
|
|
||||||
GlobalToLocal(gi, nb, Pr, pr, li);
|
|
||||||
GlobalToLocal(gj, nb, Pc, pc, lj);
|
|
||||||
GRID_ASSERT( pr == prow );
|
|
||||||
GRID_ASSERT( pc == pcol );
|
|
||||||
return li + lj*mloc; // column major, ld = mloc
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do I own global element (gi,gj)?
|
|
||||||
int Owns(int64_t gi, int64_t gj) const
|
|
||||||
{
|
|
||||||
return OwnerRank(gi,gj) == me;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Block-aligned global range [g0,g1) -> my contiguous local range [l0,l1).
|
|
||||||
//
|
|
||||||
// For fixed owner p the local index is monotone in the global index, so a
|
|
||||||
// coordinate's owned elements of ANY global range are contiguous in local
|
|
||||||
// storage; and for a BLOCK-ALIGNED range the bounds are exactly
|
|
||||||
// NumLocal(g0) and NumLocal(g1), because NumLocal(g,...) counts the owned
|
|
||||||
// elements below g. This is what lets a windowed product view the local
|
|
||||||
// sub-matrix of a global window as &data[l0 + c0*mloc] with the SAME ld --
|
|
||||||
// no gather, no copy. Verified exhaustively in Test_blockcyclic T7.
|
|
||||||
//
|
|
||||||
// g0 must be a block multiple; g1 a block multiple or N itself.
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
static void RangeToLocal(int64_t g0, int64_t g1,
|
|
||||||
int64_t N, int64_t nb, int p, int Pg,
|
|
||||||
int64_t &l0, int64_t &l1)
|
|
||||||
{
|
|
||||||
GRID_ASSERT( 0 <= g0 );
|
|
||||||
GRID_ASSERT( g0 <= g1 );
|
|
||||||
GRID_ASSERT( g1 <= N );
|
|
||||||
GRID_ASSERT( g0 % nb == 0 );
|
|
||||||
GRID_ASSERT( (g1 % nb == 0) || (g1 == N) );
|
|
||||||
l0 = NumLocal(g0, nb, p, Pg);
|
|
||||||
l1 = NumLocal(g1, nb, p, Pg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instance forms, rows and columns of my own coordinate.
|
|
||||||
void RowRange(int64_t g0, int64_t g1, int64_t &l0, int64_t &l1) const
|
|
||||||
{ RangeToLocal(g0,g1,N,nb,prow,Pr,l0,l1); }
|
|
||||||
void ColRange(int64_t g0, int64_t g1, int64_t &l0, int64_t &l1) const
|
|
||||||
{ RangeToLocal(g0,g1,N,nb,pcol,Pc,l0,l1); }
|
|
||||||
|
|
||||||
// Size of global block b (the trailing block may be short).
|
|
||||||
int64_t BlockSize(int64_t b) const
|
|
||||||
{
|
|
||||||
int64_t lo = b*nb;
|
|
||||||
GRID_ASSERT( lo < N );
|
|
||||||
return std::min(N, lo+nb) - lo;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
@@ -1,247 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./Grid/algorithms/multigrid/BlockCyclicRedistribute.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.
|
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution
|
|
||||||
directory
|
|
||||||
*************************************************************************************/
|
|
||||||
/* END LEGAL */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Grid/algorithms/multigrid/BlockCyclicSumma.h>
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Stage 4 of the 2D distributed dense inverse: redistribution between the
|
|
||||||
// 1D rank-major row layout (BlockRows: rank r owns contiguous global rows
|
|
||||||
// [rowStart[r], rowStart[r+1]) of an N x N matrix, stored rows x N column
|
|
||||||
// major with ld = rows) and the 2D block-cyclic layout.
|
|
||||||
//
|
|
||||||
// This is what lets the EXISTING stencil->dense import, its certificate,
|
|
||||||
// the fp32 slab conversion and the apply path all remain byte-for-byte
|
|
||||||
// untouched: the 2D inverse slots between them as
|
|
||||||
//
|
|
||||||
// RowsToCyclic -> BlockCyclicSchurInverse::Invert -> CyclicToRows
|
|
||||||
//
|
|
||||||
// Volume is one matrix pass each way -- N^2/P elements per rank (~1 GB at
|
|
||||||
// production), trivial against the inversion itself.
|
|
||||||
//
|
|
||||||
// Transport: PURE POINT-TO-POINT, like everything else in this stack.
|
|
||||||
// Ranks exchange in a round-robin TOURNAMENT (the circle method, on an odd
|
|
||||||
// modulus M so it covers every pair exactly once for any P, with byes):
|
|
||||||
// at round r, ranks x and y are partners iff x+y == r (mod M). Each
|
|
||||||
// meeting handles both directed edges of the pair in ONE SendToRecvFrom,
|
|
||||||
// padded to the larger of the two edge sizes -- SendToRecvFrom carries a
|
|
||||||
// single byte count for both directions, and both endpoints compute the
|
|
||||||
// same max from the shared descriptors, so there is no asymmetric-size
|
|
||||||
// case and no zero-count shape. Pairs with nothing to exchange skip the
|
|
||||||
// round, decided identically at both ends.
|
|
||||||
//
|
|
||||||
// Element enumeration within an edge is canonical -- ascending global
|
|
||||||
// column outer, ascending global row inner -- and each endpoint builds its
|
|
||||||
// OWN local offset tables from the shared descriptors, so no index data is
|
|
||||||
// ever transmitted. The round trip is BITWISE exact (pure data movement,
|
|
||||||
// no arithmetic): Test_schur2d_redist proves it.
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class BlockCyclicRedistribute
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
|
||||||
// The directed edge (1D rank r1, 2D rank r2): global rows of r1's range
|
|
||||||
// whose row-block coordinate is r2's prow; ALL global columns whose
|
|
||||||
// column-block coordinate is r2's pcol. Every rank can enumerate any
|
|
||||||
// edge from (rowStart, layout) alone.
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
|
||||||
static void EdgeRows(const std::vector<int64_t> &rowStart, int r1,
|
|
||||||
const BlockCyclicLayout &L, int r2,
|
|
||||||
std::vector<int64_t> &rows)
|
|
||||||
{
|
|
||||||
rows.clear();
|
|
||||||
int p = r2 / L.Pc; // row-major rank convention
|
|
||||||
for(int64_t i=rowStart[r1]; i<rowStart[r1+1]; i++)
|
|
||||||
if ( (int)((i/L.nb) % L.Pr) == p ) rows.push_back(i);
|
|
||||||
}
|
|
||||||
static void EdgeCols(const BlockCyclicLayout &L, int r2,
|
|
||||||
std::vector<int64_t> &cols)
|
|
||||||
{
|
|
||||||
cols.clear();
|
|
||||||
int q = r2 % L.Pc;
|
|
||||||
for(int64_t b=0; b*L.nb<L.N; b++){
|
|
||||||
if ( (int)(b % L.Pc) != q ) continue;
|
|
||||||
int64_t g0=b*L.nb, g1=std::min(L.N,(b+1)*L.nb);
|
|
||||||
for(int64_t j=g0;j<g1;j++) cols.push_back(j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
|
||||||
// Gather/scatter one edge between a matrix (device, column major, ld)
|
|
||||||
// and a dense edge buffer, through device offset tables.
|
|
||||||
// buffer(a,b) = elem(rows[a], cols[b]), a fastest.
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
|
||||||
static void MoveEdge(int toBuffer,
|
|
||||||
ComplexD *mat, int64_t ld,
|
|
||||||
const std::vector<int64_t> &roff, // per-row offset in mat
|
|
||||||
const std::vector<int64_t> &coff, // per-col offset in mat
|
|
||||||
ComplexD *buf)
|
|
||||||
{
|
|
||||||
int64_t nr = roff.size();
|
|
||||||
int64_t nc = coff.size();
|
|
||||||
if ( !(nr && nc) ) return;
|
|
||||||
deviceVector<int64_t> dro(nr), dco(nc);
|
|
||||||
acceleratorCopyToDevice((void *)&roff[0], (void *)&dro[0], nr*sizeof(int64_t));
|
|
||||||
acceleratorCopyToDevice((void *)&coff[0], (void *)&dco[0], nc*sizeof(int64_t));
|
|
||||||
int64_t *ro = &dro[0];
|
|
||||||
int64_t *co = &dco[0];
|
|
||||||
if ( toBuffer ) {
|
|
||||||
accelerator_for(idx, (uint64_t)(nr*nc), 1, {
|
|
||||||
int64_t b = idx / nr;
|
|
||||||
int64_t a = idx - b*nr;
|
|
||||||
buf[a + b*nr] = mat[ ro[a] + co[b]*ld ];
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
accelerator_for(idx, (uint64_t)(nr*nc), 1, {
|
|
||||||
int64_t b = idx / nr;
|
|
||||||
int64_t a = idx - b*nr;
|
|
||||||
mat[ ro[a] + co[b]*ld ] = buf[a + b*nr];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
|
||||||
// My offset tables for an edge, on whichever side I am.
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
|
||||||
static void Offsets1D(const std::vector<int64_t> &rows,
|
|
||||||
const std::vector<int64_t> &cols,
|
|
||||||
int64_t row0,
|
|
||||||
std::vector<int64_t> &roff, std::vector<int64_t> &coff)
|
|
||||||
{
|
|
||||||
roff.resize(rows.size()); coff.resize(cols.size());
|
|
||||||
for(uint64_t a=0;a<rows.size();a++) roff[a] = rows[a]-row0; // local row
|
|
||||||
for(uint64_t b=0;b<cols.size();b++) coff[b] = cols[b]; // global col
|
|
||||||
}
|
|
||||||
static void Offsets2D(const BlockCyclicLayout &L,
|
|
||||||
const std::vector<int64_t> &rows,
|
|
||||||
const std::vector<int64_t> &cols,
|
|
||||||
std::vector<int64_t> &roff, std::vector<int64_t> &coff)
|
|
||||||
{
|
|
||||||
roff.resize(rows.size()); coff.resize(cols.size());
|
|
||||||
for(uint64_t a=0;a<rows.size();a++){
|
|
||||||
int p; int64_t l;
|
|
||||||
BlockCyclicLayout::GlobalToLocal(rows[a], L.nb, L.Pr, p, l);
|
|
||||||
GRID_ASSERT( p == L.prow );
|
|
||||||
roff[a] = l;
|
|
||||||
}
|
|
||||||
for(uint64_t b=0;b<cols.size();b++){
|
|
||||||
int q; int64_t l;
|
|
||||||
BlockCyclicLayout::GlobalToLocal(cols[b], L.nb, L.Pc, q, l);
|
|
||||||
GRID_ASSERT( q == L.pcol );
|
|
||||||
coff[b] = l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
|
||||||
// The worker. dir=+1 : 1D rows -> block cyclic ; dir=-1 : back.
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
|
||||||
static void Redistribute(int dir, GridBase *grid,
|
|
||||||
const std::vector<int64_t> &rowStart,
|
|
||||||
ComplexD *rows1d, int64_t myrows,
|
|
||||||
BlockCyclicMatrix &A)
|
|
||||||
{
|
|
||||||
BlockCyclicLayout &L = A.layout;
|
|
||||||
int P = grid->ProcessorCount();
|
|
||||||
int me = grid->ThisRank();
|
|
||||||
GRID_ASSERT( (int)rowStart.size() == P+1 );
|
|
||||||
GRID_ASSERT( rowStart[P] == L.N );
|
|
||||||
GRID_ASSERT( rowStart[me+1]-rowStart[me] == myrows );
|
|
||||||
int64_t row0 = rowStart[me];
|
|
||||||
int64_t ld1 = myrows ? myrows : 1;
|
|
||||||
|
|
||||||
std::vector<int64_t> rows, cols, roff, coff;
|
|
||||||
deviceVector<ComplexD> sbuf(1), rbuf(1);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Self edge: purely local, via a bounce buffer (shares all the code).
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
EdgeRows(rowStart, me, L, me, rows);
|
|
||||||
EdgeCols(L, me, cols);
|
|
||||||
if ( rows.size() && cols.size() ){
|
|
||||||
uint64_t ne = rows.size()*cols.size();
|
|
||||||
if ( sbuf.size() < ne ) sbuf.resize(ne);
|
|
||||||
std::vector<int64_t> roff2, coff2;
|
|
||||||
Offsets1D(rows, cols, row0, roff, coff);
|
|
||||||
Offsets2D(L, rows, cols, roff2, coff2);
|
|
||||||
if ( dir > 0 ) {
|
|
||||||
MoveEdge(1, rows1d, ld1, roff, coff, &sbuf[0]);
|
|
||||||
MoveEdge(0, &A.data[0], L.mloc, roff2, coff2, &sbuf[0]);
|
|
||||||
} else {
|
|
||||||
MoveEdge(1, &A.data[0], L.mloc, roff2, coff2, &sbuf[0]);
|
|
||||||
MoveEdge(0, rows1d, ld1, roff, coff, &sbuf[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Tournament over all pairs: odd modulus M, partner = (r - me) mod M.
|
|
||||||
// Every unordered pair meets exactly once; partner==me or >=P is a bye.
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
int M = (P%2) ? P : P+1;
|
|
||||||
for(int r=0;r<M;r++){
|
|
||||||
int partner = (int)(((int64_t)r - me + 2L*M) % M);
|
|
||||||
if ( partner == me || partner >= P ) continue;
|
|
||||||
|
|
||||||
// outbound edge: my (dir>0 ? 1D rows : 2D data) -> partner
|
|
||||||
// inbound edge: partner -> my (dir>0 ? 2D data : 1D rows)
|
|
||||||
std::vector<int64_t> orow, ocol, irow, icol;
|
|
||||||
if ( dir > 0 ) { EdgeRows(rowStart, me, L, partner, orow); EdgeCols(L, partner, ocol);
|
|
||||||
EdgeRows(rowStart, partner, L, me, irow); EdgeCols(L, me, icol); }
|
|
||||||
else { EdgeRows(rowStart, partner, L, me, orow); EdgeCols(L, me, ocol);
|
|
||||||
EdgeRows(rowStart, me, L, partner, irow); EdgeCols(L, partner, icol); }
|
|
||||||
|
|
||||||
uint64_t nout = orow.size()*ocol.size();
|
|
||||||
uint64_t nin = irow.size()*icol.size();
|
|
||||||
if ( !(nout || nin) ) continue; // both ends compute this identically
|
|
||||||
|
|
||||||
uint64_t nmax = std::max(nout,nin); // symmetric padded transfer
|
|
||||||
if ( sbuf.size() < nmax ) sbuf.resize(nmax);
|
|
||||||
if ( rbuf.size() < nmax ) rbuf.resize(nmax);
|
|
||||||
|
|
||||||
if ( nout ){
|
|
||||||
if ( dir > 0 ) { Offsets1D(orow, ocol, row0, roff, coff);
|
|
||||||
MoveEdge(1, rows1d, ld1, roff, coff, &sbuf[0]); }
|
|
||||||
else { Offsets2D(L, orow, ocol, roff, coff);
|
|
||||||
MoveEdge(1, &A.data[0], L.mloc, roff, coff, &sbuf[0]); }
|
|
||||||
}
|
|
||||||
grid->SendToRecvFrom((void *)&sbuf[0], partner,
|
|
||||||
(void *)&rbuf[0], partner,
|
|
||||||
nmax*sizeof(ComplexD));
|
|
||||||
if ( nin ){
|
|
||||||
if ( dir > 0 ) { Offsets2D(L, irow, icol, roff, coff);
|
|
||||||
MoveEdge(0, &A.data[0], L.mloc, roff, coff, &rbuf[0]); }
|
|
||||||
else { Offsets1D(irow, icol, row0, roff, coff);
|
|
||||||
MoveEdge(0, rows1d, ld1, roff, coff, &rbuf[0]); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void RowsToCyclic(GridBase *grid, const std::vector<int64_t> &rowStart,
|
|
||||||
ComplexD *rows1d, int64_t myrows, BlockCyclicMatrix &A)
|
|
||||||
{ Redistribute(+1, grid, rowStart, rows1d, myrows, A); }
|
|
||||||
|
|
||||||
static void CyclicToRows(GridBase *grid, const std::vector<int64_t> &rowStart,
|
|
||||||
BlockCyclicMatrix &A, ComplexD *rows1d, int64_t myrows)
|
|
||||||
{ Redistribute(-1, grid, rowStart, rows1d, myrows, A); }
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
@@ -1,291 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./Grid/algorithms/multigrid/BlockCyclicSchurInverse.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.
|
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution
|
|
||||||
directory
|
|
||||||
*************************************************************************************/
|
|
||||||
/* END LEGAL */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Grid/algorithms/blas/BatchedInverse.h>
|
|
||||||
#include <Grid/algorithms/multigrid/BlockCyclicSumma.h>
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Stage 3 of the 2D distributed dense inverse: the recursive Schur
|
|
||||||
// complement on a block-cyclic matrix, in place.
|
|
||||||
//
|
|
||||||
// The nine-step algebra is IDENTICAL to RecursiveSchurInverse (1D); what
|
|
||||||
// changes is the decomposition. The recursion splits the GLOBAL INDEX
|
|
||||||
// RANGE at the block boundary nearest the midpoint -- not the rank range --
|
|
||||||
// so every rank owns part of every sub-block at every depth, and the
|
|
||||||
// ownership gating (inI/inJ, dummy operands, zero-width rank ranges) of the
|
|
||||||
// 1D scheme has no analogue here: it is simply gone.
|
|
||||||
//
|
|
||||||
// I = [c0,m) J = [m,c1) (block-aligned, m the mid block boundary)
|
|
||||||
// 1. recurse I : A11 -> A11inv (in place)
|
|
||||||
// 2. Bt = A11inv . A12 (scratch, I x J)
|
|
||||||
// 3. Ct = A21 . A11inv (scratch, J x I)
|
|
||||||
// 4. A22 -= A21 . Bt == S (in place)
|
|
||||||
// 5. recurse J : S -> Sinv (in place)
|
|
||||||
// 6. Tt = Sinv . Ct (scratch, J x I)
|
|
||||||
// 7. Ut = Bt . Sinv (scratch, I x J)
|
|
||||||
// 8. A11 += Ut . Ct == X11 (in place)
|
|
||||||
// 9. A12 = -Ut , A21 = -Tt (window copies)
|
|
||||||
//
|
|
||||||
// SCRATCH SHARING. Four full-size block-cyclic scratch matrices (Bt, Ct,
|
|
||||||
// Tt, Ut) serve the ENTIRE tree, used through windows. This is safe at
|
|
||||||
// every depth because of a window-disjointness invariant:
|
|
||||||
//
|
|
||||||
// * every temporary of a node has its row range in one half of the
|
|
||||||
// node's window and its column range in the other (I x J or J x I);
|
|
||||||
// * everything any DESCENDANT touches -- its A windows and its own
|
|
||||||
// temporaries -- has BOTH ranges inside a single half (I x I during
|
|
||||||
// step 1, J x J during step 5).
|
|
||||||
//
|
|
||||||
// Hence a descendant window and a live ancestor temporary always differ in
|
|
||||||
// at least one dimension by disjoint ranges. Only Bt and Ct are live
|
|
||||||
// across the step-5 recursion (Tt, Ut are written after it), and both are
|
|
||||||
// covered by the invariant.
|
|
||||||
//
|
|
||||||
// LEAF. A leaf is a single diagonal block, and block (b,b) of a
|
|
||||||
// block-cyclic layout lives ENTIRELY on rank (b%Pr, b%Pc). The leaf
|
|
||||||
// inversion is therefore purely local -- pack the strided block dense,
|
|
||||||
// GridBLASInverse, unpack -- with NO communication and no assembly. The
|
|
||||||
// leaf-assembly transport question of the 1D scheme does not arise.
|
|
||||||
// Successive leaves cycle over ranks, so leaf work is naturally spread.
|
|
||||||
//
|
|
||||||
// COMMUNICATION. Every transfer in the whole inversion is a
|
|
||||||
// SendToRecvFrom inside BlockCyclicSumma's rings: pure point-to-point, no
|
|
||||||
// collectives on the critical path, deterministic summation order (so
|
|
||||||
// repeated inversions are bitwise identical). ReportTelemetry() is the
|
|
||||||
// one optional exception: it performs reductions, and is only ever called
|
|
||||||
// explicitly by a caller who wants the numbers.
|
|
||||||
//
|
|
||||||
// NUMERICS. No pivoting, exactly as the 1D scheme: every A11 and every
|
|
||||||
// Schur complement met on the way down must be non-singular. The growth
|
|
||||||
// telemetry stands in for pivoting; note the recursion splits differently
|
|
||||||
// from the 1D rank-range tree, so DIFFERENT sub-blocks are inverted and
|
|
||||||
// telemetry values are NOT comparable with the 1D implementation's --
|
|
||||||
// re-baseline, do not compare.
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class BlockCyclicSchurInverse
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
BlockCyclicSumma SUMMA;
|
|
||||||
GridBLASInverse INV;
|
|
||||||
|
|
||||||
// Telemetry: accumulated LOCALLY, no comms unless ReportTelemetry().
|
|
||||||
double telLeafMaxInv;
|
|
||||||
uint64_t nLeaf;
|
|
||||||
uint64_t nNode;
|
|
||||||
double tLeaf;
|
|
||||||
double tGemm; // wall in Multiply calls (comms+gemm)
|
|
||||||
double tCopy;
|
|
||||||
|
|
||||||
BlockCyclicSchurInverse()
|
|
||||||
{
|
|
||||||
telLeafMaxInv = 0.0;
|
|
||||||
nLeaf = nNode = 0;
|
|
||||||
tLeaf = tGemm = tCopy = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Window copy-scale: Dst[i0:i1, j0:j1] = alpha * Src[same window].
|
|
||||||
// Both share one layout, so the local bands coincide; pure local kernel.
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
void WindowCopyScale(ComplexD alpha,
|
|
||||||
BlockCyclicMatrix &Src, BlockCyclicMatrix &Dst,
|
|
||||||
int64_t i0, int64_t i1, int64_t j0, int64_t j1)
|
|
||||||
{
|
|
||||||
BlockCyclicLayout &L = Dst.layout;
|
|
||||||
GRID_ASSERT( Src.layout.N==L.N && Src.layout.nb==L.nb );
|
|
||||||
GRID_ASSERT( Src.layout.Pr==L.Pr && Src.layout.Pc==L.Pc );
|
|
||||||
int64_t li0,li1, lj0,lj1;
|
|
||||||
L.RowRange(i0,i1, li0,li1);
|
|
||||||
L.ColRange(j0,j1, lj0,lj1);
|
|
||||||
int64_t m = li1-li0, n = lj1-lj0;
|
|
||||||
if ( !(m && n) ) return;
|
|
||||||
ComplexD *src = Src.LocalWindow(li0,lj0);
|
|
||||||
ComplexD *dst = Dst.LocalWindow(li0,lj0);
|
|
||||||
int64_t ldS = Src.layout.mloc;
|
|
||||||
int64_t ldD = L.mloc;
|
|
||||||
tCopy -= usecond();
|
|
||||||
accelerator_for(idx, (uint64_t)(m*n), 1, {
|
|
||||||
int64_t jj = idx / m;
|
|
||||||
int64_t ii = idx - jj*m;
|
|
||||||
dst[ii + jj*ldD] = alpha*src[ii + jj*ldS];
|
|
||||||
});
|
|
||||||
tCopy += usecond();
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Leaf: single diagonal block (b,b), entirely on rank (b%Pr, b%Pc).
|
|
||||||
// Local pack -> dense inverse -> unpack; every other rank does nothing
|
|
||||||
// and needs no synchronisation: the next SUMMA's rings pair them up.
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
void Leaf(BlockCyclicMatrix &A, int64_t b)
|
|
||||||
{
|
|
||||||
BlockCyclicLayout &L = A.layout;
|
|
||||||
nLeaf++;
|
|
||||||
if ( (int)(b % L.Pr) != L.prow ) return;
|
|
||||||
if ( (int)(b % L.Pc) != L.pcol ) return;
|
|
||||||
|
|
||||||
tLeaf -= usecond();
|
|
||||||
int64_t g0 = b*L.nb;
|
|
||||||
int64_t g1 = std::min(L.N, g0+L.nb);
|
|
||||||
int64_t w = g1-g0;
|
|
||||||
int64_t lr0,lr1, lc0,lc1;
|
|
||||||
L.RowRange(g0,g1, lr0,lr1);
|
|
||||||
L.ColRange(g0,g1, lc0,lc1);
|
|
||||||
GRID_ASSERT( lr1-lr0 == w );
|
|
||||||
GRID_ASSERT( lc1-lc0 == w );
|
|
||||||
|
|
||||||
// Pack the strided block dense (inverseBatched assumes lda == w).
|
|
||||||
deviceVector<ComplexD> dense((uint64_t)w*w);
|
|
||||||
{
|
|
||||||
ComplexD *src = A.LocalWindow(lr0,lc0);
|
|
||||||
ComplexD *dst = &dense[0];
|
|
||||||
int64_t ld = L.mloc;
|
|
||||||
accelerator_for(idx, (uint64_t)(w*w), 1, {
|
|
||||||
int64_t jj = idx / w;
|
|
||||||
int64_t ii = idx - jj*w;
|
|
||||||
dst[ii + jj*w] = src[ii + jj*ld];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
{
|
|
||||||
deviceVector<ComplexD*> bp(1);
|
|
||||||
std::vector<ComplexD*> ptr(1);
|
|
||||||
ptr[0] = &dense[0];
|
|
||||||
acceleratorCopyToDevice(&ptr[0], &bp[0], sizeof(ComplexD*));
|
|
||||||
INV.inverseBatched(w, bp);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
ComplexD *src = &dense[0];
|
|
||||||
ComplexD *dst = A.LocalWindow(lr0,lc0);
|
|
||||||
int64_t ld = L.mloc;
|
|
||||||
accelerator_for(idx, (uint64_t)(w*w), 1, {
|
|
||||||
int64_t jj = idx / w;
|
|
||||||
int64_t ii = idx - jj*w;
|
|
||||||
dst[ii + jj*ld] = src[ii + jj*w];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Growth telemetry, local only.
|
|
||||||
{
|
|
||||||
std::vector<ComplexD> h((uint64_t)w*w);
|
|
||||||
acceleratorCopyFromDevice(&dense[0], &h[0], h.size()*sizeof(ComplexD));
|
|
||||||
double mx = 0.0;
|
|
||||||
for(auto &z : h){
|
|
||||||
double re=z.real(), im=z.imag();
|
|
||||||
mx = std::max(mx, re*re+im*im);
|
|
||||||
}
|
|
||||||
telLeafMaxInv = std::max(telLeafMaxInv, std::sqrt(mx));
|
|
||||||
}
|
|
||||||
tLeaf += usecond();
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// The recursion, on global BLOCK range [b0,b1). SPMD: every rank calls
|
|
||||||
// with identical arguments; there is no ownership gating to get wrong.
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
void SchurNode(BlockCyclicMatrix &A,
|
|
||||||
BlockCyclicMatrix &Bt, BlockCyclicMatrix &Ct,
|
|
||||||
BlockCyclicMatrix &Tt, BlockCyclicMatrix &Ut,
|
|
||||||
int64_t b0, int64_t b1)
|
|
||||||
{
|
|
||||||
BlockCyclicLayout &L = A.layout;
|
|
||||||
int64_t span = b1-b0;
|
|
||||||
GRID_ASSERT( span >= 1 );
|
|
||||||
if ( span == 1 ) { Leaf(A, b0); return; }
|
|
||||||
nNode++;
|
|
||||||
|
|
||||||
int64_t bm = b0 + span/2;
|
|
||||||
int64_t c0 = b0*L.nb;
|
|
||||||
int64_t m = bm*L.nb;
|
|
||||||
int64_t c1 = std::min(L.N, b1*L.nb);
|
|
||||||
ComplexD one (1.0,0.0), mone(-1.0,0.0), zero(0.0,0.0);
|
|
||||||
|
|
||||||
// 1. A11 -> A11inv
|
|
||||||
SchurNode(A,Bt,Ct,Tt,Ut, b0,bm);
|
|
||||||
|
|
||||||
tGemm -= usecond();
|
|
||||||
// 2. Bt[I,J] = A11inv . A12
|
|
||||||
SUMMA.Multiply(one, A, A, zero, Bt, c0,m, m,c1, c0,m );
|
|
||||||
// 3. Ct[J,I] = A21 . A11inv
|
|
||||||
SUMMA.Multiply(one, A, A, zero, Ct, m,c1, c0,m, c0,m );
|
|
||||||
// 4. A22 -= A21 . Bt (the Schur complement, in place)
|
|
||||||
SUMMA.Multiply(mone, A, Bt, one, A, m,c1, m,c1, c0,m );
|
|
||||||
tGemm += usecond();
|
|
||||||
|
|
||||||
// 5. S -> Sinv (Bt, Ct live across this call: see invariant)
|
|
||||||
SchurNode(A,Bt,Ct,Tt,Ut, bm,b1);
|
|
||||||
|
|
||||||
tGemm -= usecond();
|
|
||||||
// 6. Tt[J,I] = Sinv . Ct
|
|
||||||
SUMMA.Multiply(one, A, Ct, zero, Tt, m,c1, c0,m, m,c1 );
|
|
||||||
// 7. Ut[I,J] = Bt . Sinv
|
|
||||||
SUMMA.Multiply(one, Bt, A, zero, Ut, c0,m, m,c1, m,c1 );
|
|
||||||
// 8. A11 += Ut . Ct
|
|
||||||
SUMMA.Multiply(one, Ut, Ct, one, A, c0,m, c0,m, m,c1 );
|
|
||||||
tGemm += usecond();
|
|
||||||
|
|
||||||
// 9. Off-diagonal signs
|
|
||||||
WindowCopyScale(mone, Ut, A, c0,m, m,c1);
|
|
||||||
WindowCopyScale(mone, Tt, A, m,c1, c0,m);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// PUBLIC ENTRY. In-place inverse of the whole matrix. Scratch (4x the
|
|
||||||
// matrix footprint) is allocated here and released on return.
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
void Invert(BlockCyclicMatrix &A)
|
|
||||||
{
|
|
||||||
BlockCyclicLayout &L = A.layout;
|
|
||||||
GRID_ASSERT( L.N >= 1 );
|
|
||||||
int64_t nblocks = (L.N + L.nb - 1)/L.nb;
|
|
||||||
|
|
||||||
BlockCyclicMatrix Bt(A.grid, L.N, L.nb, L.Pr, L.Pc);
|
|
||||||
BlockCyclicMatrix Ct(A.grid, L.N, L.nb, L.Pr, L.Pc);
|
|
||||||
BlockCyclicMatrix Tt(A.grid, L.N, L.nb, L.Pr, L.Pc);
|
|
||||||
BlockCyclicMatrix Ut(A.grid, L.N, L.nb, L.Pr, L.Pc);
|
|
||||||
|
|
||||||
telLeafMaxInv = 0.0;
|
|
||||||
nLeaf = nNode = 0;
|
|
||||||
tLeaf = tGemm = tCopy = 0.0;
|
|
||||||
|
|
||||||
SchurNode(A, Bt,Ct,Tt,Ut, 0, nblocks);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Optional, and the ONLY place any reduction happens: call it if you
|
|
||||||
// want the numbers, never from Invert.
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
void ReportTelemetry(GridBase *grid)
|
|
||||||
{
|
|
||||||
RealD mx = telLeafMaxInv;
|
|
||||||
grid->GlobalMax(mx);
|
|
||||||
std::cout << GridLogMessage << "BlockCyclicSchurInverse:"
|
|
||||||
<< " nodes " << nNode << " leaves " << nLeaf
|
|
||||||
<< " max|leafinv| " << mx
|
|
||||||
<< " (boss secs: gemm+comms " << tGemm/1.0e6
|
|
||||||
<< " leaf " << tLeaf/1.0e6
|
|
||||||
<< " copy " << tCopy/1.0e6 << ")"
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
@@ -1,299 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./Grid/algorithms/multigrid/BlockCyclicSumma.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.
|
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution
|
|
||||||
directory
|
|
||||||
*************************************************************************************/
|
|
||||||
/* END LEGAL */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Grid/algorithms/multigrid/BlockCyclic.h>
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Stage 2 of the 2D distributed dense inverse: the windowed SUMMA product
|
|
||||||
//
|
|
||||||
// C[i0:i1, j0:j1] <- beta C[i0:i1, j0:j1]
|
|
||||||
// + alpha A[i0:i1, k0:k1] . B[k0:k1, j0:j1]
|
|
||||||
//
|
|
||||||
// on block-cyclic matrices sharing one BlockCyclicLayout. All ranges are
|
|
||||||
// BLOCK ALIGNED (multiples of nb, or N itself at the top end): stage 3's
|
|
||||||
// recursion splits on block boundaries, so nothing else is ever needed, and
|
|
||||||
// alignment makes every local window a contiguous band of local storage
|
|
||||||
// (BlockCyclic.h RangeToLocal, Test_blockcyclic T7).
|
|
||||||
//
|
|
||||||
// Transport is PURE POINT-TO-POINT: SendToRecvFrom on explicit world ranks
|
|
||||||
// computed from the row-major rank convention. No collectives of any kind
|
|
||||||
// -- no sub-communicators, no broadcast, no allgather -- by design: the
|
|
||||||
// collective pathologies measured on this machine (MPI_Allgatherv at
|
|
||||||
// ~0.18 MB/s with skewed counts, mpir_request.h:508 aborts, allreduce at
|
|
||||||
// 39% of the P2P rate) motivated this implementation. SendToRecvFrom is
|
|
||||||
// the most exercised device-buffer path in Grid and the only one never
|
|
||||||
// implicated.
|
|
||||||
//
|
|
||||||
// Algorithm: round-based ring allgather SUMMA. The k range is processed in
|
|
||||||
// rounds of Pc consecutive global blocks. Within a round
|
|
||||||
//
|
|
||||||
// * process column c owns at most one A panel (blocks s with s%Pc == c);
|
|
||||||
// the Pc panels circulate around each process-ROW ring in Pc-1 steps;
|
|
||||||
// * process row r owns up to ceil(Pc/Pr) B panels (blocks s%Pr == r);
|
|
||||||
// they circulate around each process-COLUMN ring in Pr-1 steps;
|
|
||||||
// * every rank then accumulates Cloc += alpha * Apanel_s . Bpanel_s
|
|
||||||
// for each block s of the round, in ascending s: a fixed summation
|
|
||||||
// order, so REPEATED RUNS ARE BITWISE IDENTICAL (no reduction, no
|
|
||||||
// order ambiguity -- the property the P2P doctrine buys).
|
|
||||||
//
|
|
||||||
// Ring chunks are PADDED to a fixed size (full nb panels, fixed
|
|
||||||
// panels-per-origin): SendToRecvFrom carries one byte count for both
|
|
||||||
// directions, so symmetric transfers eliminate every variable-size edge
|
|
||||||
// case at a worst-case ~1/Pc extra volume on ragged rounds. Padding is
|
|
||||||
// never read: GEMMs address only the leading nb_s x width of each slot.
|
|
||||||
//
|
|
||||||
// Per-rank received volume: (k-extent) * (mloc_i + nloc_j) elements --
|
|
||||||
// the N^2 (1/Pr + 1/Pc) SUMMA optimum, ~sqrt(P)/2 below the 1D scheme.
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class BlockCyclicMatrix
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
GridBase *grid; // borrowed, never owned
|
|
||||||
BlockCyclicLayout layout;
|
|
||||||
deviceVector<ComplexD> data; // column major, ld = layout.mloc
|
|
||||||
|
|
||||||
BlockCyclicMatrix(GridBase *g, int64_t N, int64_t nb, int Pr, int Pc)
|
|
||||||
: grid(g),
|
|
||||||
layout(N, nb, Pr, Pc, g->ThisRank())
|
|
||||||
{
|
|
||||||
GRID_ASSERT( Pr*Pc == g->ProcessorCount() );
|
|
||||||
uint64_t sz = (uint64_t)layout.mloc*layout.nloc;
|
|
||||||
data.resize( sz ? sz : 1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
ComplexD *LocalWindow(int64_t li, int64_t lj)
|
|
||||||
{
|
|
||||||
return &data[0] + li + lj*layout.mloc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
|
||||||
// TEST-SCALE import/export of a replicated global matrix (host, O(N^2)
|
|
||||||
// loops, one collective in Export). For unit tests and the stage-3
|
|
||||||
// oracle only; production data enters through the direct block-cyclic
|
|
||||||
// import, never through these.
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
|
||||||
void ImportGlobal(const std::vector<ComplexD> &G)
|
|
||||||
{
|
|
||||||
int64_t N = layout.N;
|
|
||||||
GRID_ASSERT( (int64_t)G.size() == N*N );
|
|
||||||
std::vector<ComplexD> h((uint64_t)layout.mloc*layout.nloc, ComplexD(0.0,0.0));
|
|
||||||
for(int64_t j=0;j<N;j++){
|
|
||||||
for(int64_t i=0;i<N;i++){
|
|
||||||
if ( layout.Owns(i,j) ) h[layout.LocalOffset(i,j)] = G[i + j*N];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( h.size() )
|
|
||||||
acceleratorCopyToDevice(&h[0], &data[0], h.size()*sizeof(ComplexD));
|
|
||||||
}
|
|
||||||
void ExportGlobal(std::vector<ComplexD> &G)
|
|
||||||
{
|
|
||||||
int64_t N = layout.N;
|
|
||||||
G.assign((uint64_t)N*N, ComplexD(0.0,0.0));
|
|
||||||
std::vector<ComplexD> h((uint64_t)layout.mloc*layout.nloc);
|
|
||||||
if ( h.size() )
|
|
||||||
acceleratorCopyFromDevice(&data[0], &h[0], h.size()*sizeof(ComplexD));
|
|
||||||
for(int64_t j=0;j<N;j++){
|
|
||||||
for(int64_t i=0;i<N;i++){
|
|
||||||
if ( layout.Owns(i,j) ) G[i + j*N] = h[layout.LocalOffset(i,j)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( N ) grid->GlobalSumVector((ComplexD *)&G[0], (int)(N*N)); // zero-fill: exact
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class BlockCyclicSumma
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
GridBLAS BLAS;
|
|
||||||
|
|
||||||
static int Overlap(int64_t a0,int64_t a1,int64_t b0,int64_t b1)
|
|
||||||
{ return (a0 < b1) && (b0 < a1); }
|
|
||||||
|
|
||||||
void Multiply(ComplexD alpha,
|
|
||||||
BlockCyclicMatrix &A,
|
|
||||||
BlockCyclicMatrix &B,
|
|
||||||
ComplexD beta,
|
|
||||||
BlockCyclicMatrix &C,
|
|
||||||
int64_t i0, int64_t i1,
|
|
||||||
int64_t j0, int64_t j1,
|
|
||||||
int64_t k0, int64_t k1)
|
|
||||||
{
|
|
||||||
BlockCyclicLayout &L = C.layout;
|
|
||||||
GridBase *grid = C.grid;
|
|
||||||
const int64_t N = L.N;
|
|
||||||
const int64_t nb = L.nb;
|
|
||||||
const int Pr = L.Pr, Pc = L.Pc;
|
|
||||||
const int prow = L.prow, pcol = L.pcol;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Conformability: one layout, one communicator, aligned ranges.
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
auto same = [&](BlockCyclicLayout &X){
|
|
||||||
GRID_ASSERT( X.N==N ); GRID_ASSERT( X.nb==nb );
|
|
||||||
GRID_ASSERT( X.Pr==Pr ); GRID_ASSERT( X.Pc==Pc );
|
|
||||||
GRID_ASSERT( X.me==L.me );
|
|
||||||
};
|
|
||||||
same(A.layout); same(B.layout);
|
|
||||||
GRID_ASSERT( A.grid==grid ); GRID_ASSERT( B.grid==grid );
|
|
||||||
auto aligned = [&](int64_t g0,int64_t g1){
|
|
||||||
GRID_ASSERT( 0<=g0 ); GRID_ASSERT( g0<=g1 ); GRID_ASSERT( g1<=N );
|
|
||||||
GRID_ASSERT( g0%nb==0 ); GRID_ASSERT( (g1%nb==0)||(g1==N) );
|
|
||||||
};
|
|
||||||
aligned(i0,i1); aligned(j0,j1); aligned(k0,k1);
|
|
||||||
GRID_ASSERT( k1 > k0 ); // pure scaling not supported here
|
|
||||||
// In-place windows are legal only if the written window is disjoint
|
|
||||||
// from anything read (stage 3 uses this; make violation loud).
|
|
||||||
if ( &C==&A ) GRID_ASSERT( !Overlap(j0,j1,k0,k1) );
|
|
||||||
if ( &C==&B ) GRID_ASSERT( !Overlap(i0,i1,k0,k1) );
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// My local bands of the three windows (contiguous: T7).
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
int64_t li0,li1, lj0,lj1;
|
|
||||||
L.RowRange(i0,i1, li0,li1);
|
|
||||||
L.ColRange(j0,j1, lj0,lj1);
|
|
||||||
const int64_t mloc_i = li1-li0; // my rows of the i window
|
|
||||||
const int64_t nloc_j = lj1-lj0; // my cols of the j window
|
|
||||||
|
|
||||||
const int64_t kb0 = k0/nb;
|
|
||||||
const int64_t kb1 = (k1+nb-1)/nb; // block-aligned or ==N: exact
|
|
||||||
const int64_t S = (Pc + Pr - 1)/Pr; // max B panels per origin row
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Round buffers, padded to fixed slot sizes (see header comment).
|
|
||||||
// A: Pc slots of mloc_i x nb (slot c = panel of the block owned by c)
|
|
||||||
// B: Pr slots of S x (nb x nloc_j)
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
const uint64_t slotA = (uint64_t)mloc_i*nb;
|
|
||||||
const uint64_t slotB1 = (uint64_t)nb*nloc_j; // one panel
|
|
||||||
const uint64_t slotB = (uint64_t)S*slotB1;
|
|
||||||
deviceVector<ComplexD> Abuf( slotA*Pc ? slotA*Pc : 1 );
|
|
||||||
deviceVector<ComplexD> Bbuf( slotB*Pr ? slotB*Pr : 1 );
|
|
||||||
|
|
||||||
deviceVector<ComplexD *> ap(1), bp(1), cp(1);
|
|
||||||
std::vector<ComplexD *> ptr(1);
|
|
||||||
|
|
||||||
int firstblock = 1;
|
|
||||||
for(int64_t r0=kb0; r0<kb1; r0+=Pc){ // rounds of Pc k-blocks
|
|
||||||
int64_t r1 = std::min(kb1, r0+Pc);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// Pack MY panels of this round into my origin slots.
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
for(int64_t s=r0; s<r1; s++){
|
|
||||||
int64_t nb_s = L.BlockSize(s);
|
|
||||||
if ( (int)(s%Pc) == pcol && mloc_i ){ // my A panel: block-column s
|
|
||||||
int64_t lc0, lc1;
|
|
||||||
L.ColRange(s*nb, std::min(N,(s+1)*nb), lc0, lc1);
|
|
||||||
GRID_ASSERT( lc1-lc0 == nb_s );
|
|
||||||
ComplexD *src = A.LocalWindow(li0, lc0);
|
|
||||||
ComplexD *dst = &Abuf[0] + slotA*pcol;
|
|
||||||
int64_t ld = A.layout.mloc;
|
|
||||||
int64_t m = mloc_i;
|
|
||||||
accelerator_for(idx, (uint64_t)(m*nb_s), 1, {
|
|
||||||
int64_t jj = idx / m;
|
|
||||||
int64_t ii = idx - jj*m;
|
|
||||||
dst[ii + jj*m] = src[ii + jj*ld];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if ( (int)(s%Pr) == prow && nloc_j ){ // my B panel: block-row s
|
|
||||||
int64_t lr0, lr1;
|
|
||||||
L.RowRange(s*nb, std::min(N,(s+1)*nb), lr0, lr1);
|
|
||||||
GRID_ASSERT( lr1-lr0 == nb_s );
|
|
||||||
int64_t idxs = (s - r0 - ((prow - r0%Pr + Pr) % Pr)) / Pr; // my panel # in round
|
|
||||||
GRID_ASSERT( idxs >= 0 ); GRID_ASSERT( idxs < S );
|
|
||||||
ComplexD *src = B.LocalWindow(lr0, lj0);
|
|
||||||
ComplexD *dst = &Bbuf[0] + slotB*prow + slotB1*idxs;
|
|
||||||
int64_t ld = B.layout.mloc;
|
|
||||||
int64_t nn = nloc_j;
|
|
||||||
accelerator_for(idx, (uint64_t)(nb_s*nn), 1, {
|
|
||||||
int64_t jj = idx / nb_s;
|
|
||||||
int64_t ii = idx - jj*nb_s;
|
|
||||||
dst[ii + jj*nb_s] = src[ii + jj*ld];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
accelerator_barrier();
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// Ring allgather along my process ROW: Pc-1 symmetric steps. At
|
|
||||||
// step t send the slot of origin (pcol-t+1), receive origin (pcol-t).
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
if ( Pc > 1 && slotA ){
|
|
||||||
int dest = prow*Pc + (pcol+1)%Pc;
|
|
||||||
int src = prow*Pc + (pcol-1+Pc)%Pc;
|
|
||||||
for(int t=1;t<Pc;t++){
|
|
||||||
int cs = (pcol - t + 1 + Pc*Pc) % Pc;
|
|
||||||
int cr = (pcol - t + Pc*Pc) % Pc;
|
|
||||||
grid->SendToRecvFrom((void *)(&Abuf[0]+slotA*cs), dest,
|
|
||||||
(void *)(&Abuf[0]+slotA*cr), src,
|
|
||||||
slotA*sizeof(ComplexD));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// Ring allgather along my process COLUMN: Pr-1 symmetric steps.
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
if ( Pr > 1 && slotB ){
|
|
||||||
int dest = ((prow+1)%Pr)*Pc + pcol;
|
|
||||||
int src = ((prow-1+Pr)%Pr)*Pc + pcol;
|
|
||||||
for(int t=1;t<Pr;t++){
|
|
||||||
int rs = (prow - t + 1 + Pr*Pr) % Pr;
|
|
||||||
int rr = (prow - t + Pr*Pr) % Pr;
|
|
||||||
grid->SendToRecvFrom((void *)(&Bbuf[0]+slotB*rs), dest,
|
|
||||||
(void *)(&Bbuf[0]+slotB*rr), src,
|
|
||||||
slotB*sizeof(ComplexD));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// Local update, ascending s: fixed order, bitwise-reproducible.
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
for(int64_t s=r0; s<r1; s++){
|
|
||||||
int64_t nb_s = L.BlockSize(s);
|
|
||||||
if ( !(mloc_i && nloc_j && nb_s) ) { firstblock = 0; continue; }
|
|
||||||
int cA = (int)(s%Pc);
|
|
||||||
int rB = (int)(s%Pr);
|
|
||||||
int64_t idxs = (s - r0 - ((rB - r0%Pr + Pr) % Pr)) / Pr;
|
|
||||||
ComplexD beta_use = firstblock ? beta : ComplexD(1.0,0.0);
|
|
||||||
firstblock = 0;
|
|
||||||
|
|
||||||
ptr[0] = &Abuf[0] + slotA*cA;
|
|
||||||
acceleratorCopyToDevice(&ptr[0], &ap[0], sizeof(ComplexD *));
|
|
||||||
ptr[0] = &Bbuf[0] + slotB*rB + slotB1*idxs;
|
|
||||||
acceleratorCopyToDevice(&ptr[0], &bp[0], sizeof(ComplexD *));
|
|
||||||
ptr[0] = C.LocalWindow(li0, lj0);
|
|
||||||
acceleratorCopyToDevice(&ptr[0], &cp[0], sizeof(ComplexD *));
|
|
||||||
|
|
||||||
BLAS.gemmBatched(GridBLAS_OP_N, GridBLAS_OP_N,
|
|
||||||
(int)mloc_i, (int)nloc_j, (int)nb_s,
|
|
||||||
alpha, ap, (int)mloc_i,
|
|
||||||
bp, (int)nb_s,
|
|
||||||
beta_use, cp, (int)C.layout.mloc);
|
|
||||||
BLAS.synchronise();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
@@ -1,837 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/CoarsenedMatrix.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
|
||||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|
||||||
Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local>
|
|
||||||
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_ALGORITHM_COARSENED_MATRIX_H
|
|
||||||
#define GRID_ALGORITHM_COARSENED_MATRIX_H
|
|
||||||
|
|
||||||
#include <Grid/qcd/QCD.h> // needed for Dagger(Yes|No), Inverse(Yes|No)
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
template<class vobj,class CComplex>
|
|
||||||
inline void blockMaskedInnerProduct(Lattice<CComplex> &CoarseInner,
|
|
||||||
const Lattice<decltype(innerProduct(vobj(),vobj()))> &FineMask,
|
|
||||||
const Lattice<vobj> &fineX,
|
|
||||||
const Lattice<vobj> &fineY)
|
|
||||||
{
|
|
||||||
typedef decltype(innerProduct(vobj(),vobj())) dotp;
|
|
||||||
|
|
||||||
GridBase *coarse(CoarseInner.Grid());
|
|
||||||
GridBase *fine (fineX.Grid());
|
|
||||||
|
|
||||||
Lattice<dotp> fine_inner(fine); fine_inner.Checkerboard() = fineX.Checkerboard();
|
|
||||||
Lattice<dotp> fine_inner_msk(fine);
|
|
||||||
|
|
||||||
// Multiply could be fused with innerProduct
|
|
||||||
// Single block sum kernel could do both masks.
|
|
||||||
fine_inner = localInnerProduct(fineX,fineY);
|
|
||||||
mult(fine_inner_msk, fine_inner,FineMask);
|
|
||||||
blockSum(CoarseInner,fine_inner_msk);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fine Object == (per site) type of fine field
|
|
||||||
// nbasis == number of deflation vectors
|
|
||||||
template<class Fobj,class CComplex,int nbasis>
|
|
||||||
class CoarsenedMatrix : public CheckerBoardedSparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef iVector<CComplex,nbasis > siteVector;
|
|
||||||
typedef Lattice<CComplex > CoarseComplexField;
|
|
||||||
typedef Lattice<siteVector> CoarseVector;
|
|
||||||
typedef Lattice<iMatrix<CComplex,nbasis > > CoarseMatrix;
|
|
||||||
typedef iMatrix<CComplex,nbasis > Cobj;
|
|
||||||
typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field
|
|
||||||
typedef Lattice<Fobj > FineField;
|
|
||||||
typedef CoarseVector FermionField;
|
|
||||||
|
|
||||||
// enrich interface, use default implementation as in FermionOperator ///////
|
|
||||||
void Dminus(CoarseVector const& in, CoarseVector& out) { out = in; }
|
|
||||||
void DminusDag(CoarseVector const& in, CoarseVector& out) { out = in; }
|
|
||||||
void ImportPhysicalFermionSource(CoarseVector const& input, CoarseVector& imported) { imported = input; }
|
|
||||||
void ImportUnphysicalFermion(CoarseVector const& input, CoarseVector& imported) { imported = input; }
|
|
||||||
void ExportPhysicalFermionSolution(CoarseVector const& solution, CoarseVector& exported) { exported = solution; };
|
|
||||||
void ExportPhysicalFermionSource(CoarseVector const& solution, CoarseVector& exported) { exported = solution; };
|
|
||||||
|
|
||||||
////////////////////
|
|
||||||
// Data members
|
|
||||||
////////////////////
|
|
||||||
Geometry geom;
|
|
||||||
GridBase * _grid;
|
|
||||||
GridBase* _cbgrid;
|
|
||||||
int hermitian;
|
|
||||||
|
|
||||||
CartesianStencil<siteVector,siteVector,DefaultImplParams> Stencil;
|
|
||||||
CartesianStencil<siteVector,siteVector,DefaultImplParams> StencilEven;
|
|
||||||
CartesianStencil<siteVector,siteVector,DefaultImplParams> StencilOdd;
|
|
||||||
|
|
||||||
std::vector<CoarseMatrix> A;
|
|
||||||
std::vector<CoarseMatrix> Aeven;
|
|
||||||
std::vector<CoarseMatrix> Aodd;
|
|
||||||
|
|
||||||
CoarseMatrix AselfInv;
|
|
||||||
CoarseMatrix AselfInvEven;
|
|
||||||
CoarseMatrix AselfInvOdd;
|
|
||||||
|
|
||||||
deviceVector<RealD> dag_factor;
|
|
||||||
|
|
||||||
///////////////////////
|
|
||||||
// Interface
|
|
||||||
///////////////////////
|
|
||||||
GridBase * Grid(void) { return _grid; }; // this is all the linalg routines need to know
|
|
||||||
GridBase * RedBlackGrid() { return _cbgrid; };
|
|
||||||
|
|
||||||
int ConstEE() { return 0; }
|
|
||||||
|
|
||||||
void M (const CoarseVector &in, CoarseVector &out)
|
|
||||||
{
|
|
||||||
conformable(_grid,in.Grid());
|
|
||||||
conformable(in.Grid(),out.Grid());
|
|
||||||
out.Checkerboard() = in.Checkerboard();
|
|
||||||
|
|
||||||
SimpleCompressor<siteVector> compressor;
|
|
||||||
|
|
||||||
Stencil.HaloExchange(in,compressor);
|
|
||||||
autoView( in_v , in, AcceleratorRead);
|
|
||||||
autoView( out_v , out, AcceleratorWrite);
|
|
||||||
autoView( Stencil_v , Stencil, AcceleratorRead);
|
|
||||||
int npoint = geom.npoint;
|
|
||||||
typedef LatticeView<Cobj> Aview;
|
|
||||||
|
|
||||||
deviceVector<Aview> AcceleratorViewContainer(geom.npoint);
|
|
||||||
hostVector<Aview> hAcceleratorViewContainer(geom.npoint);
|
|
||||||
|
|
||||||
for(int p=0;p<geom.npoint;p++) {
|
|
||||||
hAcceleratorViewContainer[p] = A[p].View(AcceleratorRead);
|
|
||||||
acceleratorPut(AcceleratorViewContainer[p],hAcceleratorViewContainer[p]);
|
|
||||||
}
|
|
||||||
Aview *Aview_p = & AcceleratorViewContainer[0];
|
|
||||||
|
|
||||||
const int Nsimd = CComplex::Nsimd();
|
|
||||||
typedef decltype(coalescedRead(in_v[0])) calcVector;
|
|
||||||
typedef decltype(coalescedRead(in_v[0](0))) calcComplex;
|
|
||||||
|
|
||||||
int osites=Grid()->oSites();
|
|
||||||
|
|
||||||
accelerator_for(sss, Grid()->oSites()*nbasis, Nsimd, {
|
|
||||||
int ss = sss/nbasis;
|
|
||||||
int b = sss%nbasis;
|
|
||||||
calcComplex res = Zero();
|
|
||||||
calcVector nbr;
|
|
||||||
int ptype;
|
|
||||||
StencilEntry *SE;
|
|
||||||
|
|
||||||
for(int point=0;point<npoint;point++){
|
|
||||||
|
|
||||||
SE=Stencil_v.GetEntry(ptype,point,ss);
|
|
||||||
|
|
||||||
if(SE->_is_local) {
|
|
||||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute);
|
|
||||||
} else {
|
|
||||||
nbr = coalescedRead(Stencil_v.CommBuf()[SE->_offset]);
|
|
||||||
}
|
|
||||||
acceleratorSynchronise();
|
|
||||||
|
|
||||||
for(int bb=0;bb<nbasis;bb++) {
|
|
||||||
res = res + coalescedRead(Aview_p[point][ss](b,bb))*nbr(bb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
coalescedWrite(out_v[ss](b),res);
|
|
||||||
});
|
|
||||||
|
|
||||||
for(int p=0;p<geom.npoint;p++) hAcceleratorViewContainer[p].ViewClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
void Mdag (const CoarseVector &in, CoarseVector &out)
|
|
||||||
{
|
|
||||||
if(hermitian) {
|
|
||||||
// corresponds to Petrov-Galerkin coarsening
|
|
||||||
return M(in,out);
|
|
||||||
} else {
|
|
||||||
// corresponds to Galerkin coarsening
|
|
||||||
return MdagNonHermitian(in, out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void MdagNonHermitian(const CoarseVector &in, CoarseVector &out)
|
|
||||||
{
|
|
||||||
conformable(_grid,in.Grid());
|
|
||||||
conformable(in.Grid(),out.Grid());
|
|
||||||
out.Checkerboard() = in.Checkerboard();
|
|
||||||
|
|
||||||
SimpleCompressor<siteVector> compressor;
|
|
||||||
|
|
||||||
Stencil.HaloExchange(in,compressor);
|
|
||||||
autoView( in_v , in, AcceleratorRead);
|
|
||||||
autoView( out_v , out, AcceleratorWrite);
|
|
||||||
autoView( Stencil_v , Stencil, AcceleratorRead);
|
|
||||||
int npoint = geom.npoint;
|
|
||||||
typedef LatticeView<Cobj> Aview;
|
|
||||||
|
|
||||||
|
|
||||||
deviceVector<Aview> AcceleratorViewContainer(geom.npoint);
|
|
||||||
hostVector<Aview> hAcceleratorViewContainer(geom.npoint);
|
|
||||||
|
|
||||||
for(int p=0;p<geom.npoint;p++) {
|
|
||||||
hAcceleratorViewContainer[p] = A[p].View(AcceleratorRead);
|
|
||||||
acceleratorPut(AcceleratorViewContainer[p],hAcceleratorViewContainer[p]);
|
|
||||||
}
|
|
||||||
Aview *Aview_p = & AcceleratorViewContainer[0];
|
|
||||||
|
|
||||||
const int Nsimd = CComplex::Nsimd();
|
|
||||||
typedef decltype(coalescedRead(in_v[0])) calcVector;
|
|
||||||
typedef decltype(coalescedRead(in_v[0](0))) calcComplex;
|
|
||||||
|
|
||||||
int osites=Grid()->oSites();
|
|
||||||
|
|
||||||
deviceVector<int> points(geom.npoint);
|
|
||||||
for(int p=0; p<geom.npoint; p++) {
|
|
||||||
acceleratorPut(points[p],geom.points_dagger[p]);
|
|
||||||
}
|
|
||||||
auto points_p = &points[0];
|
|
||||||
|
|
||||||
RealD* dag_factor_p = &dag_factor[0];
|
|
||||||
|
|
||||||
accelerator_for(sss, Grid()->oSites()*nbasis, Nsimd, {
|
|
||||||
int ss = sss/nbasis;
|
|
||||||
int b = sss%nbasis;
|
|
||||||
calcComplex res = Zero();
|
|
||||||
calcVector nbr;
|
|
||||||
int ptype;
|
|
||||||
StencilEntry *SE;
|
|
||||||
|
|
||||||
for(int p=0;p<npoint;p++){
|
|
||||||
int point = points_p[p];
|
|
||||||
|
|
||||||
SE=Stencil_v.GetEntry(ptype,point,ss);
|
|
||||||
|
|
||||||
if(SE->_is_local) {
|
|
||||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute);
|
|
||||||
} else {
|
|
||||||
nbr = coalescedRead(Stencil_v.CommBuf()[SE->_offset]);
|
|
||||||
}
|
|
||||||
acceleratorSynchronise();
|
|
||||||
|
|
||||||
for(int bb=0;bb<nbasis;bb++) {
|
|
||||||
res = res + dag_factor_p[b*nbasis+bb]*coalescedRead(Aview_p[point][ss](b,bb))*nbr(bb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
coalescedWrite(out_v[ss](b),res);
|
|
||||||
});
|
|
||||||
|
|
||||||
for(int p=0;p<geom.npoint;p++) hAcceleratorViewContainer[p].ViewClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MdirComms(const CoarseVector &in)
|
|
||||||
{
|
|
||||||
SimpleCompressor<siteVector> compressor;
|
|
||||||
Stencil.HaloExchange(in,compressor);
|
|
||||||
}
|
|
||||||
void MdirCalc(const CoarseVector &in, CoarseVector &out, int point)
|
|
||||||
{
|
|
||||||
conformable(_grid,in.Grid());
|
|
||||||
conformable(_grid,out.Grid());
|
|
||||||
out.Checkerboard() = in.Checkerboard();
|
|
||||||
|
|
||||||
typedef LatticeView<Cobj> Aview;
|
|
||||||
|
|
||||||
deviceVector<Aview> AcceleratorViewContainer(geom.npoint);
|
|
||||||
hostVector<Aview> hAcceleratorViewContainer(geom.npoint);
|
|
||||||
|
|
||||||
for(int p=0;p<geom.npoint;p++) {
|
|
||||||
hAcceleratorViewContainer[p] = A[p].View(AcceleratorRead);
|
|
||||||
acceleratorPut(AcceleratorViewContainer[p],hAcceleratorViewContainer[p]);
|
|
||||||
}
|
|
||||||
Aview *Aview_p = & AcceleratorViewContainer[0];
|
|
||||||
|
|
||||||
autoView( out_v , out, AcceleratorWrite);
|
|
||||||
autoView( in_v , in, AcceleratorRead);
|
|
||||||
autoView( Stencil_v , Stencil, AcceleratorRead);
|
|
||||||
|
|
||||||
const int Nsimd = CComplex::Nsimd();
|
|
||||||
typedef decltype(coalescedRead(in_v[0])) calcVector;
|
|
||||||
typedef decltype(coalescedRead(in_v[0](0))) calcComplex;
|
|
||||||
|
|
||||||
accelerator_for(sss, Grid()->oSites()*nbasis, Nsimd, {
|
|
||||||
int ss = sss/nbasis;
|
|
||||||
int b = sss%nbasis;
|
|
||||||
calcComplex res = Zero();
|
|
||||||
calcVector nbr;
|
|
||||||
int ptype;
|
|
||||||
StencilEntry *SE;
|
|
||||||
|
|
||||||
SE=Stencil_v.GetEntry(ptype,point,ss);
|
|
||||||
|
|
||||||
if(SE->_is_local) {
|
|
||||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute);
|
|
||||||
} else {
|
|
||||||
nbr = coalescedRead(Stencil_v.CommBuf()[SE->_offset]);
|
|
||||||
}
|
|
||||||
acceleratorSynchronise();
|
|
||||||
|
|
||||||
for(int bb=0;bb<nbasis;bb++) {
|
|
||||||
res = res + coalescedRead(Aview_p[point][ss](b,bb))*nbr(bb);
|
|
||||||
}
|
|
||||||
coalescedWrite(out_v[ss](b),res);
|
|
||||||
});
|
|
||||||
for(int p=0;p<geom.npoint;p++) hAcceleratorViewContainer[p].ViewClose();
|
|
||||||
}
|
|
||||||
void MdirAll(const CoarseVector &in,std::vector<CoarseVector> &out)
|
|
||||||
{
|
|
||||||
this->MdirComms(in);
|
|
||||||
int ndir=geom.npoint-1;
|
|
||||||
if ((out.size()!=ndir)&&(out.size()!=ndir+1)) {
|
|
||||||
std::cout <<"MdirAll out size "<< out.size()<<std::endl;
|
|
||||||
std::cout <<"MdirAll ndir "<< ndir<<std::endl;
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
for(int p=0;p<ndir;p++){
|
|
||||||
MdirCalc(in,out[p],p);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
void Mdir(const CoarseVector &in, CoarseVector &out, int dir, int disp){
|
|
||||||
|
|
||||||
this->MdirComms(in);
|
|
||||||
|
|
||||||
MdirCalc(in,out,geom.point(dir,disp));
|
|
||||||
};
|
|
||||||
|
|
||||||
void Mdiag(const CoarseVector &in, CoarseVector &out)
|
|
||||||
{
|
|
||||||
int point=geom.npoint-1;
|
|
||||||
MdirCalc(in, out, point); // No comms
|
|
||||||
};
|
|
||||||
|
|
||||||
void Mooee(const CoarseVector &in, CoarseVector &out) {
|
|
||||||
MooeeInternal(in, out, DaggerNo, InverseNo);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MooeeInv(const CoarseVector &in, CoarseVector &out) {
|
|
||||||
MooeeInternal(in, out, DaggerNo, InverseYes);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MooeeDag(const CoarseVector &in, CoarseVector &out) {
|
|
||||||
MooeeInternal(in, out, DaggerYes, InverseNo);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MooeeInvDag(const CoarseVector &in, CoarseVector &out) {
|
|
||||||
MooeeInternal(in, out, DaggerYes, InverseYes);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Meooe(const CoarseVector &in, CoarseVector &out) {
|
|
||||||
if(in.Checkerboard() == Odd) {
|
|
||||||
DhopEO(in, out, DaggerNo);
|
|
||||||
} else {
|
|
||||||
DhopOE(in, out, DaggerNo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MeooeDag(const CoarseVector &in, CoarseVector &out) {
|
|
||||||
if(in.Checkerboard() == Odd) {
|
|
||||||
DhopEO(in, out, DaggerYes);
|
|
||||||
} else {
|
|
||||||
DhopOE(in, out, DaggerYes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dhop(const CoarseVector &in, CoarseVector &out, int dag) {
|
|
||||||
conformable(in.Grid(), _grid); // verifies full grid
|
|
||||||
conformable(in.Grid(), out.Grid());
|
|
||||||
|
|
||||||
out.Checkerboard() = in.Checkerboard();
|
|
||||||
|
|
||||||
DhopInternal(Stencil, A, in, out, dag);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DhopOE(const CoarseVector &in, CoarseVector &out, int dag) {
|
|
||||||
conformable(in.Grid(), _cbgrid); // verifies half grid
|
|
||||||
conformable(in.Grid(), out.Grid()); // drops the cb check
|
|
||||||
|
|
||||||
GRID_ASSERT(in.Checkerboard() == Even);
|
|
||||||
out.Checkerboard() = Odd;
|
|
||||||
|
|
||||||
DhopInternal(StencilEven, Aodd, in, out, dag);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DhopEO(const CoarseVector &in, CoarseVector &out, int dag) {
|
|
||||||
conformable(in.Grid(), _cbgrid); // verifies half grid
|
|
||||||
conformable(in.Grid(), out.Grid()); // drops the cb check
|
|
||||||
|
|
||||||
GRID_ASSERT(in.Checkerboard() == Odd);
|
|
||||||
out.Checkerboard() = Even;
|
|
||||||
|
|
||||||
DhopInternal(StencilOdd, Aeven, in, out, dag);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MooeeInternal(const CoarseVector &in, CoarseVector &out, int dag, int inv) {
|
|
||||||
out.Checkerboard() = in.Checkerboard();
|
|
||||||
GRID_ASSERT(in.Checkerboard() == Odd || in.Checkerboard() == Even);
|
|
||||||
|
|
||||||
CoarseMatrix *Aself = nullptr;
|
|
||||||
if(in.Grid()->_isCheckerBoarded) {
|
|
||||||
if(in.Checkerboard() == Odd) {
|
|
||||||
Aself = (inv) ? &AselfInvOdd : &Aodd[geom.npoint-1];
|
|
||||||
DselfInternal(StencilOdd, *Aself, in, out, dag);
|
|
||||||
} else {
|
|
||||||
Aself = (inv) ? &AselfInvEven : &Aeven[geom.npoint-1];
|
|
||||||
DselfInternal(StencilEven, *Aself, in, out, dag);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Aself = (inv) ? &AselfInv : &A[geom.npoint-1];
|
|
||||||
DselfInternal(Stencil, *Aself, in, out, dag);
|
|
||||||
}
|
|
||||||
GRID_ASSERT(Aself != nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DselfInternal(CartesianStencil<siteVector,siteVector,DefaultImplParams> &st, CoarseMatrix &a,
|
|
||||||
const CoarseVector &in, CoarseVector &out, int dag) {
|
|
||||||
int point = geom.npoint-1;
|
|
||||||
autoView( out_v, out, AcceleratorWrite);
|
|
||||||
autoView( in_v, in, AcceleratorRead);
|
|
||||||
autoView( st_v, st, AcceleratorRead);
|
|
||||||
autoView( a_v, a, AcceleratorRead);
|
|
||||||
|
|
||||||
const int Nsimd = CComplex::Nsimd();
|
|
||||||
typedef decltype(coalescedRead(in_v[0])) calcVector;
|
|
||||||
typedef decltype(coalescedRead(in_v[0](0))) calcComplex;
|
|
||||||
|
|
||||||
RealD* dag_factor_p = &dag_factor[0];
|
|
||||||
|
|
||||||
if(dag) {
|
|
||||||
accelerator_for(sss, in.Grid()->oSites()*nbasis, Nsimd, {
|
|
||||||
int ss = sss/nbasis;
|
|
||||||
int b = sss%nbasis;
|
|
||||||
calcComplex res = Zero();
|
|
||||||
calcVector nbr;
|
|
||||||
int ptype;
|
|
||||||
StencilEntry *SE;
|
|
||||||
|
|
||||||
SE=st_v.GetEntry(ptype,point,ss);
|
|
||||||
|
|
||||||
if(SE->_is_local) {
|
|
||||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute);
|
|
||||||
} else {
|
|
||||||
nbr = coalescedRead(st_v.CommBuf()[SE->_offset]);
|
|
||||||
}
|
|
||||||
acceleratorSynchronise();
|
|
||||||
|
|
||||||
for(int bb=0;bb<nbasis;bb++) {
|
|
||||||
res = res + dag_factor_p[b*nbasis+bb]*coalescedRead(a_v[ss](b,bb))*nbr(bb);
|
|
||||||
}
|
|
||||||
coalescedWrite(out_v[ss](b),res);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
accelerator_for(sss, in.Grid()->oSites()*nbasis, Nsimd, {
|
|
||||||
int ss = sss/nbasis;
|
|
||||||
int b = sss%nbasis;
|
|
||||||
calcComplex res = Zero();
|
|
||||||
calcVector nbr;
|
|
||||||
int ptype;
|
|
||||||
StencilEntry *SE;
|
|
||||||
|
|
||||||
SE=st_v.GetEntry(ptype,point,ss);
|
|
||||||
|
|
||||||
if(SE->_is_local) {
|
|
||||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute);
|
|
||||||
} else {
|
|
||||||
nbr = coalescedRead(st_v.CommBuf()[SE->_offset]);
|
|
||||||
}
|
|
||||||
acceleratorSynchronise();
|
|
||||||
|
|
||||||
for(int bb=0;bb<nbasis;bb++) {
|
|
||||||
res = res + coalescedRead(a_v[ss](b,bb))*nbr(bb);
|
|
||||||
}
|
|
||||||
coalescedWrite(out_v[ss](b),res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DhopInternal(CartesianStencil<siteVector,siteVector,DefaultImplParams> &st, std::vector<CoarseMatrix> &a,
|
|
||||||
const CoarseVector &in, CoarseVector &out, int dag) {
|
|
||||||
SimpleCompressor<siteVector> compressor;
|
|
||||||
|
|
||||||
st.HaloExchange(in,compressor);
|
|
||||||
autoView( in_v, in, AcceleratorRead);
|
|
||||||
autoView( out_v, out, AcceleratorWrite);
|
|
||||||
autoView( st_v , st, AcceleratorRead);
|
|
||||||
typedef LatticeView<Cobj> Aview;
|
|
||||||
|
|
||||||
// determine in what order we need the points
|
|
||||||
int npoint = geom.npoint-1;
|
|
||||||
deviceVector<int> points(npoint);
|
|
||||||
for(int p=0; p<npoint; p++) {
|
|
||||||
int val = (dag && !hermitian) ? geom.points_dagger[p] : p;
|
|
||||||
acceleratorPut(points[p], val);
|
|
||||||
}
|
|
||||||
auto points_p = &points[0];
|
|
||||||
|
|
||||||
deviceVector<Aview> AcceleratorViewContainer(geom.npoint);
|
|
||||||
hostVector<Aview> hAcceleratorViewContainer(geom.npoint);
|
|
||||||
|
|
||||||
for(int p=0;p<geom.npoint;p++) {
|
|
||||||
hAcceleratorViewContainer[p] = a[p].View(AcceleratorRead);
|
|
||||||
acceleratorPut(AcceleratorViewContainer[p],hAcceleratorViewContainer[p]);
|
|
||||||
}
|
|
||||||
Aview *Aview_p = & AcceleratorViewContainer[0];
|
|
||||||
|
|
||||||
const int Nsimd = CComplex::Nsimd();
|
|
||||||
typedef decltype(coalescedRead(in_v[0])) calcVector;
|
|
||||||
typedef decltype(coalescedRead(in_v[0](0))) calcComplex;
|
|
||||||
|
|
||||||
RealD* dag_factor_p = &dag_factor[0];
|
|
||||||
|
|
||||||
if(dag) {
|
|
||||||
accelerator_for(sss, in.Grid()->oSites()*nbasis, Nsimd, {
|
|
||||||
int ss = sss/nbasis;
|
|
||||||
int b = sss%nbasis;
|
|
||||||
calcComplex res = Zero();
|
|
||||||
calcVector nbr;
|
|
||||||
int ptype;
|
|
||||||
StencilEntry *SE;
|
|
||||||
|
|
||||||
for(int p=0;p<npoint;p++){
|
|
||||||
int point = points_p[p];
|
|
||||||
SE=st_v.GetEntry(ptype,point,ss);
|
|
||||||
|
|
||||||
if(SE->_is_local) {
|
|
||||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute);
|
|
||||||
} else {
|
|
||||||
nbr = coalescedRead(st_v.CommBuf()[SE->_offset]);
|
|
||||||
}
|
|
||||||
acceleratorSynchronise();
|
|
||||||
|
|
||||||
for(int bb=0;bb<nbasis;bb++) {
|
|
||||||
res = res + dag_factor_p[b*nbasis+bb]*coalescedRead(Aview_p[point][ss](b,bb))*nbr(bb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
coalescedWrite(out_v[ss](b),res);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
accelerator_for(sss, in.Grid()->oSites()*nbasis, Nsimd, {
|
|
||||||
int ss = sss/nbasis;
|
|
||||||
int b = sss%nbasis;
|
|
||||||
calcComplex res = Zero();
|
|
||||||
calcVector nbr;
|
|
||||||
int ptype;
|
|
||||||
StencilEntry *SE;
|
|
||||||
|
|
||||||
for(int p=0;p<npoint;p++){
|
|
||||||
int point = points_p[p];
|
|
||||||
SE=st_v.GetEntry(ptype,point,ss);
|
|
||||||
|
|
||||||
if(SE->_is_local) {
|
|
||||||
nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute);
|
|
||||||
} else {
|
|
||||||
nbr = coalescedRead(st_v.CommBuf()[SE->_offset]);
|
|
||||||
}
|
|
||||||
acceleratorSynchronise();
|
|
||||||
|
|
||||||
for(int bb=0;bb<nbasis;bb++) {
|
|
||||||
res = res + coalescedRead(Aview_p[point][ss](b,bb))*nbr(bb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
coalescedWrite(out_v[ss](b),res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int p=0;p<npoint;p++) hAcceleratorViewContainer[p].ViewClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
CoarsenedMatrix(GridCartesian &CoarseGrid, int hermitian_=0) :
|
|
||||||
_grid(&CoarseGrid),
|
|
||||||
_cbgrid(new GridRedBlackCartesian(&CoarseGrid)),
|
|
||||||
geom(CoarseGrid._ndimension),
|
|
||||||
hermitian(hermitian_),
|
|
||||||
Stencil(&CoarseGrid,geom.npoint,Even,geom.directions,geom.displacements),
|
|
||||||
StencilEven(_cbgrid,geom.npoint,Even,geom.directions,geom.displacements),
|
|
||||||
StencilOdd(_cbgrid,geom.npoint,Odd,geom.directions,geom.displacements),
|
|
||||||
A(geom.npoint,&CoarseGrid),
|
|
||||||
Aeven(geom.npoint,_cbgrid),
|
|
||||||
Aodd(geom.npoint,_cbgrid),
|
|
||||||
AselfInv(&CoarseGrid),
|
|
||||||
AselfInvEven(_cbgrid),
|
|
||||||
AselfInvOdd(_cbgrid),
|
|
||||||
dag_factor(nbasis*nbasis)
|
|
||||||
{
|
|
||||||
fillFactor();
|
|
||||||
};
|
|
||||||
|
|
||||||
CoarsenedMatrix(GridCartesian &CoarseGrid, GridRedBlackCartesian &CoarseRBGrid, int hermitian_=0) :
|
|
||||||
|
|
||||||
_grid(&CoarseGrid),
|
|
||||||
_cbgrid(&CoarseRBGrid),
|
|
||||||
geom(CoarseGrid._ndimension),
|
|
||||||
hermitian(hermitian_),
|
|
||||||
Stencil(&CoarseGrid,geom.npoint,Even,geom.directions,geom.displacements),
|
|
||||||
StencilEven(&CoarseRBGrid,geom.npoint,Even,geom.directions,geom.displacements),
|
|
||||||
StencilOdd(&CoarseRBGrid,geom.npoint,Odd,geom.directions,geom.displacements),
|
|
||||||
A(geom.npoint,&CoarseGrid),
|
|
||||||
Aeven(geom.npoint,&CoarseRBGrid),
|
|
||||||
Aodd(geom.npoint,&CoarseRBGrid),
|
|
||||||
AselfInv(&CoarseGrid),
|
|
||||||
AselfInvEven(&CoarseRBGrid),
|
|
||||||
AselfInvOdd(&CoarseRBGrid),
|
|
||||||
dag_factor(nbasis*nbasis)
|
|
||||||
{
|
|
||||||
fillFactor();
|
|
||||||
};
|
|
||||||
|
|
||||||
void fillFactor() {
|
|
||||||
Eigen::MatrixXd dag_factor_eigen = Eigen::MatrixXd::Ones(nbasis, nbasis);
|
|
||||||
if(!hermitian) {
|
|
||||||
const int nb = nbasis/2;
|
|
||||||
dag_factor_eigen.block(0,nb,nb,nb) *= -1.0;
|
|
||||||
dag_factor_eigen.block(nb,0,nb,nb) *= -1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GPU readable prefactor
|
|
||||||
std::vector<RealD> h_dag_factor(nbasis*nbasis);
|
|
||||||
thread_for(i, nbasis*nbasis, {
|
|
||||||
int j = i/nbasis;
|
|
||||||
int k = i%nbasis;
|
|
||||||
h_dag_factor[i] = dag_factor_eigen(j, k);
|
|
||||||
});
|
|
||||||
acceleratorCopyToDevice(&h_dag_factor[0],&dag_factor[0],dag_factor.size()*sizeof(RealD));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoarsenOperator(GridBase *FineGrid,LinearOperatorBase<Lattice<Fobj> > &linop,
|
|
||||||
Aggregation<Fobj,CComplex,nbasis> & Subspace)
|
|
||||||
{
|
|
||||||
typedef Lattice<typename Fobj::tensor_reduced> FineComplexField;
|
|
||||||
typedef typename Fobj::scalar_type scalar_type;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrix "<< std::endl;
|
|
||||||
|
|
||||||
FineComplexField one(FineGrid); one=scalar_type(1.0,0.0);
|
|
||||||
FineComplexField zero(FineGrid); zero=scalar_type(0.0,0.0);
|
|
||||||
|
|
||||||
std::vector<FineComplexField> masks(geom.npoint,FineGrid);
|
|
||||||
FineComplexField imask(FineGrid); // contributions from within this block
|
|
||||||
FineComplexField omask(FineGrid); // contributions from outwith this block
|
|
||||||
|
|
||||||
FineComplexField evenmask(FineGrid);
|
|
||||||
FineComplexField oddmask(FineGrid);
|
|
||||||
|
|
||||||
FineField phi(FineGrid);
|
|
||||||
FineField tmp(FineGrid);
|
|
||||||
FineField zz(FineGrid); zz=Zero();
|
|
||||||
FineField Mphi(FineGrid);
|
|
||||||
FineField Mphie(FineGrid);
|
|
||||||
FineField Mphio(FineGrid);
|
|
||||||
std::vector<FineField> Mphi_p(geom.npoint,FineGrid);
|
|
||||||
|
|
||||||
Lattice<iScalar<vInteger> > coor (FineGrid);
|
|
||||||
Lattice<iScalar<vInteger> > bcoor(FineGrid);
|
|
||||||
Lattice<iScalar<vInteger> > bcb (FineGrid); bcb = Zero();
|
|
||||||
|
|
||||||
CoarseVector iProj(Grid());
|
|
||||||
CoarseVector oProj(Grid());
|
|
||||||
CoarseVector SelfProj(Grid());
|
|
||||||
CoarseComplexField iZProj(Grid());
|
|
||||||
CoarseComplexField oZProj(Grid());
|
|
||||||
|
|
||||||
CoarseScalar InnerProd(Grid());
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrix Orthog "<< std::endl;
|
|
||||||
// Orthogonalise the subblocks over the basis
|
|
||||||
blockOrthogonalise(InnerProd,Subspace.subspace);
|
|
||||||
|
|
||||||
// Compute the matrix elements of linop between this orthonormal
|
|
||||||
// set of vectors.
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrix masks "<< std::endl;
|
|
||||||
int self_stencil=-1;
|
|
||||||
for(int p=0;p<geom.npoint;p++)
|
|
||||||
{
|
|
||||||
int dir = geom.directions[p];
|
|
||||||
int disp = geom.displacements[p];
|
|
||||||
A[p]=Zero();
|
|
||||||
if( geom.displacements[p]==0){
|
|
||||||
self_stencil=p;
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer block=(FineGrid->_rdimensions[dir])/(Grid()->_rdimensions[dir]);
|
|
||||||
|
|
||||||
LatticeCoordinate(coor,dir);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////
|
|
||||||
// Work out even and odd block checkerboarding for fast diagonal term
|
|
||||||
///////////////////////////////////////////////////////
|
|
||||||
if ( disp==1 ) {
|
|
||||||
bcb = bcb + div(coor,block);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( disp==0 ) {
|
|
||||||
masks[p]= Zero();
|
|
||||||
} else if ( disp==1 ) {
|
|
||||||
masks[p] = where(mod(coor,block)==(block-1),one,zero);
|
|
||||||
} else if ( disp==-1 ) {
|
|
||||||
masks[p] = where(mod(coor,block)==(Integer)0,one,zero);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
evenmask = where(mod(bcb,2)==(Integer)0,one,zero);
|
|
||||||
oddmask = one-evenmask;
|
|
||||||
|
|
||||||
GRID_ASSERT(self_stencil!=-1);
|
|
||||||
|
|
||||||
for(int i=0;i<nbasis;i++){
|
|
||||||
|
|
||||||
phi=Subspace.subspace[i];
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrix vector "<<i << std::endl;
|
|
||||||
linop.OpDirAll(phi,Mphi_p);
|
|
||||||
linop.OpDiag (phi,Mphi_p[geom.npoint-1]);
|
|
||||||
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
|
|
||||||
Mphi = Mphi_p[p];
|
|
||||||
|
|
||||||
int dir = geom.directions[p];
|
|
||||||
int disp = geom.displacements[p];
|
|
||||||
|
|
||||||
if ( (disp==-1) || (!hermitian ) ) {
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
// Pick out contributions coming from this cell and neighbour cell
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
omask = masks[p];
|
|
||||||
imask = one-omask;
|
|
||||||
|
|
||||||
for(int j=0;j<nbasis;j++){
|
|
||||||
|
|
||||||
blockMaskedInnerProduct(oZProj,omask,Subspace.subspace[j],Mphi);
|
|
||||||
|
|
||||||
autoView( iZProj_v , iZProj, AcceleratorRead) ;
|
|
||||||
autoView( oZProj_v , oZProj, AcceleratorRead) ;
|
|
||||||
autoView( A_p , A[p], AcceleratorWrite);
|
|
||||||
autoView( A_self , A[self_stencil], AcceleratorWrite);
|
|
||||||
|
|
||||||
accelerator_for(ss, Grid()->oSites(), Fobj::Nsimd(),{ coalescedWrite(A_p[ss](j,i),oZProj_v(ss)); });
|
|
||||||
if ( hermitian && (disp==-1) ) {
|
|
||||||
for(int pp=0;pp<geom.npoint;pp++){// Find the opposite link and set <j|A|i> = <i|A|j>*
|
|
||||||
int dirp = geom.directions[pp];
|
|
||||||
int dispp = geom.displacements[pp];
|
|
||||||
if ( (dirp==dir) && (dispp==1) ){
|
|
||||||
auto sft = conjugate(Cshift(oZProj,dir,1));
|
|
||||||
autoView( sft_v , sft , AcceleratorWrite);
|
|
||||||
autoView( A_pp , A[pp], AcceleratorWrite);
|
|
||||||
accelerator_for(ss, Grid()->oSites(), Fobj::Nsimd(),{ coalescedWrite(A_pp[ss](i,j),sft_v(ss)); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////
|
|
||||||
// Faster alternate self coupling.. use hermiticity to save 2x
|
|
||||||
///////////////////////////////////////////
|
|
||||||
{
|
|
||||||
mult(tmp,phi,evenmask); linop.Op(tmp,Mphie);
|
|
||||||
mult(tmp,phi,oddmask ); linop.Op(tmp,Mphio);
|
|
||||||
|
|
||||||
{
|
|
||||||
autoView( tmp_ , tmp, AcceleratorWrite);
|
|
||||||
autoView( evenmask_ , evenmask, AcceleratorRead);
|
|
||||||
autoView( oddmask_ , oddmask, AcceleratorRead);
|
|
||||||
autoView( Mphie_ , Mphie, AcceleratorRead);
|
|
||||||
autoView( Mphio_ , Mphio, AcceleratorRead);
|
|
||||||
accelerator_for(ss, FineGrid->oSites(), Fobj::Nsimd(),{
|
|
||||||
coalescedWrite(tmp_[ss],evenmask_(ss)*Mphie_(ss) + oddmask_(ss)*Mphio_(ss));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
blockProject(SelfProj,tmp,Subspace.subspace);
|
|
||||||
|
|
||||||
autoView( SelfProj_ , SelfProj, AcceleratorRead);
|
|
||||||
autoView( A_self , A[self_stencil], AcceleratorWrite);
|
|
||||||
|
|
||||||
accelerator_for(ss, Grid()->oSites(), Fobj::Nsimd(),{
|
|
||||||
for(int j=0;j<nbasis;j++){
|
|
||||||
coalescedWrite(A_self[ss](j,i), SelfProj_(ss)(j));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(hermitian) {
|
|
||||||
std::cout << GridLogMessage << " ForceHermitian, new code "<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
InvertSelfStencilLink(); std::cout << GridLogMessage << "Coarse self link inverted" << std::endl;
|
|
||||||
FillHalfCbs(); std::cout << GridLogMessage << "Coarse half checkerboards filled" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InvertSelfStencilLink() {
|
|
||||||
std::cout << GridLogDebug << "CoarsenedMatrix::InvertSelfStencilLink" << std::endl;
|
|
||||||
int localVolume = Grid()->lSites();
|
|
||||||
|
|
||||||
typedef typename Cobj::scalar_object scalar_object;
|
|
||||||
|
|
||||||
autoView(Aself_v, A[geom.npoint-1], CpuRead);
|
|
||||||
autoView(AselfInv_v, AselfInv, CpuWrite);
|
|
||||||
thread_for(site, localVolume, { // NOTE: Not able to bring this to GPU because of Eigen + peek/poke
|
|
||||||
Eigen::MatrixXcd selfLinkEigen = Eigen::MatrixXcd::Zero(nbasis, nbasis);
|
|
||||||
Eigen::MatrixXcd selfLinkInvEigen = Eigen::MatrixXcd::Zero(nbasis, nbasis);
|
|
||||||
|
|
||||||
scalar_object selfLink = Zero();
|
|
||||||
scalar_object selfLinkInv = Zero();
|
|
||||||
|
|
||||||
Coordinate lcoor;
|
|
||||||
|
|
||||||
Grid()->LocalIndexToLocalCoor(site, lcoor);
|
|
||||||
peekLocalSite(selfLink, Aself_v, lcoor);
|
|
||||||
|
|
||||||
for (int i = 0; i < nbasis; ++i)
|
|
||||||
for (int j = 0; j < nbasis; ++j)
|
|
||||||
selfLinkEigen(i, j) = static_cast<ComplexD>(TensorRemove(selfLink(i, j)));
|
|
||||||
|
|
||||||
selfLinkInvEigen = selfLinkEigen.inverse();
|
|
||||||
|
|
||||||
for(int i = 0; i < nbasis; ++i)
|
|
||||||
for(int j = 0; j < nbasis; ++j)
|
|
||||||
selfLinkInv(i, j) = selfLinkInvEigen(i, j);
|
|
||||||
|
|
||||||
pokeLocalSite(selfLinkInv, AselfInv_v, lcoor);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FillHalfCbs() {
|
|
||||||
std::cout << GridLogDebug << "CoarsenedMatrix::FillHalfCbs" << std::endl;
|
|
||||||
for(int p = 0; p < geom.npoint; ++p) {
|
|
||||||
pickCheckerboard(Even, Aeven[p], A[p]);
|
|
||||||
pickCheckerboard(Odd, Aodd[p], A[p]);
|
|
||||||
}
|
|
||||||
pickCheckerboard(Even, AselfInvEven, AselfInv);
|
|
||||||
pickCheckerboard(Odd, AselfInvOdd, AselfInv);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -1,704 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/GeneralCoarsenedMatrix.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
|
|
||||||
|
|
||||||
#include <Grid/qcd/QCD.h> // needed for Dagger(Yes|No), Inverse(Yes|No)
|
|
||||||
|
|
||||||
#include <Grid/lattice/PaddedCell.h>
|
|
||||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
|
||||||
#include <Grid/algorithms/deflation/MultiRHSBlockProject.h>
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
// Fine Object == (per site) type of fine field
|
|
||||||
// nbasis == number of deflation vectors
|
|
||||||
template<class Fobj,class CComplex,int nbasis>
|
|
||||||
class GeneralCoarsenedMatrix : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef GeneralCoarsenedMatrix<Fobj,CComplex,nbasis> GeneralCoarseOp;
|
|
||||||
typedef iVector<CComplex,nbasis > siteVector;
|
|
||||||
typedef iMatrix<CComplex,nbasis > siteMatrix;
|
|
||||||
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 Lattice<CComplex > FineComplexField;
|
|
||||||
typedef CoarseVector Field;
|
|
||||||
////////////////////
|
|
||||||
// Data members
|
|
||||||
////////////////////
|
|
||||||
int hermitian;
|
|
||||||
GridBase * _FineGrid;
|
|
||||||
GridCartesian * _CoarseGrid;
|
|
||||||
NonLocalStencilGeometry &geom;
|
|
||||||
PaddedCell Cell;
|
|
||||||
GeneralLocalStencil Stencil;
|
|
||||||
|
|
||||||
std::vector<CoarseMatrix> _A;
|
|
||||||
std::vector<CoarseMatrix> _Adag;
|
|
||||||
std::vector<CoarseVector> MultTemporaries;
|
|
||||||
|
|
||||||
int64_t MultCalls;
|
|
||||||
double MultFlopsAccum;
|
|
||||||
double MultUsecAccum;
|
|
||||||
|
|
||||||
///////////////////////
|
|
||||||
// Interface
|
|
||||||
///////////////////////
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Bilingual accessors: everything a consumer needs to read the operator
|
|
||||||
// without knowing which of the three coarse classes it holds. The D
|
|
||||||
// dimensional grid the elements live on, the geometry they are indexed by,
|
|
||||||
// and one unpadded point at a time (a whole npoint vector is too much
|
|
||||||
// memory at production nbasis).
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
GridCartesian * CoarseGridD(void) { return _CoarseGrid; };
|
|
||||||
NonLocalStencilGeometry & Geometry(void) { return geom; };
|
|
||||||
void ExtractMatrix(int p,CoarseMatrix &A) { A = Cell.Extract(_A[p]); };
|
|
||||||
|
|
||||||
GridBase * Grid(void) { return _CoarseGrid; }; // this is all the linalg routines need to know
|
|
||||||
GridBase * FineGrid(void) { return _FineGrid; }; // this is all the linalg routines need to know
|
|
||||||
GridCartesian * CoarseGrid(void) { return _CoarseGrid; }; // this is all the linalg routines need to know
|
|
||||||
|
|
||||||
/* void ShiftMatrix(RealD shift)
|
|
||||||
{
|
|
||||||
int Nd=_FineGrid->Nd();
|
|
||||||
Coordinate zero_shift(Nd,0);
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
if ( zero_shift==geom.shifts[p] ) {
|
|
||||||
_A[p] = _A[p]+shift;
|
|
||||||
// _Adag[p] = _Adag[p]+shift;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void ProjectNearestNeighbour(RealD shift, GeneralCoarseOp &CopyMe)
|
|
||||||
{
|
|
||||||
int nfound=0;
|
|
||||||
std::cout << GridLogMessage <<"GeneralCoarsenedMatrix::ProjectNearestNeighbour "<< CopyMe._A[0].Grid()<<std::endl;
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
for(int pp=0;pp<CopyMe.geom.npoint;pp++){
|
|
||||||
// Search for the same relative shift
|
|
||||||
// Avoids brutal handling of Grid pointers
|
|
||||||
if ( CopyMe.geom.shifts[pp]==geom.shifts[p] ) {
|
|
||||||
_A[p] = CopyMe.Cell.Extract(CopyMe._A[pp]);
|
|
||||||
// _Adag[p] = CopyMe.Cell.Extract(CopyMe._Adag[pp]);
|
|
||||||
nfound++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GRID_ASSERT(nfound==geom.npoint);
|
|
||||||
ExchangeCoarseLinks();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
GeneralCoarsenedMatrix(NonLocalStencilGeometry &_geom,GridBase *FineGrid, GridCartesian * CoarseGrid,int _herm=1)
|
|
||||||
: geom(_geom),
|
|
||||||
_FineGrid(FineGrid),
|
|
||||||
_CoarseGrid(CoarseGrid),
|
|
||||||
hermitian(_herm),
|
|
||||||
Cell(_geom.Depth(),_CoarseGrid),
|
|
||||||
Stencil(Cell.grids.back(),geom.shifts),
|
|
||||||
MultCalls(0), MultFlopsAccum(0.0), MultUsecAccum(0.0)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
int npoint = _geom.npoint;
|
|
||||||
}
|
|
||||||
_A.resize(geom.npoint,CoarseGrid);
|
|
||||||
if ( !hermitian ) _Adag.resize(geom.npoint,CoarseGrid);
|
|
||||||
}
|
|
||||||
void M (const CoarseVector &in, CoarseVector &out)
|
|
||||||
{
|
|
||||||
Mult(_A,in,out);
|
|
||||||
}
|
|
||||||
void Mdag (const CoarseVector &in, CoarseVector &out)
|
|
||||||
{
|
|
||||||
if(hermitian)
|
|
||||||
Mult(_A,in,out);
|
|
||||||
else
|
|
||||||
Mult(_Adag,in,out);
|
|
||||||
}
|
|
||||||
void Mult (std::vector<CoarseMatrix> &A,const CoarseVector &in, CoarseVector &out)
|
|
||||||
{
|
|
||||||
RealD tviews=0; RealD ttot=0; RealD tmult=0; RealD texch=0; RealD text=0; RealD ttemps=0; RealD tcopy=0;
|
|
||||||
RealD tmult2=0;
|
|
||||||
|
|
||||||
ttot=-usecond();
|
|
||||||
conformable(CoarseGrid(),in.Grid());
|
|
||||||
conformable(in.Grid(),out.Grid());
|
|
||||||
out.Checkerboard() = in.Checkerboard();
|
|
||||||
CoarseVector tin=in;
|
|
||||||
|
|
||||||
texch-=usecond();
|
|
||||||
CoarseVector pin = Cell.ExchangePeriodic(tin);
|
|
||||||
texch+=usecond();
|
|
||||||
|
|
||||||
CoarseVector pout(pin.Grid());
|
|
||||||
|
|
||||||
int npoint = geom.npoint;
|
|
||||||
typedef LatticeView<Cobj> Aview;
|
|
||||||
typedef LatticeView<Cvec> Vview;
|
|
||||||
|
|
||||||
const int Nsimd = CComplex::Nsimd();
|
|
||||||
|
|
||||||
int64_t osites=pin.Grid()->oSites();
|
|
||||||
|
|
||||||
RealD flops = 1.0* npoint * nbasis * nbasis * 8.0 * osites * CComplex::Nsimd();
|
|
||||||
RealD bytes = 1.0*osites*sizeof(siteMatrix)*npoint
|
|
||||||
+ 2.0*osites*sizeof(siteVector)*npoint;
|
|
||||||
|
|
||||||
{
|
|
||||||
tviews-=usecond();
|
|
||||||
autoView( in_v , pin, AcceleratorRead);
|
|
||||||
autoView( out_v , pout, AcceleratorWriteDiscard);
|
|
||||||
autoView( Stencil_v , Stencil, AcceleratorRead);
|
|
||||||
tviews+=usecond();
|
|
||||||
|
|
||||||
// Static and prereserve to keep UVM region live and not resized across multiple calls
|
|
||||||
ttemps-=usecond();
|
|
||||||
MultTemporaries.resize(npoint,pin.Grid());
|
|
||||||
ttemps+=usecond();
|
|
||||||
std::vector<Aview> AcceleratorViewContainer_h;
|
|
||||||
std::vector<Vview> AcceleratorVecViewContainer_h;
|
|
||||||
|
|
||||||
tviews-=usecond();
|
|
||||||
for(int p=0;p<npoint;p++) {
|
|
||||||
AcceleratorViewContainer_h.push_back( A[p].View(AcceleratorRead));
|
|
||||||
AcceleratorVecViewContainer_h.push_back(MultTemporaries[p].View(AcceleratorWrite));
|
|
||||||
}
|
|
||||||
tviews+=usecond();
|
|
||||||
|
|
||||||
static deviceVector<Aview> AcceleratorViewContainer; AcceleratorViewContainer.resize(npoint);
|
|
||||||
static deviceVector<Vview> AcceleratorVecViewContainer; AcceleratorVecViewContainer.resize(npoint);
|
|
||||||
|
|
||||||
auto Aview_p = &AcceleratorViewContainer[0];
|
|
||||||
auto Vview_p = &AcceleratorVecViewContainer[0];
|
|
||||||
tcopy-=usecond();
|
|
||||||
acceleratorCopyToDevice(&AcceleratorViewContainer_h[0],&AcceleratorViewContainer[0],npoint *sizeof(Aview));
|
|
||||||
acceleratorCopyToDevice(&AcceleratorVecViewContainer_h[0],&AcceleratorVecViewContainer[0],npoint *sizeof(Vview));
|
|
||||||
tcopy+=usecond();
|
|
||||||
|
|
||||||
tmult-=usecond();
|
|
||||||
accelerator_for(spb, osites*nbasis*npoint, Nsimd, {
|
|
||||||
typedef decltype(coalescedRead(in_v[0](0))) calcComplex;
|
|
||||||
int32_t ss = spb/(nbasis*npoint);
|
|
||||||
int32_t bp = spb%(nbasis*npoint);
|
|
||||||
int32_t point= bp/nbasis;
|
|
||||||
int32_t b = bp%nbasis;
|
|
||||||
auto SE = Stencil_v.GetEntry(point,ss);
|
|
||||||
auto nbr = coalescedReadGeneralPermute(in_v[SE->_offset],SE->_permute,Nd);
|
|
||||||
auto res = coalescedRead(Aview_p[point][ss](0,b))*nbr(0);
|
|
||||||
for(int bb=1;bb<nbasis;bb++) {
|
|
||||||
res = res + coalescedRead(Aview_p[point][ss](bb,b))*nbr(bb);
|
|
||||||
}
|
|
||||||
coalescedWrite(Vview_p[point][ss](b),res);
|
|
||||||
});
|
|
||||||
tmult2-=usecond();
|
|
||||||
accelerator_for(sb, osites*nbasis, Nsimd, {
|
|
||||||
int ss = sb/nbasis;
|
|
||||||
int b = sb%nbasis;
|
|
||||||
auto res = coalescedRead(Vview_p[0][ss](b));
|
|
||||||
for(int point=1;point<npoint;point++){
|
|
||||||
res = res + coalescedRead(Vview_p[point][ss](b));
|
|
||||||
}
|
|
||||||
coalescedWrite(out_v[ss](b),res);
|
|
||||||
});
|
|
||||||
tmult2+=usecond();
|
|
||||||
tmult+=usecond();
|
|
||||||
for(int p=0;p<npoint;p++) {
|
|
||||||
AcceleratorViewContainer_h[p].ViewClose();
|
|
||||||
AcceleratorVecViewContainer_h[p].ViewClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
text-=usecond();
|
|
||||||
out = Cell.Extract(pout);
|
|
||||||
text+=usecond();
|
|
||||||
ttot+=usecond();
|
|
||||||
|
|
||||||
MultCalls++;
|
|
||||||
MultFlopsAccum += flops;
|
|
||||||
MultUsecAccum += ttot;
|
|
||||||
std::cout << GridLogPerformance
|
|
||||||
<< "Coarse Mult call " << MultCalls
|
|
||||||
<< " tot " << ttot << " us"
|
|
||||||
<< " kernel " << tmult << " us"
|
|
||||||
<< " kernel " << flops/tmult*1e-3 << " GFlop/s"
|
|
||||||
<< " overall " << MultFlopsAccum/MultUsecAccum*1e-3 << " GFlop/s (cumul)"
|
|
||||||
<< " bw " << bytes/tmult*1e-3 << " GB/s"
|
|
||||||
<< std::endl;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
void PopulateAdag(void)
|
|
||||||
{
|
|
||||||
#if 0
|
|
||||||
// Serial global peek/poke reference implementation
|
|
||||||
for(int64_t bidx=0;bidx<CoarseGrid()->gSites() ;bidx++){
|
|
||||||
Coordinate bcoor;
|
|
||||||
CoarseGrid()->GlobalIndexToGlobalCoor(bidx,bcoor);
|
|
||||||
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
Coordinate scoor = bcoor;
|
|
||||||
for(int mu=0;mu<bcoor.size();mu++){
|
|
||||||
int L = CoarseGrid()->GlobalDimensions()[mu];
|
|
||||||
scoor[mu] = (bcoor[mu] - geom.shifts[p][mu] + L) % L; // Modulo arithmetic
|
|
||||||
}
|
|
||||||
// Flip to poke/peekLocalSite and not too bad
|
|
||||||
auto link = peekSite(_A[p],scoor);
|
|
||||||
int pp = geom.Reverse(p);
|
|
||||||
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
|
|
||||||
}
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// A) Only reduced flops option is to use a padded cell of depth 4
|
|
||||||
// and apply MpcDagMpc in the padded cell.
|
|
||||||
//
|
|
||||||
// Makes for ONE application of MpcDagMpc per vector instead of 30 or 80.
|
|
||||||
// With the effective cell size around (B+8)^4 perhaps 12^4/4^4 ratio
|
|
||||||
// Cost is 81x more, same as stencil size.
|
|
||||||
//
|
|
||||||
// But: can eliminate comms and do as local dirichlet.
|
|
||||||
//
|
|
||||||
// Local exchange gauge field once.
|
|
||||||
// Apply to all vectors, local only computation.
|
|
||||||
// Must exchange ghost subcells in reverse process of PaddedCell to take inner products
|
|
||||||
//
|
|
||||||
// B) Can reduce cost: pad by 1, apply Deo (4^4+6^4+8^4+8^4 )/ (4x 4^4)
|
|
||||||
// pad by 2, apply Doe
|
|
||||||
// pad by 3, apply Deo
|
|
||||||
// then break out 8x directions; cost is ~10x MpcDagMpc per vector
|
|
||||||
//
|
|
||||||
// => almost factor of 10 in setup cost, excluding data rearrangement
|
|
||||||
//
|
|
||||||
// Intermediates -- ignore the corner terms, leave approximate and force Hermitian
|
|
||||||
// Intermediates -- pad by 2 and apply 1+8+24 = 33 times.
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
|
||||||
// BFM HDCG style approach: Solve a system of equations to get Aij
|
|
||||||
//////////////////////////////////////////////////////////
|
|
||||||
/*
|
|
||||||
* Here, k,l index which possible shift within the 3^Nd "ball" connected by MdagM.
|
|
||||||
*
|
|
||||||
* conj(phases[block]) proj[k][ block*Nvec+j ] = \sum_ball e^{i q_k . delta} < phi_{block,j} | MdagM | phi_{(block+delta),i} >
|
|
||||||
* = \sum_ball e^{iqk.delta} A_ji
|
|
||||||
*
|
|
||||||
* Must invert matrix M_k,l = e^[i q_k . delta_l]
|
|
||||||
*
|
|
||||||
* Where q_k = delta_k . (2*M_PI/global_nb[mu])
|
|
||||||
*/
|
|
||||||
#if 0
|
|
||||||
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
|
||||||
Aggregation<Fobj,CComplex,nbasis> & Subspace)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<< "GeneralCoarsenMatrix "<< std::endl;
|
|
||||||
GridBase *grid = FineGrid();
|
|
||||||
|
|
||||||
RealD tproj=0.0;
|
|
||||||
RealD teigen=0.0;
|
|
||||||
RealD tmat=0.0;
|
|
||||||
RealD tphase=0.0;
|
|
||||||
RealD tinv=0.0;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
// Orthogonalise the subblocks over the basis
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
CoarseScalar InnerProd(CoarseGrid());
|
|
||||||
blockOrthogonalise(InnerProd,Subspace.subspace);
|
|
||||||
|
|
||||||
const int npoint = geom.npoint;
|
|
||||||
|
|
||||||
Coordinate clatt = CoarseGrid()->GlobalDimensions();
|
|
||||||
int Nd = CoarseGrid()->Nd();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Here, k,l index which possible momentum/shift within the N-points connected by MdagM.
|
|
||||||
* Matrix index i is mapped to this shift via
|
|
||||||
* geom.shifts[i]
|
|
||||||
*
|
|
||||||
* 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}
|
|
||||||
*
|
|
||||||
* Must assemble and invert matrix M_k,l = e^[i q_k . delta_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}
|
|
||||||
*/
|
|
||||||
teigen-=usecond();
|
|
||||||
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
|
||||||
Eigen::MatrixXcd invMkl = 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.shifts[k][mu]*geom.shifts[l][mu];
|
|
||||||
}
|
|
||||||
phase=exp(phase*ci);
|
|
||||||
Mkl(k,l) = phase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
invMkl = Mkl.inverse();
|
|
||||||
teigen+=usecond();
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Now compute the matrix elements of linop between the orthonormal
|
|
||||||
// set of vectors.
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
FineField phaV(grid); // Phased block basis vector
|
|
||||||
FineField MphaV(grid);// Matrix applied
|
|
||||||
CoarseVector coarseInner(CoarseGrid());
|
|
||||||
|
|
||||||
std::vector<CoarseVector> ComputeProj(npoint,CoarseGrid());
|
|
||||||
std::vector<CoarseVector> FT(npoint,CoarseGrid());
|
|
||||||
for(int i=0;i<nbasis;i++){// Loop over basis vectors
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl;
|
|
||||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// Stick a phase on every block
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
tphase-=usecond();
|
|
||||||
CoarseComplexField coor(CoarseGrid());
|
|
||||||
CoarseComplexField pha(CoarseGrid()); pha=Zero();
|
|
||||||
for(int mu=0;mu<Nd;mu++){
|
|
||||||
LatticeCoordinate(coor,mu);
|
|
||||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
|
||||||
pha = pha + (TwoPiL * geom.shifts[p][mu]) * coor;
|
|
||||||
}
|
|
||||||
pha =exp(pha*ci);
|
|
||||||
phaV=Zero();
|
|
||||||
blockZAXPY(phaV,pha,Subspace.subspace[i],phaV);
|
|
||||||
tphase+=usecond();
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// Multiple phased subspace vector by matrix and project to subspace
|
|
||||||
// Remove local bulk phase to leave relative phases
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
tmat-=usecond();
|
|
||||||
linop.Op(phaV,MphaV);
|
|
||||||
tmat+=usecond();
|
|
||||||
|
|
||||||
tproj-=usecond();
|
|
||||||
blockProject(coarseInner,MphaV,Subspace.subspace);
|
|
||||||
coarseInner = conjugate(pha) * coarseInner;
|
|
||||||
|
|
||||||
ComputeProj[p] = coarseInner;
|
|
||||||
tproj+=usecond();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
tinv-=usecond();
|
|
||||||
for(int k=0;k<npoint;k++){
|
|
||||||
FT[k] = Zero();
|
|
||||||
for(int l=0;l<npoint;l++){
|
|
||||||
FT[k]= FT[k]+ invMkl(l,k)*ComputeProj[l];
|
|
||||||
}
|
|
||||||
|
|
||||||
int osites=CoarseGrid()->oSites();
|
|
||||||
autoView( A_v , _A[k], AcceleratorWrite);
|
|
||||||
autoView( FT_v , FT[k], AcceleratorRead);
|
|
||||||
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++){
|
|
||||||
A_v[sss](i,j) = FT_v[sss](j);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
});
|
|
||||||
}
|
|
||||||
tinv+=usecond();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only needed if nonhermitian
|
|
||||||
if ( ! hermitian ) {
|
|
||||||
std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
|
||||||
PopulateAdag();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to write something to populate Adag from A
|
|
||||||
ExchangeCoarseLinks();
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator eigen "<<teigen<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator phase "<<tphase<<" 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;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// Galerkin projection of matrix
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
|
||||||
Aggregation<Fobj,CComplex,nbasis> & Subspace)
|
|
||||||
{
|
|
||||||
CoarsenOperator(linop,Subspace,Subspace);
|
|
||||||
}
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// Petrov - Galerkin projection of matrix
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
|
||||||
Aggregation<Fobj,CComplex,nbasis> & U,
|
|
||||||
Aggregation<Fobj,CComplex,nbasis> & V)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage<< "GeneralCoarsenMatrix "<< std::endl;
|
|
||||||
GridBase *grid = FineGrid();
|
|
||||||
|
|
||||||
RealD tproj=0.0;
|
|
||||||
RealD teigen=0.0;
|
|
||||||
RealD tmat=0.0;
|
|
||||||
RealD tphase=0.0;
|
|
||||||
RealD tphaseBZ=0.0;
|
|
||||||
RealD tinv=0.0;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
// Orthogonalise the subblocks over the basis
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
CoarseScalar InnerProd(CoarseGrid());
|
|
||||||
blockOrthogonalise(InnerProd,V.subspace);
|
|
||||||
blockOrthogonalise(InnerProd,U.subspace);
|
|
||||||
|
|
||||||
const int npoint = geom.npoint;
|
|
||||||
|
|
||||||
Coordinate clatt = CoarseGrid()->GlobalDimensions();
|
|
||||||
int Nd = CoarseGrid()->Nd();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Here, k,l index which possible momentum/shift within the N-points connected by MdagM.
|
|
||||||
* Matrix index i is mapped to this shift via
|
|
||||||
* geom.shifts[i]
|
|
||||||
*
|
|
||||||
* 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}
|
|
||||||
*
|
|
||||||
* Must assemble and invert matrix M_k,l = e^[i q_k . delta_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}
|
|
||||||
*/
|
|
||||||
teigen-=usecond();
|
|
||||||
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
|
||||||
Eigen::MatrixXcd invMkl = 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.shifts[k][mu]*geom.shifts[l][mu];
|
|
||||||
}
|
|
||||||
phase=exp(phase*ci);
|
|
||||||
Mkl(k,l) = phase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
invMkl = Mkl.inverse();
|
|
||||||
teigen+=usecond();
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Now compute the matrix elements of linop between the orthonormal
|
|
||||||
// set of vectors.
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
std::vector<FineComplexField> phaF(npoint,grid);
|
|
||||||
std::vector<CoarseComplexField> pha(npoint,CoarseGrid());
|
|
||||||
|
|
||||||
typedef typename CComplex::scalar_type SComplex;
|
|
||||||
FineComplexField one(grid); one=SComplex(1.0);
|
|
||||||
FineComplexField zz(grid); zz = Zero();
|
|
||||||
tphase=-usecond();
|
|
||||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// Stick a phase on every block
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
CoarseComplexField coor(CoarseGrid());
|
|
||||||
pha[p]=Zero();
|
|
||||||
for(int mu=0;mu<Nd;mu++){
|
|
||||||
LatticeCoordinate(coor,mu);
|
|
||||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
|
||||||
pha[p] = pha[p] + (TwoPiL * geom.shifts[p][mu]) * coor;
|
|
||||||
}
|
|
||||||
pha[p] =exp(pha[p]*ci);
|
|
||||||
|
|
||||||
blockZAXPY(phaF[p],pha[p],one,zz);
|
|
||||||
|
|
||||||
}
|
|
||||||
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> 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
|
|
||||||
accelerator_barrier(); // ensure prior iteration's async writes are retired
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl;
|
|
||||||
|
|
||||||
tphaseBZ-=usecond();
|
|
||||||
for(int p=0;p<npoint;p++)
|
|
||||||
phaV_batch[p] = phaF[p] * V.subspace[i];
|
|
||||||
tphaseBZ+=usecond();
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" phaseBZ done"<< std::endl;
|
|
||||||
|
|
||||||
tmat-=usecond();
|
|
||||||
for(int p=0;p<npoint;p++)
|
|
||||||
linop.Op(phaV_batch[p], MphaV_batch[p]);
|
|
||||||
tmat+=usecond();
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" mat done"<< std::endl;
|
|
||||||
|
|
||||||
// One batched GEMM reads BLAS_V once for all npoint vectors.
|
|
||||||
tproj-=usecond();
|
|
||||||
Projector.blockProject(MphaV_batch, proj_batch);
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" blockProject done"<< std::endl;
|
|
||||||
for(int p=0;p<npoint;p++)
|
|
||||||
ComputeProj[p] = conjugate(pha[p]) * proj_batch[p];
|
|
||||||
tproj+=usecond();
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" proj done"<< std::endl;
|
|
||||||
|
|
||||||
tinv-=usecond();
|
|
||||||
for(int k=0;k<npoint;k++){
|
|
||||||
FT[k] = Zero();
|
|
||||||
for(int l=0;l<npoint;l++){
|
|
||||||
FT[k]= FT[k]+ invMkl(l,k)*ComputeProj[l];
|
|
||||||
}
|
|
||||||
|
|
||||||
int osites=CoarseGrid()->oSites();
|
|
||||||
autoView( A_v , _A[k], AcceleratorWrite);
|
|
||||||
autoView( FT_v , FT[k], AcceleratorRead);
|
|
||||||
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++){
|
|
||||||
A_v[sss](i,j) = FT_v[sss](j);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
});
|
|
||||||
}
|
|
||||||
tinv+=usecond();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only needed if nonhermitian
|
|
||||||
if ( ! hermitian ) {
|
|
||||||
std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
|
||||||
PopulateAdag();
|
|
||||||
}
|
|
||||||
|
|
||||||
// for(int p=0;p<geom.npoint;p++){
|
|
||||||
// std::cout << " _A["<<p<<"] "<<norm2(_A[p])<<std::endl;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Need to write something to populate Adag from A
|
|
||||||
ExchangeCoarseLinks();
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator eigen "<<teigen<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator phase "<<tphase<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator phaseBZ "<<tphaseBZ<<" 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;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
void ExchangeCoarseLinks(void){
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
_A[p] = Cell.ExchangePeriodic(_A[p]);
|
|
||||||
if ( !hermitian ) _Adag[p]= Cell.ExchangePeriodic(_Adag[p]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
@@ -1,738 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
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 MultiGeneralCoarsenedMatrix : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
|
|
||||||
public:
|
|
||||||
typedef typename CComplex::scalar_object SComplex;
|
|
||||||
typedef GeneralCoarsenedMatrix<Fobj,CComplex,nbasis> GeneralCoarseOp;
|
|
||||||
typedef MultiGeneralCoarsenedMatrix<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 Lattice<CComplex > FineComplexField;
|
|
||||||
typedef CoarseVector Field;
|
|
||||||
|
|
||||||
////////////////////
|
|
||||||
// Data members
|
|
||||||
////////////////////
|
|
||||||
GridCartesian * _CoarseGridMulti;
|
|
||||||
NonLocalStencilGeometry geom;
|
|
||||||
NonLocalStencilGeometry geom_srhs;
|
|
||||||
PaddedCell Cell;
|
|
||||||
GeneralLocalStencil Stencil;
|
|
||||||
|
|
||||||
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) { return _CoarseGridMulti; }; // this is all the linalg routines need to know
|
|
||||||
GridCartesian * CoarseGrid(void) { return _CoarseGridMulti; }; // this is all the linalg routines need to know
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Bilingual accessors, matching GeneralCoarsenedMatrix. Grid() here is the
|
|
||||||
// D+1 multiRHS grid and this class never holds the D dimensional one, so
|
|
||||||
// ExtractMatrix writes into whatever grid the caller's lattice is on.
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
NonLocalStencilGeometry & Geometry(void) { return geom_srhs; };
|
|
||||||
void ExtractMatrix(int p,CoarseMatrix &A) { BLAStoGrid(A,BLAS_A[p]); };
|
|
||||||
|
|
||||||
// I/O on the operator matrices, via the BLAS layout array. The parameter is
|
|
||||||
// a vector over the geometry points; the body indexes A[p].
|
|
||||||
void SetMatrix (int p,std::vector<CoarseMatrix> & A)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(A.size()==geom_srhs.npoint);
|
|
||||||
GridtoBLAS(A[p],BLAS_A[p]);
|
|
||||||
}
|
|
||||||
void GetMatrix (int p,std::vector<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;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
MultiGeneralCoarsenedMatrix(NonLocalStencilGeometry &_geom,GridCartesian *CoarseGridMulti) :
|
|
||||||
_CoarseGridMulti(CoarseGridMulti),
|
|
||||||
geom_srhs(_geom),
|
|
||||||
geom(_CoarseGridMulti,_geom.hops,_geom.skip+1),
|
|
||||||
Cell(geom.Depth(),_CoarseGridMulti),
|
|
||||||
Stencil(Cell.grids.back(),geom.shifts) // padded cell stencil
|
|
||||||
{
|
|
||||||
int32_t padded_sites = Cell.grids.back()->lSites();
|
|
||||||
int32_t unpadded_sites = CoarseGridMulti->lSites();
|
|
||||||
|
|
||||||
int32_t nrhs = CoarseGridMulti->FullDimensions()[0]; // # RHS
|
|
||||||
int32_t orhs = nrhs/CComplex::Nsimd();
|
|
||||||
|
|
||||||
padded_sites = padded_sites/nrhs;
|
|
||||||
unpadded_sites = unpadded_sites/nrhs;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
// Device data vector storage
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
BLAS_A.resize(geom.npoint);
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
BLAS_A[p].resize (unpadded_sites); // no ghost zone, npoint elements
|
|
||||||
}
|
|
||||||
|
|
||||||
BLAS_B.resize(nrhs *padded_sites); // includes ghost zone
|
|
||||||
BLAS_C.resize(nrhs *unpadded_sites); // no ghost zone
|
|
||||||
BLAS_AP.resize(geom.npoint);
|
|
||||||
BLAS_BP.resize(geom.npoint);
|
|
||||||
for(int p=0;p<geom.npoint;p++){
|
|
||||||
BLAS_AP[p].resize(unpadded_sites);
|
|
||||||
BLAS_BP[p].resize(unpadded_sites);
|
|
||||||
}
|
|
||||||
BLAS_CP.resize(unpadded_sites);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
// Pointers to data
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 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++){ // 4 volume, padded
|
|
||||||
int ghost_zone=0;
|
|
||||||
for(int32_t point = 0 ; point < geom.npoint; point++){
|
|
||||||
int i=s*orhs*geom.npoint+point;
|
|
||||||
if( Stencil._entries[i]._wrap ) { // stencil is indexed by the oSite of the CoarseGridMulti, hence orhs factor
|
|
||||||
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*orhs*geom.npoint+point;
|
|
||||||
int32_t nbr = Stencil._entries[i]._offset*CComplex::Nsimd(); // oSite -> lSite
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
|
|
||||||
Aggregation<Fobj,CComplex,nbasis> & Subspace,
|
|
||||||
GridBase *CoarseGrid)
|
|
||||||
{
|
|
||||||
#if 0
|
|
||||||
std::cout << GridLogMessage<< "GeneralCoarsenMatrixMrhs "<< std::endl;
|
|
||||||
|
|
||||||
GridBase *grid = Subspace.FineGrid;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
// Orthogonalise the subblocks over the basis
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
CoarseScalar InnerProd(CoarseGrid);
|
|
||||||
blockOrthogonalise(InnerProd,Subspace.subspace);
|
|
||||||
|
|
||||||
const int npoint = geom_srhs.npoint;
|
|
||||||
|
|
||||||
Coordinate clatt = CoarseGrid->GlobalDimensions();
|
|
||||||
int Nd = CoarseGrid->Nd();
|
|
||||||
/*
|
|
||||||
* Here, k,l index which possible momentum/shift within the N-points connected by MdagM.
|
|
||||||
* Matrix index i is mapped to this shift via
|
|
||||||
* geom.shifts[i]
|
|
||||||
*
|
|
||||||
* 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}
|
|
||||||
*
|
|
||||||
* Must assemble and invert matrix M_k,l = e^[i q_k . delta_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}
|
|
||||||
*/
|
|
||||||
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
|
||||||
Eigen::MatrixXcd invMkl = 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();
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Now compute the matrix elements of linop between the orthonormal
|
|
||||||
// set of vectors.
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
FineField phaV(grid); // Phased block basis vector
|
|
||||||
FineField MphaV(grid);// Matrix applied
|
|
||||||
std::vector<FineComplexField> phaF(npoint,grid);
|
|
||||||
std::vector<CoarseComplexField> pha(npoint,CoarseGrid);
|
|
||||||
|
|
||||||
CoarseVector coarseInner(CoarseGrid);
|
|
||||||
|
|
||||||
typedef typename CComplex::scalar_type SComplex;
|
|
||||||
FineComplexField one(grid); one=SComplex(1.0);
|
|
||||||
FineComplexField zz(grid); zz = Zero();
|
|
||||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// Stick a phase on every block
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
CoarseComplexField coor(CoarseGrid);
|
|
||||||
pha[p]=Zero();
|
|
||||||
for(int mu=0;mu<Nd;mu++){
|
|
||||||
LatticeCoordinate(coor,mu);
|
|
||||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
|
||||||
pha[p] = pha[p] + (TwoPiL * geom_srhs.shifts[p][mu]) * coor;
|
|
||||||
}
|
|
||||||
pha[p] =exp(pha[p]*ci);
|
|
||||||
|
|
||||||
blockZAXPY(phaF[p],pha[p],one,zz);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Could save on temporary storage here
|
|
||||||
std::vector<CoarseMatrix> _A;
|
|
||||||
_A.resize(geom_srhs.npoint,CoarseGrid);
|
|
||||||
|
|
||||||
std::vector<CoarseVector> ComputeProj(npoint,CoarseGrid);
|
|
||||||
CoarseVector FT(CoarseGrid);
|
|
||||||
for(int i=0;i<nbasis;i++){// Loop over basis vectors
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl;
|
|
||||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
|
||||||
|
|
||||||
phaV = phaF[p]*Subspace.subspace[i];
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// Multiple phased subspace vector by matrix and project to subspace
|
|
||||||
// Remove local bulk phase to leave relative phases
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
linop.Op(phaV,MphaV);
|
|
||||||
|
|
||||||
// Fixme, could use batched block projector here
|
|
||||||
blockProject(coarseInner,MphaV,Subspace.subspace);
|
|
||||||
|
|
||||||
coarseInner = conjugate(pha[p]) * coarseInner;
|
|
||||||
|
|
||||||
ComputeProj[p] = coarseInner;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Could do this with a block promote or similar BLAS call via the MultiRHSBlockProjector with a const matrix.
|
|
||||||
for(int k=0;k<npoint;k++){
|
|
||||||
|
|
||||||
FT = Zero();
|
|
||||||
for(int l=0;l<npoint;l++){
|
|
||||||
FT= FT+ invMkl(l,k)*ComputeProj[l];
|
|
||||||
}
|
|
||||||
|
|
||||||
int osites=CoarseGrid->oSites();
|
|
||||||
autoView( A_v , _A[k], AcceleratorWrite);
|
|
||||||
autoView( FT_v , FT, AcceleratorRead);
|
|
||||||
accelerator_for(sss, osites, 1, {
|
|
||||||
for(int j=0;j<nbasis;j++){
|
|
||||||
A_v[sss](i,j) = FT_v[sss](j);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only needed if nonhermitian
|
|
||||||
// if ( ! hermitian ) {
|
|
||||||
// std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
|
||||||
// PopulateAdag();
|
|
||||||
// }
|
|
||||||
// Need to write something to populate Adag from A
|
|
||||||
|
|
||||||
for(int p=0;p<geom_srhs.npoint;p++){
|
|
||||||
GridtoBLAS(_A[p],BLAS_A[p]);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
Grid : Message : 11698.730546 s : CoarsenOperator eigen 1334 us
|
|
||||||
Grid : Message : 11698.730563 s : CoarsenOperator phase 34729 us
|
|
||||||
Grid : Message : 11698.730565 s : CoarsenOperator phaseBZ 2423814 us
|
|
||||||
Grid : Message : 11698.730566 s : CoarsenOperator mat 127890998 us
|
|
||||||
Grid : Message : 11698.730567 s : CoarsenOperator proj 515840840 us
|
|
||||||
Grid : Message : 11698.730568 s : CoarsenOperator inv 103948313 us
|
|
||||||
Takes 600s to compute matrix elements, DOMINATED by the block project.
|
|
||||||
Easy to speed up with the batched block project.
|
|
||||||
Store npoint vectors, get npoint x Nbasis block projection, and 81 fold faster.
|
|
||||||
|
|
||||||
// Block project below taks to 240s
|
|
||||||
Grid : Message : 328.193418 s : CoarsenOperator phase 38338 us
|
|
||||||
Grid : Message : 328.193434 s : CoarsenOperator phaseBZ 1711226 us
|
|
||||||
Grid : Message : 328.193436 s : CoarsenOperator mat 122213270 us
|
|
||||||
//Grid : Message : 328.193438 s : CoarsenOperator proj 1181154 us <-- this is mistimed
|
|
||||||
//Grid : Message : 11698.730568 s : CoarsenOperator inv 103948313 us <-- Cut this ~10x if lucky by loop fusion
|
|
||||||
*/
|
|
||||||
#else
|
|
||||||
RealD tproj=0.0;
|
|
||||||
RealD tmat=0.0;
|
|
||||||
RealD tphase=0.0;
|
|
||||||
RealD tphaseBZ=0.0;
|
|
||||||
RealD tinv=0.0;
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<< "GeneralCoarsenMatrixMrhs "<< std::endl;
|
|
||||||
|
|
||||||
GridBase *grid = Subspace.FineGrid;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
// Orthogonalise the subblocks over the basis
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
CoarseScalar InnerProd(CoarseGrid);
|
|
||||||
blockOrthogonalise(InnerProd,Subspace.subspace);
|
|
||||||
|
|
||||||
|
|
||||||
MultiRHSBlockProject<Lattice<Fobj> > Projector;
|
|
||||||
Projector.Allocate(nbasis,grid,CoarseGrid);
|
|
||||||
Projector.ImportBasis(Subspace.subspace);
|
|
||||||
|
|
||||||
const int npoint = geom_srhs.npoint;
|
|
||||||
|
|
||||||
Coordinate clatt = CoarseGrid->GlobalDimensions();
|
|
||||||
int Nd = CoarseGrid->Nd();
|
|
||||||
/*
|
|
||||||
* Here, k,l index which possible momentum/shift within the N-points connected by MdagM.
|
|
||||||
* Matrix index i is mapped to this shift via
|
|
||||||
* geom.shifts[i]
|
|
||||||
*
|
|
||||||
* 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}
|
|
||||||
*
|
|
||||||
* Must assemble and invert matrix M_k,l = e^[i q_k . delta_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}
|
|
||||||
*/
|
|
||||||
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
|
|
||||||
Eigen::MatrixXcd invMkl = 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();
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Now compute the matrix elements of linop between the orthonormal
|
|
||||||
// set of vectors.
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
FineField phaV(grid); // Phased block basis vector
|
|
||||||
FineField MphaV(grid);// Matrix applied
|
|
||||||
std::vector<FineComplexField> phaF(npoint,grid);
|
|
||||||
std::vector<CoarseComplexField> pha(npoint,CoarseGrid);
|
|
||||||
|
|
||||||
CoarseVector coarseInner(CoarseGrid);
|
|
||||||
|
|
||||||
tphase=-usecond();
|
|
||||||
typedef typename CComplex::scalar_type SComplex;
|
|
||||||
FineComplexField one(grid); one=SComplex(1.0);
|
|
||||||
FineComplexField zz(grid); zz = Zero();
|
|
||||||
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
// Stick a phase on every block
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
CoarseComplexField coor(CoarseGrid);
|
|
||||||
pha[p]=Zero();
|
|
||||||
for(int mu=0;mu<Nd;mu++){
|
|
||||||
LatticeCoordinate(coor,mu);
|
|
||||||
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
|
|
||||||
pha[p] = pha[p] + (TwoPiL * geom_srhs.shifts[p][mu]) * coor;
|
|
||||||
}
|
|
||||||
pha[p] =exp(pha[p]*ci);
|
|
||||||
|
|
||||||
blockZAXPY(phaF[p],pha[p],one,zz);
|
|
||||||
}
|
|
||||||
tphase+=usecond();
|
|
||||||
|
|
||||||
// Could save on temporary storage here
|
|
||||||
std::vector<CoarseMatrix> _A;
|
|
||||||
_A.resize(geom_srhs.npoint,CoarseGrid);
|
|
||||||
|
|
||||||
// Count use small chunks than npoint == 81 and save memory
|
|
||||||
int batch = 9;
|
|
||||||
std::vector<FineField> _MphaV(batch,grid);
|
|
||||||
std::vector<CoarseVector> TmpProj(batch,CoarseGrid);
|
|
||||||
|
|
||||||
std::vector<CoarseVector> ComputeProj(npoint,CoarseGrid);
|
|
||||||
CoarseVector FT(CoarseGrid);
|
|
||||||
for(int i=0;i<nbasis;i++){// Loop over basis vectors
|
|
||||||
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl;
|
|
||||||
|
|
||||||
// std::cout << GridLogMessage << " phasing the fine vector "<<std::endl;
|
|
||||||
// Fixme : do this in batches
|
|
||||||
for(int p=0;p<npoint;p+=batch){ // Loop over momenta in npoint
|
|
||||||
|
|
||||||
for(int b=0;b<MIN(batch,npoint-p);b++){
|
|
||||||
tphaseBZ-=usecond();
|
|
||||||
phaV = phaF[p+b]*Subspace.subspace[i];
|
|
||||||
tphaseBZ+=usecond();
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// Multiple phased subspace vector by matrix and project to subspace
|
|
||||||
// Remove local bulk phase to leave relative phases
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// Memory footprint was an issue
|
|
||||||
tmat-=usecond();
|
|
||||||
linop.Op(phaV,MphaV);
|
|
||||||
_MphaV[b] = MphaV;
|
|
||||||
tmat+=usecond();
|
|
||||||
}
|
|
||||||
|
|
||||||
// std::cout << GridLogMessage << " Calling block project "<<std::endl;
|
|
||||||
tproj-=usecond();
|
|
||||||
Projector.blockProject(_MphaV,TmpProj);
|
|
||||||
tproj+=usecond();
|
|
||||||
|
|
||||||
// std::cout << GridLogMessage << " conj phasing the coarse vectors "<<std::endl;
|
|
||||||
for(int b=0;b<MIN(batch,npoint-p);b++){
|
|
||||||
ComputeProj[p+b] = conjugate(pha[p+b])*TmpProj[b];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Could do this with a block promote or similar BLAS call via the MultiRHSBlockProjector with a const matrix.
|
|
||||||
|
|
||||||
// std::cout << GridLogMessage << " Starting FT inv "<<std::endl;
|
|
||||||
tinv-=usecond();
|
|
||||||
for(int k=0;k<npoint;k++){
|
|
||||||
FT = Zero();
|
|
||||||
// 81 kernel calls as many ComputeProj vectors
|
|
||||||
// Could fuse with a vector of views, but ugly
|
|
||||||
// Could unroll the expression and run fewer kernels -- much more attractive
|
|
||||||
// Could also do non blocking.
|
|
||||||
#if 0
|
|
||||||
for(int l=0;l<npoint;l++){
|
|
||||||
FT= FT+ invMkl(l,k)*ComputeProj[l];
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
const int radix = 9;
|
|
||||||
int ll;
|
|
||||||
for(ll=0;ll+radix-1<npoint;ll+=radix){
|
|
||||||
// When ll = npoint-radix, ll+radix-1 = npoint-1, and we do it all.
|
|
||||||
FT = FT
|
|
||||||
+ invMkl(ll+0,k)*ComputeProj[ll+0]
|
|
||||||
+ invMkl(ll+1,k)*ComputeProj[ll+1]
|
|
||||||
+ invMkl(ll+2,k)*ComputeProj[ll+2]
|
|
||||||
+ invMkl(ll+3,k)*ComputeProj[ll+3]
|
|
||||||
+ invMkl(ll+4,k)*ComputeProj[ll+4]
|
|
||||||
+ invMkl(ll+5,k)*ComputeProj[ll+5]
|
|
||||||
+ invMkl(ll+6,k)*ComputeProj[ll+6]
|
|
||||||
+ invMkl(ll+7,k)*ComputeProj[ll+7]
|
|
||||||
+ invMkl(ll+8,k)*ComputeProj[ll+8];
|
|
||||||
}
|
|
||||||
for(int l=ll;l<npoint;l++){
|
|
||||||
FT= FT+ invMkl(l,k)*ComputeProj[l];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 1 kernel call -- must be cheaper
|
|
||||||
int osites=CoarseGrid->oSites();
|
|
||||||
autoView( A_v , _A[k], AcceleratorWrite);
|
|
||||||
autoView( FT_v , FT, AcceleratorRead);
|
|
||||||
accelerator_for(sss, osites, 1, {
|
|
||||||
for(int j=0;j<nbasis;j++){
|
|
||||||
A_v[sss](i,j) = FT_v[sss](j);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
tinv+=usecond();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only needed if nonhermitian
|
|
||||||
// if ( ! hermitian ) {
|
|
||||||
// std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
|
|
||||||
// PopulateAdag();
|
|
||||||
// }
|
|
||||||
// Need to write something to populate Adag from A
|
|
||||||
// std::cout << GridLogMessage << " Calling GridtoBLAS "<<std::endl;
|
|
||||||
for(int p=0;p<geom_srhs.npoint;p++){
|
|
||||||
GridtoBLAS(_A[p],BLAS_A[p]);
|
|
||||||
}
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator phase "<<tphase<<" us"<<std::endl;
|
|
||||||
std::cout << GridLogMessage<<"CoarsenOperator phaseBZ "<<tphaseBZ<<" 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;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
|
|
||||||
t_tot=-usecond();
|
|
||||||
CoarseVector tin=in;
|
|
||||||
t_exch=-usecond();
|
|
||||||
CoarseVector pin = Cell.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);
|
|
||||||
@@ -1,870 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Bilingual accessors, matching GeneralCoarsenedMatrix. Note Grid() is the
|
|
||||||
// D+1 multiRHS grid here, so a consumer wanting the space the elements live
|
|
||||||
// on must ask for CoarseGridD().
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
NonLocalStencilGeometry & Geometry(void) { return geom_srhs; };
|
|
||||||
void ExtractMatrix(int p,CoarseMatrix &A) { BLAStoGrid(A,BLAS_A[p]); };
|
|
||||||
|
|
||||||
// I/O on the operator matrices, via the BLAS layout array. The parameter is
|
|
||||||
// a vector over the geometry points; the body indexes A[p].
|
|
||||||
void SetMatrix (int p,std::vector<CoarseMatrix> & A)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(A.size()==geom_srhs.npoint);
|
|
||||||
GridtoBLAS(A[p],BLAS_A[p]);
|
|
||||||
}
|
|
||||||
void GetMatrix (int p,std::vector<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);
|
|
||||||
}
|
|
||||||
|
|
||||||
GRID_TRACE("CoarseV2Mult");
|
|
||||||
t_tot=-usecond();
|
|
||||||
CoarseVector tin=in;
|
|
||||||
t_exch=-usecond();
|
|
||||||
// lambda scope so the roctx range covers exactly the exchange; the
|
|
||||||
// PaddedCellFwd/BwdMPI markers inside it then nest properly.
|
|
||||||
CoarseVector pin = [&](){ GRID_TRACE("CoarseV2Exchange");
|
|
||||||
return 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();
|
|
||||||
{ GRID_TRACE("CoarseV2GridToBLAS");
|
|
||||||
GridtoBLAS(pin,BLAS_B);
|
|
||||||
}
|
|
||||||
t_GtoB+=usecond();
|
|
||||||
|
|
||||||
GridBLAS BLAS;
|
|
||||||
|
|
||||||
t_mult=-usecond();
|
|
||||||
{ GRID_TRACE("CoarseV2StencilGEMM");
|
|
||||||
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();
|
|
||||||
{ GRID_TRACE("CoarseV2BLASToGrid");
|
|
||||||
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);
|
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/algorithms/GeneralCoarsenedMatrix.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);
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
|
||||||
// Geometry class in cartesian case
|
|
||||||
/////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class Geometry {
|
|
||||||
public:
|
|
||||||
int npoint;
|
|
||||||
int base;
|
|
||||||
std::vector<int> directions ;
|
|
||||||
std::vector<int> displacements;
|
|
||||||
std::vector<int> points_dagger;
|
|
||||||
|
|
||||||
Geometry(int _d) {
|
|
||||||
|
|
||||||
base = (_d==5) ? 1:0;
|
|
||||||
|
|
||||||
// make coarse grid stencil for 4d , not 5d
|
|
||||||
if ( _d==5 ) _d=4;
|
|
||||||
|
|
||||||
npoint = 2*_d+1;
|
|
||||||
directions.resize(npoint);
|
|
||||||
displacements.resize(npoint);
|
|
||||||
points_dagger.resize(npoint);
|
|
||||||
for(int d=0;d<_d;d++){
|
|
||||||
directions[d ] = d+base;
|
|
||||||
directions[d+_d] = d+base;
|
|
||||||
displacements[d ] = +1;
|
|
||||||
displacements[d+_d]= -1;
|
|
||||||
points_dagger[d ] = d+_d;
|
|
||||||
points_dagger[d+_d] = d;
|
|
||||||
}
|
|
||||||
directions [2*_d]=0;
|
|
||||||
displacements[2*_d]=0;
|
|
||||||
points_dagger[2*_d]=2*_d;
|
|
||||||
}
|
|
||||||
|
|
||||||
int point(int dir, int disp) {
|
|
||||||
GRID_ASSERT(disp == -1 || disp == 0 || disp == 1);
|
|
||||||
GRID_ASSERT(base+0 <= dir && dir < base+4);
|
|
||||||
|
|
||||||
// directions faster index = new indexing
|
|
||||||
// 4d (base = 0):
|
|
||||||
// point 0 1 2 3 4 5 6 7 8
|
|
||||||
// dir 0 1 2 3 0 1 2 3 0
|
|
||||||
// disp +1 +1 +1 +1 -1 -1 -1 -1 0
|
|
||||||
// 5d (base = 1):
|
|
||||||
// point 0 1 2 3 4 5 6 7 8
|
|
||||||
// dir 1 2 3 4 1 2 3 4 0
|
|
||||||
// disp +1 +1 +1 +1 -1 -1 -1 -1 0
|
|
||||||
|
|
||||||
// displacements faster index = old indexing
|
|
||||||
// 4d (base = 0):
|
|
||||||
// point 0 1 2 3 4 5 6 7 8
|
|
||||||
// dir 0 0 1 1 2 2 3 3 0
|
|
||||||
// disp +1 -1 +1 -1 +1 -1 +1 -1 0
|
|
||||||
// 5d (base = 1):
|
|
||||||
// point 0 1 2 3 4 5 6 7 8
|
|
||||||
// dir 1 1 2 2 3 3 4 4 0
|
|
||||||
// disp +1 -1 +1 -1 +1 -1 +1 -1 0
|
|
||||||
|
|
||||||
if(dir == 0 and disp == 0)
|
|
||||||
return 8;
|
|
||||||
else // New indexing
|
|
||||||
return (1 - disp) / 2 * 4 + dir - base;
|
|
||||||
// else // Old indexing
|
|
||||||
// return (4 * (dir - base) + 1 - disp) / 2;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
|
||||||
// Less local equivalent of Geometry class in cartesian case
|
|
||||||
/////////////////////////////////////////////////////////////////
|
|
||||||
class NonLocalStencilGeometry {
|
|
||||||
public:
|
|
||||||
// int depth;
|
|
||||||
int skip;
|
|
||||||
int hops;
|
|
||||||
int npoint;
|
|
||||||
std::vector<Coordinate> shifts;
|
|
||||||
Coordinate stencil_size;
|
|
||||||
Coordinate stencil_lo;
|
|
||||||
Coordinate stencil_hi;
|
|
||||||
GridCartesian *grid;
|
|
||||||
GridCartesian *Grid() {return grid;};
|
|
||||||
int Depth(void){return 1;}; // Ghost zone depth
|
|
||||||
int Hops(void){return hops;}; // # of hops=> level of corner fill in in stencil
|
|
||||||
int DimSkip(void){return skip;};
|
|
||||||
|
|
||||||
virtual ~NonLocalStencilGeometry() {};
|
|
||||||
|
|
||||||
int Reverse(int point)
|
|
||||||
{
|
|
||||||
int Nd = Grid()->Nd();
|
|
||||||
Coordinate shft = shifts[point];
|
|
||||||
Coordinate rev(Nd);
|
|
||||||
for(int mu=0;mu<Nd;mu++) rev[mu]= -shft[mu];
|
|
||||||
for(int p=0;p<npoint;p++){
|
|
||||||
if(rev==shifts[p]){
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
void BuildShifts(void)
|
|
||||||
{
|
|
||||||
this->shifts.resize(0);
|
|
||||||
int Nd = this->grid->Nd();
|
|
||||||
|
|
||||||
int dd = this->DimSkip();
|
|
||||||
for(int s0=this->stencil_lo[dd+0];s0<=this->stencil_hi[dd+0];s0++){
|
|
||||||
for(int s1=this->stencil_lo[dd+1];s1<=this->stencil_hi[dd+1];s1++){
|
|
||||||
for(int s2=this->stencil_lo[dd+2];s2<=this->stencil_hi[dd+2];s2++){
|
|
||||||
for(int s3=this->stencil_lo[dd+3];s3<=this->stencil_hi[dd+3];s3++){
|
|
||||||
Coordinate sft(Nd,0);
|
|
||||||
sft[dd+0] = s0;
|
|
||||||
sft[dd+1] = s1;
|
|
||||||
sft[dd+2] = s2;
|
|
||||||
sft[dd+3] = s3;
|
|
||||||
int nhops = abs(s0)+abs(s1)+abs(s2)+abs(s3);
|
|
||||||
if(nhops<=this->hops) this->shifts.push_back(sft);
|
|
||||||
}}}}
|
|
||||||
this->npoint = this->shifts.size();
|
|
||||||
std::cout << GridLogMessage << "NonLocalStencilGeometry has "<< this->npoint << " terms in stencil "<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
NonLocalStencilGeometry(GridCartesian *_coarse_grid,int _hops,int _skip) : grid(_coarse_grid), hops(_hops), skip(_skip)
|
|
||||||
{
|
|
||||||
Coordinate latt = grid->GlobalDimensions();
|
|
||||||
stencil_size.resize(grid->Nd());
|
|
||||||
stencil_lo.resize(grid->Nd());
|
|
||||||
stencil_hi.resize(grid->Nd());
|
|
||||||
for(int d=0;d<grid->Nd();d++){
|
|
||||||
if ( latt[d] == 1 ) {
|
|
||||||
stencil_lo[d] = 0;
|
|
||||||
stencil_hi[d] = 0;
|
|
||||||
stencil_size[d]= 1;
|
|
||||||
} else if ( latt[d] == 2 ) {
|
|
||||||
stencil_lo[d] = -1;
|
|
||||||
stencil_hi[d] = 0;
|
|
||||||
stencil_size[d]= 2;
|
|
||||||
} else if ( latt[d] > 2 ) {
|
|
||||||
stencil_lo[d] = -1;
|
|
||||||
stencil_hi[d] = 1;
|
|
||||||
stencil_size[d]= 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this->BuildShifts();
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// Need to worry about red-black now
|
|
||||||
class NonLocalStencilGeometry4D : public NonLocalStencilGeometry {
|
|
||||||
public:
|
|
||||||
virtual int DerivedDimSkip(void) { return 0;};
|
|
||||||
NonLocalStencilGeometry4D(GridCartesian *Coarse,int _hops) : NonLocalStencilGeometry(Coarse,_hops,0) { };
|
|
||||||
virtual ~NonLocalStencilGeometry4D() {};
|
|
||||||
};
|
|
||||||
class NonLocalStencilGeometry5D : public NonLocalStencilGeometry {
|
|
||||||
public:
|
|
||||||
virtual int DerivedDimSkip(void) { return 1; };
|
|
||||||
NonLocalStencilGeometry5D(GridCartesian *Coarse,int _hops) : NonLocalStencilGeometry(Coarse,_hops,1) { };
|
|
||||||
virtual ~NonLocalStencilGeometry5D() {};
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
* Bunch of different options classes
|
|
||||||
*/
|
|
||||||
class NextToNextToNextToNearestStencilGeometry4D : public NonLocalStencilGeometry4D {
|
|
||||||
public:
|
|
||||||
NextToNextToNextToNearestStencilGeometry4D(GridCartesian *Coarse) : NonLocalStencilGeometry4D(Coarse,4)
|
|
||||||
{
|
|
||||||
};
|
|
||||||
};
|
|
||||||
class NextToNextToNextToNearestStencilGeometry5D : public NonLocalStencilGeometry5D {
|
|
||||||
public:
|
|
||||||
NextToNextToNextToNearestStencilGeometry5D(GridCartesian *Coarse) : NonLocalStencilGeometry5D(Coarse,4)
|
|
||||||
{
|
|
||||||
};
|
|
||||||
};
|
|
||||||
class NextToNearestStencilGeometry4D : public NonLocalStencilGeometry4D {
|
|
||||||
public:
|
|
||||||
NextToNearestStencilGeometry4D(GridCartesian *Coarse) : NonLocalStencilGeometry4D(Coarse,2)
|
|
||||||
{
|
|
||||||
};
|
|
||||||
};
|
|
||||||
class NextToNearestStencilGeometry5D : public NonLocalStencilGeometry5D {
|
|
||||||
public:
|
|
||||||
NextToNearestStencilGeometry5D(GridCartesian *Coarse) : NonLocalStencilGeometry5D(Coarse,2)
|
|
||||||
{
|
|
||||||
};
|
|
||||||
};
|
|
||||||
class NearestStencilGeometry4D : public NonLocalStencilGeometry4D {
|
|
||||||
public:
|
|
||||||
NearestStencilGeometry4D(GridCartesian *Coarse) : NonLocalStencilGeometry4D(Coarse,1)
|
|
||||||
{
|
|
||||||
};
|
|
||||||
};
|
|
||||||
class NearestStencilGeometry5D : public NonLocalStencilGeometry5D {
|
|
||||||
public:
|
|
||||||
NearestStencilGeometry5D(GridCartesian *Coarse) : NonLocalStencilGeometry5D(Coarse,1)
|
|
||||||
{
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
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);
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: Grid/algorithms/multigrid/MultiGrid.h
|
|
||||||
|
|
||||||
Copyright (C) 2023
|
|
||||||
|
|
||||||
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/multigrid/Aggregates.h>
|
|
||||||
#include <Grid/algorithms/multigrid/Geometry.h>
|
|
||||||
#include <Grid/algorithms/multigrid/CoarsenedMatrix.h>
|
|
||||||
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrix.h>
|
|
||||||
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHS.h>
|
|
||||||
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHSV2.h>
|
|
||||||
#include <Grid/algorithms/multigrid/MrhsPromotedOperator.h>
|
|
||||||
@@ -1,874 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
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>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
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; // panel collective (see tBarrier)
|
|
||||||
double tBarrier; // arrival skew, when DENSE_BARRIER_PROBE
|
|
||||||
double tRepack; // rank-major -> panel (gather path)
|
|
||||||
double tGemm; // strided gemm + synchronise
|
|
||||||
double tLeaf; // leaf inversions
|
|
||||||
double tARmin; // fastest single panel collective
|
|
||||||
double tARmax; // slowest single panel collective
|
|
||||||
std::vector<double> tARall; // every panel collective, for percentiles
|
|
||||||
uint64_t bytesAllreduce;
|
|
||||||
uint64_t nAllreduce; // panel collectives
|
|
||||||
uint64_t nGatherGemm; // GatherGemm calls
|
|
||||||
uint64_t nGather; // gather-path collectives (debug gate)
|
|
||||||
uint64_t nGatherV; // ... of which MPI_Allgatherv
|
|
||||||
uint64_t nBcast; // ... of which Bcast-assembled
|
|
||||||
|
|
||||||
// Persistent grow-only device panel; assembly and collectives are
|
|
||||||
// device-resident. Device builds require GPU-aware MPI.
|
|
||||||
deviceVector<ComplexD> dPanelBuf;
|
|
||||||
// Rank-major receive staging for the AllGatherV path, plus the per-panel-row
|
|
||||||
// owner maps that drive the repack. Grow-only, same discipline as dPanelBuf:
|
|
||||||
// a fresh device allocation per collective is catastrophic (measured).
|
|
||||||
deviceVector<ComplexD> dRecvBuf;
|
|
||||||
deviceVector<int64_t> dOwnerOff; // panel row -> owner's first panel row
|
|
||||||
deviceVector<int64_t> dOwnerRows; // panel row -> owner's row count
|
|
||||||
|
|
||||||
// DENSE_GATHER : transport for the panel assembly.
|
|
||||||
// 0 = zero-fill + GlobalSumVector (default; moves the payload twice)
|
|
||||||
// 1 = MPI_Allgatherv (KNOWN BROKEN here, see guard below)
|
|
||||||
// 2 = C sequential MPI_Bcast, one per contributing rank. Bcast has no
|
|
||||||
// count vector, so the zero-count shape that breaks Allgatherv
|
|
||||||
// cannot arise. Costs C collectives instead of 1 and the roots do
|
|
||||||
// not transmit concurrently, so the byte cost is ~2*panel per rank
|
|
||||||
// -- the same as the allreduce. It wins only if Bcast is faster
|
|
||||||
// PER BYTE than Allreduce, which is why it must be measured.
|
|
||||||
// DENSE_GATHER_MIN_BYTES : panels below this stay on the allreduce path.
|
|
||||||
// Default 0 (always gather). The Schur tree puts
|
|
||||||
// 94% of its bytes in panels with ~3.7 MB per-rank
|
|
||||||
// messages, i.e. squarely bandwidth bound; only the
|
|
||||||
// two deepest levels (1.2% of bytes, 16-65 KB per
|
|
||||||
// rank) are latency bound. This exists so that
|
|
||||||
// tail can be excluded with an env var rather than
|
|
||||||
// a rebuild, if it ever proves to matter.
|
|
||||||
// DENSE_BARRIER_PROBE : Barrier() before each panel collective.
|
|
||||||
// DEFAULTS ON, and it is an optimisation rather than instrumentation.
|
|
||||||
// Measured at 288 ranks, N=138240: comms 380.3 s -> 314.7 s and the whole
|
|
||||||
// invert 394.3 s -> 327.7 s, with the worst single collective falling
|
|
||||||
// 2474.9 ms -> 256.2 ms and effective rate 2.40 -> 4.27 GB/s. Convoy
|
|
||||||
// accounting alone predicts NO saving (barrier and collective both
|
|
||||||
// complete at max-over-ranks), so the mechanism is that the collective
|
|
||||||
// itself degrades under skewed arrival, not that skew is rebooked.
|
|
||||||
// Set DENSE_BARRIER_PROBE=0 to recover the old behaviour. It also
|
|
||||||
// separates arrival skew (tBarrier) from transfer (tAllreduce).
|
|
||||||
int useGather;
|
|
||||||
int64_t gatherMinBytes;
|
|
||||||
int barrierProbe;
|
|
||||||
// DENSE_GATHER_DEBUG=N : dump the descriptor of the first N AllGatherV
|
|
||||||
// calls from rank 0. The tiling invariant below is checked ALWAYS -- it
|
|
||||||
// costs one O(P) host loop against a collective, and a counts array that
|
|
||||||
// does not tile the panel is exactly the failure we are hunting.
|
|
||||||
int gatherDebug;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
useGather = getenv("DENSE_GATHER") ? atoi(getenv("DENSE_GATHER")) : 0;
|
|
||||||
gatherMinBytes = getenv("DENSE_GATHER_MIN_BYTES")
|
|
||||||
? atol(getenv("DENSE_GATHER_MIN_BYTES")) : 0;
|
|
||||||
barrierProbe = getenv("DENSE_BARRIER_PROBE") ? atoi(getenv("DENSE_BARRIER_PROBE")) : 1;
|
|
||||||
gatherDebug = getenv("DENSE_GATHER_DEBUG") ? atoi(getenv("DENSE_GATHER_DEBUG")) : 0;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// DENSE_GATHER=1 is KNOWN BROKEN on Cray MPICH and refuses to start.
|
|
||||||
//
|
|
||||||
// The gather asks MPI_Allgatherv to assemble a panel to which only the
|
|
||||||
// rank sub-range [rB0,rB1) contributes; every other rank passes count 0.
|
|
||||||
// At Schur depth d that is P/2^(d+1) contributors out of P, i.e. 9 of 288
|
|
||||||
// at depth 4 and ~1 of 288 at depth 7. Measured consequences at 288
|
|
||||||
// ranks (tests/debug/Test_allgather):
|
|
||||||
// T5 288/288 contributing, 18.4 MB on device : sub-second.
|
|
||||||
// T6 144/288 contributing, 9.2 MB on device : ~53 s.
|
|
||||||
// and in production, 9/288 contributing at 299 MB hangs and then aborts
|
|
||||||
// inside PMPI_Allgatherv ("req != NULL", mpir_request.h:508).
|
|
||||||
//
|
|
||||||
// The primitive itself is sound -- Test_allgather T1-T6 pass, and T3/T4
|
|
||||||
// verify it BITWISE against the zero-fill+GlobalSum idiom. What fails is
|
|
||||||
// MPI's handling of a collective in which almost every rank contributes
|
|
||||||
// nothing. Fixing it needs either P2P (a bisection allgather over
|
|
||||||
// [r0,r1)) or the 2D block-cyclic layout, which removes the shape
|
|
||||||
// altogether by giving every rank part of every sub-block.
|
|
||||||
//
|
|
||||||
// Fail here, in the first second, rather than 100 s and 36 nodes into a
|
|
||||||
// job. DENSE_GATHER_FORCE=1 proceeds anyway for debugging.
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
if ( (useGather==1) && !getenv("DENSE_GATHER_FORCE") )
|
|
||||||
{
|
|
||||||
std::cout << GridLogError
|
|
||||||
<< "DENSE_GATHER=1 is disabled: MPI_Allgatherv on this MPI is"
|
|
||||||
<< " pathological when most ranks contribute count 0 (see the"
|
|
||||||
<< " note at RecursiveSchurInverse.h, and Test_allgather T5 vs"
|
|
||||||
<< " T6). Unset DENSE_GATHER, or set DENSE_GATHER_FORCE=1 to"
|
|
||||||
<< " proceed anyway." << std::endl;
|
|
||||||
GRID_ASSERT(0 && "DENSE_GATHER=1 known broken: see comment above");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// 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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
// COLUMN chunking, not row chunking. Each rank's contribution to a
|
|
||||||
// column chunk is rows_r x nchunk at ld = B.rows -- i.e. exactly the
|
|
||||||
// contiguous window B.ColumnWindow(colB+j0) -- so the AllGatherV send
|
|
||||||
// needs no pack. Row chunking would slice each column and force one.
|
|
||||||
// It also makes every chunk a full-k product, so beta applies directly
|
|
||||||
// and the cross-chunk accumulation disappears.
|
|
||||||
int64_t bufs = useGather ? 2 : 1; // panel, plus receive staging
|
|
||||||
int64_t nc = panelBytes / ( bufs * (int64_t)sizeof(ComplexD) * k );
|
|
||||||
if ( nc < 1 ) nc = 1;
|
|
||||||
if ( nc > n ) nc = n;
|
|
||||||
GRID_ASSERT( k*nc < 2147483647L ); // collective counts are int
|
|
||||||
|
|
||||||
deviceVector<ComplexD> &dPanel = dPanelBuf;
|
|
||||||
if ( dPanel.size() < (uint64_t)k*nc ) dPanel.resize((uint64_t)k*nc);
|
|
||||||
deviceVector<ComplexD*> ap(1);
|
|
||||||
deviceVector<ComplexD*> bp(1);
|
|
||||||
deviceVector<ComplexD*> cp(1);
|
|
||||||
std::vector<ComplexD*> ptr(1);
|
|
||||||
|
|
||||||
// Panel-row -> owner maps for the repack. Depend only on [rB0,rB1),
|
|
||||||
// so they are built once per call rather than once per chunk.
|
|
||||||
if ( useGather )
|
|
||||||
{
|
|
||||||
if ( dRecvBuf.size() < (uint64_t)k*nc ) dRecvBuf.resize((uint64_t)k*nc);
|
|
||||||
if ( dOwnerOff.size() < (uint64_t)k ) dOwnerOff.resize((uint64_t)k);
|
|
||||||
if ( dOwnerRows.size() < (uint64_t)k ) dOwnerRows.resize((uint64_t)k);
|
|
||||||
std::vector<int64_t> hoff(k), hrows(k);
|
|
||||||
for(int r=rB0; r<rB1; r++)
|
|
||||||
{
|
|
||||||
int64_t off_r = rowStart[r] - rowStart[rB0];
|
|
||||||
int64_t rows_r = rowStart[r+1] - rowStart[r];
|
|
||||||
for(int64_t i=0;i<rows_r;i++){ hoff[off_r+i]=off_r; hrows[off_r+i]=rows_r; }
|
|
||||||
}
|
|
||||||
acceleratorCopyToDevice(&hoff[0], &dOwnerOff[0], (uint64_t)k*sizeof(int64_t));
|
|
||||||
acceleratorCopyToDevice(&hrows[0],&dOwnerRows[0],(uint64_t)k*sizeof(int64_t));
|
|
||||||
if ( owner ) GRID_ASSERT( B.rows == rowStart[me+1]-rowStart[me] );
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int64_t j0=0; j0<n; j0+=nc)
|
|
||||||
{
|
|
||||||
int64_t nchunk = std::min(nc, n-j0);
|
|
||||||
uint64_t panelWords = (uint64_t)k*nchunk;
|
|
||||||
uint64_t panelBytesThis = panelWords*sizeof(ComplexD);
|
|
||||||
|
|
||||||
// The MODE (0/1/2), not a boolean: `useGather && cond` collapses 2 to 1
|
|
||||||
// and silently dispatched DENSE_GATHER=2 onto the known-broken
|
|
||||||
// AllGatherV path (Frontier hang, 2026-08-24). Identical on all ranks.
|
|
||||||
int gatherThis = ( (int64_t)panelBytesThis >= gatherMinBytes ) ? useGather : 0;
|
|
||||||
|
|
||||||
// Arrival skew is charged to the barrier, so that tAllreduce measures
|
|
||||||
// transfer alone. Diagnostic only; off by default.
|
|
||||||
if ( barrierProbe ) { double tb = -usecond(); grid->Barrier(); tBarrier += tb+usecond(); }
|
|
||||||
|
|
||||||
double tar = -usecond();
|
|
||||||
if ( gatherThis )
|
|
||||||
{
|
|
||||||
//////////////////////////////////////////////////////////////////
|
|
||||||
// AllGatherV: move the payload once, no arithmetic, no zero fill.
|
|
||||||
// Receive is rank major -- a concatenation of rows_r x nchunk
|
|
||||||
// column-major blocks -- which is the correct ROW order but the
|
|
||||||
// wrong LAYOUT, so one device repack follows.
|
|
||||||
//////////////////////////////////////////////////////////////////
|
|
||||||
std::vector<int> counts(P,0), displs(P,0);
|
|
||||||
for(int r=rB0; r<rB1; r++)
|
|
||||||
{
|
|
||||||
counts[r] = (int)((rowStart[r+1]-rowStart[r])*nchunk);
|
|
||||||
displs[r] = (int)((rowStart[r] -rowStart[rB0])*nchunk);
|
|
||||||
}
|
|
||||||
//////////////////////////////////////////////////////////////////
|
|
||||||
// The counts MUST tile the panel exactly: every one of the
|
|
||||||
// k*nchunk words has exactly one owner, or MPI is handed an
|
|
||||||
// inconsistent descriptor. Always checked; O(P) against a
|
|
||||||
// collective is free.
|
|
||||||
//////////////////////////////////////////////////////////////////
|
|
||||||
int64_t csum = 0; int nz = 0;
|
|
||||||
for(int r=0;r<P;r++){ csum += counts[r]; if ( counts[r] ) nz++; }
|
|
||||||
GRID_ASSERT( csum == (int64_t)panelWords );
|
|
||||||
GRID_ASSERT( displs[rB1-1] + counts[rB1-1] == (int)panelWords );
|
|
||||||
// Gate on the count of GATHERS, not of all collectives. nAllreduce
|
|
||||||
// is already in the hundreds by the first gather -- the recursion is
|
|
||||||
// depth first and everything below the size threshold takes the
|
|
||||||
// allreduce path -- so gating on it prints nothing at any sane value.
|
|
||||||
nGather++;
|
|
||||||
if ( gatherDebug && ((int)nGather <= gatherDebug) && (me==0) ) {
|
|
||||||
std::cout << GridLogMessage << "GATHER["<<nGather-1<<"]"
|
|
||||||
<< " ranks ["<<rB0<<","<<rB1<<")"
|
|
||||||
<< " k "<<k<<" n "<<n<<" nchunk "<<nchunk
|
|
||||||
<< " panelWords "<<panelWords
|
|
||||||
<< " ("<<panelBytesThis/1024/1024<<" MB)"
|
|
||||||
<< " contributors "<<nz<<"/"<<P
|
|
||||||
<< " maxcount "<<*std::max_element(counts.begin(),counts.end())
|
|
||||||
<< " rank0: count "<<counts[me]<<(owner?" owner":" non-owner")
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
if ( gatherThis == 1 )
|
|
||||||
{
|
|
||||||
nGatherV++;
|
|
||||||
void *send = owner ? (void *)B.ColumnWindow(colB+j0) : (void *)&dRecvBuf[0];
|
|
||||||
grid->AllGatherV(send, counts[me],
|
|
||||||
(void *)&dRecvBuf[0], counts, displs, sizeof(ComplexD));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
// C sequential broadcasts into the SAME rank-major staging
|
|
||||||
// buffer, so the repack below is shared with the AllGatherV arm.
|
|
||||||
// The root must already hold its own block: B.ColumnWindow is
|
|
||||||
// contiguous rows_r x nchunk, and so is its slot in dRecvBuf.
|
|
||||||
// Every rank issues all C broadcasts in the same order, so
|
|
||||||
// collective matching stays positional and safe.
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
nBcast++;
|
|
||||||
if ( owner )
|
|
||||||
{
|
|
||||||
int64_t off_me = rowStart[me] - rowStart[rB0];
|
|
||||||
acceleratorCopyDeviceToDevice((void *)B.ColumnWindow(colB+j0),
|
|
||||||
(void *)&dRecvBuf[off_me*nchunk],
|
|
||||||
(uint64_t)B.rows*nchunk*sizeof(ComplexD));
|
|
||||||
}
|
|
||||||
for(int r=rB0;r<rB1;r++)
|
|
||||||
{
|
|
||||||
int64_t off_r = rowStart[r] - rowStart[rB0];
|
|
||||||
int64_t rows_r = rowStart[r+1] - rowStart[r];
|
|
||||||
if ( rows_r )
|
|
||||||
grid->Broadcast(r,(void *)&dRecvBuf[off_r*nchunk],
|
|
||||||
(uint64_t)rows_r*nchunk*sizeof(ComplexD));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tar += usecond();
|
|
||||||
|
|
||||||
tRepack -= usecond();
|
|
||||||
{
|
|
||||||
ComplexD *pan = &dPanel[0];
|
|
||||||
ComplexD *rcv = &dRecvBuf[0];
|
|
||||||
int64_t *ooff = &dOwnerOff[0];
|
|
||||||
int64_t *orows = &dOwnerRows[0];
|
|
||||||
int64_t kk = k, ncw = nchunk;
|
|
||||||
accelerator_for(idx, panelWords, 1, {
|
|
||||||
int64_t j = idx / kk;
|
|
||||||
int64_t p = idx - j*kk;
|
|
||||||
int64_t o = ooff[p];
|
|
||||||
int64_t rr= orows[p];
|
|
||||||
pan[idx] = rcv[(uint64_t)(o*ncw + (p-o) + j*rr)];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
tRepack += usecond();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//////////////////////////////////////////////////////////////////
|
|
||||||
// Zero fill + deposit + GlobalSumVector. Exact because the zero
|
|
||||||
// fill leaves exactly one contributing rank per element, but it
|
|
||||||
// moves the payload twice and reduces over zeros.
|
|
||||||
//////////////////////////////////////////////////////////////////
|
|
||||||
tar += usecond();
|
|
||||||
tMemset -= usecond();
|
|
||||||
acceleratorMemSet(&dPanel[0], 0, panelBytesThis);
|
|
||||||
tMemset += usecond();
|
|
||||||
if ( owner )
|
|
||||||
{
|
|
||||||
int64_t brows = B.rows;
|
|
||||||
int64_t kk = k;
|
|
||||||
int64_t dof = myOff;
|
|
||||||
ComplexD *src = B.ColumnWindow(colB+j0);
|
|
||||||
ComplexD *dst = &dPanel[0];
|
|
||||||
tDeposit -= usecond();
|
|
||||||
accelerator_for(idx, (uint64_t)(brows*nchunk), 1, {
|
|
||||||
int64_t j = idx / brows;
|
|
||||||
int64_t i = idx - j*brows;
|
|
||||||
dst[(uint64_t)(dof + i + j*kk)] = src[(uint64_t)(i + j*brows)];
|
|
||||||
});
|
|
||||||
tDeposit += usecond();
|
|
||||||
}
|
|
||||||
tar = -usecond();
|
|
||||||
grid->GlobalSumVector(&dPanel[0], (int)panelWords);
|
|
||||||
tar += usecond();
|
|
||||||
}
|
|
||||||
tAllreduce += tar;
|
|
||||||
tARmin = std::min(tARmin, tar);
|
|
||||||
tARmax = std::max(tARmax, tar);
|
|
||||||
tARall.push_back(tar);
|
|
||||||
bytesAllreduce += panelBytesThis;
|
|
||||||
nAllreduce++;
|
|
||||||
|
|
||||||
if ( m > 0 )
|
|
||||||
{
|
|
||||||
// Full-k product into this column chunk of C: beta applies directly.
|
|
||||||
ptr[0] = A.ColumnWindow(colA);
|
|
||||||
acceleratorCopyToDevice(&ptr[0], &ap[0], sizeof(ComplexD*));
|
|
||||||
ptr[0] = &dPanel[0];
|
|
||||||
acceleratorCopyToDevice(&ptr[0], &bp[0], sizeof(ComplexD*));
|
|
||||||
ptr[0] = C.ColumnWindow(colC+j0);
|
|
||||||
acceleratorCopyToDevice(&ptr[0], &cp[0], sizeof(ComplexD*));
|
|
||||||
|
|
||||||
tGemm -= usecond();
|
|
||||||
BLAS.gemmBatched(GridBLAS_OP_N, GridBLAS_OP_N,
|
|
||||||
(int)m, (int)nchunk, (int)k,
|
|
||||||
alpha, ap, (int)A.ld,
|
|
||||||
bp, (int)k,
|
|
||||||
beta, 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;
|
|
||||||
tBarrier = 0.0;
|
|
||||||
tRepack = 0.0;
|
|
||||||
tGemm = 0.0;
|
|
||||||
tLeaf = 0.0;
|
|
||||||
tARmin = 1.0e30;
|
|
||||||
tARmax = 0.0;
|
|
||||||
tARall.clear();
|
|
||||||
bytesAllreduce = 0;
|
|
||||||
nAllreduce = 0;
|
|
||||||
nGatherGemm = 0;
|
|
||||||
nGather = 0;
|
|
||||||
nGatherV = 0;
|
|
||||||
nBcast = 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
|
|
||||||
<< " barrier " << tBarrier/1.0e6
|
|
||||||
<< " repack " << tRepack/1.0e6
|
|
||||||
<< " gemm " << tGemm/1.0e6
|
|
||||||
<< " leaf " << tLeaf/1.0e6
|
|
||||||
<< std::endl;
|
|
||||||
std::cout << GridLogMessage << "Schur comms:"
|
|
||||||
<< " [transports run: allreduce " << (nAllreduce - nGatherV - nBcast)
|
|
||||||
<< " allgatherv " << nGatherV << " bcast " << nBcast << "]"
|
|
||||||
<< ( barrierProbe ? " [barrier probe on]" : "" )
|
|
||||||
<< " 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"
|
|
||||||
<< " effective " << (tAllreduce>0 ? bytesAllreduce/tAllreduce*1.0e6/1.0e9 : 0.0)
|
|
||||||
<< " GB/s"
|
|
||||||
<< std::endl;
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// Distribution, not just min/avg/max. A barrier that merely REBOOKS
|
|
||||||
// skew shifts the median; a barrier that suppresses pathological
|
|
||||||
// collectives shortens the tail. p50 vs p99 separates the two from a
|
|
||||||
// single run, which min/avg/max cannot.
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
if ( tARall.size() ) {
|
|
||||||
std::vector<double> v = tARall;
|
|
||||||
std::sort(v.begin(),v.end());
|
|
||||||
size_t n = v.size();
|
|
||||||
auto pc = [&](double f){ size_t i=(size_t)(f*(n-1)); return v[i]/1.0e3; };
|
|
||||||
double med = pc(0.50);
|
|
||||||
// how much time is spent in calls that are gross outliers
|
|
||||||
double tail=0.0; size_t ntail=0;
|
|
||||||
for(size_t i=0;i<n;i++) if ( v[i] > 4.0*med*1.0e3 ) { tail+=v[i]; ntail++; }
|
|
||||||
std::cout << GridLogMessage << "Schur comms distribution (boss):"
|
|
||||||
<< " p50 " << med
|
|
||||||
<< " p90 " << pc(0.90)
|
|
||||||
<< " p99 " << pc(0.99)
|
|
||||||
<< " ms calls > 4x median: " << ntail << "/" << n
|
|
||||||
<< " carrying " << tail/1.0e6 << " s"
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
@@ -1,25 +1,84 @@
|
|||||||
#include <Grid/GridCore.h>
|
#include <Grid/GridCore.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
namespace Grid {
|
||||||
|
|
||||||
MemoryStats *MemoryProfiler::stats = nullptr;
|
MemoryStats *MemoryProfiler::stats = nullptr;
|
||||||
bool MemoryProfiler::debug = false;
|
bool MemoryProfiler::debug = false;
|
||||||
|
|
||||||
|
int PointerCache::victim;
|
||||||
|
|
||||||
|
PointerCache::PointerCacheEntry PointerCache::Entries[PointerCache::Ncache];
|
||||||
|
|
||||||
|
void *PointerCache::Insert(void *ptr,size_t bytes) {
|
||||||
|
|
||||||
|
if (bytes < 4096 ) return ptr;
|
||||||
|
|
||||||
|
#ifdef GRID_OMP
|
||||||
|
assert(omp_in_parallel()==0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void * ret = NULL;
|
||||||
|
int v = -1;
|
||||||
|
|
||||||
|
for(int e=0;e<Ncache;e++) {
|
||||||
|
if ( Entries[e].valid==0 ) {
|
||||||
|
v=e;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( v==-1 ) {
|
||||||
|
v=victim;
|
||||||
|
victim = (victim+1)%Ncache;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( Entries[v].valid ) {
|
||||||
|
ret = Entries[v].address;
|
||||||
|
Entries[v].valid = 0;
|
||||||
|
Entries[v].address = NULL;
|
||||||
|
Entries[v].bytes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entries[v].address=ptr;
|
||||||
|
Entries[v].bytes =bytes;
|
||||||
|
Entries[v].valid =1;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *PointerCache::Lookup(size_t bytes) {
|
||||||
|
|
||||||
|
if (bytes < 4096 ) return NULL;
|
||||||
|
|
||||||
|
#ifdef _OPENMP
|
||||||
|
assert(omp_in_parallel()==0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for(int e=0;e<Ncache;e++){
|
||||||
|
if ( Entries[e].valid && ( Entries[e].bytes == bytes ) ) {
|
||||||
|
Entries[e].valid = 0;
|
||||||
|
return Entries[e].address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void check_huge_pages(void *Buf,uint64_t BYTES)
|
void check_huge_pages(void *Buf,uint64_t BYTES)
|
||||||
{
|
{
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
int fd = open("/proc/self/pagemap", O_RDONLY);
|
int fd = open("/proc/self/pagemap", O_RDONLY);
|
||||||
GRID_ASSERT(fd >= 0);
|
assert(fd >= 0);
|
||||||
const int page_size = 4096;
|
const int page_size = 4096;
|
||||||
uint64_t virt_pfn = (uint64_t)Buf / page_size;
|
uint64_t virt_pfn = (uint64_t)Buf / page_size;
|
||||||
off_t offset = sizeof(uint64_t) * virt_pfn;
|
off_t offset = sizeof(uint64_t) * virt_pfn;
|
||||||
uint64_t npages = (BYTES + page_size-1) / page_size;
|
uint64_t npages = (BYTES + page_size-1) / page_size;
|
||||||
std::vector<uint64_t> pagedata(npages);
|
uint64_t pagedata[npages];
|
||||||
uint64_t ret = lseek(fd, offset, SEEK_SET);
|
uint64_t ret = lseek(fd, offset, SEEK_SET);
|
||||||
GRID_ASSERT(ret == offset);
|
assert(ret == offset);
|
||||||
ret = ::read(fd, &pagedata[0], sizeof(uint64_t)*npages);
|
ret = ::read(fd, pagedata, sizeof(uint64_t)*npages);
|
||||||
GRID_ASSERT(ret == sizeof(uint64_t) * npages);
|
assert(ret == sizeof(uint64_t) * npages);
|
||||||
int nhugepages = npages / 512;
|
int nhugepages = npages / 512;
|
||||||
int n4ktotal, nnothuge;
|
int n4ktotal, nnothuge;
|
||||||
n4ktotal = 0;
|
n4ktotal = 0;
|
||||||
@@ -31,7 +90,7 @@ void check_huge_pages(void *Buf,uint64_t BYTES)
|
|||||||
++n4ktotal;
|
++n4ktotal;
|
||||||
if (pageaddr != baseaddr + j * page_size)
|
if (pageaddr != baseaddr + j * page_size)
|
||||||
++nnothuge;
|
++nnothuge;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int rank = CartesianCommunicator::RankWorld();
|
int rank = CartesianCommunicator::RankWorld();
|
||||||
printf("rank %d Allocated %d 4k pages, %d not in huge pages\n", rank, n4ktotal, nnothuge);
|
printf("rank %d Allocated %d 4k pages, %d not in huge pages\n", rank, n4ktotal, nnothuge);
|
||||||
@@ -47,21 +106,20 @@ std::string sizeString(const size_t bytes)
|
|||||||
double count = bytes;
|
double count = bytes;
|
||||||
|
|
||||||
while (count >= 1024 && s < 7)
|
while (count >= 1024 && s < 7)
|
||||||
{
|
{
|
||||||
s++;
|
s++;
|
||||||
count /= 1024;
|
count /= 1024;
|
||||||
}
|
}
|
||||||
if (count - floor(count) == 0.0)
|
if (count - floor(count) == 0.0)
|
||||||
{
|
{
|
||||||
snprintf(buf, bufSize, "%d %sB", (int)count, suffixes[s]);
|
snprintf(buf, bufSize, "%d %sB", (int)count, suffixes[s]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
snprintf(buf, bufSize, "%.1f %sB", count, suffixes[s]);
|
snprintf(buf, bufSize, "%.1f %sB", count, suffixes[s]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::string(buf);
|
return std::string(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
}
|
||||||
|
|
||||||
+209
-116
@@ -24,11 +24,109 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
See the full license in the file "LICENSE" in the top level distribution directory
|
See the full license in the file "LICENSE" in the top level distribution directory
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
/* END LEGAL */
|
/* END LEGAL */
|
||||||
#pragma once
|
#ifndef GRID_ALIGNED_ALLOCATOR_H
|
||||||
|
#define GRID_ALIGNED_ALLOCATOR_H
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
#ifdef HAVE_MALLOC_MALLOC_H
|
||||||
|
#include <malloc/malloc.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_MALLOC_H
|
||||||
|
#include <malloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_MM_MALLOC_H
|
||||||
|
#include <mm_malloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Grid {
|
||||||
|
|
||||||
|
class PointerCache {
|
||||||
|
private:
|
||||||
|
|
||||||
|
static const int Ncache=8;
|
||||||
|
static int victim;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void *address;
|
||||||
|
size_t bytes;
|
||||||
|
int valid;
|
||||||
|
} PointerCacheEntry;
|
||||||
|
|
||||||
|
static PointerCacheEntry Entries[Ncache];
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
static void *Insert(void *ptr,size_t bytes) ;
|
||||||
|
static void *Lookup(size_t bytes) ;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string sizeString(size_t bytes);
|
||||||
|
|
||||||
|
struct MemoryStats
|
||||||
|
{
|
||||||
|
size_t totalAllocated{0}, maxAllocated{0},
|
||||||
|
currentlyAllocated{0}, totalFreed{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
class MemoryProfiler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static MemoryStats *stats;
|
||||||
|
static bool debug;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define memString(bytes) std::to_string(bytes) + " (" + sizeString(bytes) + ")"
|
||||||
|
#define profilerDebugPrint \
|
||||||
|
if (MemoryProfiler::stats)\
|
||||||
|
{\
|
||||||
|
auto s = MemoryProfiler::stats;\
|
||||||
|
std::cout << GridLogDebug << "[Memory debug] Stats " << MemoryProfiler::stats << std::endl;\
|
||||||
|
std::cout << GridLogDebug << "[Memory debug] total : " << memString(s->totalAllocated) \
|
||||||
|
<< std::endl;\
|
||||||
|
std::cout << GridLogDebug << "[Memory debug] max : " << memString(s->maxAllocated) \
|
||||||
|
<< std::endl;\
|
||||||
|
std::cout << GridLogDebug << "[Memory debug] current: " << memString(s->currentlyAllocated) \
|
||||||
|
<< std::endl;\
|
||||||
|
std::cout << GridLogDebug << "[Memory debug] freed : " << memString(s->totalFreed) \
|
||||||
|
<< std::endl;\
|
||||||
|
}
|
||||||
|
|
||||||
|
#define profilerAllocate(bytes)\
|
||||||
|
if (MemoryProfiler::stats)\
|
||||||
|
{\
|
||||||
|
auto s = MemoryProfiler::stats;\
|
||||||
|
s->totalAllocated += (bytes);\
|
||||||
|
s->currentlyAllocated += (bytes);\
|
||||||
|
s->maxAllocated = std::max(s->maxAllocated, s->currentlyAllocated);\
|
||||||
|
}\
|
||||||
|
if (MemoryProfiler::debug)\
|
||||||
|
{\
|
||||||
|
std::cout << GridLogDebug << "[Memory debug] allocating " << memString(bytes) << std::endl;\
|
||||||
|
profilerDebugPrint;\
|
||||||
|
}
|
||||||
|
|
||||||
|
#define profilerFree(bytes)\
|
||||||
|
if (MemoryProfiler::stats)\
|
||||||
|
{\
|
||||||
|
auto s = MemoryProfiler::stats;\
|
||||||
|
s->totalFreed += (bytes);\
|
||||||
|
s->currentlyAllocated -= (bytes);\
|
||||||
|
}\
|
||||||
|
if (MemoryProfiler::debug)\
|
||||||
|
{\
|
||||||
|
std::cout << GridLogDebug << "[Memory debug] freeing " << memString(bytes) << std::endl;\
|
||||||
|
profilerDebugPrint;\
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_huge_pages(void *Buf,uint64_t BYTES);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// A lattice of something, but assume the something is SIMDized.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template<typename _Tp>
|
template<typename _Tp>
|
||||||
class alignedAllocator {
|
class alignedAllocator {
|
||||||
@@ -53,22 +151,46 @@ public:
|
|||||||
{
|
{
|
||||||
size_type bytes = __n*sizeof(_Tp);
|
size_type bytes = __n*sizeof(_Tp);
|
||||||
profilerAllocate(bytes);
|
profilerAllocate(bytes);
|
||||||
_Tp *ptr = (_Tp*) MemoryManager::CpuAllocate(bytes);
|
|
||||||
if ( (_Tp*)ptr == (_Tp *) NULL ) {
|
_Tp *ptr = (_Tp *) PointerCache::Lookup(bytes);
|
||||||
printf("Grid CPU Allocator got NULL for %lu bytes\n",(unsigned long) bytes );
|
// if ( ptr != NULL )
|
||||||
|
// std::cout << "alignedAllocator "<<__n << " cache hit "<< std::hex << ptr <<std::dec <<std::endl;
|
||||||
|
|
||||||
|
//////////////////
|
||||||
|
// Hack 2MB align; could make option probably doesn't need configurability
|
||||||
|
//////////////////
|
||||||
|
//define GRID_ALLOC_ALIGN (128)
|
||||||
|
#define GRID_ALLOC_ALIGN (2*1024*1024)
|
||||||
|
#ifdef HAVE_MM_MALLOC_H
|
||||||
|
if ( ptr == (_Tp *) NULL ) ptr = (_Tp *) _mm_malloc(bytes,GRID_ALLOC_ALIGN);
|
||||||
|
#else
|
||||||
|
if ( ptr == (_Tp *) NULL ) ptr = (_Tp *) memalign(GRID_ALLOC_ALIGN,bytes);
|
||||||
|
#endif
|
||||||
|
// std::cout << "alignedAllocator " << std::hex << ptr <<std::dec <<std::endl;
|
||||||
|
// First touch optimise in threaded loop
|
||||||
|
uint8_t *cp = (uint8_t *)ptr;
|
||||||
|
#ifdef GRID_OMP
|
||||||
|
#pragma omp parallel for
|
||||||
|
#endif
|
||||||
|
for(size_type n=0;n<bytes;n+=4096){
|
||||||
|
cp[n]=0;
|
||||||
}
|
}
|
||||||
GRID_ASSERT( ( (_Tp*)ptr != (_Tp *)NULL ) );
|
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void deallocate(pointer __p, size_type __n)
|
void deallocate(pointer __p, size_type __n) {
|
||||||
{
|
|
||||||
size_type bytes = __n * sizeof(_Tp);
|
size_type bytes = __n * sizeof(_Tp);
|
||||||
profilerFree(bytes);
|
|
||||||
MemoryManager::CpuFree((void *)__p,bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: hack for the copy constructor: it must be avoided to avoid single thread loop
|
profilerFree(bytes);
|
||||||
|
|
||||||
|
pointer __freeme = (pointer)PointerCache::Insert((void *)__p,bytes);
|
||||||
|
|
||||||
|
#ifdef HAVE_MM_MALLOC_H
|
||||||
|
if ( __freeme ) _mm_free((void *)__freeme);
|
||||||
|
#else
|
||||||
|
if ( __freeme ) free((void *)__freeme);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
void construct(pointer __p, const _Tp& __val) { };
|
void construct(pointer __p, const _Tp& __val) { };
|
||||||
void construct(pointer __p) { };
|
void construct(pointer __p) { };
|
||||||
void destroy(pointer __p) { };
|
void destroy(pointer __p) { };
|
||||||
@@ -76,11 +198,21 @@ public:
|
|||||||
template<typename _Tp> inline bool operator==(const alignedAllocator<_Tp>&, const alignedAllocator<_Tp>&){ return true; }
|
template<typename _Tp> inline bool operator==(const alignedAllocator<_Tp>&, const alignedAllocator<_Tp>&){ return true; }
|
||||||
template<typename _Tp> inline bool operator!=(const alignedAllocator<_Tp>&, const alignedAllocator<_Tp>&){ return false; }
|
template<typename _Tp> inline bool operator!=(const alignedAllocator<_Tp>&, const alignedAllocator<_Tp>&){ return false; }
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Unified virtual memory
|
// MPI3 : comms must use shm region
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
// SHMEM: comms must use symmetric heap
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
#ifdef GRID_COMMS_SHMEM
|
||||||
|
extern "C" {
|
||||||
|
#include <mpp/shmem.h>
|
||||||
|
extern void * shmem_align(size_t, size_t);
|
||||||
|
extern void shmem_free(void *);
|
||||||
|
}
|
||||||
|
#define PARANOID_SYMMETRIC_HEAP
|
||||||
|
#endif
|
||||||
|
|
||||||
template<typename _Tp>
|
template<typename _Tp>
|
||||||
class uvmAllocator {
|
class commAllocator {
|
||||||
public:
|
public:
|
||||||
typedef std::size_t size_type;
|
typedef std::size_t size_type;
|
||||||
typedef std::ptrdiff_t difference_type;
|
typedef std::ptrdiff_t difference_type;
|
||||||
@@ -90,133 +222,94 @@ public:
|
|||||||
typedef const _Tp& const_reference;
|
typedef const _Tp& const_reference;
|
||||||
typedef _Tp value_type;
|
typedef _Tp value_type;
|
||||||
|
|
||||||
template<typename _Tp1> struct rebind { typedef uvmAllocator<_Tp1> other; };
|
template<typename _Tp1> struct rebind { typedef commAllocator<_Tp1> other; };
|
||||||
uvmAllocator() throw() { }
|
commAllocator() throw() { }
|
||||||
uvmAllocator(const uvmAllocator&) throw() { }
|
commAllocator(const commAllocator&) throw() { }
|
||||||
template<typename _Tp1> uvmAllocator(const uvmAllocator<_Tp1>&) throw() { }
|
template<typename _Tp1> commAllocator(const commAllocator<_Tp1>&) throw() { }
|
||||||
~uvmAllocator() throw() { }
|
~commAllocator() throw() { }
|
||||||
pointer address(reference __x) const { return &__x; }
|
pointer address(reference __x) const { return &__x; }
|
||||||
size_type max_size() const throw() { return size_t(-1) / sizeof(_Tp); }
|
size_type max_size() const throw() { return size_t(-1) / sizeof(_Tp); }
|
||||||
|
|
||||||
|
#ifdef GRID_COMMS_SHMEM
|
||||||
pointer allocate(size_type __n, const void* _p= 0)
|
pointer allocate(size_type __n, const void* _p= 0)
|
||||||
{
|
{
|
||||||
size_type bytes = __n*sizeof(_Tp);
|
size_type bytes = __n*sizeof(_Tp);
|
||||||
|
|
||||||
profilerAllocate(bytes);
|
profilerAllocate(bytes);
|
||||||
_Tp *ptr = (_Tp*) MemoryManager::SharedAllocate(bytes);
|
#ifdef CRAY
|
||||||
if ( (_Tp*)ptr == (_Tp *) NULL ) {
|
_Tp *ptr = (_Tp *) shmem_align(bytes,64);
|
||||||
printf("Grid Shared Allocator got NULL for %lu bytes\n",(unsigned long) bytes );
|
#else
|
||||||
|
_Tp *ptr = (_Tp *) shmem_align(64,bytes);
|
||||||
|
#endif
|
||||||
|
#ifdef PARANOID_SYMMETRIC_HEAP
|
||||||
|
static void * bcast;
|
||||||
|
static long psync[_SHMEM_REDUCE_SYNC_SIZE];
|
||||||
|
|
||||||
|
bcast = (void *) ptr;
|
||||||
|
shmem_broadcast32((void *)&bcast,(void *)&bcast,sizeof(void *)/4,0,0,0,shmem_n_pes(),psync);
|
||||||
|
|
||||||
|
if ( bcast != ptr ) {
|
||||||
|
std::printf("inconsistent alloc pe %d %lx %lx \n",shmem_my_pe(),bcast,ptr);std::fflush(stdout);
|
||||||
|
// BACKTRACEFILE();
|
||||||
|
exit(0);
|
||||||
}
|
}
|
||||||
GRID_ASSERT( ( (_Tp*)ptr != (_Tp *)NULL ) );
|
assert( bcast == (void *) ptr);
|
||||||
|
#endif
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
void deallocate(pointer __p, size_type __n) {
|
||||||
|
size_type bytes = __n*sizeof(_Tp);
|
||||||
|
|
||||||
void deallocate(pointer __p, size_type __n)
|
|
||||||
{
|
|
||||||
size_type bytes = __n * sizeof(_Tp);
|
|
||||||
profilerFree(bytes);
|
profilerFree(bytes);
|
||||||
MemoryManager::SharedFree((void *)__p,bytes);
|
shmem_free((void *)__p);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
void construct(pointer __p, const _Tp& __val) { new((void *)__p) _Tp(__val); };
|
|
||||||
void construct(pointer __p) { };
|
|
||||||
void destroy(pointer __p) { };
|
|
||||||
};
|
|
||||||
template<typename _Tp> inline bool operator==(const uvmAllocator<_Tp>&, const uvmAllocator<_Tp>&){ return true; }
|
|
||||||
template<typename _Tp> inline bool operator!=(const uvmAllocator<_Tp>&, const uvmAllocator<_Tp>&){ return false; }
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Device memory
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
template<typename _Tp>
|
|
||||||
class devAllocator {
|
|
||||||
public:
|
|
||||||
typedef std::size_t size_type;
|
|
||||||
typedef std::ptrdiff_t difference_type;
|
|
||||||
typedef _Tp* pointer;
|
|
||||||
typedef const _Tp* const_pointer;
|
|
||||||
typedef _Tp& reference;
|
|
||||||
typedef const _Tp& const_reference;
|
|
||||||
typedef _Tp value_type;
|
|
||||||
|
|
||||||
template<typename _Tp1> struct rebind { typedef devAllocator<_Tp1> other; };
|
|
||||||
devAllocator() throw() { }
|
|
||||||
devAllocator(const devAllocator&) throw() { }
|
|
||||||
template<typename _Tp1> devAllocator(const devAllocator<_Tp1>&) throw() { }
|
|
||||||
~devAllocator() throw() { }
|
|
||||||
pointer address(reference __x) const { return &__x; }
|
|
||||||
size_type max_size() const throw() { return size_t(-1) / sizeof(_Tp); }
|
|
||||||
|
|
||||||
pointer allocate(size_type __n, const void* _p= 0)
|
pointer allocate(size_type __n, const void* _p= 0)
|
||||||
{
|
{
|
||||||
size_type bytes = __n*sizeof(_Tp);
|
size_type bytes = __n*sizeof(_Tp);
|
||||||
|
|
||||||
profilerAllocate(bytes);
|
profilerAllocate(bytes);
|
||||||
_Tp *ptr = (_Tp*) MemoryManager::AcceleratorAllocate(bytes);
|
#ifdef HAVE_MM_MALLOC_H
|
||||||
if ( (_Tp*)ptr == (_Tp *) NULL ) {
|
_Tp * ptr = (_Tp *) _mm_malloc(bytes, GRID_ALLOC_ALIGN);
|
||||||
printf("Grid Device Allocator got NULL for %lu bytes\n",(unsigned long) bytes );
|
#else
|
||||||
|
_Tp * ptr = (_Tp *) memalign(GRID_ALLOC_ALIGN, bytes);
|
||||||
|
#endif
|
||||||
|
uint8_t *cp = (uint8_t *)ptr;
|
||||||
|
if ( ptr ) {
|
||||||
|
// One touch per 4k page, static OMP loop to catch same loop order
|
||||||
|
#ifdef GRID_OMP
|
||||||
|
#pragma omp parallel for schedule(static)
|
||||||
|
#endif
|
||||||
|
for(size_type n=0;n<bytes;n+=4096){
|
||||||
|
cp[n]=0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
GRID_ASSERT( ( (_Tp*)ptr != (_Tp *)NULL ) );
|
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
void deallocate(pointer __p, size_type __n) {
|
||||||
|
size_type bytes = __n*sizeof(_Tp);
|
||||||
|
|
||||||
void deallocate(pointer __p, size_type __n)
|
|
||||||
{
|
|
||||||
size_type bytes = __n * sizeof(_Tp);
|
|
||||||
profilerFree(bytes);
|
profilerFree(bytes);
|
||||||
MemoryManager::AcceleratorFree((void *)__p,bytes);
|
#ifdef HAVE_MM_MALLOC_H
|
||||||
|
_mm_free((void *)__p);
|
||||||
|
#else
|
||||||
|
free((void *)__p);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
void construct(pointer __p, const _Tp& __val) { };
|
void construct(pointer __p, const _Tp& __val) { };
|
||||||
void construct(pointer __p) { };
|
void construct(pointer __p) { };
|
||||||
void destroy(pointer __p) { };
|
void destroy(pointer __p) { };
|
||||||
};
|
};
|
||||||
template<typename _Tp> inline bool operator==(const devAllocator<_Tp>&, const devAllocator<_Tp>&){ return true; }
|
template<typename _Tp> inline bool operator==(const commAllocator<_Tp>&, const commAllocator<_Tp>&){ return true; }
|
||||||
template<typename _Tp> inline bool operator!=(const devAllocator<_Tp>&, const devAllocator<_Tp>&){ return false; }
|
template<typename _Tp> inline bool operator!=(const commAllocator<_Tp>&, const commAllocator<_Tp>&){ return false; }
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Template typedefs
|
// Template typedefs
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
template<class T> using hostVector = std::vector<T,alignedAllocator<T> >; // Needs autoview
|
template<class T> using Vector = std::vector<T,alignedAllocator<T> >;
|
||||||
template<class T> using Vector = std::vector<T,uvmAllocator<T> >; // Really want to deprecate
|
template<class T> using commVector = std::vector<T,commAllocator<T> >;
|
||||||
template<class T> using uvmVector = std::vector<T,uvmAllocator<T> >; // auto migrating page
|
template<class T> using Matrix = std::vector<std::vector<T,alignedAllocator<T> > >;
|
||||||
template<class T> using deviceVector = std::vector<T,devAllocator<T> >; // device vector
|
|
||||||
|
|
||||||
/*
|
|
||||||
template<class T> class vecView
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
T * data;
|
|
||||||
uint64_t size;
|
|
||||||
ViewMode mode;
|
|
||||||
void * cpu_ptr;
|
|
||||||
public:
|
|
||||||
// Rvalue accessor
|
|
||||||
accelerator_inline T & operator[](size_t i) const { return this->data[i]; };
|
|
||||||
vecView(Vector<T> &refer_to_me,ViewMode _mode)
|
|
||||||
{
|
|
||||||
cpu_ptr = &refer_to_me[0];
|
|
||||||
size = refer_to_me.size();
|
|
||||||
mode = _mode;
|
|
||||||
data =(T *) MemoryManager::ViewOpen(cpu_ptr,
|
|
||||||
size*sizeof(T),
|
|
||||||
mode,
|
|
||||||
AdviseDefault);
|
|
||||||
}
|
|
||||||
void ViewClose(void)
|
|
||||||
{ // Inform the manager
|
|
||||||
MemoryManager::ViewClose(this->cpu_ptr,this->mode);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class T> vecView<T> VectorView(Vector<T> &vec,ViewMode _mode)
|
|
||||||
{
|
|
||||||
vecView<T> ret(vec,_mode); // does the open
|
|
||||||
return ret; // must be closed
|
|
||||||
}
|
|
||||||
|
|
||||||
#define autoVecView(v_v,v,mode) \
|
|
||||||
auto v_v = VectorView(v,mode); \
|
|
||||||
ViewCloser<decltype(v_v)> _autoView##v_v(v_v);
|
|
||||||
*/
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
|
|
||||||
|
}; // namespace Grid
|
||||||
|
#endif
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <Grid/allocator/MemoryStats.h>
|
|
||||||
#include <Grid/allocator/MemoryManager.h>
|
|
||||||
#include <Grid/allocator/AlignedAllocator.h>
|
|
||||||
@@ -1,360 +0,0 @@
|
|||||||
#include <Grid/GridCore.h>
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
/*Allocation types, saying which pointer cache should be used*/
|
|
||||||
#define Cpu (0)
|
|
||||||
#define CpuHuge (1)
|
|
||||||
#define CpuSmall (2)
|
|
||||||
#define Acc (3)
|
|
||||||
#define AccHuge (4)
|
|
||||||
#define AccSmall (5)
|
|
||||||
#define Shared (6)
|
|
||||||
#define SharedHuge (7)
|
|
||||||
#define SharedSmall (8)
|
|
||||||
#undef GRID_MM_VERBOSE
|
|
||||||
uint64_t total_shared;
|
|
||||||
uint64_t total_device;
|
|
||||||
uint64_t total_host;;
|
|
||||||
|
|
||||||
#if defined(__has_feature)
|
|
||||||
#if __has_feature(leak_sanitizer)
|
|
||||||
#define ASAN_LEAK_CHECK
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ASAN_LEAK_CHECK
|
|
||||||
#include <sanitizer/asan_interface.h>
|
|
||||||
#include <sanitizer/common_interface_defs.h>
|
|
||||||
#include <sanitizer/lsan_interface.h>
|
|
||||||
#define LEAK_CHECK(A) { __lsan_do_recoverable_leak_check(); }
|
|
||||||
#else
|
|
||||||
#define LEAK_CHECK(A) { }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void MemoryManager::DisplayMallinfo(void)
|
|
||||||
{
|
|
||||||
#ifdef __linux__
|
|
||||||
struct mallinfo mi; // really want mallinfo2, but glibc version isn't uniform
|
|
||||||
|
|
||||||
mi = mallinfo();
|
|
||||||
|
|
||||||
std::cout << "MemoryManager: Total non-mmapped bytes (arena): "<< (size_t)mi.arena<<std::endl;
|
|
||||||
std::cout << "MemoryManager: # of free chunks (ordblks): "<< (size_t)mi.ordblks<<std::endl;
|
|
||||||
std::cout << "MemoryManager: # of free fastbin blocks (smblks): "<< (size_t)mi.smblks<<std::endl;
|
|
||||||
std::cout << "MemoryManager: # of mapped regions (hblks): "<< (size_t)mi.hblks<<std::endl;
|
|
||||||
std::cout << "MemoryManager: Bytes in mapped regions (hblkhd): "<< (size_t)mi.hblkhd<<std::endl;
|
|
||||||
std::cout << "MemoryManager: Max. total allocated space (usmblks): "<< (size_t)mi.usmblks<<std::endl;
|
|
||||||
std::cout << "MemoryManager: Free bytes held in fastbins (fsmblks): "<< (size_t)mi.fsmblks<<std::endl;
|
|
||||||
std::cout << "MemoryManager: Total allocated space (uordblks): "<< (size_t)mi.uordblks<<std::endl;
|
|
||||||
std::cout << "MemoryManager: Total free space (fordblks): "<< (size_t)mi.fordblks<<std::endl;
|
|
||||||
std::cout << "MemoryManager: Topmost releasable block (keepcost): "<< (size_t)mi.keepcost<<std::endl;
|
|
||||||
#endif
|
|
||||||
LEAK_CHECK();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemoryManager::PrintBytes(void)
|
|
||||||
{
|
|
||||||
std::cout << " MemoryManager : ------------------------------------ "<<std::endl;
|
|
||||||
std::cout << " MemoryManager : PrintBytes "<<std::endl;
|
|
||||||
std::cout << " MemoryManager : ------------------------------------ "<<std::endl;
|
|
||||||
std::cout << " MemoryManager : "<<(total_shared>>20)<<" shared Mbytes "<<std::endl;
|
|
||||||
std::cout << " MemoryManager : "<<(total_device>>20)<<" accelerator Mbytes "<<std::endl;
|
|
||||||
std::cout << " MemoryManager : "<<(total_host>>20) <<" cpu Mbytes "<<std::endl;
|
|
||||||
uint64_t cacheBytes;
|
|
||||||
cacheBytes = HostCacheBytes();
|
|
||||||
std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" cpu alloc cache Mbytes "<<std::endl;
|
|
||||||
cacheBytes = DeviceCacheBytes();
|
|
||||||
std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" acc alloc cache Mbytes "<<std::endl;
|
|
||||||
|
|
||||||
#ifdef GRID_CUDA
|
|
||||||
cuda_mem();
|
|
||||||
#endif
|
|
||||||
DisplayMallinfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t MemoryManager::DeviceCacheBytes() { return CacheBytes[Acc] + CacheBytes[AccHuge] + CacheBytes[AccSmall]; }
|
|
||||||
uint64_t MemoryManager::HostCacheBytes() { return CacheBytes[Cpu] + CacheBytes[CpuHuge] + CacheBytes[CpuSmall]; }
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// Data tables for recently freed pooiniter caches
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
MemoryManager::AllocationCacheEntry MemoryManager::Entries[MemoryManager::NallocType][MemoryManager::NallocCacheMax];
|
|
||||||
int MemoryManager::Victim[MemoryManager::NallocType];
|
|
||||||
int MemoryManager::Ncache[MemoryManager::NallocType] = { 2, 0, 8, 8, 0, 16, 8, 0, 16 };
|
|
||||||
uint64_t MemoryManager::CacheBytes[MemoryManager::NallocType];
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// Actual allocation and deallocation utils
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
void *MemoryManager::AcceleratorAllocate(size_t bytes)
|
|
||||||
{
|
|
||||||
total_device+=bytes;
|
|
||||||
void *ptr = (void *) Lookup(bytes,Acc);
|
|
||||||
if ( ptr == (void *) NULL ) {
|
|
||||||
ptr = (void *) acceleratorAllocDevice(bytes);
|
|
||||||
}
|
|
||||||
#ifdef GRID_MM_VERBOSE
|
|
||||||
std::cout <<"AcceleratorAllocate "<<std::endl;
|
|
||||||
PrintBytes();
|
|
||||||
#endif
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
void MemoryManager::AcceleratorFree (void *ptr,size_t bytes)
|
|
||||||
{
|
|
||||||
total_device-=bytes;
|
|
||||||
void *__freeme = Insert(ptr,bytes,Acc);
|
|
||||||
if ( __freeme ) {
|
|
||||||
acceleratorFreeDevice(__freeme);
|
|
||||||
}
|
|
||||||
#ifdef GRID_MM_VERBOSE
|
|
||||||
std::cout <<"AcceleratorFree "<<std::endl;
|
|
||||||
PrintBytes();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
void *MemoryManager::SharedAllocate(size_t bytes)
|
|
||||||
{
|
|
||||||
total_shared+=bytes;
|
|
||||||
void *ptr = (void *) Lookup(bytes,Shared);
|
|
||||||
if ( ptr == (void *) NULL ) {
|
|
||||||
ptr = (void *) acceleratorAllocShared(bytes);
|
|
||||||
}
|
|
||||||
#ifdef GRID_MM_VERBOSE
|
|
||||||
std::cout <<"SharedAllocate "<<std::endl;
|
|
||||||
PrintBytes();
|
|
||||||
#endif
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
void MemoryManager::SharedFree (void *ptr,size_t bytes)
|
|
||||||
{
|
|
||||||
total_shared-=bytes;
|
|
||||||
void *__freeme = Insert(ptr,bytes,Shared);
|
|
||||||
if ( __freeme ) {
|
|
||||||
acceleratorFreeShared(__freeme);
|
|
||||||
}
|
|
||||||
#ifdef GRID_MM_VERBOSE
|
|
||||||
std::cout <<"SharedFree "<<std::endl;
|
|
||||||
PrintBytes();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef GRID_UVM
|
|
||||||
void *MemoryManager::CpuAllocate(size_t bytes)
|
|
||||||
{
|
|
||||||
total_host+=bytes;
|
|
||||||
void *ptr = (void *) Lookup(bytes,Cpu);
|
|
||||||
if ( ptr == (void *) NULL ) {
|
|
||||||
ptr = (void *) acceleratorAllocShared(bytes);
|
|
||||||
}
|
|
||||||
#ifdef GRID_MM_VERBOSE
|
|
||||||
std::cout <<"CpuAllocate "<<std::endl;
|
|
||||||
PrintBytes();
|
|
||||||
#endif
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
void MemoryManager::CpuFree (void *_ptr,size_t bytes)
|
|
||||||
{
|
|
||||||
total_host-=bytes;
|
|
||||||
NotifyDeletion(_ptr);
|
|
||||||
void *__freeme = Insert(_ptr,bytes,Cpu);
|
|
||||||
if ( __freeme ) {
|
|
||||||
acceleratorFreeShared(__freeme);
|
|
||||||
}
|
|
||||||
#ifdef GRID_MM_VERBOSE
|
|
||||||
std::cout <<"CpuFree "<<std::endl;
|
|
||||||
PrintBytes();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void *MemoryManager::CpuAllocate(size_t bytes)
|
|
||||||
{
|
|
||||||
total_host+=bytes;
|
|
||||||
void *ptr = (void *) Lookup(bytes,Cpu);
|
|
||||||
if ( ptr == (void *) NULL ) {
|
|
||||||
ptr = (void *) acceleratorAllocCpu(bytes);
|
|
||||||
}
|
|
||||||
#ifdef GRID_MM_VERBOSE
|
|
||||||
std::cout <<"CpuAllocate "<<std::endl;
|
|
||||||
PrintBytes();
|
|
||||||
#endif
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
void MemoryManager::CpuFree (void *_ptr,size_t bytes)
|
|
||||||
{
|
|
||||||
total_host-=bytes;
|
|
||||||
NotifyDeletion(_ptr);
|
|
||||||
void *__freeme = Insert(_ptr,bytes,Cpu);
|
|
||||||
if ( __freeme ) {
|
|
||||||
acceleratorFreeCpu(__freeme);
|
|
||||||
}
|
|
||||||
#ifdef GRID_MM_VERBOSE
|
|
||||||
std::cout <<"CpuFree "<<std::endl;
|
|
||||||
PrintBytes();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//////////////////////////////////////////
|
|
||||||
// call only once
|
|
||||||
//////////////////////////////////////////
|
|
||||||
void MemoryManager::Init(void)
|
|
||||||
{
|
|
||||||
|
|
||||||
char * str;
|
|
||||||
int Nc;
|
|
||||||
|
|
||||||
str= getenv("GRID_ALLOC_NCACHE_LARGE");
|
|
||||||
if ( str ) {
|
|
||||||
Nc = atoi(str);
|
|
||||||
if ( (Nc>=0) && (Nc < NallocCacheMax)) {
|
|
||||||
Ncache[Cpu]=Nc;
|
|
||||||
Ncache[Acc]=Nc;
|
|
||||||
Ncache[Shared]=Nc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
str= getenv("GRID_ALLOC_NCACHE_HUGE");
|
|
||||||
if ( str ) {
|
|
||||||
Nc = atoi(str);
|
|
||||||
if ( (Nc>=0) && (Nc < NallocCacheMax)) {
|
|
||||||
Ncache[CpuHuge]=Nc;
|
|
||||||
Ncache[AccHuge]=Nc;
|
|
||||||
Ncache[SharedHuge]=Nc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
str= getenv("GRID_ALLOC_NCACHE_SMALL");
|
|
||||||
if ( str ) {
|
|
||||||
Nc = atoi(str);
|
|
||||||
if ( (Nc>=0) && (Nc < NallocCacheMax)) {
|
|
||||||
Ncache[CpuSmall]=Nc;
|
|
||||||
Ncache[AccSmall]=Nc;
|
|
||||||
Ncache[SharedSmall]=Nc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemoryManager::InitMessage(void) {
|
|
||||||
|
|
||||||
#ifndef GRID_UVM
|
|
||||||
std::cout << GridLogMessage << "MemoryManager Cache "<< MemoryManager::DeviceMaxBytes <<" bytes "<<std::endl;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::cout << GridLogMessage<< "MemoryManager::Init() setting up"<<std::endl;
|
|
||||||
#ifdef ALLOCATION_CACHE
|
|
||||||
std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent host allocations: SMALL "<<Ncache[CpuSmall]<<" LARGE "<<Ncache[Cpu]<<" HUGE "<<Ncache[CpuHuge]<<std::endl;
|
|
||||||
std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent device allocations: SMALL "<<Ncache[AccSmall]<<" LARGE "<<Ncache[Acc]<<" Huge "<<Ncache[AccHuge]<<std::endl;
|
|
||||||
std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent shared allocations: SMALL "<<Ncache[SharedSmall]<<" LARGE "<<Ncache[Shared]<<" Huge "<<Ncache[SharedHuge]<<std::endl;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef GRID_UVM
|
|
||||||
std::cout << GridLogMessage<< "MemoryManager::Init() Unified memory space"<<std::endl;
|
|
||||||
#ifdef GRID_CUDA
|
|
||||||
std::cout << GridLogMessage<< "MemoryManager::Init() Using cudaMallocManaged"<<std::endl;
|
|
||||||
#endif
|
|
||||||
#ifdef GRID_HIP
|
|
||||||
std::cout << GridLogMessage<< "MemoryManager::Init() Using hipMallocManaged"<<std::endl;
|
|
||||||
#endif
|
|
||||||
#ifdef GRID_SYCL
|
|
||||||
std::cout << GridLogMessage<< "MemoryManager::Init() Using SYCL malloc_shared"<<std::endl;
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
std::cout << GridLogMessage<< "MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory"<<std::endl;
|
|
||||||
#ifdef GRID_CUDA
|
|
||||||
std::cout << GridLogMessage<< "MemoryManager::Init() Using cudaMalloc"<<std::endl;
|
|
||||||
#endif
|
|
||||||
#ifdef GRID_HIP
|
|
||||||
std::cout << GridLogMessage<< "MemoryManager::Init() Using hipMalloc"<<std::endl;
|
|
||||||
#endif
|
|
||||||
#ifdef GRID_SYCL
|
|
||||||
std::cout << GridLogMessage<< "MemoryManager::Init() Using SYCL malloc_device"<<std::endl;
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void *MemoryManager::Insert(void *ptr,size_t bytes,int type)
|
|
||||||
{
|
|
||||||
#ifdef ALLOCATION_CACHE
|
|
||||||
int cache;
|
|
||||||
if (bytes < GRID_ALLOC_SMALL_LIMIT) cache = type + 2;
|
|
||||||
else if (bytes >= GRID_ALLOC_HUGE_LIMIT) cache = type + 1;
|
|
||||||
else cache = type;
|
|
||||||
|
|
||||||
return Insert(ptr,bytes,Entries[cache],Ncache[cache],Victim[cache],CacheBytes[cache]);
|
|
||||||
#else
|
|
||||||
return ptr;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void *MemoryManager::Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries,int ncache,int &victim, uint64_t &cacheBytes)
|
|
||||||
{
|
|
||||||
#ifdef GRID_OMP
|
|
||||||
GRID_ASSERT(omp_in_parallel()==0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ncache == 0) return ptr;
|
|
||||||
|
|
||||||
void * ret = NULL;
|
|
||||||
int v = -1;
|
|
||||||
|
|
||||||
for(int e=0;e<ncache;e++) {
|
|
||||||
if ( entries[e].valid==0 ) {
|
|
||||||
v=e;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( v==-1 ) {
|
|
||||||
v=victim;
|
|
||||||
victim = (victim+1)%ncache;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( entries[v].valid ) {
|
|
||||||
ret = entries[v].address;
|
|
||||||
cacheBytes -= entries[v].bytes;
|
|
||||||
entries[v].valid = 0;
|
|
||||||
entries[v].address = NULL;
|
|
||||||
entries[v].bytes = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
entries[v].address=ptr;
|
|
||||||
entries[v].bytes =bytes;
|
|
||||||
entries[v].valid =1;
|
|
||||||
cacheBytes += bytes;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *MemoryManager::Lookup(size_t bytes,int type)
|
|
||||||
{
|
|
||||||
#ifdef ALLOCATION_CACHE
|
|
||||||
int cache;
|
|
||||||
if (bytes < GRID_ALLOC_SMALL_LIMIT) cache = type + 2;
|
|
||||||
else if (bytes >= GRID_ALLOC_HUGE_LIMIT) cache = type + 1;
|
|
||||||
else cache = type;
|
|
||||||
|
|
||||||
return Lookup(bytes,Entries[cache],Ncache[cache],CacheBytes[cache]);
|
|
||||||
#else
|
|
||||||
return NULL;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void *MemoryManager::Lookup(size_t bytes,AllocationCacheEntry *entries,int ncache,uint64_t & cacheBytes)
|
|
||||||
{
|
|
||||||
#ifdef GRID_OMP
|
|
||||||
GRID_ASSERT(omp_in_parallel()==0);
|
|
||||||
#endif
|
|
||||||
for(int e=0;e<ncache;e++){
|
|
||||||
if ( entries[e].valid && ( entries[e].bytes == bytes ) ) {
|
|
||||||
entries[e].valid = 0;
|
|
||||||
cacheBytes -= entries[e].bytes;
|
|
||||||
return entries[e].address;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
@@ -1,228 +0,0 @@
|
|||||||
/*************************************************************************************
|
|
||||||
|
|
||||||
Grid physics library, www.github.com/paboyle/Grid
|
|
||||||
|
|
||||||
Source file: ./lib/MemoryManager.h
|
|
||||||
|
|
||||||
Copyright (C) 2015
|
|
||||||
|
|
||||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
|
||||||
Author: Peter Boyle <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 */
|
|
||||||
#pragma once
|
|
||||||
#include <list>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
// Move control to configure.ac and Config.h?
|
|
||||||
|
|
||||||
#define GRID_ALLOC_SMALL_LIMIT (4096)
|
|
||||||
#define GRID_ALLOC_HUGE_LIMIT (2147483648)
|
|
||||||
|
|
||||||
#define STRINGIFY(x) #x
|
|
||||||
#define TOSTRING(x) STRINGIFY(x)
|
|
||||||
#define FILE_LINE __FILE__ ":" TOSTRING(__LINE__)
|
|
||||||
#define AUDIT(a) MemoryManager::Audit(FILE_LINE)
|
|
||||||
|
|
||||||
/*Pinning pages is costly*/
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Advise the LatticeAccelerator class
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
enum ViewAdvise {
|
|
||||||
AdviseDefault = 0x0, // Regular data
|
|
||||||
AdviseInfrequentUse = 0x1 // Advise that the data is used infrequently. This can
|
|
||||||
// significantly influence performance of bulk storage.
|
|
||||||
|
|
||||||
// AdviseTransient = 0x2, // Data will mostly be read. On some architectures
|
|
||||||
// enables read-only copies of memory to be kept on
|
|
||||||
// host and device.
|
|
||||||
|
|
||||||
// AdviseAcceleratorWriteDiscard = 0x4 // Field will be written in entirety on device
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
// View Access Mode
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
enum ViewMode {
|
|
||||||
AcceleratorRead = 0x01,
|
|
||||||
AcceleratorWrite = 0x02,
|
|
||||||
AcceleratorWriteDiscard = 0x04,
|
|
||||||
CpuRead = 0x08,
|
|
||||||
CpuWrite = 0x10,
|
|
||||||
CpuWriteDiscard = 0x10 // same for now
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MemoryStatus {
|
|
||||||
uint64_t DeviceBytes;
|
|
||||||
uint64_t DeviceLRUBytes;
|
|
||||||
uint64_t DeviceMaxBytes;
|
|
||||||
uint64_t HostToDeviceBytes;
|
|
||||||
uint64_t DeviceToHostBytes;
|
|
||||||
uint64_t HostToDeviceXfer;
|
|
||||||
uint64_t DeviceToHostXfer;
|
|
||||||
uint64_t DeviceEvictions;
|
|
||||||
uint64_t DeviceDestroy;
|
|
||||||
uint64_t DeviceAllocCacheBytes;
|
|
||||||
uint64_t HostAllocCacheBytes;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class MemoryManager {
|
|
||||||
private:
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
// For caching recently freed allocations
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
typedef struct {
|
|
||||||
void *address;
|
|
||||||
size_t bytes;
|
|
||||||
int valid;
|
|
||||||
} AllocationCacheEntry;
|
|
||||||
|
|
||||||
static const int NallocCacheMax=128;
|
|
||||||
static const int NallocType=9;
|
|
||||||
static AllocationCacheEntry Entries[NallocType][NallocCacheMax];
|
|
||||||
static int Victim[NallocType];
|
|
||||||
static int Ncache[NallocType];
|
|
||||||
static uint64_t CacheBytes[NallocType];
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
// Free pool
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
static void *Insert(void *ptr,size_t bytes,int type) ;
|
|
||||||
static void *Lookup(size_t bytes,int type) ;
|
|
||||||
static void *Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries,int ncache,int &victim,uint64_t &cbytes) ;
|
|
||||||
static void *Lookup(size_t bytes,AllocationCacheEntry *entries,int ncache,uint64_t &cbytes) ;
|
|
||||||
|
|
||||||
public:
|
|
||||||
static void PrintBytes(void);
|
|
||||||
static void Audit(std::string s);
|
|
||||||
static void Init(void);
|
|
||||||
static void InitMessage(void);
|
|
||||||
static void *AcceleratorAllocate(size_t bytes);
|
|
||||||
static void AcceleratorFree (void *ptr,size_t bytes);
|
|
||||||
static void *SharedAllocate(size_t bytes);
|
|
||||||
static void SharedFree (void *ptr,size_t bytes);
|
|
||||||
static void *CpuAllocate(size_t bytes);
|
|
||||||
static void CpuFree (void *ptr,size_t bytes);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////
|
|
||||||
// Footprint tracking
|
|
||||||
////////////////////////////////////////////////////////
|
|
||||||
static uint64_t DeviceBytes;
|
|
||||||
static uint64_t DeviceLRUBytes;
|
|
||||||
static uint64_t DeviceMaxBytes;
|
|
||||||
static uint64_t HostToDeviceBytes;
|
|
||||||
static uint64_t DeviceToHostBytes;
|
|
||||||
static uint64_t HostToDeviceXfer;
|
|
||||||
static uint64_t DeviceToHostXfer;
|
|
||||||
static uint64_t DeviceEvictions;
|
|
||||||
static uint64_t DeviceDestroy;
|
|
||||||
|
|
||||||
static uint64_t DeviceCacheBytes();
|
|
||||||
static uint64_t HostCacheBytes();
|
|
||||||
|
|
||||||
static MemoryStatus GetFootprint(void) {
|
|
||||||
MemoryStatus stat;
|
|
||||||
stat.DeviceBytes = DeviceBytes;
|
|
||||||
stat.DeviceLRUBytes = DeviceLRUBytes;
|
|
||||||
stat.DeviceMaxBytes = DeviceMaxBytes;
|
|
||||||
stat.HostToDeviceBytes = HostToDeviceBytes;
|
|
||||||
stat.DeviceToHostBytes = DeviceToHostBytes;
|
|
||||||
stat.HostToDeviceXfer = HostToDeviceXfer;
|
|
||||||
stat.DeviceToHostXfer = DeviceToHostXfer;
|
|
||||||
stat.DeviceEvictions = DeviceEvictions;
|
|
||||||
stat.DeviceDestroy = DeviceDestroy;
|
|
||||||
stat.DeviceAllocCacheBytes = DeviceCacheBytes();
|
|
||||||
stat.HostAllocCacheBytes = HostCacheBytes();
|
|
||||||
return stat;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
#ifndef GRID_UVM
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// Data tables for ViewCache
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
typedef std::list<uint64_t> LRU_t;
|
|
||||||
typedef typename LRU_t::iterator LRUiterator;
|
|
||||||
typedef struct {
|
|
||||||
int LRU_valid;
|
|
||||||
LRUiterator LRU_entry;
|
|
||||||
uint64_t CpuPtr;
|
|
||||||
uint64_t AccPtr;
|
|
||||||
size_t bytes;
|
|
||||||
uint32_t transient;
|
|
||||||
uint32_t state;
|
|
||||||
uint32_t accLock;
|
|
||||||
uint32_t cpuLock;
|
|
||||||
} AcceleratorViewEntry;
|
|
||||||
|
|
||||||
typedef std::unordered_map<uint64_t,AcceleratorViewEntry> AccViewTable_t;
|
|
||||||
typedef typename AccViewTable_t::iterator AccViewTableIterator ;
|
|
||||||
|
|
||||||
static AccViewTable_t AccViewTable;
|
|
||||||
static LRU_t LRU;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
// Device motion
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
static void Create(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint);
|
|
||||||
static void EvictVictims(uint64_t bytes); // Frees up <bytes>
|
|
||||||
static void Evict(AcceleratorViewEntry &AccCache);
|
|
||||||
static void Flush(AcceleratorViewEntry &AccCache);
|
|
||||||
static void Clone(AcceleratorViewEntry &AccCache);
|
|
||||||
static void AccDiscard(AcceleratorViewEntry &AccCache);
|
|
||||||
static void CpuDiscard(AcceleratorViewEntry &AccCache);
|
|
||||||
|
|
||||||
// static void LRUupdate(AcceleratorViewEntry &AccCache);
|
|
||||||
static void LRUinsert(AcceleratorViewEntry &AccCache);
|
|
||||||
static void LRUremove(AcceleratorViewEntry &AccCache);
|
|
||||||
|
|
||||||
// manage entries in the table
|
|
||||||
static int EntryPresent(uint64_t CpuPtr);
|
|
||||||
static void EntryCreate(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint);
|
|
||||||
static void EntryErase (uint64_t CpuPtr);
|
|
||||||
static AccViewTableIterator EntryLookup(uint64_t CpuPtr);
|
|
||||||
static void EntrySet (uint64_t CpuPtr,AcceleratorViewEntry &entry);
|
|
||||||
|
|
||||||
static void AcceleratorViewClose(uint64_t AccPtr);
|
|
||||||
static uint64_t AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint);
|
|
||||||
static void CpuViewClose(uint64_t Ptr);
|
|
||||||
static uint64_t CpuViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
|
||||||
static void DisplayMallinfo(void);
|
|
||||||
static void NotifyDeletion(void * CpuPtr);
|
|
||||||
static void Print(void);
|
|
||||||
static void PrintAll(void);
|
|
||||||
static void EvictAll(void);
|
|
||||||
static void PrintState( void* CpuPtr);
|
|
||||||
static int isOpen (void* CpuPtr);
|
|
||||||
static void ViewClose(void* CpuPtr,ViewMode mode);
|
|
||||||
static void *ViewOpen (void* CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,653 +0,0 @@
|
|||||||
#include <Grid/GridCore.h>
|
|
||||||
#ifndef GRID_UVM
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
|
|
||||||
#define MAXLINE 512
|
|
||||||
static char print_buffer [ MAXLINE ];
|
|
||||||
|
|
||||||
#define mprintf(...) snprintf (print_buffer,MAXLINE, __VA_ARGS__ ); std::cout << GridLogMemory << print_buffer << std::endl;
|
|
||||||
#define dprintf(...) snprintf (print_buffer,MAXLINE, __VA_ARGS__ ); std::cout << GridLogDebug << print_buffer << std::endl;
|
|
||||||
//#define dprintf(...)
|
|
||||||
//#define mprintf(...)
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
// For caching copies of data on device
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
MemoryManager::AccViewTable_t MemoryManager::AccViewTable;
|
|
||||||
MemoryManager::LRU_t MemoryManager::LRU;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////
|
|
||||||
// Footprint tracking
|
|
||||||
////////////////////////////////////////////////////////
|
|
||||||
uint64_t MemoryManager::DeviceBytes;
|
|
||||||
uint64_t MemoryManager::DeviceLRUBytes;
|
|
||||||
uint64_t MemoryManager::DeviceMaxBytes = 1024*1024*128;
|
|
||||||
uint64_t MemoryManager::HostToDeviceBytes;
|
|
||||||
uint64_t MemoryManager::DeviceToHostBytes;
|
|
||||||
uint64_t MemoryManager::HostToDeviceXfer;
|
|
||||||
uint64_t MemoryManager::DeviceToHostXfer;
|
|
||||||
uint64_t MemoryManager::DeviceEvictions;
|
|
||||||
uint64_t MemoryManager::DeviceDestroy;
|
|
||||||
|
|
||||||
////////////////////////////////////
|
|
||||||
// Priority ordering for unlocked entries
|
|
||||||
// Empty
|
|
||||||
// CpuDirty
|
|
||||||
// Consistent
|
|
||||||
// AccDirty
|
|
||||||
////////////////////////////////////
|
|
||||||
#define Empty (0x0) /*Entry unoccupied */
|
|
||||||
#define CpuDirty (0x1) /*CPU copy is golden, Acc buffer MAY not be allocated*/
|
|
||||||
#define Consistent (0x2) /*ACC copy AND CPU copy are valid */
|
|
||||||
#define AccDirty (0x4) /*ACC copy is golden */
|
|
||||||
#define EvictNext (0x8) /*Priority for eviction*/
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
// Mechanics of data table maintenance
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
int MemoryManager::EntryPresent(uint64_t CpuPtr)
|
|
||||||
{
|
|
||||||
if(AccViewTable.empty()) return 0;
|
|
||||||
|
|
||||||
auto count = AccViewTable.count(CpuPtr); GRID_ASSERT((count==0)||(count==1));
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
void MemoryManager::EntryCreate(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(!EntryPresent(CpuPtr));
|
|
||||||
AcceleratorViewEntry AccCache;
|
|
||||||
AccCache.CpuPtr = CpuPtr;
|
|
||||||
AccCache.AccPtr = (uint64_t)NULL;
|
|
||||||
AccCache.bytes = bytes;
|
|
||||||
AccCache.state = CpuDirty;
|
|
||||||
AccCache.LRU_valid=0;
|
|
||||||
AccCache.transient=0;
|
|
||||||
AccCache.accLock=0;
|
|
||||||
AccCache.cpuLock=0;
|
|
||||||
AccViewTable[CpuPtr] = AccCache;
|
|
||||||
}
|
|
||||||
MemoryManager::AccViewTableIterator MemoryManager::EntryLookup(uint64_t CpuPtr)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(EntryPresent(CpuPtr));
|
|
||||||
auto AccCacheIterator = AccViewTable.find(CpuPtr);
|
|
||||||
GRID_ASSERT(AccCacheIterator!=AccViewTable.end());
|
|
||||||
return AccCacheIterator;
|
|
||||||
}
|
|
||||||
void MemoryManager::EntryErase(uint64_t CpuPtr)
|
|
||||||
{
|
|
||||||
auto AccCache = EntryLookup(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)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(AccCache.LRU_valid==0);
|
|
||||||
if (AccCache.transient) {
|
|
||||||
LRU.push_back(AccCache.CpuPtr);
|
|
||||||
AccCache.LRU_entry = --LRU.end();
|
|
||||||
} else {
|
|
||||||
LRU.push_front(AccCache.CpuPtr);
|
|
||||||
AccCache.LRU_entry = LRU.begin();
|
|
||||||
}
|
|
||||||
AccCache.LRU_valid = 1;
|
|
||||||
DeviceLRUBytes+=AccCache.bytes;
|
|
||||||
}
|
|
||||||
void MemoryManager::LRUremove(AcceleratorViewEntry &AccCache)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(AccCache.LRU_valid==1);
|
|
||||||
LRU.erase(AccCache.LRU_entry);
|
|
||||||
AccCache.LRU_valid = 0;
|
|
||||||
DeviceLRUBytes-=AccCache.bytes;
|
|
||||||
}
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
// Accelerator cache motion & consistency logic
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
void MemoryManager::AccDiscard(AcceleratorViewEntry &AccCache)
|
|
||||||
{
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
// Remove from Accelerator, remove entry, without flush
|
|
||||||
// Cannot be locked. If allocated Must be in LRU pool.
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
GRID_ASSERT(AccCache.state!=Empty);
|
|
||||||
|
|
||||||
dprintf("MemoryManager: Discard(%lx) %lx",(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr);
|
|
||||||
GRID_ASSERT(AccCache.accLock==0);
|
|
||||||
GRID_ASSERT(AccCache.cpuLock==0);
|
|
||||||
GRID_ASSERT(AccCache.CpuPtr!=(uint64_t)NULL);
|
|
||||||
if(AccCache.AccPtr) {
|
|
||||||
AcceleratorFree((void *)AccCache.AccPtr,AccCache.bytes);
|
|
||||||
DeviceDestroy++;
|
|
||||||
DeviceBytes -=AccCache.bytes;
|
|
||||||
LRUremove(AccCache);
|
|
||||||
AccCache.AccPtr=(uint64_t) NULL;
|
|
||||||
dprintf("MemoryManager: Free(%lx) LRU %ld Total %ld",(uint64_t)AccCache.AccPtr,DeviceLRUBytes,DeviceBytes);
|
|
||||||
}
|
|
||||||
uint64_t CpuPtr = AccCache.CpuPtr;
|
|
||||||
EntryErase(CpuPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemoryManager::Evict(AcceleratorViewEntry &AccCache)
|
|
||||||
{
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Make CPU consistent, remove from Accelerator, remove from LRU, LEAVE CPU only entry
|
|
||||||
// Cannot be locked. If allocated must be in LRU pool.
|
|
||||||
//
|
|
||||||
// (Historical: a Nov 2022 incident (two CpuPtrs; eviction called from
|
|
||||||
// CpuViewOpen -- since excised) could present a cpuLocked entry here, and
|
|
||||||
// silent-return guards were added. The LRU membership invariant (see
|
|
||||||
// LRUinsert) now excludes ALL locked entries from the queue eagerly at the
|
|
||||||
// lock edges, so a locked victim is an invariant violation: asserted.)
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
GRID_ASSERT(AccCache.state!=Empty);
|
|
||||||
|
|
||||||
mprintf("MemoryManager: Evict CpuPtr %lx AccPtr %lx cpuLock %ld accLock %ld",
|
|
||||||
(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr,
|
|
||||||
(uint64_t)AccCache.cpuLock,(uint64_t)AccCache.accLock);
|
|
||||||
GRID_ASSERT(AccCache.accLock==0);
|
|
||||||
GRID_ASSERT(AccCache.cpuLock==0);
|
|
||||||
if(AccCache.state==AccDirty) {
|
|
||||||
Flush(AccCache);
|
|
||||||
}
|
|
||||||
if(AccCache.AccPtr) {
|
|
||||||
AcceleratorFree((void *)AccCache.AccPtr,AccCache.bytes);
|
|
||||||
LRUremove(AccCache);
|
|
||||||
AccCache.AccPtr=(uint64_t)NULL;
|
|
||||||
AccCache.state=CpuDirty; // CPU primary now
|
|
||||||
DeviceBytes -=AccCache.bytes;
|
|
||||||
dprintf("MemoryManager: Free(AccPtr %lx) footprint now %ld ",(uint64_t)AccCache.AccPtr,DeviceBytes);
|
|
||||||
}
|
|
||||||
// uint64_t CpuPtr = AccCache.CpuPtr;
|
|
||||||
DeviceEvictions++;
|
|
||||||
// EntryErase(CpuPtr);
|
|
||||||
}
|
|
||||||
void MemoryManager::Flush(AcceleratorViewEntry &AccCache)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(AccCache.state==AccDirty);
|
|
||||||
GRID_ASSERT(AccCache.cpuLock==0);
|
|
||||||
GRID_ASSERT(AccCache.accLock==0);
|
|
||||||
GRID_ASSERT(AccCache.AccPtr!=(uint64_t)NULL);
|
|
||||||
GRID_ASSERT(AccCache.CpuPtr!=(uint64_t)NULL);
|
|
||||||
acceleratorCopyFromDevice((void *)AccCache.AccPtr,(void *)AccCache.CpuPtr,AccCache.bytes);
|
|
||||||
mprintf("MemoryManager: acceleratorCopyFromDevice Flush size %ld AccPtr %lx -> CpuPtr %lx",(uint64_t)AccCache.bytes,(uint64_t)AccCache.AccPtr,(uint64_t)AccCache.CpuPtr); fflush(stdout);
|
|
||||||
DeviceToHostBytes+=AccCache.bytes;
|
|
||||||
DeviceToHostXfer++;
|
|
||||||
AccCache.state=Consistent;
|
|
||||||
}
|
|
||||||
void MemoryManager::Clone(AcceleratorViewEntry &AccCache)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(AccCache.state==CpuDirty);
|
|
||||||
GRID_ASSERT(AccCache.cpuLock==0);
|
|
||||||
GRID_ASSERT(AccCache.accLock==0);
|
|
||||||
GRID_ASSERT(AccCache.CpuPtr!=(uint64_t)NULL);
|
|
||||||
if(AccCache.AccPtr==(uint64_t)NULL){
|
|
||||||
AccCache.AccPtr=(uint64_t)AcceleratorAllocate(AccCache.bytes);
|
|
||||||
DeviceBytes+=AccCache.bytes;
|
|
||||||
}
|
|
||||||
mprintf("MemoryManager: acceleratorCopyToDevice Clone size %ld AccPtr %lx <- CpuPtr %lx",
|
|
||||||
(uint64_t)AccCache.bytes,
|
|
||||||
(uint64_t)AccCache.AccPtr,(uint64_t)AccCache.CpuPtr); fflush(stdout);
|
|
||||||
acceleratorCopyToDevice((void *)AccCache.CpuPtr,(void *)AccCache.AccPtr,AccCache.bytes);
|
|
||||||
HostToDeviceBytes+=AccCache.bytes;
|
|
||||||
HostToDeviceXfer++;
|
|
||||||
AccCache.state=Consistent;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemoryManager::CpuDiscard(AcceleratorViewEntry &AccCache)
|
|
||||||
{
|
|
||||||
GRID_ASSERT(AccCache.state!=Empty);
|
|
||||||
GRID_ASSERT(AccCache.cpuLock==0);
|
|
||||||
GRID_ASSERT(AccCache.accLock==0);
|
|
||||||
GRID_ASSERT(AccCache.CpuPtr!=(uint64_t)NULL);
|
|
||||||
if(AccCache.AccPtr==(uint64_t)NULL){
|
|
||||||
AccCache.AccPtr=(uint64_t)AcceleratorAllocate(AccCache.bytes);
|
|
||||||
DeviceBytes+=AccCache.bytes;
|
|
||||||
}
|
|
||||||
AccCache.state=AccDirty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// View management
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void MemoryManager::ViewClose(void* Ptr,ViewMode mode)
|
|
||||||
{
|
|
||||||
if( (mode==AcceleratorRead)||(mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard) ){
|
|
||||||
dprintf("AcceleratorViewClose %lx",(uint64_t)Ptr);
|
|
||||||
AcceleratorViewClose((uint64_t)Ptr);
|
|
||||||
} else if( (mode==CpuRead)||(mode==CpuWrite)){
|
|
||||||
CpuViewClose((uint64_t)Ptr);
|
|
||||||
} else {
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void *MemoryManager::ViewOpen(void* _CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint)
|
|
||||||
{
|
|
||||||
uint64_t CpuPtr = (uint64_t)_CpuPtr;
|
|
||||||
if( (mode==AcceleratorRead)||(mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard) ){
|
|
||||||
dprintf("AcceleratorViewOpen %lx",(uint64_t)CpuPtr);
|
|
||||||
return (void *) AcceleratorViewOpen(CpuPtr,bytes,mode,hint);
|
|
||||||
} else if( (mode==CpuRead)||(mode==CpuWrite)){
|
|
||||||
return (void *)CpuViewOpen(CpuPtr,bytes,mode,hint);
|
|
||||||
} else {
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void MemoryManager::EvictVictims(uint64_t bytes)
|
|
||||||
{
|
|
||||||
if(bytes>=DeviceMaxBytes) {
|
|
||||||
printf("EvictVictims bytes %ld DeviceMaxBytes %ld\n",bytes,DeviceMaxBytes);
|
|
||||||
}
|
|
||||||
GRID_ASSERT(bytes<DeviceMaxBytes);
|
|
||||||
while(bytes+DeviceLRUBytes > DeviceMaxBytes){
|
|
||||||
if ( DeviceLRUBytes > 0){
|
|
||||||
GRID_ASSERT(LRU.size()>0);
|
|
||||||
uint64_t victim = LRU.back(); // From the LRU
|
|
||||||
auto AccCacheIterator = EntryLookup(victim);
|
|
||||||
auto & AccCache = AccCacheIterator->second;
|
|
||||||
Evict(AccCache);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Find if present, otherwise get or force an empty
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
if ( EntryPresent(CpuPtr)==0 ){
|
|
||||||
EntryCreate(CpuPtr,bytes,mode,hint);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto AccCacheIterator = EntryLookup(CpuPtr);
|
|
||||||
auto & AccCache = AccCacheIterator->second;
|
|
||||||
if (!AccCache.AccPtr) {
|
|
||||||
EvictVictims(bytes);
|
|
||||||
}
|
|
||||||
GRID_ASSERT((mode==AcceleratorRead)||(mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard));
|
|
||||||
|
|
||||||
GRID_ASSERT(AccCache.cpuLock==0); // Programming error
|
|
||||||
|
|
||||||
if(AccCache.state!=Empty) {
|
|
||||||
dprintf("ViewOpen found entry %lx %lx : sizes %ld %ld accLock %ld",
|
|
||||||
(uint64_t)AccCache.CpuPtr,
|
|
||||||
(uint64_t)CpuPtr,
|
|
||||||
(uint64_t)AccCache.bytes,
|
|
||||||
(uint64_t)bytes,
|
|
||||||
(uint64_t)AccCache.accLock);
|
|
||||||
GRID_ASSERT(AccCache.CpuPtr == CpuPtr);
|
|
||||||
GRID_ASSERT(AccCache.bytes ==bytes);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* State transitions and actions
|
|
||||||
*
|
|
||||||
* Action State StateNext Flush Clone
|
|
||||||
*
|
|
||||||
* AccRead Empty Consistent - Y
|
|
||||||
* AccWrite Empty AccDirty - Y
|
|
||||||
* AccRead CpuDirty Consistent - Y
|
|
||||||
* AccWrite CpuDirty AccDirty - Y
|
|
||||||
* AccRead Consistent Consistent - -
|
|
||||||
* AccWrite Consistent AccDirty - -
|
|
||||||
* AccRead AccDirty AccDirty - -
|
|
||||||
* AccWrite AccDirty AccDirty - -
|
|
||||||
*/
|
|
||||||
if(AccCache.state==Empty) {
|
|
||||||
GRID_ASSERT(AccCache.LRU_valid==0);
|
|
||||||
AccCache.CpuPtr = CpuPtr;
|
|
||||||
AccCache.AccPtr = (uint64_t)NULL;
|
|
||||||
AccCache.bytes = bytes;
|
|
||||||
AccCache.state = CpuDirty; // Cpu starts primary
|
|
||||||
if(mode==AcceleratorWriteDiscard){
|
|
||||||
CpuDiscard(AccCache);
|
|
||||||
AccCache.state = AccDirty; // Empty + AcceleratorWrite=> AccDirty
|
|
||||||
} else if(mode==AcceleratorWrite){
|
|
||||||
Clone(AccCache);
|
|
||||||
AccCache.state = AccDirty; // Empty + AcceleratorWrite=> AccDirty
|
|
||||||
} else {
|
|
||||||
Clone(AccCache);
|
|
||||||
AccCache.state = Consistent; // Empty + AccRead => Consistent
|
|
||||||
}
|
|
||||||
AccCache.accLock= 1;
|
|
||||||
dprintf("Copied Empty entry into device accLock= %d",AccCache.accLock);
|
|
||||||
} else if(AccCache.state==CpuDirty ){
|
|
||||||
if(mode==AcceleratorWriteDiscard) {
|
|
||||||
CpuDiscard(AccCache);
|
|
||||||
AccCache.state = AccDirty; // CpuDirty + AcceleratorWrite=> AccDirty
|
|
||||||
} else if(mode==AcceleratorWrite) {
|
|
||||||
Clone(AccCache);
|
|
||||||
AccCache.state = AccDirty; // CpuDirty + AcceleratorWrite=> AccDirty
|
|
||||||
} else {
|
|
||||||
Clone(AccCache);
|
|
||||||
AccCache.state = Consistent; // CpuDirty + AccRead => Consistent
|
|
||||||
}
|
|
||||||
AccCache.accLock++;
|
|
||||||
dprintf("CpuDirty entry into device ++accLock= %d",AccCache.accLock);
|
|
||||||
} else if(AccCache.state==Consistent) {
|
|
||||||
if((mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard))
|
|
||||||
AccCache.state = AccDirty; // Consistent + AcceleratorWrite=> AccDirty
|
|
||||||
else
|
|
||||||
AccCache.state = Consistent; // Consistent + AccRead => Consistent
|
|
||||||
AccCache.accLock++;
|
|
||||||
dprintf("Consistent entry into device ++accLock= %d",AccCache.accLock);
|
|
||||||
} else if(AccCache.state==AccDirty) {
|
|
||||||
if((mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard))
|
|
||||||
AccCache.state = AccDirty; // AccDirty + AcceleratorWrite=> AccDirty
|
|
||||||
else
|
|
||||||
AccCache.state = AccDirty; // AccDirty + AccRead => AccDirty
|
|
||||||
AccCache.accLock++;
|
|
||||||
dprintf("AccDirty entry ++accLock= %d",AccCache.accLock);
|
|
||||||
} else {
|
|
||||||
GRID_ASSERT(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
GRID_ASSERT(AccCache.accLock>0);
|
|
||||||
// If view is opened on device must remove from LRU
|
|
||||||
if(AccCache.LRU_valid==1){
|
|
||||||
// must possibly remove from LRU as now locked on GPU
|
|
||||||
dprintf("AccCache entry removed from LRU ");
|
|
||||||
LRUremove(AccCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
int transient =hint;
|
|
||||||
AccCache.transient= transient? EvictNext : 0;
|
|
||||||
|
|
||||||
return AccCache.AccPtr;
|
|
||||||
}
|
|
||||||
////////////////////////////////////
|
|
||||||
// look up & decrement lock count
|
|
||||||
////////////////////////////////////
|
|
||||||
void MemoryManager::AcceleratorViewClose(uint64_t CpuPtr)
|
|
||||||
{
|
|
||||||
auto AccCacheIterator = EntryLookup(CpuPtr);
|
|
||||||
auto & AccCache = AccCacheIterator->second;
|
|
||||||
|
|
||||||
GRID_ASSERT(AccCache.cpuLock==0);
|
|
||||||
GRID_ASSERT(AccCache.accLock>0);
|
|
||||||
|
|
||||||
AccCache.accLock--;
|
|
||||||
// Move to LRU queue if not locked and close on device
|
|
||||||
if(AccCache.accLock==0) {
|
|
||||||
dprintf("AccleratorViewClose %lx AccLock decremented to %ld move to LRU queue",(uint64_t)CpuPtr,(uint64_t)AccCache.accLock);
|
|
||||||
LRUinsert(AccCache);
|
|
||||||
} else {
|
|
||||||
dprintf("AccleratorViewClose %lx AccLock decremented to %ld",(uint64_t)CpuPtr,(uint64_t)AccCache.accLock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void MemoryManager::CpuViewClose(uint64_t CpuPtr)
|
|
||||||
{
|
|
||||||
auto AccCacheIterator = EntryLookup(CpuPtr);
|
|
||||||
auto & AccCache = AccCacheIterator->second;
|
|
||||||
|
|
||||||
GRID_ASSERT(AccCache.cpuLock>0);
|
|
||||||
GRID_ASSERT(AccCache.accLock==0);
|
|
||||||
|
|
||||||
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
|
|
||||||
*
|
|
||||||
* CpuRead Empty CpuDirty - -
|
|
||||||
* CpuWrite Empty CpuDirty - -
|
|
||||||
* CpuRead CpuDirty CpuDirty - -
|
|
||||||
* CpuWrite CpuDirty CpuDirty - -
|
|
||||||
* CpuRead Consistent Consistent - -
|
|
||||||
* CpuWrite Consistent CpuDirty - -
|
|
||||||
* CpuRead AccDirty Consistent Y -
|
|
||||||
* CpuWrite AccDirty CpuDirty Y -
|
|
||||||
*/
|
|
||||||
uint64_t MemoryManager::CpuViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise transient)
|
|
||||||
{
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Find if present, otherwise get or force an empty
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
if ( EntryPresent(CpuPtr)==0 ){
|
|
||||||
EntryCreate(CpuPtr,bytes,mode,transient);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto AccCacheIterator = EntryLookup(CpuPtr);
|
|
||||||
auto & AccCache = AccCacheIterator->second;
|
|
||||||
|
|
||||||
// CPU doesn't need to free space
|
|
||||||
// if (!AccCache.AccPtr) {
|
|
||||||
// EvictVictims(bytes);
|
|
||||||
// }
|
|
||||||
|
|
||||||
GRID_ASSERT((mode==CpuRead)||(mode==CpuWrite));
|
|
||||||
GRID_ASSERT(AccCache.accLock==0); // Programming error
|
|
||||||
|
|
||||||
if(AccCache.state!=Empty) {
|
|
||||||
GRID_ASSERT(AccCache.CpuPtr == CpuPtr);
|
|
||||||
GRID_ASSERT(AccCache.bytes==bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(AccCache.state==Empty) {
|
|
||||||
AccCache.CpuPtr = CpuPtr;
|
|
||||||
AccCache.AccPtr = (uint64_t)NULL;
|
|
||||||
AccCache.bytes = bytes;
|
|
||||||
AccCache.state = CpuDirty; // Empty + CpuRead/CpuWrite => CpuDirty
|
|
||||||
AccCache.accLock= 0;
|
|
||||||
AccCache.cpuLock= 1;
|
|
||||||
} else if(AccCache.state==CpuDirty ){
|
|
||||||
// AccPtr dont care, deferred allocate
|
|
||||||
AccCache.state = CpuDirty; // CpuDirty +CpuRead/CpuWrite => CpuDirty
|
|
||||||
AccCache.cpuLock++;
|
|
||||||
} else if(AccCache.state==Consistent) {
|
|
||||||
GRID_ASSERT(AccCache.AccPtr != (uint64_t)NULL);
|
|
||||||
if(mode==CpuWrite)
|
|
||||||
AccCache.state = CpuDirty; // Consistent +CpuWrite => CpuDirty
|
|
||||||
else
|
|
||||||
AccCache.state = Consistent; // Consistent +CpuRead => Consistent
|
|
||||||
AccCache.cpuLock++;
|
|
||||||
} else if(AccCache.state==AccDirty) {
|
|
||||||
GRID_ASSERT(AccCache.AccPtr != (uint64_t)NULL);
|
|
||||||
Flush(AccCache);
|
|
||||||
if(mode==CpuWrite) AccCache.state = CpuDirty; // AccDirty +CpuWrite => CpuDirty, Flush
|
|
||||||
else AccCache.state = Consistent; // AccDirty +CpuRead => Consistent, Flush
|
|
||||||
AccCache.cpuLock++;
|
|
||||||
} else {
|
|
||||||
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;
|
|
||||||
|
|
||||||
return AccCache.CpuPtr;
|
|
||||||
}
|
|
||||||
void MemoryManager::NotifyDeletion(void *_ptr)
|
|
||||||
{
|
|
||||||
// Look up in ViewCache
|
|
||||||
uint64_t ptr = (uint64_t)_ptr;
|
|
||||||
if(EntryPresent(ptr)) {
|
|
||||||
auto e = EntryLookup(ptr);
|
|
||||||
AccDiscard(e->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void MemoryManager::Print(void)
|
|
||||||
{
|
|
||||||
PrintBytes();
|
|
||||||
std::cout << GridLogMessage << "--------------------------------------------" << std::endl;
|
|
||||||
std::cout << GridLogMessage << "Memory Manager " << std::endl;
|
|
||||||
std::cout << GridLogMessage << "--------------------------------------------" << std::endl;
|
|
||||||
std::cout << GridLogMessage << DeviceBytes << " bytes allocated on device " << std::endl;
|
|
||||||
std::cout << GridLogMessage << DeviceLRUBytes<< " bytes evictable on device " << std::endl;
|
|
||||||
std::cout << GridLogMessage << DeviceMaxBytes<< " bytes max on device " << std::endl;
|
|
||||||
std::cout << GridLogMessage << HostToDeviceXfer << " transfers to device " << std::endl;
|
|
||||||
std::cout << GridLogMessage << DeviceToHostXfer << " transfers from device " << std::endl;
|
|
||||||
std::cout << GridLogMessage << HostToDeviceBytes<< " bytes transfered to device " << std::endl;
|
|
||||||
std::cout << GridLogMessage << DeviceToHostBytes<< " bytes transfered from device " << std::endl;
|
|
||||||
std::cout << GridLogMessage << DeviceEvictions << " Evictions from device " << std::endl;
|
|
||||||
std::cout << GridLogMessage << DeviceDestroy << " Destroyed vectors on device " << std::endl;
|
|
||||||
std::cout << GridLogMessage << AccViewTable.size()<< " vectors " << LRU.size()<<" evictable"<< std::endl;
|
|
||||||
acceleratorMem();
|
|
||||||
std::cout << GridLogMessage << "--------------------------------------------" << std::endl;
|
|
||||||
}
|
|
||||||
void MemoryManager::PrintAll(void)
|
|
||||||
{
|
|
||||||
Print();
|
|
||||||
std::cout << GridLogMessage << std::endl;
|
|
||||||
std::cout << GridLogMessage << "--------------------------------------------" << std::endl;
|
|
||||||
std::cout << GridLogMessage << "CpuAddr\t\tAccAddr\t\tState\t\tcpuLock\taccLock\tLRU_valid "<<std::endl;
|
|
||||||
std::cout << GridLogMessage << "--------------------------------------------" << std::endl;
|
|
||||||
for(auto it=AccViewTable.begin();it!=AccViewTable.end();it++){
|
|
||||||
auto &AccCache = it->second;
|
|
||||||
|
|
||||||
std::string str;
|
|
||||||
if ( AccCache.state==Empty ) str = std::string("Empty");
|
|
||||||
if ( AccCache.state==CpuDirty ) str = std::string("CpuDirty");
|
|
||||||
if ( AccCache.state==AccDirty ) str = std::string("AccDirty");
|
|
||||||
if ( AccCache.state==Consistent)str = std::string("Consistent");
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "0x"<<std::hex<<AccCache.CpuPtr<<std::dec
|
|
||||||
<< "\t0x"<<std::hex<<AccCache.AccPtr<<std::dec<<"\t" <<str
|
|
||||||
<< "\t" << AccCache.cpuLock
|
|
||||||
<< "\t" << AccCache.accLock
|
|
||||||
<< "\t" << AccCache.LRU_valid<<std::endl;
|
|
||||||
}
|
|
||||||
std::cout << GridLogMessage << "--------------------------------------------" << std::endl;
|
|
||||||
|
|
||||||
};
|
|
||||||
int MemoryManager::isOpen (void* _CpuPtr)
|
|
||||||
{
|
|
||||||
uint64_t CpuPtr = (uint64_t)_CpuPtr;
|
|
||||||
if ( EntryPresent(CpuPtr) ){
|
|
||||||
auto AccCacheIterator = EntryLookup(CpuPtr);
|
|
||||||
auto & AccCache = AccCacheIterator->second;
|
|
||||||
return AccCache.cpuLock+AccCache.accLock;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void MemoryManager::Audit(std::string s)
|
|
||||||
{
|
|
||||||
uint64_t CpuBytes=0;
|
|
||||||
uint64_t AccBytes=0;
|
|
||||||
uint64_t LruBytes1=0;
|
|
||||||
uint64_t LruBytes2=0;
|
|
||||||
uint64_t LruCnt=0;
|
|
||||||
|
|
||||||
std::cout << " Memory Manager::Audit() from "<<s<<std::endl;
|
|
||||||
for(auto it=LRU.begin();it!=LRU.end();it++){
|
|
||||||
uint64_t cpuPtr = *it;
|
|
||||||
GRID_ASSERT(EntryPresent(cpuPtr));
|
|
||||||
auto AccCacheIterator = EntryLookup(cpuPtr);
|
|
||||||
auto & AccCache = AccCacheIterator->second;
|
|
||||||
LruBytes2+=AccCache.bytes;
|
|
||||||
GRID_ASSERT(AccCache.LRU_valid==1);
|
|
||||||
GRID_ASSERT(AccCache.LRU_entry==it);
|
|
||||||
}
|
|
||||||
std::cout << " Memory Manager::Audit() LRU queue matches table entries "<<std::endl;
|
|
||||||
|
|
||||||
for(auto it=AccViewTable.begin();it!=AccViewTable.end();it++){
|
|
||||||
auto &AccCache = it->second;
|
|
||||||
|
|
||||||
std::string str;
|
|
||||||
if ( AccCache.state==Empty ) str = std::string("Empty");
|
|
||||||
if ( AccCache.state==CpuDirty ) str = std::string("CpuDirty");
|
|
||||||
if ( AccCache.state==AccDirty ) str = std::string("AccDirty");
|
|
||||||
if ( AccCache.state==Consistent)str = std::string("Consistent");
|
|
||||||
|
|
||||||
CpuBytes+=AccCache.bytes;
|
|
||||||
if( AccCache.AccPtr ) AccBytes+=AccCache.bytes;
|
|
||||||
if( AccCache.LRU_valid ) LruBytes1+=AccCache.bytes;
|
|
||||||
if( AccCache.LRU_valid ) LruCnt++;
|
|
||||||
|
|
||||||
if ( AccCache.cpuLock || AccCache.accLock ) {
|
|
||||||
GRID_ASSERT(AccCache.LRU_valid==0);
|
|
||||||
|
|
||||||
std::cout << GridLogError << s<< "\n\t 0x"<<std::hex<<AccCache.CpuPtr<<std::dec
|
|
||||||
<< "\t0x"<<std::hex<<AccCache.AccPtr<<std::dec<<"\t" <<str
|
|
||||||
<< "\t cpuLock " << AccCache.cpuLock
|
|
||||||
<< "\t accLock " << AccCache.accLock
|
|
||||||
<< "\t LRUvalid " << AccCache.LRU_valid<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
GRID_ASSERT( AccCache.cpuLock== 0 ) ;
|
|
||||||
GRID_ASSERT( AccCache.accLock== 0 ) ;
|
|
||||||
}
|
|
||||||
std::cout << " Memory Manager::Audit() no locked table entries "<<std::endl;
|
|
||||||
GRID_ASSERT(LruBytes1==LruBytes2);
|
|
||||||
GRID_ASSERT(LruBytes1==DeviceLRUBytes);
|
|
||||||
std::cout << " Memory Manager::Audit() evictable bytes matches sum over table "<<std::endl;
|
|
||||||
GRID_ASSERT(AccBytes==DeviceBytes);
|
|
||||||
std::cout << " Memory Manager::Audit() device bytes matches sum over table "<<std::endl;
|
|
||||||
GRID_ASSERT(LruCnt == LRU.size());
|
|
||||||
std::cout << " Memory Manager::Audit() LRU entry count matches "<<std::endl;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemoryManager::PrintState(void* _CpuPtr)
|
|
||||||
{
|
|
||||||
uint64_t CpuPtr = (uint64_t)_CpuPtr;
|
|
||||||
|
|
||||||
if ( EntryPresent(CpuPtr) ){
|
|
||||||
auto AccCacheIterator = EntryLookup(CpuPtr);
|
|
||||||
auto & AccCache = AccCacheIterator->second;
|
|
||||||
std::string str;
|
|
||||||
if ( AccCache.state==Empty ) str = std::string("Empty");
|
|
||||||
if ( AccCache.state==CpuDirty ) str = std::string("CpuDirty");
|
|
||||||
if ( AccCache.state==AccDirty ) str = std::string("AccDirty");
|
|
||||||
if ( AccCache.state==Consistent)str = std::string("Consistent");
|
|
||||||
if ( AccCache.state==EvictNext) str = std::string("EvictNext");
|
|
||||||
|
|
||||||
std::cout << GridLogMessage << "CpuAddr\t\tAccAddr\t\tState\t\tcpuLock\taccLock\tLRU_valid "<<std::endl;
|
|
||||||
std::cout << GridLogMessage << "\tx"<<std::hex<<AccCache.CpuPtr<<std::dec
|
|
||||||
<< "\tx"<<std::hex<<AccCache.AccPtr<<std::dec<<"\t" <<str
|
|
||||||
<< "\t" << AccCache.cpuLock
|
|
||||||
<< "\t" << AccCache.accLock
|
|
||||||
<< "\t" << AccCache.LRU_valid<<std::endl;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
std::cout << GridLogMessage << "No Entry in AccCache table." << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
#include <Grid/GridCore.h>
|
|
||||||
#ifdef GRID_UVM
|
|
||||||
|
|
||||||
NAMESPACE_BEGIN(Grid);
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// View management is 1:1 address space mapping
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
|
||||||
uint64_t MemoryManager::DeviceBytes;
|
|
||||||
uint64_t MemoryManager::DeviceLRUBytes;
|
|
||||||
uint64_t MemoryManager::DeviceMaxBytes = 1024*1024*128;
|
|
||||||
uint64_t MemoryManager::HostToDeviceBytes;
|
|
||||||
uint64_t MemoryManager::DeviceToHostBytes;
|
|
||||||
uint64_t MemoryManager::HostToDeviceXfer;
|
|
||||||
uint64_t MemoryManager::DeviceToHostXfer;
|
|
||||||
uint64_t MemoryManager::DeviceEvictions;
|
|
||||||
uint64_t MemoryManager::DeviceDestroy;
|
|
||||||
|
|
||||||
void MemoryManager::Audit(std::string s){};
|
|
||||||
void MemoryManager::ViewClose(void* AccPtr,ViewMode mode){};
|
|
||||||
void *MemoryManager::ViewOpen(void* CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint){ return CpuPtr; };
|
|
||||||
int MemoryManager::isOpen (void* CpuPtr) { return 0;}
|
|
||||||
void MemoryManager::PrintState(void* CpuPtr)
|
|
||||||
{
|
|
||||||
std::cout << GridLogMessage << "Host<->Device memory movement not currently managed by Grid." << std::endl;
|
|
||||||
};
|
|
||||||
void MemoryManager::Print(void){};
|
|
||||||
void MemoryManager::PrintAll(void){};
|
|
||||||
void MemoryManager::NotifyDeletion(void *ptr){};
|
|
||||||
|
|
||||||
NAMESPACE_END(Grid);
|
|
||||||
#endif
|
|
||||||
Loaded 100 of 1421 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user