Browse Source

Started rewrite with Python

Adam Kelly 5 years ago
parent
commit
0030c67a3c

+ 3 - 10
.editorconfig

@@ -1,16 +1,9 @@
 root = true
 
 [*]
-end_of_line = lf
-charset = utf-8
-trim_trailing_whitespace = true
-insert_final_newline = true
 indent_style = space
 indent_size = 4
-
-[*.md]
+end_of_line = lf
+charset = utf-8
 trim_trailing_whitespace = false
-
-[Makefile]
-indent_style = tab
-indent_size = 4
+insert_final_newline = false

+ 258 - 3
.gitignore

@@ -1,3 +1,258 @@
-/target
-**/*.rs.bk
-Cargo.lock
+
+# Created by https://www.gitignore.io/api/c,c++,cuda,linux,macos,windows
+
+### C ###
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
+
+### C++ ###
+# Prerequisites
+
+# Compiled Object files
+*.slo
+
+# Precompiled Headers
+
+# Compiled Dynamic libraries
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+
+# Executables
+
+### CUDA ###
+*.i
+*.ii
+*.gpu
+*.ptx
+*.cubin
+*.fatbin
+
+### Linux ###
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+.vscode
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+
+# pytest
+.pytest_cache
+
+# End of https://www.gitignore.io/api/c,c++,cuda,linux,macos,windows

+ 0 - 29
.travis.yml

@@ -1,29 +0,0 @@
-language: rust
-rust:
-  - stable
-  - beta
-  - nightly
-matrix:
-  allow_failures:
-    - rust: nightly
-os: osx
-sudo: false
-before_install:
-    # - brew install libquantum --devel
-    - cargo update
-after_success:
-    - cargo install mdbook
-    - cargo doc
-    - mkdir -p pages/docs pages/book
-    - cp -R target/doc pages/
-    - cd book && mdbook build
-    - cp -R  book ../pages/
-deploy:
-  provider: pages
-  skip-cleanup: true
-  github-token: $GITHUB_TOKEN  # Set in travis-ci.org dashboard, marked secure
-  keep-history: true
-  local-dir: pages
-  on:
-    branch: master
-    rust: stable

+ 0 - 31
Cargo.toml

@@ -1,31 +0,0 @@
-[package]
-name = "qcgpu"
-version = "1.0.0"
-authors = ["Adam Kelly <adamkelly2201@gmail.com>"]
-documentation = "https://qcgpu.github.io/qcgpu-rust/doc/qcgpu/index.html"
-homepage = "https://qcgpu.github.io"
-readme = "./README.md"
-keywords = ["quantum", "computation", "gpu", "simulation"]
-license = "MIT"
-exclude = ["examples/**/*"]
-
-[badges]
-travis-ci = { repository = "qcgpu/qcgpu-rust", branch = "master" }
-maintenance = { status = "actively-developed" }
-
-[dependencies]
-num-complex = "0.1.0"
-ocl = "0.18.0"
-rand = "0.5.1"
-failure = "0.1.1"
-failure_derive = "0.1.1"
-
-[[example]]
-name = "bell"
-doc = false
-path = "examples/bell-state.rs"
-
-[[example]]
-name = "super-dense"
-doc = false
-path = "examples/super-dense.rs"

+ 0 - 49
NOTES.md

@@ -1,49 +0,0 @@
-# Notes
-
-> This document is to keep track of various ideas, plans ect. relating
-> to the development of QCGPU.
-
-## Simulators
-
-At a bare minimum, a quantum computer simulator must have the following parts:
-
-* Ability to create a quantum register with a given numebr of qubits
-* Ability to apply controlled gates, or at minimum the controlled pauli-x gate (CNOT)
-* Ability to apply single qubit gates, which combined with the CNOT form a universal set of gates.
-* Ability to measure single qubits in the register and collapse their state into the measured state.
-
-Other functionality can be added which will make the simulator more useful, which is discussed later
-
-## Alternative Simulator Architectures
-
-* Could try using the Feynmann path integral formulation of quantum mechanics
-* Could use CUDA, Apple's Metal compute and standard Rust
-
-## Installing on EC2
-
-To run this on a p3.2xlarge instance, you will need to do the following:
-
-```bash
-sudo yum update -y
-sudo yum install git -y
-sudo yum groupinstall -y "Development tools"
-sudo yum install -y kernel-devel-`uname -r`
-wget https://developer.nvidia.com/compute/cuda/9.1/Prod/local_installers/cuda_9.1.85_387.26_linux
-chmod +x cuda_9.1.85_387.26_linux
-
-# MUST RUN MANUALLY ================
-./cuda_9.1.85_387.26_linux
-#===================================
-
-sudo nvidia-smi -pm 1
-sudo nvidia-smi -acp 0
-sudo nvidia-smi --auto-boost-permission=0
-sudo nvidia-smi -ac 2505,875
-
-curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly -y
-
-source $HOME/.cargo/env
-
-git clone https://github.com/QCGPU/qcgpu-rust
-cd qcgpu-rust
-```

+ 2 - 70
README.md

