diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91639d4..7e88c8b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,7 +121,7 @@ jobs: - js-test strategy: matrix: - package-type: [ "nodejs", "bundler", "web" ] + package-type: [ "nodejs", "bundler", "web", "web-parallel" ] steps: - uses: actions/checkout@v2 name: Clone @@ -150,9 +150,17 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - uses: actions-rs/toolchain@v1 name: Setup Rust + if: matrix.package-type != 'web-parallel' with: toolchain: nightly override: true + - uses: actions-rs/toolchain@v1 + name: Setup Rust + if: matrix.package-type == 'web-parallel' + with: + toolchain: nightly + override: true + components: rust-src - uses: jetli/wasm-pack-action@v0.3.0 name: Install WasmPack with: @@ -160,9 +168,15 @@ jobs: - name: Install npm dependencies run: yarn install - name: Build Wasm + if: matrix.package-type != 'web-parallel' run: | wasm-pack build --target ${{ matrix.package-type }} -- --features wasm-pack --no-default-features rm pkg/{package.json,README.md,.gitignore} + - name: Build Wasm + if: matrix.package-type == 'web-parallel' + run: | + RUSTFLAGS='-C target-feature=+atomics,+bulk-memory,+mutable-globals' wasm-pack build --target web -- --features wasm-pack,safe,wasm-parallel --no-default-features -Z build-std=panic_abort,std + rm pkg/{package.json,README.md,.gitignore} - name: Build Ts run: yarn run build:ts:${{ matrix.package-type }} - name: Assemble @@ -191,6 +205,10 @@ jobs: with: name: pkg-web path: pkg.web + - uses: actions/download-artifact@master + with: + name: pkg-web-parallel + path: pkg.web-parallel - uses: actions/upload-artifact@v2 name: Upload Artifacts with: @@ -199,6 +217,7 @@ jobs: pkg.bundler/ pkg.nodejs/ pkg.web/ + pkg.web-parallel/ package.json index.browser.js LICENCE diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1f6a740..0fbe477 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -124,7 +124,7 @@ jobs: - js-test strategy: matrix: - package-type: [ "nodejs", "bundler", "web" ] + package-type: [ "nodejs", "bundler", "web", "web-parallel" ] steps: - uses: actions/checkout@v2 name: Clone @@ -153,9 +153,17 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - uses: actions-rs/toolchain@v1 name: Setup Rust + if: matrix.package-type != 'web-parallel' with: toolchain: nightly override: true + - uses: actions-rs/toolchain@v1 + name: Setup Rust + if: matrix.package-type == 'web-parallel' + with: + toolchain: nightly + override: true + components: rust-src - uses: jetli/wasm-pack-action@v0.3.0 name: Install WasmPack with: @@ -163,9 +171,15 @@ jobs: - name: Install npm dependencies run: yarn install - name: Build Wasm + if: matrix.package-type != 'web-parallel' run: | wasm-pack build --target ${{ matrix.package-type }} -- --features wasm-pack --no-default-features rm pkg/{package.json,README.md,.gitignore} + - name: Build Wasm + if: matrix.package-type == 'web-parallel' + run: | + RUSTFLAGS='-C target-feature=+atomics,+bulk-memory,+mutable-globals' wasm-pack build --target web -- --features wasm-pack,safe,wasm-parallel --no-default-features -Z build-std=panic_abort,std + rm pkg/{package.json,README.md,.gitignore} - name: Build Ts run: yarn run build:ts:${{ matrix.package-type }} - name: Assemble @@ -194,6 +208,10 @@ jobs: with: name: pkg-web path: pkg.web + - uses: actions/download-artifact@master + with: + name: pkg-web-parallel + path: pkg.web-parallel - uses: actions/upload-artifact@v2 name: Upload Artifacts with: @@ -202,6 +220,7 @@ jobs: pkg.bundler/ pkg.nodejs/ pkg.web/ + pkg.web-parallel/ package.json index.browser.js LICENCE diff --git a/.gitignore b/.gitignore index f7c67d8..73a0b54 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ /pkg.nodejs /pkg.dist /pkg.web +/pkg.web-parallel /dist /package .pnp.* diff --git a/Cargo.lock b/Cargo.lock index b0008a6..c342b90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,23 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bit-array" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87124d87d4baa369b13485ffc0237a1b41e51c1d8ded7c4e01c002f79ed20a19" +dependencies = [ + "bit-vec", + "generic-array", + "typenum", +] + +[[package]] +name = "bit-vec" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" + [[package]] name = "bumpalo" version = "3.10.0" @@ -36,6 +53,51 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "memoffset", + "once_cell", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", +] + [[package]] name = "darling" version = "0.14.1" @@ -71,6 +133,12 @@ dependencies = [ "syn", ] +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "float-cmp" version = "0.9.0" @@ -86,6 +154,25 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "generic-array" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d8e56f1cb411920d382decdf54ed2273d1cf392725397fe7d0fad2d5397187" +dependencies = [ + "nodrop", + "typenum", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -134,12 +221,27 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "memory_units" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + [[package]] name = "num-traits" version = "0.2.15" @@ -149,6 +251,22 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" + [[package]] name = "proc-macro2" version = "1.0.39" @@ -160,17 +278,21 @@ dependencies = [ [[package]] name = "qukit" -version = "0.0.0-pre2" +version = "0.0.0-pre3" dependencies = [ + "bit-array", "console_error_panic_hook", "float-cmp", "js-sys", "libm", "rand", + "rayon", "serde", "serde-wasm-bindgen", "tsify", + "typenum", "wasm-bindgen", + "wasm-bindgen-rayon", "wee_alloc", ] @@ -198,12 +320,42 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +[[package]] +name = "rayon" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + [[package]] name = "ryu" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "serde" version = "1.0.137" @@ -257,6 +409,12 @@ dependencies = [ "serde", ] +[[package]] +name = "spmc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02a8428da277a8e3a15271d79943e80ccc2ef254e78813a166a08d65e4c3ece5" + [[package]] name = "strsim" version = "0.10.0" @@ -276,8 +434,9 @@ dependencies = [ [[package]] name = "tsify" -version = "0.2.0" -source = "git+https://github.com/28Smiles/tsify?rev=ea87fda#ea87fdadcdd87cef5fb036cfc2d257224c87b966" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a44e83b0e49e19860fbad4d577c134164751d7c1cab8251563b12249b74c7e7" dependencies = [ "tsify-macros", "wasm-bindgen", @@ -285,8 +444,9 @@ dependencies = [ [[package]] name = "tsify-macros" -version = "0.2.0" -source = "git+https://github.com/28Smiles/tsify?rev=ea87fda#ea87fdadcdd87cef5fb036cfc2d257224c87b966" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cef9fb1445892cfd74e03698917a558b28445960d6331eec12eac54caa88a79" dependencies = [ "darling", "proc-macro2", @@ -295,6 +455,12 @@ dependencies = [ "syn", ] +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + [[package]] name = "unicode-ident" version = "1.0.1" @@ -351,6 +517,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-rayon" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df87c67450805c305d3ae44a3ac537b0253d029153c25afc3ecd2edc36ccafb1" +dependencies = [ + "js-sys", + "rayon", + "spmc", + "wasm-bindgen", +] + [[package]] name = "wasm-bindgen-shared" version = "0.2.81" diff --git a/Cargo.toml b/Cargo.toml index ef1f260..fade000 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qukit" -version = "0.0.0-pre2" +version = "0.0.0-pre3" description = "a quantum simulator for rust and wasm" repository = "https://github.com/28Smiles/qukit" authors = ["Leon Camus "] @@ -13,16 +13,22 @@ crate-type = ["cdylib", "rlib"] [features] std = [] +safe = [] +parallel = ["rayon"] +wasm-parallel = ["parallel", "wasm-bindgen-rayon"] wasm-pack = [ "serde", "wasm-bindgen", "tsify", "wee_alloc", "console_error_panic_hook", "js-sys", "serde-wasm-bindgen" ] -default = [ "std" ] +default = [ "std", "parallel" ] [dependencies] libm = "0.2" +rayon = { version = "1.5", optional = true } +wasm-bindgen-rayon = { version = "1.0", optional = true } +typenum = "1.15" +bit-array = "0.4" rand = { version = "0.8", default-features = false, features = ["small_rng"] } serde = { version = "1.0", features = ["derive"], optional = true } wasm-bindgen = { version = "0.2", features = ["serde-serialize"], optional = true } -#tsify = { version = "0.2", optional = true } -tsify = { git = "https://github.com/28Smiles/tsify", rev = "ea87fda", optional = true } +tsify = { version = "0.3", optional = true } console_error_panic_hook = { version = "0.1", optional = true } wee_alloc = { version = "0.4", optional = true } js-sys = { version = "0.3", optional = true } diff --git a/README.md b/README.md index 6492189..de87eaf 100644 --- a/README.md +++ b/README.md @@ -7,4 +7,13 @@ # Qukit - Quantum Simulation Toolkit -[Qukit](https://github.com/28Smiles/qukit) is an open source quantum circuit simulator implemented in rust and compiled for wasm. [Qukit](https://github.com/28Smiles/qukit) is capable of running 30+ qubit simulations in browser or at the server (rust and node.js). You can use it in your javascript program to run quantum simulations. +[Qukit](https://github.com/28Smiles/qukit) is an open source quantum circuit simulator implemented in rust and compiled for wasm. [Qukit](https://github.com/28Smiles/qukit) is capable of running 20+ q-bit simulations in browser or at the server (rust and node.js). You can use it in your javascript program to run quantum simulations. + +## Wasm Limitations + +In wasm you are limited to 2GB/4GB of memory, this means, with this library you are able to simulate up to 25 q-bits. +For a 25 q-bit system we need to keep track of `2^26` states. +A state is described by a complex value, which is composed of 2 `f64` values, which equates to `2 x 8 = 16 Bytes`. +This equates to a state vector of `2^26 x 16 = 1073731824 Bytes ≈ 1.07 GB`. +For each transformation we need one source and one target vector, this leads to a memory usage of `2.14 GB`. +With a future stabilisation of wasm64 we can simulate large vectors. diff --git a/lib/operators/special/measurement.ts b/lib/operators/special/measurement.ts index f644fbe..be25b0f 100644 --- a/lib/operators/special/measurement.ts +++ b/lib/operators/special/measurement.ts @@ -1,8 +1,8 @@ import {WasmLib} from "@/index"; import {OneWire} from "@/operators/internal"; -export class Measure extends OneWire implements WasmLib.Special.Measure { - type: "Measure" = "Measure"; +export class Measurement extends OneWire implements WasmLib.Special.Measurement { + type: "Measurement" = "Measurement"; basis?: WasmLib.MeasurementBasis; creg?: number; cregBit?: number; diff --git a/package.json b/package.json index c9e8d0a..34d307d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "qukit", - "version": "0.0.0-pre2", + "version": "0.0.0-pre3", "main": "./pkg.nodejs/index.js", "types": "./pkg.nodejs/index.d.ts", "browser": "./index.browser.js", @@ -9,24 +9,33 @@ "pkg.nodejs", "pkg.bundler", "pkg.web", + "pkg.web-parallel", "index.browser.js", "README.md", "LICENCE" ], "scripts": { - "build:wasm:nodejs": "rm -rf pkg && wasm-pack build --target nodejs -- --features wasm-pack --no-default-features && rm pkg/{package.json,README.md,.gitignore}", + "build:wasm:nodejs": "rm -rf pkg && wasm-pack build --target nodejs -- --features wasm-pack,safe --no-default-features && rm pkg/{package.json,README.md,.gitignore}", "build:ts:nodejs": "rm -rf dist && tsc --project tsconfig.node.json", "build:assemble:nodejs": "rm -rf pkg.nodejs && mkdir pkg.nodejs && mv dist/* pkg.nodejs && mv pkg pkg.nodejs/pkg && cp tsconfig.dist.json ./pkg.nodejs/tsconfig.json && tscpaths -p ./pkg.nodejs/tsconfig.json -s ./pkg.nodejs -o ./pkg.nodejs && rm ./pkg.nodejs/tsconfig.json", "build:nodejs": "yarn run build:wasm:nodejs && yarn run build:ts:nodejs && yarn run build:assemble:nodejs", - "build:wasm:bundler": "rm -rf pkg && wasm-pack build --target bundler -- --features wasm-pack --no-default-features && rm pkg/{package.json,README.md,.gitignore}", + + "build:wasm:bundler": "rm -rf pkg && wasm-pack build --target bundler -- --features wasm-pack,safe --no-default-features && rm pkg/{package.json,README.md,.gitignore}", "build:ts:bundler": "rm -rf dist && tsc --project tsconfig.json", "build:assemble:bundler": "rm -rf pkg.bundler && mkdir pkg.bundler && mv dist/* pkg.bundler && mv pkg pkg.bundler/pkg && cp tsconfig.dist.json ./pkg.bundler/tsconfig.json && tscpaths -p ./pkg.bundler/tsconfig.json -s ./pkg.bundler -o ./pkg.bundler && rm ./pkg.bundler/tsconfig.json", "build:bundler": "yarn run build:wasm:bundler && yarn run build:ts:bundler && yarn run build:assemble:bundler", - "build:wasm:web": "rm -rf pkg && wasm-pack build --target web -- --features wasm-pack --no-default-features && rm pkg/{package.json,README.md,.gitignore}", + + "build:wasm:web": "rm -rf pkg && wasm-pack build --target web -- --features wasm-pack,safe --no-default-features && rm pkg/{package.json,README.md,.gitignore}", "build:ts:web": "rm -rf dist && tsc --project tsconfig.json", "build:assemble:web": "rm -rf pkg.web && mkdir pkg.web && mv dist/* pkg.web && mv pkg pkg.web/pkg && cp tsconfig.dist.json ./pkg.web/tsconfig.json && tscpaths -p ./pkg.web/tsconfig.json -s ./pkg.web -o ./pkg.web && rm ./pkg.web/tsconfig.json", "build:web": "yarn run build:wasm:web && yarn run build:ts:web && yarn run build:assemble:web", - "build": "yarn run build:nodejs && yarn run build:bundler && yarn run build:web", + + "build:wasm:web-parallel": "rm -rf pkg && RUSTFLAGS='-C target-feature=+atomics,+bulk-memory,+mutable-globals'; wasm-pack build --target web -- --features wasm-pack,safe,wasm-parallel --no-default-features -Z build-std=panic_abort,std && rm pkg/{package.json,README.md,.gitignore}", + "build:ts:web-parallel": "rm -rf dist && tsc --project tsconfig.json", + "build:assemble:web-parallel": "rm -rf pkg.web-parallel && mkdir pkg.web-parallel && mv dist/* pkg.web-parallel && mv pkg pkg.web-parallel/pkg && cp tsconfig.dist.json ./pkg.web-parallel/tsconfig.json && tscpaths -p ./pkg.web-parallel/tsconfig.json -s ./pkg.web-parallel -o ./pkg.web-parallel && rm ./pkg.web-parallel/tsconfig.json", + "build:web-parallel": "yarn run build:wasm:web-parallel && yarn run build:ts:web-parallel && yarn run build:assemble:web-parallel", + + "build": "yarn run build:nodejs && yarn run build:bundler && yarn run build:web && yarn run build:web-parallel", "pretest": "rm -rf pkg && wasm-pack build --target nodejs -- --features wasm-pack --no-default-features", "test": "jest test/wasm/*", "test:wasm": "jest test/wasm/*", diff --git a/src/bindgen.rs b/src/bindgen.rs index 7fe0e34..986f2d6 100644 --- a/src/bindgen.rs +++ b/src/bindgen.rs @@ -3,6 +3,9 @@ use js_sys::Float64Array; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; +#[cfg(feature = "wasm-bindgen-rayon")] +pub use wasm_bindgen_rayon::init_thread_pool; + #[wasm_bindgen] extern "C" { #[wasm_bindgen(typescript_type = "Complex[]")] @@ -19,7 +22,10 @@ pub struct QuantumComputer(crate::quantum::computer::QuantumComputer); impl QuantumComputer { /// Creates a new quantum computer instance #[wasm_bindgen(constructor)] - pub fn new(q_bits: u32, seed: Option) -> QuantumComputer { + pub fn new(q_bits: usize, seed: Option) -> QuantumComputer { + #[cfg(feature="console_error_panic_hook")] + console_error_panic_hook::set_once(); + QuantumComputer(crate::quantum::computer::QuantumComputer::new(q_bits, seed)) } @@ -39,7 +45,7 @@ impl QuantumComputer { } /// Returns the probability of a qbit - pub fn probability(&self, bit: u32) -> f64 { + pub fn probability(&self, bit: usize) -> f64 { self.0.probability(bit) } } @@ -52,6 +58,9 @@ impl QuantumAlgorithm { /// Creates a new quantum algorithm instance #[wasm_bindgen(constructor)] pub fn new(gates: AlgorithmGateArray, step_size: Option) -> QuantumAlgorithm { + #[cfg(feature="console_error_panic_hook")] + console_error_panic_hook::set_once(); + QuantumAlgorithm(crate::quantum::algorithm::QuantumAlgorithm::new( match serde_wasm_bindgen::from_value(gates.unchecked_into()) { Ok(value) => value, diff --git a/src/complex.rs b/src/complex.rs index 056ba79..412a567 100644 --- a/src/complex.rs +++ b/src/complex.rs @@ -94,6 +94,12 @@ impl Display for Complex { } } +impl Default for Complex { + fn default() -> Self { + Complex::zero() + } +} + #[cfg(test)] mod test { use crate::complex::Complex; diff --git a/src/lib.rs b/src/lib.rs index 06d30f5..2bbce4c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,9 @@ #![feature(test)] #![feature(core_intrinsics)] #![feature(extern_types)] +#![feature(bench_black_box)] +extern crate typenum; extern crate alloc; #[cfg(feature = "std")] @@ -15,11 +17,6 @@ extern crate std; #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; -#[cfg(feature="console_error_panic_hook")] -pub fn set_panic_hook() { - console_error_panic_hook::set_once(); -} - pub mod error; pub mod complex; pub mod quantum; diff --git a/src/quantum/algorithm.rs b/src/quantum/algorithm.rs index de72287..8383405 100644 --- a/src/quantum/algorithm.rs +++ b/src/quantum/algorithm.rs @@ -17,7 +17,7 @@ pub struct AlgorithmGate { } impl AlgorithmGate { - fn new(position: u32, gate: OperatorType) -> AlgorithmGate { + pub fn new(position: u32, gate: OperatorType) -> AlgorithmGate { AlgorithmGate { position, gate, diff --git a/src/quantum/computer.rs b/src/quantum/computer.rs index f998dc3..8d5f0a6 100644 --- a/src/quantum/computer.rs +++ b/src/quantum/computer.rs @@ -11,7 +11,7 @@ pub struct QuantumComputer { } impl QuantumComputer { - pub fn new(q_bits: u32, seed: Option) -> QuantumComputer { + pub fn new(q_bits: usize, seed: Option) -> QuantumComputer { QuantumComputer { state: Ket::new(q_bits), seed: SmallRng::seed_from_u64(seed.unwrap_or(42)), @@ -31,11 +31,11 @@ impl QuantumComputer { for state_id in 0..self.state.vec.len() { for bit_id in 0..self.state.size { if state_id & (0x1 << bit_id) > 0 { - #[cfg(not(test))] + #[cfg(all(not(test), not(feature = "safe")))] { *unsafe { probalillites.get_unchecked_mut(bit_id as usize) } += unsafe { self.state.vec.get_unchecked(state_id) }.amplitude(); } - #[cfg(test)] + #[cfg(any(test, feature = "safe"))] { *{ probalillites.get_mut(bit_id as usize).unwrap() } += self.state.vec.get(state_id).unwrap().amplitude(); } @@ -46,17 +46,17 @@ impl QuantumComputer { return probalillites } - pub fn probability(&self, bit: u32) -> f64 { + pub fn probability(&self, bit: usize) -> f64 { assert!(self.state.size > bit); let mut proballily = 0.0; let bit_m = 0x1 << bit; for state_id in 0..self.state.vec.len() { if state_id & bit_m > 0 { - #[cfg(not(test))] + #[cfg(all(not(test), not(feature = "safe")))] { proballily += unsafe { self.state.vec.get_unchecked(state_id) }.amplitude(); } - #[cfg(test)] + #[cfg(any(test, feature = "safe"))] { proballily += self.state.vec.get(state_id).unwrap().amplitude(); } diff --git a/src/quantum/gate/matrix/const_sized.rs b/src/quantum/gate/matrix/const_sized.rs index b3b5373..7fcb6dd 100644 --- a/src/quantum/gate/matrix/const_sized.rs +++ b/src/quantum/gate/matrix/const_sized.rs @@ -1,10 +1,13 @@ use alloc::vec::Vec; -use core::ops::{BitXor, Mul, Not}; +use core::ops::{BitXor, Mul}; use crate::complex::Complex; use crate::quantum::computer::QuantumComputer; use crate::quantum::gate::matrix::dynamic::DGate; -use crate::quantum::ket::Ket; +use crate::quantum::ket::{IndexType, Ket}; + +#[cfg(feature = "rayon")] +use rayon::prelude::*; #[derive(Copy, Clone, Debug, PartialEq)] pub struct Gate @@ -23,7 +26,7 @@ impl Gate Gate { matrix } } - pub fn apply(&self, computer: &mut QuantumComputer, qbits: [u32; SIZE]) { + pub fn apply(&self, computer: &mut QuantumComputer, qbits: [usize; SIZE]) { // Assert all qbits exist and are different for i in 0..SIZE { assert!(computer.get_state().size >= qbits[i]); @@ -34,42 +37,44 @@ impl Gate } } + let old_vec = &computer.get_state().vec; + let mut new_ket = Ket::new_zero(computer.get_state().size); if SIZE > 0 { - let mut new_ket = Ket::new_zero(computer.get_state().size); - for i in 0_usize..computer.get_state().vec.len() { - #[cfg(not(test))] - let v = *unsafe { computer.get_state().vec.get_unchecked(i) }; - #[cfg(test)] - let v = *computer.get_state().vec.get(i).unwrap(); - - let mut x: usize = 0; - for pos in 0..SIZE { - x |= ((i & (0x1_usize << qbits[pos]) > 0) as usize) << pos; - } - // set all qbit indexes to 0 - let mut ir = i; + #[cfg(feature = "rayon")] + let iter = new_ket.vec.par_iter_mut(); + #[cfg(not(feature = "rayon"))] + let iter = new_ket.vec.iter_mut(); + iter.enumerate().for_each(|(ket_idx, new_ket_value)| { + let mut ket_idx: IndexType = ket_idx.into(); + let mut y = IndexType::from(0); for pos in 0..SIZE { - ir &= (0x1_usize << qbits[pos]).not() + #[cfg(all(not(test), not(feature = "safe")))] + unsafe { y.set(pos, ket_idx.get(qbits[pos]).unwrap_unchecked()) }; + #[cfg(any(test, feature = "safe"))] + y.set(pos, ket_idx.get(qbits[pos]).unwrap()); } - for y in 0..0x1 << SIZE { - let mut it = ir; + let y: usize = y.into(); + + for x in 0..0x1 << SIZE { + let x_idx: IndexType = x.into(); for pos in 0..SIZE { - it |= ((y & (0x1_usize << pos) > 0) as usize) << qbits[pos]; - } - #[cfg(not(test))] - { - *unsafe { new_ket.vec.get_unchecked_mut(it) } = - *unsafe { new_ket.vec.get_unchecked(it) } + self.matrix[x][y] * v; - } - #[cfg(test)] - { - *new_ket.vec.get_mut(it).unwrap() = - *new_ket.vec.get(it).unwrap() + self.matrix[x][y] * v; + #[cfg(all(not(test), not(feature = "safe")))] + unsafe { ket_idx.set(qbits[pos], x_idx.get(pos).unwrap_unchecked()) }; + #[cfg(any(test, feature = "safe"))] + ket_idx.set(qbits[pos], x_idx.get(pos).unwrap()); } + let ket_idx: usize = ket_idx.clone().into(); + + #[cfg(all(not(test), not(feature = "safe")))] + let v = *unsafe { old_vec.get(ket_idx).unwrap_unchecked() }; + #[cfg(any(test, feature = "safe"))] + let v = *old_vec.get(ket_idx).unwrap(); + *new_ket_value = *new_ket_value + self.matrix[x][y] * v; } - } - computer.set_state(new_ket); + }); } + + computer.set_state(new_ket); } pub const fn matrix(&self) -> &[[Complex; 0x1 << SIZE]; 0x1 << SIZE] { @@ -179,9 +184,9 @@ impl Into for Gate } } - #[cfg(not(test))] + #[cfg(all(not(test), not(feature = "safe")))] unsafe { DGate::new(matrix).unwrap_unchecked() } - #[cfg(test)] + #[cfg(any(test, feature = "safe"))] DGate::new(matrix).unwrap() } } diff --git a/src/quantum/gate/matrix/dynamic.rs b/src/quantum/gate/matrix/dynamic.rs index 546e124..3efa076 100644 --- a/src/quantum/gate/matrix/dynamic.rs +++ b/src/quantum/gate/matrix/dynamic.rs @@ -6,7 +6,10 @@ use core::ops::{BitXor, Mul, Not}; use crate::complex::Complex; use crate::error::{QuantumError, Result}; use crate::quantum::computer::QuantumComputer; -use crate::quantum::ket::Ket; +use crate::quantum::ket::{IndexType, Ket}; + +#[cfg(feature = "rayon")] +use rayon::prelude::*; #[cfg(feature = "wasm-pack")] use tsify::Tsify; @@ -30,7 +33,7 @@ impl TryFrom for DGate { pub struct DGate { matrix: Vec, width: usize, - qbit_size: u32, + qbit_size: usize, } impl DGate { @@ -51,7 +54,7 @@ impl DGate { return Ok(DGate { matrix, width: 0x1 << (i / 2), - qbit_size: (i / 2) as u32, + qbit_size: i / 2, }); } } @@ -61,9 +64,9 @@ impl DGate { )) } - pub fn apply(&self, computer: &mut QuantumComputer, qbits: &[u32]) { + pub fn apply(&self, computer: &mut QuantumComputer, qbits: &[usize]) { // Assert all qbits exist and are different - assert_eq!(self.qbit_size, qbits.len() as u32); + assert_eq!(self.qbit_size, qbits.len()); for i in 0..qbits.len() { assert!(computer.get_state().size >= qbits[i]); for j in 0..qbits.len() { @@ -73,41 +76,44 @@ impl DGate { } } + let old_vec = &computer.get_state().vec; let mut new_ket = Ket::new_zero(computer.get_state().size); - for i in 0_usize..computer.get_state().vec.len() { - #[cfg(not(test))] - let v = *unsafe { computer.get_state().vec.get_unchecked(i) }; - #[cfg(test)] - let v = *computer.get_state().vec.get(i).unwrap(); - let mut x: usize = 0; - for pos in 0..qbits.len() { - x |= ((i & (0x1_usize << qbits[pos]) > 0) as usize) << pos; - } - // set all qbit indexes to 0 - let mut ir = i; - for pos in 0..qbits.len() { - ir &= (0x1_usize << qbits[pos]).not() + #[cfg(feature = "rayon")] + let iter = new_ket.vec.par_iter_mut(); + #[cfg(not(feature = "rayon"))] + let iter = new_ket.vec.iter_mut(); + iter.enumerate().for_each(|(ket_idx, new_ket_value)| { + let mut ket_idx: IndexType = ket_idx.into(); + let mut y = IndexType::from(0); + for pos in 0..self.qbit_size { + #[cfg(all(not(test), not(feature = "safe")))] + unsafe { y.set(pos, ket_idx.get(qbits[pos]).unwrap_unchecked()) }; + #[cfg(any(test, feature = "safe"))] + y.set(pos, ket_idx.get(qbits[pos]).unwrap()); } - for y in 0..0x1 << qbits.len() { - let mut it = ir; - for pos in 0..qbits.len() { - it |= ((y & (0x1_usize << pos) > 0) as usize) << qbits[pos]; - } - #[cfg(not(test))] - { - *unsafe { new_ket.vec.get_unchecked_mut(it) } = - *unsafe { new_ket.vec.get_unchecked(it) } - + *unsafe { self.matrix.get_unchecked(x * self.width + y) } * v; - } - #[cfg(test)] - { - *{ new_ket.vec.get_mut(it).unwrap() } = - *{ new_ket.vec.get(it).unwrap() } - + *{ self.matrix.get(x * self.width + y).unwrap() } * v; + let y: usize = y.into(); + + for x in 0..self.width { + let x_idx: IndexType = x.into(); + for pos in 0..self.qbit_size { + #[cfg(all(not(test), not(feature = "safe")))] + unsafe { ket_idx.set(qbits[pos], x_idx.get(pos).unwrap_unchecked()) }; + #[cfg(any(test, feature = "safe"))] + ket_idx.set(qbits[pos], x_idx.get(pos).unwrap()); } - } - } + let ket_idx: usize = ket_idx.clone().into(); + #[cfg(all(not(test), not(feature = "safe")))] + let v = *unsafe { old_vec.get(ket_idx).unwrap_unchecked() }; + #[cfg(any(test, feature = "safe"))] + let v = *old_vec.get(ket_idx).unwrap(); + #[cfg(any(test, feature = "safe"))] + let matrix_value = *{ self.matrix.get(x * self.width + y).unwrap() }; + #[cfg(all(not(test), not(feature = "safe")))] + let matrix_value = *unsafe { self.matrix.get(x * self.width + y).unwrap_unchecked() }; + *new_ket_value = *new_ket_value + matrix_value * v; + } + }); computer.set_state(new_ket); } } @@ -120,14 +126,14 @@ impl Mul for DGate { for x in 0..self.width { for y in 0..self.width { for i in 0..self.width { - #[cfg(not(test))] + #[cfg(all(not(test), not(feature = "safe")))] { *unsafe { matrix.get_unchecked_mut(x * self.width + y) } = *unsafe { matrix.get_unchecked(x * self.width + y) } + *unsafe { self.matrix.get_unchecked(i * self.width + y) } * *unsafe { rhs.matrix.get_unchecked(x * self.width + i) }; } - #[cfg(test)] + #[cfg(any(test, feature = "safe"))] { *{ matrix.get_mut(x * self.width + y).unwrap() } = *{ matrix.get(x * self.width + y).unwrap() } @@ -138,9 +144,9 @@ impl Mul for DGate { } } - #[cfg(not(test))] + #[cfg(all(not(test), not(feature = "safe")))] unsafe { DGate::new(matrix).unwrap_unchecked() } - #[cfg(test)] + #[cfg(any(test, feature = "safe"))] DGate::new(matrix).unwrap() } } @@ -152,12 +158,12 @@ impl + Copy> Mul for DGate { let mut matrix: Vec = vec![Complex::zero(); self.matrix.len()]; for x in 0..self.width { for y in 0..self.width { - #[cfg(not(test))] + #[cfg(all(not(test), not(feature = "safe")))] { *unsafe { matrix.get_unchecked_mut(x * self.width + y) } = rhs.mul(*unsafe { self.matrix.get_unchecked(x * self.width + y) }); } - #[cfg(test)] + #[cfg(any(test, feature = "safe"))] { *{ matrix.get_mut(x * self.width + y).unwrap() } = rhs.mul(*{ self.matrix.get(x * self.width + y).unwrap() }); @@ -165,9 +171,9 @@ impl + Copy> Mul for DGate { } } - #[cfg(not(test))] + #[cfg(all(not(test), not(feature = "safe")))] unsafe { DGate::new(matrix).unwrap_unchecked() } - #[cfg(test)] + #[cfg(any(test, feature = "safe"))] DGate::new(matrix).unwrap() } } @@ -199,20 +205,20 @@ impl BitXor for DGate { for ax in 0..self.width { for ay in 0..self.width { - #[cfg(not(test))] + #[cfg(all(not(test), not(feature = "safe")))] let a = *unsafe { self.matrix.get_unchecked(ax * self.width + ay) }; - #[cfg(test)] + #[cfg(any(test, feature = "safe"))] let a = *self.matrix.get(ax * self.width + ay).unwrap(); for bx in 0..rhs.width { for by in 0..rhs.width { let mx = ax + bx * self.width; let my = ay + by * self.width; - #[cfg(not(test))] + #[cfg(all(not(test), not(feature = "safe")))] { *unsafe { matrix.get_unchecked_mut(mx * matrix_width + my) } = a * *unsafe { rhs.matrix.get_unchecked(bx * rhs.width + by) }; } - #[cfg(test)] + #[cfg(any(test, feature = "safe"))] { *{ matrix.get_mut(mx * matrix_width + my).unwrap() } = a * *{ rhs.matrix.get(bx * rhs.width + by).unwrap() }; @@ -222,9 +228,9 @@ impl BitXor for DGate { } } - #[cfg(not(test))] + #[cfg(all(not(test), not(feature = "safe")))] unsafe { DGate::new(matrix).unwrap_unchecked() } - #[cfg(test)] + #[cfg(any(test, feature = "safe"))] DGate::new(matrix).unwrap() } } @@ -246,14 +252,14 @@ mod test { let mut a = true; for x in 0..self.width { for y in 0..self.width { - #[cfg(not(test))] + #[cfg(all(not(test), not(feature = "safe")))] { a = a && unsafe { *self.matrix.get_unchecked(x * self.width + y) }.approx_eq( unsafe { *other.matrix.get_unchecked(x * self.width + y) }, margin ); } - #[cfg(test)] + #[cfg(any(test, feature = "safe"))] { a = a && { *self.matrix.get(x * self.width + y).unwrap() }.approx_eq( *other.matrix.get(x * self.width + y).unwrap(), diff --git a/src/quantum/gate/sized.rs b/src/quantum/gate/sized.rs index af4cfa5..ae57cc4 100644 --- a/src/quantum/gate/sized.rs +++ b/src/quantum/gate/sized.rs @@ -4,11 +4,11 @@ use crate::quantum::gate::matrix::const_sized::Gate; use crate::quantum::gate::matrix::dynamic::DGate; pub enum SizedGate { - G1(Gate<1>, [u32; 1]), - G2(Gate<2>, [u32; 2]), - G3(Gate<3>, [u32; 3]), - G4(Gate<4>, [u32; 4]), - GD(DGate, Vec), + G1(Gate<1>, [usize; 1]), + G2(Gate<2>, [usize; 2]), + G3(Gate<3>, [usize; 3]), + G4(Gate<4>, [usize; 4]), + GD(DGate, Vec), } impl SizedGate { diff --git a/src/quantum/ket.rs b/src/quantum/ket.rs index a7542e3..53f83e1 100644 --- a/src/quantum/ket.rs +++ b/src/quantum/ket.rs @@ -2,21 +2,65 @@ use crate::complex::Complex; use alloc::format; use alloc::vec::Vec; use core::fmt::{Display, Formatter}; +use core::ops::{Deref, DerefMut}; +use alloc::vec; +use bit_array::BitArray; + +#[cfg(target_pointer_width = "32")] +#[derive(Eq, PartialEq, Clone)] +pub(crate) struct IndexType(BitArray); +#[cfg(target_pointer_width = "64")] +#[derive(Eq, PartialEq, Clone)] +pub(crate) struct IndexType(BitArray); + +impl Into for IndexType { + #[inline(always)] + fn into(self) -> usize { + self.0.storage()[0] + } +} + +impl From for IndexType { + #[inline(always)] + fn from(value: usize) -> Self { + let mut idx = IndexType(BitArray::new()); + unsafe { idx.0.storage_mut()[0] = value }; + + return idx + } +} + +impl Deref for IndexType { + #[cfg(target_pointer_width = "32")] + type Target = BitArray; + #[cfg(target_pointer_width = "64")] + type Target = BitArray; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for IndexType { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} #[derive(Debug, Clone)] pub struct Ket { - pub(crate) size: u32, + pub(crate) size: usize, pub(crate) vec: Vec, } impl Ket { - pub fn new(size: u32) -> Ket { + pub fn new(size: usize) -> Ket { let mut k = Ket::new_zero(size); - #[cfg(not(test))] + #[cfg(all(not(test), not(feature = "safe")))] { *unsafe { k.vec.get_unchecked_mut(0) } = Complex::new(1.0, 0.0); } - #[cfg(test)] + #[cfg(any(test, feature = "safe"))] { *{ k.vec.get_mut(0).unwrap() } = Complex::new(1.0, 0.0); } @@ -24,9 +68,9 @@ impl Ket { k } - pub(crate) fn new_zero(size: u32) -> Ket { + pub(crate) fn new_zero(size: usize) -> Ket { assert!(size > 0); - let vec = Vec::from_iter((0..0x1 << size).map(|_| Complex::new(0.0, 0.0))); + let vec = vec![Complex::zero(); 0x1 << size]; Ket { size, vec } } diff --git a/src/quantum/operator.rs b/src/quantum/operator.rs index e95e9e9..d2b8040 100644 --- a/src/quantum/operator.rs +++ b/src/quantum/operator.rs @@ -12,7 +12,7 @@ pub mod traits; use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", derive(Tsify))] -#[cfg_attr(feature = "wasm-pack", tsify(from_wasm_abi, enum_reimport_module))] +#[cfg_attr(feature = "wasm-pack", tsify(from_wasm_abi))] #[cfg_attr(feature = "wasm-pack", derive(serde::Deserialize))] #[cfg_attr(feature = "wasm-pack", serde(untagged))] #[derive(Copy, Clone, PartialEq)] diff --git a/src/quantum/operator/rotation.rs b/src/quantum/operator/rotation.rs index 165f52e..abaca1b 100644 --- a/src/quantum/operator/rotation.rs +++ b/src/quantum/operator/rotation.rs @@ -24,7 +24,7 @@ pub mod rotation_z; use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", derive(Tsify))] -#[cfg_attr(feature = "wasm-pack", tsify(from_wasm_abi, enum_reimport_module))] +#[cfg_attr(feature = "wasm-pack", tsify(from_wasm_abi))] #[cfg_attr(feature = "wasm-pack", derive(serde::Deserialize))] #[cfg_attr(feature = "wasm-pack", serde(tag = "type"))] #[derive(Copy, Clone, PartialEq)] @@ -145,4 +145,4 @@ impl Rotation { Rotation::ControlledControlledRotationSwap(gate) => gate.apply(computer), } } -} \ No newline at end of file +} diff --git a/src/quantum/operator/rotation/rotation_hadamard.rs b/src/quantum/operator/rotation/rotation_hadamard.rs index 2d3330b..9a2aab9 100644 --- a/src/quantum/operator/rotation/rotation_hadamard.rs +++ b/src/quantum/operator/rotation/rotation_hadamard.rs @@ -38,12 +38,12 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct RotationHadamard { - wire: u32, + wire: usize, theta: f64, } impl RotationHadamard { - pub fn new(wire: u32, theta: f64) -> RotationHadamard { + pub fn new(wire: usize, theta: f64) -> RotationHadamard { RotationHadamard { wire, theta } } } @@ -55,7 +55,7 @@ impl ToGate<1> for RotationHadamard { } impl UsedWires<1> for RotationHadamard { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/rotation/rotation_pauli_x.rs b/src/quantum/operator/rotation/rotation_pauli_x.rs index 71626a2..9fdd166 100644 --- a/src/quantum/operator/rotation/rotation_pauli_x.rs +++ b/src/quantum/operator/rotation/rotation_pauli_x.rs @@ -20,12 +20,12 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct RotationPauliX { - wire: u32, + wire: usize, theta: f64, } impl RotationPauliX { - pub fn new(wire: u32, theta: f64) -> RotationPauliX { + pub fn new(wire: usize, theta: f64) -> RotationPauliX { RotationPauliX { wire, theta } } } @@ -37,7 +37,7 @@ impl ToGate<1> for RotationPauliX { } impl UsedWires<1> for RotationPauliX { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/rotation/rotation_pauli_y.rs b/src/quantum/operator/rotation/rotation_pauli_y.rs index f98daf8..220f925 100644 --- a/src/quantum/operator/rotation/rotation_pauli_y.rs +++ b/src/quantum/operator/rotation/rotation_pauli_y.rs @@ -20,12 +20,12 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct RotationPauliY { - wire: u32, + wire: usize, theta: f64, } impl RotationPauliY { - pub fn new(wire: u32, theta: f64) -> RotationPauliY { + pub fn new(wire: usize, theta: f64) -> RotationPauliY { RotationPauliY { wire, theta } } } @@ -37,7 +37,7 @@ impl ToGate<1> for RotationPauliY { } impl UsedWires<1> for RotationPauliY { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/rotation/rotation_pauli_z.rs b/src/quantum/operator/rotation/rotation_pauli_z.rs index 397dc40..7cf1d50 100644 --- a/src/quantum/operator/rotation/rotation_pauli_z.rs +++ b/src/quantum/operator/rotation/rotation_pauli_z.rs @@ -20,12 +20,12 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct RotationPauliZ { - wire: u32, + wire: usize, theta: f64, } impl RotationPauliZ { - pub fn new(wire: u32, theta: f64) -> RotationPauliZ { + pub fn new(wire: usize, theta: f64) -> RotationPauliZ { RotationPauliZ { wire, theta } } } @@ -37,7 +37,7 @@ impl ToGate<1> for RotationPauliZ { } impl UsedWires<1> for RotationPauliZ { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/rotation/rotation_swap.rs b/src/quantum/operator/rotation/rotation_swap.rs index 3a154af..fece579 100644 --- a/src/quantum/operator/rotation/rotation_swap.rs +++ b/src/quantum/operator/rotation/rotation_swap.rs @@ -96,13 +96,13 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct RotationSwap { - wire_0: u32, - wire_1: u32, + wire_0: usize, + wire_1: usize, theta: f64, } impl RotationSwap { - pub fn new(wire_0: u32, wire_1: u32, theta: f64) -> RotationSwap { + pub fn new(wire_0: usize, wire_1: usize, theta: f64) -> RotationSwap { RotationSwap { wire_0, wire_1, @@ -124,7 +124,7 @@ impl ApplyGate<2> for RotationSwap { } impl UsedWires<2> for RotationSwap { - fn wires(&self) -> [u32; 2] { + fn wires(&self) -> [usize; 2] { [self.wire_1, self.wire_0] } } diff --git a/src/quantum/operator/rotation/rotation_x.rs b/src/quantum/operator/rotation/rotation_x.rs index ccd519a..9c860cb 100644 --- a/src/quantum/operator/rotation/rotation_x.rs +++ b/src/quantum/operator/rotation/rotation_x.rs @@ -27,12 +27,12 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct RotationX { - wire: u32, + wire: usize, theta: f64, } impl RotationX { - pub fn new(wire: u32, theta: f64) -> RotationX { + pub fn new(wire: usize, theta: f64) -> RotationX { RotationX { wire, theta } } } @@ -44,7 +44,7 @@ impl ToGate<1> for RotationX { } impl UsedWires<1> for RotationX { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/rotation/rotation_y.rs b/src/quantum/operator/rotation/rotation_y.rs index 73cc529..7d16273 100644 --- a/src/quantum/operator/rotation/rotation_y.rs +++ b/src/quantum/operator/rotation/rotation_y.rs @@ -27,12 +27,12 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct RotationY { - wire: u32, + wire: usize, theta: f64, } impl RotationY { - pub fn new(wire: u32, theta: f64) -> RotationY { + pub fn new(wire: usize, theta: f64) -> RotationY { RotationY { wire, theta } } } @@ -44,7 +44,7 @@ impl ToGate<1> for RotationY { } impl UsedWires<1> for RotationY { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/rotation/rotation_z.rs b/src/quantum/operator/rotation/rotation_z.rs index 2cb3c1b..fd88aa6 100644 --- a/src/quantum/operator/rotation/rotation_z.rs +++ b/src/quantum/operator/rotation/rotation_z.rs @@ -28,12 +28,12 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct RotationZ { - wire: u32, + wire: usize, theta: f64, } impl RotationZ { - pub fn new(wire: u32, theta: f64) -> RotationZ { + pub fn new(wire: usize, theta: f64) -> RotationZ { RotationZ { wire, theta } } } @@ -45,7 +45,7 @@ impl ToGate<1> for RotationZ { } impl UsedWires<1> for RotationZ { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/simple.rs b/src/quantum/operator/simple.rs index f348c7d..7362dce 100644 --- a/src/quantum/operator/simple.rs +++ b/src/quantum/operator/simple.rs @@ -29,7 +29,7 @@ pub mod t_gate; use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", derive(Tsify))] -#[cfg_attr(feature = "wasm-pack", tsify(from_wasm_abi, enum_reimport_module))] +#[cfg_attr(feature = "wasm-pack", tsify(from_wasm_abi))] #[cfg_attr(feature = "wasm-pack", derive(serde::Deserialize))] #[cfg_attr(feature = "wasm-pack", serde(tag = "type"))] #[derive(Copy, Clone, PartialEq)] @@ -166,4 +166,4 @@ impl Simple { Simple::ControlledSwapRoot(gate) => gate.apply(computer), } } -} \ No newline at end of file +} diff --git a/src/quantum/operator/simple/controlled.rs b/src/quantum/operator/simple/controlled.rs index 78b3a8a..542608f 100644 --- a/src/quantum/operator/simple/controlled.rs +++ b/src/quantum/operator/simple/controlled.rs @@ -18,7 +18,7 @@ pub struct Controlled< [(); 0x1 << SIZE]:, [(); 0x1 << { SIZE - 1 }]:, { - wire: u32, + wire: usize, transformation: T, } @@ -28,7 +28,7 @@ where [(); 0x1 << SIZE]:, [(); 0x1 << { SIZE - 1 }]:, { - pub fn new(wire: u32, transformation: T) -> Controlled { + pub fn new(wire: usize, transformation: T) -> Controlled { Controlled { wire, transformation, @@ -77,10 +77,10 @@ where [(); 0x1 << SIZE]:, [(); 0x1 << { SIZE - 1 }]:, { - fn wires(&self) -> [u32; SIZE] { - let mut w: [u32; SIZE] = [0; SIZE]; + fn wires(&self) -> [usize; SIZE] { + let mut w: [usize; SIZE] = [0; SIZE]; w[SIZE - 1] = self.wire; - let iw: [u32; SIZE - 1] = self.transformation.wires(); + let iw: [usize; SIZE - 1] = self.transformation.wires(); for i in 0..SIZE - 1 { w[i] = iw[i]; } diff --git a/src/quantum/operator/simple/hadamard.rs b/src/quantum/operator/simple/hadamard.rs index 7ab2098..850cfeb 100644 --- a/src/quantum/operator/simple/hadamard.rs +++ b/src/quantum/operator/simple/hadamard.rs @@ -25,11 +25,11 @@ pub static HADAMARD: Gate<1> = Gate::new([ #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct Hadamard { - wire: u32, + wire: usize, } impl Hadamard { - pub fn new(wire: u32) -> Hadamard { + pub fn new(wire: usize) -> Hadamard { Hadamard { wire } } } @@ -41,7 +41,7 @@ impl ToGate<1> for Hadamard { } impl UsedWires<1> for Hadamard { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/simple/pauli_x.rs b/src/quantum/operator/simple/pauli_x.rs index 05e5966..0fea10e 100644 --- a/src/quantum/operator/simple/pauli_x.rs +++ b/src/quantum/operator/simple/pauli_x.rs @@ -19,11 +19,11 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct PauliX { - wire: u32, + wire: usize, } impl PauliX { - pub fn new(wire: u32) -> PauliX { + pub fn new(wire: usize) -> PauliX { PauliX { wire } } } @@ -41,7 +41,7 @@ impl ApplyGate<1> for PauliX { } impl UsedWires<1> for PauliX { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/simple/pauli_x_root.rs b/src/quantum/operator/simple/pauli_x_root.rs index b503be8..4d03d93 100644 --- a/src/quantum/operator/simple/pauli_x_root.rs +++ b/src/quantum/operator/simple/pauli_x_root.rs @@ -18,11 +18,11 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct PauliXRoot { - wire: u32, + wire: usize, } impl PauliXRoot { - pub fn new(wire: u32) -> PauliXRoot { + pub fn new(wire: usize) -> PauliXRoot { PauliXRoot { wire } } } @@ -40,7 +40,7 @@ impl ApplyGate<1> for PauliXRoot { } impl UsedWires<1> for PauliXRoot { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/simple/pauli_y.rs b/src/quantum/operator/simple/pauli_y.rs index 116eb09..c5cd8eb 100644 --- a/src/quantum/operator/simple/pauli_y.rs +++ b/src/quantum/operator/simple/pauli_y.rs @@ -18,11 +18,11 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct PauliY { - wire: u32, + wire: usize, } impl PauliY { - pub fn new(wire: u32) -> PauliY { + pub fn new(wire: usize) -> PauliY { PauliY { wire } } } @@ -40,7 +40,7 @@ impl ApplyGate<1> for PauliY { } impl UsedWires<1> for PauliY { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/simple/pauli_z.rs b/src/quantum/operator/simple/pauli_z.rs index 34ab86b..cedc525 100644 --- a/src/quantum/operator/simple/pauli_z.rs +++ b/src/quantum/operator/simple/pauli_z.rs @@ -18,11 +18,11 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct PauliZ { - wire: u32, + wire: usize, } impl PauliZ { - pub fn new(wire: u32) -> PauliZ { + pub fn new(wire: usize) -> PauliZ { PauliZ { wire } } } @@ -34,7 +34,7 @@ impl ToGate<1> for PauliZ { } impl UsedWires<1> for PauliZ { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/simple/s_gate.rs b/src/quantum/operator/simple/s_gate.rs index a8ee113..eacafc5 100644 --- a/src/quantum/operator/simple/s_gate.rs +++ b/src/quantum/operator/simple/s_gate.rs @@ -18,11 +18,11 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct SGate { - wire: u32, + wire: usize, } impl SGate { - pub fn new(wire: u32) -> SGate { + pub fn new(wire: usize) -> SGate { SGate { wire } } } @@ -34,7 +34,7 @@ impl ToGate<1> for SGate { } impl UsedWires<1> for SGate { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/simple/s_gate_inverse.rs b/src/quantum/operator/simple/s_gate_inverse.rs index 883c32e..0f62b49 100644 --- a/src/quantum/operator/simple/s_gate_inverse.rs +++ b/src/quantum/operator/simple/s_gate_inverse.rs @@ -18,11 +18,11 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct SGateInverse { - wire: u32, + wire: usize, } impl SGateInverse { - pub fn new(wire: u32) -> SGateInverse { + pub fn new(wire: usize) -> SGateInverse { SGateInverse { wire } } } @@ -34,7 +34,7 @@ impl ToGate<1> for SGateInverse { } impl UsedWires<1> for SGateInverse { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/simple/swap.rs b/src/quantum/operator/simple/swap.rs index 260283b..9a7430b 100644 --- a/src/quantum/operator/simple/swap.rs +++ b/src/quantum/operator/simple/swap.rs @@ -40,12 +40,12 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct Swap { - wire_0: u32, - wire_1: u32, + wire_0: usize, + wire_1: usize, } impl Swap { - pub fn new(wire_0: u32, wire_1: u32) -> Swap { + pub fn new(wire_0: usize, wire_1: usize) -> Swap { Swap { wire_0, wire_1 } } } @@ -63,7 +63,7 @@ impl ApplyGate<2> for Swap { } impl UsedWires<2> for Swap { - fn wires(&self) -> [u32; 2] { + fn wires(&self) -> [usize; 2] { [self.wire_1, self.wire_0] } } diff --git a/src/quantum/operator/simple/swap_root.rs b/src/quantum/operator/simple/swap_root.rs index 93c63f6..2cf3310 100644 --- a/src/quantum/operator/simple/swap_root.rs +++ b/src/quantum/operator/simple/swap_root.rs @@ -40,12 +40,12 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct SwapRoot { - wire_0: u32, - wire_1: u32, + wire_0: usize, + wire_1: usize, } impl SwapRoot { - pub fn new(wire_0: u32, wire_1: u32) -> SwapRoot { + pub fn new(wire_0: usize, wire_1: usize) -> SwapRoot { SwapRoot { wire_0, wire_1 } } } @@ -63,7 +63,7 @@ impl ApplyGate<2> for SwapRoot { } impl UsedWires<2> for SwapRoot { - fn wires(&self) -> [u32; 2] { + fn wires(&self) -> [usize; 2] { [self.wire_1, self.wire_0] } } diff --git a/src/quantum/operator/simple/t_gate.rs b/src/quantum/operator/simple/t_gate.rs index 0f715e6..bd5422a 100644 --- a/src/quantum/operator/simple/t_gate.rs +++ b/src/quantum/operator/simple/t_gate.rs @@ -22,11 +22,11 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct TGate { - wire: u32, + wire: usize, } impl TGate { - pub fn new(wire: u32) -> TGate { + pub fn new(wire: usize) -> TGate { TGate { wire } } } @@ -38,7 +38,7 @@ impl ToGate<1> for TGate { } impl UsedWires<1> for TGate { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/special.rs b/src/quantum/operator/special.rs index 50ca0e3..ef76950 100644 --- a/src/quantum/operator/special.rs +++ b/src/quantum/operator/special.rs @@ -10,19 +10,19 @@ pub mod reset; use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", derive(Tsify))] -#[cfg_attr(feature = "wasm-pack", tsify(from_wasm_abi, enum_reimport_module))] +#[cfg_attr(feature = "wasm-pack", tsify(from_wasm_abi))] #[cfg_attr(feature = "wasm-pack", derive(serde::Deserialize))] #[cfg_attr(feature = "wasm-pack", serde(tag = "type"))] #[derive(Copy, Clone, PartialEq)] pub enum Special { - Measure(Measurement), + Measurement(Measurement), Reset(Reset), } impl Special { pub fn apply(&self, computer: &mut QuantumComputer) { match self { - Special::Measure(gate) => gate.apply(computer), + Special::Measurement(gate) => gate.apply(computer), Special::Reset(gate) => gate.apply(computer), } } diff --git a/src/quantum/operator/special/measurement.rs b/src/quantum/operator/special/measurement.rs index a31d7a0..853c7c0 100644 --- a/src/quantum/operator/special/measurement.rs +++ b/src/quantum/operator/special/measurement.rs @@ -22,7 +22,7 @@ use crate::quantum::operator::simple::s_gate_inverse::S_INV_GATE; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct Measurement { - wire: u32, + wire: usize, #[cfg_attr(feature = "wasm-pack", tsify(optional))] #[cfg_attr(feature = "wasm-pack", serde(default))] @@ -36,7 +36,7 @@ pub struct Measurement { } #[cfg_attr(feature = "wasm-pack", derive(Tsify))] -#[cfg_attr(feature = "wasm-pack", tsify(from_wasm_abi, enum_reimport_module))] +#[cfg_attr(feature = "wasm-pack", tsify(from_wasm_abi))] #[cfg_attr(feature = "wasm-pack", derive(serde::Deserialize))] #[derive(Copy, Clone, PartialEq)] pub enum MeasurementBasis { @@ -51,7 +51,7 @@ impl Default for MeasurementBasis { impl Measurement { pub fn new( - wire: u32, + wire: usize, basis: Option, creg: Option, creg_bit: Option, @@ -104,7 +104,7 @@ impl ApplyGate<1> for Measurement { } impl UsedWires<1> for Measurement { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } @@ -136,4 +136,4 @@ mod test { assert_approx_eq!(Complex, *state.get(2).unwrap(), Complex::zero(), epsilon = 0.00001); assert_approx_eq!(Complex, *state.get(3).unwrap(), Complex::new(1.0 / SQRT_2, 0.0), epsilon = 0.00001); } -} \ No newline at end of file +} diff --git a/src/quantum/operator/special/reset.rs b/src/quantum/operator/special/reset.rs index 604c725..eed2df1 100644 --- a/src/quantum/operator/special/reset.rs +++ b/src/quantum/operator/special/reset.rs @@ -13,12 +13,12 @@ use tsify::Tsify; #[cfg_attr(feature = "wasm-pack", serde(rename_all = "camelCase"))] #[derive(Copy, Clone, PartialEq)] pub struct Reset { - wire: u32, + wire: usize, state: bool, } impl Reset { - pub fn new(wire: u32, state: bool) -> Reset { + pub fn new(wire: usize, state: bool) -> Reset { Reset { wire, state, @@ -45,7 +45,7 @@ impl ApplyGate<1> for Reset { } impl UsedWires<1> for Reset { - fn wires(&self) -> [u32; 1] { + fn wires(&self) -> [usize; 1] { [self.wire] } } diff --git a/src/quantum/operator/traits.rs b/src/quantum/operator/traits.rs index 2cb884a..d13cb00 100644 --- a/src/quantum/operator/traits.rs +++ b/src/quantum/operator/traits.rs @@ -18,7 +18,7 @@ pub trait UsedWires where [(); 0x1 << SIZE]:, { - fn wires(&self) -> [u32; SIZE]; + fn wires(&self) -> [usize; SIZE]; } pub trait Parameterized diff --git a/src/quantum/test.rs b/src/quantum/test.rs index 331873f..84e0cee 100644 --- a/src/quantum/test.rs +++ b/src/quantum/test.rs @@ -9,6 +9,7 @@ use crate::quantum::computer::QuantumComputer; use float_cmp::{approx_eq, assert_approx_eq}; use crate::complex::Complex; use crate::quantum::gate::matrix::const_sized::Gate; +use test::Bencher; extern crate test; @@ -132,3 +133,116 @@ fn ptest() { assert_approx_eq!(Complex, *c0, c1, epsilon = 0.00001); } } + +#[test] +fn bv_small() { + let m = [ + true, true, false, false, false + ]; + let mut computer = QuantumComputer::new(m.len() + 1, None); + for i in 0..m.len() + 1 { + Hadamard::new(i).apply(&mut computer); + } + PauliZ::new(m.len()).apply(&mut computer); + for i in 0..m.len() { + if m[i] == true { + Controlled::<2, _>::new(i, PauliX::new(m.len())).apply(&mut computer); + } + } + for i in 0..m.len() { + Hadamard::new(i).apply(&mut computer); + } + let probabilities = computer.probabilities(); + for (&p, v) in probabilities.iter().zip(m) { + if v { + assert_approx_eq!(f64, p, 1.0, epsilon = 0.00001); + } else { + assert_approx_eq!(f64, p, 0.0, epsilon = 0.00001); + } + } +} + +#[test] +fn bv_medium() { + let m = [ + true, true, false, true, false, true, true, false, + true, false, true, true, false, true, false, + ]; + let mut computer = QuantumComputer::new(m.len() + 1, None); + for i in 0..m.len() + 1 { + Hadamard::new(i).apply(&mut computer); + } + PauliZ::new(m.len()).apply(&mut computer); + for i in 0..m.len() { + if m[i] == true { + Controlled::<2, _>::new(i, PauliX::new(m.len())).apply(&mut computer); + } + } + for i in 0..m.len() { + Hadamard::new(i).apply(&mut computer); + } + let probabilities = computer.probabilities(); + for (&p, v) in probabilities.iter().zip(m) { + if v { + assert_approx_eq!(f64, p, 1.0, epsilon = 0.00001); + } else { + assert_approx_eq!(f64, p, 0.0, epsilon = 0.00001); + } + } +} + +#[test] +#[ignore] +fn bv_large() { + let m = [ + true, true, false, true, false, true, true, false, true, false, true, true, + false, true, false, true, true, false, true, false, true, true, false, true, + false, true, true, + ]; + let mut computer = QuantumComputer::new(m.len() + 1, None); + for i in 0..m.len() + 1 { + Hadamard::new(i).apply(&mut computer); + } + PauliZ::new(m.len()).apply(&mut computer); + for i in 0..m.len() { + if m[i] == true { + Controlled::<2, _>::new(i, PauliX::new(m.len())).apply(&mut computer); + } + } + for i in 0..m.len() { + Hadamard::new(i).apply(&mut computer); + } + let probabilities = computer.probabilities(); + for (&p, v) in probabilities.iter().zip(m) { + if v { + assert_approx_eq!(f64, p, 1.0, epsilon = 0.00001); + } else { + assert_approx_eq!(f64, p, 0.0, epsilon = 0.00001); + } + } +} + +#[bench] +fn bv_bench(bencher: &mut Bencher) { + bencher.iter(|| { + let m = [ + true, true, false, true, false, true, true, + false, true, false, true, false + ]; + let mut computer = QuantumComputer::new(m.len() + 1, None); + for i in 0..m.len() + 1 { + Hadamard::new(i).apply(&mut computer); + } + PauliZ::new(m.len()).apply(&mut computer); + for i in 0..m.len() { + if m[i] == true { + Controlled::<2, _>::new(i, PauliX::new(m.len())).apply(&mut computer); + } + } + for i in 0..m.len() { + Hadamard::new(i).apply(&mut computer); + } + + computer + }); +} diff --git a/test/integration/quantum-simulator.test.ts b/test/integration/quantum-simulator.test.ts index f94bbcb..a4a881f 100644 --- a/test/integration/quantum-simulator.test.ts +++ b/test/integration/quantum-simulator.test.ts @@ -1,4 +1,12 @@ -import {AlgorithmGate, Controlled, Hadamard, PauliX, QuantumAlgorithm, QuantumComputer} from "../../"; +import { + AlgorithmGate, + Controlled, + Hadamard, + PauliX, + PauliZ, + QuantumAlgorithm, + QuantumComputer +} from "../../"; test('test hadamard', () => { const computer = new QuantumComputer(2); @@ -87,3 +95,51 @@ test('test error', () => { {} as unknown as AlgorithmGate, ], 8)).toThrowError(); }); + +function bvAlgorithm(hidden: boolean[]) { + const computer = new QuantumComputer(hidden.length + 1); + const algorithm = new QuantumAlgorithm([ + ...hidden.map((_, index) => new Hadamard(index)), + new Hadamard(hidden.length), + new PauliZ(hidden.length), + + ...hidden.map((v, index) => + v ? new Controlled(index, new PauliX(hidden.length)) : undefined + ).filter(value => value !== undefined) as Controlled[], + + ...hidden.map((_, index) => new Hadamard(index)), + //...hidden.split("").map((_, index) => new Measurement(index, "X")), + ].map((v, index) => new AlgorithmGate(v, index)), 1); + algorithm.run(computer); + const state = computer.probabilities(); + for (let index = 0; index < hidden.length; index++) { + const exp = hidden[index]; + const prob = state[index]; + if (exp) { + expect(prob).toBeCloseTo(1, 0.00001); + } else { + expect(prob).toBeCloseTo(0, 0.00001); + } + } +} + +test.skip('test large', () => { + bvAlgorithm([ + true, true, false, true, false, true, true, false, + true, false, true, true, false, true, false + ]); +}); + +test('test med bv', () => { + bvAlgorithm([ + true, true, false, true, false, true, true, false, + true, false, true, true, false, true, false + ]); +}); + +test('test small bv', () => { + bvAlgorithm([ + true, true, false, true, false, true, true, false, + true, false, true, true, false, true, false + ]); +}); diff --git a/test/wasm/quantum-simulator.test.ts b/test/wasm/quantum-simulator.test.ts index 86e16c9..192f8d8 100644 --- a/test/wasm/quantum-simulator.test.ts +++ b/test/wasm/quantum-simulator.test.ts @@ -87,3 +87,61 @@ test('test error', () => { {type: "ch", wire: 0}, ], 8)).toThrowError(); }); + +function bvAlgorithm(hidden: boolean[]) { + const computer = new QuantumComputer(hidden.length + 1); + const algorithm = new QuantumAlgorithm([ + ...hidden.map((_, index) => ({ type: "Hadamard", wire: index })), + { type: "Hadamard", wire: hidden.length }, + { type: "PauliZ", wire: hidden.length }, + + ...hidden.map((v, index) => + v ? { + type: "ControlledPauliX", + wire: index, + transformation: { + type: "PauliX", + wire: hidden.length, + }, + } : undefined + ).filter(value => value !== undefined), + + ...hidden.map((_, index) => ({ type: "Hadamard", wire: index })), + //...hidden.split("").map((_, index) => new Measurement(index, "X")), + ].map((v, index) => ({ + position: index, + gate: v, + })), 1); + algorithm.run(computer); + const state = computer.probabilities(); + for (let index = 0; index < hidden.length; index++) { + const exp = hidden[index]; + const prob = state[index]; + if (exp) { + expect(prob).toBeCloseTo(1, 0.00001); + } else { + expect(prob).toBeCloseTo(0, 0.00001); + } + } +} + +test.skip('test large', () => { + bvAlgorithm([ + true, true, false, true, false, true, true, false, + true, false, true, true, false, true, false + ]); +}); + +test('test med bv', () => { + bvAlgorithm([ + true, true, false, true, false, true, true, false, + true, false, true, true, false, true, false + ]); +}); + +test('test small bv', () => { + bvAlgorithm([ + true, true, false, true, false, true, true, false, + true, false, true, true, false, true, false + ]); +});