├── .github └── workflows │ ├── binaries.yml │ ├── linux.yml │ ├── macos.yml │ └── windows.yml ├── .gitmodules ├── Binaries ├── linux_quest_link ├── macos_arm_quest_link ├── macos_x86_quest_link └── windows_quest_link.exe ├── Demos ├── README.md ├── demo_realtime_simulation.nb ├── demo_realtime_simulation.pdf ├── demo_variational_minimisation.nb └── demo_variational_minimisation.pdf ├── Doc ├── README.md ├── WINDOWS.md ├── banner.png ├── guide_creating_device_spec.nb ├── guide_creating_device_spec.pdf ├── release_summary_v011.nb ├── release_summary_v011.pdf ├── release_summary_v012.nb ├── release_summary_v012.pdf ├── release_summary_v06.nb ├── release_summary_v06.pdf ├── release_summary_v08.nb └── release_summary_v08.pdf ├── LICENCE.txt ├── Link ├── QuESTlink.m ├── circuits.cpp ├── circuits.hpp ├── decoders.cpp ├── decoders.hpp ├── derivatives.cpp ├── derivatives.hpp ├── errors.cpp ├── errors.hpp ├── extensions.cpp ├── extensions.cu ├── extensions.hpp ├── link.cpp ├── link.hpp ├── templates.tm ├── utilities.cpp └── utilities.hpp ├── README.md ├── Tests ├── v016_RecompileCircuit_cliffordAndRz.nb ├── v016_RecompileCircuit_cliffordAndRz.pdf ├── v016_RecompileCircuit_singleQubitAndCNOT.nb ├── v016_RecompileCircuit_singleQubitAndCNOT.pdf ├── v017_CalcCircuitGenerator.nb ├── v017_CalcCircuitGenerator.pdf ├── v017_GetCircuitCompacted.nb ├── v017_GetCircuitCompacted.pdf ├── v017_GetCircuitQubits.nb ├── v017_GetCircuitQubits.pdf ├── v017_GetPauliStringFromMatrix.nb ├── v017_GetPauliStringFromMatrix.pdf ├── v017_RetargetCircuit.nb ├── v017_RetargetCircuit.pdf ├── v018_ApplyPauliTransferMap.nb ├── v018_ApplyPauliTransferMap.pdf ├── v018_CalcPauliTransferEval.nb ├── v018_CalcPauliTransferEval.pdf ├── v018_CalcPauliTransferMap.nb ├── v018_CalcPauliTransferMap.pdf ├── v018_CalcPauliTransferMatrix.nb ├── v018_CalcPauliTransferMatrix.pdf ├── v018_DrawPauliTransferEval.nb ├── v018_DrawPauliTransferEval.pdf ├── v018_DrawPauliTransferMap.nb ├── v018_DrawPauliTransferMap.pdf ├── v018_GetCircuitParameterised.nb ├── v018_GetCircuitParameterised.pdf ├── v018_GetPauliString.nb ├── v018_GetPauliString.pdf ├── v018_GetPauliStringOverlap.nb ├── v018_GetPauliStringOverlap.pdf ├── v018_GetPauliStringReformatted.nb ├── v018_GetPauliStringReformatted.pdf ├── v018_GetPauliStringRetargeted.nb ├── v018_GetPauliStringRetargeted.pdf ├── v019_GetCircuitConjugated.nb ├── v019_GetCircuitConjugated.pdf ├── v019_GetCircuitSuperoperator.nb ├── v019_GetCircuitSuperoperator.pdf ├── v019_GetCircuitsFromChannel.nb ├── v019_GetCircuitsFromChannel.pdf ├── v019_GetRandomCircuitFromChannel.nb ├── v019_GetRandomCircuitFromChannel.pdf ├── v019_SampleExpecPauliString.nb ├── v019_SampleExpecPauliString.pdf ├── v019_SimplifyPaulis.nb └── v019_SimplifyPaulis.pdf ├── Tools └── linksnooper.nb ├── WSTP ├── Linux │ ├── linux_libWSTP64i4.a │ ├── linux_wscc │ ├── linux_wsprep │ └── wstp.h ├── MacOS-ARM │ ├── macos_libWSTPi4.a │ ├── macos_wscc │ ├── macos_wsprep │ └── wstp.h ├── MacOS │ ├── macos_libWSTPi4.36.a │ ├── macos_wscc │ ├── macos_wsprep │ └── wstp.h └── Windows │ ├── windows_wsprep.exe │ ├── windows_wstp32i4.lib │ ├── windows_wstp32i4m.lib │ ├── windows_wstp32i4s.lib │ ├── windows_wstp64i4.lib │ ├── windows_wstp64i4m.lib │ ├── windows_wstp64i4s.lib │ ├── wstp.h │ ├── wstp32i4.dll │ └── wstp64i4.dll └── makefile /.github/workflows/binaries.yml: -------------------------------------------------------------------------------- 1 | name: Binaries 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | jobs: 8 | 9 | cpu_linux_build: 10 | name: Linux 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | submodules: recursive 16 | - name: Install dependencies 17 | run: | 18 | sudo apt-get update 19 | sudo apt-get install -y \ 20 | uuid-dev \ 21 | build-essential 22 | - name: Build QuESTlink 23 | run: make OS=LINUX compiler=g++ COMPILER_TYPE=GNU EXE=linux_quest_link 24 | - name: Prepare to upload binary 25 | run: | 26 | mkdir -p Binaries/linux 27 | mv linux_quest_link Binaries/linux/. 28 | - uses: actions/upload-artifact@v1 29 | with: 30 | name: linux_quest_link 31 | path: Binaries/linux 32 | 33 | cpu_macos_x86_build: 34 | name: MacOS_x86 35 | runs-on: macos-latest 36 | steps: 37 | - uses: actions/checkout@v2 38 | with: 39 | submodules: recursive 40 | - name: Build QuESTlink 41 | run: make OS=MACOS compiler=clang COMPILER_TYPE=CLANG MACOS_ARCH=x64 EXE=macos_x86_quest_link 42 | - name: Prepare to upload binary 43 | run: | 44 | mkdir -p Binaries/macos_x86 45 | mv macos_x86_quest_link Binaries/macos_x86/. 46 | - uses: actions/upload-artifact@v1 47 | with: 48 | name: macos_x86_quest_link 49 | path: Binaries/macos_x86 50 | 51 | cpu_macos_arm_build: 52 | name: MacOS_ARM 53 | runs-on: macos-latest 54 | steps: 55 | - uses: actions/checkout@v2 56 | with: 57 | submodules: recursive 58 | - name: Build QuESTlink 59 | run: make OS=MACOS compiler=clang COMPILER_TYPE=CLANG MACOS_ARCH=ARM EXE=macos_arm_quest_link 60 | - name: Prepare to upload binary 61 | run: | 62 | mkdir -p Binaries/macos_arm 63 | mv macos_arm_quest_link Binaries/macos_arm/. 64 | - uses: actions/upload-artifact@v1 65 | with: 66 | name: macos_arm_quest_link 67 | path: Binaries/macos_arm 68 | 69 | cpu_windows_build: 70 | name: Windows 71 | runs-on: windows-latest 72 | steps: 73 | - uses: actions/checkout@v2 74 | with: 75 | submodules: recursive 76 | - uses: ilammy/msvc-dev-cmd@v1 77 | - name: Build QuESTlink 78 | shell: cmd 79 | run: make OS=WINDOWS COMPILER=cl COMPILER_TYPE=MSVC WINDOWS_ARCH=64 EXE=windows_quest_link 80 | - name: Prepare to upload binary 81 | shell: cmd 82 | run: | 83 | if not exist Binaries mkdir Binaries 84 | if not exist Binaries\windows mkdir Binaries\windows 85 | move windows_quest_link.exe Binaries\windows 86 | - uses: actions/upload-artifact@v1 87 | with: 88 | name: windows_quest_link 89 | path: Binaries/windows 90 | 91 | publish_binaries: 92 | name: Publish binaries 93 | needs: [cpu_linux_build, cpu_macos_x86_build, cpu_macos_arm_build, cpu_windows_build] 94 | runs-on: ubuntu-latest 95 | steps: 96 | - uses: actions/checkout@v2 97 | with: 98 | ref: main 99 | - name: Prepare binaries directory 100 | run: | 101 | rm -rf Binaries 102 | mkdir Binaries 103 | - uses: actions/download-artifact@v3 104 | with: 105 | name: linux_quest_link 106 | path: Binaries 107 | - uses: actions/download-artifact@v3 108 | with: 109 | name: macos_x86_quest_link 110 | path: Binaries 111 | - uses: actions/download-artifact@v3 112 | with: 113 | name: macos_arm_quest_link 114 | path: Binaries 115 | - uses: actions/download-artifact@v3 116 | with: 117 | name: windows_quest_link 118 | path: Binaries 119 | - name: Commit recompiled binaries 120 | run: | 121 | git config --local user.email "action@github.com" 122 | git config --local user.name "GitHub Action" 123 | git add Binaries 124 | git commit -m "Update precompiled binaries" 125 | - name: Push recompiled binaries 126 | uses: ad-m/github-push-action@v0.8.0 127 | with: 128 | github_token: ${{ secrets.GITHUB_TOKEN }} 129 | branch: main 130 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: Linux 2 | 3 | on: 4 | push: 5 | branches: [ develop ] 6 | pull_request: 7 | branches: [ develop ] 8 | 9 | jobs: 10 | cpu_linux_build: 11 | name: Linux 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | with: 16 | submodules: recursive 17 | - name: Install dependencies 18 | run: | 19 | sudo apt-get update 20 | sudo apt-get install -y \ 21 | uuid-dev \ 22 | build-essential 23 | - name: Build QuESTlink 24 | run: make OS=LINUX compiler=g++ COMPILER_TYPE=GNU -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: MacOS 2 | 3 | on: 4 | push: 5 | branches: [ develop ] 6 | pull_request: 7 | branches: [ develop ] 8 | 9 | jobs: 10 | cpu_macos_x86_build: 11 | name: MacOS_x86 12 | runs-on: macos-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | with: 16 | submodules: recursive 17 | - name: Build QuESTlink 18 | run: make OS=MACOS compiler=clang COMPILER_TYPE=CLANG MACOS_ARCH=x64 19 | cpu_macos_arm_build: 20 | name: MacOS_ARM 21 | runs-on: macos-latest 22 | steps: 23 | - uses: actions/checkout@v2 24 | with: 25 | submodules: recursive 26 | - name: Build QuESTlink 27 | run: make OS=MACOS compiler=clang COMPILER_TYPE=CLANG MACOS_ARCH=ARM -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: Windows 2 | 3 | on: 4 | push: 5 | branches: [ develop ] 6 | pull_request: 7 | branches: [ develop ] 8 | 9 | jobs: 10 | cpu_windows_build: 11 | name: Windows 12 | runs-on: windows-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | with: 16 | submodules: recursive 17 | - uses: ilammy/msvc-dev-cmd@v1 18 | - name: Build QuESTlink 19 | shell: cmd 20 | run: make OS=WINDOWS COMPILER=cl COMPILER_TYPE=MSVC WINDOWS_ARCH=64 -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "QuEST"] 2 | path = QuEST 3 | url = https://github.com/QuEST-Kit/QuEST 4 | branch = develop 5 | -------------------------------------------------------------------------------- /Binaries/linux_quest_link: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Binaries/linux_quest_link -------------------------------------------------------------------------------- /Binaries/macos_arm_quest_link: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Binaries/macos_arm_quest_link -------------------------------------------------------------------------------- /Binaries/macos_x86_quest_link: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Binaries/macos_x86_quest_link -------------------------------------------------------------------------------- /Binaries/windows_quest_link.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Binaries/windows_quest_link.exe -------------------------------------------------------------------------------- /Demos/README.md: -------------------------------------------------------------------------------- 1 | Demos 2 | ==== 3 | 4 | Above are a handful of demos. View more at the [QuESTlink](https://questlink.qtechtheory.org) site, and in our [whitepaper](https://arxiv.org/abs/1912.07904). -------------------------------------------------------------------------------- /Demos/demo_realtime_simulation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Demos/demo_realtime_simulation.pdf -------------------------------------------------------------------------------- /Demos/demo_variational_minimisation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Demos/demo_variational_minimisation.pdf -------------------------------------------------------------------------------- /Doc/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Doc 3 | 4 | If you wish to run QuESTlink in multithreaded or GPU-accelerated modes, or as a server, you will need to download and manually compile it. Below are instructions for getting QuESTlink up and running, when it doesn't quite run out-of-the-box. 5 | 6 | * [Tools](#tools) 7 | * [Download](#download) 8 | * [Compile](#compile) 9 | * [Launch offline](#launch-offline) 10 | * [Launch as a server](#launch-as-a-server) 11 | 12 | ______________ 13 | 14 | 15 | ## Tools 16 | 17 | Don't have a compiler handy? We recommend the [comprehensive guide](https://quest.qtechtheory.org/download/) for the parent QuEST project to obtain the tools needed below. 18 | 19 | 20 | ## Download 21 | 22 | The best way to download the QuESTlink source code is through [`git`](https://git-scm.com/) at the command-line. 23 | 24 | ```bash 25 | git clone --recurse-submodules https://github.com/QTechTheory/QuESTlink.git 26 | cd QuESTlink 27 | ``` 28 | This will also download the [QuEST](https://github.com/QuEST-Kit/QuEST) submodule source-code. 29 | You are thereafter in the root directory of QuESTlink and ready to compile. 30 | 31 | > If you wish to compile a specific *Github branch* like `develop`, simply additionally run 32 | > ```bash 33 | > git checkout develop 34 | > ``` 35 | > You can also perform this within the `/QuEST` subdirectory to change the branch of the QuEST submodule and access in-development features. 36 | 37 | ______________________________ 38 | 39 | ## Compile 40 | 41 | Compiling is trivial with [GNUMake](https://www.gnu.org/software/make/) and the provided [makefile](../makefile), and a C++ compiler (which supports `C++11`). 42 | 43 | > See [here](WINDOWS.md) for a comprehensive guide to compiling QuESTlink on **Windows**, including how to obtain the necessary compilers. 44 | 45 | > Note **Linux** users should first run `sudo apt-get install uuid-dev` before compiling. If this fails (and/or compiling says `cannot find -luuid`), also try to install `uuid`, `uuid-devel` and `libuuid-devel`. On a SLURM cluster, this can often be resolved with `module load util-linux`. 46 | 47 | Within the root directory, edit the [makefile](../makefile) and set: 48 | 49 | - `OS` to your operating system (`LINUX`, `MACOS` or `WINDOWS`) 50 | 51 | - `COMPILER` to your C++11 compiler command. 52 | > If in doubt, leave this as `g++` 53 | - `COMPILER_TYPE` to the type of your compiler (`GNU`, `INTEL`, `CLANG` or `MSVC`). 54 | > If in doubt, run `g++ --version` in terminal for a clue. Otherwise, `COMPILER_TYPE` will likely match your `OS`, as: `LINUX` & `GNU`, `MACOS` & `CLANG`, `WINDOWS` & `MSVC`. 55 | - `MULTITHREADED = 1` to compile in multithreaded mode 56 | > After compiling, and before calling `CreateLocalQuESTEnv[]`, you should set (in terminal) 57 | > ```bash 58 | > export OMP_NUM_THREADS= 59 | > ``` 60 | > replacing `` with the number of CPU cores of your machine, minus a few (sparing them for the Mathematica kernel itself). 61 | > 62 | > **Note** `MULTITHREADED` mode requires an [OpenMP](https://scc.ustc.edu.cn/zlsc/sugon/intel/compiler_f/main_for/optaps/common/optaps_par_openmp_multiple_compilers.htm)-compatible compiler. MacOS users should thus avoid `clang`, and download/use a GNU compiler (e.g. [`gcc@8`](https://formulae.brew.sh/formula/gcc@8)). 63 | - `GPUACCELERATED = 1` to use an NVIDIA GPU. 64 | > This requires an [`nvcc`](https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html) compiler compatible with your chosen `COMPILER`. The `nvcc` compiler command can be changed by overwriting `CUDA_COMPILER` in the makefile. 65 | > An `nvcc` compiler can be obtained on Linux with `sudo apt install nvidia-cuda-toolkit` 66 | > 67 | > **Note** you must also set `GPU_COMPUTE_CAPABILITY` in the makefile to the CC corresponding to your GPU. You can look this up [here](https://developer.nvidia.com/cuda-gpus). 68 | 69 | 70 | With these settings set, QuESTlink is compiled from terminal, in the root directory [`QuESTlink/`](../), via 71 | ```bash 72 | make 73 | ``` 74 | 75 | Compiling will create an executable `quest_link`, and some other build files. These other unneeded files can be removed with 76 | ```bash 77 | make tidy 78 | ``` 79 | leaving only `quest_link`. 80 | 81 | Should you wish to remove all compiled files and start again, run 82 | 83 | ```bash 84 | make clean 85 | ``` 86 | 87 | From within Mathematica, the compiled `quest_link` environment is connected to via 88 | ```Mathematica 89 | Import[...] 90 | CreateLocalQuESTEnv["path/to/quest_link"]; 91 | ``` 92 | 93 | _______________________________ 94 | 95 | ## Launch offline 96 | 97 | QuESTlink can be launched without an internet connection, using a copy of this repository. In Mathematica, simply run 98 | 99 | ```Mathematica 100 | Import["path/to/QuESTlink/Link/QuESlink.m"]; 101 | CreateLocalQuESTEnv["path/to/quest_link"]; 102 | ``` 103 | 104 | where `quest_link` has been compiled as above, or previously obtained using 105 | ```Mathematica 106 | CreateDownloadedQuESTEnv[]; 107 | ``` 108 | 109 | ## Launch as a server 110 | 111 | To launch `quest_link` as a server, to access remotely from a local kernel, run 112 | ```bash 113 | ./quest_link -linkcreate -linkprotocol TCPIP -linkname @,@ 114 | ``` 115 | substituting `` and `` with two open and available ports, and 116 | `` with the server IP or domain name. 117 | 118 | Then in your local Mathematica kernel, connect to it via 119 | ```Mathematica 120 | CreateRemoteQuESTEnv[, , ]; 121 | ``` 122 | -------------------------------------------------------------------------------- /Doc/WINDOWS.md: -------------------------------------------------------------------------------- 1 | 2 | # Windows 3 | 4 | Below are instructions for compiling QuESTlink from source on Windows, using only the command line. These instructions will install the Microsoft Visual Studio Compiler (MSVC), the Chocolatey package manager, and a Windows-compatible GNUMake. 5 | 6 | - [1 - Build Tools for VS](#1---build-tools-for-vs) 7 | - [2 - Choco](#2---choco) 8 | - [3 - GNUMake](#3---gnumake) 9 | - [4 - Compile](#4---compile) 10 | - [5 - Run](#5---run) 11 | 12 | ### 1 - Build Tools for VS 13 | 14 | Download **Build Tools for Visual Studio** from [this page](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019), and install it. 15 | 16 | This will provide both the **Developer Powershell for VS** and the **Developer Command Prompt for VS**. 17 | 18 | Open **Developer Command Prompt** and check that commands 19 | 20 | ```bash 21 | cl 22 | ``` 23 | and 24 | ```bash 25 | link -help 26 | ``` 27 | are correctly recognised. 28 | 29 | ### 2 - Choco 30 | 31 | Open **Developer Powershell** as an administrator and enter 32 | ```bash 33 | Set-ExecutionPolicy AllSigned 34 | ``` 35 | then 36 | ```bash 37 | Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) 38 | ``` 39 | 40 | > If you are unable to paste this command via ctrl-v, click the icon top-left of the powershell window and select paste 41 | 42 | This downloads [Chocolatey](https://chocolatey.org/), a Windows package manager. 43 | 44 | ### 3 - GNUMake 45 | 46 | Still in **Developer Powershell**, enter 47 | ```bash 48 | choco install make 49 | ``` 50 | This creates the `make` command in the **Developer Command Prompt**. 51 | 52 | ### 4 - Compile 53 | 54 | We're now ready to compile. Open **Developer Command Prompt** and make sure 55 | ```bash 56 | make --version 57 | ``` 58 | is a recognised command; you may have to close and re-open the command prompt first. 59 | 60 | Next, open the [`makefile`](../makefile) in any editor, and set: 61 | - `OS = WINDOWS` 62 | - `COMPILER = cl` 63 | - `COMPILER_TYPE = MSVC` 64 | - `WINDOWS_ARCH = 64` if using 64-bit Windows, else `32` (for x86) 65 | 66 | > If using 64-bit Windows, you must use a *64-bit* **Developer Command Prompt** for the following instructions. E.g. `VS2019 x64 Native Tools Command Prompt`. You can find this prompt in the same directory as the developer command prompt. 67 | 68 | To compile for GPU mode, install the [CUDA toolkit](https://developer.nvidia.com/cuda-downloads) (to get the `nvcc` command), and additionally set 69 | - `GPUACCELERATED = 1` 70 | - `GPU_COMPUTE_CAPABILITY = ` to the value corresponding to your GPU (look up [here](https://developer.nvidia.com/cuda-gpus)) with no decimal-point (e.g. `7.1` becomes `71`). 71 | 72 | Then, in the **Developer Command Prompt**, navigate to the root QuESTlink directory (where [`makefile`](../makefile) is located) and run 73 | ```bash 74 | make 75 | ``` 76 | If successful, the `quest_link.exe` executable will be created, along with several leftover `.o` files which can be safely removed with 77 | ```bash 78 | make tidy 79 | ``` 80 | To recompile after changing a setting in the makefile, run 81 | ```bash 82 | make clean 83 | make 84 | ``` 85 | 86 | > Note that trying to run `quest_link.exe` directly at this stage will report a DLL error. 87 | 88 | ### 5 - Run 89 | 90 | #### 5.1 - Locally 91 | 92 | You can now open Mathematica and run 93 | 94 | ```Mathematica 95 | SetDirectory["path/to/QuESTlink/"] 96 | 97 | Import["Link/QuESTlink.m"] 98 | CreateLocalQuESTEnv["quest_link.exe"] 99 | ``` 100 | and use all facilities of QuEST. 101 | 102 | 103 | 104 | #### 5.2 - Remotely 105 | 106 | The created `quest_link.exe` can be used as a server, accessed by Mathematica on another machine. To do this, after compiling, you must copy one of the following DLLs (depending on whether `WINDOWS_ARCH` is 32 or 64 bit) to the same location as `quest_link.exe`. 107 | 108 | ```bash 109 | copy WSTP\Windows\wstp32i4.dll . 110 | copy WSTP\Windows\wstp64i4.dll . 111 | ``` 112 | Running `quest_link.exe` directly now will create a network prompt, which can be ignored/closed. 113 | 114 | To launch the server from the **Developer Command Prompt**, run 115 | ```bash 116 | quest_link.exe -linkcreate -linkprotocol TCPIP -linkname @,@ 117 | ``` 118 | substituting and with two open and available ports, and with the server IP or domain name. 119 | 120 | Then in your Mathematica kernel on another machine, connect to it via 121 | 122 | ```Mathematica 123 | CreateRemoteQuESTEnv[, , ]; 124 | ``` 125 | -------------------------------------------------------------------------------- /Doc/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Doc/banner.png -------------------------------------------------------------------------------- /Doc/guide_creating_device_spec.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Doc/guide_creating_device_spec.pdf -------------------------------------------------------------------------------- /Doc/release_summary_v011.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Doc/release_summary_v011.pdf -------------------------------------------------------------------------------- /Doc/release_summary_v012.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Doc/release_summary_v012.pdf -------------------------------------------------------------------------------- /Doc/release_summary_v06.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Doc/release_summary_v06.pdf -------------------------------------------------------------------------------- /Doc/release_summary_v08.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Doc/release_summary_v08.pdf -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 TysonRayJones 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Link/circuits.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CIRCUITS_H 3 | #define CIRCUITS_H 4 | 5 | #include 6 | 7 | 8 | /* 9 | * Codes for Mathematica gate symbols. 10 | * 11 | * Yes, I absolutely hate the duplication below. But alas, it seems impossible 12 | * to make all compilers happy with a better-practise method. For instance, 13 | * MSVC won't let me initialise with { [OPCODE_H] = "H", ...}. Have mercy. 14 | */ 15 | 16 | #define OPCODE_H 0 17 | #define OPCODE_X 1 18 | #define OPCODE_Y 2 19 | #define OPCODE_Z 3 20 | #define OPCODE_Rx 4 21 | #define OPCODE_Ry 5 22 | #define OPCODE_Rz 6 23 | #define OPCODE_R 7 24 | #define OPCODE_S 8 25 | #define OPCODE_T 9 26 | #define OPCODE_U 10 27 | #define OPCODE_Deph 11 28 | #define OPCODE_Depol 12 29 | #define OPCODE_Damp 13 30 | #define OPCODE_SWAP 14 31 | #define OPCODE_M 15 32 | #define OPCODE_P 16 33 | #define OPCODE_Kraus 17 34 | #define OPCODE_G 18 35 | #define OPCODE_Id 19 36 | #define OPCODE_Ph 20 37 | #define OPCODE_KrausNonTP 21 38 | #define OPCODE_Matr 22 39 | #define OPCODE_UNonNorm 23 40 | #define OPCODE_Fac 24 41 | 42 | #define NUM_OPCODES 25 43 | 44 | static const std::string opcodeStrings[] = { 45 | "H", // OPCODE_H 0 46 | "X", // OPCODE_X 1 47 | "Y", // OPCODE_Y 2 48 | "Z", // OPCODE_Z 3 49 | "Rx", // OPCODE_Rx 4 50 | "Ry", // OPCODE_Ry 5 51 | "Rz", // OPCODE_Rz 6 52 | "R", // OPCODE_R 7 53 | "S", // OPCODE_S 8 54 | "T", // OPCODE_T 9 55 | "U", // OPCODE_U 10 56 | "Deph", // OPCODE_Deph 11 57 | "Depol", // OPCODE_Depol 12 58 | "Damp", // OPCODE_Damp 13 59 | "SWAP", // OPCODE_SWAP 14 60 | "M", // OPCODE_M 15 61 | "P", // OPCODE_P 16 62 | "Kraus", // OPCODE_Kraus 17 63 | "G", // OPCODE_G 18 64 | "Id", // OPCODE_Id 19 65 | "Ph", // OPCODE_Ph 20 66 | "KrausNonTP", // OPCODE_KrausNonTP 21 67 | "Matr", // OPCODE_Matr 22 68 | "UNonNorm", // OPCODE_UNonNorm 23 69 | "Fac", // OPCODE_Fac 24 70 | }; 71 | 72 | static const std::string opcodeNames[] = { 73 | "Hadamard", // OPCODE_H 0 74 | "Pauli X", // OPCODE_X 1 75 | "Pauli Y", // OPCODE_Y 2 76 | "Pauli Z", // OPCODE_Z 3 77 | "x-rotation", // OPCODE_Rx 4 78 | "y-rotation", // OPCODE_Ry 5 79 | "z-rotation", // OPCODE_Rz 6 80 | "Pauli gadget", // OPCODE_R 7 81 | "S-gate", // OPCODE_S 8 82 | "T-gate", // OPCODE_T 9 83 | "general unitary", // OPCODE_U 10 84 | "dephasing", // OPCODE_Deph 11 85 | "depolarising", // OPCODE_Depol 12 86 | "amplitude damping", // OPCODE_Damp 13 87 | "SWAP-gate", // OPCODE_SWAP 14 88 | "measurement", // OPCODE_M 15 89 | "projection", // OPCODE_P 16 90 | "Kraus map", // OPCODE_Kraus 17 91 | "global phase", // OPCODE_G 18 92 | "identity", // OPCODE_Id 19 93 | "phase-shfit", // OPCODE_Ph 20 94 | "non-trace-preserving Kraus map", // OPCODE_KrausNonTP 21 95 | "general matrix", // OPCODE_Matr 22 96 | "unnormalised general unitary", // OPCODE_UNonNorm 23 97 | "factor", // OPCODE_Fac 24 98 | }; 99 | 100 | 101 | 102 | /* 103 | * Max number of target and control qubits which can be specified 104 | * for an individual gate 105 | */ 106 | #define MAX_NUM_TARGS_CTRLS 100 107 | 108 | 109 | 110 | int* local_prepareCtrlCache(int* ctrls, int numCtrls, int addTarg); 111 | 112 | pauliOpType* local_preparePauliCache(pauliOpType pauli, int numPaulis); 113 | 114 | pauliOpType* local_preparePauliCache(qreal* paulis, int numPaulis); 115 | 116 | 117 | 118 | /** A single quantum gate or decoherence operator. 119 | * A Gate instance does not need explicit deletion. 120 | */ 121 | class Gate { 122 | private: 123 | 124 | int opcode; 125 | 126 | /** All array attributes are actually pointers to a circuit-wide array 127 | * shared by all Gate instances, and hence should not be modified, and do not 128 | * need individual freeing. This is to avoid superfluous translation of 129 | * lists between QuESTlink's C++ and QuEST's C APIs. 130 | */ 131 | int* ctrls; int numCtrls; 132 | int* targs; int numTargs; 133 | qreal* params; int numParams; 134 | 135 | /* Validates the meta-gate conventions like number of targets and parameters. 136 | * It does not validate whether a qubit is in bounds of a given Qureg, 137 | * or whether the parameter values are normalised, or other run-time validations 138 | * performed by the QuEST backend. If a gate is determined invalid, a 139 | * QuESTException is thrown with 'thrower' set to the gate's Mathematica 140 | * syntax (as can best be inferred by gate.getSyntax()). 141 | * @throws if the gate is superficially invalid 142 | */ 143 | void validate(); 144 | 145 | /* Returns the gate's Mathematica symbol is the gate's opcode is recognised, 146 | * otherwise returning the opcode integer (with string "opcode : " prepended) 147 | */ 148 | std::string getSymb(); 149 | 150 | public: 151 | 152 | /** Initialise the gate attributes, after object creation 153 | * (since the latter will happen for many Gate instances in an array). 154 | */ 155 | void init(int opcode, 156 | int* ctrls, int numCtrls, 157 | int* targs, int numTargs, 158 | qreal* params, int numParams); 159 | 160 | /** Getters. 161 | * Warning: ctrls, targs and params are shared mutable arrays! 162 | */ 163 | int getOpcode() { return opcode; } 164 | int* getCtrlsAddr() { return ctrls; } 165 | int* getTargsAddr() { return targs; } 166 | qreal* getParamsAddr() { return params; } 167 | 168 | /** Generates a phrase which describes the gate, such as "many-controlled 169 | * multi-qubit general unitary". This function does not require nor invoke 170 | * validation, and hence can describe invalid and unsupported gates. 171 | */ 172 | std::string getName(); 173 | 174 | /** Generates a Mathematica-parsable string which can be concatenated with 175 | * generic strings, but will be rendered by the front-end as graphical entities 176 | * resembling the originally input gate syntax. This function does not need 177 | * nor perform validation, and can hence reproduce invalid input gates. 178 | * Invocation involves a C++ and MMA communication. 179 | */ 180 | std::string getSyntax(); 181 | 182 | /** Returns whether the gate is (at least, intended) unitary 183 | * (such as Rx, H, U, UNonNorm), or not (like M, P, Matr, Damp) 184 | */ 185 | bool isUnitary(); 186 | 187 | /** Returns whether the gate is pure, i.e. can be performed upon 188 | * statevectors (true), else whether it can only be performed upon 189 | * density matrices (false) 190 | */ 191 | bool isPure(); 192 | 193 | /** Returns whether the gate can be inverted, and ergo undone from a 194 | * state. 195 | */ 196 | bool isInvertible(); 197 | 198 | /** Returns whether the gate is trace-preserving, as is determined by the 199 | * type of the gate (and NOT its arguments). Ergo, U and UNonNorm are both 200 | * trace preserving, but Matr and Fac are not. Note that somewhat whimsically, 201 | * KrausNonTP is assumed trace-preserving, expecting to deviate from CPTP 202 | * only by a small numerical error. 203 | */ 204 | bool isTracePreserving(); 205 | 206 | /** Returns the number of outputs that this gate produces when performed 207 | * in a circuit. This is the number of elements added to the outputs array 208 | * in applyTo(qureg, outputs). 209 | */ 210 | int getNumOutputs(); 211 | 212 | /** Perform this gate upon the given qureg, capturing any outputs to the 213 | * front of the given outputs array (unless it is NULL). Although cast to 214 | * qreal, some outputs may be integers (like the results of measurement) 215 | * needing later recasting. 216 | * @throws if the gate details are invalid 217 | */ 218 | void applyTo(Qureg qureg, qreal* outputs=NULL); 219 | 220 | /** Apply the conjugate transpose of this gate upon the qureg. 221 | * @throws if the gate details are invalid 222 | * @throws if the gate has no known conjugate transpose 223 | */ 224 | void applyDaggerTo(Qureg qureg); 225 | 226 | /** Apply the conjugate transpose of this gate upon the qureg. 227 | * @throws if the gate details are invalid 228 | * @throws if the gate has no known inverse 229 | */ 230 | void applyInverseTo(Qureg qureg); 231 | 232 | /** Apply the derivative of this gate upon the qureg. In simple cases, 233 | * the derivative is with respect to the real scalar parameter of the 234 | * gate. Generally, it is with respect to a variable which itself 235 | * determines the gate parameter. derivParams should contain the necessary 236 | * additional scalars to determine the full derivative. 237 | * @throws if the gate details are invalid 238 | * @throws if derivParams are invalid 239 | */ 240 | void applyDerivTo(Qureg qureg, qreal* derivParams, int numDerivParams); 241 | 242 | /** Applies one of the statevector-operator decompositions of the gate to qureg, 243 | * and returns the probability of the chosen decomposition. 244 | * If decompInd = -1 (default), the decomposition is randomly chosen, 245 | * weighted by the operator's probability. If decompInd is determined, 246 | * the returned probability is unchanged. 247 | * This is used in Monte Carlo statevector estimation of a channel. 248 | * @throws if the gate details are invalid 249 | */ 250 | qreal applyDecompTo(Qureg qureg, int decompInd=-1); 251 | 252 | /** Returns the number of operators in the decomposition of the gate into 253 | * coherent (state-vector compatible) operations. This counts the 254 | * number of possible distinct operations effected by applyDecompTo() 255 | */ 256 | int getNumDecomps(); 257 | }; 258 | 259 | 260 | 261 | /** A sequence of Gate instances. 262 | * A Circuit must be later deleted or fall out of scope, in which case persistent 263 | * MMA arrays (pointed to by the Gate instances) are freed. 264 | * Some methods below are defined in circuits.cpp, others in decoders.cpp. 265 | */ 266 | class Circuit { 267 | private: 268 | 269 | Gate* gates; 270 | int numGates; 271 | 272 | /** Aggregate circuit info needed by freeMMA(); 273 | */ 274 | int totalNumCtrls; 275 | int totalNumTargs; 276 | int totalNumParams; 277 | 278 | /** Destroys the MMA arrays which supply ctrls, targs and params to 279 | * the gate instances. This should only be called by the destructor. 280 | * This method is defined in decoders.cpp. 281 | */ 282 | void freeMMA(); 283 | 284 | public: 285 | 286 | /** Load Gate instances from the WSTP link, populating the Circuit 287 | * attributes. Calling this before the WSTP messages are sent will cause 288 | * a crash. Unlike the other methods defined in circuits.cpp, this method 289 | * is defined in decoders.cpp. 290 | */ 291 | void loadFromMMA(); 292 | 293 | /** Returns gates[ind] (does not explicitly throw for out of bounds error). 294 | */ 295 | Gate getGate(int ind); 296 | 297 | /** Returns the number of Gate instances. 298 | */ 299 | int getNumGates(); 300 | 301 | /** Returns the number of gates in the circuit which would 302 | * produce a non-zero number of outputs when simulated by applyTo(). 303 | */ 304 | int getNumGatesWithOutputs(); 305 | 306 | /** Returns the total number of outputs aggregated between all gates 307 | * in the circuit. This is the number of elements written to array 308 | * outputs in applyTo() below. 309 | */ 310 | int getTotalNumOutputs(); 311 | 312 | /** Returns whether the circuit contains only unitary operations (like 313 | * U, UNonNorm, Rx, etc) as opposed to non-unitaries (like Matr, P, M, Damp) 314 | */ 315 | bool isUnitary(); 316 | 317 | /** Returns whether the circuit contains only pure gates and can hence be 318 | * performed upon statevectors (true), else whether it can only be performed 319 | * upon density matrices (false) 320 | */ 321 | bool isPure(); 322 | 323 | /** Returns whether the circuit is invertible, which consulting both the 324 | * type of gate (returning false for measurements and projections), but also 325 | * the parameters (whether Kraus superoperators are non-invertible). 326 | */ 327 | bool isInvertible(); 328 | 329 | /** Returns whether the circuit contains only trace-preserving gates, as 330 | * determined solely by the type of the gate (and NOT its arguments). 331 | * Ergo, U and UNonNorm are both trace preserving, but Matr and Fac are not. 332 | * Note that somewhat whimsically, KrausNonTP is assumed trace-preserving, 333 | * expecting to deviate from CPTP by only a small numerical error. 334 | */ 335 | bool isTracePreserving(); 336 | 337 | /** Modify qureg by sequentially applying every gate within the circuit, 338 | * with increasing index. Array outputs is modified to have its first n 339 | * elements modified to the gate outputs, where n = getTotalNumOutputs(), 340 | * unless outputs=NULL (in which case, outputs are discarded). 341 | * If showProgress = true, a front-end loading bar will display the progress 342 | * of the circuit simulation (via local_updateCircuitProgress()). 343 | */ 344 | void applyTo(Qureg qureg, qreal* outputs=NULL, bool showProgress=false); 345 | 346 | /** Apply only a contiguous subset of the circuit gates to qureg, starting 347 | * at index startGateInd (inclusive) and ending with endGateInd (exclusive). 348 | * No outputs are recorded and progress is not shown. 349 | */ 350 | void applySubTo(Qureg qureg, int startGateInd, int endGateInd); 351 | 352 | /** Apply the dagger of a contiguous subset of the circuit gates to qureg. 353 | * The subset starts at startGateInd (inclusvie), and ends at endGateInd 354 | * (exclusive). Then, each gate in the subset in REVERSE order has its 355 | * dagger applied. 356 | */ 357 | void applyDaggerSubTo(Qureg qureg, int startGateInd, int endGateInd); 358 | 359 | /** Apply the inverse of a contiguous subset of the circuit gates to qureg. 360 | * The subset starts at startGateInd (inclusvie), and ends at endGateInd 361 | * (exclusive). Then, each gate in the subset in REVERSE order has its 362 | * inverse applied. 363 | */ 364 | void applyInverseSubTo(Qureg qureg, int startGateInd, int endGateInd); 365 | 366 | /** Decomposes every decoherence channel in the circuit into state-vector 367 | * compatible operators, and applies the one identified by decompInd, 368 | * a mixed-radix index (base given by gate.getNumDecomps), onto the 369 | * given statevector. If decompInd=-1, then one of the decompositions is 370 | * randomly chosen (weighted by their probabilities), and the function returns 371 | * zero. In contrast, when decompInd is fixed so that the circuit decomposition 372 | * is pre-determined, the probability of the forced decomposition is returned. 373 | * A subsequent observable measurement would be a sample of a Monte Carlo 374 | * estimation of that observable under the input channel. 375 | */ 376 | qreal applyDecompTo(Qureg qureg, long decompInd=-1); 377 | 378 | /** Returns the total number of circuit decompositions. This describes the 379 | * number of unique circuits effected by applyDecompTo(), or equivalently 380 | * its maximum decompInd. 381 | */ 382 | long getNumDecomps(); 383 | 384 | /** Send the given list of outputs (which must have been produced from 385 | * this circuit instance via applyCircuit()) to Mathematica, formatting 386 | * them into sub-lists according to the circuit structure / outputting 387 | * gate types. 388 | */ 389 | void sendOutputsToMMA(qreal* outputs); 390 | 391 | /** Destructor will free the persistent Mathematica arrays accesssed by 392 | * the gate instances, and the gates array. 393 | */ 394 | ~Circuit(); 395 | }; 396 | 397 | 398 | 399 | #endif // CIRCUITS_H -------------------------------------------------------------------------------- /Link/decoders.cpp: -------------------------------------------------------------------------------- 1 | /* @file 2 | * Translates between Mathematica encodings of structures like matrices and circuits, 3 | * and their C++ and/or representations. 4 | * 5 | * @author Tyson Jones 6 | */ 7 | 8 | #include "QuEST.h" 9 | #include "QuEST_complex.h" 10 | #include "QuEST_internal.h" 11 | #include "wstp.h" 12 | 13 | #include "errors.hpp" 14 | #include "circuits.hpp" 15 | #include "derivatives.hpp" 16 | #include "link.hpp" 17 | 18 | #include 19 | #include 20 | 21 | 22 | 23 | /* 24 | * Expression parsing 25 | */ 26 | 27 | std::string local_getCommaSep(int* elems, int len) { 28 | std::string form = ""; 29 | for (int i=0; iloadFromMMA(); 241 | 242 | int* derivGateInds; // may contain repetitions (multi-var gates) 243 | int* derivVarInds; // may contain repetitions (repetition of params, product rule) 244 | qreal* derivParams; int totalNumDerivParams; // needed for later freeing 245 | int* numDerivParamsPerDerivGate; // used only for fault-checking 246 | 247 | WSGetInteger32List(stdlink, &derivGateInds, &numTerms); 248 | WSGetInteger32List(stdlink, &derivVarInds, &numTerms); 249 | WSGetQrealList(stdlink, &derivParams, &totalNumDerivParams); 250 | WSGetInteger32List(stdlink, &numDerivParamsPerDerivGate, &numTerms); 251 | 252 | /* 253 | derivQuregInds are numbers 0 to numQuregs which indicate which of the 254 | given quregs (in quregIds) correspond to the current deriv term. 255 | */ 256 | 257 | terms = new DerivTerm[numTerms]; 258 | int derivParamInd = 0; 259 | int maxVarInd = 0; 260 | 261 | for (int t=0; tgetGate(gateInd); 265 | int varInd = derivVarInds[t]; 266 | int numDerivParams = numDerivParamsPerDerivGate[t]; 267 | 268 | terms[t].init(gate, gateInd, varInd, &derivParams[derivParamInd], numDerivParams); 269 | derivParamInd += numDerivParams; 270 | 271 | if (varInd > maxVarInd) 272 | maxVarInd = varInd; 273 | } 274 | 275 | numVars = maxVarInd + 1; 276 | 277 | WSReleaseInteger32List(stdlink, derivGateInds, numTerms); 278 | WSReleaseInteger32List(stdlink, derivVarInds, numTerms); 279 | WSReleaseInteger32List(stdlink, numDerivParamsPerDerivGate, numTerms); 280 | } 281 | 282 | void DerivCircuit::freeMMA() { 283 | 284 | // first term holds (i.e. has array beginning pointer) all term's derivParams 285 | WSReleaseQrealList(stdlink, terms[0].getDerivParamsAddr(), totalNumDerivParams); 286 | } 287 | 288 | 289 | 290 | /* 291 | * Hamiltonian loading 292 | */ 293 | 294 | void local_loadEncodedPauliStringFromMMA(int* numPaulis, int* numTerms, qreal** termCoeffs, int** allPauliCodes, int** allPauliTargets, int** numPaulisPerTerm) { 295 | 296 | WSGetQrealList(stdlink, termCoeffs, numTerms); 297 | WSGetInteger32List(stdlink, allPauliCodes, numPaulis); 298 | WSGetInteger32List(stdlink, allPauliTargets, numPaulis); 299 | WSGetInteger32List(stdlink, numPaulisPerTerm, numTerms); 300 | } 301 | 302 | /* can throw exception if pauli targets are invalid indices. 303 | * in that event, arrPaulis will be freed internally, and set to NULL (if init'd by caller) 304 | */ 305 | pauliOpType* local_decodePauliString(int numQb, int numTerms, int* allPauliCodes, int* allPauliTargets, int* numPaulisPerTerm) { 306 | 307 | // convert {allPauliCodes}, {allPauliTargets}, {numPaulisPerTerm}, and 308 | // qureg.numQubitsRepresented into {pauli-code-for-every-qubit} 309 | int arrLen = numTerms * numQb; 310 | pauliOpType* arrPaulis = (pauliOpType*) malloc(arrLen * sizeof *arrPaulis); 311 | for (int i=0; i < arrLen; i++) 312 | arrPaulis[i] = PAULI_I; 313 | 314 | int allPaulisInd = 0; 315 | for (int t=0; t < numTerms; t++) { 316 | for (int j=0; j < numPaulisPerTerm[t]; j++) { 317 | int currTarget = allPauliTargets[allPaulisInd]; 318 | 319 | // clean-up and error on invalid target qubit 320 | if (currTarget >= numQb) { 321 | free(arrPaulis); 322 | throw QuESTException("", 323 | "Invalid target index (" + std::to_string(currTarget) + 324 | ") of Pauli operator in Pauli sum of " + std::to_string(numQb) + " qubits."); 325 | } 326 | 327 | int arrInd = t*numQb + currTarget; 328 | int code = allPauliCodes[allPaulisInd++]; 329 | pauliOpType op = (code == OPCODE_Id)? PAULI_I : (pauliOpType) code; 330 | arrPaulis[arrInd] = op; 331 | } 332 | } 333 | 334 | // must be freed by caller 335 | return arrPaulis; 336 | } 337 | 338 | void local_freePauliString(int numPaulis, int numTerms, qreal* termCoeffs, int* allPauliCodes, int* allPauliTargets, int* numPaulisPerTerm, pauliOpType* arrPaulis) { 339 | WSReleaseQrealList(stdlink, termCoeffs, numTerms); 340 | WSReleaseInteger32List(stdlink, allPauliCodes, numPaulis); 341 | WSReleaseInteger32List(stdlink, allPauliTargets, numPaulis); 342 | WSReleaseInteger32List(stdlink, numPaulisPerTerm, numTerms); 343 | 344 | // may be none if validation triggers clean-up before arrPaulis gets created 345 | if (arrPaulis != NULL) 346 | free(arrPaulis); 347 | } 348 | 349 | PauliHamil local_loadPauliHamilForQuregFromMMA(int quregId) { 350 | 351 | PauliHamil hamil; 352 | 353 | // load/flush all Hamiltonian terms from MMA 354 | int numPaulis; 355 | int *allPauliCodes, *allPauliTargets, *numPaulisPerTerm; 356 | local_loadEncodedPauliStringFromMMA( 357 | &numPaulis, &hamil.numSumTerms, &hamil.termCoeffs, &allPauliCodes, &allPauliTargets, &numPaulisPerTerm); 358 | 359 | // validate the qureg, so we can safely access its numQubits for tailoring Hamiltonian dimension 360 | local_throwExcepIfQuregNotCreated(quregId); // throws 361 | 362 | // encode the Hamiltonian terms for compatibility with the qureg 363 | int numQubits = quregs[quregId].numQubitsRepresented; 364 | hamil.pauliCodes = local_decodePauliString( 365 | numQubits, hamil.numSumTerms, allPauliCodes, allPauliTargets, numPaulisPerTerm); // throws 366 | hamil.numQubits = numQubits; 367 | 368 | // immediately free superfluous MMA arrays 369 | WSReleaseInteger32List(stdlink, allPauliCodes, numPaulis); 370 | WSReleaseInteger32List(stdlink, allPauliTargets, numPaulis); 371 | WSReleaseInteger32List(stdlink, numPaulisPerTerm, hamil.numSumTerms); 372 | 373 | return hamil; 374 | } 375 | 376 | void local_freePauliHamil(PauliHamil hamil) { 377 | 378 | WSReleaseQrealList(stdlink, hamil.termCoeffs, hamil.numSumTerms); 379 | free(hamil.pauliCodes); 380 | } 381 | -------------------------------------------------------------------------------- /Link/decoders.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DECODERS_H 2 | #define DECODERS_H 3 | 4 | #include "QuEST.h" 5 | #include "QuEST_complex.h" 6 | 7 | #include 8 | 9 | #include "utilities.hpp" 10 | 11 | 12 | 13 | std::string local_getCommaSep(int* elems, int len); 14 | std::string local_getCommaSep(qreal* elems, int len); 15 | std::string local_getCommaSep(qcomp* elems, int len); 16 | 17 | std::string local_qrealToStr(qreal r); 18 | std::string local_qcompToStr(qcomp s); 19 | std::string local_qvectorToStr(qvector v); 20 | std::string local_qmatrixToStr(qmatrix m); 21 | 22 | std::string local_getStandardFormFromMMA(std::string expr); 23 | 24 | void local_sendMatrixToMMA(qmatrix matrix); 25 | 26 | void local_loadEncodedPauliStringFromMMA( 27 | int* numPaulis, int* numTerms, qreal** termCoeffs, int** allPauliCodes, int** allPauliTargets, int** numPaulisPerTerm); 28 | 29 | pauliOpType* local_decodePauliString( 30 | int numQb, int numTerms, int* allPauliCodes, int* allPauliTargets, int* numPaulisPerTerm); 31 | 32 | void local_freePauliString( 33 | int numPaulis, int numTerms, qreal* termCoeffs, int* allPauliCodes, int* allPauliTargets, int* numPaulisPerTerm, pauliOpType* arrPaulis); 34 | 35 | PauliHamil local_loadPauliHamilForQuregFromMMA(int numQubits); 36 | 37 | void local_freePauliHamil(PauliHamil hamil); 38 | 39 | 40 | 41 | # endif // DECODERS_H -------------------------------------------------------------------------------- /Link/derivatives.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef DERIVATIVES_H 3 | #define DERIVATIVES_H 4 | 5 | #include "QuEST_complex.h" 6 | 7 | #include "utilities.hpp" 8 | 9 | 10 | 11 | /** A single term among the partial derivatives of a parameterised circuit, 12 | * after expansion via the chain rule. 13 | */ 14 | class DerivTerm { 15 | private: 16 | 17 | Gate gate; 18 | 19 | /** The index of this term's corresponding gate relative to the 20 | * circuit in which it is embedded. 21 | */ 22 | int gateInd; 23 | 24 | /** The index of this term's corresponding differential variable. This 25 | * maps one-to-one with a Qureg to be populated with a derivative state. 26 | */ 27 | int varInd; 28 | 29 | /** Additional parameter data needed to effect the derivative of this 30 | * term's corresponding gate. Note all DerivTerm instances secretly share 31 | * the same derivParams array with different subpointers, and ergo none 32 | * should attempt to modify it nor delete it. 33 | */ 34 | qreal* derivParams; 35 | 36 | /** The length of derivParams, used only for on-the-fly internal error 37 | * checking during DerivTerm::applyTo() 38 | */ 39 | int numDerivParams; 40 | 41 | public: 42 | 43 | /** Initialise the DerivTerm attributes, after object creation 44 | * (since the latter will happen for many DerivTerm instances in an array). 45 | */ 46 | void init(Gate gate, int gateInd, int varInd, qreal* derivParams, int numDerivParams); 47 | 48 | /** Getters. 49 | * Warning, derivParams is a mutable array shared between DerivTerm instances! 50 | */ 51 | int getGateInd() { return gateInd; }; 52 | int getVarInd() { return varInd; }; 53 | qreal* getDerivParamsAddr() { return derivParams; }; 54 | 55 | /** Apply this derivative term (as the analytic derivative of its corresponding 56 | * gate) to qureg. 57 | * @throws QuESTException without qureg modification if gate or derivParams 58 | * contains an invalid gate or deriv spec 59 | */ 60 | void applyTo(Qureg qureg); 61 | }; 62 | 63 | 64 | 65 | /** A complete specification of the derivatives of a parameterised circuit with 66 | * respect to a collection of real variables. This can include multi-variable 67 | * parameter gates, gates whose parameters are functions of the differential 68 | * parameters (that are already numerically evaluated), circuits with variables 69 | * repeated over multiple gates, gates with general element-wise parameters 70 | * (like U), and even differential-parameterised decoherence channels. 71 | */ 72 | class DerivCircuit { 73 | private: 74 | 75 | /** A complete specification of the original non-differentiated circuit 76 | */ 77 | Circuit *circuit; 78 | 79 | /** The number of represented differential variables. 80 | */ 81 | int numVars; 82 | 83 | /** A complete description of the differential terms of the circuit, 84 | * after expansion via the chain rule. The gateInd of each successive 85 | * term is increasing or the same, to permit optimisations. All instances 86 | * secretly share the same mutable derivParams array loaded from MMA. 87 | */ 88 | DerivTerm* terms; 89 | int numTerms; 90 | 91 | /** The total number of derivParams aggregated between all terms of the 92 | * differential circuit. This is needed to later free the MMA-loaded 93 | * derivParams array which is shared between DerivTerm instances. 94 | */ 95 | int totalNumDerivParams; 96 | 97 | /** Qureg type-specific implementations of public methods 98 | */ 99 | void calcDerivEnergiesStateVec(qreal* energies, PauliHamil hamil, Qureg initQureg, Qureg* workQuregs, int numWorkQuregs); 100 | void calcDerivEnergiesDensMatr(qreal* energyGrad, PauliHamil hamil, Qureg initQureg, Qureg* workQuregs, int numWorkQuregs); 101 | qmatrix calcMetricTensorStateVec(Qureg initQureg, Qureg* workQuregs, int numWorkQuregs); 102 | qmatrix calcMetricTensorDensMatr(Qureg initQureg, Qureg* workQuregs, int numWorkQuregs); 103 | 104 | /** Destroys the MMA arrays shared between DerivTerm instances (derivPArams), 105 | * invoked during the destructor. This method is defined in decoders.cpp. 106 | */ 107 | void freeMMA(); 108 | 109 | public: 110 | 111 | /** Load attributes from the WSTP link, including the non-differentiated 112 | * circuit, and each DerivTerm info. Calling this before the WSTP messages 113 | * are sent will cause an unpreventable crash. 114 | * Unlike the other methods defined in derivatives.cpp, this method 115 | * is defined in decoders.cpp. 116 | */ 117 | void loadFromMMA(); 118 | 119 | /** Getters 120 | */ 121 | int getNumVars() { return numVars; }; 122 | 123 | /** Modify quregs to be the derivative of the circuit state produced 124 | * by attribute circuit upon constant initial state initQureg. 125 | * @param quregs the list of to-be-modified quregs which match the order 126 | * of varInd between the DerivTerm instances 127 | * @precondition varInds between all terms lie in [0, numQuregs) 128 | * @precondition numQuregs = number of unique varInd between terms 129 | * @precondition gateInd between terms is increasing (or repeating) 130 | * @precondition all quregs are created, and of equal dimension & type 131 | * @throws QuESTException if the circuit of deriv info is invalid 132 | * (i.e. contains invalid gate details), or if the user aborts 133 | */ 134 | void applyTo(Qureg* quregs, int numQuregs, Qureg initQureg, Qureg workspace); 135 | 136 | /** Modifies eneryGrad to be the gradient of the expected energy under 137 | * the given Hamiltonian, as prescribed by the circuit derivatives. 138 | * This function uses the bespoke O(#parameters) time and O(1) memory 139 | * algorithm from arXiv 2009.02823, using a novel adaptation 140 | * for density matrices. Note that the density-matrix version involves 141 | * populating one of the workQuregs with a dense representation of the 142 | * hamil; use calcDerivEnergiesDenseHamil() to use a pre-prepared qureg. 143 | * @param energyGrad must be a pre-allocated length-numVars array. 144 | */ 145 | void calcDerivEnergies(qreal* energyGrad, PauliHamil hamil, Qureg initQureg, Qureg* workQuregs, int numWorkQuregs); 146 | 147 | /** This is a density-matrix only version of calcDerivEnergies(), where 148 | * hamilQureg has been pre-prepared to be a matrix form of a PauliHamil, 149 | * via setQuregToPauliString(). 150 | */ 151 | void calcDerivEnergiesDenseHamil(qreal* energyGrad, Qureg hamilQureg, Qureg initQureg, Qureg* workQuregs, int numWorkQuregs); 152 | 153 | /** Returns a derivative metric tensor which can be used for quantum 154 | * natural gradient. If initQureg is a state-vector and the circuit is pure, 155 | * then this function computes the quantum geometric tensor; a complex matrix, 156 | * related to the Fubini-Study metric, the classical Fisher information matrix, 157 | * and the imaginary-time Li tensor plus Berry connections. If initQureg 158 | * is a density matrix and/or the circuit contains decoherence channels, this 159 | * function computes the Hilbert-Schmidt distance between the derivatives 160 | * of the output states, HS_ij = Tr(d rho/di d rho/dj), which is a real matrix 161 | * which approximates (a scaling of) the (also real) quantum Fisher information 162 | * matrix in typical NISQ settings (such as when modellable as global depolarisation, 163 | * see https://arxiv.org/abs/1912.08660). In both scenarios, the metric tensor 164 | * is computed in O(#parameters^2) time and O(1) memory, using my algorithm 165 | * from https://arxiv.org/abs/2011.02991 and a density-matrix adaptation. 166 | * @throws exception when numWorkQuregs != 4 167 | */ 168 | qmatrix calcMetricTensor(Qureg initQureg, Qureg* workQuregs, int numWorkQuregs); 169 | 170 | /** Returns the number of working registers needed to perform the method 171 | * indicated by funcName upon given the initial register. 172 | */ 173 | int getNumNeededWorkQuregsFor(std::string funcName, Qureg initQureg); 174 | 175 | /** Throws an exception if the workQuregIds are invalid or if there are too 176 | * few for the given method. 177 | * @precondition initQuregId must be valid 178 | */ 179 | void validateWorkQuregsFor(std::string methodName, int initQuregId, int* workQuregIds, int numWorkQuregs); 180 | 181 | /** Destructor will free the persistent Mathematica arrays accesssed by 182 | * the DerivTerm instances, as well as the Circuit. 183 | */ 184 | ~DerivCircuit(); 185 | }; 186 | 187 | 188 | 189 | #endif // DERIVATIVES_H -------------------------------------------------------------------------------- /Link/errors.cpp: -------------------------------------------------------------------------------- 1 | /** @file 2 | * Contains functions for throwing errors and propogating QuEST exceptions 3 | * to Mathematica errors. 4 | * 5 | * User-validation occurs both in the core QuEST backend, within this file, 6 | * and some within the QuESTlink.m front-end. Validation problems in the backend 7 | * propogate here by a QuESTException, which mentions the throwing function 8 | * (a core QuEST API function) and the error message. These exceptions are caught here, 9 | * and sent to the front-end as a 'Message[Func::error]' error, using Func::error 10 | * string specified in quest_templates.tm or QuESTlink.m. 11 | * Sometimes, code QuESTlink will catch a core QuEST exception, tweak the message, and 12 | * rethrow the exception to another QuESTlink catcher. 13 | * 14 | * @author Tyson Jones 15 | */ 16 | 17 | #include "errors.hpp" 18 | #include "link.hpp" 19 | #include "wstp.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | 30 | /* channel core-QuEST validation errors into catchable exceptions 31 | */ 32 | extern "C" void invalidQuESTInputError(const char* errMsg, const char* errFunc) { 33 | throw QuESTException(errFunc, errMsg); 34 | } 35 | 36 | /* Reports an error message to MMA without closing the pipe (more output must follow). 37 | * funcName must have a ::error tag defined in either quest_templates.tm or 38 | * QuESTlink.m 39 | */ 40 | void local_sendErrorAndWait(std::string funcName, std::string errMsg) { 41 | 42 | // send error to Mathematica 43 | WSPutFunction(stdlink, "EvaluatePacket", 1); 44 | 45 | // Message[myFunc::errormsg, err] 46 | WSPutFunction(stdlink, "Message", 2); 47 | 48 | // myFunc::errormsg = MessageName[myFunc, "errormsg"] 49 | WSPutFunction(stdlink, "MessageName", 2); 50 | WSPutSymbol(stdlink, funcName.c_str()); 51 | WSPutString(stdlink, "error"); 52 | 53 | WSPutString(stdlink, errMsg.c_str()); 54 | 55 | WSEndPacket(stdlink); 56 | WSNextPacket(stdlink); 57 | WSNewPacket(stdlink); 58 | 59 | // a new packet is now expected; caller MUST send something else 60 | } 61 | 62 | void local_sendErrorAndFail(std::string funcName, std::string errMsg) { 63 | local_sendErrorAndWait(funcName, errMsg); 64 | WSPutSymbol(stdlink, "$Failed"); 65 | 66 | // this closes the pipe; no further WSPut's should follow before control flow returns 67 | } 68 | 69 | void local_sendErrorAndAbort(std::string funcName, std::string errMsg) { 70 | local_sendErrorAndWait(funcName, errMsg); 71 | WSPutFunction(stdlink, "Abort", 0); 72 | 73 | // this closes the pipe; no further WSPut's should follow before control flow returns 74 | } 75 | 76 | void local_sendWarningAndContinue(std::string funcName, std::string warnMsg) { 77 | 78 | // send error to Mathematica 79 | WSPutFunction(stdlink, "EvaluatePacket", 1); 80 | 81 | // Message[myFunc::errormsg, err] 82 | WSPutFunction(stdlink, "Message", 2); 83 | 84 | // myFunc::errormsg = MessageName[myFunc, "errormsg"] 85 | WSPutFunction(stdlink, "MessageName", 2); 86 | WSPutSymbol(stdlink, funcName.c_str()); 87 | WSPutString(stdlink, "error"); // TODO: maybe this changes too?? 88 | 89 | WSPutString(stdlink, warnMsg.c_str()); 90 | 91 | WSEndPacket(stdlink); 92 | WSNextPacket(stdlink); 93 | WSNewPacket(stdlink); 94 | 95 | // a new packet is now expected; caller MUST send something else 96 | } 97 | 98 | void local_sendErrorAndFailOrAbortFromExcep(std::string funcName, std::string excepThrower, std::string errMsg) { 99 | 100 | if (excepThrower == "") 101 | local_sendErrorAndFail(funcName, errMsg); 102 | else if (excepThrower == "Abort") 103 | local_sendErrorAndAbort(funcName, errMsg); 104 | else 105 | local_sendErrorAndFail(funcName, "Cannot simulate " + excepThrower + ". " + errMsg); 106 | } 107 | 108 | void local_throwExcepIfQuregNotCreated(int id) { 109 | if (id < 0) 110 | throw QuESTException("", "qureg id " + std::to_string(id) + " is invalid (must be >= 0)."); 111 | if (id >= (int) quregs.size() || !quregIsCreated[id]) 112 | throw QuESTException("", "qureg (with id " + std::to_string(id) + ") has not been created"); 113 | } 114 | 115 | QuESTException local_gateUnsupportedExcep(std::string gateSyntax, std::string gateName) { 116 | return QuESTException(gateSyntax, "The implied operation \\\"" + gateName + "\\\" is not supported."); 117 | } 118 | 119 | QuESTException local_wrongNumGateParamsExcep(std::string gateSyntax, std::string gateSymb, int wrongNumParams, int rightNumParams) { 120 | return QuESTException(gateSyntax, 121 | "Operator " + gateSymb + " accepts " + std::to_string(rightNumParams) + " parameter" + ((rightNumParams==1)? "":"s") + 122 | ", but " + std::to_string(wrongNumParams) + " " + ((wrongNumParams==1)? "was":"were") + " passed."); 123 | } 124 | 125 | QuESTException local_wrongNumGateTargsExcep(std::string gateSyntax, std::string gateSymb, int wrongNumTargs, int rightNumTargs) { 126 | return QuESTException(gateSyntax, 127 | "Operator " + gateSymb + " accepts " + std::to_string(rightNumTargs) + " target qubit" + ((rightNumTargs==1)? "":"s") + 128 | ", but " + std::to_string(wrongNumTargs) + " " + ((wrongNumTargs==1)? "was":"were") + " passed."); 129 | } 130 | 131 | QuESTException local_wrongNumGateTargsExcep(std::string gateSyntax, std::string gateSymb, int wrongNumTargs, int rightNumA, int rightNumB) { 132 | return QuESTException(gateSyntax, 133 | "Operator " + gateSymb + " accepts " + std::to_string(rightNumA) + " or " + std::to_string(rightNumB) + 134 | " target qubits, but " + std::to_string(wrongNumTargs) + " " + ((wrongNumTargs==1)? "was":"were") + " passed."); 135 | } 136 | 137 | QuESTException local_wrongNumDerivParamsExcep(std::string gate, int wrongNumParams, int rightNumParams) { 138 | return QuESTException("", 139 | "An internal error has occurred. The QuESTlink backend expected to receive " + std::to_string(rightNumParams) + 140 | " 'derivative' scalars in order to effect the derivative of gate " + gate + ", but instead " + 141 | "received " + std::to_string(wrongNumParams) + "."); 142 | } 143 | 144 | QuESTException local_unrecognisedGateExcep(std::string gateSyntax, int opcode, const char* caller) { 145 | throw QuESTException(gateSyntax, 146 | "An internal error has occurred. The operator (opcode " + std::to_string(opcode) + 147 | ") was not recognised by internal function Gate::" + std::string(caller) + "()."); 148 | } 149 | 150 | QuESTException local_invalidProbExcep(std::string gateName, qreal prob, std::string maxProb) { 151 | if (prob < 0) 152 | return QuESTException("", "The error probability is negative and ergo invalid."); 153 | return QuESTException("", 154 | "The error probability exceeds the maximum of " + maxProb + 155 | " for which " + gateName + " is maximally mixing."); // throws 156 | } 157 | 158 | void local_throwExcepIfUserAborted() { 159 | 160 | /* Dear ancient Wolfram Gods; why does this no longer work? 161 | * Why is WSAbort undefined despite appearing in the WSTP doc? 162 | * Why is MLAbort undefined despite appearing in wstp.h? 163 | * Why is LinkSnooper not reporting a WSAbortMessage when aborting? 164 | */ 165 | 166 | if (WSMessageReady(stdlink)) { 167 | int code, arg; 168 | WSGetMessage(stdlink, &code, &arg); 169 | if (code == WSTerminateMessage || code == WSInterruptMessage || 170 | code == WSAbortMessage || code == WSImDyingMessage) { 171 | 172 | throw QuESTException("Abort", "Calculation aborted."); // throws 173 | } 174 | } 175 | } 176 | 177 | void sendDebugEcho(std::string msg) { 178 | 179 | WSPutFunction(stdlink, "EvaluatePacket", 1); 180 | WSPutFunction(stdlink, "Echo", 1); 181 | WSPutString(stdlink, msg.c_str()); 182 | 183 | WSEndPacket(stdlink); 184 | WSNextPacket(stdlink); 185 | WSNewPacket(stdlink); 186 | 187 | // a new packet is now expected; caller MUST send something else 188 | } 189 | -------------------------------------------------------------------------------- /Link/errors.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef ERRORS_H 3 | #define ERRORS_H 4 | 5 | #include "QuEST_precision.h" 6 | 7 | #include 8 | #include 9 | 10 | 11 | class QuESTException : public std::exception { 12 | public: 13 | std::string thrower; 14 | std::string message; 15 | QuESTException(std::string func, std::string msg, ...) { 16 | thrower = func; 17 | message = msg; 18 | } 19 | }; 20 | 21 | extern "C" void invalidQuESTInputError(const char* errMsg, const char* errFunc); 22 | 23 | void local_sendErrorAndWait(std::string funcName, std::string errMsg); 24 | 25 | void local_sendErrorAndFail(std::string funcName, std::string errMsg); 26 | 27 | void local_sendErrorAndAbort(std::string funcName, std::string errMsg); 28 | 29 | void local_sendErrorAndFailOrAbortFromExcep(std::string funcName, std::string excepThrower, std::string errMsg); 30 | 31 | void local_sendWarningAndContinue(std::string funcName, std::string warnMsg); 32 | 33 | void local_throwExcepIfQuregNotCreated(int id); 34 | 35 | void local_throwExcepIfUserAborted(); 36 | 37 | QuESTException local_gateUnsupportedExcep(std::string gateSyntax, std::string gateName); 38 | 39 | QuESTException local_wrongNumGateParamsExcep(std::string gateSyntax, std::string gateSymb, int wrongNumParams, int rightNumParams); 40 | 41 | QuESTException local_wrongNumGateTargsExcep(std::string gateSyntax, std::string gateSymb, int wrongNumTargs, int rightNumTargs); 42 | QuESTException local_wrongNumGateTargsExcep(std::string gateSyntax, std::string gateSymb, int wrongNumTargs, int rightNumA, int rightNumB); 43 | 44 | QuESTException local_wrongNumDerivParamsExcep(std::string gate, int wrongNumParams, int rightNumParams); 45 | 46 | QuESTException local_unrecognisedGateExcep(std::string gateSyntax, int opcode, const char* caller); 47 | 48 | QuESTException local_invalidProbExcep(std::string gateName, qreal prob, std::string maxProb); 49 | 50 | void sendDebugEcho(std::string msg); 51 | 52 | 53 | # endif // ERRORS_H -------------------------------------------------------------------------------- /Link/extensions.cpp: -------------------------------------------------------------------------------- 1 | /** @file 2 | * Contains CPU and multithreaded specific high-performance backend facilities, 3 | * which can be considered as extensions to the QuEST API. 4 | * 5 | * @author Tyson Jones 6 | */ 7 | 8 | #include "QuEST.h" 9 | #include "QuEST_validation.h" 10 | #include "QuEST_cpu_internal.h" 11 | 12 | #include "errors.hpp" 13 | 14 | #include 15 | #include 16 | 17 | 18 | 19 | bool extension_isHermitian(Qureg qureg) { 20 | 21 | validateDensityMatrQureg(qureg, "isHermitian (internal)"); 22 | 23 | qreal tolerance = 1E4 * REAL_EPS; 24 | 25 | long long int dim = 1LL << qureg.numQubitsRepresented; 26 | qreal* vecRe = qureg.stateVec.real; 27 | qreal* vecIm = qureg.stateVec.imag; 28 | 29 | long long int c, r, i, j; 30 | 31 | // assume Hermitian until encountering a violating amplitude 32 | bool isHermit = true; 33 | 34 | // iterate |r> and |j> where |r>|r> = |i>, |j> = |c> tolerance) 62 | isHermit = false; 63 | 64 | // non-Hermitiain if imag( amp[i] ) != - imag( amp[j] ) 65 | if (absReal(vecIm[i] + vecIm[j]) > tolerance) 66 | isHermit = false; 67 | } 68 | } 69 | } 70 | 71 | return isHermit; 72 | } 73 | 74 | void extension_addAdjointToSelf(Qureg qureg) { 75 | 76 | validateDensityMatrQureg(qureg, "addAdjointToSelf (internal)"); 77 | 78 | long long int numTasks = qureg.numAmpsPerChunk; 79 | int numQubits = qureg.numQubitsRepresented; 80 | 81 | qreal* vecRe = qureg.stateVec.real; 82 | qreal* vecIm = qureg.stateVec.imag; 83 | 84 | long long int k, i, j, l; 85 | qreal tmp; 86 | 87 | # ifdef _OPENMP 88 | # pragma omp parallel \ 89 | default (none) \ 90 | shared (numTasks, numQubits, vecRe,vecIm) \ 91 | private (k, i, j, l, tmp) 92 | # endif 93 | { 94 | # ifdef _OPENMP 95 | # pragma omp for schedule (static) 96 | # endif 97 | for (k=0LL; k = |j>|i> 100 | i = k & ((1LL << numQubits)-1); 101 | j = k >> numQubits; 102 | 103 | if (i < j) { 104 | // |l> = |i>|j> 105 | l = (i << numQubits) | j; 106 | 107 | tmp = vecRe[k] + vecRe[l]; 108 | vecRe[k] = tmp; 109 | vecRe[l] = tmp; 110 | 111 | tmp = vecIm[k]; 112 | vecIm[k] -= vecIm[l]; 113 | vecIm[l] -= tmp; 114 | } 115 | else if (i == j) { 116 | vecRe[k] *= 2; 117 | vecIm[k] = 0; 118 | } 119 | } 120 | } 121 | } 122 | 123 | void extension_applyImagFactor(Qureg qureg, qreal imagFac) { 124 | 125 | long long int numTasks = qureg.numAmpsPerChunk; 126 | 127 | qreal* vecRe = qureg.stateVec.real; 128 | qreal* vecIm = qureg.stateVec.imag; 129 | 130 | long long int i; 131 | qreal tmp; 132 | # ifdef _OPENMP 133 | # pragma omp parallel \ 134 | default (none) \ 135 | shared (numTasks, vecRe,vecIm, imagFac) \ 136 | private (i, tmp) 137 | # endif 138 | { 139 | # ifdef _OPENMP 140 | # pragma omp for schedule (static) 141 | # endif 142 | for (i=0LL; i= k) { 283 | tmpRe = vecRe[k]; 284 | tmpIm = vecIm[k]; 285 | 286 | vecRe[k] = c1*vecRe[k] - c1*vecRe[j]; 287 | vecIm[k] = c1*vecIm[k] - c1*vecIm[j]; 288 | 289 | vecRe[j] = c1*vecRe[j] - c1*tmpRe; 290 | vecIm[j] = c1*vecIm[j] - c1*tmpIm; 291 | } 292 | } else { 293 | vecRe[k] *= c2; 294 | vecIm[k] *= c2; 295 | } 296 | } 297 | } 298 | 299 | extension_addAdjointToSelf(qureg); 300 | } 301 | 302 | void extension_mixTwoQubitDepolarisingDeriv(Qureg qureg, int t1, int t2, qreal probDeriv) { 303 | 304 | validateDensityMatrQureg(qureg, "two-qubit Depol (derivative)"); 305 | validateUniqueTargets(qureg, t1, t2, "two-qubit Depol (derivative)"); 306 | 307 | long long int numTasks = qureg.numAmpsPerChunk; 308 | 309 | int numQb = qureg.numQubitsRepresented; 310 | int s1 = t1 + numQb; 311 | int s2 = t2 + numQb; 312 | 313 | qreal* vecRe = qureg.stateVec.real; 314 | qreal* vecIm = qureg.stateVec.imag; 315 | 316 | qreal c1 = (-8/15.)*probDeriv; 317 | qreal c2 = ( 2/15.)*probDeriv; 318 | 319 | long long int k, j2, j3, j4; 320 | int b1, b2; 321 | qreal re1, re2, re3, re4; 322 | qreal im1, im2, im3, im4; 323 | qreal reSum, imSum; 324 | 325 | # ifdef _OPENMP 326 | # pragma omp parallel \ 327 | default (none) \ 328 | shared (numTasks, t1,t2,s1,s2, vecRe,vecIm, c1,c2) \ 329 | private (k, j2,j3,j4, b1,b2, re1,re2,re3,re4,im1,im2,im3,im4, reSum,imSum) 330 | # endif 331 | { 332 | # ifdef _OPENMP 333 | # pragma omp for schedule (static) 334 | # endif 335 | for (k=0LL; k &prodExpecVals, long numProds, 426 | int* sampleBases, int* sampleOutcomes, int numQb, long numSamples, 427 | int* pauliCodes, int* pauliTargs, int* numPaulisPerProd, 428 | int numBatches 429 | ) { 430 | // The input arrays are total size O(numSamples*numQb + numProds*numQb), 431 | // permitting creation of temporary vectors with negligible memory overhead. 432 | // We hence convert the inputs to bitmasks for asymptotically faster shadow 433 | // processing, as suggested by Balint Koczor 434 | std::vector pauliIndOffset(numProds); 435 | std::vector pauliBitseqs(numProds); 436 | std::vector pauliTargBitseqs(numProds); 437 | std::vector outcomeTargBitseqs(numProds); 438 | std::vector baseBitseqs(numSamples); 439 | std::vector outcomeBitseqs(numSamples); 440 | 441 | // serially prepare starting indices of each pauli product, in time O(numProds) 442 | pauliIndOffset[0] = 0; 443 | for (int i=1; i 3) { 469 | errMsg = "A Pauli product contained an invalid Pauli operator code (" + 470 | std::to_string(pauliCodes[k]) + "). Each Pauli operator code must be an integer " + 471 | "0, 1, 2, 3, corresponding to Pauli operators Id, X, Y, Z respectively."; 472 | invalid = true; 473 | } 474 | 475 | if (pauliTargs[k] < 0 || pauliTargs[k] >= numQb) { 476 | errMsg = "A Pauli product targeted an invalid qubit (" + 477 | std::to_string(pauliTargs[k]) + "). Note that the number of qubits in " + 478 | "the shadow was inferred to be " + std::to_string(numQb) + "."; 479 | invalid = true; 480 | } 481 | } 482 | } 483 | 484 | if (invalid) 485 | throw QuESTException("", errMsg); 486 | 487 | // parallel validate the shadow sample bases and outcomes, in time O(numSamples*numQb) 488 | unsigned long long numTotalSampVals = numSamples * numQb; 489 | invalid = false; 490 | # ifdef _OPENMP 491 | # pragma omp parallel \ 492 | default (none) \ 493 | shared (invalid,errMsg, numTotalSampVals, numSamples,sampleBases,sampleOutcomes, numQb) \ 494 | private (k) 495 | # endif 496 | { 497 | # ifdef _OPENMP 498 | # pragma omp for schedule (static) 499 | # endif 500 | for (k=0; k 3) { 506 | errMsg = "A shadow sample contained an invalid Pauli measurement basis code (" + 507 | std::to_string(sampleBases[k]) + "). Each Pauli measurement basis code must be an integer " + 508 | "1, 2, 3, corresponding to the X, Y, Z bases respectively."; 509 | invalid = true; 510 | } 511 | 512 | if (sampleOutcomes[k] < 0 || sampleOutcomes[k] > 1) { 513 | errMsg = "A shadow sample contained an invalid qubit measurement outcome (" + 514 | std::to_string(sampleOutcomes[k]) + "). Measurement outcomes must be 0 or 1."; 515 | invalid = true; 516 | } 517 | } 518 | } 519 | 520 | if (invalid) 521 | throw QuESTException("", errMsg); 522 | 523 | // parallel encode paulis as length 2*numQb bit sequences, and their targets 524 | // as bit masks, in time O(numProds*prodSize) << O(numProds*numQb) 525 | int p; 526 | long i, j; 527 | unsigned long long seqPaulis, seqPauliTargs, seqOutcomeTargs; 528 | # ifdef _OPENMP 529 | # pragma omp parallel \ 530 | default (none) \ 531 | shared (numProds,numPaulisPerProd,pauliIndOffset,pauliCodes,pauliTargs, \ 532 | pauliBitseqs,pauliTargBitseqs,outcomeTargBitseqs) \ 533 | private (i,j,p, seqPaulis,seqPauliTargs,seqOutcomeTargs) 534 | # endif 535 | { 536 | # ifdef _OPENMP 537 | # pragma omp for schedule (static) 538 | # endif 539 | for (i=0L; i=0; q--) { 579 | ind = offset+q; 580 | seqBases = (seqBases << 2) | sampleBases[ind]; 581 | seqOuts = (seqOuts << 1) | sampleOutcomes[ind]; 582 | } 583 | 584 | baseBitseqs[s] = seqBases; 585 | outcomeBitseqs[s] = seqOuts; 586 | } 587 | } 588 | 589 | // parallel evauate the expected values of each pauli product, in time O(numProds*numSamples), 590 | // modifying output array prodExpecVals. 591 | unsigned long long targOuts; 592 | long numSampsPerBatch = (long) ceil(numSamples/(qreal) numBatches); 593 | int numProdPaulis, b, isFinalBatch, match, par; // re-using p 594 | long batchSize, val; // re-using s, i 595 | qreal fac; 596 | std::vector batchVals(numBatches); // thread-private 597 | # ifdef _OPENMP 598 | # pragma omp parallel \ 599 | default (none) \ 600 | shared (numProds,numBatches,numSamples,numSampsPerBatch, pauliCodes,pauliTargs, \ 601 | numPaulisPerProd, baseBitseqs,outcomeBitseqs,pauliBitseqs, \ 602 | pauliTargBitseqs,outcomeTargBitseqs, prodExpecVals) \ 603 | private (numProdPaulis,isFinalBatch,batchSize, targOuts, p,b,i,s, par,val,match,fac) \ 604 | firstprivate (batchVals) 605 | # endif 606 | { 607 | # ifdef _OPENMP 608 | # pragma omp for schedule (static) 609 | # endif 610 | for (p=0; p> 1; 634 | targOuts ^= targOuts >> 2; 635 | targOuts = (targOuts & 0x1111111111111111UL) * 0x1111111111111111UL; 636 | par = (targOuts >> 60) & 1; 637 | 638 | // contribute sample if match (without branching) 639 | val += match * (1-2*par); 640 | } 641 | 642 | batchVals[b] = val / (qreal) batchSize; 643 | } 644 | 645 | // choose the median of the batch values 646 | fac = pow(3, numProdPaulis); 647 | std::sort(batchVals.begin(), batchVals.end()); 648 | if (numBatches % 2) 649 | prodExpecVals[p] = fac * batchVals[numBatches/2]; 650 | else 651 | prodExpecVals[p] = fac * .5 * (batchVals[numBatches/2] + batchVals[numBatches/2 + 1]); 652 | } 653 | } 654 | } 655 | 656 | -------------------------------------------------------------------------------- /Link/extensions.cu: -------------------------------------------------------------------------------- 1 | /** @file 2 | * Contains GPU specific high-performance backend facilities, 3 | * which can be considered as extensions to the QuEST API. 4 | * 5 | * @author Tyson Jones 6 | */ 7 | 8 | #include "QuEST.h" 9 | #include "QuEST_validation.h" 10 | 11 | #include "errors.hpp" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | 20 | __forceinline__ __device__ int extractBit (const int locationOfBitFromRight, const long long int theEncodedNumber) { 21 | return (theEncodedNumber & ( 1LL << locationOfBitFromRight )) >> locationOfBitFromRight; 22 | } 23 | 24 | __forceinline__ __device__ long long int flipBit(const long long int number, const int bitInd) { 25 | return (number ^ (1LL << bitInd)); 26 | } 27 | 28 | 29 | 30 | __global__ void extension_isHermitianKernel(Qureg qureg, int *isHermit) { 31 | 32 | // if any kernel set the flag to false, stop 33 | if (! *isHermit) 34 | return; 35 | 36 | long long int numTasks = qureg.numAmpsPerChunk; 37 | long long int thisTask = blockIdx.x*blockDim.x + threadIdx.x; 38 | if (thisTask >= numTasks) return; 39 | 40 | qreal* stateRe = qureg.deviceStateVec.real; 41 | qreal* stateIm = qureg.deviceStateVec.imag; 42 | 43 | // |k> = |j>|i> 44 | int numQubits = qureg.numQubitsRepresented; 45 | long long int k = thisTask; 46 | long long int i = k & ((1LL << numQubits)-1); 47 | long long int j = k >> numQubits; 48 | 49 | if (j>i) 50 | return; 51 | 52 | // |l> = |i>|j> 53 | long long int l = (i << numQubits) | j; 54 | 55 | // non-Hermit when real(amp[k]) != real(amp[l]) or imag(amp[k]) != - imag(amp[l]) 56 | qreal threshold = 4*REAL_EPS; 57 | qreal difRe = abs(stateRe[k] - stateRe[l]); 58 | qreal difIm = abs(stateIm[k] + stateIm[l]); 59 | if (difRe > threshold || difIm > threshold) 60 | *isHermit = 0; 61 | } 62 | 63 | bool extension_isHermitian(Qureg qureg) { 64 | 65 | validateDensityMatrQureg(qureg, "isHermitianKernel (internal)"); 66 | 67 | // prepare flag in GPU memory 68 | int isHermit = 1; 69 | int *d_isHermit; 70 | cudaMalloc(&d_isHermit, sizeof(int)); 71 | cudaMemcpy(d_isHermit, &isHermit, sizeof(int), cudaMemcpyHostToDevice); 72 | 73 | // only threads encountering Hermitian-breaking 74 | int threadsPerCUDABlock = 128; 75 | int CUDABlocks = ceil(qureg.numAmpsPerChunk/ (qreal) threadsPerCUDABlock); 76 | extension_isHermitianKernel<<>>(qureg, d_isHermit); 77 | 78 | // copy flag to CPU memory 79 | cudaMemcpy(&isHermit, d_isHermit, sizeof(int), cudaMemcpyDeviceToHost); 80 | cudaFree(d_isHermit); 81 | 82 | return isHermit; 83 | } 84 | 85 | 86 | 87 | __global__ void extension_addAdjointToSelfKernel(Qureg qureg) { 88 | 89 | // each thread modifies one value (blegh) 90 | long long int numTasks = qureg.numAmpsPerChunk; 91 | long long int thisTask = blockIdx.x*blockDim.x + threadIdx.x; 92 | if (thisTask >= numTasks) return; 93 | 94 | qreal* stateRe = qureg.deviceStateVec.real; 95 | qreal* stateIm = qureg.deviceStateVec.imag; 96 | 97 | // |k> = |j>|i> 98 | int numQubits = qureg.numQubitsRepresented; 99 | long long int k = thisTask; 100 | long long int i = k & ((1LL << numQubits)-1); 101 | long long int j = k >> numQubits; 102 | 103 | if (i < j) { 104 | // |l> = |i>|j> 105 | long long int l = (i << numQubits) | j; 106 | 107 | qreal tmp = stateRe[k] + stateRe[l]; 108 | stateRe[k] = tmp; 109 | stateRe[l] = tmp; 110 | 111 | tmp = stateIm[k]; 112 | stateIm[k] -= stateIm[l]; 113 | stateIm[l] -= tmp; 114 | } 115 | else if (i == j) { 116 | stateRe[k] *= 2; 117 | stateIm[k] = 0; 118 | } 119 | } 120 | 121 | void extension_addAdjointToSelf(Qureg qureg) { 122 | 123 | validateDensityMatrQureg(qureg, "addAdjointToSelf (internal)"); 124 | 125 | int threadsPerCUDABlock = 128; 126 | int CUDABlocks = ceil(qureg.numAmpsPerChunk/ (qreal) threadsPerCUDABlock); 127 | extension_addAdjointToSelfKernel<<>>(qureg); 128 | } 129 | 130 | 131 | 132 | __global__ void extension_applyImagFactorKernel(Qureg qureg, qreal imagFac) { 133 | 134 | // each thread modifies one value (blegh) 135 | long long int numTasks = qureg.numAmpsPerChunk; 136 | long long int thisTask = blockIdx.x*blockDim.x + threadIdx.x; 137 | if (thisTask >= numTasks) return; 138 | 139 | qreal* stateRe = qureg.deviceStateVec.real; 140 | qreal* stateIm = qureg.deviceStateVec.imag; 141 | 142 | // (a + b i) (fac i) = (- fac b + a fac i) 143 | qreal a = stateRe[thisTask]; 144 | qreal b = stateIm[thisTask]; 145 | stateRe[thisTask] = - imagFac * b; 146 | stateIm[thisTask] = + imagFac * a; 147 | } 148 | 149 | void extension_applyImagFactor(Qureg qureg, qreal imagFac) { 150 | 151 | int threadsPerCUDABlock = 128; 152 | int CUDABlocks = ceil(qureg.numAmpsPerChunk/ (qreal) threadsPerCUDABlock); 153 | extension_applyImagFactorKernel<<>>(qureg, imagFac); 154 | } 155 | 156 | 157 | 158 | __global__ void extension_applyRealFactorKernel(Qureg qureg, qreal realFac) { 159 | 160 | // each thread modifies one value (blegh) 161 | long long int numTasks = qureg.numAmpsPerChunk; 162 | long long int thisTask = blockIdx.x*blockDim.x + threadIdx.x; 163 | if (thisTask >= numTasks) return; 164 | 165 | qureg.deviceStateVec.real[thisTask] *= realFac; 166 | qureg.deviceStateVec.imag[thisTask] *= realFac; 167 | } 168 | 169 | void extension_applyRealFactor(Qureg qureg, qreal realFac) { 170 | 171 | int threadsPerCUDABlock = 128; 172 | int CUDABlocks = ceil(qureg.numAmpsPerChunk/ (qreal) threadsPerCUDABlock); 173 | extension_applyRealFactorKernel<<>>(qureg, realFac); 174 | } 175 | 176 | 177 | 178 | __global__ void extension_mixDephasingDerivKernel(Qureg qureg, int targ, qreal probDeriv) { 179 | 180 | // each thread modifies one value (blegh) 181 | long long int numTasks = qureg.numAmpsPerChunk; 182 | long long int thisTask = blockIdx.x*blockDim.x + threadIdx.x; 183 | if (thisTask >= numTasks) return; 184 | 185 | long long int targShift = targ + qureg.numQubitsRepresented; 186 | int s = 2*(extractBit(targ, thisTask) == extractBit(targShift, thisTask)) - 1; 187 | qreal f = (probDeriv/2.)*(s-1); 188 | qureg.deviceStateVec.real[thisTask] *= f; 189 | qureg.deviceStateVec.imag[thisTask] *= f; 190 | } 191 | 192 | void extension_mixDephasingDeriv(Qureg qureg, int targ, qreal probDeriv) { 193 | 194 | validateDensityMatrQureg(qureg, "Deph (derivative)"); 195 | validateTarget(qureg, targ, "Deph (derivative)"); 196 | 197 | int threadsPerCUDABlock = 128; 198 | int CUDABlocks = ceil(qureg.numAmpsPerChunk/ (qreal) threadsPerCUDABlock); 199 | extension_mixDephasingDerivKernel<<>>(qureg, targ, probDeriv); 200 | extension_addAdjointToSelfKernel<<>>(qureg); 201 | } 202 | 203 | 204 | 205 | __global__ void extension_mixTwoQubitDephasingDerivKernel(Qureg qureg, int t1, int t2, qreal probDeriv) { 206 | 207 | // each thread modifies one value (blegh) 208 | long long int numTasks = qureg.numAmpsPerChunk; 209 | long long int thisTask = blockIdx.x*blockDim.x + threadIdx.x; 210 | if (thisTask >= numTasks) return; 211 | 212 | int t1Shift = t1 + qureg.numQubitsRepresented; 213 | int t2Shift = t2 + qureg.numQubitsRepresented; 214 | qreal c = (-2/3.) * probDeriv; 215 | 216 | long long int k = thisTask; 217 | int b1 = (extractBit(t1, k) == extractBit(t1Shift, k)); 218 | int b2 = (extractBit(t2, k) == extractBit(t2Shift, k)); 219 | qreal f = !(b1 && b2) * c; 220 | qureg.deviceStateVec.real[k] *= f; 221 | qureg.deviceStateVec.imag[k] *= f; 222 | } 223 | 224 | void extension_mixTwoQubitDephasingDeriv(Qureg qureg, int t1, int t2, qreal probDeriv) { 225 | 226 | validateDensityMatrQureg(qureg, "two-qubit Deph (derivative)"); 227 | validateUniqueTargets(qureg, t1, t2, "two-qubit Deph (derivative)"); 228 | 229 | int threadsPerCUDABlock = 128; 230 | int CUDABlocks = ceil(qureg.numAmpsPerChunk/ (qreal) threadsPerCUDABlock); 231 | extension_mixTwoQubitDephasingDerivKernel<<>>(qureg, t1, t2, probDeriv); 232 | extension_addAdjointToSelfKernel<<>>(qureg); 233 | } 234 | 235 | 236 | 237 | __global__ void extension_mixDepolarisingDerivKernel(Qureg qureg, int targ, qreal probDeriv) { 238 | 239 | // each thread modifies one value (blegh) 240 | long long int numTasks = qureg.numAmpsPerChunk; 241 | long long int thisTask = blockIdx.x*blockDim.x + threadIdx.x; 242 | if (thisTask >= numTasks) return; 243 | 244 | qreal* vecRe = qureg.deviceStateVec.real; 245 | qreal* vecIm = qureg.deviceStateVec.imag; 246 | 247 | long long int targShift = targ + qureg.numQubitsRepresented; 248 | qreal c1 = (-1/3.)*probDeriv; 249 | qreal c2 = 2*c1; 250 | 251 | long long int k = thisTask; 252 | long long int j = flipBit(flipBit(k, targ), targShift); 253 | 254 | if (extractBit(targ, k) == extractBit(targShift, k)) { 255 | if (j >= k) { 256 | qreal tmpRe = vecRe[k]; 257 | qreal tmpIm = vecIm[k]; 258 | 259 | vecRe[k] = c1*vecRe[k] - c1*vecRe[j]; 260 | vecIm[k] = c1*vecIm[k] - c1*vecIm[j]; 261 | 262 | vecRe[j] = c1*vecRe[j] - c1*tmpRe; 263 | vecIm[j] = c1*vecIm[j] - c1*tmpIm; 264 | } 265 | } else { 266 | vecRe[k] *= c2; 267 | vecIm[k] *= c2; 268 | } 269 | } 270 | 271 | void extension_mixDepolarisingDeriv(Qureg qureg, int targ, qreal probDeriv) { 272 | 273 | validateDensityMatrQureg(qureg, "Depol (derivative)"); 274 | validateTarget(qureg, targ, "Depol (derivative)"); 275 | 276 | int threadsPerCUDABlock = 128; 277 | int CUDABlocks = ceil(qureg.numAmpsPerChunk/ (qreal) threadsPerCUDABlock); 278 | extension_mixDepolarisingDerivKernel<<>>(qureg, targ, probDeriv); 279 | extension_addAdjointToSelfKernel<<>>(qureg); 280 | } 281 | 282 | 283 | 284 | __global__ void extension_mixTwoQubitDepolarisingDerivKernel(Qureg qureg, int t1, int t2, qreal c1, qreal c2) { 285 | 286 | // each thread modifies one value (blegh) 287 | long long int numTasks = qureg.numAmpsPerChunk; 288 | long long int thisTask = blockIdx.x*blockDim.x + threadIdx.x; 289 | if (thisTask >= numTasks) return; 290 | 291 | int numQb = qureg.numQubitsRepresented; 292 | int s1 = t1 + numQb; 293 | int s2 = t2 + numQb; 294 | 295 | qreal* vecRe = qureg.deviceStateVec.real; 296 | qreal* vecIm = qureg.deviceStateVec.imag; 297 | 298 | long long int k = thisTask; 299 | long long int j2 = flipBit(flipBit(k, t1), s1); 300 | long long int j3 = flipBit(flipBit(k, t2), s2); 301 | long long int j4 = flipBit(flipBit(j2, t2), s2); 302 | 303 | int b1 = (extractBit(t1, k) == extractBit(s1, k)); 304 | int b2 = (extractBit(t2, k) == extractBit(s2, k)); 305 | 306 | if (b2 && b1) { 307 | if (k < j2 && k < j3 && k < j4) { 308 | qreal re1 = vecRe[k]; qreal im1 = vecIm[k]; 309 | qreal re2 = vecRe[j2]; qreal im2 = vecIm[j2]; 310 | qreal re3 = vecRe[j3]; qreal im3 = vecIm[j3]; 311 | qreal re4 = vecRe[j4]; qreal im4 = vecIm[j4]; 312 | 313 | qreal reSum = c2 * (re1 + re2 + re3 + re4); 314 | qreal imSum = c2 * (im1 + im2 + im3 + im4); 315 | 316 | vecRe[k] = c1*vecRe[k] + reSum; 317 | vecRe[j2] = c1*vecRe[j2] + reSum; 318 | vecRe[j3] = c1*vecRe[j3] + reSum; 319 | vecRe[j4] = c1*vecRe[j4] + reSum; 320 | 321 | vecIm[k] = c1*vecIm[k] + imSum; 322 | vecIm[j2] = c1*vecIm[j2] + imSum; 323 | vecIm[j3] = c1*vecIm[j3] + imSum; 324 | vecIm[j4] = c1*vecIm[j4] + imSum; 325 | } 326 | } else { 327 | vecRe[k] *= c1; 328 | vecIm[k] *= c1; 329 | } 330 | } 331 | 332 | void extension_mixTwoQubitDepolarisingDeriv(Qureg qureg, int t1, int t2, qreal probDeriv) { 333 | 334 | validateDensityMatrQureg(qureg, "two-qubit Depol (derivative)"); 335 | validateUniqueTargets(qureg, t1, t2, "two-qubit Depol (derivative)"); 336 | 337 | qreal c1 = (-8/15.)*probDeriv; 338 | qreal c2 = ( 2/15.)*probDeriv; 339 | 340 | // 12 of every 16 amplitudes merely scale, 341 | // the remaining 4 are mixed 342 | 343 | int threadsPerCUDABlock = 128; 344 | int CUDABlocks = ceil(qureg.numAmpsPerChunk/ (qreal) threadsPerCUDABlock); 345 | extension_mixTwoQubitDepolarisingDerivKernel<<>>(qureg, t1, t2, c1, c2); 346 | extension_addAdjointToSelfKernel<<>>(qureg); 347 | } 348 | 349 | 350 | 351 | __global__ void extension_mixDampingDerivKernel(Qureg qureg, int targ, qreal c1, qreal c2) { 352 | 353 | // each thread modifies one value (blegh) 354 | long long int numTasks = qureg.numAmpsPerChunk; 355 | long long int thisTask = blockIdx.x*blockDim.x + threadIdx.x; 356 | if (thisTask >= numTasks) return; 357 | 358 | int conjTarg = targ + qureg.numQubitsRepresented; 359 | qreal* vecRe = qureg.deviceStateVec.real; 360 | qreal* vecIm = qureg.deviceStateVec.imag; 361 | 362 | long long int i = thisTask; 363 | int b1 = extractBit(targ, i); 364 | int b2 = extractBit(conjTarg, i); 365 | 366 | /* easy to refactor without warp divergence, albeit with the same 367 | * (possibly even worse) memory bottleneck due to non-local memory 368 | * modification in the last condition 369 | */ 370 | if (b2 == 0 && b1 == 1) { 371 | vecRe[i] *= c2; 372 | vecIm[i] *= c2; 373 | } else if (b2 == 1 && b1 == 0) { 374 | vecRe[i] = 0; 375 | vecIm[i] = 0; 376 | } else if (b1 == 1 && b2 == 1){ 377 | vecRe[i] *= - c1; 378 | vecIm[i] *= - c1; 379 | long long int j = flipBit(flipBit(i, targ), conjTarg); 380 | vecRe[j] = - vecRe[i]; 381 | vecIm[j] = - vecIm[i]; 382 | } 383 | } 384 | 385 | void extension_mixDampingDeriv(Qureg qureg, int targ, qreal prob, qreal probDeriv) { 386 | 387 | validateDensityMatrQureg(qureg, "Damp (derivative)"); 388 | validateTarget(qureg, targ, "Damp (derivative)"); 389 | 390 | qreal c1 = probDeriv/2.; 391 | qreal c2 = - c1 / sqrt(1. - prob); 392 | 393 | int threadsPerCUDABlock = 128; 394 | int CUDABlocks = ceil(qureg.numAmpsPerChunk/ (qreal) threadsPerCUDABlock); 395 | extension_mixDampingDerivKernel<<>>(qureg, targ, c1, c2); 396 | extension_addAdjointToSelfKernel<<>>(qureg); 397 | } 398 | 399 | __global__ void local_validateShadowPaulisKernel(int* invalid, int numQb, unsigned long long numTotalPaulis, int* pauliCodes, int* pauliTargs) { 400 | long long int k = blockIdx.x*blockDim.x + threadIdx.x; 401 | if (k >= numTotalPaulis) return; 402 | 403 | if (pauliCodes[k] < 0 || pauliCodes[k] > 3 || pauliTargs[k] < 0 || pauliTargs[k] >= numQb) 404 | *invalid = 1; 405 | } 406 | 407 | __global__ void local_validateShadowSampsKernel(int* invalid, unsigned long long numTotalSampVals, int* sampleBases, int* sampleOutcomes) { 408 | long long int k = blockIdx.x*blockDim.x + threadIdx.x; 409 | if (k >= numTotalSampVals) return; 410 | 411 | if (sampleBases[k] < 1 || sampleBases[k] > 3 || sampleOutcomes[k] < 0 || sampleOutcomes[k] > 1) 412 | *invalid = 1; 413 | } 414 | 415 | __global__ void local_prepareShadowPauliBitseqsKernel( 416 | unsigned long long* pauliBitseqs, unsigned long long* pauliTargBitseqs, unsigned long long* outcomeTargBitseqs, 417 | long numProds, int* numPaulisPerProd, long* pauliIndOffset, int* pauliCodes, int* pauliTargs 418 | ) { 419 | long long int i = blockIdx.x*blockDim.x + threadIdx.x; 420 | if (i >= numProds) return; 421 | 422 | int offset = pauliIndOffset[i]; 423 | int numPaulisInProd = numPaulisPerProd[i]; 424 | 425 | unsigned long long seqPaulis = 00ULL; // length 2*numQb 426 | unsigned long long seqPauliTargs = 00ULL; // length 2*numQb 427 | unsigned long long seqOutcomeTargs = 0ULL; // length numQb 428 | 429 | for (int p=0; p= numSamples) return; 447 | 448 | long offset = numQb*s; 449 | unsigned long long seqBases = 00ULL; // length 2*numQb 450 | unsigned long long seqOuts = 0ULL; // length numQb 451 | 452 | for (int q=numQb-1; q>=0; q--) { 453 | int ind = offset + q; 454 | seqBases = (seqBases << 2) | sampleBases[ind]; 455 | seqOuts = (seqOuts << 1) | sampleOutcomes[ind]; 456 | } 457 | 458 | baseBitseqs[s] = seqBases; 459 | outcomeBitseqs[s] = seqOuts; 460 | } 461 | 462 | #define MAX_NUM_SHADOW_BATCHES 200 463 | 464 | __global__ void extension_calcExpecPauliProdsFromClassicalShadowKernel( 465 | qreal* prodExpecVals, 466 | int numProds, int numSamples, int numBatches, 467 | int* numPaulisPerProd, 468 | unsigned long long *baseBitseqs, unsigned long long *pauliTargBitseqs, 469 | unsigned long long *pauliBitseqs, unsigned long long *outcomeBitseqs, 470 | unsigned long long *outcomeTargBitseqs 471 | ) { 472 | // parallel evauate the expected values of each pauli product, modifying output array prodExpecVals. 473 | long long int p = blockIdx.x*blockDim.x + threadIdx.x; 474 | if (p >= numProds) return; 475 | 476 | // divide this thread's job into batches 477 | qreal batchVals[MAX_NUM_SHADOW_BATCHES]; 478 | long numSampsPerBatch = (long) ceil(numSamples/(qreal) numBatches); 479 | int numProdPaulis = numPaulisPerProd[p]; 480 | 481 | // populate batch values 482 | for (int b=0; b> 1; 501 | targOuts ^= targOuts >> 2; 502 | targOuts = (targOuts & 0x1111111111111111UL) * 0x1111111111111111UL; 503 | int par = (targOuts >> 60) & 1; 504 | 505 | // contribute sample if match (without branching) 506 | val += match * (1-2*par); 507 | } 508 | 509 | batchVals[b] = val / (qreal) batchSize; 510 | } 511 | 512 | qreal fac = pow((qreal) 3., (qreal) numProdPaulis); 513 | 514 | // choose the median of the batch values 515 | thrust::sort(thrust::seq, batchVals, &(batchVals[numBatches])); 516 | if (numBatches % 2) 517 | prodExpecVals[p] = fac * batchVals[numBatches/2]; 518 | else 519 | prodExpecVals[p] = fac * .5 * (batchVals[numBatches/2] + batchVals[numBatches/2 + 1]); 520 | } 521 | 522 | void extension_calcExpecPauliProdsFromClassicalShadow( 523 | std::vector &prodExpecVals, long numProds, 524 | int* sampleBases, int* sampleOutcomes, int numQb, long numSamples, 525 | int* pauliCodes, int* pauliTargs, int* numPaulisPerProd, 526 | int numBatches 527 | ) { 528 | // early numBatches validation, since we must be more strict than the CPU variant 529 | if (numBatches > MAX_NUM_SHADOW_BATCHES) 530 | throw QuESTException("", "The maximum number of batches permitted in GPU mode is " + std::to_string(MAX_NUM_SHADOW_BATCHES)); 531 | 532 | // serial array encode: O(numProds) 533 | std::vector pauliIndOffset(numProds); 534 | pauliIndOffset[0] = 0; 535 | for (int i=1; i>>( 576 | d_invalid, numQb, numTotalPaulis, d_pauliCodes, d_pauliTargs); 577 | local_validateShadowSampsKernel<<>>( 578 | d_invalid, numSamples*numQb, d_sampleBases, d_sampleOutcomes); 579 | cudaMemcpy(&invalid, d_invalid, sizeof(int), cudaMemcpyDeviceToHost); 580 | cudaFree(d_invalid); 581 | 582 | if (!invalid) { 583 | 584 | // prepare bit sequences 585 | local_prepareShadowPauliBitseqsKernel<<>>( 586 | d_pauliBitseqs, d_pauliTargBitseqs, d_outcomeTargBitseqs, 587 | numProds, d_numPaulisPerProd, d_pauliIndOffset, d_pauliCodes, d_pauliTargs); 588 | local_prepareShadowSampleBitseqsKernel<<>>( 589 | d_baseBitseqs, d_outcomeBitseqs, 590 | numQb, numSamples, d_sampleBases, d_sampleOutcomes); 591 | 592 | // evaluate shadow expec values 593 | extension_calcExpecPauliProdsFromClassicalShadowKernel<<>>( 594 | d_prodExpecVals, 595 | numProds, numSamples, numBatches, 596 | d_numPaulisPerProd, d_baseBitseqs, d_pauliTargBitseqs, 597 | d_pauliBitseqs, d_outcomeBitseqs, d_outcomeTargBitseqs); 598 | 599 | // copy expec vals back to RAM 600 | cudaMemcpy(prodExpecVals.data(), d_prodExpecVals, memExpecVals, cudaMemcpyDeviceToHost); 601 | } 602 | 603 | // clean-up 604 | cudaFree(d_prodExpecVals); 605 | cudaFree(d_sampleOutcomes); 606 | cudaFree(d_pauliCodes); 607 | cudaFree(d_pauliTargs); 608 | cudaFree(d_numPaulisPerProd); 609 | cudaFree(d_pauliIndOffset); 610 | cudaFree(d_pauliBitseqs); 611 | cudaFree(d_pauliTargBitseqs); 612 | cudaFree(d_outcomeTargBitseqs); 613 | cudaFree(d_baseBitseqs); 614 | cudaFree(d_outcomeBitseqs); 615 | 616 | if (invalid) 617 | throw QuESTException("", "The input classical shadow, or the Pauli products, were invalid."); 618 | } 619 | 620 | -------------------------------------------------------------------------------- /Link/extensions.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef EXTENSIONS_H 3 | #define EXTENSIONS_H 4 | 5 | 6 | 7 | bool extension_isHermitian(Qureg qureg); 8 | 9 | void extension_addAdjointToSelf(Qureg qureg); 10 | 11 | void extension_applyImagFactor(Qureg qureg, qreal imagFac); 12 | 13 | void extension_applyRealFactor(Qureg qureg, qreal realFac); 14 | 15 | void extension_mixDephasingDeriv(Qureg qureg, int targetQubit, qreal probDeriv); 16 | 17 | void extension_mixTwoQubitDephasingDeriv(Qureg qureg, int t1, int t2, qreal probDeriv); 18 | 19 | void extension_mixDepolarisingDeriv(Qureg qureg, int targ, qreal probDeriv); 20 | 21 | void extension_mixTwoQubitDepolarisingDeriv(Qureg qureg, int t1, int t2, qreal probDeriv); 22 | 23 | void extension_mixDampingDeriv(Qureg qureg, int targ, qreal prob, qreal probDeriv); 24 | 25 | void extension_calcExpecPauliProdsFromClassicalShadow( 26 | std::vector &prodExpecVals, long numProds, 27 | int* sampleBases, int* sampleOutcomes, int numQb, long numSamples, 28 | int* pauliCodes, int* pauliTargs, int* numPaulisPerProd, 29 | int numBatches 30 | ); // throws 31 | 32 | 33 | 34 | #endif // EXTENSIONS_H -------------------------------------------------------------------------------- /Link/link.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LINK_H 2 | #define LINK_H 3 | 4 | #include "QuEST.h" 5 | #include 6 | 7 | 8 | /* 9 | * Global instance of QuESTEnv, created when MMA is linked. 10 | */ 11 | extern QuESTEnv env; 12 | 13 | /* 14 | * Collection of instantiated Quregs 15 | */ 16 | extern std::vector quregs; 17 | extern std::vector quregIsCreated; 18 | 19 | 20 | #endif // LINK_H -------------------------------------------------------------------------------- /Link/utilities.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "QuEST_precision.h" 5 | #include "QuEST_complex.h" 6 | 7 | #include "utilities.hpp" 8 | #include "errors.hpp" 9 | 10 | 11 | 12 | #define MIN_NON_ZERO_EPS_FAC 1E4 13 | 14 | 15 | 16 | /* 17 | * RNG 18 | */ 19 | 20 | std::mt19937 randGen(std::random_device{}()); // auto-seeds 21 | std::uniform_real_distribution randDist(0,1); 22 | 23 | int local_getRandomIndex(qreal* weights, int numInds) { 24 | 25 | //weights are assumed in [0,1] and normalised to sum to 1 26 | 27 | qreal r = randDist(randGen); 28 | 29 | qreal weightSum = 0; 30 | 31 | for (int i=0; i &array) { 61 | 62 | // this is a monstrously poor shuffle; merely swapping random elements a 63 | // fixed number of times. It should only be used when the array doesn't *really* 64 | // need to be shuffled, just mucked about a little for numerical reasons, 65 | // in runtime-performance critical code 66 | 67 | int len = (int) array.size(); 68 | 69 | for (int n=0; nreal[r][c] *= fac; 240 | matr->imag[r][c] *= fac; 241 | } 242 | } 243 | 244 | void local_setComplexMatrix4RealFactor(ComplexMatrix4 *matr, qreal fac) { 245 | int dim = 4; 246 | for (int r=0; rreal[r][c] *= fac; 249 | matr->imag[r][c] *= fac; 250 | } 251 | } 252 | 253 | void local_setComplexMatrixToRealFactor(ComplexMatrixN matr, qreal fac) { 254 | long long int dim = (1LL << matr.numQubits); 255 | for (long long int r=0; r MIN_NON_ZERO_EPS_FAC * REAL_EPS); 414 | } 415 | 416 | bool local_isNonZero(qcomp scalar) { 417 | 418 | return local_isNonZero(abs(scalar)); 419 | } 420 | 421 | qmatrix local_decomposeLU(qmatrix matr, std::vector& pivots, int *numPivots) { 422 | 423 | size_t dim = matr.size(); 424 | for (size_t i=0; i maxAbs) { 437 | maxAbs = elemAbs; 438 | maxInd = k; 439 | } 440 | } 441 | 442 | // halt if singularity detected 443 | if (!local_isNonZero(maxAbs)) 444 | throw QuESTException("", "Attempted LU decomposition of a non-invertible matrix."); 445 | 446 | // perform pivot if necessary 447 | if (maxInd != i) { 448 | (*numPivots)++; 449 | 450 | // pivot rows of matr (swap row i and maxInd) 451 | for (size_t c=0; c pivots(m.size()); // ignored 493 | qmatrix lu = local_decomposeLU(m, pivots, &numPivots); // throws 494 | 495 | qcomp det = 1; 496 | for (size_t i=0; i pivots(dim); 524 | int numPivots; // ignored 525 | qmatrix lu = local_decomposeLU(matr, pivots, &numPivots); // throws 526 | qmatrix inv = local_getQmatrix(dim); 527 | 528 | // populate inverse 529 | for (int j=0; j=0; i--) { 538 | for (int k=i+1; k 9 | 10 | 11 | 12 | // variable precision WSTP scalar communication 13 | #if QuEST_PREC==1 14 | #define WSGetQreal WSGetReal32 15 | #define WSPutQreal WSPutReal32 16 | 17 | #define WSGetQrealList WSGetReal32List 18 | #define WSPutQrealList WSPutReal32List 19 | #define WSReleaseQrealList WSReleaseReal32List 20 | #elif QuEST_PREC==2 21 | #define WSGetQreal WSGetReal64 22 | #define WSPutQreal WSPutReal64 23 | 24 | #define WSGetQrealList WSGetReal64List 25 | #define WSPutQrealList WSPutReal64List 26 | #define WSReleaseQrealList WSReleaseReal64List 27 | #elif QuEST_PREC==4 28 | #define WSGetQreal WSGetReal128 29 | #define WSPutQreal WSPutReal128 30 | 31 | #define WSGetQrealList WSGetReal128List 32 | #define WSPutQrealList WSPutReal128List 33 | #define WSReleaseQrealList WSReleaseReal128List 34 | #endif 35 | 36 | 37 | 38 | int local_getRandomIndex(qreal* weights, int numInds); 39 | 40 | int local_getRandomIndex(int numInds); 41 | 42 | void local_lazyShuffle(std::vector &array); 43 | 44 | 45 | typedef std::vector qvector; 46 | 47 | typedef std::vector> qmatrix; 48 | 49 | qvector local_getQvector(int dim); 50 | 51 | qmatrix local_getQmatrix(int dim); 52 | 53 | qmatrix operator + (const qmatrix& m1, const qmatrix& m2); 54 | 55 | 56 | long long int local_getNumRealScalarsToFormMatrix(int numQubits); 57 | 58 | long long int local_getNumRealScalarsToFormDiagonalMatrix(int numQubits); 59 | 60 | qvector local_getQvectorFromFlatList(qreal* flatElems, int dim); 61 | 62 | qmatrix local_getQmatrixFromFlatList(qreal* flatElems, int dim); 63 | 64 | qmatrix local_getKrausSuperoperatorFromFlatList(qreal* flatElems, int numQubits); 65 | 66 | 67 | ComplexMatrix2 local_getMatrix2FromFlatList(qreal* flatElems); 68 | 69 | ComplexMatrix4 local_getMatrix4FromFlatList(qreal* flatElems); 70 | 71 | void local_setMatrixNFromFlatList(qreal* list, ComplexMatrixN m, int numQubits); 72 | 73 | void local_createManyMatrixNFromFlatList(qreal* list, ComplexMatrixN* matrs, int numOps, int numQubits); 74 | 75 | ComplexMatrix2 local_getMatrix2FromFlatListAtIndex(qreal* list, int n); 76 | 77 | ComplexMatrix4 local_getMatrix4FromFlatListAtIndex(qreal* list, int n); 78 | 79 | ComplexMatrix2 local_getZeroComplexMatrix2(); 80 | 81 | void local_setMatrixNFromFlatListAtIndex(qreal* list, ComplexMatrixN m, int numQubits, int n); 82 | 83 | void local_setSubDiagonalOpFromFlatList(qreal* flatElems, SubDiagonalOp op); 84 | 85 | 86 | void local_setFlatListFromMatrixN(qreal* list, ComplexMatrixN m, int numQubits); 87 | 88 | void local_setFlatListToMatrixDagger(qreal* list, int numQubits); 89 | 90 | void local_setFlatListFromQmatrix(qreal* list, qmatrix m); 91 | 92 | void local_setFlatListFromQvector(qreal* list, qvector v); 93 | 94 | void local_setMatrixNFromQmatrix(ComplexMatrixN cm, qmatrix qm); 95 | 96 | void local_setComplexMatrix2RealFactor(ComplexMatrix2 *m, qreal fac); 97 | 98 | void local_setComplexMatrix4RealFactor(ComplexMatrix4 *m, qreal fac); 99 | 100 | void local_setComplexMatrixToRealFactor(ComplexMatrixN matr, qreal fac); 101 | 102 | void local_setFlatListToDiagonalMatrixDagger(qreal* list, int numQubits); 103 | 104 | 105 | bool local_isInvertible(qmatrix matr); 106 | 107 | bool local_isInvertible(qvector diag); 108 | 109 | bool local_isNonZero(qreal scalar); 110 | 111 | bool local_isNonZero(qcomp scalar); 112 | 113 | qmatrix local_getInverse(qmatrix matr); // throws 114 | 115 | qvector local_getInverse(qvector diagonal); 116 | 117 | qmatrix local_getDagger(qmatrix matr); 118 | 119 | 120 | bool local_isInt(qreal num); 121 | 122 | bool local_isEncodedVector(qreal paramDim); 123 | 124 | bool local_isEncodedMatrix(qreal paramDim); 125 | 126 | bool local_isPossiblySquareMatrix(int numFlatReals); 127 | 128 | 129 | #endif // UTILITIES_H -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [QuESTlink](https://questlink.qtechtheory.org) 4 | 5 | 6 | [![GitHub release](https://img.shields.io/github/release/QTechTheory/QuESTlink)](https://GitHub.com/QTechTheory/QuESTlink/releases/) 7 | [![unit tests](https://img.shields.io/badge/build-passing-green.svg)](https://github.com/QTechTheory/QuESTlink/actions) 8 | [![MIT license](https://img.shields.io/badge/license-MIT-lightgrey.svg)](LICENCE.txt) 9 | 10 | 11 | 14 | 15 | QuESTlink brings the power of [QuEST](https://github.com/QuEST-Kit/QuEST)'s quantum computer simulation to Mathematica, integrating it with powerful symbolic and diagrammatic facilities. QuESTlink is no mere *interface* to QuEST; it boasts a substantial number of additional features like analytic calculations, circuit rendering and recompiling, automatic differentiation of parameterised circuits and channels, and realistic quantum device descriptions. QuESTlink is especially fast at emulating variational quantum algorithms using a variety of novel asymptotically superior schemes. 16 | 17 | 18 | 19 | 20 | [![Languages](https://img.shields.io/badge/API-Mathematica-ff69b4.svg)](http://www.open-std.org/jtc1/sc22/wg14/www/standards.html#9899) 21 | [![Languages](https://img.shields.io/badge/backend-C++11-ff69b4.svg)](https://isocpp.org/wiki/faq/cpp11) 22 | ![OS](https://img.shields.io/badge/os-MacOS-9cbd3c.svg) 23 | ![OS](https://img.shields.io/badge/os-Linux-9cbd3c.svg) 24 | ![OS](https://img.shields.io/badge/os-Windows-9cbd3c.svg) 25 | [![Platforms](https://img.shields.io/badge/multithreaded-OpenMP-6699ff.svg)](https://www.openmp.org/) 26 | [![Platforms](https://img.shields.io/badge/GPU-CUDA-6699ff.svg)](https://developer.nvidia.com/cuda-zone) 27 | [![Link](https://img.shields.io/badge/link-WSTP-6699ff.svg)](https://www.wolfram.com/wstp/) 28 | 29 | 30 | QuESTlink is developed by [Tyson Jones](https://tysonjones.io) within the [QTechTheory](https://qtechtheory.org/) group at the University of Oxford. To learn more: 31 | - read our [whitepaper](https://iopscience.iop.org/article/10.1088/2058-9565/ab8506) 32 | - visit our [website](http://questlink.qtechtheory.org/) 33 | 34 | 35 | [![DOI](https://img.shields.io/badge/DOI-10.5281%2Fzenodo.6794669-yellow.svg)](https://doi.org/10.5281/zenodo.6794669) 36 | [![Email](https://img.shields.io/badge/email-tyson.jones.input@gmail.com-red.svg)](mailto:tyson.jones.input@gmail.com) 37 | 38 | 39 | ## :rocket:  Getting started 40 | 41 | QuESTlink *just works*; it is standalone, requires no installation, and can be obtained and deployed entirely within Mathematica or the free [Wolfram Engine](https://www.wolfram.com/engine/): 42 | 43 | ```Mathematica 44 | Import["https://qtechtheory.org/questlink.m"] 45 | 46 | CreateDownloadedQuESTEnv[] 47 | ``` 48 | 49 | > Read the [doc](Doc/README.md) to compile QuESTlink from source to enable multithreading and GPU-acceleration, or to deploy to a remote server. 50 | 51 | The extensive API can then be viewed with 52 | ```Mathematica 53 | ?QuEST`* 54 | ``` 55 | 56 | 57 | ## :white_check_mark:  Features 58 | QuESTlink provides a Mathematica interface to the entire [QuEST API](https://quest-kit.github.io/QuEST/modules.html) and so ergo supports 59 | - :ballot_box_with_check:   **statevectors** for precise simulation of perfect quantum computers 60 | - :ballot_box_with_check:   **density matrices** for precise simulation of noisy quantum computers 61 | - :ballot_box_with_check:   **general operators** with any number of control and target qubits 62 | - :ballot_box_with_check:   **general decoherence channels** of any dimension 63 | - :ballot_box_with_check:   **general Hermitian operators** in the Pauli basis 64 | - :ballot_box_with_check:   **many *many* operators**, including even [Pauli gadgets](https://quest-kit.github.io/QuEST-develop-doc/group__unitary.html#ga34aa4865c92f9aa5d898c91286c9eca5), [analytic phase functions](https://quest-kit.github.io/QuEST-develop-doc/group__operator.html#ga467f517abd18dbc3d6fced84c6589161) and [Trotter circuits](https://quest-kit.github.io/QuEST-develop-doc/group__operator.html#ga35b6321c578a8c69470132b5ee95f930) 65 | - :ballot_box_with_check:   **many tools to analyse** quantum states, such as calculations of [probability](https://quest-kit.github.io/QuEST-develop-doc/group__calc.html#gad0cc08d52cad5062553d6f78126780cc), [fidelity](https://quest-kit.github.io/QuEST-develop-doc/group__calc.html#gaa266ed6c8ae5d0d0f49e1ac50819cffc), and [expected value](https://quest-kit.github.io/QuEST-develop-doc/group__calc.html#ga82f17e96a4cb7612fb9c6ef856df3810) 66 | 67 | QuESTlink significantly extends the funcionality of QuEST to include: 68 | - :ballot_box_with_check:   **symbolic gates** for concise expression of quantum circuits akin to the literature 69 | - :ballot_box_with_check:   **plotting** of circuits and channels, of circuit topologies, of density matrices, and of operator schedules. 70 | - :ballot_box_with_check:   **analytic** calculation of circuits, channels, superoperators and Hermitian operators 71 | - :ballot_box_with_check:   **automatic differentiation** of parameterised circuits and channels to rapidly compute observable gradients and metric tensors in (even noisy) variational quantum algorithms. 72 | - :ballot_box_with_check:   **noise insertion** to translate a pure quantum circuit into a noisy channel as prescribed by realistic hardware devices 73 | - :ballot_box_with_check:   **much more** such as Monte Carlo simulation, calculation of classical shadows, circuit simplification, and convenience functions for generating ansatz circuits and operators 74 | 75 | ## :heart:  Acknowledgements 76 | 77 | We sincerely thank the following contributors to QuESTlink: 78 | 79 | - [Balint Koczor](https://github.com/BalintKoczor) 80 | - [Richard Meister](https://github.com/rrmeister) 81 | -------------------------------------------------------------------------------- /Tests/v016_RecompileCircuit_cliffordAndRz.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v016_RecompileCircuit_cliffordAndRz.pdf -------------------------------------------------------------------------------- /Tests/v016_RecompileCircuit_singleQubitAndCNOT.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v016_RecompileCircuit_singleQubitAndCNOT.pdf -------------------------------------------------------------------------------- /Tests/v017_CalcCircuitGenerator.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v017_CalcCircuitGenerator.pdf -------------------------------------------------------------------------------- /Tests/v017_GetCircuitCompacted.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v017_GetCircuitCompacted.pdf -------------------------------------------------------------------------------- /Tests/v017_GetCircuitQubits.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v017_GetCircuitQubits.pdf -------------------------------------------------------------------------------- /Tests/v017_GetPauliStringFromMatrix.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v017_GetPauliStringFromMatrix.pdf -------------------------------------------------------------------------------- /Tests/v017_RetargetCircuit.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v017_RetargetCircuit.pdf -------------------------------------------------------------------------------- /Tests/v018_ApplyPauliTransferMap.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v018_ApplyPauliTransferMap.pdf -------------------------------------------------------------------------------- /Tests/v018_CalcPauliTransferEval.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v018_CalcPauliTransferEval.pdf -------------------------------------------------------------------------------- /Tests/v018_CalcPauliTransferMap.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v018_CalcPauliTransferMap.pdf -------------------------------------------------------------------------------- /Tests/v018_CalcPauliTransferMatrix.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v018_CalcPauliTransferMatrix.pdf -------------------------------------------------------------------------------- /Tests/v018_DrawPauliTransferEval.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v018_DrawPauliTransferEval.pdf -------------------------------------------------------------------------------- /Tests/v018_DrawPauliTransferMap.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v018_DrawPauliTransferMap.pdf -------------------------------------------------------------------------------- /Tests/v018_GetCircuitParameterised.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v018_GetCircuitParameterised.pdf -------------------------------------------------------------------------------- /Tests/v018_GetPauliString.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v018_GetPauliString.pdf -------------------------------------------------------------------------------- /Tests/v018_GetPauliStringOverlap.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v018_GetPauliStringOverlap.pdf -------------------------------------------------------------------------------- /Tests/v018_GetPauliStringReformatted.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v018_GetPauliStringReformatted.pdf -------------------------------------------------------------------------------- /Tests/v018_GetPauliStringRetargeted.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v018_GetPauliStringRetargeted.pdf -------------------------------------------------------------------------------- /Tests/v019_GetCircuitConjugated.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v019_GetCircuitConjugated.pdf -------------------------------------------------------------------------------- /Tests/v019_GetCircuitSuperoperator.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v019_GetCircuitSuperoperator.pdf -------------------------------------------------------------------------------- /Tests/v019_GetCircuitsFromChannel.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v019_GetCircuitsFromChannel.pdf -------------------------------------------------------------------------------- /Tests/v019_GetRandomCircuitFromChannel.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v019_GetRandomCircuitFromChannel.pdf -------------------------------------------------------------------------------- /Tests/v019_SampleExpecPauliString.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v019_SampleExpecPauliString.pdf -------------------------------------------------------------------------------- /Tests/v019_SimplifyPaulis.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/Tests/v019_SimplifyPaulis.pdf -------------------------------------------------------------------------------- /Tools/linksnooper.nb: -------------------------------------------------------------------------------- 1 | (* Content-type: application/vnd.wolfram.mathematica *) 2 | 3 | (*** Wolfram Notebook File ***) 4 | (* http://www.wolfram.com/nb *) 5 | 6 | (* CreatedBy='Mathematica 13.0' *) 7 | 8 | (*CacheID: 234*) 9 | (* Internal cache information: 10 | NotebookFileLineBreakTest 11 | NotebookFileLineBreakTest 12 | NotebookDataPosition[ 158, 7] 13 | NotebookDataLength[ 4419, 129] 14 | NotebookOptionsPosition[ 3665, 109] 15 | NotebookOutlinePosition[ 4062, 125] 16 | CellTagsIndexPosition[ 4019, 122] 17 | WindowFrame->Normal*) 18 | 19 | (* Beginning of Notebook Content *) 20 | Notebook[{ 21 | Cell[BoxData[{ 22 | RowBox[{ 23 | RowBox[{"SetDirectory", " ", "@", " ", 24 | RowBox[{"NotebookDirectory", "[", "]"}]}], ";"}], "\[IndentingNewLine]", 25 | RowBox[{ 26 | RowBox[{ 27 | RowBox[{"Import", "[", "\"\<../Link/QuESTlink.m\>\"", "]"}], ";"}], 28 | "\n"}], "\[IndentingNewLine]", 29 | RowBox[{ 30 | RowBox[{"cmdline", "=", 31 | RowBox[{"StringJoin", "[", "\n", "\t", 32 | RowBox[{"\"\\"", ",", " ", 33 | RowBox[{"FileNameJoin", "[", 34 | RowBox[{"{", 35 | RowBox[{ 36 | "$InstallationDirectory", ",", "\"\\"", ",", 37 | "\"\\"", ",", "\"\\"", ",", "\"\\""}], 38 | "}"}], "]"}], ",", "\n", "\t", 39 | "\"\< com.wolfram.jlink.util.LinkSnooper -kernelname \\\"\>\"", ",", 40 | "\n", "\t", "\"\<../quest_link\\\"\>\""}], "]"}]}], ";"}], "\n", 41 | RowBox[{ 42 | RowBox[{"Install", "[", "cmdline", "]"}], ";"}]}], "Code", 43 | CellChangeTimes->{ 44 | 3.866704487519043*^9, {3.866705127913443*^9, 3.866705129634906*^9}, { 45 | 3.8667051852474327`*^9, 3.86670520778517*^9}}, 46 | CellLabel->"In[1]:=",ExpressionUUID->"cb9f6134-00b7-472a-a0f0-9a2fef31dab4"], 47 | 48 | Cell[TextData[{ 49 | "The above should open a new ", 50 | StyleBox["LinkSnooper", 51 | FontWeight->"Bold"], 52 | " window, displaying only the messages between Mathematica and WSTP (and ", 53 | StyleBox["not", 54 | FontSlant->"Italic"], 55 | " the kernel)" 56 | }], "Text", 57 | CellChangeTimes->{{3.866705214142157*^9, 3.8667052363848047`*^9}}, 58 | Background->RGBColor[ 59 | 0.88, 1, 0.88],ExpressionUUID->"6a13648a-7bf3-4738-95a4-e50392218798"], 60 | 61 | Cell[BoxData[{ 62 | RowBox[{ 63 | RowBox[{"\[Psi]", " ", "=", " ", 64 | RowBox[{"CreateQureg", "[", "23", "]"}]}], ";"}], "\[IndentingNewLine]", 65 | RowBox[{ 66 | RowBox[{"u", " ", "=", " ", 67 | RowBox[{"Circuit", "[", 68 | RowBox[{ 69 | SubscriptBox["X", "0"], 70 | SubscriptBox["Y", "3"], 71 | SubscriptBox["Z", "5"], " ", 72 | RowBox[{ 73 | SubscriptBox["C", 74 | RowBox[{"0", ",", "1", ",", "2"}]], "[", 75 | RowBox[{"R", "[", 76 | RowBox[{".1", ",", " ", 77 | RowBox[{ 78 | SubscriptBox["X", "3"], 79 | SubscriptBox["Y", "4"], 80 | SubscriptBox["Z", "5"]}]}], "]"}], "]"}], " ", 81 | RowBox[{ 82 | SubscriptBox["Matr", "0"], "[", 83 | RowBox[{"(", "\[NoBreak]", GridBox[{ 84 | { 85 | RowBox[{".1", "+", 86 | RowBox[{".2", "\[ImaginaryI]"}]}], "0"}, 87 | { 88 | RowBox[{"-", "5"}], 89 | RowBox[{ 90 | RowBox[{"-", "7"}], "-", 91 | RowBox[{"\[Pi]", " ", "\[ImaginaryI]"}]}]} 92 | }], "\[NoBreak]", ")"}], "]"}]}], "]"}]}], ";"}]}], "Input", 93 | CellChangeTimes->{{3.86670524847995*^9, 3.8667053352298613`*^9}}, 94 | CellLabel->"In[5]:=",ExpressionUUID->"d7b273ce-0a76-4687-bd6d-21a5e82e5337"], 95 | 96 | Cell[CellGroupData[{ 97 | 98 | Cell[BoxData[ 99 | RowBox[{"ApplyCircuit", "[", 100 | RowBox[{"\[Psi]", ",", " ", "u"}], "]"}]], "Input", 101 | CellChangeTimes->{{3.8667053545205812`*^9, 3.8667053570150957`*^9}}, 102 | CellLabel->"In[9]:=",ExpressionUUID->"6f77eab7-01a0-4d9e-92c8-2dc5e04684b6"], 103 | 104 | Cell[BoxData[ 105 | RowBox[{"{", "}"}]], "Output", 106 | CellChangeTimes->{3.866705357770567*^9}, 107 | CellLabel->"Out[9]=",ExpressionUUID->"f9177644-b18e-465b-b22a-edeb6b4bc37d"] 108 | }, Open ]] 109 | }, 110 | WindowSize->{808, 755}, 111 | WindowMargins->{{190, Automatic}, {49, Automatic}}, 112 | FrontEndVersion->"13.0 for Mac OS X x86 (64-bit) (February 4, 2022)", 113 | StyleDefinitions->"Default.nb", 114 | ExpressionUUID->"dcb7dc0b-a010-41ed-b001-3c48d3c7fded" 115 | ] 116 | (* End of Notebook Content *) 117 | 118 | (* Internal cache information *) 119 | (*CellTagsOutline 120 | CellTagsIndex->{} 121 | *) 122 | (*CellTagsIndex 123 | CellTagsIndex->{} 124 | *) 125 | (*NotebookFileOutline 126 | Notebook[{ 127 | Cell[558, 20, 1084, 25, 186, "Code",ExpressionUUID->"cb9f6134-00b7-472a-a0f0-9a2fef31dab4"], 128 | Cell[1645, 47, 406, 11, 74, "Text",ExpressionUUID->"6a13648a-7bf3-4738-95a4-e50392218798"], 129 | Cell[2054, 60, 1156, 33, 66, "Input",ExpressionUUID->"d7b273ce-0a76-4687-bd6d-21a5e82e5337"], 130 | Cell[CellGroupData[{ 131 | Cell[3235, 97, 246, 4, 30, "Input",ExpressionUUID->"6f77eab7-01a0-4d9e-92c8-2dc5e04684b6"], 132 | Cell[3484, 103, 165, 3, 34, "Output",ExpressionUUID->"f9177644-b18e-465b-b22a-edeb6b4bc37d"] 133 | }, Open ]] 134 | } 135 | ] 136 | *) 137 | 138 | -------------------------------------------------------------------------------- /WSTP/Linux/linux_libWSTP64i4.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/WSTP/Linux/linux_libWSTP64i4.a -------------------------------------------------------------------------------- /WSTP/Linux/linux_wscc: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | ## begin copyright ####################################################### 4 | # 5 | # Mathematica source file 6 | # 7 | # Copyright 1986 through 2012 by Wolfram Research Inc. 8 | # 9 | # Program: wscc - Mathematica MathLink Template Compiler 10 | # Language: Bourne Shell 11 | # 12 | ######################################################### end copyright ## 13 | 14 | wscc_name="Wolfram Mathematica MathLink Template Compiler" 15 | 16 | # Define wscc_verbose here so that it is visible in all the function definitions 17 | wscc_verbose="no" 18 | wscc_verbose_log="/tmp/wscc_verbose_$$.log" 19 | 20 | # find_wstpdk_dir 21 | # The function searches for the path to the MathLink Developer Kit CompilerAdditions directory 22 | # 23 | # Returns: a string with the path to the wscc directory 24 | find_wstpdk_dir() 25 | { 26 | spath=`expr "$0"'/' : '\(/\)[^/]*//*$' \| "$0"'/' : \ 27 | '\(.*[^/]\)//*[^/][^/]*//*$' \| .` 28 | 29 | if [ "." = "$spath" ]; then 30 | WSPATH=`pwd` 31 | else 32 | if [ ".`(echo $spath | grep \"^/\")`" = ".$spath" ]; then 33 | WSPATH="$spath" 34 | else 35 | WSPATH="`pwd`/$spath" 36 | fi 37 | fi 38 | echo ${WSPATH} 39 | } 40 | 41 | 42 | # find_default_C_compiler 43 | # The function uses a list of default C compilers and searches all the 44 | # directories available from $PATH for a compiler 45 | # 46 | # Arguments: 47 | # $1 - The platform ID (the result of `uname`) 48 | # 49 | # Returns: a string containing the path to the discovered C compiler 50 | find_default_C_compiler() 51 | { 52 | if [ "$1" = "Darwin" ] ; then 53 | c_compilers="clang gcc cc" 54 | else 55 | c_compilers="gcc cc" 56 | fi 57 | 58 | path_dirs=`echo $PATH | sed -e 's/:/ /g'` 59 | 60 | for p in ${path_dirs}; do 61 | for c in ${c_compilers} ; do 62 | if [ -x "${p}/${c}" ] ; then 63 | c_compiler="${p}/${c}" 64 | break 65 | fi 66 | done 67 | if [ ! -z "${c_compiler}" ] ; then 68 | break 69 | fi 70 | done 71 | 72 | echo ${c_compiler} 73 | } 74 | 75 | 76 | # find_default_CXX_compiler 77 | # The function uses a list of default C++ compilers and searches all the 78 | # directories available from $PATH for a compiler 79 | # 80 | # Arguments: 81 | # $1 - The platform ID (the result of `uname`) 82 | # 83 | # Returns: a string containing the path to the discovered C++ compiler 84 | find_default_CXX_compiler() 85 | { 86 | if [ "$1" = "Darwin" ] ; then 87 | cxx_compilers="clang++ g++ c++" 88 | else 89 | cxx_compilers="g++ c++ gcc-c++" 90 | fi 91 | 92 | path_dirs=`echo $PATH | sed -e 's/:/ /g'` 93 | 94 | for p in ${path_dirs}; do 95 | for c in ${cxx_compilers} ; do 96 | if [ -x "${p}/${c}" ] ; then 97 | cxx_compiler="${p}/${c}" 98 | break 99 | fi 100 | done 101 | 102 | if [ ! -z "${cxx_compiler}" ] ; then 103 | break 104 | fi 105 | done 106 | 107 | echo ${cxx_compiler} 108 | } 109 | 110 | 111 | # find_compilers 112 | # The function checks $CC and $CXX first for compilers. If 113 | # $CC and/or $CXX are not set the function calls find_default_C_compiler 114 | # and/or find_default_CXX_compiler to locate a suitable compiler 115 | # 116 | # Arguments 117 | # $1 - The platform ID (the result of `uname`) 118 | # $2 - String of either "C" or "C++" indicating which compiler value 119 | # to return 120 | # 121 | # Returns: either a string containing the path to the located C compiler 122 | # or the located C++ compiler 123 | find_compilers() 124 | { 125 | if [ "$2" = "C" ] ; then 126 | if [ ! -z "${CC}" ] ; then 127 | echo ${CC} 128 | else 129 | echo `find_default_C_compiler $1` 130 | fi 131 | elif [ "$2" = "C++" ] ; then 132 | if [ ! -z "${CXX}" ] ; then 133 | echo ${CXX} 134 | else 135 | echo `find_default_CXX_compiler $1` 136 | fi 137 | fi 138 | } 139 | 140 | # Various string values used in the test program and later in this script 141 | bit32="32" 142 | bit64="64" 143 | bit3264="32/64" 144 | 145 | 146 | # get_compilation_flags 147 | # The function uses the test program and stored compiler settings to 148 | # determine the proper compilation flags 149 | # 150 | # Arguments: 151 | # $1 - 32, 64, or 32/64 (as strings) 152 | # $2 - platform id (result of `uname`) 153 | # $3 - architecture id (generated by the test program) 154 | # $4 - compiler id (generated by the test program) 155 | # $5 - the C++ compiler 156 | # $6 - yes or no indicating whether to try all possible OSX architectures 157 | # or just use the default architecture of the currently running system 158 | # 159 | # Returns: a string containing the proper compiler flags for the identified compiler 160 | get_compilation_flags() 161 | { 162 | bitsize=$1 163 | platform=$2 164 | archi=$3 165 | compiler=$4 166 | native_only=$5 167 | 168 | # With Clang, target architecture is set by the -arch flag 169 | if [ "${platform}" = "Darwin" ] ; then 170 | if [ "${native_only}" = "no" ] ; then 171 | if [ ${bitsize} = ${bit32} ] ; then 172 | arch_flags="-arch i386" 173 | elif [ ${bitsize} = ${bit64} ] && [ "${archi}" = "x86_64" ] ; then 174 | arch_flags="-arch x86_64" 175 | fi 176 | fi 177 | else # With gcc, target architecture is set by the -m32 and -m64 flags 178 | if [ ${bitsize} = ${bit32} ] ; then 179 | arch_flags="-m32" 180 | elif [ ${bitsize} = ${bit64} ] && [ "${archi}" = "x86_64" ] ; then 181 | arch_flags="-m64" 182 | fi 183 | fi 184 | echo ${arch_flags} 185 | } 186 | 187 | 188 | # get_linker_libraries 189 | # The function determines the correct system libraries for use in 190 | # linking the MathLink binary 191 | # 192 | # Arguments: 193 | # $1 - platform id (result of `uname`) 194 | # $2 - yes or no indicating whether to use the .a or the .so library 195 | # (currently not valid on OSX) 196 | # $3 - MathLink interface number 197 | # $4 - 32 or 64 (as strings) 198 | # $5 - path to MathLink Developer Kit CompilerAdditions directory 199 | # 200 | # Returns: a string containing the libraries to link into the MathLink program 201 | get_linker_libraries() 202 | { 203 | 204 | platform=$1 205 | dynamic=$2 206 | interface=$3 207 | bittype=$4 208 | wstpdkpath=$5 209 | 210 | if [ "${platform}" = "Darwin" ] ; then 211 | if [ "$dynamic" = "yes" ]; then 212 | libs="-lc++ -F ${wstpdkpath} -framework wstp -framework Foundation" 213 | else 214 | libs="-lc++ ${wstpdkpath}/libWSTPi${interface}.a -framework Foundation" 215 | fi 216 | elif [ "${platform}" = "Linux" ] ; then 217 | if [ ${bittype} = ${bit32} ] ; then 218 | libname="${wstpdkpath}/libWSTP32i${interface}" 219 | elif [ ${bittype} = ${bit64} ] ; then 220 | libname="${wstpdkpath}/libWSTP64i${interface}" 221 | fi 222 | 223 | if [ "${dynamic}" = "yes" ] ; then 224 | libs="${libname}.so -lm -lpthread -lrt -ldl -lstdc++ -luuid" 225 | else 226 | libs="${libname}.a -lm -lpthread -lrt -ldl -lstdc++ -luuid" 227 | fi 228 | elif [ "${platform}" = "CYGWIN" ] ; then 229 | libs="-lstdc++ ${wstpdkpath}/../lib/libWSTP32i${interface}.a -lGdi32" 230 | fi 231 | 232 | echo ${libs} 233 | } 234 | 235 | 236 | # messages with spaces need to be stored with _ instead of the spaces 237 | # for generate_ok_message_awk_script() to correctly put the whole string 238 | # into the awk script. 239 | wscc_ok_compiler_messages=" 240 | -xarch=native_has_been_explicitly_specified 241 | " 242 | 243 | # generate_ok_messages_awk_script 244 | # The function creates an script suitable for parsing compiler output. 245 | # The goal is to swallow harmless compiler warnings from compilers such as 246 | # the Sun Studio compiler that generates things like: 247 | # 248 | # cc: Warning: -xarch=native has been explicitly specified, or implicitly 249 | # specified by a macro option, -xarch=native on this architecture implies 250 | # -xarch=sparcvis which generates code that does not run on pre UltraSPARC 251 | # processors 252 | # 253 | # The warning is perfectly harmless, but there is no good way to disable 254 | # the warning without generating other equally harmless warnings. Instead 255 | # we just swallow the warnings and only spit back warnings/errors that do 256 | # not match the list in ${wscc_ok_compiler_messages} 257 | # 258 | # Arguments 259 | # $1 - the file name that will hold the awk script 260 | generate_ok_messages_awk_script() 261 | { 262 | filename=$1 263 | touch ${filename} 264 | for message in ${wscc_ok_compiler_messages} 265 | do 266 | echo "! /${message}/ { print }" | sed -e 's/_/ /g' >> ${filename} 267 | done 268 | } 269 | 270 | 271 | # compile_files 272 | # The function process the list of input files and compiles them to object files. 273 | # The function can compile either C or C++ source files. 274 | # 275 | # Arguments: 276 | # $1 - the compiler 277 | # $2 - the compiler arguments 278 | # $3 - the source files 279 | # $4 - the file extension to use for the output file. This argument is used 280 | # to support the -P command line flag for wscc that outputs the preprocesses 281 | # source files to files ending in the .i extension 282 | # $5 - the awk script filename 283 | # $6 - yes or no indicating whether to be verbose and report actions. 284 | # $7 - The compiler output log filename 285 | # 286 | # Returns: a list of the compiled object file names 287 | compile_files() 288 | { 289 | compiler=$1 290 | args=$2 291 | files=$3 292 | fileextension=$4 293 | verbose=$5 294 | compile_log=$6 295 | tmp_log="/tmp/wscc_tmp_$$.log" 296 | 297 | for file in ${files} 298 | do 299 | fileroot=`echo ${file} | sed -e 's/\(.*\)\..*/\1/'` 300 | fileoutputname=${fileroot}${fileextension} 301 | 302 | ${compiler} ${args} -o ${fileoutputname} ${file} >> ${tmp_log} 2>&1 303 | 304 | if [ "${verbose}" = "yes" ] ; then 305 | echo "${compiler} ${args} -o ${fileoutputname} ${file}" >> ${compile_log} 306 | cat ${tmp_log} >> ${compile_log} 307 | else 308 | if [ -s ${tmp_log} ] ; then 309 | echo "${file}: `cat ${tmp_log}`" >> ${compile_log} 310 | fi 311 | fi 312 | 313 | rm -rf ${tmp_log} 314 | 315 | if [ -f ${fileoutputname} ] ; then 316 | object_files="${object_files} ${fileoutputname}" 317 | fi 318 | done 319 | echo ${object_files} 320 | } 321 | 322 | 323 | # handle_compiler_log 324 | # The function handles the display of the compiler log file 325 | # 326 | # Arguments: 327 | # $1 - the compiler log file name. 328 | # $2 - the awk file name 329 | handle_compiler_log() 330 | { 331 | logfile=$1 332 | awkfile=$2 333 | if [ -s ${logfile} ] ; then 334 | cat ${logfile} | awk -f ${awkfile} 335 | rm -rf ${logfile} 336 | fi 337 | } 338 | 339 | 340 | # wscc_usage 341 | # The function generates a usage message for wscc 342 | wscc_usage() 343 | { 344 | cat <&1 | awk -f ${wscc_awk_message_file} 765 | 766 | # Remove the awk script 767 | rm -rf ${wscc_awk_message_file} 768 | 769 | # Clean up leftover files if necessary 770 | if [ "${wscc_debug_compile}" = "no" ] ; then 771 | if [ ! -z "${wscc_tm_c_file}" ] ; then 772 | rm -rf ${wscc_tm_c_file} 773 | fi 774 | 775 | for file in ${wscc_c_object_files} 776 | do 777 | rm -rf ${file} 778 | done 779 | 780 | for file in ${wscc_cxx_object_files} 781 | do 782 | rm -rf ${file} 783 | done 784 | fi 785 | 786 | -------------------------------------------------------------------------------- /WSTP/Linux/linux_wsprep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/WSTP/Linux/linux_wsprep -------------------------------------------------------------------------------- /WSTP/MacOS-ARM/macos_libWSTPi4.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/WSTP/MacOS-ARM/macos_libWSTPi4.a -------------------------------------------------------------------------------- /WSTP/MacOS-ARM/macos_wsprep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/WSTP/MacOS-ARM/macos_wsprep -------------------------------------------------------------------------------- /WSTP/MacOS/macos_libWSTPi4.36.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/WSTP/MacOS/macos_libWSTPi4.36.a -------------------------------------------------------------------------------- /WSTP/MacOS/macos_wsprep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/WSTP/MacOS/macos_wsprep -------------------------------------------------------------------------------- /WSTP/Windows/windows_wsprep.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/WSTP/Windows/windows_wsprep.exe -------------------------------------------------------------------------------- /WSTP/Windows/windows_wstp32i4.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/WSTP/Windows/windows_wstp32i4.lib -------------------------------------------------------------------------------- /WSTP/Windows/windows_wstp32i4m.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/WSTP/Windows/windows_wstp32i4m.lib -------------------------------------------------------------------------------- /WSTP/Windows/windows_wstp32i4s.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/WSTP/Windows/windows_wstp32i4s.lib -------------------------------------------------------------------------------- /WSTP/Windows/windows_wstp64i4.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/WSTP/Windows/windows_wstp64i4.lib -------------------------------------------------------------------------------- /WSTP/Windows/windows_wstp64i4m.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/WSTP/Windows/windows_wstp64i4m.lib -------------------------------------------------------------------------------- /WSTP/Windows/windows_wstp64i4s.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/WSTP/Windows/windows_wstp64i4s.lib -------------------------------------------------------------------------------- /WSTP/Windows/wstp32i4.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/WSTP/Windows/wstp32i4.dll -------------------------------------------------------------------------------- /WSTP/Windows/wstp64i4.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QTechTheory/QuESTlink/be7d396524a50235e880b3a4b9e9b3f53e78221c/WSTP/Windows/wstp64i4.dll -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | 2 | # This makefile builds the QuEST library and links QuESTlink. 3 | # While attempting to accomodate as many platforms and compilers, 4 | # unforeseen problems are inevitable: please email tyson.jones@materials.ox.ac.uk 5 | # about any errors or complications, or raise an issue on Github. 6 | # This makefile is a small change to that created by Tyson Jones for QuEST, 7 | # which in turn is based off the makefile by Ania Brown for QuEST. 8 | 9 | #======================================================================# 10 | # # 11 | # User settings # 12 | # # 13 | #======================================================================# 14 | 15 | # operating system, one of {MACOS, LINUX, WINDOWS} 16 | OS = LINUX 17 | 18 | # compiler to use, which should support both C and C++, to be wrapped by GPU/MPI compilers 19 | # this is likely to be one of {g++, clang, ic, cl} 20 | COMPILER = g++ 21 | 22 | # type of above compiler, one of {GNU, INTEL, CLANG, MSVC}, used for setting compiler flags 23 | COMPILER_TYPE = GNU 24 | 25 | # only for WINDOWS: whether OS is 32-bit (x86) or 64-bit (x64). Choose {32, 64} 26 | WINDOWS_ARCH = 64 27 | 28 | # only for MACOS: whether OS runs on Intel (x64) or Apple Silicon (ARM). Choose {x64, ARM} 29 | MACOS_ARCH = x64 30 | 31 | # hardwares to target: 1 means use, 0 means don't use 32 | MULTITHREADED = 0 33 | GPUACCELERATED = 0 34 | 35 | # GPU hardware dependent, lookup at https://developer.nvidia.com/cuda-gpus, write without fullstop 36 | GPU_COMPUTE_CAPABILITY = 61 37 | 38 | # whether to use single, double or quad floating point precision in the state-vector {1,2,4} 39 | PRECISION = 2 40 | 41 | # whether to suppress the below warnings about compiler compatibility 42 | SUPPRESS_WARNING = 0 43 | 44 | 45 | 46 | #======================================================================# 47 | # # 48 | # Constants # 49 | # # 50 | #======================================================================# 51 | 52 | # name of the executable to create 53 | EXE = quest_link 54 | 55 | # space-separated names (no file type) of all user source files (.c or .cpp) in the root directory 56 | SOURCES = link templates.tm 57 | 58 | # path to QuEST library from root directory 59 | QUEST_DIR = QuEST/QuEST 60 | 61 | # path to WSTP libs from root directory 62 | WSTP_DIR = WSTP 63 | 64 | # path to QuESTlink code from root directory 65 | LINK_DIR = Link 66 | 67 | # wrapper compiler for GPU accel 68 | CUDA_COMPILER = nvcc 69 | 70 | 71 | 72 | #======================================================================# 73 | # # 74 | # Checking user settings # 75 | # # 76 | #======================================================================# 77 | 78 | # suppresses all non-gcc output, useful for calling scripts 79 | SILENT = 0 80 | 81 | # always allow cleaning without errors or warnings 82 | ifneq ($(MAKECMDGOALS), clean) 83 | ifneq ($(MAKECMDGOALS), veryclean) 84 | ifneq ($(SILENT), 1) 85 | 86 | # check $OS is correct 87 | ifneq ($(OS), LINUX) 88 | ifneq ($(OS), MACOS) 89 | ifneq ($(OS), WINDOWS) 90 | $(error OS must be LINUX, MACOS or WINDOWS) 91 | endif 92 | endif 93 | endif 94 | 95 | # check $COMPILER_TYPE is correct 96 | ifneq ($(COMPILER_TYPE), CLANG) 97 | ifneq ($(COMPILER_TYPE), GNU) 98 | ifneq ($(COMPILER_TYPE), INTEL) 99 | ifneq ($(COMPILER_TYPE), MSVC) 100 | $(error COMPILER_TYPE must be one of CLANG, GNU, INTEL or MSVC) 101 | endif 102 | endif 103 | endif 104 | endif 105 | 106 | # distributed GPU not supported 107 | ifeq ($(DISTRIBUTED), 1) 108 | ifeq ($(GPUACCELERATED), 1) 109 | $(error Distributed GPU acceleration not supported) 110 | endif 111 | endif 112 | 113 | # GPU doesn't use threading 114 | ifeq ($(MULTITHREADED), 1) 115 | ifeq ($(GPUACCELERATED), 1) 116 | $(warning GPU acceleration makes no use of multithreading. Disabling the latter...) 117 | override MULTITHREADED = 0 118 | endif 119 | endif 120 | 121 | # CLANG compilers don't support threading 122 | ifeq ($(MULTITHREADED), 1) 123 | ifeq ($(COMPILER_TYPE), CLANG) 124 | $(warning Clang does not support multithreading. Disabling...) 125 | override MULTITHREADED = 0 126 | endif 127 | endif 128 | 129 | # check PRECISION is valid 130 | ifneq ($(PRECISION), 1) 131 | ifneq ($(PRECISION), 2) 132 | ifneq ($(PRECISION), 4) 133 | $(error PRECISION must be set to 1, 2 or 4) 134 | endif 135 | endif 136 | endif 137 | 138 | # GPU does not support quad precision 139 | ifeq ($(PRECISION), 4) 140 | ifeq ($(GPUACCELERATED), 1) 141 | $(warning GPUs do not support quad precision. Setting PRECISION=2...) 142 | override PRECISION = 2 143 | endif 144 | endif 145 | 146 | # NVCC doesn't support new CLANG compilers 147 | ifeq ($(GPUACCELERATED), 1) 148 | ifeq ($(COMPILER_TYPE), CLANG) 149 | ifeq ($(SUPPRESS_WARNING), 0) 150 | $(info Some versions of Clang are not NVIDIA-GPU compatible. If compilation fails, try Clang 3.7) 151 | endif 152 | endif 153 | endif 154 | 155 | # NVCC doesn't support GNU compilers on OSX 156 | ifeq ($(GPUACCELERATED), 1) 157 | ifeq ($(COMPILER_TYPE), GNU) 158 | ifeq ($(SUPPRESS_WARNING), 0) 159 | $(info On some platforms (e.g. OSX), NVIDIA-GPUs are not compatible with GNU compilers. If compilation fails, try an alternative compiler, like Clang 3.7) 160 | endif 161 | endif 162 | endif 163 | 164 | # Windows users must set WINDOWS_ARCH as {32, 64} 165 | ifeq ($(OS), WINDOWS) 166 | ifneq ($(WINDOWS_ARCH), 32) 167 | ifneq ($(WINDOWS_ARCH), 64) 168 | $(error When compiling on WINDOWS, WINDOWS_ARCH must be 32 or 64) 169 | endif 170 | endif 171 | endif 172 | 173 | ifeq ($(OS), MACOS) 174 | ifneq ($(MACOS_ARCH), x64) 175 | ifneq ($(MACOS_ARCH), ARM) 176 | $(error When compiling on MACOS, MACOS_ARCH must be x64 or ARM) 177 | endif 178 | endif 179 | endif 180 | 181 | # end of allowed cleaning 182 | endif 183 | endif 184 | endif 185 | 186 | 187 | 188 | #======================================================================# 189 | # # 190 | # Compilation # 191 | # # 192 | #======================================================================# 193 | 194 | # 195 | # --- WSTP path 196 | # 197 | 198 | ifeq ($(OS), WINDOWS) 199 | WSTP_SRC_DIR = $(WSTP_DIR)/Windows 200 | else ifeq ($(OS), MACOS) 201 | ifeq ($(MACOS_ARCH), x64) 202 | WSTP_SRC_DIR = $(WSTP_DIR)/MacOS 203 | else ifeq ($(MACOS_ARCH), ARM) 204 | WSTP_SRC_DIR = $(WSTP_DIR)/MacOS-ARM 205 | endif 206 | else ifeq ($(OS), LINUX) 207 | WSTP_SRC_DIR = $(WSTP_DIR)/Linux 208 | endif 209 | 210 | 211 | 212 | # 213 | # --- libraries 214 | # 215 | 216 | ifeq ($(OS), MACOS) 217 | ifeq ($(MACOS_ARCH), x64) 218 | LIBS = -lm -lc++ -lstdc++ $(WSTP_SRC_DIR)/MACOS_libWSTPi4.36.a 219 | else ifeq ($(MACOS_ARCH), ARM) 220 | LIBS = -lm -lc++ -lstdc++ $(WSTP_SRC_DIR)/macos_libWSTPi4.a 221 | endif 222 | ifeq ($(GPUACCELERATED), 0) 223 | LIBS := $(LIBS) -framework Foundation 224 | endif 225 | else ifeq ($(OS), WINDOWS) 226 | LIBS = kernel32.lib user32.lib gdi32.lib $(WSTP_SRC_DIR)/windows_wstp$(WINDOWS_ARCH)i4.lib $(WSTP_SRC_DIR)/windows_wstp$(WINDOWS_ARCH)i4m.lib $(WSTP_SRC_DIR)/windows_wstp$(WINDOWS_ARCH)i4s.lib 227 | else ifeq ($(OS), LINUX) 228 | LIBS = -lm -ldl -lutil -lpthread -luuid -lrt -lstdc++ $(WSTP_SRC_DIR)/linux_libWSTP64i4.a 229 | ifeq ($(GPUACCELERATED), 0) 230 | LIBS := -Wl,--no-as-needed $(LIBS) 231 | endif 232 | endif 233 | 234 | 235 | 236 | # 237 | # --- QuEST source and include paths 238 | # 239 | 240 | QUEST_INCLUDE_DIR = ${QUEST_DIR}/include 241 | QUEST_SRC_DIR = ${QUEST_DIR}/src 242 | 243 | QUEST_COMMON_DIR = $(QUEST_SRC_DIR) 244 | ifeq ($(GPUACCELERATED), 1) 245 | QUEST_INNER_DIR = $(QUEST_SRC_DIR)/GPU 246 | else 247 | QUEST_INNER_DIR = $(QUEST_SRC_DIR)/CPU 248 | endif 249 | QUESTLINK_INCLUDE = -I${QUEST_INCLUDE_DIR} -I$(QUEST_INNER_DIR) -I$(QUEST_COMMON_DIR) -I$(WSTP_SRC_DIR) -I$(LINK_DIR) 250 | 251 | 252 | 253 | # 254 | # --- compiler flags 255 | # 256 | 257 | # threading flag 258 | ifeq ($(MULTITHREADED), 1) 259 | ifeq ($(COMPILER_TYPE), GNU) 260 | THREAD_FLAGS = -fopenmp 261 | else ifeq ($(COMPILER_TYPE), INTEL) 262 | THREAD_FLAGS = -qopenmp 263 | else ifeq ($(COMPILER_TYPE), MSVC) 264 | THREAD_FLAGS = -openmp 265 | endif 266 | else 267 | THREAD_FLAGS = 268 | endif 269 | 270 | # windows architecture flag 271 | ifeq ($(WINDOWS_ARCH), 32) 272 | ARCH_FLAG = X86 273 | else 274 | ARCH_FLAG = X64 275 | endif 276 | 277 | AVX = -mavx 278 | # ARM doesn't have AVX 279 | ifeq ($(OS), MACOS) 280 | ifeq ($(MACOS_ARCH), ARM) 281 | AVX = 282 | endif 283 | endif 284 | 285 | # c 286 | C_CLANG_FLAGS = -O2 -std=c99 $(AVX) -Wall -DQuEST_PREC=$(PRECISION) 287 | C_GNU_FLAGS = -O2 -std=c99 $(AVX) -Wall -DQuEST_PREC=$(PRECISION) $(THREAD_FLAGS) 288 | C_INTEL_FLAGS = -O2 -std=c99 -fprotect-parens -Wall -xAVX -axCORE-AVX2 -diag-disable -cpu-dispatch -DQuEST_PREC=$(PRECISION) $(THREAD_FLAGS) 289 | C_MSVC_FLAGS = -O2 -EHs -DQuEST_PREC=$(PRECISION) $(THREAD_FLAGS) -nologo -DDWIN$(WINDOWS_ARCH) -D_WINDOWS -Fo$@ 290 | 291 | # c++ 292 | CPP_CLANG_FLAGS = -O2 -std=c++11 $(AVX) -Wall -DQuEST_PREC=$(PRECISION) 293 | CPP_GNU_FLAGS = -O2 -std=c++11 $(AVX) -Wall -DQuEST_PREC=$(PRECISION) $(THREAD_FLAGS) 294 | CPP_INTEL_FLAGS = -O2 -std=c++11 -fprotect-parens -Wall -xAVX -axCORE-AVX2 -diag-disable -cpu-dispatch -DQuEST_PREC=$(PRECISION) $(THREAD_FLAGS) 295 | CPP_MSVC_FLAGS = -O2 -EHs -std:c++latest -DQuEST_PREC=$(PRECISION) $(THREAD_FLAGS) -nologo -DDWIN$(WINDOWS_ARCH) -D_WINDOWS -Fo$@ 296 | 297 | # wrappers 298 | CPP_CUDA_FLAGS := -O2 -std=c++11 -arch=compute_$(GPU_COMPUTE_CAPABILITY) -code=sm_$(GPU_COMPUTE_CAPABILITY) -DQuEST_PREC=$(PRECISION) 299 | 300 | # choose c/c++ flags based on compiler type 301 | ifeq ($(COMPILER_TYPE), CLANG) 302 | C_FLAGS = $(C_CLANG_FLAGS) 303 | CPP_FLAGS = $(CPP_CLANG_FLAGS) 304 | else ifeq ($(COMPILER_TYPE), GNU) 305 | C_FLAGS = $(C_GNU_FLAGS) 306 | CPP_FLAGS = $(CPP_GNU_FLAGS) 307 | else ifeq ($(COMPILER_TYPE), INTEL) 308 | C_FLAGS = $(C_INTEL_FLAGS) 309 | CPP_FLAGS = $(CPP_INTEL_FLAGS) 310 | else ifeq ($(COMPILER_TYPE), MSVC) 311 | C_FLAGS = $(C_MSVC_FLAGS) 312 | CPP_FLAGS = $(CPP_MSVC_FLAGS) 313 | 314 | # must specify machine type on Windows 315 | CPP_CUDA_FLAGS := $(CPP_CUDA_FLAGS) -m=$(WINDOWS_ARCH) -DDWIN$(WINDOWS_ARCH) 316 | endif 317 | 318 | 319 | 320 | # 321 | # --- compiler mode and linker flags 322 | # 323 | 324 | ifeq ($(COMPILER_TYPE), MSVC) 325 | C_MODE = 326 | LINKER = link.exe 327 | LINK_FLAGS := -SUBSYSTEM:WINDOWS -nologo -MACHINE:$(ARCH_FLAG) $(THREAD_FLAGS) 328 | 329 | # must forward linker flags from NVCC to link.exe on Windows 330 | ifeq ($(GPUACCELERATED), 1) 331 | LINK_FLAGS := -o $(EXE).exe $(foreach option, $(LINK_FLAGS), -Xlinker $(option)) 332 | else 333 | LINK_FLAGS := -out:$(EXE).exe $(LINK_FLAGS) 334 | endif 335 | else 336 | C_MODE = -x c 337 | LINKER = $(COMPILER) 338 | LINK_FLAGS := -o $(EXE) $(THREAD_FLAGS) 339 | endif 340 | 341 | 342 | 343 | # 344 | # --- targets 345 | # 346 | 347 | OBJ = QuEST.o QuEST_validation.o QuEST_common.o QuEST_qasm.o mt19937ar.o 348 | OBJ += extensions.o circuits.o derivatives.o errors.o decoders.o utilities.o 349 | ifeq ($(GPUACCELERATED), 1) 350 | OBJ += QuEST_gpu.o 351 | else 352 | OBJ += QuEST_cpu.o QuEST_cpu_local.o 353 | endif 354 | OBJ += $(addsuffix .o, $(SOURCES)) 355 | 356 | 357 | 358 | # 359 | # --- rules 360 | # 361 | 362 | # GPU (CUDA) 363 | ifeq ($(GPUACCELERATED), 1) 364 | 365 | # final -o to force NVCC to use '.o' extension even on Windows 366 | %.o: %.cu 367 | $(CUDA_COMPILER) -dc $(CPP_CUDA_FLAGS) -ccbin $(COMPILER) $(QUESTLINK_INCLUDE) -o $@ $< 368 | %.o: $(LINK_DIR)/%.cu 369 | $(CUDA_COMPILER) -dc $(CPP_CUDA_FLAGS) -ccbin $(COMPILER) $(QUESTLINK_INCLUDE) -o $@ $< 370 | %.o: $(QUEST_INNER_DIR)/%.cu 371 | $(CUDA_COMPILER) -dc $(CPP_CUDA_FLAGS) -ccbin $(COMPILER) $(QUESTLINK_INCLUDE) -o $@ $< 372 | 373 | endif 374 | 375 | # CPU (C) 376 | %.o: %.c 377 | $(COMPILER) $(C_MODE) $(C_FLAGS) $(QUESTLINK_INCLUDE) -c $< 378 | %.o: $(QUEST_INNER_DIR)/%.c 379 | $(COMPILER) $(C_MODE) $(C_FLAGS) $(QUESTLINK_INCLUDE) -c $< 380 | %.o: $(QUEST_COMMON_DIR)/%.c 381 | $(COMPILER) $(C_MODE) $(C_FLAGS) $(QUESTLINK_INCLUDE) -c $< 382 | 383 | # CPU (C++) 384 | %.o: %.cpp templates.tm.cpp 385 | $(COMPILER) $(CPP_FLAGS) $(QUESTLINK_INCLUDE) -c $< 386 | %.o: $(QUEST_INNER_DIR)/%.cpp 387 | $(COMPILER) $(CPP_FLAGS) -c $< 388 | %.o: $(LINK_DIR)/%.cpp 389 | $(COMPILER) $(CPP_FLAGS) $(QUESTLINK_INCLUDE) -c $< 390 | 391 | 392 | 393 | # 394 | # --- linking 395 | # 396 | 397 | # CUDA 398 | ifeq ($(GPUACCELERATED), 1) 399 | 400 | # a dirty hack to silence cl when NVCC linking 401 | # (https://stackoverflow.com/questions/61178458/force-nvcc-straight-to-linking-phase) 402 | SHUTUP := 403 | ifeq ($(COMPILER_TYPE), MSVC) 404 | SHUTUP := -Xcompiler 2>nul: 405 | endif 406 | 407 | all: $(OBJ) 408 | $(CUDA_COMPILER) $(SHUTUP) $(CPP_CUDA_FLAGS) -ccbin $(COMPILER) $(OBJ) $(LIBS) $(LINK_FLAGS) 409 | 410 | # C and C++ 411 | else 412 | 413 | default: $(EXE) 414 | $(EXE): $(OBJ) 415 | $(LINKER) $(OBJ) $(LIBS) $(LINK_FLAGS) 416 | 417 | endif 418 | 419 | 420 | 421 | # 422 | # --- generate C code from MMA templates 423 | # 424 | 425 | ifeq ($(OS), MACOS) 426 | PREP = MACOS_wsprep 427 | else ifeq ($(OS), LINUX) 428 | PREP = linux_wsprep 429 | else ifeq ($(OS), WINDOWS) 430 | PREP = windows_wsprep.exe 431 | endif 432 | 433 | templates.tm.cpp: 434 | $(WSTP_SRC_DIR)/$(PREP) $(LINK_DIR)/templates.tm -o templates.tm.cpp 435 | 436 | 437 | 438 | # 439 | # --- clean 440 | # 441 | 442 | # resolve os remove command 443 | ifeq ($(OS), MACOS) 444 | REM = /bin/rm -f 445 | EXE_FN = $(EXE) 446 | else ifeq ($(OS), LINUX) 447 | REM = /bin/rm -f 448 | EXE_FN = $(EXE) 449 | else ifeq ($(OS), WINDOWS) 450 | REM = del 451 | EXE_FN = $(EXE).exe 452 | endif 453 | 454 | 455 | # define tidy cmds 456 | .PHONY: tidy clean veryclean 457 | tidy: 458 | $(REM) *.o *.lib *.exp 459 | $(REM) templates.tm.cpp 460 | clean: tidy 461 | $(REM) $(EXE_FN) 462 | veryclean: clean 463 | $(REM) *.h~ *.c~ makefile~ 464 | 465 | 466 | 467 | # 468 | # --- debug 469 | # 470 | 471 | print-%: 472 | @echo $*=$($*) 473 | 474 | getvalue-%: 475 | @echo $($*) 476 | 477 | 478 | 479 | --------------------------------------------------------------------------------