@@ -1,71 +1,3 @@
-<h1 align="center">QCGPU</h1>
-<div align="center">Open Source, High Performance & Hardware Accelerated - Quantum Computer Simulation Library</div>
-
-
-<div align="center">
-	<a href="https://qcgpu.github.io/qcgpu-rust/book/getting-started.html">Getting Started</a>
-    |
-    <a href="https://qcgpu.github.io/qcgpu-rust/book/">User Guide</a>
-    |
-    <a href="https://qcgpu.github.io/qcgpu-rust/doc/qcgpu/index.html">Documentation</a>
-    |
-    <a href="https://qcgpu.github.io/qcgpu-rust/book/algorithms/algorithms.html">Algorithms</a>
-</div>
-
-<div align="center">
-	<a href="https://travis-ci.org/QCGPU/qcgpu-rust">
-        <img src="https://travis-ci.org/QCGPU/qcgpu-rust.svg?branch=master" alt="Travis-CI">
-    </a>
-    <a href="https://crates.io/crates/qcgpu">
-        <img src="https://img.shields.io/crates/v/qcgpu.svg" alt="Crates.io">
-    </a>
-</div>
-
-The goal of QCGPU is to provide a library for the simulation of 
-quantum computers that is fast, efficient and portable.
-
-QCGPU is written in Rust and uses OpenCL to run code on the CPU, GPU or any other OpenCL supported devices.
-This library is meant to be used both independently and alongside 
-established tools for example compilers or more general and high level frameworks.
-If you are interested in using QCGPU with IBM's [QISKit](http://qiskit.org/) 
-framework or [QISKit ACQUA](https://qiskit.org/acqua/), please see 
-the repository [qiskit-addon-qcgpu](https://github.com/qcgpu/qiskit-addon-qcgpu).
-
-## Prerequisites
-
-To use this library, you will need two things:
-
-* OpenCL (Ensure that an OpenCL library is installed for your platform and that `clinfo` or some other diagnostic command will run).
-* Rust (install [here](https://www.rustup.rs)). Please use a nightly build.
-
-## Usage
-
-First, add the crate to `cargo.toml`
-
-```toml
-[dependencies]
-qcgpu = 1.0.0 
-```
-
-Then use the crate!
-
-```rust
-extern crate qcgpu;
-use qcgpu::State;
-
-fn main() {
-    let mut state = State::new(2, 0).unwrap();
-    state.x(0);
-
-    println!("Measured: {}", state.measure().unwrap());
-    // 1
-}
-```
-
-## License
-
-This software is licensed under the MIT licence (see `LICENSE`)
-
-Copyright (c) 2018 Adam Kelly
-
+# QCGPU
 
+> The Python Edition

File diff suppressed because it is too large
+ 104 - 0
Testing.ipynb


+ 0 - 1
book/.gitignore

@@ -1 +0,0 @@
-book

+ 0 - 9
book/book.toml

@@ -1,9 +0,0 @@
-[book]
-authors = ["Adam Kelly <adamkelly2201@gmail.com>"]
-description = "The user guide / prose documentation for QCGPU"
-title = "QCGPU User Guide"
-multilingual = false
-src = "src"
-
-[output.html]
-mathjax-support = true

+ 0 - 16
book/src/SUMMARY.md

@@ -1,16 +0,0 @@
-# Summary
-
-- [QCGPU](./qcgpu.md)
-    - [Getting Started](./getting-started.md)
-- [User Guide](./user-guide/user-guide.md)
-    - [Quantum Registers](./user-guide/registers.md)
-    - [Quantum Gates](./user-guide/gates.md)
-    - [Quantum Operations](./user-guide/operations.md)
-    - [Examples](./user-guide/examples.md)
-    - [Decoherence](./user-guide/decoherence.md)
-- [Algorithms](./algorithms/algorithms.md)
-    - [Bernstein-Vazirani](./algorithms/bernstein-vazirani.md)
-    - [Deutsch-Jozsa](./algorithms/deutsch-jozsa.md)
-    - [Grovers](./algorithms/grover.md)
-    - [Shors](./algorithms/shor.md)
-    - [Super Dense Coding](./algorithms/super-dense.md)

+ 0 - 5
book/src/algorithms/algorithms.md

@@ -1,5 +0,0 @@
-# Algorithms
-
-The following chapter details some implementations of non trivial quantum algorithms. They are based on the source code from [libquantum](http://www.libquantum.de/) along with the lecture notes from [the University of Waterloo](https://cs.uwaterloo.ca/~watrous/LectureNotes.html).
-
-Note that some implementations may not yet be complete.

+ 0 - 61
book/src/algorithms/bernstein-vazirani.md

@@ -1,61 +0,0 @@
-# Bernstein-Vazirani Algorithm
-
-
-This algorithm finds a hidden integer \\(a \in \{ 0, 1\}^n\\)from
-an oracle \\(f_a\\)which returns a bit \\(a \cdot x \equiv \sum_i a_i x_i \mod 2\\)
-for an input \\(x \in \{0,1\}^n\\).
-
-A classical oracle returns \\(f_a(x) = a \dot x \mod 2\\), while the quantum oracle
-must be queried with superpositions of input \\(x\\)'s.
-
-To solve this problem classically, the hidden integer can be found by checking the
-oracle with the inputs \\(x = 1,2,\dots,2^i,2^{n-1}\\), where each
-query reveals the \\(i\\)th bit of \\(a\\) (\\(a_i\\)).
-This is the optimal classical solution, and is O(n). Using a quantum oracle and the
-Bernstein-Vazirani algorithm, \\(a\\) can be found with just one query to the oracle.
-
-## The Algorithm
-
-1. Initialize \\(n\\) qubits in the state \\(\lvert 0, \dots, 0\rangle\\).
-2. Apply the Hadamard gate \\(H\\) to each qubit.
-3. Apply the inner product oracle.
-4. Apply the Hadamard gate \\(H\\) to each qubit.
-5. Measure the register
-
-From this procedure, we find that the registers measured value is equal to that of
-the original hidden integer.
-
-```rust
-extern crate qcgpu;
-
-use qcgpu::State;
-use qcgpu::gates::h;
-
-fn main() {
-    let num_qubits = 16; // Number of qubits to use
-    let a = 101; // Hidden integer, bitstring is 1100101
-
-    // You should also make sure that a is representable with n qubits,
-    // by settings a as a mod 2^n.
-
-    // Bernstein-Vazirani algorithm
-    let mut state = State::new(num_qubits, 1); // New quantum register, using the GPU.
-
-    // Apply a hadamard gate to each qubit
-    state.apply_all(h());
-
-    // Apply the inner products oracle
-    for i in 0..num_qubits {
-        if a & (1 << i) != 0 {
-            state.z(i as i32);
-        }
-        // Otherwise should apply identity gate, but computationally this doens't change the state.
-    }
-
-    // Apply hadamard gates before measuring
-    state.apply_all(h());
-
-    println!("Measurement Results: {:?}", state.measure_many(1000));
-    // Measurement Results: {"0000000001100101": 1000}
-}
-```

+ 0 - 76
book/src/algorithms/deutsch-jozsa.md

@@ -1,76 +0,0 @@
-# Deutsch-Jozsa Algorithm
-
-This algorithm was the first to show that quantum computers could have a speedup over classical computers.
-
-Consider a function \\(f(x)\\) which takes an input of an \\(n\\)-bit string \\(x\\) and returns 0 or 1.
-
-Suppose that \\(f(x)\\) is either a **constant** function that has the same value \\(c \in \{0, 1\},  \forall x\\), or a **balanced** function, where the value is 0 for half of the inputs, and 1 for the other half.
-
-The Deutsch-Jozsa problem is to find whether \\(f\\) is *constant* or *balanced*, in as few function evaluations as possible.
-
-Using classical computing, in the worst case, this requires \\(2^{n-1}+1\\) function evaluations.
-Using quantum computing, this can be done with just one function evaluation.
-
-The function \\(f\\), to be used in a quantum computer, must be specified by an oracle circuit \\(U_{f}\\) such that \\(U_{f} \lvert x \rangle = (-1)^{f(x)}\lvert x \rangle\\).
-
-## The Algorithm
-
-1. Initialize \\(n\\) qubits in the state \\(\lvert 0, \dots, 0\rangle\\).
-2. Apply the Hadamard gate \\(H\\) to each qubit.
-3. Apply the oracle circuit \\(U_f\\).
-4. Apply the Hadamard gate \\(H\\) to each qubit.
-5. Measure each qubit. Let \\(y = (y_{1}, \dots, y_{n})\\) be the list of measurement outcomes.
-
-From this procedure, we find that \\(f\\) is constant if \\(y\\) is the all zero string.
-
-```rust
-extern crate qcgpu;
-
-use qcgpu::State;
-use qcgpu::gates::h;
-
-fn main() {
-    // 3 qubits, f(x) = x_0 NOT x_1 x_2
-    // Balanced
-    let mut balanced_state = State::new(3, 1);
-
-    balanced_state.apply_all(h());
-
-    // Oracle U_f
-    balanced_state.h(2);
-    balanced_state.z(0);
-    balanced_state.cx(1, 2);
-    balanced_state.h(2);
-
-    balanced_state.apply_all(h());
-
-    println!(
-        "{}",
-        if balanced_state.measure() == 0 {
-            "constant"
-        } else {
-            "balanced"
-        }
-    );
-
-    // 3 qubits, f(x) = 0
-    // Constant
-    let mut constant_state = State::new(3, 1);
-
-    constant_state.apply_all(h());
-
-    // Oracle is equivilant to the identity gate, thus has no effect on the
-    // state.
-
-    constant_state.apply_all(h());
-
-    println!(
-        "{}",
-        if constant_state.measure() == 0 {
-            "constant"
-        } else {
-            "balanced"
-        }
-    );
-}
-```

+ 0 - 129
book/src/algorithms/grover.md

@@ -1,129 +0,0 @@
-# Grovers Algorithm
-
-Given an unstructured set \\(N = \{a_1, a_2,\dots,a_n\}\\), find
-a given element \\(a_i \in N\\).
-
-This implementation looks for a given number \\(target\\) in the set \\(\{0,1,\dots, \text{regwidth}\}\\).
-
-See <https://cs.uwaterloo.ca/~watrous/LectureNotes.html>
-
-```rust
-extern crate qcgpu;
-extern crate rand;
-
-use qcgpu::State;
-use std::f32::consts::PI;
-
-fn main() {
-    let mut state = State::new(8, 1);
-
-    let target = 5;
-    let reg_width = 3;
-
-    let num_inversions = ((PI / 4.0) * ((1 << reg_width) as f32).sqrt()) as i32;
-
-    state.x(reg_width);
-
-    for i in 0..(reg_width + 1) {
-        state.h(i);
-    }
-
-    for _ in 0..(num_inversions) {
-        iteration(&mut state, target, reg_width);
-    }
-
-    state.h(reg_width);
-
-    println!("Measured: {:?}", state.measure_first(reg_width, 1000));
-}
-
-fn oracle(state: &mut State, target: i32, reg_width: i32) {
-    for i in 0..reg_width {
-        if get_bit(target, i) == 0 {
-            state.x(i);
-        }
-    }
-
-    state.toffoli(0, 1, reg_width + 1);
-
-    let mut i = 1;
-    while i < reg_width {
-        state.toffoli(i, reg_width + i, reg_width + i + 1);
-        i += 1;
-    }
-
-    state.cx(reg_width + i, reg_width);
-
-    i = reg_width - 1;
-    while i > 0 {
-        state.toffoli(i, reg_width + i, reg_width + i + 1);
-        i -= 1;
-    }
-
-    state.toffoli(0, 1, reg_width + 1);
-
-    for i in 0..reg_width {
-        if get_bit(target, i) == 0 {
-            state.x(i);
-        }
-    }
-}
-
-fn inversion(state: &mut State, reg_width: i32) {
-    for i in 0..reg_width {
-        state.x(i);
-    }
-
-    state.h(reg_width - 1);
-
-    if reg_width == 3 {
-        state.toffoli(0, 1, 2);
-    } else {
-        state.toffoli(0, 1, reg_width + 1);
-
-        let mut i = 1;
-        while i < reg_width {
-            state.toffoli(i, reg_width + i, reg_width + i + 1);
-            i += 1;
-        }
-
-        state.cx(reg_width + i, reg_width - 1);
-
-        i = reg_width - 2;
-        while i > 0 {
-            state.toffoli(i, reg_width + i, reg_width + i + 1);
-            i -= 1;
-        }
-
-        state.toffoli(0, 1, reg_width + 1);
-    }
-
-    state.h(reg_width - 1);
-
-    for i in 0..reg_width {
-        state.x(i);
-    }
-}
-
-fn iteration(state: &mut State, target: i32, reg_width: i32) {
-    oracle(state, target, reg_width);
-
-    for i in 0..reg_width {
-        state.h(i);
-    }
-
-    inversion(state, reg_width);
-
-    for i in 0..reg_width {
-        state.h(i);
-    }
-}
-
-/// Get the value of a bit
-fn get_bit(number: i32, n: i32) -> i32 {
-    if number & (1 << n) != 0 {
-        return 1;
-    }
-    0
-}
-```

+ 0 - 100
book/src/algorithms/shor.md

@@ -1,100 +0,0 @@
-# Shors Algorithm
-
-This algorithm finds the prime factors (\\)u\\) and \\(v\\)) of an odd, composite integer \\(n\\),
-that is not a prime power.
-
-## The Algorithm
-
-(pseudo code)
-
-```pseudo
-Repeat
-    Randomly choose \\(a \in \{ 2, \dots, n - 1 \}\\)
-    Compute \\(d = gcd(a, n)\\)
-    If \\(d \geq 2\\) then
-        Return \\(u = d\\) and \\(v = n/d\\)
-    Else // We know \\(a \in \mathbb{Z}^*_N\\)
-        Let \\(r\\) be the order of \\(a\\) in \\(\mathbb{Z}^*_N\\) // Order finding algorithm
-        If \\(r\\) is even then
-            Compute \\(x = a^{r/2} - 1 (\mod n)\\)
-            Compute \\(d = \gcd(x, n)\\)
-            If \\(d \geq 2\\) then
-                Return \\(u = d\\) and \\(v = n/d\\)
-Until a value is returned.
-```
-
-See <https://cs.uwaterloo.ca/~watrous/LectureNotes.html>
-
-```rust
-extern crate qcgpu;
-extern crate rand;
-
-use rand::{thread_rng, Rng};
-use qcgpu::{gcd, State};
-use qcgpu::gates::h;
-
-
-fn main() {
-    let n = 15; // Number to factor
-    println!("Factoring {}.", n);
-
-    // Here we should check if the number is even, or if it is a power factor
-
-    let mut rng = thread_rng();
-    loop {
-        let mut a = rng.gen_range(2, n); // Randomly choose \\(a \in \{ 2, \dots, n - 1 \}\\)
-
-        let mut d = gcd(a, n);
-        if d >= 2 {
-            // Found the factors; No Quantum needed
-            println!(
-                "Factors are {} and {} (No quantum algorithm used)",
-                d,
-                n / d
-            );
-            break;
-        } else {
-            // We know \\(a \in \mathbb{Z}^*_N\\)
-            let r = find_order(a, n);
-            if r % 2 == 0 {
-                let x = (a.pow((r as f32 / 2 as f32) as u32) - 1) % n;
-                d = gcd(x, n);
-                if d >= 2 {
-                    println!("Factors are {} and {}", d, n / d);
-                    break;
-                }
-            } else {
-                println!("Period is odd");
-            }
-        }
-    }
-}
-```
-
-## Order Finding
-
-Given a positive integer \\(n \geq 2\\) and an element \\(a \in \mathbb{Z}_n^* \\),
-Find the order of \\(a\\) in \\(\mathbb{Z}_n^*\\).
-
-Order finding is the only quantum part in shors algorithm.
-
-\\(\mathbb{Z}_n\\) is the set of integers from \\(\{0,\dots, n - 1\}\\) or \\(\mathbb{z} \mod n\\).
-The set \\(\mathbb{Z}_n^* = \{a \in \mathbb{Z}_n : \gcd(a,n) = 1\}\\)
-
-The set \\( \mathbb{Z}_n^* \\) forms a group wth the multiplication modulo \\(n\\) operation.
-Thus, for \\( a \in \mathbb{Z}_n^* \\) , \\(\exists b \in \mathbb{Z}_n^*\\) that uniquely satisfies
-
-\\[
-ab \equiv 1 \mod n
-\\]
-
-The order of a given element \\(a \in \mathbb{Z}_n^*\\) is the smallest positive integer \\(r\\) such that
-
-\\[
-a^r \equiv 1 \mod n
-\\]
-
-```rust
-fn find_order(a: i32, n: i32) -> i32 {
-    unimplemented!()
-}

+ 0 - 56
book/src/algorithms/super-dense.md

@@ -1,56 +0,0 @@
-# Super Dense Coding
-
-if Alice and Bob share a pair of entangled qubits, then Alice can encode two classical bits into her one entangled qubit,
-send it to Bob, and Bob can decode it with the help of his entangled qubit.
-
-```rust
-extern crate qcgpu;
-
-use qcgpu::State;
-
-fn superdense(input: &str) -> i32 {
-    let mut state = State::new(2, 0);
-    let input_str = String::from(input);
-
-    // Prepare the bell state
-    state.h(0);
-    state.cx(0, 1);
-
-    // Alice prepares her qubit
-    let alice = 1;
-    if input_str.get(0..1) == Some("1") {
-        state.z(alice);
-    }
-    if input_str.get(1..2) == Some("1") {
-        state.x(alice);
-    }
-
-    println!("\nState after Alice prepares her qubit: \n{}", state);
-
-    // Alice sends her qubit to Bob
-    let bob = 0;
-    state.cx(alice, bob);
-    state.h(alice);
-
-    println!(
-        "\nState after Bob receives Alice's qubit and 'decodes' it: \n{}",
-        state
-    );
-
-    state.measure()
-}
-
-fn main() {
-    use std::io;
-
-    println!("Two bit string to send:");
-    let mut input = String::new();
-    match io::stdin().read_line(&mut input) {
-        Ok(_n) => {
-            let result = superdense(input.as_str());
-            println!("\nDecoded string is: {}", result);
-        }
-        Err(error) => println!("error: {}", error),
-    }
-}
-```

+ 0 - 28
book/src/getting-started.md

@@ -1,28 +0,0 @@
-# Getting Started
-
-## Installing The Requirements
-
-To use QCGPU, you will need Rust and OpenCL installed.
-
-The setup process for OpenCL will be different for every device. All apple devices (MacOS / OSX) will have OpenCL already installed.
-For some hints on how to install on linux, look at the [AWS EC2 Install Instructions](https://github.com/QCGPU/qcgpu-rust/blob/master/EC2-install.md).
-There is also a good chance that it is installed already. Check that `clinfo` for some other diagnostic command will run.
-
-Rust is very easy to install. Check out [rustup](https://www.rustup.rs).
-
-## Adding The Dependency
-
-To use the library with rust, you must add the following to your `cargo.toml` file:
-
-```toml
-[dependencies]
-qcgpu = "0.1"
-```
-
-You should now be able to use the library by adding 
-
-```
-extern crate qcgpu;
-```
-
-to `lib.rs` or `main.rs`, depending on if you are writing an executable or a library.

+ 0 - 19
book/src/qcgpu.md

@@ -1,19 +0,0 @@
-# QCGPU
-
-**QCGPU** is a high performance, hardware accelerated quantum computer simulator written with [Rust](https://rust-lang.org/) and [OpenCL](https://www.khronos.org/opencl/).
-
-QCGPU is free and open source. The source code is available on [GitHub](https://github.com/qcgpu/qcgpu-rust). Please report any places where the documentation is unclear, or where examples could be added or improved by creating an [issue](https://github.com/qcgpu/qcgpu-rust/issues).
-
-## Help Wanted
-
-Please request any functionality you would like, need or appreciate by creating an [issue](https://github.com/qcgpu/qcgpu-rust/issues). If you would like to add functionality or fix issues, please create a [pull request](https://github.com/qcgpu/qcgpu-rust/pulls) and I will get back to you (usually within the day).
-
-Any other feedback, suggestions or even tiny bits of criticism are welcome. When in doubt, file an [issue](https://github.com/qcgpu/qcgpu-rust/issues)!
-
-## API Docs
-
-Alongside this guide, you may also want the [documentation](https://qcgpu.github.com/qcgpu/documentation).
-
-## License
-
-QCGPU is licensed under the [MIT](https://github.com/qcgpu/qcgpu-rust/blob/master/LICENSE) License.

+ 0 - 32
book/src/user-guide/decoherence.md

@@ -1,32 +0,0 @@
-# Decoherence
-
-QCGPU provides a way to easily simulate the effects of decoherence on the quantum computer. The effects are simulated by a random gate, corresponding to a random rotation around the \\(z\\) axis of the bloch sphere.
-
-The angle of the rotation is a normal distrobuted value, with the varience as the strength factor `d`.
-
-To avoid performance costs when not being used, decoherence can be enabled via a feature.
-
-Change the dependency in `cargo.toml` to
-
-```toml
-[dependencies]
-qcgpu = { version = "0.1", features = ["decoherence"] }
-```
-
-Now you can add decoherence to your simulator.
-
-To set the amount of decoherence, the `set_decoherence` method is used. Its arguments are the strength of the decoherence.
-This will affect all following gate applications.
-
-```rust
-use qcgpu::State;
-
-let mut register = State::new(5, 0);
-register.set_decoherence(0.4);
-```
-
-You can also manually decohere the register based on the previously set strength value. This method is automatically called by all gate applications.
-
-```rust
-register.decohere();
-```

+ 0 - 31
book/src/user-guide/examples.md

@@ -1,31 +0,0 @@
-# Examples
-
-Here is a complete example of using QCGPU to simulate the bell state.
-
-```rust
-extern crate qcgpu;
-
-use qcgpu::State;
-
-fn main() {
-    // Create a new quantum register with 2 qubits on OpenCL device #0
-    let mut register = State::new(2, 0);
-
-    // Apply a hadamard gate to the first qubit
-    register.h(0);
-
-    // Apply a CNOT, with the control = 0, and target = 1
-    register.cx(0, 1);
-
-    // Measure the register 1000 times and print the output
-    println!("Measured: {:?}", register.measure_many(1000));
-}
-```
-
-The output should be something like 
-
-```shell
-Measured: {"00": 516, "11": 484}
-```
-
-For more, non trivial examples, see the [Algorithms](../algorithms/algorithms.html)

+ 0 - 92
book/src/user-guide/gates.md

@@ -1,92 +0,0 @@
-# Quantum Gates
-
-Gates are used to manipulate quantum registers and to implement quantum algorithms.
-
-## Built In Gates
-
-There are a number of gates built in to QCGPU. They can all be applied the same way:
-
-```rust
-use qcgpu::State;
-
-let mut state = State::new(5, 0);
-
-state.h(0); // Applies the hadamard (`h`) gate to the 0th qubit
-```
-
-`h` can be replaced with any of the following:
-
-* The hadmard gate: **h** - `state.h(0);`
-* The S gate: **s** - `state.s(0);`
-* The T gate: **t** - `state.t(0);`
-* The Pauli-X / NOT gate: **x** - `state.x(0);`
-* The Pauli-Y gate: **y** - `state.y(0);`
-* The Pauli-Z gate: **z** - `state.z(0);`
-* The CNOT gate: **cx** - `state.cx(0, 1); // CNOT with control = 0, target = 1`
-* The SWAP gate: **swap** - `state.swap(0,1); // Swaps the 0th and 1st qubit`
-* The Toffoli gate: **toffoli** - `state.toffoli(0, 1, 2); // Toffoli with control1 = 0, control1 = 1, target = 2`
-
-These are all shorthand methods for the application of arbitrary gates. For example, the application of a hadamard gate above is shorthand for 
-
-```rust
-use qcgpu::gates::{h};
-use qcgpu::State;
-
-let mut state = State::new(5, 0);
-state.apply_gate(h(), 0);
-```
-
-You can also use any of the gates as controlled gates. For example, the application of the CNOT gate above is shorthand for
-
-```rust
-use qcgpu::gates::{x};
-use qcgpu::State;
-
-let mut state = State::new(5, 0);
-state.apply_controlled_gate(x(), 0, 1);
-```
-
-## User Defined Gates
-
-Gates in QCGPU are represented by the `Gate` struct, available through `qcgpu::Gate`.
-
-It is defined as follows:
-
-```rust
-extern crate num_complex;
-use num_complex::Complex32;
-
-struct Gate {
-    a: Complex32,
-    b: Complex32,
-    c: Complex32,
-    d: Complex32,
-}
-```
-
-To create your own gate, you will need to add the `num_complex` crate to your dependencies.
-
-A gate is created as follows:
-
-```rust
-let x = Gate {
-    Gate {
-        a: Complex32::new(0.0, 0.0),
-        b: Complex32::new(1.0, 0.0),
-        c: Complex32::new(1.0, 0.0),
-        d: Complex32::new(0.0, 0.0),
-    }
-}
-```
-
-This corresponds to the matrix
-
-\\[x = \begin{bmatrix} 0 & 1 \\\ 1 & 0 \end{bmatrix}\\]
-
-This can be applied using the same long hand method as above:
-
-```rust
-let mut state = State::new(1, 0);
-state.apply_gate(x, 0);
-```
-

+ 0 - 44
book/src/user-guide/operations.md

@@ -1,44 +0,0 @@
-# Quantum Operations
-
-There are a number of operations you can preform on quantum registers with QCGPU.
-
-## Measurement
-You can measure the register in two ways. You can either do a single measurement and return an integer with the measured value or you can measure multiple times and return a `HashMap<String, i32>`, with the key being the bitstring, and the value being the number of times it was measured.
-
-The measurements don't collapse the state.
-
-They are used as follows:
-
-```rust
-use qcgpu::State;
-
-let mut state = State::new(5, 0);
-state.measure(); // Returns an integer
-state.measure_many(1000); // Measures 1000 times, returns a HashMap<String, i32>
-```
-
-There is also a convenience method to measure the first \\(n\\) qubits in the register. Again, the state is not collapsed
-
-```rust
-use qcgpu::State;
-
-let mut state = State::new(5, 0);
-state.measure_first(3, 1000); // Measures the first 3 qubits 1000 times, returns a HashMap<String, i32>
-```
-
-## Probability
-QCGPU provides another method for getting the probability of each outcome.
-
-The probability is calculated for a state \\(\lvert \psi \rangle = \sum_{j = 0}^{2^n - 1} \alpha_j \lvert j \rangle\\), 
-
-\\[P(j) = |\alpha_j|^2\\]
-
-The method `get_probabilities` returns a `Vec<f32>` with each of the values corresponding to \\(|\alpha_j|^2\\) for each index \\(j\\).
-
-```rust
-use qcgpu::State;
-
-let mut state = State::new(1, 0);
-state.h(0);
-state.get_probabilities(); // [0.5, 0.5]
-```

+ 0 - 48
book/src/user-guide/registers.md

@@ -1,48 +0,0 @@
-# Quantum Registers
-
-All of the simulation is done through quantum registers. QCGPU provides a struct as a register, but that contains fields to do with the OpenCL buffers and related items, so the creation of registers should be done through the provided methods.
-
-The library is optimized for complex superpositions, so the registers are all dense. This means that the number of qubits you can initialize is directly related to the capacity / available memory of the device.
-
-The register struct is called `State` and is available through `qcgpu::State`.
-
-To create a register, the easiest way to do it is with the `State::new` method.
-It takes two parameters, the number of qubits and the device to use. The device is given as a usize, and corresponds to the OpenCL device which the register will be on.
-
-The following example creates a register with 5 qubits on the 1st device.
-
-```rust
-# extern crate qcgpu;
-
-use qcgpu::State;
-
-# fn main() {
-let mut register = State::new(5, 0);
-# }
-```
-
-Notice that the register is mutable. This allows the register to change. Also, the device is 0 indexed.
-
-The implementation is equivilent to the description of a state vector \\( \lvert \psi \rangle \\) with
-
-\\[ \lvert \psi \rangle = \sum_{j = 0}^{2^n - 1} \alpha_j \lvert j \rangle \\]
-
-where \\(n\\) is the number of qubits, \\(\alpha_j\\) is the amplitude and the state is \\(j\\) runs over all \\(2^n\\) basis states.
-
-There is one other way to initialize a state. Given a bitstring, a register can be initialized in that state using the `State::from_bit_string` method. For example, to initialize a register with the value `0100` the following is used:
-
-```rust
-# extern crate qcgpu;
-
-use qcgpu::State;
-
-# fn main() {
-let mut register = State::from_bit_string("|0100>", 0);
-# }
-```
-
-The second argument is the same as before. The register that is outputed from this method is equivilent to the state
-
-\\[ \lvert 0100 \rangle\\]
-
-

+ 0 - 5
book/src/user-guide/user-guide.md

@@ -1,5 +0,0 @@
-# User Guide
-
-This chapter covers the usage of the QCGPU library. It will also contain some information about the mathematics that each of the functions represents.
-
-All aspects of the library are covered by this chapter, whereas complete examples (in the form of algorithm implementations) are available in the [Algorithms Chapter](../algorithms/algorithms.html)

+ 22 - 0
examples/bell-state.py

@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+"""
+Bell State / EPR Pair
+---------------------
+
+The Bell State, also known as the EPR pair (after Einstein, Podosky and Rosen)
+is the simplest example of entanglement.
+
+The Bell State is defined as the maximally entangled quantum state of two qubits.
+"""
+
+import qcgpu
+
+print("Creating Bell State")
+
+state = qcgpu.state(2)
+
+state.h(0)
+state.cx(0, 1)
+
+print("Measurement Results:")
+print(state.measure())

+ 0 - 21
examples/bell-state.rs

@@ -1,21 +0,0 @@
-//! # Bell State / EPR Pair
-//!
-//! The Bell State, also known as the EPR pair (after Einstein, Podosky and Rosen)
-//! is the simplest example of entanglement.
-//!
-//! The Bell State is defined as the maximally entangled quantum state of two qubits.
-extern crate qcgpu;
-
-use qcgpu::Simulator;
-
-fn main() {
-    println!("Creating Bell State");
-
-    let mut sim = Simulator::new_opencl(2).unwrap();
-
-    sim.h(0).unwrap();
-    sim.cx(0, 1).unwrap();
-
-    println!("Measurement Results:");
-    println!("{}", sim.measure().unwrap());
-}

+ 0 - 56
examples/super-dense.rs

@@ -1,56 +0,0 @@
-//! # Super Dense Coding
-//!
-//! If Alice and Bob share a pair of entangled qubits, then Alice can encode two classical bits into her one entangled qubit,
-//! send it to Bob, and Bob can decode it with the help of his entangled qubit.
-
-extern crate failure;
-extern crate qcgpu;
-
-use failure::Error;
-use qcgpu::Simulator;
-
-fn superdense(input: &str) -> Result<u64, Error> {
-    let mut state = Simulator::new_opencl(2)?;
-    let input_str = String::from(input);
-
-    // Prepare the bell state
-    state.h(0)?;
-    state.cx(0, 1)?;
-
-    // Alice prepares her qubit
-    let alice = 1;
-    if input_str.get(0..1) == Some("1") {
-        state.z(alice)?;
-    }
-    if input_str.get(1..2) == Some("1") {
-        state.x(alice)?;
-    }
-
-    println!("\nState after Alice prepares her qubit: \n{}", state);
-
-    // Alice sends her qubit to Bob
-    let bob = 0;
-    state.cx(alice, bob)?;
-    state.h(alice)?;
-
-    println!(
-        "\nState after Bob receives Alice's qubit and 'decodes' it: \n{}",
-        state
-    );
-
-    state.measure()
-}
-
-fn main() {
-    use std::io;
-
-    println!("Two bit string to send:");
-    let mut input = String::new();
-    match io::stdin().read_line(&mut input) {
-        Ok(_n) => {
-            let result = superdense(input.as_str()).unwrap();
-            println!("\nDecoded string is: {}", result);
-        }
-        Err(error) => println!("error: {}", error),
-    }
-}

+ 2 - 0
qcgpu/__init__.py

@@ -0,0 +1,2 @@
+import gate
+from state import State

+ 114 - 0
qcgpu/backend.py

@@ -0,0 +1,114 @@
+import os
+import pyopencl as cl
+import pyopencl.array as pycl_array
+import numpy as np
+
+# Get the OpenCL kernel
+kernel_path = os.path.join(
+    os.path.dirname(__file__),
+    "kernels/brute-force.cl"
+)
+kernel = open(kernel_path, "r").read()
+
+
+class Backend:
+    """
+    A class for the OpenCL backend to the simulator.
+
+    This class shouldn't be used directly, as many of the
+    methods don't have the same input checking as the State
+    class.
+    """
+
+    def __init__(self, num_qubits, dtype=np.complex64):
+        """
+        Initialize a new OpenCL Backend
+
+        Takes an argument of the number of qubits to use
+        in the register, and returns the backend.
+        """
+        self.num_qubits = num_qubits
+        self.dtype = dtype
+
+        # Setup the opencl context, queue and program
+        self.context = cl.create_some_context()
+        self.queue = cl.CommandQueue(self.context)
+        self.program = cl.Program(self.context, kernel).build()
+
+        # Buffer for the state vector
+        self.buffer = pycl_array.to_device(
+            self.queue,
+            np.eye(2**num_qubits, 1, dtype=dtype)
+        )
+
+    def apply_gate(self, gate, target):
+        """Applies a gate to the quantum register"""
+
+        kernel = self.program.apply_gate
+        kernel.set_scalar_arg_dtypes([
+            None,
+            np.int32,
+            self.dtype,
+            self.dtype,
+            self.dtype,
+            self.dtype,
+        ])
+        kernel(
+            self.queue,
+            [self.buffer.shape[0] / 2],
+            None,
+            self.buffer.data,
+            np.int32(target),
+            gate.a,
+            gate.b,
+            gate.c,
+            gate.d
+        )
+
+    def apply_controlled_gate(self, gate, control, target):
+        """Applies a controlled gate to the quantum register"""
+
+        kernel = self.program.apply_controlled_gate
+        kernel.set_scalar_arg_dtypes([
+            None,
+            np.int32,
+            np.int32,
+            self.dtype,
+            self.dtype,
+            self.dtype,
+            self.dtype,
+        ])
+        kernel(
+            self.queue,
+            [self.buffer.shape[0] / 2],
+            None,
+            self.buffer.data,
+            np.int32(control),
+            np.int32(target),
+            gate.a,
+            gate.b,
+            gate.c,
+            gate.d
+        )
+    
+    def amplitudes(self):
+        """Gets the probability amplitudes"""
+        return self.buffer.get()
+    
+    def probabilities(self):
+        """Gets the squared absolute value of each of the amplitudes"""
+        out = pycl_array.to_device(
+            self.queue,
+            np.zeros(2**self.num_qubits, dtype=np.float32)
+        )
+
+        self.program.calculate_probabilities(
+            self.queue,
+            self.buffer.shape,
+            None,
+            self.buffer.data,
+            out.data
+        )
+
+        return out.get()
+        

+ 34 - 0
qcgpu/gate.py

@@ -0,0 +1,34 @@
+import numpy as np
+
+class Gate:
+    def __init__(self, gate):
+        if gate.shape != (2, 2):
+            raise ValueError(
+                "Gate is not a 2x2 matrix. " + 
+                "For larger gates, please decompose into 2x2 matrices " +
+                "and/or use the controlled gate functionality."
+            )
+
+        # Check the gate is unitary
+        if (not np.allclose(np.eye(gate.shape[0]), gate.H * gate)):
+            raise ValueError("gate is not unitary.")
+
+        self.a = complex(gate[0, 0])
+        self.b = complex(gate[0, 1])
+        self.c = complex(gate[1, 0])
+        self.d = complex(gate[1, 1])
+
+    def __repr__(self):
+        return '[{:.4f}, {:.4f}]\n[{:.4f}, {:.4f}]'.format(self.a, self.b, self.c, self.d)
+
+def h():
+    return Gate(np.matrix([[1, 1], [1, -1]]) / np.sqrt(2))
+
+def x():
+    return Gate(np.matrix([[0, 1], [1, 0]]))
+
+def y():
+    return Gate(np.matrix([[0, -1j], [1j, 0]]))
+
+def z():
+    return Gate(np.matrix([[1, 0], [0, -1j]]))

+ 182 - 0
qcgpu/kernels/brute-force.cl

@@ -0,0 +1,182 @@
+#include <pyopencl-complex.h>
+
+/*
+ * Returns the nth number where a given digit
+ * is cleared in the binary representation of the number
+ */
+static uint nth_cleared(uint n, uint target)
+{
+    uint mask = (1 << target) - 1;
+    uint not_mask = ~mask;
+
+    return (n & mask) | ((n & not_mask) << 1);
+}
+
+///////////////////////////////////////////////
+// KERNELS
+///////////////////////////////////////////////
+
+/*
+ * Applies a single qubit gate to the register.
+ * The gate matrix must be given in the form:
+ *
+ *  A B
+ *  C D
+ */
+__kernel void apply_gate(
+    __global cfloat_t *amplitudes,
+    uint target,
+    cfloat_t A,
+    cfloat_t B,
+    cfloat_t C,
+    cfloat_t D)
+{
+    uint const global_id = get_global_id(0);
+
+    uint const zero_state = nth_cleared(global_id, target);
+
+    // uint const zero_state = state & (~(1 << target)); // Could just be state
+    uint const one_state = zero_state | (1 << target);
+
+    cfloat_t const zero_amp = amplitudes[zero_state];
+    cfloat_t const one_amp = amplitudes[one_state];
+
+    amplitudes[zero_state] = cfloat_add(cfloat_mul(A, zero_amp), cfloat_mul(B, one_amp));
+    amplitudes[one_state] = cfloat_add(cfloat_mul(D, one_amp), cfloat_mul(C, zero_amp));
+}
+
+/*
+ * Applies a controlled single qubit gate to the register.
+ */
+__kernel void apply_controlled_gate(
+    __global cfloat_t *amplitudes,
+    uint control,
+    uint target,
+    cfloat_t A,
+    cfloat_t B,
+    cfloat_t C,
+    cfloat_t D)
+{
+    uint const global_id = get_global_id(0);
+    uint const zero_state = nth_cleared(global_id, target);
+    uint const one_state = zero_state | (1 << target); // Set the target bit
+
+    uint const control_val_zero = (((1 << control) & zero_state) > 0) ? 1 : 0;
+    uint const control_val_one = (((1 << control) & one_state) > 0) ? 1 : 0;
+
+    cfloat_t const zero_amp = amplitudes[zero_state];
+    cfloat_t const one_amp = amplitudes[one_state];
+
+    if (control_val_zero == 1)
+    {
+        amplitudes[zero_state] = cfloat_add(cfloat_mul(A, zero_amp), cfloat_mul(B, one_amp));
+    }
+
+    if (control_val_one == 1)
+    {
+        amplitudes[one_state] = cfloat_add(cfloat_mul(D, one_amp), cfloat_mul(C, zero_amp));
+    }
+
+
+}
+
+/*
+ * Applies a controlled-controlled single qubit gate to the register.
+ * NOT MIGRATED
+ */
+__kernel void apply_controlled_controlled_gate(
+    __global cfloat_t *const amplitudes,
+    __global cfloat_t *amps,
+    uint control1,
+    uint control2,
+    uint target,
+    cfloat_t A,
+    cfloat_t B,
+    cfloat_t C,
+    cfloat_t D)
+{
+    uint const state = get_global_id(0);
+    cfloat_t const amp = amplitudes[state];
+
+    uint const zero_state = state & (~(1 << target));
+    uint const one_state = state | (1 << target);
+
+    uint const bit_val = (((1 << target) & state) > 0) ? 1 : 0;
+    uint const control1_val = (((1 << control1) & state) > 0) ? 1 : 0;
+    uint const control2_val = (((1 << control2) & state) > 0) ? 1 : 0;
+
+    if (control1_val == 0 || control2_val == 0)
+    {
+        // Control is 0, don't apply gate
+        amps[state] = amp;
+    }
+    else
+    {
+        // control is 1, apply gate.
+        if (bit_val == 0)
+        {
+            // Bitval = 0
+            amps[state] = cfloat_add(cfloat_mul(A, amp), cfloat_mul(B, amplitudes[one_state]));
+        }
+        else
+        {
+            amps[state] = cfloat_add(cfloat_mul(D, amp), cfloat_mul(C, amplitudes[zero_state]));
+        }
+    }
+}
+
+/*
+ * Swaps the states of two qubits in the register
+ * NOT MIGRATED
+ */
+// __kernel void swap(
+//     __global cfloat_t *const amplitudes,
+//     __global cfloat_t *amps,
+//     uint first_qubit,
+//     uint second_qubit)
+// {
+//     uint const state = get_global_id(0);
+
+//     uint const first_bit_mask = 1 << first_qubit;
+//     uint const second_bit_mask = 1 << second_qubit;
+
+//     uint const new_second_bit = ((state & first_bit_mask) >> first_qubit) << second_qubit;
+//     uint const new_first_bit = ((state & second_bit_mask) >> second_qubit) << first_qubit;
+
+//     uint const new_state = (state & !first_bit_mask & !second_bit_mask) | new_first_bit | new_second_bit;
+
+//     amps[new_state] = amplitudes[state];
+// }
+
+
+/**
+ * Calculates The Probabilities Of A State Vector
+ */
+__kernel void calculate_probabilities(
+    __global cfloat_t *const amplitudes,
+    __global float *probabilities)
+{
+    uint const state = get_global_id(0);
+    cfloat_t amp = amplitudes[state];
+
+    probabilities[state] = cfloat_abs(cfloat_mul(amp, amp));
+}
+
+/**
+ * Initializes a register to the value 1|0..100...0>
+ *                                          ^ target
+ */
+__kernel void initialize_register(
+    __global cfloat_t *amplitudes,
+    uint const target)
+{
+    uint const state = get_global_id(0);
+    if (state == target)
+    {
+        amplitudes[state] = cfloat_new(1, 0);
+    }
+    else
+    {
+        amplitudes[state] = cfloat_new(0, 0);
+    }
+}

+ 57 - 0
qcgpu/state.py

@@ -0,0 +1,57 @@
+from backend import Backend
+import pyopencl as cl
+import numpy as np
+
+class State:
+    """Represents the state of a quantum register"""
+    def __init__(self, num_qubits):
+        if not isinstance(num_qubits, int):
+            raise ValueError("num_qubits must be an int")
+        if num_qubits <= 0:
+            raise ValueError("num_qubits must be a positive integer")
+
+        self.num_qubits = num_qubits
+        self.backend = Backend(num_qubits)
+
+    def apply_gate(self, gate, target):
+        if not isinstance(target, int) or target < 0:
+            raise ValueError("target must be an int > 0")
+
+        # TODO: Check that gate is correct
+
+        self.backend.apply_gate(gate, target)
+
+    def apply_controlled_gate(self, gate, control, target):
+        if not isinstance(target, int) or target < 0:
+            raise ValueError("target must be an int > 0")
+        
+        if not isinstance(control, int) or control < 0:
+            raise ValueError("control must be an int > 0")
+
+        # TODO: Check that gate is correct
+
+        self.backend.apply_controlled_gate(gate, control, target)
+
+
+
+
+
+
+
+
+
+
+    def amplitudes(self):
+        return self.backend.amplitudes()
+    
+    def probabilities(self):
+        return self.backend.probabilities()
+
+    def sum(self):
+        return cl.array.sum(self.backend.buffer)
+
+    def __repr__(self):
+        """A string representation of the state"""
+
+        # TODO: Finish this method
+        return np.array_str(self.backend.buffer)

+ 21 - 0
setup.py

@@ -0,0 +1,21 @@
+import setuptools
+
+with open("README.md", "r") as fh:
+    long_description = fh.read()
+
+setuptools.setup(
+    name="qcgpu",
+    version="0.0.1",
+    author="Adam Kelly",
+    author_email="adamkelly2201@gmail.com",
+    description="An OpenCL based quantum computer simulator",
+    long_description=long_description,
+    long_description_content_type="text/markdown",
+    url="https://github.com/qcgpu/qcgpu",
+    packages=setuptools.find_packages(),
+    classifiers=[
+        "Programming Language :: Python :: 3",
+        "License :: OSI Approved :: MIT License",
+        "Operating System :: OS Independent",
+    ],
+)

+ 0 - 3
src/backends/mod.rs

@@ -1,3 +0,0 @@
-mod opencl;
-
-pub use self::opencl::OpenCL;

+ 0 - 229
src/backends/opencl/kernel.cl

@@ -1,229 +0,0 @@
-typedef float2 complex_f;
-
-/*
- * Addition of two complex numbers:
- *
- * a + b = (Re(a) + Re(b)) + i(Im(a) + Im(b))
- */
-static complex_f add(complex_f a, complex_f b)
-{
-    return (complex_f)(a.x + b.x, a.y + b.y);
-}
-
-/*
- * Negation of a complex numbers:
- *
- * -a = -(Re(a) - i(Im(a))
- */
-static complex_f neg(complex_f a)
-{
-    return (complex_f)(-a.x, -a.y);
-}
-
-/*
- * Multiplication of two complex numbers:
- *
- * a * b =
- *   ((Re(a) * Re(b)) - (Im(a) * Im(b)))
- * + ((Im(a) * Re(b)) + (Re(a) * Im(b)))i
- */
-static complex_f mul(complex_f a, complex_f b)
-{
-    return (complex_f)(
-        (a.x * b.x) - (a.y * b.y),
-        (a.y * b.x) + (a.x * b.y));
-}
-/**
- * Absolute value of a complex number
- *
- * |a| = √(Re(a)^2 + Im(a)^2)
- */
-static float complex_abs(complex_f a)
-{
-    return sqrt((a.x * a.x) + (a.y * a.y));
-}
-
-static complex_f cexp(float a) {
-    return (complex_f)(cos(a), sin(a));
-}
-
-/*
- * Returns the nth number where a given digit
- * is cleared in the binary representation of the number
- */
-static uint nth_cleared(uint n, uint target)
-{
-    uint mask = (1 << target) - 1;
-    uint not_mask = ~mask;
-
-    return (n & mask) | ((n & not_mask) << 1);
-}
-
-///////////////////////////////////////////////
-// KERNELS
-///////////////////////////////////////////////
-
-/*
- * Applies a single qubit gate to the register.
- * The gate matrix must be given in the form:
- *
- *  A B
- *  C D
- */
-__kernel void apply_gate(
-    __global complex_f *amplitudes,
-    uint target,
-    complex_f A,
-    complex_f B,
-    complex_f C,
-    complex_f D)
-{
-    uint const global_id = get_global_id(0);
-
-    uint const zero_state = nth_cleared(global_id, target);
-
-    // uint const zero_state = state & (~(1 << target)); // Could just be state
-    uint const one_state = zero_state | (1 << target);
-
-    complex_f const zero_amp = amplitudes[zero_state];
-    complex_f const one_amp = amplitudes[one_state];
-
-    amplitudes[zero_state] = add(mul(A, zero_amp), mul(B, one_amp));
-    amplitudes[one_state] = add(mul(D, one_amp), mul(C, zero_amp));
-}
-
-/*
- * Applies a controlled single qubit gate to the register.
- */
-__kernel void apply_controlled_gate(
-    __global complex_f *amplitudes,
-    uint control,
-    uint target,
-    complex_f A,
-    complex_f B,
-    complex_f C,
-    complex_f D)
-{
-    uint const global_id = get_global_id(0);
-    uint const zero_state = nth_cleared(global_id, target);
-    uint const one_state = zero_state | (1 << target); // Set the target bit
-
-    uint const control_val_zero = (((1 << control) & zero_state) > 0) ? 1 : 0;
-    uint const control_val_one = (((1 << control) & one_state) > 0) ? 1 : 0;
-
-    complex_f const zero_amp = amplitudes[zero_state];
-    complex_f const one_amp = amplitudes[one_state];
-
-    if (control_val_zero == 1)
-    {
-        amplitudes[zero_state] = add(mul(A, zero_amp), mul(B, one_amp));
-    }
-
-    if (control_val_one == 1)
-    {
-        amplitudes[one_state] = add(mul(D, one_amp), mul(C, zero_amp));
-    }
-
-
-}
-
-/*
- * Applies a controlled-controlled single qubit gate to the register.
- * NOT MIGRATED
- */
-__kernel void apply_controlled_controlled_gate(
-    __global complex_f *const amplitudes,
-    __global complex_f *amps,
-    uint control1,
-    uint control2,
-    uint target,
-    complex_f A,
-    complex_f B,
-    complex_f C,
-    complex_f D)
-{
-    uint const state = get_global_id(0);
-    complex_f const amp = amplitudes[state];
-
-    uint const zero_state = state & (~(1 << target));
-    uint const one_state = state | (1 << target);
-
-    uint const bit_val = (((1 << target) & state) > 0) ? 1 : 0;
-    uint const control1_val = (((1 << control1) & state) > 0) ? 1 : 0;
-    uint const control2_val = (((1 << control2) & state) > 0) ? 1 : 0;
-
-    if (control1_val == 0 || control2_val == 0)
-    {
-        // Control is 0, don't apply gate
-        amps[state] = amp;
-    }
-    else
-    {
-        // control is 1, apply gate.
-        if (bit_val == 0)
-        {
-            // Bitval = 0
-            amps[state] = add(mul(A, amp), mul(B, amplitudes[one_state]));
-        }
-        else
-        {
-            amps[state] = add(mul(D, amp), mul(C, amplitudes[zero_state]));
-        }
-    }
-}
-
-/*
- * Swaps the states of two qubits in the register
- * NOT MIGRATED
- */
-__kernel void swap(
-    __global complex_f *const amplitudes,
-    __global complex_f *amps,
-    uint first_qubit,
-    uint second_qubit)
-{
-    uint const state = get_global_id(0);
-
-    uint const first_bit_mask = 1 << first_qubit;
-    uint const second_bit_mask = 1 << second_qubit;
-
-    uint const new_second_bit = ((state & first_bit_mask) >> first_qubit) << second_qubit;
-    uint const new_first_bit = ((state & second_bit_mask) >> second_qubit) << first_qubit;
-
-    uint const new_state = (state & !first_bit_mask & !second_bit_mask) | new_first_bit | new_second_bit;
-
-    amps[new_state] = amplitudes[state];
-}
-
-
-/**
- * Calculates The Probabilities Of A State Vector
- */
-__kernel void calculate_probabilities(
-    __global complex_f *const amplitudes,
-    __global float *probabilities)
-{
-    uint const state = get_global_id(0);
-    complex_f amp = amplitudes[state];
-
-    probabilities[state] = complex_abs(mul(amp, amp));
-}
-
-/**
- * Initializes a register to the value 1|0..100...0>
- *                                          ^ target
- */
-__kernel void initialize_register(
-    __global complex_f *amplitudes,
-    uint const target)
-{
-    uint const state = get_global_id(0);
-    if (state == target)
-    {
-        amplitudes[state] = (complex_f)(1, 0);
-    }
-    else
-    {
-        amplitudes[state] = (complex_f)(0, 0);
-    }
-}

+ 0 - 199
src/backends/opencl/mod.rs

@@ -1,199 +0,0 @@
-use gate::Gate;
-use traits::Backend;
-
-use failure::Error;
-use num_complex::{Complex, Complex32};
-use ocl::{Buffer, MemFlags, ProQue};
-use rand::random;
-use std::fmt;
-
-// OpenCL Kernel
-pub static KERNEL: &'static str = include_str!("kernel.cl");
-
-#[derive(Debug)]
-pub struct OpenCL {
-    /// OpenCL Buffer for the state vector
-    pub buffer: Buffer<Complex<f32>>,
-    pro_que: ProQue,
-    num_qubits: u8,
-}
-
-impl OpenCL {
-    /// Initialize a new OpenCL Backend
-    ///
-    /// Takes an argument of the number of qubits to use
-    /// in the register, and returns a result with the backend.
-    pub fn new(num_qubits: u8) -> Result<OpenCL, Error> {
-        // How many amplitudes needed?
-        let num_amps = 2_usize.pow(u32::from(num_qubits)) as usize;
-
-        let ocl_pq = ProQue::builder()
-            .src(KERNEL)
-            .device(1)
-            .dims(num_amps)
-            .build()?;
-
-        let buffer: Buffer<Complex32> = Buffer::builder()
-            .queue(ocl_pq.queue().clone())
-            .flags(MemFlags::new().read_write())
-            .len(num_amps)
-            .build()?;
-
-        let apply = ocl_pq
-            .kernel_builder("initialize_register")
-            .arg(&buffer)
-            .arg(0)
-            .build()?;
-
-        unsafe {
-            apply.enq()?;
-        }
-
-        Ok(OpenCL {
-            pro_que: ocl_pq,
-            buffer,
-            num_qubits,
-        })
-    }
-
-    /// Note that this method doesn't mutate the state, thus
-    /// a new vector must be created, which means you will have to have
-    /// enough memory to store another object half the size of the
-    /// state vector
-    ///
-    /// **This methods is very likely to change!**
-    fn get_probabilities(&self) -> Result<Vec<f32>, Error> {
-        let result_buffer: Buffer<f32> = self.pro_que.create_buffer()?;
-
-        let apply = self
-            .pro_que
-            .kernel_builder("calculate_probabilities")
-            .arg(&self.buffer)
-            .arg(&result_buffer)
-            .build()?;
-
-        unsafe {
-            apply.enq()?;
-        }
-
-        let mut vec_result = vec![0.0f32; self.buffer.len()];
-        result_buffer.read(&mut vec_result).enq()?;
-
-        Ok(vec_result)
-    }
-}
-
-impl Backend for OpenCL {
-    fn apply_gate(&mut self, gate: Gate, target: u8) -> Result<(), Error> {
-        let apply = self
-            .pro_que
-            .kernel_builder("apply_gate")
-            .global_work_size(&self.buffer.len() / 2)
-            .arg(&self.buffer)
-            .arg(i32::from(target))
-            .arg(gate.a)
-            .arg(gate.b)
-            .arg(gate.c)
-            .arg(gate.d)
-            .build()?;
-
-        unsafe {
-            apply.enq()?;
-        }
-
-        Ok(())
-    }
-
-    fn apply_controlled_gate(&mut self, gate: Gate, control: u8, target: u8) -> Result<(), Error> {
-        let apply = self
-            .pro_que
-            .kernel_builder("apply_controlled_gate")
-            .global_work_size(&self.buffer.len() / 2)
-            .arg(&self.buffer)
-            .arg(i32::from(control))
-            .arg(i32::from(target))
-            .arg(gate.a)
-            .arg(gate.b)
-            .arg(gate.c)
-            .arg(gate.d)
-            .build()?;
-
-        unsafe {
-            apply.enq()?;
-        }
-
-        Ok(())
-    }
-
-    /// Measure the whole register, leaving the register in
-    /// the measured state
-    /// 
-    /// Note: Currently this leaves the register in the unmeasured state.
-    fn measure(&mut self) -> Result<u64, Error> {
-        let probabilities = self.get_probabilities()?;
-
-        // A key must be generated on the host, as most
-        // external accelerators do not have in built support
-        // for random number generation
-        let mut key = random::<f32>();
-        if key > 1.0 {
-            key %= 1.0;
-        }
-
-        let mut i = 0;
-        while i < probabilities.len() {
-            key -= probabilities[i];
-            if key <= 0.0 {
-                break;
-            }
-            i += 1;
-        }
-
-        Ok(i as u64)
-    }
-
-    /// Measure the value of a single qubit, leaving the register in
-    /// the state where only that qubit (or any entangled qubits) have
-    /// been collapsed
-    fn measure_qubit(&mut self, _target: u8) -> Result<u64, Error> {
-        unimplemented!()
-    }
-
-    /// Get the number of qubits that this backend was initialized for
-    fn num_qubits(&self) -> u8 {
-        self.num_qubits
-    }
-}
-
-impl fmt::Display for OpenCL {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let mut first = true;
-
-        let mut vec_result = vec![Complex32::new(0.0, 0.0); self.buffer.len()];
-        self.buffer
-            .read(&mut vec_result)
-            .enq()
-            .expect("Error Reading Memory From Device");
-
-        for (idx, item) in vec_result.iter().enumerate() {
-            if !first {
-                write!(f, ", ")?;
-            } else {
-                first = false;
-            }
-
-            write!(f, "[{}]: ", idx)?;
-
-            // Do we print the imaginary part?
-            if item.im == 0.0 {
-                write!(f, "{}", item.re)?;
-            } else if item.re == 0.0 {
-                write!(f, "{}i", item.im)?;
-            } else {
-                write!(f, "{}", item)?;
-            }
-        }
-
-        Ok(())
-    }
-}

+ 0 - 49
src/error.rs

@@ -1,49 +0,0 @@
-use failure::{Backtrace, Context, Fail};
-use std::fmt;
-
-#[derive(Debug)]
-struct BackendError {
-    inner: Context<BackendErrorKind>,
-}
-
-#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
-enum BackendErrorKind {
-    #[fail(display = "System doesn't have enough memory.")]
-    MemoryError,
-}
-
-impl Fail for BackendError {
-    fn cause(&self) -> Option<&Fail> {
-        self.inner.cause()
-    }
-
-    fn backtrace(&self) -> Option<&Backtrace> {
-        self.inner.backtrace()
-    }
-}
-
-impl fmt::Display for BackendError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(&self.inner, f)
-    }
-}
-
-// impl BackendError {
-//     pub fn kind(&self) -> BackendErrorKind {
-//         *self.inner.get_context()
-//     }
-// }
-
-impl From<BackendErrorKind> for BackendError {
-    fn from(kind: BackendErrorKind) -> BackendError {
-        BackendError {
-            inner: Context::new(kind),
-        }
-    }
-}
-
-impl From<Context<BackendErrorKind>> for BackendError {
-    fn from(inner: Context<BackendErrorKind>) -> BackendError {
-        BackendError { inner }
-    }
-}

+ 0 - 90
src/gate.rs

@@ -1,90 +0,0 @@
-use num_complex::Complex32;
-use std::f32::consts::FRAC_1_SQRT_2;
-use std::fmt;
-
-/// Representation of a gate
-///
-/// ```
-///# extern crate qcgpu;
-///# extern crate num_complex;
-///# use qcgpu::Gate;
-///# use num_complex::Complex32;
-/// Gate {
-///    a: Complex32::new(0.0, 0.0), b: Complex32::new(1.0, 0.0),
-///    c: Complex32::new(1.0, 0.0), d: Complex32::new(0.0, 0.0)
-/// };
-///
-///
-#[derive(Debug, Clone, Copy)]
-pub struct Gate {
-    pub a: Complex32,
-    pub b: Complex32,
-    pub c: Complex32,
-    pub d: Complex32,
-}
-
-impl fmt::Display for Gate {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "[[{}, {}], [{}, {}]]", self.a, self.b, self.c, self.d)
-    }
-}
-
-/// Hadamard Gate
-///
-/// [0.70710678118, 0.70710678118]
-///
-/// [0.70710678118, -0.70710678118]
-#[inline]
-pub fn h() -> Gate {
-    Gate {
-        a: Complex32::new(FRAC_1_SQRT_2, 0.0),
-        b: Complex32::new(FRAC_1_SQRT_2, 0.0),
-        c: Complex32::new(FRAC_1_SQRT_2, 0.0),
-        d: Complex32::new(-FRAC_1_SQRT_2, 0.0),
-    }
-}
-
-/// Pauli X / NOT Gate
-///
-/// [0, 1]
-///
-/// [1, 0]
-#[inline]
-pub fn x() -> Gate {
-    Gate {
-        a: Complex32::new(0.0, 0.0),
-        b: Complex32::new(1.0, 0.0),
-        c: Complex32::new(1.0, 0.0),
-        d: Complex32::new(0.0, 0.0),
-    }
-}
-
-/// Pauli Y Gate
-///
-/// [0, -i]
-///
-/// [i, 0]
-#[inline]
-pub fn y() -> Gate {
-    Gate {
-        a: Complex32::new(0.0, 0.0),
-        b: Complex32::new(0.0, -1.0),
-        c: Complex32::new(0.0, 1.0),
-        d: Complex32::new(0.0, 0.0),
-    }
-}
-
-/// Pauli Z Gate
-///
-/// [1, 0]
-///
-/// [0, -1]
-#[inline]
-pub fn z() -> Gate {
-    Gate {
-        a: Complex32::new(1.0, 0.0),
-        b: Complex32::new(0.0, 0.0),
-        c: Complex32::new(0.0, 0.0),
-        d: Complex32::new(-1.0, 0.0),
-    }
-}

+ 0 - 75
src/lib.rs

@@ -1,75 +0,0 @@
-extern crate failure;
-extern crate num_complex;
-extern crate ocl;
-extern crate rand;
-
-#[macro_use]
-extern crate failure_derive;
-
-pub mod backends;
-pub mod error;
-pub mod gate;
-pub mod traits;
-
-use backends::OpenCL;
-pub use gate::{h, x, y, z, Gate};
-
-use failure::Error;
-use std::fmt;
-
-#[derive(Debug)]
-pub struct Simulator {
-    backend: Box<traits::Backend>,
-    num_qubits: u8,
-}
-
-impl Simulator {
-    pub fn new_opencl(num_qubits: u8) -> Result<Simulator, Error> {
-        let backend = OpenCL::new(num_qubits)?;
-
-        Ok(Simulator {
-            backend: Box::new(backend),
-            num_qubits,
-        })
-    }
-
-    pub fn apply_gate(&mut self, gate: Gate, target: u8) -> Result<(), Error> {
-        self.backend.apply_gate(gate, target)
-    }
-
-    pub fn apply_all(&mut self, gate: Gate) -> Result<(), Error> {
-        for i in 0..self.num_qubits {
-            self.backend.apply_gate(gate, i)?
-        }
-
-        Ok(())
-    }
-
-    pub fn x(&mut self, target: u8) -> Result<(), Error> {
-        self.backend.apply_gate(x(), target)
-    }
-    pub fn y(&mut self, target: u8) -> Result<(), Error> {
-        self.backend.apply_gate(y(), target)
-    }
-    pub fn z(&mut self, target: u8) -> Result<(), Error> {
-        self.backend.apply_gate(z(), target)
-    }
-    pub fn h(&mut self, target: u8) -> Result<(), Error> {
-        self.backend.apply_gate(h(), target)
-    }
-    pub fn cx(&mut self, control: u8, target: u8) -> Result<(), Error> {
-        self.backend.apply_controlled_gate(x(), control, target)
-    }
-    pub fn measure(&mut self) -> Result<u64, Error> {
-        self.backend.measure()
-    }
-    pub fn num_qubits(&mut self) -> u8 {
-        self.backend.num_qubits()
-    }
-}
-
-impl fmt::Display for Simulator {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", self.backend)
-    }
-}

+ 0 - 26
src/traits.rs

@@ -1,26 +0,0 @@
-use failure::Error;
-use gate::Gate;
-use std::fmt::{Debug, Display};
-
-pub trait Backend: Debug + Display {
-    fn num_qubits(&self) -> u8;
-    fn apply_gate(&mut self, gate: Gate, target: u8) -> Result<(), Error>;
-    fn apply_controlled_gate(&mut self, gate: Gate, control: u8, target: u8) -> Result<(), Error>;
-    fn measure_qubit(&mut self, target: u8) -> Result<u64, Error>;
-
-    fn measure(&mut self) -> Result<u64, Error> {
-        let mut result = 0;
-        for i in 0..self.num_qubits() {
-            let bit_mask = 1 << i;
-            if self.measure_qubit(i)? == 1 {
-                // 1, set the bit in result
-                result |= bit_mask
-            } else {
-                // 0, clear the bit in result
-                result &= !bit_mask
-            }
-        }
-
-        Ok(result)
-    }
-}

+ 10 - 0
testing.py

@@ -0,0 +1,10 @@
+import qcgpu
+
+state = qcgpu.State(2)
+h = qcgpu.gate.h()
+x = qcgpu.gate.x()
+
+state.apply_gate(x, 0)
+state.apply_controlled_gate(x, 0)
+
+print(state)

+ 0 - 101
tests/opencl.rs

@@ -1,101 +0,0 @@
-extern crate qcgpu;
-
-use qcgpu::Simulator;
-use qcgpu::gate::x;
-
-fn create_simulator(n: u8) -> Simulator {
-    let sim = Simulator::new_opencl(n);
-
-    assert!(
-            sim.is_ok(),
-            "Error initializing OpenCL simulator"
-        );
-
-    return sim.unwrap();
-}
-
-#[test]
-fn can_initialize_simulator() {
-    for n in 1..25 {
-        create_simulator(n);
-    }
-}
-
-#[test]
-fn can_apply_x_gate() {
-    for n in 1..25 {
-        let mut sim = create_simulator(n);
-
-        for i in 0..n {
-            assert!(
-                sim.x(i).is_ok(),
-                "Error applying pauli-x (not) gate to simulator"
-            );
-        }
-    }
-}
-
-#[test]
-fn can_apply_y_gate() {
-    for n in 1..25 {
-        let mut sim = create_simulator(n);
-
-        for i in 0..n {
-            assert!(
-                sim.y(i).is_ok(),
-                "Error applying pauli-y gate to simulator"
-            );
-        }
-    }
-}
-
-#[test]
-fn can_apply_z_gate() {
-    for n in 1..25 {
-        let mut sim = create_simulator(n);
-
-        for i in 0..n {
-            assert!(
-                sim.z(i).is_ok(),
-                "Error applying pauli-z gate to simulator"
-            );
-        }
-    }
-}
-
-#[test]
-fn can_apply_h_gate() {
-    for n in 1..25 {
-        let mut sim = create_simulator(n);
-
-        for i in 0..n {
-            assert!(
-                sim.h(i).is_ok(),
-                "Error applying hadamard gate to simulator"
-            );
-        }
-    }
-}
-
-#[test]
-fn can_apply_cx_gate() {
-    for n in 2..25 {
-        let mut sim = create_simulator(n);
-
-        for i in 1..n {
-            assert!(
-                sim.cx(0, i).is_ok(),
-                "Error applying controlled not gate to simulator"
-            );
-        }
-    }
-}
-
-#[test]
-fn can_perform_measurement() {
-    for n in 1..25 {
-        let mut sim = create_simulator(n);
-        sim.apply_all(x()).unwrap();
-        assert_eq!(sim.measure().unwrap() as i32, 2i32.pow(n as u32) - 1);
-    }
-}