From e460796e1c66d5f319f44b900eaffe8d684e410f Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Sun, 16 Oct 2022 11:49:00 -0700 Subject: [PATCH 01/20] Update --- Cargo.toml | 124 ++----- Dockerfile | 7 +- benches/bench_opt.rs | 222 ------------ benches/unitaries/adder.npy | Bin 4224 -> 0 bytes benches/unitaries/fredkin.npy | Bin 1152 -> 0 bytes benches/unitaries/grover3.npy | Bin 1152 -> 0 bytes benches/unitaries/hhl.npy | Bin 1152 -> 0 bytes benches/unitaries/hlf.npy | Bin 16512 -> 0 bytes benches/unitaries/mul.npy | Bin 16512 -> 0 bytes benches/unitaries/or.npy | Bin 1152 -> 0 bytes benches/unitaries/peres.npy | Bin 1152 -> 0 bytes benches/unitaries/qaoa.npy | Bin 16512 -> 0 bytes benches/unitaries/qft3.npy | Bin 1152 -> 0 bytes benches/unitaries/qft4.npy | Bin 4224 -> 0 bytes benches/unitaries/qft5.npy | Bin 16512 -> 0 bytes benches/unitaries/tfim-4-95.npy | Bin 4224 -> 0 bytes benches/unitaries/tfim-5-100.npy | Bin 16512 -> 0 bytes benches/unitaries/tfim-6-1.npy | Bin 65664 -> 0 bytes benches/unitaries/toffoli.npy | Bin 1152 -> 0 bytes benches/unitaries/vqe.npy | Bin 16512 -> 0 bytes build.rs | 48 --- ceres/ceres-sys/Cargo.toml | 6 +- ceres/ceres-sys/build.rs | 6 +- pyproject.toml | 10 +- squaremat/.gitignore | 3 - squaremat/Cargo.toml | 48 --- squaremat/build.rs | 40 --- squaremat/src/conj.rs | 18 - squaremat/src/lib.rs | 43 --- squaremat/src/matmul.rs | 109 ------ src/circuit/mod.rs | 317 ------------------ src/ir/circuit.rs | 214 ++++++++++++ src/{ => ir}/gates/constant.rs | 10 +- src/{ => ir}/gates/dynamic.rs | 12 +- src/{ => ir}/gates/gradient.rs | 8 +- src/{ => ir}/gates/mod.rs | 12 +- src/{ => ir}/gates/optimize.rs | 4 +- src/{ => ir}/gates/parameterized/crx.rs | 16 +- src/{ => ir}/gates/parameterized/cry.rs | 16 +- src/{ => ir}/gates/parameterized/crz.rs | 16 +- src/{ => ir}/gates/parameterized/mod.rs | 0 src/{ => ir}/gates/parameterized/rx.rs | 18 +- src/{ => ir}/gates/parameterized/rxx.rs | 16 +- src/{ => ir}/gates/parameterized/ry.rs | 18 +- src/{ => ir}/gates/parameterized/ryy.rs | 16 +- src/{ => ir}/gates/parameterized/rz.rs | 18 +- src/{ => ir}/gates/parameterized/rzz.rs | 16 +- src/{ => ir}/gates/parameterized/u1.rs | 18 +- src/{ => ir}/gates/parameterized/u2.rs | 18 +- src/{ => ir}/gates/parameterized/u3.rs | 18 +- src/{ => ir}/gates/parameterized/u8.rs | 22 +- src/{ => ir}/gates/parameterized/variable.rs | 53 +-- src/{ => ir}/gates/size.rs | 0 src/{ => ir}/gates/unitary.rs | 4 +- src/{ => ir}/gates/utils.rs | 52 +-- src/{ => ir/inst}/minimizers/bfgs.rs | 2 +- src/{ => ir/inst}/minimizers/ceres.rs | 0 src/{ => ir/inst}/minimizers/cost_fn.rs | 10 +- src/{ => ir/inst}/minimizers/mod.rs | 0 src/{ => ir/inst}/minimizers/residual_fn.rs | 10 +- src/{instantiators => ir/inst}/mod.rs | 7 +- src/{instantiators => ir/inst}/qfactor.rs | 10 +- src/ir/mod.rs | 6 + src/{circuit => ir}/operation.rs | 14 +- src/lib.rs | 34 +- src/permutation_matrix.rs | 10 +- src/python/circuit.rs | 23 +- src/python/gate.rs | 22 +- src/python/instantiators/qfactor.rs | 10 +- src/python/minimizers/bfgs.rs | 6 +- src/python/minimizers/ceres.rs | 2 +- src/python/minimizers/cost_fn.rs | 10 +- src/python/minimizers/residual_fn.rs | 10 +- src/python/mod.rs | 46 +-- src/qis/mod.rs | 1 + .../unitary/builder.rs} | 96 +++++- src/qis/unitary/function.rs | 15 + src/qis/unitary/matrix.rs | 7 + src/qis/unitary/mod.rs | 5 + src/squaremat/conj.rs | 18 + {squaremat/src => src/squaremat}/kron.rs | 12 +- src/squaremat/matmul.rs | 113 +++++++ src/squaremat/mod.rs | 13 + {squaremat/src => src/squaremat}/multiply.rs | 14 +- .../src => src/squaremat}/split_complex.rs | 4 +- {squaremat/src => src/squaremat}/swap_rows.rs | 4 +- src/utils.rs | 40 +-- 87 files changed, 851 insertions(+), 1319 deletions(-) delete mode 100644 benches/bench_opt.rs delete mode 100644 benches/unitaries/adder.npy delete mode 100644 benches/unitaries/fredkin.npy delete mode 100644 benches/unitaries/grover3.npy delete mode 100644 benches/unitaries/hhl.npy delete mode 100644 benches/unitaries/hlf.npy delete mode 100644 benches/unitaries/mul.npy delete mode 100644 benches/unitaries/or.npy delete mode 100644 benches/unitaries/peres.npy delete mode 100644 benches/unitaries/qaoa.npy delete mode 100644 benches/unitaries/qft3.npy delete mode 100644 benches/unitaries/qft4.npy delete mode 100644 benches/unitaries/qft5.npy delete mode 100644 benches/unitaries/tfim-4-95.npy delete mode 100644 benches/unitaries/tfim-5-100.npy delete mode 100644 benches/unitaries/tfim-6-1.npy delete mode 100644 benches/unitaries/toffoli.npy delete mode 100644 benches/unitaries/vqe.npy delete mode 100644 build.rs delete mode 100644 squaremat/.gitignore delete mode 100644 squaremat/Cargo.toml delete mode 100644 squaremat/build.rs delete mode 100644 squaremat/src/conj.rs delete mode 100644 squaremat/src/lib.rs delete mode 100644 squaremat/src/matmul.rs delete mode 100644 src/circuit/mod.rs create mode 100644 src/ir/circuit.rs rename src/{ => ir}/gates/constant.rs (72%) rename src/{ => ir}/gates/dynamic.rs (69%) rename src/{ => ir}/gates/gradient.rs (62%) rename src/{ => ir}/gates/mod.rs (94%) rename src/{ => ir}/gates/optimize.rs (56%) rename src/{ => ir}/gates/parameterized/crx.rs (84%) rename src/{ => ir}/gates/parameterized/cry.rs (84%) rename src/{ => ir}/gates/parameterized/crz.rs (85%) rename src/{ => ir}/gates/parameterized/mod.rs (100%) rename src/{ => ir}/gates/parameterized/rx.rs (68%) rename src/{ => ir}/gates/parameterized/rxx.rs (86%) rename src/{ => ir}/gates/parameterized/ry.rs (68%) rename src/{ => ir}/gates/parameterized/ryy.rs (85%) rename src/{ => ir}/gates/parameterized/rz.rs (70%) rename src/{ => ir}/gates/parameterized/rzz.rs (84%) rename src/{ => ir}/gates/parameterized/u1.rs (76%) rename src/{ => ir}/gates/parameterized/u2.rs (86%) rename src/{ => ir}/gates/parameterized/u3.rs (91%) rename src/{ => ir}/gates/parameterized/u8.rs (96%) rename src/{ => ir}/gates/parameterized/variable.rs (66%) rename src/{ => ir}/gates/size.rs (100%) rename src/{ => ir}/gates/unitary.rs (59%) rename src/{ => ir}/gates/utils.rs (68%) rename src/{ => ir/inst}/minimizers/bfgs.rs (97%) rename src/{ => ir/inst}/minimizers/ceres.rs (100%) rename src/{ => ir/inst}/minimizers/cost_fn.rs (93%) rename src/{ => ir/inst}/minimizers/mod.rs (100%) rename src/{ => ir/inst}/minimizers/residual_fn.rs (96%) rename src/{instantiators => ir/inst}/mod.rs (77%) rename src/{instantiators => ir/inst}/qfactor.rs (95%) create mode 100644 src/ir/mod.rs rename src/{circuit => ir}/operation.rs (72%) create mode 100644 src/qis/mod.rs rename src/{unitary_builder.rs => qis/unitary/builder.rs} (56%) create mode 100644 src/qis/unitary/function.rs create mode 100644 src/qis/unitary/matrix.rs create mode 100644 src/qis/unitary/mod.rs create mode 100644 src/squaremat/conj.rs rename {squaremat/src => src/squaremat}/kron.rs (78%) create mode 100644 src/squaremat/matmul.rs create mode 100644 src/squaremat/mod.rs rename {squaremat/src => src/squaremat}/multiply.rs (51%) rename {squaremat/src => src/squaremat}/split_complex.rs (91%) rename {squaremat/src => src/squaremat}/swap_rows.rs (83%) diff --git a/Cargo.toml b/Cargo.toml index cc858bd..9146792 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,119 +1,57 @@ [package] name = "bqskitrs" -version = "0.2.3" -authors = ["Ethan Smith "] -license = "LGPL-2.1 AND BSD-3-Clause" -edition = "2018" -build = "build.rs" -publish = false -include = [ - "src/**/*", - "LICENSE", - "README.md", - "build.rs", - "Dockerfile", - "pyproject.toml", - "squaremat/**/*", - "ceres/**/*", +version = "0.3.0" +authors = [ + "Ethan Smith ", + "Ed Younis ", ] +publish = false +license = "LGPL-2.1 AND BSD-3-Clause" +edition = "2021" [features] -python = ["numpy", "pyo3"] -static = [ + +openblas = [ "openblas-src", - "blas-src/openblas", "openblas-src/static", - "openblas-src/cblas", - "openblas-src/lapacke", "ndarray-linalg/openblas-static", - "squaremat/openblas-static", - "ceres/static", ] -default = [ - "python", - "mimalloc/local_dynamic_tls", - "openblas-src", - "blas-src/openblas", - "openblas-src/system", - "openblas-src/cblas", - "openblas-src/lapacke", - "ndarray-linalg/openblas-system", - "squaremat/openblas-system", +accelerate = [ + "accelerate-src", +] +mkl = [ + "intel-mkl-src", + "ndarray-linalg/intel-mkl-static" ] -accelerate = ["accelerate-src", "blas-src/accelerate", "squaremat/accelerate"] -mkl = ["intel-mkl-src", "blas-src/intel-mkl", "squaremat/mkl", "ndarray-linalg/intel-mkl-system"] [dependencies] -num-complex = { version = "0.4.0", features = ["serde"] } -ndarray = { version = "0.15.4", features = ["blas"] } -ndarray-linalg = "0.14.1" -blas-src = { version = "0.8.0", default-features = false } -openblas-src = { version = "0.10.4", default-features = false, optional = true } +ndarray = { version = "0.15.6", features = ["blas"] } +ndarray-linalg = "0.16.0" + +openblas-src = { version = "0.10.5", optional = true } accelerate-src = { version = "0.3.2", optional = true } -intel-mkl-src = { version = "0.6.0", optional = true, features = ["mkl-static-lp64-seq"] } +intel-mkl-src = { version = "0.8.1", optional = true } + +ndarray_einsum_beta = "0.7.0" +nlopt = "0.6.0" + enum_dispatch = "0.3.8" -numpy = { version = "0.16.2", optional = true } -libc = "0.2.122" -num-traits = "0.2.14" -better-panic = "0.3.0" -squaremat = { path="./squaremat" } -md5 = "0.7.0" -rand = "0.8.5" -nlopt = "0.5.4" -ceres = { path="./ceres" } -itertools = "0.10.3" -pyo3 = { version = "0.16.3", optional = true, features = ["extension-module", "abi3-py37"] } -lax = "0.2.0" +itertools = "0.10.5" derive_more = "0.99.17" -mimalloc = { version = "0.1.28", optional = true, default-features = false, features = ["local_dynamic_tls"] } -ndarray_einsum_beta = "0.7.0" +mimalloc = { version = "0.1.30", optional = true, default-features = false, features = ["local_dynamic_tls"] } -[patch.crates-io] -# remove once https://github.com/blas-lapack-rs/blas-src/pull/9 is merged -blas-src = { git="https://github.com/ethanhs/blas-src", branch="patch-1" } -# Waiting on openblas-src to make a new 0.11 release... -openblas-src = { git="https://github.com/blas-lapack-rs/openblas-src" } +ceres = { path="./ceres", features = ["static"] } -[target.'cfg(target_os = "windows")'.build-dependencies] -vcpkg = "0.2.15" +numpy = "0.17.2" +pyo3 = { version = "0.17.2", features = ["extension-module", "abi3-py38"] } [lib] name = "bqskitrs" path = "src/lib.rs" -crate-type = ["cdylib", "rlib"] - -[dev-dependencies] -criterion = "0.3.5" -ndarray-npy = { version = "0.8.1", default-features = false, features = ["num-complex-0_4"] } -num-complex = { version = "0.4.0", features = ["serde"] } - -[[bench]] -name = "bench_opt" -harness = false +crate-type = ["cdylib"] [profile.release] lto = "fat" codegen-units = 1 opt-level = 3 -debug = true - -[profile.bench] -lto = "fat" -codegen-units = 1 -opt-level = 3 -debug = true - - -[package.metadata.vcpkg] -git = "https://github.com/microsoft/vcpkg" -rev = "2021.05.12" - -[package.metadata.vcpkg.target] -x86_64-pc-windows-msvc = { triplet="x64-windows-static-md", install=[ - "ceres", - "eigen3", - "openblas", - "glog", - "gflags", - "clapack", -] } +debug = true \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 6d99932..aaef514 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM quay.io/pypa/manylinux2014_x86_64 # This docker file is based on the official maturin docker file https://github.com/PyO3/maturin/blob/master/Dockerfile ENV PATH /root/.cargo/bin:$PATH # Add all supported python versions -ENV PATH /opt/python/cp37-cp37m/bin/:/opt/python/cp38-cp38/bin/:/opt/python/cp39-cp39/bin/:$PATH +ENV PATH /opt/python/cp38-cp38/bin/:/opt/python/cp39-cp39/bin/:/opt/python/cp310-cp310/bin/:$PATH # Otherwise `cargo new` errors ENV USER root @@ -10,18 +10,15 @@ ENV CMAKE cmake3 RUN curl https://sh.rustup.rs -sSf | sh -s -- -y \ && rustup set profile minimal \ - && rustup toolchain add nightly-2021-11-02 \ && python3 -m pip install --no-cache-dir cffi \ && mkdir /io RUN git clone https://github.com/PyO3/maturin /maturin/ -RUN cargo +nightly-2021-11-02 rustc --bin maturin --manifest-path /maturin/Cargo.toml -- -C link-arg=-s \ +RUN cargo rustc --bin maturin --manifest-path /maturin/Cargo.toml -- -C link-arg=-s \ && mv /maturin/target/debug/maturin /usr/bin/maturin \ && rm -rf /maturin -RUN rustup default nightly-2021-11-02 - RUN yum install -y git cmake3 eigen3-devel llvm-toolset-7 && \ yum clean all && \ rm -rf /var/cache/yum diff --git a/benches/bench_opt.rs b/benches/bench_opt.rs deleted file mode 100644 index fd5828c..0000000 --- a/benches/bench_opt.rs +++ /dev/null @@ -1,222 +0,0 @@ -use criterion::Criterion; -use criterion::{criterion_group, criterion_main}; - -use bqskitrs::circuit::operation::Operation; -use bqskitrs::circuit::Circuit; -use bqskitrs::gates::*; -use bqskitrs::instantiators::*; -use bqskitrs::minimizers::*; - -use ndarray::Array2; -use num_complex::Complex64; - -use ndarray_npy::ReadNpyExt; - -extern "C" { - fn srand(seed: u32); -} - -fn make_circuit(positions: &[usize], num_qubits: usize, u3: bool) -> Circuit { - let mut ops = vec![]; - let mut constant_gates = vec![]; - // CNOT - let v: Vec = vec![ - 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., - ] - .iter() - .map(|i| Complex64::new(*i, 0.0)) - .collect(); - constant_gates.push(Array2::from_shape_vec((4, 4), v).unwrap()); - // Fill in the first row - for qubit in 0..num_qubits { - if u3 { - ops.push(( - 0, - Operation::new(U3Gate::new().into(), vec![qubit], vec![0.0; 3]), - )); - } else { - ops.push(( - 0, - Operation::new( - VariableUnitaryGate::new(1, vec![2]).into(), - vec![qubit], - vec![0.0; 8], - ), - )); - } - } - - for (cycle, position) in positions.iter().enumerate() { - ops.push(( - cycle, - Operation::new( - ConstantGate::new(0, 2).into(), - vec![*position, *position + 1], - vec![], - ), - )); - ops.push(( - cycle, - Operation::new(RXGate::new().into(), vec![*position], vec![0.0]), - )); - ops.push(( - cycle, - Operation::new(RZGate::new().into(), vec![*position], vec![0.0]), - )); - ops.push(( - cycle, - Operation::new(RXGate::new().into(), vec![*position], vec![0.0]), - )); - ops.push(( - cycle, - Operation::new(RZGate::new().into(), vec![*position], vec![0.0]), - )); - if u3 { - ops.push(( - cycle, - Operation::new(U3Gate::new().into(), vec![position + 1], vec![0.0; 3]), - )); - } else { - ops.push(( - cycle, - Operation::new( - VariableUnitaryGate::new(1, vec![2]).into(), - vec![position + 1], - vec![0.0; 8], - ), - )); - } - } - Circuit::new( - num_qubits, - vec![2; num_qubits], - ops, - constant_gates, - bqskitrs::circuit::SimulationBackend::Matrix, - ) -} - -fn optimize_ceres(minimizer: &CeresJacSolver, cost: &ResidualFunction, x0: &[f64]) { - let _x = minimizer.minimize(cost, x0); -} - -fn optimize_qfactor( - instantiator: &QFactorInstantiator, - circ: &mut Circuit, - target: Array2, - x0: &[f64], -) { - let _x = instantiator.instantiate(circ, target, x0); -} - -fn create_benches() -> Vec<(&'static str, Vec, Vec, usize)> { - // Set random seed for reproducability - unsafe { srand(21211411) } - - // Positions of CNOTs in each circuit - let qft3 = vec![1, 0, 1, 0, 1, 0, 1]; - let qft5 = vec![ - 0, 3, 0, 2, 2, 1, 2, 0, 1, 2, 3, 0, 0, 1, 2, 3, 1, 2, 3, 0, 1, 1, 2, 1, 2, 0, 2, 3, 0, 0, 1, - ]; - let mul = vec![2, 1, 0, 1, 2, 3, 1, 2, 1, 2, 0, 0, 1, 1]; - let fredkin = vec![0, 1, 1, 0, 1, 1, 0, 1]; - let qaoa = vec![ - 0, 2, 3, 1, 3, 0, 3, 2, 2, 2, 3, 2, 1, 0, 1, 1, 2, 1, 2, 3, 0, 3, 0, 1, 2, 1, 0, - ]; - let hhl = vec![1, 0, 1, 0]; - let peres = vec![1, 0, 1, 0, 0, 1, 0]; - let grover3 = vec![1, 0, 1, 0, 0, 1, 0]; - let tfim_5_100 = vec![1, 3, 0, 2, 3, 1, 2, 2, 3, 1, 3, 0, 2, 3, 1, 2, 3, 0, 0, 1]; - let toffoli = vec![0, 1, 0, 1, 0, 0, 1, 0]; - let tfim_6_1 = vec![0, 2, 3, 4, 0, 2, 4, 1, 3, 1]; - let adder = vec![1, 2, 1, 0, 1, 0, 1, 0, 1, 2, 1, 2, 0, 1]; - let qft4 = vec![0, 2, 1, 2, 0, 2, 1, 2, 0, 1, 0, 1, 2, 1, 2, 0]; - let or = vec![1, 0, 1, 0, 0, 1, 0, 0]; - let tfim_4_95 = vec![1, 2, 0, 2, 1, 1, 2, 0, 0, 1, 2, 2]; - let vqe = vec![ - 0, 2, 0, 0, 1, 0, 0, 1, 1, 1, 2, 0, 0, 1, 2, 0, 2, 1, 2, 0, 2, 1, 1, 0, - ]; - let hlf = vec![1, 3, 0, 3, 0, 1, 1, 2, 2, 3, 1, 2, 0, 2, 1]; - - let qft3_bytes = include_bytes!("unitaries/qft3.npy"); - let qft5_bytes = include_bytes!("unitaries/qft5.npy"); - let mul_bytes = include_bytes!("unitaries/mul.npy"); - let fredkin_bytes = include_bytes!("unitaries/fredkin.npy"); - let qaoa_bytes = include_bytes!("unitaries/qaoa.npy"); - let hhl_bytes = include_bytes!("unitaries/hhl.npy"); - let peres_bytes = include_bytes!("unitaries/peres.npy"); - let grover3_bytes = include_bytes!("unitaries/grover3.npy"); - let tfim_5_100_bytes = include_bytes!("unitaries/tfim-5-100.npy"); - let toffoli_bytes = include_bytes!("unitaries/toffoli.npy"); - let tfim_6_1_bytes = include_bytes!("unitaries/tfim-6-1.npy"); - let adder_bytes = include_bytes!("unitaries/adder.npy"); - let qft4_bytes = include_bytes!("unitaries/qft4.npy"); - let or_bytes = include_bytes!("unitaries/or.npy"); - let tfim_4_95_bytes = include_bytes!("unitaries/tfim-4-95.npy"); - let vqe_bytes = include_bytes!("unitaries/vqe.npy"); - let hlf_bytes = include_bytes!("unitaries/hlf.npy"); - - let benchs = vec![ - ("qft3", qft3, qft3_bytes.to_vec(), 3), - ("fredkin", fredkin, fredkin_bytes.to_vec(), 3), - ("hhl", hhl, hhl_bytes.to_vec(), 3), - ("peres", peres, peres_bytes.to_vec(), 3), - ("grover3", grover3, grover3_bytes.to_vec(), 3), - ("toffoli", toffoli, toffoli_bytes.to_vec(), 3), - ("or", or, or_bytes.to_vec(), 3), - ("adder", adder, adder_bytes.to_vec(), 4), - ("qft4", qft4, qft4_bytes.to_vec(), 4), - ("tfim_4_95", tfim_4_95, tfim_4_95_bytes.to_vec(), 4), - ("tfim_5_100", tfim_5_100, tfim_5_100_bytes.to_vec(), 5), - ("qft5", qft5, qft5_bytes.to_vec(), 5), - ("qaoa", qaoa, qaoa_bytes.to_vec(), 5), - ("mul", mul, mul_bytes.to_vec(), 5), - ("vqe", vqe, vqe_bytes.to_vec(), 5), - ("hlf", hlf, hlf_bytes.to_vec(), 5), - ("tfim_6_1", tfim_6_1, tfim_6_1_bytes.to_vec(), 6), - ]; - return benchs; -} - -fn bench_qfactor(c: &mut Criterion) { - // Setup numerical optimizers - let instantiator = QFactorInstantiator::new(None, None, None, None, None, None, None); - - let benchs = create_benches(); - // Run QFactor benchmarks - let mut group = c.benchmark_group("qfactor"); - for (name, positions, npy, qubits) in benchs { - let circ = make_circuit(&positions, qubits, false); - let x0 = vec![0.0; circ.num_params()]; - let target = Array2::read_npy(&npy[..]).unwrap(); - group.sample_size(10).bench_function(name, |b| { - b.iter(|| optimize_qfactor(&instantiator, &mut circ.clone(), target.clone(), &x0)) - }); - } -} - -fn bench_ceres(c: &mut Criterion) { - let benchs = create_benches(); - let minimizer = CeresJacSolver::new(1, 1e-6, 1e-10, false); - // Run Ceres benchmarks - let mut group = c.benchmark_group("ceres"); - for (name, positions, npy, qubits) in &benchs { - let circ = make_circuit(positions, *qubits, true); - let x0 = vec![0.0; circ.num_params()]; - let target = Array2::read_npy(&npy[..]).unwrap(); - let cost = HilbertSchmidtResidualFn::new(circ, target); - group.sample_size(10).bench_function(name.clone(), |b| { - b.iter(|| { - optimize_ceres( - &minimizer, - &ResidualFunction::HilbertSchmidt(Box::new(cost.clone())), - &x0, - ) - }) - }); - } -} - -criterion_group!(ceres, bench_ceres); -criterion_group!(qfactor, bench_qfactor); -criterion_main!(ceres, qfactor); diff --git a/benches/unitaries/adder.npy b/benches/unitaries/adder.npy deleted file mode 100644 index 9f3e5dea35597bafcd2ab0c5ada79907d1774684..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4224 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 diff --git a/benches/unitaries/fredkin.npy b/benches/unitaries/fredkin.npy deleted file mode 100644 index eb2bfa11e925bf0d88296476fbb13086d9cd2df4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1152 zcmbVKYc$(u7`Hlt={mahSazvoEuoUN%u=2Ae^?OAny4;qF`JAKEt4inbLtYOjjGE? zLR{8jGU8H2m)XYq2A!!o7)OFp8ke)vo=xj=x<#n%-*4I1{k}cVbI$X-Jdc9KIT04| zl0}-uWl9`R6f2~BK%#(H4?2ZPqQs{NQ-$0wKTi?H@m>s=+$0eX?~C|c0S~WTy{RN` zH!3NU^nda4);}-s)Yk%sq^1(HqDR_&#%n>vF&l&saxbQj$UA>$w-#w=W!K&4_2`Ln zseYLz#N20AJvw|cvC;IJGlwwWFadjX>NHPpF~+OSI@ znFFWw$!Pl1b$zj0GfJf&^wpn(m|Wj@qUpm#xNPfVI^~%PNvvUCRaXW`>R!3>#yl5V zZsgycx?KxslimH!>H_%F?2=!CHXm&DXSg~S-h$-U{9^idYcYeaND+kl>l>7h9Mhuuk zB{mOMufoQzSA~YAD&RWM8>mro!5@o*SNJ_;aO?efpU9~an3g+QNIoJ&2d@wAkj-4}bUJd^=`(!roD!z@AVS8Z8Arml$c}j3i)v zE}`!2)Qez(s92v^tAG*DRJOEq704J+FW6}tU|z7p)MJ}^P}qNHwK1v&-0}_2KlWo8 zv`cenO;0I?+hT`FvE?T)V*Tb&XNVkHb%@ZT!y}zw5n(Z%Yq1~OH(~LKf;)4BX>8v0Ajf&&XKX=6J&KOkqItHe@9dih zz1!u?Sv#o)4fQ*ZXZKgZ3+!WEAU6-b?*Gth1&Cm>A$i&uS_N0-tOeFm1rT~~R+EWE zP@&??_+H9^$<_kdEMXcQrdkXi!S|E)=Bt&4b}VSQJj=_!1Z|q-Whj*)-LKR_&*&CR zu~SguV$`DOYze!aB!@|2Nt@&SVo<<%-rJCq1Fi+QUr6wmKuO@XX7A#8c(yfMN*zxH zH>6zwWRnUSQ%KBRx^hq%TXr{m5uwXYV{@l)zvg*JzLO!-nO6TBhIZspC;Z67C_(Fg zi8xEVA;gH{IMGIjQN=Gc+zAFBtm~=cyS`CEwzsCR$*&Yb=GJ63vl-4jebnJ4&V^B- z>D@Gq7_Ly*gM7a diff --git a/benches/unitaries/grover3.npy b/benches/unitaries/grover3.npy deleted file mode 100644 index 5e914e8ae1092b32d8d42aeee72f2d515cd573fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1152 zcmbV~{ZCV86vwZnDK7&fzf>lMT{A+_RWcS|x@G6K;>MW40w&f;w7gkFVTH7n)eW+X z4&uu)M52k^R<7tEHrtpatm1YG5e=p$zGOu}x~^o24I8Ot4B7!LySK~JKVbK_b3e~H z-|zE1Pp5kS-r|F6s7C4&*%6(w${>4-mc3n-yF(_YW!3ctlc7RiR&O|>6F`1Nol(dC zH`Y`%==eHYA*U6a<#aRsf4`(OSCOq1Jk02;7@IIxF{dh@vRf57Qwm{qR(^ zm3yuf>g|ybiZe2g@?5AJ#*0KuC4jw5zJeaLZKJEpk1sDB;Lk@nWv`h5OIwhuye?*_ zocfma&k&dVmk4K-wJvP1jJ|K*eFx5dw|aL^LJ;$g&k_CMMEe3lQj?%B_@VX{E<^FR zsYKr!u8d={Qc~WT+PVy;L*hq$r2$C2S4QSjy|;a!C{0X|=p_zT`A9!6X$9wGPmEvI z|AuLo;zuqYTf**xllMoAv#71QC~!fKEfP<`zU+;__cIsTcZbuTA*kv~Vm$ZxFIXqxG@(8H-cFcorBx>_wl-W1KE$aI9cp4N8a5Y&9Bqk$MF6& z>kpIp>_Cne`t#D{Xodi)R|NJ4Mcv0?K$7|U}TWF6VpLtnI-vTt~T?S z7*XnGphUl7LeXn)8dP)xsN80HXk$3xvS-rXk;@=E+`0#O(??-okB|`+A%xy+|AXRE zAyhPEd~@uzMCfu^C{Ls+f$vy(!$w*J3aVnov^4|nFn;n?E9Gbd<2uKUe)4Ee{&mjx2(UN}N+D&yW(ax;BvC4<&!wd~r=@g07&c@B7pzkTT_E zhbeIw+Ew7xf4aCBwI|1yIW=Yh7WpToM#g)sw3?iY-b3l6NSEviSqKvS3mBf|J?9hb=LCe~1-W z;7(RNAWXXHOpvou|Mg0Jcyp7>CZ}r&*2jxu)mT2m6P0$B+7_Lu5=^cw3VM zMQ*Vuys*q9vEV=ST+jk#w^xH#feG~&&ZoaYHNsQ>cRk-VC$*2Ynnvat-||fS94E|s zBL(vwU4ZZdeP^rLr=ik&H#X3ajZ}6`&d)8rjZS{OlofxDj|}?Tr5)p`NOLm47!Ii* zsjzCWqpkwSFgM|zH;Vifol8T7?u1Mmxl535OHw-X+10f;@g?_`=E0{la>EaoYOdH^ F+kY)l{eu7i diff --git a/benches/unitaries/hlf.npy b/benches/unitaries/hlf.npy deleted file mode 100644 index a9978425fba041802e4ac926beb256ded095858f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16512 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 diff --git a/benches/unitaries/mul.npy b/benches/unitaries/mul.npy deleted file mode 100644 index 536f48b4902791827dc4fe4dbbd66c981616cd9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16512 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 d>wxEsLp zv_MXn$suwZ8!4!yg1gzHbPhV!S`LO-L&7&EasWj}gSP$;eW9=2U!LE z-$SC=%;?wH%mU{B9Xe%UCiUdQJl};@iyF3g^_md3h&4Or)rbsxW*)`}6hKi0jwY?k zLtA>3x>t1?n0LEviu500sdQh4GR%Z%y=^o>-w3MyazG&uCs6&eRWF9wj3}(q#-*h^ zM4rp-Z_U|gywsAfK$-FI3LjXt&RZ-WVL4NBvmV7W__o>Dsz!uQ?Z-hAIq)o_ihY?wpbU=>U7>#%(M>gX*y{}w3cjtWe{^F6 z-R?+RO~rcKkE{t8#Cp*+{Zx|Y+@NsOiFxXO>0HjxY0wivx=W4knLS>vKi{f`_>77A z7`(r8)8}{GDeb7?t0h}skT-$LP0qxEU%As-R2Q_X{ov+*(E+nAA23@D;G08H5Rz!DPLv6 z{n)5^>1EsxxI5djgY4^ksCpOnt1I%GjA_!ZGu`~$nQMBKlm5K|aDMy>nROOakTeh& zv=r~tHe~g$=|2nnLnX>z@qMB0nN!aQ+fmKwWD7^z^q+o*{G>t@*I|Yi*7L7)iAlZM z{NelVW(#zc?M}OjdA#rW$;SF7=X(toka$;Vi*eUG6tnhiLDG|j&lJwaZC|3K!Av@q3y8pWkEz?P&1~* zp%ry45fx!Wu!BhqlL|W*R6cYBErZep!bcg-x($ks3|p+*Iqb*yz4Pzho4n6+pZnhP zyAosLQygdov?7jJl9w*$Y#}(|>B3MBpWsMyh=zd;?C|826 z1H<@47?)3cM*QF7{OWIfES)qOO|ahXPBj{X=>bd=Absku?8?3eBAFN5TSir|I<+;V zsB{ocwH!Y+P*DiW2R!E5^4owdB)IL7@(B=&-+j|rX$9JpNZD_c1%9*i>kd7`0B#)m ze4^91h9Vp{d~dAdlCM1XxMq0qsL&;M>t$?ry|e;nS5h;Y>-{ zKfNb1L9q4Hil`w&4Q!!LGfh(}c!n{}xQ={I><6db?|TO4d)iYu$F;x`>f$mZoCkfp z$=>9vi-14L;$xnTsT;<;Quc#e*vH#c-++C(ssyn|MpZy_f8nkI%?o9Y$Z|~(9@98g zTM%zK&%b@9Ue`L3V{tp{{ZSu5N0_H|`y(hncSaH6!6Tz4U+#Ho@F3M6F4UNZ*O1Tc zo*l(sb17o_jmuvpSAw0}ntaomZLnDHS80Drr<9h&vQ6toVc6zHVO>QbP^Zl7Yezkm zKaTghO{sv~>{XyeK9hg7wc1K@?#8@ApRy9{!#bTN z!9EjNo0><)ov2SvdRg7fkKsikJ0zf14-frmHfnlaz=M@{en&mX{`BhGxQFfzkBxef zZVJaC-0N!NoIXCe5-NGm_hSFlF-AF>M<(brhh5RUhraGttceCt9WNDLJ^*1v#pd+| zaYJB|5xCZ`?-sN#3oq{%t)g=^=~`0UkN0a;S5juAvKRP==dRde9Q7j0f(vVcP_HEQrK7&Jdcc`w^{A2mwN_cQ18)qN)d@#dN7sUeS=~jy zBm;1u5AY7iAnZPSI0ErtT@CBzR>XVxpU?dDEEf3z8Gq~#$^sgPXKaILU*9q^ntu__ z!zGF83%oyWx)l&sV&8VNe&zcL_$e%^ z=Hs-{oVX#ln`v9eK|PEQG6MXQ4AA9tkh>K5`7DTn29-}>c=nC}{Qqn$2uuj?dj>vo zkj8qWeOAbB=VM+#RFe$z(zhMFhkY_{(%ejTU{MqHp`FmbhbZZ#r+SH|q5d{Yow@ diff --git a/benches/unitaries/qaoa.npy b/benches/unitaries/qaoa.npy deleted file mode 100644 index eac02113cfbcf1ba9e4403495f71567a3bff0761..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16512 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 T-xr~xgL1rHK)M6j=!5tdo?GfQ diff --git a/benches/unitaries/qft4.npy b/benches/unitaries/qft4.npy deleted file mode 100644 index 5e31b52a5690abd95b332bae66baee5d1da833b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4224 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 MnY{EWu?^K4%h{9ONx_rRL2!M;xB zr*6^G|Ae2(up^#mH{@wHbUz25jadTSZ@<9L)9DrQt4wmWG)&x6Wnle5mRyJK!c6%2 zCUm+aDRi6WqP_U-hi=Df@KY%Xy3f2t{1*e2_}iCiCCfvI-!TGy z?q240Z2WQfS@#LL&A{jLN3CcrRfWON+9+;EE@#6}0nQ!B?2GUdxeoC^1D~mOD(jzJ zQ&Oaxy)my0UyOOiYC6_KKA1;I&4Fm2Rn}sDD%yhm=Nh~x%J#-#eho{;{Bk-JdacWF z-r|*o`SE7}|2rYB|Bo&yQiu-gpx<-A9ps34_A=HDq-O-yMMg2;wt@~;74QAAeCS|b zHh{Y{8uOg=7TkX#k?+!QaQ7U?{5V)svHnuE^i82>9l&)B<2u;ZU~t=(q8^tAV_fK! zh;>4^j>pfacu(}^N^r9hG0(FIa8;kgc~365(V_S|DY^Yl9G^ljAbz%gF1YcXQHRH3 z{ZbD)h|yMX>kmR5(%WGlxF!L0$PnDx+fj!vufSaphB`>W;AV+BOv1fBliP7W{XM@E z!M;4sL{^6V|1)civ~4~1nfYS>*lY^cleciM%*W4e`hB)a?2FHsaeLXXDz$-z*3)30 z-B-NVZ-sp}b1qf(pG9#RT1W7U#67v*T)m{WMw*%oe%rU$=UIe;UoPHDkiR!T|DHiH z-U}f6p`XV!&{#ujigSjaFR@SBFYf816u!D=l7^0ULHw!WJ%8En&ld?&z((+Q{qJ61 z@cWf0`^VecHBy~Hs7HETy(RYl>H+Q=+6MV!(iiZ5_+hHRwbQCn^Ty}3abol$@w&_3us&Dkhu@5CrM+6Lr23p$omMP z!@3IcJ{NT$pTH$2|Ar3N{>ZzZ(6LxT-mQ#y{Omd2Lu0!uK}T8(FU_m_(xQKw;JkBJG3-v^J!|&sDb{fg-!Y!aPfPq( zOU^-v!#@~y{c+yG4mU%6s)Ek~-^0$>4ExHwaD(`rSJ4$BewzTq;a|e}%uBpKKd2P( z_ZD%;Pfxh6ri()m$1&(8P8|@(Z@iaH3Pk(|ejsn+JxDTOx)T5Fi>ssvTiA`7fH?lV z&-tXcnCGm@5r6szCH{ApR!Q5YAdXvMuv4{19rs~gVqPMCv3_Iucz=;O|ABi8nUb(M zTH4tb=gYQVPxF5McQiPQm!e+d+TeWrN)*oNO+&E%tZRlkcN&ay<%1uwKEB=@_y4)k zxL3)^{hROmy@N1*$XxT-XemM5-&9uNeEq5w&iR99gL`EK@)oiTTx(mLkN>U*?t|%E z2dVZ0+_xS$XZKcvyT}^%|F1*ARiEKH$f#+`{Y&4z(bUTt+-c8w{A~LYa65#cz5MD3 zZhR!}2gXkYx1%xI*||~RZvBdN`Z5b}uSH?}c$x?9ylEIeZWj-1)6g xhf5LQnk+>fV&;H*$p&>WuMci~5bCfBd1ot!pblYzyTTN8xP`p4T?bHy{{!17c258R diff --git a/benches/unitaries/tfim-4-95.npy b/benches/unitaries/tfim-4-95.npy deleted file mode 100644 index 5f9783d4e34d230116343aeab303063ceef38d8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4224 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 diff --git a/benches/unitaries/toffoli.npy b/benches/unitaries/toffoli.npy deleted file mode 100644 index 035ca9fa0aa14dd51c78498c4c3c113b8aef6803..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1152 zcmbV~Yc$(;9EW2u=hT_9D&5*$qBYS{s+%&tL03tu#Tea&ZbjUxE|ElCDsFRxk*0B} zEfb}o;$HtCk^WMIV`x^FtgPvwYNb`1b~(&#o5^0)+kM{se&09GdCpVpeb&n-KnHgT zcbOO#&Y;qXP6Q%Q?T!(x3B-taI+Gp}cRrpT7Op)|LSh)2LnG7p^$%ipX-s$jgQMm`ILuD-WDXmcRsFfN4}1D32w~=S&H~G`7Hon#uvn z@YTz9F;{@F7o+YMlp+%Skk`BC#K=4#L0TA_iEyJ9&iCr8Ab4ow^z*q8aA8}`{$|h( zn~9>_Mv)jrob4y)RY>6@?!eJiOhm#>dq&6@GWh3Jo(wXnibKlTc&i0;Hy-V&$u5Jj z@^L){4FlQyUA?ER95C-<+tPyANN<3!)}2v|ZWvom>=3k~T9zi-Kd=)W|1yr$cp5`p zvn0NAiwrUqedF-4AJlb^eHwYP5rS9aD;t_H#Bm>bCJVR?x_N3>4?z+7cGh5@m{q-< zr-v+`sig3ywSI787ou1t;Gj;-dQ*Q8iR$r2J2(X}Wn;5aQ;>@)4g}2(&hSv3`+KD; zKCNJQO))mjy@Yhq9)G@`&qUs4ix+$L^I(o&t6{{YLFb}P>ho)5V8m1()D04#6YWZV zhcO13p7TqOwSI7xc>VtT?L4Q*q?1C@XzY&y_`@+FUMH&zxlFn@A*K{r(w!X4o^p}N z3-je?R6gX=riCURY&ej#WPA}XfzU5I-kJv;yumtS&xxguEZ4L4C4X2FzG=96~YI<&2yaxbas z%jWe1&Mk}A`@jg6Li5UXt#85M`?5V}zOF(}CS0jnRSo`R)rk@@0}Pi8)7(#S5i#;n zr!c(}!tji~{&fk8our$8!^}jnF0XCzF;H9n?wz8=PROI0wWveC1F>KConN(nd>VE~ z9qog^UhOA^Od9U`JE`)*b#%9V*Q9u*9xOOK+6OmV!Op?;=eJG7aP`mH&0@S17S2=0 zJx!(HO75Ks%&$SZ*%W2=hYhf1(}qhC-2%@Hd`9!3X7E=;2wzWSgYzzo>RnWUrYVVj zo4tN89H2=#7jqMA2YtzFnj47p*-CK9CxsyLogD~cE4F$WIbPnxf^EKz1k3HQ>W4E0 z5(IAc28GHFa9ph={PeILb{d*5Br)W01ha@tZb?Vv^(M)jeJN6@zIyV&q8SJ&(QB1m zd&E0uV0k@B57UV7X2uu4m{@`?;XBfq!c&uBWf_0qjBm?Rsi76!2uTRl0B{?L;J F{{k~gA~FB~ diff --git a/benches/unitaries/vqe.npy b/benches/unitaries/vqe.npy deleted file mode 100644 index 011c7331928b7bc5ad61c3ef90f341da4efea93b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16512 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 Result<(), std::io::Error> { - // Homebrew/macOS gcc don't add libgfortran to the rpath, - // so we manually go prodding around for it here - if cfg!(target_os = "windows") && cfg!(feature = "static") { - #[cfg(target_os = "windows")] - let _lapack = vcpkg::Config::new() - .target_triplet("x64-windows-static-md") - .find_package("clapack") - .unwrap(); - println!("cargo:rustc-link-lib=static=lapack"); - } - if cfg!(target_os = "macos") { - // First, we get brew prefix, if installed. Fall back to system gcc. - let prefix = String::from_utf8( - Command::new("brew") - .arg("--prefix") - .output() - .expect("Failed to run brew --prefix") - .stdout, - ) - .unwrap() - .replace('\n', ""); - let mut gcc_dir = PathBuf::new(); - gcc_dir.push(prefix.clone()); - - gcc_dir = fs::read_dir(gcc_dir.join("Cellar/gcc"))? - .map(|res| res.map(|e| e.path())) - .filter_map(Result::ok) - .last() - .expect("No gcc installed?"); - gcc_dir = gcc_dir.join("lib/gcc"); - let version_dir = fs::read_dir(gcc_dir)? - .map(|res| res.map(|e| e.path())) - .filter_map(Result::ok) - .last() - .expect("No directories in prefix?"); - let mut openblas_dir = PathBuf::new(); - openblas_dir.push(prefix); - openblas_dir.push("opt/openblas/lib"); - println!("cargo:rustc-link-search={}", version_dir.to_str().unwrap()); - println!("cargo:rustc-link-search={}", openblas_dir.to_str().unwrap()); - } - Ok(()) -} diff --git a/ceres/ceres-sys/Cargo.toml b/ceres/ceres-sys/Cargo.toml index b5f6739..a60c9fd 100644 --- a/ceres/ceres-sys/Cargo.toml +++ b/ceres/ceres-sys/Cargo.toml @@ -10,11 +10,11 @@ edition = "2018" cxx = "1.0" [build-dependencies] -cxx-build = "1.0" -cmake = "0.1.45" +cxx-build = "1.0.79" +cmake = "0.1.48" [target.'cfg(target_os = "windows")'.build-dependencies] -vcpkg = "0.2.13" +vcpkg = "0.2.15" [features] static = [] diff --git a/ceres/ceres-sys/build.rs b/ceres/ceres-sys/build.rs index d12e82a..a89054f 100644 --- a/ceres/ceres-sys/build.rs +++ b/ceres/ceres-sys/build.rs @@ -34,7 +34,7 @@ fn main() { } else { let ceres = Config::new("ceres-solver") .define("EXPORT_BUILD_DIR", "ON") - .define("CXX_THREADS", "ON") + .define("NO_THREADS", "ON") .define("BUILD_TESTING", "OFF") .define("BUILD_BENCHMARKS", "OFF") .define("MINIGLOG", "ON") @@ -46,7 +46,7 @@ fn main() { .define("SUITESPARSE", "OFF") .define("CXSPARSE", "OFF") .build(); - println!("cargo:rustc-link-search=native={}/lib", ceres.display()); + println!("cargo:rustc-link-search=native={}/build/lib", ceres.display()); let profile = std::env::var("PROFILE").unwrap(); let lib_name = match profile.as_str() { "debug" => "ceres-debug", @@ -63,6 +63,7 @@ fn main() { ceres.display() )); let targetinclude = std::path::PathBuf::from(format!("{}/include", ceres.display())); + // let compiledlib = std::path::PathBuf::from(format!("{}/lib/{}.a", ceres.display(), lib_name)); let mut b = cxx_build::bridge("src/solve_silent.rs"); b.flag_if_supported("-std=c++14") .flag_if_supported("-Wno-unused-parameter") @@ -72,6 +73,7 @@ fn main() { .include(localinclude) .include(targetminiglog) .include(targetinclude) + // .object(compiledlib) .include("src") .compile("autocxx-ceres"); println!("cargo:rerun-if-changed=src/lib.rs"); diff --git a/pyproject.toml b/pyproject.toml index 98352d2..3b349ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] requires = [ - "maturin>=0.10,<0.11", + "maturin>=0.13,<0.14", "wheel", "oldest-supported-numpy", ] @@ -8,7 +8,7 @@ build-backend = "maturin" [project] name = "bqskitrs" -version = "0.2.3" +version = "0.3.0" maintainers = [ {name = "Ethan Smith", email = "ethanhs@lbl.gov"}, {name = "Ed Younis", email = "edyounis@lbl.gov"}, @@ -23,9 +23,3 @@ Source = "https://github.com/bqskit/bqskitrs" [tool.maturin] manylinux = "off" bindings = "pyo3" -# To use Apple's Accelerate framework, uncomment the following line -#cargo-extra-args = "--no-default-features --features=python,accelerate,ceres/static,mimalloc/local_dynamic_tls" -# To use Intel's MKL, uncomment the following line -#cargo-extra-args = "--no-default-features --features=python,mkl,mimalloc/local_dynamic_tls" -# On Windows we statically link to ceres and other dependencies -#cargo-extra-args="--no-default-features --features=python,static,mimalloc/local_dynamic_tls" diff --git a/squaremat/.gitignore b/squaremat/.gitignore deleted file mode 100644 index 6936990..0000000 --- a/squaremat/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -**/*.rs.bk -Cargo.lock diff --git a/squaremat/Cargo.toml b/squaremat/Cargo.toml deleted file mode 100644 index 32fc5b5..0000000 --- a/squaremat/Cargo.toml +++ /dev/null @@ -1,48 +0,0 @@ -[package] -name = "squaremat" -version = "0.6.0" -authors = ["Ethan Smith "] -edition = "2018" -build = "build.rs" - -[features] -openblas-static = [ - "openblas-src", - "blas-src/openblas", - "openblas-src/static", - "openblas-src/cblas", -] -openblas-system = [ - "openblas-src", - "blas-src/openblas", - "openblas-src/system", - "openblas-src/cblas", -] -accelerate = ["accelerate-src", "blas-src/accelerate"] -mkl = ["intel-mkl-src", "blas-src/intel-mkl"] - -[dependencies] -num-complex = "0.4.0" -cblas-sys = { version = "0.1.4" } -ndarray = { version = "0.15.3", features = ["blas"] } -blas-src = { version = "0.8.0", default-features = false } -openblas-src = { version = "0.10.4", default-features = false, optional = true } -accelerate-src = { version = "0.3.2", optional = true } -intel-mkl-src = { version = "0.6.0", optional = true } -libc = "0.2.104" - -[patch.crates-io] -blas-src = { git="https://github.com/ethanhs/blas-src", branch="patch-1" } -# Waiting on openblas-src to make a new 0.11 release... -openblas-src = { git="https://github.com/blas-lapack-rs/openblas-src" } - -[lib] -name = "squaremat" -path = "src/lib.rs" -crate-type = ["cdylib", "rlib"] - -[profile.release] -lto = "fat" -codegen-units = 1 -opt-level = 3 -debug = true diff --git a/squaremat/build.rs b/squaremat/build.rs deleted file mode 100644 index 371759d..0000000 --- a/squaremat/build.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::fs; -use std::path::PathBuf; -use std::process::Command; - -fn main() -> Result<(), std::io::Error> { - // Homebrew/macOS gcc don't add libgfortran to the rpath, - // so we manually go prodding around for it here - if cfg!(target_os = "macos") { - // First, we get brew prefix, if installed. Fall back to system gcc. - let prefix = String::from_utf8( - Command::new("brew") - .arg("--prefix") - .output() - .expect("Failed to run brew --prefix") - .stdout, - ) - .unwrap() - .replace("\n", ""); - let mut gcc_dir = PathBuf::new(); - gcc_dir.push(prefix.clone()); - - gcc_dir = fs::read_dir(gcc_dir.join("Cellar/gcc"))? - .map(|res| res.map(|e| e.path())) - .filter_map(Result::ok) - .last() - .expect("No gcc installed?"); - gcc_dir = gcc_dir.join("lib/gcc"); - let version_dir = fs::read_dir(gcc_dir)? - .map(|res| res.map(|e| e.path())) - .filter_map(Result::ok) - .last() - .expect("No directories in prefix?"); - let mut openblas_dir = PathBuf::new(); - openblas_dir.push(prefix); - openblas_dir.push("opt/openblas/lib"); - println!("cargo:rustc-link-search={}", version_dir.to_str().unwrap()); - println!("cargo:rustc-link-search={}", openblas_dir.to_str().unwrap()); - } - Ok(()) -} diff --git a/squaremat/src/conj.rs b/squaremat/src/conj.rs deleted file mode 100644 index c0830ee..0000000 --- a/squaremat/src/conj.rs +++ /dev/null @@ -1,18 +0,0 @@ -use ndarray::{Array2, ArrayView2}; -use num_complex::Complex64; - -pub trait Conj { - fn conj(&self) -> Array2; -} - -impl Conj for Array2 { - fn conj(&self) -> Array2 { - self.mapv(|i| i.conj()) - } -} - -impl Conj for ArrayView2<'_, Complex64> { - fn conj(&self) -> Array2 { - self.mapv(|i| i.conj()) - } -} diff --git a/squaremat/src/lib.rs b/squaremat/src/lib.rs deleted file mode 100644 index 332dbd0..0000000 --- a/squaremat/src/lib.rs +++ /dev/null @@ -1,43 +0,0 @@ -#[cfg(feature = "accelerate")] -extern crate accelerate_src; -extern crate cblas_sys; -#[cfg(feature = "mkl")] -extern crate intel_mkl_src; -#[cfg(any(feature = "openblas-static", feature = "openblas-system"))] -#[link(name = "openblas")] -extern "C" {} - -#[macro_export] -macro_rules! c { - ($re:expr, $im:expr) => { - Complex64::new($re, $im) - }; -} - -#[macro_export] -macro_rules! r { - ($re:expr) => { - Complex64::new($re, 0.0) - }; -} - -#[macro_export] -macro_rules! i { - ($im:expr) => { - Complex64::new(0.0, $im) - }; -} - -mod conj; -mod kron; -mod matmul; -mod multiply; -mod split_complex; -mod swap_rows; - -pub use conj::Conj; -pub use kron::Kronecker; -pub use matmul::Matmul; -pub use multiply::Multiply; -pub use split_complex::SplitComplex; -pub use swap_rows::SwapRows; diff --git a/squaremat/src/matmul.rs b/squaremat/src/matmul.rs deleted file mode 100644 index 858870b..0000000 --- a/squaremat/src/matmul.rs +++ /dev/null @@ -1,109 +0,0 @@ -use ndarray::{Array2, ArrayView2, ArrayViewMut2, CowArray, Ix2, ShapeBuilder}; -use num_complex::Complex64; - -use cblas_sys::cblas_zgemm; -use cblas_sys::{CblasNoTrans, CblasRowMajor, CblasTrans}; - -pub trait Matmul { - fn matmul(&self, other: ArrayView2) -> Array2; -} - -/// Complex matrix-matrix multiplication using BLAS zgemm. Based on the matmu_impl in the -/// ndarray crate, see the LICENSE file for more details -/// https://github.com/rust-ndarray/ndarray/blob/562104a5326acdefbd0235599b91a59bcc8d73d4/src/linalg/impl_linalg.rs#L367 -fn matmul_impl(lhs: ArrayView2, rhs: ArrayView2) -> Array2 { - let ((mut m, a), (_, mut n)) = (lhs.dim(), rhs.dim()); - let mut lhs_ = lhs.view(); - let mut rhs_ = rhs.view(); - - let lhs_s0 = lhs_.strides()[0]; - let rhs_s0 = rhs_.strides()[0]; - - let column_major = lhs_s0 == 1 && rhs_s0 == 1; - let mut v = Vec::with_capacity(m * n); - let mut out; - unsafe { - v.set_len(m * n); - out = Array2::from_shape_vec_unchecked((m, n).set_f(column_major), v); - } - let mut c_ = out.view_mut(); - let both_f = lhs_s0 == 1 && rhs_s0 == 1; - let mut lhs_trans = CblasNoTrans; - let mut rhs_trans = CblasNoTrans; - if both_f { - // A^t B^t = C^t => B A = C - let lhs_t = lhs_.reversed_axes(); - lhs_ = rhs_.reversed_axes(); - rhs_ = lhs_t; - c_ = c_.reversed_axes(); - std::mem::swap(&mut m, &mut n); - } else if lhs_s0 == 1 && m == a { - lhs_ = lhs_.reversed_axes(); - lhs_trans = CblasTrans; - } else if rhs_s0 == 1 && a == n { - rhs_ = rhs_.reversed_axes(); - rhs_trans = CblasTrans; - } - - let (m, k) = match lhs_trans { - CblasNoTrans => lhs_.dim(), - _ => { - let (rows, cols) = lhs_.dim(); - (cols, rows) - } - }; - let n = match rhs_trans { - CblasNoTrans => rhs_.raw_dim()[1], - _ => rhs_.raw_dim()[0], - }; - - // adjust strides, these may [1, 1] for column matrices - let lhs_stride = std::cmp::max(lhs_.strides()[0] as i32, k as i32); - let rhs_stride = std::cmp::max(rhs_.strides()[0] as i32, n as i32); - let c_stride = std::cmp::max(c_.strides()[0] as i32, n as i32); - let one = r!(1.); - let zero = r!(0.); - unsafe { - cblas_zgemm( - CblasRowMajor, - lhs_trans, - rhs_trans, - m as i32, - n as i32, - k as i32, - &one as *const Complex64 as *const _, - lhs_.as_ptr() as *const _, - lhs_stride as i32, - rhs_.as_ptr() as *const _, - rhs_stride as i32, - &zero as *const Complex64 as *const _, - c_.as_mut_ptr() as *mut _, - c_stride as i32, - ) - }; - out -} - -impl Matmul for CowArray<'_, Complex64, Ix2> { - fn matmul(&self, other: ArrayView2) -> Array2 { - matmul_impl(self.view(), other) - } -} - -impl Matmul for Array2 { - fn matmul(&self, other: ArrayView2) -> Array2 { - matmul_impl(self.view(), other.view()) - } -} - -impl Matmul for ArrayView2<'_, Complex64> { - fn matmul(&self, other: ArrayView2) -> Array2 { - matmul_impl(self.view(), other) - } -} - -impl Matmul for ArrayViewMut2<'_, Complex64> { - fn matmul(&self, other: ArrayView2) -> Array2 { - matmul_impl(self.view(), other) - } -} diff --git a/src/circuit/mod.rs b/src/circuit/mod.rs deleted file mode 100644 index 89158ac..0000000 --- a/src/circuit/mod.rs +++ /dev/null @@ -1,317 +0,0 @@ -use crate::{ - gates::{Gate, Gradient, Unitary}, - permutation_matrix::calc_permutation_matrix, - unitary_builder::UnitaryBuilder, -}; - -use itertools::izip; -use ndarray::{Array2, Array3}; -use num_complex::Complex64; -use squaremat::*; - -pub mod operation; - -use operation::Operation; - -use crate::permutation_matrix::permute_unitary; - -#[derive(Clone)] -pub enum SimulationBackend { - Tensor, - Matrix, -} - -type Cycle = usize; - -/// A list of gates in a quantum circuit -#[derive(Clone)] -pub struct Circuit { - pub size: usize, - pub radixes: Vec, - pub ops: Vec, - pub constant_gates: Vec>, - pub cycle_boundaries: Vec<(usize, usize)>, - pub num_params: usize, - pub sendable: bool, - pub backend: SimulationBackend, -} - -impl Circuit { - pub fn new( - size: usize, - radixes: Vec, - ops_with_cycles: Vec<(Cycle, Operation)>, - constant_gates: Vec>, - backend: SimulationBackend, - ) -> Self { - let mut sendable = true; - let mut num_params = 0; - let mut cycle_boundaries = Vec::new(); - let mut current_cycle = 0usize; - for (cycle, op) in &ops_with_cycles { - num_params += op.gate.num_params(); - if let Gate::Dynamic(_) = op.gate { - sendable = false - } - if *cycle != current_cycle { - cycle_boundaries.push((current_cycle, *cycle)); - current_cycle += 1; - } - } - Circuit { - size, - radixes, - ops: ops_with_cycles.iter().map(|(_, op)| op.clone()).collect(), - constant_gates, - cycle_boundaries, - num_params, - sendable, - backend, - } - } - - pub fn is_sendable(&self) -> bool { - self.sendable - } - - pub fn get_params(&self) -> Vec { - let ret = Vec::with_capacity(self.num_params()); - self.ops.iter().fold(ret, |mut ret, op| { - ret.extend_from_slice(&op.params); - ret - }) - } - - pub fn set_params(&mut self, params: &[f64]) { - if params.len() != self.num_params() { - panic!( - "Incorrect number of parameters in set_params, expected {} got {}", - self.num_params(), - params.len() - ); - } - let mut param_idx = 0; - for op in self.ops.iter_mut() { - let parameters = ¶ms[param_idx..param_idx + op.num_params()]; - op.params.copy_from_slice(parameters); - param_idx += op.num_params(); - } - } -} - -impl Unitary for Circuit { - fn num_params(&self) -> usize { - self.num_params - } - - fn get_utry(&self, params: &[f64], const_gates: &[Array2]) -> Array2 { - match &self.backend { - SimulationBackend::Tensor => { - if !params.is_empty() { - let mut param_idx = 0; - let mut builder = UnitaryBuilder::new(self.size, self.radixes.clone()); - for op in &self.ops { - let utry = op - .get_utry(¶ms[param_idx..param_idx + op.num_params()], const_gates); - param_idx += op.num_params(); - builder.apply_right(utry.view(), &op.location, false); - } - builder.get_utry() - } else { - let mut builder = UnitaryBuilder::new(self.size, self.radixes.clone()); - for op in &self.ops { - let utry = op.get_utry(&[], const_gates); - builder.apply_right(utry.view(), &op.location, false); - } - builder.get_utry() - } - } - SimulationBackend::Matrix => { - let mut matrices = vec![]; - - if params.is_empty() { - for op in &self.ops { - let mut utry = op.get_utry(&[], const_gates); - utry = permute_unitary(utry.view(), self.size, op.location.clone()); - matrices.push(utry); - } - } else { - let mut param_idx = 0; - for op in &self.ops { - let mut utry = op - .get_utry(¶ms[param_idx..param_idx + op.num_params()], const_gates); - param_idx += op.num_params(); - utry = permute_unitary(utry.view(), self.size, op.location.clone()); - matrices.push(utry); - } - } - - matrices - .iter() - .cloned() - .reduce(|a, b| b.matmul(a.view())) - .unwrap() - } - } - } -} - -impl Gradient for Circuit { - fn get_utry_and_grad( - &self, - params: &[f64], - const_gates: &[Array2], - ) -> (Array2, Array3) { - match &self.backend { - SimulationBackend::Tensor => { - if params.len() != self.num_params() { - panic!( - "Incorrect number of params passed to circuit, expected {}, got {}", - self.num_params(), - params.len() - ); - } - let mut matrices = vec![]; - let mut grads = vec![]; - let mut locations = vec![]; - let mut num_grads = 0usize; - if params.is_empty() { - for op in &self.ops { - let (utry, grad) = op.get_utry_and_grad(&[], const_gates); - num_grads += grad.shape()[0]; - matrices.push(utry); - grads.push(grad); - locations.push(&op.location); - } - } else { - let mut param_idx = 0; - for op in &self.ops { - let (utry, grad) = op.get_utry_and_grad( - ¶ms[param_idx..param_idx + op.num_params()], - const_gates, - ); - num_grads += grad.shape()[0]; - param_idx += op.num_params(); - matrices.push(utry); - grads.push(grad); - locations.push(&op.location); - } - } - - let mut left = UnitaryBuilder::new(self.size, self.radixes.clone()); - let mut right = UnitaryBuilder::new(self.size, self.radixes.clone()); - let mut full_grads = Vec::with_capacity(num_grads); - let mut out_grad = Array3::zeros(( - num_grads, - 2usize.pow(self.size as u32), - 2usize.pow(self.size as u32), - )); - for (m, location) in matrices.iter().zip(locations.iter()) { - right.apply_right(m.view(), location, false); - } - - for (m, location, d_m) in izip!(matrices, locations, grads) { - let perm = calc_permutation_matrix(self.size, (*location).clone()); - let perm_t = perm.t(); - let id = Array2::eye(2usize.pow((self.size - location.len()) as u32)); - - right.apply_left(m.view(), location, true); - let right_utry = right.get_utry(); - let left_utry = left.get_utry(); - for grad in d_m.outer_iter() { - let mut full_grad = grad.kron(&id); - full_grad = perm.matmul(full_grad.view()); - full_grad = full_grad.matmul(perm_t); - let right_grad = right_utry.matmul(full_grad.view()); - full_grads.push(right_grad.matmul(left_utry.view())); - } - left.apply_right(m.view(), location, false); - } - - for (mut arr, grad) in out_grad.outer_iter_mut().zip(full_grads) { - arr.assign(&grad); - } - - (left.get_utry(), out_grad) - } - SimulationBackend::Matrix => { - if params.len() != self.num_params() { - panic!( - "Incorrect number of params passed to circuit, expected {}, got {}", - self.num_params(), - params.len() - ); - } - let mut matrices = vec![]; - let mut grads = vec![]; - let mut locations = vec![]; - let mut num_grads = 0usize; - - if params.is_empty() { - for op in &self.ops { - let (utry, grad) = op.get_utry_and_grad(&[], const_gates); - num_grads += grad.shape()[0]; - matrices.push(permute_unitary(utry.view(), self.size, op.location.clone())); - grads.push(grad); - locations.push(&op.location); - } - } else { - let mut param_idx = 0; - for op in &self.ops { - let (utry, grad) = op.get_utry_and_grad( - ¶ms[param_idx..param_idx + op.num_params()], - const_gates, - ); - num_grads += grad.shape()[0]; - param_idx += op.num_params(); - matrices.push(permute_unitary(utry.view(), self.size, op.location.clone())); - grads.push(grad); - locations.push(&op.location); - } - } - - let dim = 2usize.pow(self.size as u32); - - let mut left = Array2::eye(dim); - let mut right = matrices - .iter() - .cloned() - .reduce(|a, b| b.matmul(a.view())) - .unwrap(); - let mut full_grads = Vec::with_capacity(num_grads); - let mut out_grad = Array3::zeros(( - num_grads, - 2usize.pow(self.size as u32), - 2usize.pow(self.size as u32), - )); - - for (m, location, d_m) in izip!(matrices, locations, grads) { - right = right.matmul(m.conj().t()); - for grad in d_m.outer_iter() { - let full_grad = permute_unitary(grad.view(), self.size, location.clone()); - let tmp = full_grad.matmul(left.view()); - full_grads.push(right.matmul(tmp.view())); - } - left = m.matmul(left.view()); - } - - for (mut arr, grad) in out_grad.outer_iter_mut().zip(full_grads) { - arr.assign(&grad); - } - - (left, out_grad) - } - } - } - - fn get_grad(&self, params: &[f64], const_gates: &[Array2]) -> Array3 { - if params.len() != self.num_params() { - panic!( - "Incorrect number of params passed to circuit, expected {}, got {}", - self.num_params(), - params.len() - ); - } - self.get_utry_and_grad(params, const_gates).1 - } -} diff --git a/src/ir/circuit.rs b/src/ir/circuit.rs new file mode 100644 index 0000000..0c8776f --- /dev/null +++ b/src/ir/circuit.rs @@ -0,0 +1,214 @@ +use crate::qis::unitary::UnitaryBuilder; +use super::gates::{Gate, Gradient, Unitary}; +use super::Operation; + +use itertools::izip; +use ndarray::{Array2, Array3}; +use ndarray_linalg::c64; +use crate::squaremat::*; +use crate::permutation_matrix::calc_permutation_matrix; + +type Cycle = usize; + +#[derive(Clone)] +pub struct Circuit { + pub size: usize, + pub radixes: Vec, + pub ops: Vec, + pub constant_gates: Vec>, + pub cycle_boundaries: Vec<(usize, usize)>, + pub num_params: usize, + pub sendable: bool, +} + +impl Circuit { + pub fn new( + size: usize, + radixes: Vec, + ops_with_cycles: Vec<(Cycle, Operation)>, + constant_gates: Vec>, + ) -> Self { + let mut sendable = true; + let mut num_params = 0; + let mut cycle_boundaries = Vec::new(); + let mut current_cycle = 0usize; + for (cycle, op) in &ops_with_cycles { + num_params += op.gate.num_params(); + if let Gate::Dynamic(_) = op.gate { + sendable = false + } + if *cycle != current_cycle { + cycle_boundaries.push((current_cycle, *cycle)); + current_cycle += 1; + } + } + Circuit { + size, + radixes, + ops: ops_with_cycles.iter().map(|(_, op)| op.clone()).collect(), + constant_gates, + cycle_boundaries, + num_params, + sendable, + } + } + + pub fn is_sendable(&self) -> bool { + self.sendable + } + + pub fn get_params(&self) -> Vec { + let ret = Vec::with_capacity(self.num_params()); + self.ops.iter().fold(ret, |mut ret, op| { + ret.extend_from_slice(&op.params); + ret + }) + } + + pub fn set_params(&mut self, params: &[f64]) { + if params.len() != self.num_params() { + panic!( + "Incorrect number of parameters in set_params, expected {} got {}", + self.num_params(), + params.len() + ); + } + let mut param_idx = 0; + for op in self.ops.iter_mut() { + let parameters = ¶ms[param_idx..param_idx + op.num_params()]; + op.params.copy_from_slice(parameters); + param_idx += op.num_params(); + } + } +} + +impl Unitary for Circuit { + fn num_params(&self) -> usize { + self.num_params + } + + fn get_utry(&self, params: &[f64], const_gates: &[Array2]) -> Array2 { + if !params.is_empty() { + let mut param_idx = 0; + let mut builder = UnitaryBuilder::new(self.size, self.radixes.clone()); + for op in &self.ops { + let utry = op + .get_utry(¶ms[param_idx..param_idx + op.num_params()], const_gates); + param_idx += op.num_params(); + builder.apply_right(utry.view(), &op.location, false); + } + builder.get_utry() + } else { + let mut builder = UnitaryBuilder::new(self.size, self.radixes.clone()); + for op in &self.ops { + let utry = op.get_utry(&[], const_gates); + builder.apply_right(utry.view(), &op.location, false); + } + builder.get_utry() + } + } +} + + +impl Gradient for Circuit { + fn get_utry_and_grad( + &self, + params: &[f64], + const_gates: &[Array2], + ) -> (Array2, Array3) { + if params.len() != self.num_params() { + panic!( + "Incorrect number of params passed to circuit, expected {}, got {}", + self.num_params(), + params.len() + ); + } + let mut matrices = vec![]; + let mut grads = vec![]; + let mut locations = vec![]; + let mut num_grads = 0usize; + if params.is_empty() { + for op in &self.ops { + let (utry, grad) = op.get_utry_and_grad(&[], const_gates); + num_grads += grad.shape()[0]; + matrices.push(utry); + grads.push(grad); + locations.push(&op.location); + } + } else { + let mut param_idx = 0; + for op in &self.ops { + let (utry, grad) = op.get_utry_and_grad( + ¶ms[param_idx..param_idx + op.num_params()], + const_gates, + ); + num_grads += grad.shape()[0]; + param_idx += op.num_params(); + matrices.push(utry); + grads.push(grad); + locations.push(&op.location); + } + } + + let mut left = UnitaryBuilder::new(self.size, self.radixes.clone()); + let mut right = UnitaryBuilder::new(self.size, self.radixes.clone()); + let mut full_grads = Vec::with_capacity(num_grads); + // ///////////////////////////////////////////////////////////// + // let grad_dim: usize = self.radixes.iter().product(); + // let mut out_grad = Array3::zeros(( + // num_grads, + // grad_dim, + // grad_dim, + // )); + + let mut out_grad = Array3::zeros(( + num_grads, + 2usize.pow(self.size as u32), + 2usize.pow(self.size as u32), + )); + // ///////////////////////////////////////////////////////////// + + for (m, location) in matrices.iter().zip(locations.iter()) { + right.apply_right(m.view(), location, false); + } + + for (m, location, d_m) in izip!(matrices, locations, grads) { + // ///////////////////////////////////////////////////////////// + let perm = calc_permutation_matrix(self.size, (*location).clone()); + let perm_t = perm.t(); + let id = Array2::eye(2usize.pow((self.size - location.len()) as u32)); + // ///////////////////////////////////////////////////////////// + + right.apply_left(m.view(), location, true); + let right_utry = right.get_utry(); + let left_utry = left.get_utry(); + for grad in d_m.outer_iter() { + // let left_grad = left.eval_apply_right(grad.view(), location); + // full_grads.push(right_utry.matmul(left_grad.view())); + let mut full_grad = grad.kron(&id); + full_grad = perm.matmul(full_grad.view()); + full_grad = full_grad.matmul(perm_t); + let right_grad = right_utry.matmul(full_grad.view()); + full_grads.push(right_grad.matmul(left_utry.view())); + } + left.apply_right(m.view(), location, false); + } + + for (mut arr, grad) in out_grad.outer_iter_mut().zip(full_grads) { + arr.assign(&grad); + } + + (left.get_utry(), out_grad) + } + + fn get_grad(&self, params: &[f64], const_gates: &[Array2]) -> Array3 { + if params.len() != self.num_params() { + panic!( + "Incorrect number of params passed to circuit, expected {}, got {}", + self.num_params(), + params.len() + ); + } + self.get_utry_and_grad(params, const_gates).1 + } +} \ No newline at end of file diff --git a/src/gates/constant.rs b/src/ir/gates/constant.rs similarity index 72% rename from src/gates/constant.rs rename to src/ir/gates/constant.rs index e643784..5a0f79c 100644 --- a/src/gates/constant.rs +++ b/src/ir/gates/constant.rs @@ -1,6 +1,6 @@ use ndarray::Array2; use ndarray::Array3; -use num_complex::Complex64; +use ndarray_linalg::c64; use super::Gradient; use super::Optimize; @@ -30,21 +30,21 @@ impl Unitary for ConstantGate { 0 } - fn get_utry(&self, _params: &[f64], const_gates: &[Array2]) -> Array2 { + fn get_utry(&self, _params: &[f64], const_gates: &[Array2]) -> Array2 { const_gates[self.index].clone() } } impl Gradient for ConstantGate { - fn get_grad(&self, _params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, _params: &[f64], _const_gates: &[Array2]) -> Array3 { Array3::zeros((0, 0, 0)) } fn get_utry_and_grad( &self, _params: &[f64], - const_gates: &[Array2], - ) -> (Array2, Array3) { + const_gates: &[Array2], + ) -> (Array2, Array3) { (const_gates[self.index].clone(), Array3::zeros((0, 0, 0))) } } diff --git a/src/gates/dynamic.rs b/src/ir/gates/dynamic.rs similarity index 69% rename from src/gates/dynamic.rs rename to src/ir/gates/dynamic.rs index 30a6da3..5c463a7 100644 --- a/src/gates/dynamic.rs +++ b/src/ir/gates/dynamic.rs @@ -1,7 +1,7 @@ use std::fmt; use ndarray::{Array2, Array3, ArrayViewMut2}; -use num_complex::Complex64; +use ndarray_linalg::c64; use super::{Gradient, Optimize, Size, Unitary}; @@ -17,7 +17,7 @@ where (**self).num_params() } - fn get_utry(&self, params: &[f64], const_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], const_gates: &[Array2]) -> Array2 { (**self).get_utry(params, const_gates) } } @@ -26,15 +26,15 @@ impl Gradient for Box where T: DynGate, { - fn get_grad(&self, params: &[f64], const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], const_gates: &[Array2]) -> Array3 { (**self).get_grad(params, const_gates) } fn get_utry_and_grad( &self, params: &[f64], - const_gates: &[Array2], - ) -> (Array2, Array3) { + const_gates: &[Array2], + ) -> (Array2, Array3) { (**self).get_utry_and_grad(params, const_gates) } } @@ -52,7 +52,7 @@ impl Optimize for Box where T: Optimize + DynGate, { - fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { + fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { (**self).optimize(env_matrix) } } diff --git a/src/gates/gradient.rs b/src/ir/gates/gradient.rs similarity index 62% rename from src/gates/gradient.rs rename to src/ir/gates/gradient.rs index 461cb86..3fa7f15 100644 --- a/src/gates/gradient.rs +++ b/src/ir/gates/gradient.rs @@ -1,6 +1,6 @@ use enum_dispatch::enum_dispatch; use ndarray::{Array2, Array3}; -use num_complex::Complex64; +use ndarray_linalg::c64; use super::Unitary; /// Gradient should be implemented for all gates where one can take their gradient. @@ -10,9 +10,9 @@ pub trait Gradient: Unitary { fn get_utry_and_grad( &self, params: &[f64], - const_gates: &[Array2], - ) -> (Array2, Array3); + const_gates: &[Array2], + ) -> (Array2, Array3); /// Get the gradient of `self`. - fn get_grad(&self, params: &[f64], const_gates: &[Array2]) -> Array3; + fn get_grad(&self, params: &[f64], const_gates: &[Array2]) -> Array3; } diff --git a/src/gates/mod.rs b/src/ir/gates/mod.rs similarity index 94% rename from src/gates/mod.rs rename to src/ir/gates/mod.rs index 86af5ef..0a96d78 100644 --- a/src/gates/mod.rs +++ b/src/ir/gates/mod.rs @@ -18,7 +18,7 @@ pub use self::size::Size; pub use self::unitary::Unitary; use ndarray::{Array2, Array3, ArrayViewMut2}; -use num_complex::Complex64; +use ndarray_linalg::c64; use derive_more::From; @@ -64,7 +64,7 @@ impl Unitary for Gate { } } - fn get_utry(&self, params: &[f64], const_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], const_gates: &[Array2]) -> Array2 { match self { Gate::Constant(c) => c.get_utry(params, const_gates), Gate::U1(u) => u.get_utry(params, const_gates), @@ -87,7 +87,7 @@ impl Unitary for Gate { } impl Gradient for Gate { - fn get_grad(&self, params: &[f64], const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], const_gates: &[Array2]) -> Array3 { match self { Gate::Constant(c) => c.get_grad(params, const_gates), Gate::U1(u) => u.get_grad(params, const_gates), @@ -111,8 +111,8 @@ impl Gradient for Gate { fn get_utry_and_grad( &self, params: &[f64], - const_gates: &[Array2], - ) -> (Array2, Array3) { + const_gates: &[Array2], + ) -> (Array2, Array3) { match self { Gate::Constant(c) => c.get_utry_and_grad(params, const_gates), Gate::U1(u) => u.get_utry_and_grad(params, const_gates), @@ -158,7 +158,7 @@ impl Size for Gate { } impl Optimize for Gate { - fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { + fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { match self { Gate::Constant(_) => todo!(), Gate::U1(u) => u.optimize(env_matrix), diff --git a/src/gates/optimize.rs b/src/ir/gates/optimize.rs similarity index 56% rename from src/gates/optimize.rs rename to src/ir/gates/optimize.rs index bca7f08..69d578b 100644 --- a/src/gates/optimize.rs +++ b/src/ir/gates/optimize.rs @@ -1,10 +1,10 @@ use enum_dispatch::enum_dispatch; use ndarray::ArrayViewMut2; -use num_complex::Complex64; +use ndarray_linalg::c64; #[enum_dispatch] pub trait Optimize { - fn optimize(&self, _env_matrix: ArrayViewMut2) -> Vec { + fn optimize(&self, _env_matrix: ArrayViewMut2) -> Vec { unimplemented!() } } diff --git a/src/gates/parameterized/crx.rs b/src/ir/gates/parameterized/crx.rs similarity index 84% rename from src/gates/parameterized/crx.rs rename to src/ir/gates/parameterized/crx.rs index ca5a289..1825e7d 100644 --- a/src/gates/parameterized/crx.rs +++ b/src/ir/gates/parameterized/crx.rs @@ -1,9 +1,9 @@ -use crate::gates::{Gradient, Size}; -use crate::gates::{Optimize, Unitary}; +use crate::ir::gates::{Gradient, Size}; +use crate::ir::gates::{Optimize, Unitary}; use crate::{i, r}; use ndarray::{Array2, Array3, ArrayViewMut2}; -use num_complex::Complex64; +use ndarray_linalg::c64; /// A gate representing a controlled X rotation. #[derive(Copy, Clone, Debug, PartialEq, Default)] @@ -20,7 +20,7 @@ impl Unitary for CRXGate { 1 } - fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { let cos = r!((params[0] / 2.).cos()); let sin = i!(-(params[0] / 2.).sin()); let zero = r!(0.0); @@ -37,7 +37,7 @@ impl Unitary for CRXGate { } impl Gradient for CRXGate { - fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { let dcos = -1. * r!(params[0] / 2.).sin() / 2.; let dsin = i!(-(params[0] / 2.).cos() / 2.); let zero = r!(0.0); @@ -54,8 +54,8 @@ impl Gradient for CRXGate { fn get_utry_and_grad( &self, params: &[f64], - _const_gates: &[Array2], - ) -> (Array2, Array3) { + _const_gates: &[Array2], + ) -> (Array2, Array3) { let cos = r!((params[0] / 2.).cos()); let sin = i!(-(params[0] / 2.).sin()); let dcos = -1. * r!(params[0] / 2.).sin() / 2.; @@ -90,7 +90,7 @@ impl Size for CRXGate { } impl Optimize for CRXGate { - fn optimize(&self, _env_matrix: ArrayViewMut2) -> Vec { + fn optimize(&self, _env_matrix: ArrayViewMut2) -> Vec { unimplemented!() } } diff --git a/src/gates/parameterized/cry.rs b/src/ir/gates/parameterized/cry.rs similarity index 84% rename from src/gates/parameterized/cry.rs rename to src/ir/gates/parameterized/cry.rs index fe936b4..6a782a3 100644 --- a/src/gates/parameterized/cry.rs +++ b/src/ir/gates/parameterized/cry.rs @@ -1,9 +1,9 @@ -use crate::gates::{Gradient, Size}; -use crate::gates::{Optimize, Unitary}; +use crate::ir::gates::{Gradient, Size}; +use crate::ir::gates::{Optimize, Unitary}; use crate::{i, r}; use ndarray::{Array2, Array3, ArrayViewMut2}; -use num_complex::Complex64; +use ndarray_linalg::c64; /// A gate representing a controlled Y rotation #[derive(Copy, Clone, Debug, PartialEq, Default)] @@ -20,7 +20,7 @@ impl Unitary for CRYGate { 1 } - fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { let cos = r!((params[0] / 2.).cos()); let sin = r!((params[0] / 2.).sin()); let zero = r!(0.0); @@ -37,7 +37,7 @@ impl Unitary for CRYGate { } impl Gradient for CRYGate { - fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { let dcos = -1. * r!(params[0] / 2.).sin() / 2.; let dsin = i!(-1.) * r!((params[0] / 2.).cos() / 2.); let zero = r!(0.0); @@ -54,8 +54,8 @@ impl Gradient for CRYGate { fn get_utry_and_grad( &self, params: &[f64], - _const_gates: &[Array2], - ) -> (Array2, Array3) { + _const_gates: &[Array2], + ) -> (Array2, Array3) { let cos = r!((params[0] / 2.).cos()); let sin = r!((params[0] / 2.).sin()); let dcos = -1. * r!(params[0] / 2.).sin() / 2.; @@ -91,7 +91,7 @@ impl Size for CRYGate { } impl Optimize for CRYGate { - fn optimize(&self, _env_matrix: ArrayViewMut2) -> Vec { + fn optimize(&self, _env_matrix: ArrayViewMut2) -> Vec { unimplemented!() } } diff --git a/src/gates/parameterized/crz.rs b/src/ir/gates/parameterized/crz.rs similarity index 85% rename from src/gates/parameterized/crz.rs rename to src/ir/gates/parameterized/crz.rs index 092a7b6..a9026c8 100644 --- a/src/gates/parameterized/crz.rs +++ b/src/ir/gates/parameterized/crz.rs @@ -1,9 +1,9 @@ -use crate::gates::{Gradient, Size}; -use crate::gates::{Optimize, Unitary}; +use crate::ir::gates::{Gradient, Size}; +use crate::ir::gates::{Optimize, Unitary}; use crate::{i, r}; use ndarray::{Array2, Array3, ArrayViewMut2}; -use num_complex::Complex64; +use ndarray_linalg::c64; /// A gate representing an arbitrary rotation around the ZZ axis #[derive(Copy, Clone, Debug, PartialEq, Default)] @@ -20,7 +20,7 @@ impl Unitary for CRZGate { 1 } - fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { let pos = (i!(1.) * params[0] / 2.).exp(); let neg = (i!(-1.) * params[0] / 2.).exp(); let zero = r!(0.0); @@ -37,7 +37,7 @@ impl Unitary for CRZGate { } impl Gradient for CRZGate { - fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { let zero = r!(0.0); let dpos = i!(1. / 2.) * (i!(1.) * params[0] / 2.).exp(); let dneg = i!(-1. / 2.) * (i!(-1.) * params[0] / 2.).exp(); @@ -54,8 +54,8 @@ impl Gradient for CRZGate { fn get_utry_and_grad( &self, params: &[f64], - _const_gates: &[Array2], - ) -> (Array2, Array3) { + _const_gates: &[Array2], + ) -> (Array2, Array3) { let pos = (i!(1.) * params[0] / 2.).exp(); let neg = (i!(-1.) * params[0] / 2.).exp(); let zero = r!(0.0); @@ -91,7 +91,7 @@ impl Size for CRZGate { } impl Optimize for CRZGate { - fn optimize(&self, _env_matrix: ArrayViewMut2) -> Vec { + fn optimize(&self, _env_matrix: ArrayViewMut2) -> Vec { unimplemented!() } } diff --git a/src/gates/parameterized/mod.rs b/src/ir/gates/parameterized/mod.rs similarity index 100% rename from src/gates/parameterized/mod.rs rename to src/ir/gates/parameterized/mod.rs diff --git a/src/gates/parameterized/rx.rs b/src/ir/gates/parameterized/rx.rs similarity index 68% rename from src/gates/parameterized/rx.rs rename to src/ir/gates/parameterized/rx.rs index 071e16a..0bc8234 100644 --- a/src/gates/parameterized/rx.rs +++ b/src/ir/gates/parameterized/rx.rs @@ -1,9 +1,9 @@ -use crate::gates::utils::{rot_x, rot_x_jac}; -use crate::gates::{Gradient, Size}; -use crate::gates::{Optimize, Unitary}; +use crate::ir::gates::utils::{rot_x, rot_x_jac}; +use crate::ir::gates::{Gradient, Size}; +use crate::ir::gates::{Optimize, Unitary}; use ndarray::{Array2, Array3, ArrayViewMut2}; -use num_complex::Complex64; +use ndarray_linalg::c64; /// Arbitrary X rotation single qubit gate #[derive(Copy, Clone, Debug, PartialEq, Default)] @@ -20,21 +20,21 @@ impl Unitary for RXGate { 1 } - fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { rot_x(params[0]) } } impl Gradient for RXGate { - fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { rot_x_jac(params[0]) } fn get_utry_and_grad( &self, params: &[f64], - _const_gates: &[Array2], - ) -> (Array2, Array3) { + _const_gates: &[Array2], + ) -> (Array2, Array3) { (rot_x(params[0]), rot_x_jac(params[0])) } } @@ -46,7 +46,7 @@ impl Size for RXGate { } impl Optimize for RXGate { - fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { + fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { let real = (env_matrix[[0, 0]] + env_matrix[[1, 1]]).re; let imag = (env_matrix[[0, 1]] + env_matrix[[1, 0]]).im; let mut theta = 2.0 * (real / (real.powi(2) + imag.powi(2)).sqrt()).acos(); diff --git a/src/gates/parameterized/rxx.rs b/src/ir/gates/parameterized/rxx.rs similarity index 86% rename from src/gates/parameterized/rxx.rs rename to src/ir/gates/parameterized/rxx.rs index 56dd808..04e5b35 100644 --- a/src/gates/parameterized/rxx.rs +++ b/src/ir/gates/parameterized/rxx.rs @@ -1,9 +1,9 @@ -use crate::gates::{Gradient, Size}; -use crate::gates::{Optimize, Unitary}; +use crate::ir::gates::{Gradient, Size}; +use crate::ir::gates::{Optimize, Unitary}; use crate::{i, r}; use ndarray::{Array2, Array3, ArrayViewMut2}; -use num_complex::Complex64; +use ndarray_linalg::c64; /// A gate representing an arbitrary rotation around the XX axis #[derive(Copy, Clone, Debug, PartialEq, Default)] @@ -20,7 +20,7 @@ impl Unitary for RXXGate { 1 } - fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { let cos = r!((params[0] / 2.).cos()); let sin = i!(-(params[0] / 2.).sin()); let zero = r!(0.0); @@ -36,7 +36,7 @@ impl Unitary for RXXGate { } impl Gradient for RXXGate { - fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { let dcos = -1. * r!(params[0] / 2.).sin() / 2.; let dsin = i!(-(params[0] / 2.).cos() / 2.); let zero = r!(0.0); @@ -53,8 +53,8 @@ impl Gradient for RXXGate { fn get_utry_and_grad( &self, params: &[f64], - _const_gates: &[Array2], - ) -> (Array2, Array3) { + _const_gates: &[Array2], + ) -> (Array2, Array3) { let cos = r!((params[0] / 2.).cos()); let sin = i!(-(params[0] / 2.).sin()); let dcos = -1. * r!(params[0] / 2.).sin() / 2.; @@ -88,7 +88,7 @@ impl Size for RXXGate { } impl Optimize for RXXGate { - fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { + fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { let re = env_matrix.diag().sum().re; let im = (env_matrix[[0, 3]] + env_matrix[[1, 2]] + env_matrix[[2, 1]] + env_matrix[[3, 0]]).im; diff --git a/src/gates/parameterized/ry.rs b/src/ir/gates/parameterized/ry.rs similarity index 68% rename from src/gates/parameterized/ry.rs rename to src/ir/gates/parameterized/ry.rs index 2020ba6..203b6bc 100644 --- a/src/gates/parameterized/ry.rs +++ b/src/ir/gates/parameterized/ry.rs @@ -1,9 +1,9 @@ -use crate::gates::utils::{rot_y, rot_y_jac}; -use crate::gates::{Gradient, Size}; -use crate::gates::{Optimize, Unitary}; +use crate::ir::gates::utils::{rot_y, rot_y_jac}; +use crate::ir::gates::{Gradient, Size}; +use crate::ir::gates::{Optimize, Unitary}; use ndarray::{Array2, Array3, ArrayViewMut2}; -use num_complex::Complex64; +use ndarray_linalg::c64; /// Arbitrary Y rotation single qubit gate #[derive(Copy, Clone, Debug, PartialEq, Default)] @@ -20,21 +20,21 @@ impl Unitary for RYGate { 1 } - fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { rot_y(params[0]) } } impl Gradient for RYGate { - fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { rot_y_jac(params[0]) } fn get_utry_and_grad( &self, params: &[f64], - _const_gates: &[Array2], - ) -> (Array2, Array3) { + _const_gates: &[Array2], + ) -> (Array2, Array3) { (rot_y(params[0]), rot_y_jac(params[0])) } } @@ -46,7 +46,7 @@ impl Size for RYGate { } impl Optimize for RYGate { - fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { + fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { let real = (env_matrix[[0, 0]] + env_matrix[[1, 1]]).re; let imag = (env_matrix[[1, 0]] - env_matrix[[0, 1]]).im; let mut theta = 2.0 * (real / (real.powi(2) + imag.powi(2)).sqrt()).acos(); diff --git a/src/gates/parameterized/ryy.rs b/src/ir/gates/parameterized/ryy.rs similarity index 85% rename from src/gates/parameterized/ryy.rs rename to src/ir/gates/parameterized/ryy.rs index 74a62fb..78feb36 100644 --- a/src/gates/parameterized/ryy.rs +++ b/src/ir/gates/parameterized/ryy.rs @@ -1,9 +1,9 @@ -use crate::gates::{Gradient, Size}; -use crate::gates::{Optimize, Unitary}; +use crate::ir::gates::{Gradient, Size}; +use crate::ir::gates::{Optimize, Unitary}; use crate::{i, r}; use ndarray::{Array2, Array3, ArrayViewMut2}; -use num_complex::Complex64; +use ndarray_linalg::c64; /// A gate representing an arbitrary rotation around the YY axis #[derive(Copy, Clone, Debug, PartialEq, Default)] @@ -20,7 +20,7 @@ impl Unitary for RYYGate { 1 } - fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { let cos = r!((params[0] / 2.).cos()); let nsin = i!(-1.0) * (params[0] / 2.).sin(); let psin = i!(1.0) * (params[0] / 2.).sin(); @@ -37,7 +37,7 @@ impl Unitary for RYYGate { } impl Gradient for RYYGate { - fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { let dcos = -1. * r!(params[0] / 2.).sin() / 2.; let dnsin = i!(-1.0) * (params[0] / 2.).cos() / 2.; let dpsin = i!(1.0) * (params[0] / 2.).cos() / 2.; @@ -55,8 +55,8 @@ impl Gradient for RYYGate { fn get_utry_and_grad( &self, params: &[f64], - _const_gates: &[Array2], - ) -> (Array2, Array3) { + _const_gates: &[Array2], + ) -> (Array2, Array3) { let cos = r!((params[0] / 2.).cos()); let nsin = i!(-1.0) * (params[0] / 2.).sin(); let psin = i!(1.0) * (params[0] / 2.).sin(); @@ -93,7 +93,7 @@ impl Size for RYYGate { } impl Optimize for RYYGate { - fn optimize(&self, _env_matrix: ArrayViewMut2) -> Vec { + fn optimize(&self, _env_matrix: ArrayViewMut2) -> Vec { unimplemented!() } } diff --git a/src/gates/parameterized/rz.rs b/src/ir/gates/parameterized/rz.rs similarity index 70% rename from src/gates/parameterized/rz.rs rename to src/ir/gates/parameterized/rz.rs index 343ff32..f4d9ac4 100644 --- a/src/gates/parameterized/rz.rs +++ b/src/ir/gates/parameterized/rz.rs @@ -1,11 +1,11 @@ use std::f64::consts::PI; -use crate::gates::utils::{rot_z, rot_z_jac}; -use crate::gates::{Gradient, Size}; -use crate::gates::{Optimize, Unitary}; +use crate::ir::gates::utils::{rot_z, rot_z_jac}; +use crate::ir::gates::{Gradient, Size}; +use crate::ir::gates::{Optimize, Unitary}; use ndarray::{Array2, Array3, ArrayViewMut2}; -use num_complex::Complex64; +use ndarray_linalg::c64; /// Arbitrary Y rotation single qubit gate #[derive(Copy, Clone, Debug, PartialEq, Default)] @@ -22,21 +22,21 @@ impl Unitary for RZGate { 1 } - fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { rot_z(params[0], None) } } impl Gradient for RZGate { - fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { rot_z_jac(params[0], None) } fn get_utry_and_grad( &self, params: &[f64], - _const_gates: &[Array2], - ) -> (Array2, Array3) { + _const_gates: &[Array2], + ) -> (Array2, Array3) { (rot_z(params[0], None), rot_z_jac(params[0], None)) } } @@ -48,7 +48,7 @@ impl Size for RZGate { } impl Optimize for RZGate { - fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { + fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { let real = env_matrix[[1, 1]].re; let imag = env_matrix[[1, 1]].im; let mut theta = (imag / real).atan(); diff --git a/src/gates/parameterized/rzz.rs b/src/ir/gates/parameterized/rzz.rs similarity index 84% rename from src/gates/parameterized/rzz.rs rename to src/ir/gates/parameterized/rzz.rs index 335b3cd..de23fb3 100644 --- a/src/gates/parameterized/rzz.rs +++ b/src/ir/gates/parameterized/rzz.rs @@ -1,9 +1,9 @@ -use crate::gates::{Gradient, Size}; -use crate::gates::{Optimize, Unitary}; +use crate::ir::gates::{Gradient, Size}; +use crate::ir::gates::{Optimize, Unitary}; use crate::{i, r}; use ndarray::{Array2, Array3, ArrayViewMut2}; -use num_complex::Complex64; +use ndarray_linalg::c64; /// A gate representing an arbitrary rotation around the ZZ axis #[derive(Copy, Clone, Debug, PartialEq, Default)] @@ -20,7 +20,7 @@ impl Unitary for RZZGate { 1 } - fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { let pos = (i!(1.) * params[0] / 2.).exp(); let neg = (i!(-1.) * params[0] / 2.).exp(); let zero = r!(0.0); @@ -36,7 +36,7 @@ impl Unitary for RZZGate { } impl Gradient for RZZGate { - fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { let dpos = i!(1. / 2.) * (i!(1.) * params[0] / 2.).exp(); let dneg = i!(-1. / 2.) * (i!(-1.) * params[0] / 2.).exp(); let zero = r!(0.0); @@ -53,8 +53,8 @@ impl Gradient for RZZGate { fn get_utry_and_grad( &self, params: &[f64], - _const_gates: &[Array2], - ) -> (Array2, Array3) { + _const_gates: &[Array2], + ) -> (Array2, Array3) { let pos = (i!(1.) * params[0] / 2.).exp(); let neg = (i!(-1.) * params[0] / 2.).exp(); let zero = r!(0.0); @@ -89,7 +89,7 @@ impl Size for RZZGate { } impl Optimize for RZZGate { - fn optimize(&self, _env_matrix: ArrayViewMut2) -> Vec { + fn optimize(&self, _env_matrix: ArrayViewMut2) -> Vec { unimplemented!() } } diff --git a/src/gates/parameterized/u1.rs b/src/ir/gates/parameterized/u1.rs similarity index 76% rename from src/gates/parameterized/u1.rs rename to src/ir/gates/parameterized/u1.rs index 96fa393..1c61bcd 100644 --- a/src/gates/parameterized/u1.rs +++ b/src/ir/gates/parameterized/u1.rs @@ -1,12 +1,12 @@ use std::f64::consts::PI; -use crate::gates::utils::{rot_z, rot_z_jac}; -use crate::gates::{Gradient, Size}; -use crate::gates::{Optimize, Unitary}; +use crate::ir::gates::utils::{rot_z, rot_z_jac}; +use crate::ir::gates::{Gradient, Size}; +use crate::ir::gates::{Optimize, Unitary}; use crate::i; use ndarray::{Array2, Array3, ArrayViewMut2}; -use num_complex::Complex64; +use ndarray_linalg::c64; /// IBM's U1 single qubit gate #[derive(Copy, Clone, Debug, PartialEq, Default)] @@ -23,14 +23,14 @@ impl Unitary for U1Gate { 1 } - fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { let phase = (i!(1.0) * params[0] / 2.0).exp(); rot_z(params[0], Some(phase)) } } impl Gradient for U1Gate { - fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { let phase = (i!(1.0) * params[0] / 2.0).exp(); let dphase = i!(1.0) / 2.0 * phase; rot_z(params[0], Some(dphase)) + rot_z_jac(params[0], Some(phase)) @@ -39,8 +39,8 @@ impl Gradient for U1Gate { fn get_utry_and_grad( &self, params: &[f64], - _const_gates: &[Array2], - ) -> (Array2, Array3) { + _const_gates: &[Array2], + ) -> (Array2, Array3) { let phase = (i!(1.0) * params[0] / 2.0).exp(); let dphase = i!(1.0) / 2.0 * phase; ( @@ -57,7 +57,7 @@ impl Size for U1Gate { } impl Optimize for U1Gate { - fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { + fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { let real = env_matrix[[1, 1]].re; let imag = env_matrix[[1, 1]].im; let mut theta = (imag / real).atan(); diff --git a/src/gates/parameterized/u2.rs b/src/ir/gates/parameterized/u2.rs similarity index 86% rename from src/gates/parameterized/u2.rs rename to src/ir/gates/parameterized/u2.rs index fd880e0..e038b2b 100644 --- a/src/gates/parameterized/u2.rs +++ b/src/ir/gates/parameterized/u2.rs @@ -1,12 +1,12 @@ -use crate::gates::Gradient; -use crate::gates::Optimize; -use crate::gates::Size; -use crate::gates::Unitary; +use crate::ir::gates::Gradient; +use crate::ir::gates::Optimize; +use crate::ir::gates::Size; +use crate::ir::gates::Unitary; use crate::{i, r}; use ndarray::Array2; use ndarray::Array3; -use num_complex::Complex64; +use ndarray_linalg::c64; /// IBM's U2 single qubit gate #[derive(Copy, Clone, Debug, PartialEq, Default)] @@ -23,7 +23,7 @@ impl Unitary for U2Gate { 2 } - fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { let phase = r!(1.0) / r!(2.0f64.sqrt()); let e1 = (i!(1.0) * params[1]).exp(); let e2 = (i!(1.0) * params[0]).exp(); @@ -33,7 +33,7 @@ impl Unitary for U2Gate { } impl Gradient for U2Gate { - fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { let phase = r!(1.0) / r!(2.0f64.sqrt()); let e1 = (i!(1.0) * params[1]).exp(); let e2 = (i!(1.0) * params[0]).exp(); @@ -59,8 +59,8 @@ impl Gradient for U2Gate { fn get_utry_and_grad( &self, params: &[f64], - _const_gates: &[Array2], - ) -> (Array2, Array3) { + _const_gates: &[Array2], + ) -> (Array2, Array3) { let phase = r!(1.0) / r!(2.0f64.sqrt()); let e1 = (i!(1.0) * params[1]).exp(); let e2 = (i!(1.0) * params[0]).exp(); diff --git a/src/gates/parameterized/u3.rs b/src/ir/gates/parameterized/u3.rs similarity index 91% rename from src/gates/parameterized/u3.rs rename to src/ir/gates/parameterized/u3.rs index 4be1e4c..b368765 100644 --- a/src/gates/parameterized/u3.rs +++ b/src/ir/gates/parameterized/u3.rs @@ -1,12 +1,12 @@ -use crate::gates::Gradient; -use crate::gates::Optimize; -use crate::gates::Size; -use crate::gates::Unitary; +use crate::ir::gates::Gradient; +use crate::ir::gates::Optimize; +use crate::ir::gates::Size; +use crate::ir::gates::Unitary; use crate::{i, r}; use ndarray::Array2; use ndarray::Array3; -use num_complex::Complex64; +use ndarray_linalg::c64; /// IBM's U3 single qubit gate #[derive(Copy, Clone, Debug, PartialEq, Default)] @@ -23,7 +23,7 @@ impl Unitary for U3Gate { 3 } - fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { let ct = r!((params[0] / 2.0).cos()); let st = r!((params[0] / 2.0).sin()); let cp = (params[1]).cos(); @@ -44,7 +44,7 @@ impl Unitary for U3Gate { } impl Gradient for U3Gate { - fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { let ct = r!((params[0] / 2.0).cos()); let st = r!((params[0] / 2.0).sin()); let cp = (params[1]).cos(); @@ -77,8 +77,8 @@ impl Gradient for U3Gate { fn get_utry_and_grad( &self, params: &[f64], - _const_gates: &[Array2], - ) -> (Array2, Array3) { + _const_gates: &[Array2], + ) -> (Array2, Array3) { let ct = r!((params[0] / 2.0).cos()); let st = r!((params[0] / 2.0).sin()); let cp = (params[1]).cos(); diff --git a/src/gates/parameterized/u8.rs b/src/ir/gates/parameterized/u8.rs similarity index 96% rename from src/gates/parameterized/u8.rs rename to src/ir/gates/parameterized/u8.rs index ca5304d..0daf3e3 100644 --- a/src/gates/parameterized/u8.rs +++ b/src/ir/gates/parameterized/u8.rs @@ -1,12 +1,12 @@ -use crate::gates::Gradient; -use crate::gates::Optimize; -use crate::gates::Size; -use crate::gates::Unitary; +use crate::ir::gates::Gradient; +use crate::ir::gates::Optimize; +use crate::ir::gates::Size; +use crate::ir::gates::Unitary; use crate::{i, r}; use ndarray::Array2; use ndarray::Array3; -use num_complex::Complex64; +use ndarray_linalg::c64; /// IBM's U3 single qubit gate #[derive(Copy, Clone, Debug, PartialEq, Default)] @@ -23,7 +23,7 @@ impl Unitary for U8Gate { 8 } - fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { let s1 = (params[0]).sin(); let c1 = (params[0]).cos(); let s2 = (params[1]).sin(); @@ -61,7 +61,7 @@ impl Unitary for U8Gate { } impl Gradient for U8Gate { - fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { let s1 = (params[0]).sin(); let c1 = (params[0]).cos(); let s2 = (params[1]).sin(); @@ -80,7 +80,7 @@ impl Gradient for U8Gate { let p5 = (i!(1.0) * params[7]).exp(); let m5 = (i!(-1.0) * params[7]).exp(); Array3::from_shape_vec( - (3, 3, 8), + (8, 3, 3), vec![ // param 0 -s1 * c2 * p1, @@ -170,8 +170,8 @@ impl Gradient for U8Gate { fn get_utry_and_grad( &self, params: &[f64], - _const_gates: &[Array2], - ) -> (Array2, Array3) { + _const_gates: &[Array2], + ) -> (Array2, Array3) { let s1 = (params[0]).sin(); let c1 = (params[0]).cos(); let s2 = (params[1]).sin(); @@ -207,7 +207,7 @@ impl Gradient for U8Gate { ) .unwrap(), Array3::from_shape_vec( - (3, 3, 8), + (8, 3, 3), vec![ // param 0 -s1 * c2 * p1, diff --git a/src/gates/parameterized/variable.rs b/src/ir/gates/parameterized/variable.rs similarity index 66% rename from src/gates/parameterized/variable.rs rename to src/ir/gates/parameterized/variable.rs index 0f4f62b..37de734 100644 --- a/src/gates/parameterized/variable.rs +++ b/src/ir/gates/parameterized/variable.rs @@ -1,27 +1,28 @@ -use crate::gates::{Gradient, Size}; -use crate::gates::{Optimize, Unitary}; +use crate::ir::gates::{Gradient, Size}; +use crate::ir::gates::{Optimize, Unitary}; use ndarray::{Array2, Array3, ArrayViewMut2}; -use num_complex::Complex64; +use ndarray_linalg::c64; +use crate::squaremat::*; -use lax::{layout::MatrixLayout, UVTFlag, SVDDC_}; +// use lax::{layout::MatrixLayout, UVTFlag, SVDDC_}; +use ndarray_linalg::SVD; -use squaremat::Matmul; - -fn svd(mut matrix: ArrayViewMut2) -> (Array2, Array2) { - let size = matrix.shape()[0]; - let layout = MatrixLayout::C { - row: size as i32, - lda: size as i32, - }; - let result = SVDDC_::svddc(layout, UVTFlag::Full, matrix.as_slice_mut().unwrap()).unwrap(); +fn svd(matrix: ArrayViewMut2) -> (Array2, Array2) { + // let size = matrix.shape()[0]; + // let layout = MatrixLayout::C { + // row: size as i32, + // lda: size as i32, + // }; + let result = matrix.svd(true, true).unwrap(); // Safety: u/vt are the same size since matrix is a square matrix with sides of size `size` - unsafe { - ( - Array2::from_shape_vec_unchecked((size, size), result.u.unwrap()), - Array2::from_shape_vec_unchecked((size, size), result.vt.unwrap()), - ) - } + (result.0.unwrap(), result.2.unwrap()) + // unsafe { + // ( + // Array2::from_shape_vec_unchecked((size, size), result.U.unwrap()), + // Array2::from_shape_vec_unchecked((size, size), result.VT.unwrap()), + // ) + // } } /// A variable n-qudit unitary gate @@ -52,14 +53,14 @@ impl Unitary for VariableUnitaryGate { self.num_parameters } - fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], _constant_gates: &[Array2]) -> Array2 { assert_eq!(self.num_params(), params.len()); let mid = params.len() / 2; let (re, im) = params.split_at(mid); - let vec: Vec = re + let vec: Vec = re .iter() .zip(im) - .map(|(re, im)| Complex64::new(*re, *im)) + .map(|(re, im)| c64::new(*re, *im)) .collect(); let len = vec.len(); let mut matrix = Array2::from_shape_vec((self.dim, self.dim), vec) @@ -70,15 +71,15 @@ impl Unitary for VariableUnitaryGate { } impl Gradient for VariableUnitaryGate { - fn get_grad(&self, _params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, _params: &[f64], _const_gates: &[Array2]) -> Array3 { unimplemented!() } fn get_utry_and_grad( &self, _params: &[f64], - _const_gates: &[Array2], - ) -> (Array2, Array3) { + _const_gates: &[Array2], + ) -> (Array2, Array3) { unimplemented!() } } @@ -90,7 +91,7 @@ impl Size for VariableUnitaryGate { } impl Optimize for VariableUnitaryGate { - fn optimize(&self, mut env_matrix: ArrayViewMut2) -> Vec { + fn optimize(&self, mut env_matrix: ArrayViewMut2) -> Vec { let (mut u, mut vt) = svd(env_matrix.view_mut()); u.map_inplace(|i| *i = i.conj()); vt.map_inplace(|i| *i = i.conj()); diff --git a/src/gates/size.rs b/src/ir/gates/size.rs similarity index 100% rename from src/gates/size.rs rename to src/ir/gates/size.rs diff --git a/src/gates/unitary.rs b/src/ir/gates/unitary.rs similarity index 59% rename from src/gates/unitary.rs rename to src/ir/gates/unitary.rs index 8593f9a..5eb907a 100644 --- a/src/gates/unitary.rs +++ b/src/ir/gates/unitary.rs @@ -1,10 +1,10 @@ use enum_dispatch::enum_dispatch; use ndarray::Array2; -use num_complex::Complex64; +use ndarray_linalg::c64; /// Trait to calculate the unitary for a given gate. #[enum_dispatch] pub trait Unitary { fn num_params(&self) -> usize; - fn get_utry(&self, params: &[f64], const_gates: &[Array2]) -> Array2; + fn get_utry(&self, params: &[f64], const_gates: &[Array2]) -> Array2; } diff --git a/src/gates/utils.rs b/src/ir/gates/utils.rs similarity index 68% rename from src/gates/utils.rs rename to src/ir/gates/utils.rs index 6d6b931..b85feb4 100644 --- a/src/gates/utils.rs +++ b/src/ir/gates/utils.rs @@ -1,10 +1,10 @@ use ndarray::{Array2, Array3}; -use num_complex::Complex64; +use ndarray_linalg::c64; #[inline(always)] -pub fn rot_x(theta: f64) -> Array2 { - let half_theta = Complex64::new(theta / 2.0, 0.0); - let negi = Complex64::new(0.0, -1.0); +pub fn rot_x(theta: f64) -> Array2 { + let half_theta = c64::new(theta / 2.0, 0.0); + let negi = c64::new(0.0, -1.0); unsafe { Array2::from_shape_vec_unchecked( (2, 2), @@ -19,11 +19,11 @@ pub fn rot_x(theta: f64) -> Array2 { } #[inline(always)] -pub fn rot_x_jac(theta: f64) -> Array3 { - let half_theta = Complex64::new(theta / 2.0, 0.0); - let negi = Complex64::new(0.0, -1.0); - let half = Complex64::new(0.5, 0.0); - let neghalf = Complex64::new(-0.5, 0.0); +pub fn rot_x_jac(theta: f64) -> Array3 { + let half_theta = c64::new(theta / 2.0, 0.0); + let negi = c64::new(0.0, -1.0); + let half = c64::new(0.5, 0.0); + let neghalf = c64::new(-0.5, 0.0); unsafe { Array3::from_shape_vec_unchecked( (1, 2, 2), @@ -38,8 +38,8 @@ pub fn rot_x_jac(theta: f64) -> Array3 { } #[inline(always)] -pub fn rot_y(theta: f64) -> Array2 { - let half_theta = Complex64::new(theta / 2.0, 0.0); +pub fn rot_y(theta: f64) -> Array2 { + let half_theta = c64::new(theta / 2.0, 0.0); unsafe { Array2::from_shape_vec_unchecked( (2, 2), @@ -54,10 +54,10 @@ pub fn rot_y(theta: f64) -> Array2 { } #[inline(always)] -pub fn rot_y_jac(theta: f64) -> Array3 { - let half_theta = Complex64::new(theta / 2.0, 0.0); - let neghalf = Complex64::new(-0.5, 0.0); - let half = Complex64::new(0.5, 0.0); +pub fn rot_y_jac(theta: f64) -> Array3 { + let half_theta = c64::new(theta / 2.0, 0.0); + let neghalf = c64::new(-0.5, 0.0); + let half = c64::new(0.5, 0.0); unsafe { Array3::from_shape_vec_unchecked( (1, 2, 2), @@ -72,11 +72,11 @@ pub fn rot_y_jac(theta: f64) -> Array3 { } #[inline(always)] -pub fn rot_z(theta: f64, phase: Option) -> Array2 { - let half_theta = Complex64::new(theta / 2.0, 0.0); - let negi = Complex64::new(0.0, -1.0); - let posi = Complex64::new(0.0, 1.0); - let zero = Complex64::new(0.0, 0.0); +pub fn rot_z(theta: f64, phase: Option) -> Array2 { + let half_theta = c64::new(theta / 2.0, 0.0); + let negi = c64::new(0.0, -1.0); + let posi = c64::new(0.0, 1.0); + let zero = c64::new(0.0, 0.0); if let Some(phase) = phase { unsafe { Array2::from_shape_vec_unchecked( @@ -105,12 +105,12 @@ pub fn rot_z(theta: f64, phase: Option) -> Array2 { } #[inline(always)] -pub fn rot_z_jac(theta: f64, phase: Option) -> Array3 { - let half_theta = Complex64::new(theta / 2.0, 0.0); - let negi = Complex64::new(0.0, -1.0); - let posi = Complex64::new(0.0, 1.0); - let zero = Complex64::new(0.0, 0.0); - let half = Complex64::new(0.5, 0.0); +pub fn rot_z_jac(theta: f64, phase: Option) -> Array3 { + let half_theta = c64::new(theta / 2.0, 0.0); + let negi = c64::new(0.0, -1.0); + let posi = c64::new(0.0, 1.0); + let zero = c64::new(0.0, 0.0); + let half = c64::new(0.5, 0.0); if let Some(phase) = phase { unsafe { Array3::from_shape_vec_unchecked( diff --git a/src/minimizers/bfgs.rs b/src/ir/inst/minimizers/bfgs.rs similarity index 97% rename from src/minimizers/bfgs.rs rename to src/ir/inst/minimizers/bfgs.rs index da5f375..3655b9d 100644 --- a/src/minimizers/bfgs.rs +++ b/src/ir/inst/minimizers/bfgs.rs @@ -1,6 +1,6 @@ use super::{CostFunction, DifferentiableCostFn, Minimizer}; -use crate::minimizers::CostFn; +use crate::ir::inst::minimizers::CostFn; use nlopt::*; pub struct BfgsJacSolver { diff --git a/src/minimizers/ceres.rs b/src/ir/inst/minimizers/ceres.rs similarity index 100% rename from src/minimizers/ceres.rs rename to src/ir/inst/minimizers/ceres.rs diff --git a/src/minimizers/cost_fn.rs b/src/ir/inst/minimizers/cost_fn.rs similarity index 93% rename from src/minimizers/cost_fn.rs rename to src/ir/inst/minimizers/cost_fn.rs index 262a869..37f9c5e 100644 --- a/src/minimizers/cost_fn.rs +++ b/src/ir/inst/minimizers/cost_fn.rs @@ -1,12 +1,12 @@ use crate::{ - circuit::Circuit, - gates::{Gradient, Unitary}, + ir::circuit::Circuit, + ir::gates::{Gradient, Unitary}, utils::{matrix_distance_squared, matrix_distance_squared_jac}, }; use enum_dispatch::enum_dispatch; use ndarray::Array2; -use num_complex::Complex64; +use ndarray_linalg::c64; /// Trait defining the signature of a cost function used by minimizers. #[enum_dispatch] @@ -33,11 +33,11 @@ pub trait DifferentiableCostFn: CostFn { #[derive(Clone)] pub struct HilbertSchmidtCostFn { circ: Circuit, - target: Array2, + target: Array2, } impl HilbertSchmidtCostFn { - pub fn new(circ: Circuit, target: Array2) -> Self { + pub fn new(circ: Circuit, target: Array2) -> Self { HilbertSchmidtCostFn { circ, target } } diff --git a/src/minimizers/mod.rs b/src/ir/inst/minimizers/mod.rs similarity index 100% rename from src/minimizers/mod.rs rename to src/ir/inst/minimizers/mod.rs diff --git a/src/minimizers/residual_fn.rs b/src/ir/inst/minimizers/residual_fn.rs similarity index 96% rename from src/minimizers/residual_fn.rs rename to src/ir/inst/minimizers/residual_fn.rs index 5942505..abb3a95 100644 --- a/src/minimizers/residual_fn.rs +++ b/src/ir/inst/minimizers/residual_fn.rs @@ -1,9 +1,9 @@ use ndarray::Array2; -use num_complex::Complex64; +use ndarray_linalg::c64; use crate::{ - circuit::Circuit, - gates::{Gradient, Unitary}, + ir::circuit::Circuit, + ir::gates::{Gradient, Unitary}, utils::{matrix_distance_squared, matrix_residuals, matrix_residuals_jac}, }; @@ -54,12 +54,12 @@ where #[derive(Clone)] pub struct HilbertSchmidtResidualFn { circ: Circuit, - target: Array2, + target: Array2, eye: Array2, } impl HilbertSchmidtResidualFn { - pub fn new(circ: Circuit, target: Array2) -> Self { + pub fn new(circ: Circuit, target: Array2) -> Self { let size = target.shape()[0]; HilbertSchmidtResidualFn { circ, diff --git a/src/instantiators/mod.rs b/src/ir/inst/mod.rs similarity index 77% rename from src/instantiators/mod.rs rename to src/ir/inst/mod.rs index 494bf94..edc1671 100644 --- a/src/instantiators/mod.rs +++ b/src/ir/inst/mod.rs @@ -1,15 +1,16 @@ -use crate::circuit::Circuit; +use crate::ir::circuit::Circuit; use enum_dispatch::enum_dispatch; mod qfactor; +pub mod minimizers; use ndarray::Array2; -use num_complex::Complex64; +use ndarray_linalg::c64; pub use qfactor::QFactorInstantiator; #[enum_dispatch] pub trait Instantiate { - fn instantiate(&self, circuit: &mut Circuit, target: Array2, x0: &[f64]) + fn instantiate(&self, circuit: &mut Circuit, target: Array2, x0: &[f64]) -> Vec; } diff --git a/src/instantiators/qfactor.rs b/src/ir/inst/qfactor.rs similarity index 95% rename from src/instantiators/qfactor.rs rename to src/ir/inst/qfactor.rs index 1262490..49569c3 100644 --- a/src/instantiators/qfactor.rs +++ b/src/ir/inst/qfactor.rs @@ -1,11 +1,11 @@ use ndarray::Array2; -use num_complex::Complex64; +use ndarray_linalg::c64; use ndarray_linalg::trace::Trace; use super::Instantiate; -use crate::gates::Optimize; -use crate::{circuit::Circuit, gates::Unitary, unitary_builder::UnitaryBuilder}; +use crate::ir::gates::Optimize; +use crate::{ir::circuit::Circuit, ir::gates::Unitary, qis::unitary::UnitaryBuilder}; #[derive(Clone, Copy)] pub struct QFactorInstantiator { @@ -56,7 +56,7 @@ impl QFactorInstantiator { pub fn initialize_circuit_tensor( &self, circuit: &Circuit, - target: &Array2, + target: &Array2, ) -> UnitaryBuilder { let mut unitary_builder = UnitaryBuilder::new(circuit.size, circuit.radixes.clone()); let entire_circ_location: Vec = (0..circuit.size).collect(); @@ -103,7 +103,7 @@ impl Instantiate for QFactorInstantiator { fn instantiate( &self, circuit: &mut Circuit, - target: Array2, + target: Array2, x0: &[f64], ) -> Vec { if x0.len() != circuit.num_params() { diff --git a/src/ir/mod.rs b/src/ir/mod.rs new file mode 100644 index 0000000..bbff258 --- /dev/null +++ b/src/ir/mod.rs @@ -0,0 +1,6 @@ +pub mod operation; +pub mod circuit; +pub mod gates; +pub mod inst; + +pub use operation::Operation; \ No newline at end of file diff --git a/src/circuit/operation.rs b/src/ir/operation.rs similarity index 72% rename from src/circuit/operation.rs rename to src/ir/operation.rs index 3a306ff..c244a91 100644 --- a/src/circuit/operation.rs +++ b/src/ir/operation.rs @@ -1,7 +1,7 @@ use ndarray::{Array2, Array3, ArrayViewMut2}; -use num_complex::Complex64; +use ndarray_linalg::c64; -use crate::gates::{Gate, Gradient, Optimize, Unitary}; +use super::gates::{Gate, Gradient, Optimize, Unitary}; #[derive(Clone)] pub struct Operation { @@ -24,7 +24,7 @@ impl Unitary for Operation { fn num_params(&self) -> usize { self.gate.num_params() } - fn get_utry(&self, params: &[f64], const_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], const_gates: &[Array2]) -> Array2 { if params.is_empty() { self.gate.get_utry(&self.params, const_gates) } else { @@ -34,7 +34,7 @@ impl Unitary for Operation { } impl Gradient for Operation { - fn get_grad(&self, params: &[f64], const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], const_gates: &[Array2]) -> Array3 { if params.is_empty() { self.gate.get_grad(&self.params, const_gates) } else { @@ -45,8 +45,8 @@ impl Gradient for Operation { fn get_utry_and_grad( &self, params: &[f64], - const_gates: &[Array2], - ) -> (Array2, Array3) { + const_gates: &[Array2], + ) -> (Array2, Array3) { if params.is_empty() { self.gate.get_utry_and_grad(&self.params, const_gates) } else { @@ -56,7 +56,7 @@ impl Gradient for Operation { } impl Optimize for Operation { - fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { + fn optimize(&self, env_matrix: ArrayViewMut2) -> Vec { self.gate.optimize(env_matrix) } } diff --git a/src/lib.rs b/src/lib.rs index 942141a..3298382 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,9 @@ -pub mod circuit; -pub mod gates; -pub mod instantiators; -pub mod minimizers; -mod permutation_matrix; -#[cfg(feature = "python")] -mod python; -mod unitary_builder; +pub mod qis; +pub mod ir; pub mod utils; +pub mod squaremat; +pub mod python; +pub mod permutation_matrix; #[cfg(feature = "mimalloc")] use mimalloc::MiMalloc; @@ -17,29 +14,42 @@ static GLOBAL: MiMalloc = MiMalloc; #[cfg(feature = "accelerate")] extern crate accelerate_src; + #[cfg(feature = "mkl")] extern crate intel_mkl_src; -#[cfg(any(feature = "static", feature = "default"))] + +#[cfg(any(feature = "default", feature = "openblas"))] #[link(name = "openblas")] extern "C" {} +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = CNOTfn{}; + println!("{:?}", result.get_utry(&[0.0])); + } +} + #[macro_export] macro_rules! c { ($re:expr, $im:expr) => { - Complex64::new($re, $im) + c64::new($re, $im) }; } #[macro_export] macro_rules! r { ($re:expr) => { - Complex64::new($re, 0.0) + c64::new($re, 0.0) }; } #[macro_export] macro_rules! i { ($im:expr) => { - Complex64::new(0.0, $im) + c64::new(0.0, $im) }; } diff --git a/src/permutation_matrix.rs b/src/permutation_matrix.rs index 08f336f..066bfb9 100644 --- a/src/permutation_matrix.rs +++ b/src/permutation_matrix.rs @@ -1,8 +1,8 @@ use std::{ops::MulAssign, vec}; use ndarray::{Array2, ArrayView2}; -use num_complex::Complex64; -use squaremat::*; +use ndarray_linalg::c64; +use crate::squaremat::*; pub struct Permutation { perm: Vec, @@ -97,7 +97,7 @@ pub fn swap(x: usize, y: usize, n: usize) -> Permutation { } } -pub fn calc_permutation_matrix(num_qubits: usize, location: Vec) -> Array2 { +pub fn calc_permutation_matrix(num_qubits: usize, location: Vec) -> Array2 { let max_qubit = location.iter().max().unwrap(); let num_core_qubits = max_qubit + 1; let num_gate_qubits = location.len(); @@ -127,10 +127,10 @@ pub fn calc_permutation_matrix(num_qubits: usize, location: Vec) -> Array /// Permute a unitary so that it spans a circuit of size and is in the correct location pub fn permute_unitary( - unitary: ArrayView2, + unitary: ArrayView2, size: usize, location: Vec, -) -> Array2 { +) -> Array2 { if size == 0 { panic!("Invalid size for permute_unitary"); } diff --git a/src/python/circuit.rs b/src/python/circuit.rs index 9592e2a..aebc6ce 100644 --- a/src/python/circuit.rs +++ b/src/python/circuit.rs @@ -1,13 +1,13 @@ use std::sync::Arc; -use crate::circuit::operation::Operation; -use crate::circuit::Circuit; -use crate::gates::Gradient; -use crate::gates::Unitary; -use crate::gates::*; +use crate::ir::operation::Operation; +use crate::ir::circuit::Circuit; +use crate::ir::gates::Gradient; +use crate::ir::gates::Unitary; +use crate::ir::gates::*; use ndarray::Array2; -use num_complex::Complex64; +use ndarray_linalg::c64; use numpy::IntoPyArray; use numpy::PyArray2; @@ -18,7 +18,7 @@ use pyo3::{prelude::*, types::PyIterator}; use super::gate::PyGate; -fn pygate_to_native(pygate: &PyAny, constant_gates: &mut Vec>) -> PyResult { +fn pygate_to_native(pygate: &PyAny, constant_gates: &mut Vec>) -> PyResult { let cls = pygate.getattr("__class__")?; let dunder_name = cls.getattr("__name__")?; let name = dunder_name.extract::<&str>()?; @@ -45,7 +45,7 @@ fn pygate_to_native(pygate: &PyAny, constant_gates: &mut Vec>) if pygate.getattr("num_params")?.extract::()? == 0 { let args: Vec = vec![]; let pyobj = pygate.call_method("get_unitary", (args,), None)?; - let pymat = pyobj.getattr("numpy")?.extract::<&PyArray2>()?; + let pymat = pyobj.getattr("numpy")?.extract::<&PyArray2>()?; let mat = pymat.to_owned_array(); let gate_size = pygate.getattr("num_qudits")?.extract::()?; let index = constant_gates.len(); @@ -93,7 +93,6 @@ impl<'source> FromPyObject<'source> for Circuit { radixes, ops, constant_gates, - crate::circuit::SimulationBackend::Tensor, )) } } @@ -110,14 +109,14 @@ impl PyCircuit { PyCircuit { circ } } - pub fn get_unitary(&self, py: Python, params: Vec) -> Py> { + pub fn get_unitary(&self, py: Python, params: Vec) -> Py> { self.circ .get_utry(¶ms, &self.circ.constant_gates) .into_pyarray(py) .to_owned() } - pub fn get_grad(&self, py: Python, params: Vec) -> Py> { + pub fn get_grad(&self, py: Python, params: Vec) -> Py> { let grad = self.circ.get_grad(¶ms, &self.circ.constant_gates); grad.into_pyarray(py).to_owned() } @@ -126,7 +125,7 @@ impl PyCircuit { &self, py: Python, params: Vec, - ) -> (Py>, Py>) { + ) -> (Py>, Py>) { let (utry, grad) = self .circ .get_utry_and_grad(¶ms, &self.circ.constant_gates); diff --git a/src/python/gate.rs b/src/python/gate.rs index d13b232..d714f29 100644 --- a/src/python/gate.rs +++ b/src/python/gate.rs @@ -1,11 +1,11 @@ use ndarray::{Array2, Array3}; -use num_complex::Complex64; +use ndarray_linalg::c64; use numpy::{PyArray1, PyArray2, PyArray3}; use pyo3::prelude::*; use std::fmt; -use crate::gates::{DynGate, Gradient, Optimize, Size, Unitary}; +use crate::ir::gates::{DynGate, Gradient, Optimize, Size, Unitary}; pub struct PyGate { gate: PyObject, @@ -38,7 +38,7 @@ impl Unitary for PyGate { .expect("Return of num_params could not be converted into integral type.") } - fn get_utry(&self, params: &[f64], _const_gates: &[Array2]) -> Array2 { + fn get_utry(&self, params: &[f64], _const_gates: &[Array2]) -> Array2 { let gil = Python::acquire_gil(); let py = gil.python(); let args = (PyArray1::from_slice(py, params).to_object(py),); @@ -52,14 +52,14 @@ impl Unitary for PyGate { .expect("Failed to convert UnitaryMatrix to ndarray."), false => pyutry, } - .extract::>>(py) + .extract::>>(py) .expect("Failed to convert return of get array into complex matrix."); pyarray.into_ref(py).to_owned_array() } } impl Gradient for PyGate { - fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { + fn get_grad(&self, params: &[f64], _const_gates: &[Array2]) -> Array3 { let gil = Python::acquire_gil(); let py = gil.python(); let args = (PyArray1::from_slice(py, params).to_object(py),); @@ -75,7 +75,7 @@ impl Gradient for PyGate { .expect("Failed to convert UnitaryMatrix to ndarray."), false => pygrads, } - .extract::>>(py) + .extract::>>(py) .expect("Failed to convert return of get_grad into complex matrix.") .into_ref(py) .to_owned_array() @@ -84,8 +84,8 @@ impl Gradient for PyGate { fn get_utry_and_grad( &self, params: &[f64], - _const_gates: &[Array2], - ) -> (Array2, Array3) { + _const_gates: &[Array2], + ) -> (Array2, Array3) { let gil = Python::acquire_gil(); let py = gil.python(); let args = (PyArray1::from_slice(py, params).to_object(py),); @@ -102,7 +102,7 @@ impl Gradient for PyGate { .expect("Failed to convert UnitaryMatrix to ndarray."), false => pyutry, } - .extract::>>(py) + .extract::>>(py) .expect("Failed to convert return of get array into complex matrix."); let grads = match pygrads.as_ref(py).hasattr("numpy").unwrap() { @@ -111,7 +111,7 @@ impl Gradient for PyGate { .expect("Failed to convert UnitaryMatrix to ndarray."), false => pygrads, } - .extract::>>(py) + .extract::>>(py) .expect("Failed to convert return of get_grad into complex matrix."); ( @@ -134,7 +134,7 @@ impl Size for PyGate { } impl Optimize for PyGate { - fn optimize(&self, env_matrix: ndarray::ArrayViewMut2) -> Vec { + fn optimize(&self, env_matrix: ndarray::ArrayViewMut2) -> Vec { let gil = Python::acquire_gil(); let py = gil.python(); let args = (PyArray2::from_array(py, &env_matrix.to_owned()).to_object(py),); diff --git a/src/python/instantiators/qfactor.rs b/src/python/instantiators/qfactor.rs index 7622796..64028a6 100644 --- a/src/python/instantiators/qfactor.rs +++ b/src/python/instantiators/qfactor.rs @@ -1,10 +1,10 @@ -use num_complex::Complex64; +use ndarray_linalg::c64; use numpy::PyArray2; use pyo3::prelude::*; -use crate::{ +use crate::ir::{ circuit::Circuit, - instantiators::{Instantiate, QFactorInstantiator}, + inst::{Instantiate, QFactorInstantiator}, }; #[pyclass(name = "QFactorInstantiatorNative", subclass, module = "bqskitrs")] @@ -45,11 +45,11 @@ impl PyQFactorInstantiator { target: PyObject, x0: Vec, ) -> PyResult> { - let target_rs = match target.extract::>>(py) { + let target_rs = match target.extract::>>(py) { Ok(arr) => arr, Err(..) => { let target_np = target.getattr(py, "numpy")?; - target_np.extract::>>(py)? + target_np.extract::>>(py)? } }; let target_rs = target_rs.as_ref(py).to_owned_array(); diff --git a/src/python/minimizers/bfgs.rs b/src/python/minimizers/bfgs.rs index cc02f1e..9055d4d 100644 --- a/src/python/minimizers/bfgs.rs +++ b/src/python/minimizers/bfgs.rs @@ -4,9 +4,9 @@ use numpy::IntoPyArray; use numpy::PyArray1; use pyo3::types::PyTuple; -use crate::minimizers::BfgsJacSolver; -use crate::minimizers::CostFunction; -use crate::minimizers::Minimizer; +use crate::ir::inst::minimizers::BfgsJacSolver; +use crate::ir::inst::minimizers::CostFunction; +use crate::ir::inst::minimizers::Minimizer; use pyo3::exceptions::PyTypeError; diff --git a/src/python/minimizers/ceres.rs b/src/python/minimizers/ceres.rs index 6c168cd..b6ef84f 100644 --- a/src/python/minimizers/ceres.rs +++ b/src/python/minimizers/ceres.rs @@ -1,6 +1,6 @@ use pyo3::{exceptions::PyTypeError, prelude::*, types::PyTuple}; -use crate::minimizers::{CeresJacSolver, Minimizer, ResidualFunction}; +use crate::ir::inst::minimizers::{CeresJacSolver, Minimizer, ResidualFunction}; use numpy::IntoPyArray; use numpy::PyArray1; diff --git a/src/python/minimizers/cost_fn.rs b/src/python/minimizers/cost_fn.rs index 684bdec..3e17552 100644 --- a/src/python/minimizers/cost_fn.rs +++ b/src/python/minimizers/cost_fn.rs @@ -1,8 +1,8 @@ use crate::{ - circuit::Circuit, - minimizers::{CostFn, CostFunction, DifferentiableCostFn, HilbertSchmidtCostFn}, + ir::circuit::Circuit, + ir::inst::minimizers::{CostFn, CostFunction, DifferentiableCostFn, HilbertSchmidtCostFn}, }; -use num_complex::Complex64; +use ndarray_linalg::c64; use numpy::{PyArray1, PyArray2}; use pyo3::{prelude::*, types::PyTuple}; @@ -67,11 +67,11 @@ impl PyHilberSchmidtCostFn { "UnitaryMatrix" => { let np = target_matrix .getattr("numpy")? - .extract::<&PyArray2>()?; + .extract::<&PyArray2>()?; np.to_owned_array() } "ndarray" => target_matrix - .extract::<&PyArray2>()? + .extract::<&PyArray2>()? .to_owned_array(), _ => panic!("HilbertSchmidtCost only takes numpy arrays or UnitaryMatrix types."), }; diff --git a/src/python/minimizers/residual_fn.rs b/src/python/minimizers/residual_fn.rs index 42782ff..4685e1a 100644 --- a/src/python/minimizers/residual_fn.rs +++ b/src/python/minimizers/residual_fn.rs @@ -1,11 +1,11 @@ use crate::{ - circuit::Circuit, - minimizers::{ + ir::circuit::Circuit, + ir::inst::minimizers::{ CostFn, DifferentiableResidualFn, HilbertSchmidtResidualFn, ResidualFn, ResidualFunction, }, }; use ndarray::Array2; -use num_complex::Complex64; +use ndarray_linalg::c64; use numpy::IntoPyArray; use numpy::{PyArray1, PyArray2}; use pyo3::{prelude::*, types::PyTuple}; @@ -102,11 +102,11 @@ impl PyHilberSchmidtResidualFn { "UnitaryMatrix" => { let np = target_matrix .getattr("numpy")? - .extract::<&PyArray2>()?; + .extract::<&PyArray2>()?; np.to_owned_array() } "ndarray" => target_matrix - .extract::<&PyArray2>()? + .extract::<&PyArray2>()? .to_owned_array(), _ => panic!("HilbertSchmidtCost only takes numpy arrays or UnitaryMatrix types."), }; diff --git a/src/python/mod.rs b/src/python/mod.rs index f1fa876..2aba5e8 100644 --- a/src/python/mod.rs +++ b/src/python/mod.rs @@ -1,11 +1,9 @@ -use num_complex::Complex64; +use ndarray_linalg::c64; use numpy::{PyArray2, PyReadonlyArray2, PyReadonlyArray3}; use pyo3::prelude::*; -use better_panic::install; - use crate::python::circuit::PyCircuit; use crate::utils::{ matrix_distance_squared, matrix_distance_squared_jac, matrix_residuals, matrix_residuals_jac, @@ -21,15 +19,11 @@ mod instantiators; mod circuit; -use crate::permutation_matrix::{calc_permutation_matrix, swap_bit}; - mod gate; #[pymodule] +#[pyo3(name = "bqskitrs")] fn bqskitrs(_py: Python, m: &PyModule) -> PyResult<()> { - // Install better panic for better tracebacks - install(); - m.add_class::()?; m.add_class::()?; m.add_class::()?; @@ -37,44 +31,28 @@ fn bqskitrs(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; - #[pyfn(m)] - #[pyo3(name = "calc_permutation_matrix")] - fn calc_permutation_matrix_py( - py: Python, - num_qubits: usize, - location: Vec, - ) -> Py> { - PyArray2::from_array(py, &calc_permutation_matrix(num_qubits, location)).to_owned() - } - - #[pyfn(m)] - #[pyo3(name = "swap_bit")] - fn swap_bit_py(i: usize, j: usize, b: usize) -> usize { - swap_bit(i, j, b) - } - #[pyfn(m)] #[pyo3(name = "matrix_distance_squared")] fn matrix_distance_squared_py( - a: PyReadonlyArray2, - b: PyReadonlyArray2, + a: PyReadonlyArray2, + b: PyReadonlyArray2, ) -> f64 { matrix_distance_squared(a.as_array(), b.as_array()) } #[pyfn(m)] #[pyo3(name = "matrix_distance_squared_jac")] fn matrix_distance_squared_jac_py( - a: PyReadonlyArray2, - b: PyReadonlyArray2, - jacs: PyReadonlyArray3, + a: PyReadonlyArray2, + b: PyReadonlyArray2, + jacs: PyReadonlyArray3, ) -> (f64, Vec) { matrix_distance_squared_jac(a.as_array(), b.as_array(), jacs.as_array()) } #[pyfn(m)] #[pyo3(name = "matrix_residuals")] fn matrix_residuals_py( - a: PyReadonlyArray2, - b: PyReadonlyArray2, + a: PyReadonlyArray2, + b: PyReadonlyArray2, eye: PyReadonlyArray2, ) -> Vec { matrix_residuals( @@ -87,9 +65,9 @@ fn bqskitrs(_py: Python, m: &PyModule) -> PyResult<()> { #[pyo3(name = "matrix_residuals_jac")] fn matrix_residuals_jac_py( py: Python, - u: PyReadonlyArray2, - m: PyReadonlyArray2, - jacs: PyReadonlyArray3, + u: PyReadonlyArray2, + m: PyReadonlyArray2, + jacs: PyReadonlyArray3, ) -> Py> { PyArray2::from_array( py, diff --git a/src/qis/mod.rs b/src/qis/mod.rs new file mode 100644 index 0000000..1a46dc0 --- /dev/null +++ b/src/qis/mod.rs @@ -0,0 +1 @@ +pub mod unitary; \ No newline at end of file diff --git a/src/unitary_builder.rs b/src/qis/unitary/builder.rs similarity index 56% rename from src/unitary_builder.rs rename to src/qis/unitary/builder.rs index 31df117..0295af7 100644 --- a/src/unitary_builder.rs +++ b/src/qis/unitary/builder.rs @@ -1,22 +1,21 @@ use ndarray::{Array2, ArrayD, ArrayView2, Ix2}; -use num_complex::Complex64; - -use squaremat::*; +use ndarray_linalg::c64; use crate::utils::{argsort, trace}; +use crate::squaremat::*; /// A type to build unitaries using tensor networks pub struct UnitaryBuilder { size: usize, radixes: Vec, - tensor: Option>, + tensor: Option>, dim: usize, } impl UnitaryBuilder { pub fn new(size: usize, radixes: Vec) -> Self { let dim = radixes.iter().product(); - let mut tensor = Array2::::eye(dim).into_dyn(); + let mut tensor = Array2::::eye(dim).into_dyn(); tensor = tensor .into_shape([&radixes[..], &radixes[..]].concat()) .unwrap(); @@ -28,7 +27,7 @@ impl UnitaryBuilder { } } - pub fn get_utry(&self) -> Array2 { + pub fn get_utry(&self) -> Array2 { match &self.tensor { Some(t) => t .to_shape((self.dim, self.dim)) @@ -40,7 +39,7 @@ impl UnitaryBuilder { } } - pub fn apply_right(&mut self, utry: ArrayView2, location: &[usize], inverse: bool) { + pub fn apply_right(&mut self, utry: ArrayView2, location: &[usize], inverse: bool) { let left_perm = location.iter(); let mid_perm = (0..self.size).filter(|x| !location.contains(&x)); let right_perm = (0..self.size).map(|x| x + self.size); @@ -72,7 +71,7 @@ impl UnitaryBuilder { self.tensor = Some(reshape_back.permuted_axes(argsort(perm)).into_dyn()); } - pub fn apply_left(&mut self, utry: ArrayView2, location: &[usize], inverse: bool) { + pub fn apply_left(&mut self, utry: ArrayView2, location: &[usize], inverse: bool) { let left_perm = 0..self.size; let mid_perm = left_perm.clone().filter_map(|x| { if location.contains(&x) { @@ -103,7 +102,7 @@ impl UnitaryBuilder { let reshaped = permuted .to_shape((dim / right_dim, right_dim)) .expect("Cannot reshape tensor to matrix"); - let prod = reshaped.matmul(unitary.view()); + let prod = reshaped.view().matmul(unitary.view()); let radixes = [&self.radixes[..], &self.radixes[..]].concat(); let shape: Vec = perm.iter().map(|p| radixes[*p]).collect(); let reshape_back = prod @@ -112,7 +111,84 @@ impl UnitaryBuilder { self.tensor = Some(reshape_back.permuted_axes(argsort(perm)).into_dyn()); } - pub fn calc_env_matrix(&self, location: &[usize]) -> Array2 { + pub fn eval_apply_right(&self, m: ArrayView2, location: &[usize]) -> Array2 { + let left_perm = location.iter(); + let mid_perm = (0..self.size).filter(|x| !location.contains(&x)); + let right_perm = (0..self.size).map(|x| x + self.size); + + let left_dim: usize = left_perm.clone().map(|i| self.radixes[*i]).product(); + + let matrix = m.to_owned(); + + let mut perm = vec![]; + perm.extend(left_perm); + perm.extend(mid_perm); + perm.extend(right_perm); + + let mut tensor_copy = self.tensor.clone(); + let permuted = tensor_copy.take().unwrap().permuted_axes(perm.clone()); + let dim: usize = permuted.shape().iter().product(); + let reshaped = permuted + .to_shape((left_dim, dim / left_dim)) + .expect("Cannot reshape tensor to matrix"); + let prod = matrix.matmul(reshaped.view()); + + let radixes = [&self.radixes[..], &self.radixes[..]].concat(); + let shape: Vec = perm.iter().map(|p| radixes[*p]).collect(); + let reshape_back = prod + .into_shape(shape) + .expect("Failed to reshape matrix product back"); + let eval_tensor = reshape_back.permuted_axes(argsort(perm)).into_dyn(); + let matrix_dim = self.dim.clone(); + let eval_matrix = eval_tensor + .to_shape((matrix_dim, matrix_dim)) + .expect("Cannot reshape tensor to matrix"); + eval_matrix.to_owned() + } + + pub fn eval_apply_left(&self, m: ArrayView2, location: &[usize]) -> Array2 { + let left_perm = 0..self.size; + let mid_perm = left_perm.clone().filter_map(|x| { + if location.contains(&x) { + None + } else { + Some(x + self.size) + } + }); + let right_perm = location.iter().map(|x| x + self.size); + + let right_dim: usize = right_perm + .clone() + .map(|i| self.radixes[i - self.size]) + .product(); + + let mut perm = vec![]; + perm.extend(left_perm); + perm.extend(mid_perm); + perm.extend(right_perm); + + let mut tensor_copy = self.tensor.clone(); + let permuted = tensor_copy.take().unwrap().permuted_axes(perm.clone()); + let dim: usize = permuted.shape().iter().product(); + let reshaped = permuted + .to_shape((dim / right_dim, right_dim)) + .expect("Cannot reshape tensor to matrix"); + let prod = reshaped.view().matmul(m.view()); + + let radixes = [&self.radixes[..], &self.radixes[..]].concat(); + let shape: Vec = perm.iter().map(|p| radixes[*p]).collect(); + let reshape_back = prod + .into_shape(shape) + .expect("Failed to reshape matrix product back"); + let eval_tensor = reshape_back.permuted_axes(argsort(perm)).into_dyn(); + let matrix_dim = self.dim.clone(); + let eval_matrix = eval_tensor + .to_shape((matrix_dim, matrix_dim)) + .expect("Cannot reshape tensor to matrix"); + eval_matrix.to_owned() + } + + pub fn calc_env_matrix(&self, location: &[usize]) -> Array2 { let mut left_perm: Vec = (0..self.size).filter(|x| !location.contains(x)).collect(); let left_perm_copy = left_perm.clone(); let left_extension = left_perm_copy.iter().map(|x| x + self.size); diff --git a/src/qis/unitary/function.rs b/src/qis/unitary/function.rs new file mode 100644 index 0000000..e452eef --- /dev/null +++ b/src/qis/unitary/function.rs @@ -0,0 +1,15 @@ +use enum_dispatch::enum_dispatch; +use ndarray::Array2; +use ndarray_linalg::c64; + +#[enum_dispatch] +pub trait UnitaryFunction { + // fn get_num_params(&self) -> usize; + // fn get_num_qudits(&self) -> usize; + // fn get_radixes(&self) -> Vec; + // fn get_dim(&self) -> usize + // { + // return self.get_radixes().iter().product() + // } + fn get_utry(&self, params: &[f64]) -> Array2; +} \ No newline at end of file diff --git a/src/qis/unitary/matrix.rs b/src/qis/unitary/matrix.rs new file mode 100644 index 0000000..0618fab --- /dev/null +++ b/src/qis/unitary/matrix.rs @@ -0,0 +1,7 @@ +pub struct UnitaryMatrix { + input: Array2, + radixes: Vec, +} + +impl UnitaryMatrix { +} \ No newline at end of file diff --git a/src/qis/unitary/mod.rs b/src/qis/unitary/mod.rs new file mode 100644 index 0000000..a87050d --- /dev/null +++ b/src/qis/unitary/mod.rs @@ -0,0 +1,5 @@ +// pub mod function; +pub mod builder; + +pub use builder::UnitaryBuilder; +// pub use matrix::UnitaryMatrix; \ No newline at end of file diff --git a/src/squaremat/conj.rs b/src/squaremat/conj.rs new file mode 100644 index 0000000..870b2b7 --- /dev/null +++ b/src/squaremat/conj.rs @@ -0,0 +1,18 @@ +use ndarray::{Array2, ArrayView2}; +use ndarray_linalg::c64; + +pub trait Conj { + fn conj(&self) -> Array2; +} + +impl Conj for Array2 { + fn conj(&self) -> Array2 { + self.mapv(|i| i.conj()) + } +} + +impl Conj for ArrayView2<'_, c64> { + fn conj(&self) -> Array2 { + self.mapv(|i| i.conj()) + } +} diff --git a/squaremat/src/kron.rs b/src/squaremat/kron.rs similarity index 78% rename from squaremat/src/kron.rs rename to src/squaremat/kron.rs index 01ab5ad..425b2a8 100644 --- a/squaremat/src/kron.rs +++ b/src/squaremat/kron.rs @@ -1,10 +1,10 @@ use std::mem::MaybeUninit; use ndarray::{Array2, ArrayBase, ArrayView2, Data, Ix2, LinalgScalar, OwnedRepr, Zip}; -use num_complex::Complex64; +use ndarray_linalg::c64; pub trait Kronecker { - fn kron(&self, other: &Array2) -> Array2; + fn kron(&self, other: &Array2) -> Array2; } /// Kronecker product of 2D matrices. @@ -36,14 +36,14 @@ where unsafe { out.assume_init() } } -impl Kronecker for Array2 { - fn kron(&self, other: &Array2) -> Array2 { +impl Kronecker for Array2 { + fn kron(&self, other: &Array2) -> Array2 { kron(self, other) } } -impl Kronecker for ArrayView2<'_, Complex64> { - fn kron(&self, other: &Array2) -> Array2 { +impl Kronecker for ArrayView2<'_, c64> { + fn kron(&self, other: &Array2) -> Array2 { kron(self, other) } } diff --git a/src/squaremat/matmul.rs b/src/squaremat/matmul.rs new file mode 100644 index 0000000..00c36e2 --- /dev/null +++ b/src/squaremat/matmul.rs @@ -0,0 +1,113 @@ +use ndarray::{Array2, ArrayView2, ArrayViewMut2, CowArray, Ix2, ShapeBuilder}; +use ndarray_linalg::c64; + +// use cblas_sys::cblas_zgemm; +// use cblas_sys::{CblasNoTrans, CblasRowMajor, CblasTrans}; +// use crate::r; + +pub trait Matmul { + fn matmul(&self, other: ArrayView2) -> Array2; +} + +/// Complex matrix-matrix multiplication using BLAS zgemm. Based on the matmu_impl in the +/// ndarray crate, see the LICENSE file for more details +/// https://github.com/rust-ndarray/ndarray/blob/562104a5326acdefbd0235599b91a59bcc8d73d4/src/linalg/impl_linalg.rs#L367 +// fn matmul_impl(lhs: ArrayView2, rhs: ArrayView2) -> Array2 { +// let ((mut m, a), (_, mut n)) = (lhs.dim(), rhs.dim()); +// let mut lhs_ = lhs.view(); +// let mut rhs_ = rhs.view(); + +// let lhs_s0 = lhs_.strides()[0]; +// let rhs_s0 = rhs_.strides()[0]; + +// let column_major = lhs_s0 == 1 && rhs_s0 == 1; +// let mut v = Vec::with_capacity(m * n); +// let mut out; +// unsafe { +// v.set_len(m * n); +// out = Array2::from_shape_vec_unchecked((m, n).set_f(column_major), v); +// } +// let mut c_ = out.view_mut(); +// let both_f = lhs_s0 == 1 && rhs_s0 == 1; +// let mut lhs_trans = CblasNoTrans; +// let mut rhs_trans = CblasNoTrans; +// if both_f { +// // A^t B^t = C^t => B A = C +// let lhs_t = lhs_.reversed_axes(); +// lhs_ = rhs_.reversed_axes(); +// rhs_ = lhs_t; +// c_ = c_.reversed_axes(); +// std::mem::swap(&mut m, &mut n); +// } else if lhs_s0 == 1 && m == a { +// lhs_ = lhs_.reversed_axes(); +// lhs_trans = CblasTrans; +// } else if rhs_s0 == 1 && a == n { +// rhs_ = rhs_.reversed_axes(); +// rhs_trans = CblasTrans; +// } + +// let (m, k) = match lhs_trans { +// CblasNoTrans => lhs_.dim(), +// _ => { +// let (rows, cols) = lhs_.dim(); +// (cols, rows) +// } +// }; +// let n = match rhs_trans { +// CblasNoTrans => rhs_.raw_dim()[1], +// _ => rhs_.raw_dim()[0], +// }; + +// // adjust strides, these may [1, 1] for column matrices +// let lhs_stride = std::cmp::max(lhs_.strides()[0] as i32, k as i32); +// let rhs_stride = std::cmp::max(rhs_.strides()[0] as i32, n as i32); +// let c_stride = std::cmp::max(c_.strides()[0] as i32, n as i32); +// let one = r!(1.); +// let zero = r!(0.); +// unsafe { +// cblas_zgemm( +// CblasRowMajor, +// lhs_trans, +// rhs_trans, +// m as i32, +// n as i32, +// k as i32, +// &one as *const c64 as *const _, +// lhs_.as_ptr() as *const _, +// lhs_stride as i32, +// rhs_.as_ptr() as *const _, +// rhs_stride as i32, +// &zero as *const c64 as *const _, +// c_.as_mut_ptr() as *mut _, +// c_stride as i32, +// ) +// }; +// out +// } + +// impl Matmul for CowArray<'_, c64, Ix2> { +// fn matmul(&self, other: ArrayView2) -> Array2 { +// matmul_impl(self.view(), other) +// } +// } + +impl Matmul for Array2 { + fn matmul(&self, other: ArrayView2) -> Array2 { + // matmul_impl(self.view(), other.view()) + self.dot(&other) + } +} + +impl Matmul for ArrayView2<'_, c64> { + fn matmul(&self, other: ArrayView2) -> Array2 { + // matmul_impl(self.view(), other) + self.dot(&other) + } +} + +impl Matmul for ArrayViewMut2<'_, c64> { + fn matmul(&self, other: ArrayView2) -> Array2 { + // matmul_impl(self.view(), other) + self.dot(&other) + } +} diff --git a/src/squaremat/mod.rs b/src/squaremat/mod.rs new file mode 100644 index 0000000..f2b0faf --- /dev/null +++ b/src/squaremat/mod.rs @@ -0,0 +1,13 @@ +mod conj; +mod kron; +mod matmul; +mod multiply; +mod split_complex; +mod swap_rows; + +pub use conj::Conj; +pub use kron::Kronecker; +pub use matmul::Matmul; +pub use multiply::Multiply; +pub use split_complex::SplitComplex; +pub use swap_rows::SwapRows; diff --git a/squaremat/src/multiply.rs b/src/squaremat/multiply.rs similarity index 51% rename from squaremat/src/multiply.rs rename to src/squaremat/multiply.rs index 02026b1..27fce06 100644 --- a/squaremat/src/multiply.rs +++ b/src/squaremat/multiply.rs @@ -1,9 +1,9 @@ use std::mem::MaybeUninit; use ndarray::{Array2, ArrayView2, Zip}; -use num_complex::Complex64; +use ndarray_linalg::c64; -fn multiply(first: &ArrayView2, other: &ArrayView2) -> Array2 { +fn multiply(first: &ArrayView2, other: &ArrayView2) -> Array2 { let mut out = Array2::uninit((first.shape()[0], first.shape()[1])); Zip::from(out.view_mut()) .and(first) @@ -15,17 +15,17 @@ fn multiply(first: &ArrayView2, other: &ArrayView2) -> Arr } pub trait Multiply { - fn multiply(&self, other: &ArrayView2) -> Array2; + fn multiply(&self, other: &ArrayView2) -> Array2; } -impl Multiply for Array2 { - fn multiply(&self, other: &ArrayView2) -> Array2 { +impl Multiply for Array2 { + fn multiply(&self, other: &ArrayView2) -> Array2 { multiply(&self.view(), other) } } -impl Multiply for ArrayView2<'_, Complex64> { - fn multiply(&self, other: &ArrayView2) -> Array2 { +impl Multiply for ArrayView2<'_, c64> { + fn multiply(&self, other: &ArrayView2) -> Array2 { multiply(&self, &other) } } diff --git a/squaremat/src/split_complex.rs b/src/squaremat/split_complex.rs similarity index 91% rename from squaremat/src/split_complex.rs rename to src/squaremat/split_complex.rs index 432f679..d923652 100644 --- a/squaremat/src/split_complex.rs +++ b/src/squaremat/split_complex.rs @@ -1,11 +1,11 @@ use ndarray::{Array2, Zip}; -use num_complex::Complex64; +use ndarray_linalg::c64; pub trait SplitComplex { fn split_complex(&self) -> (Array2, Array2); } -impl SplitComplex for Array2 { +impl SplitComplex for Array2 { fn split_complex(&self) -> (Array2, Array2) { let size = self.shape()[0]; // Safety: these two arrays are initialized from `self` in the Zip below. diff --git a/squaremat/src/swap_rows.rs b/src/squaremat/swap_rows.rs similarity index 83% rename from squaremat/src/swap_rows.rs rename to src/squaremat/swap_rows.rs index e45788a..054e69d 100644 --- a/squaremat/src/swap_rows.rs +++ b/src/squaremat/swap_rows.rs @@ -1,10 +1,10 @@ use ndarray::{s, Array2, Zip}; -use num_complex::Complex64; +use ndarray_linalg::c64; pub trait SwapRows { fn swap_rows(&mut self, idx_a: usize, idx_b: usize); } -impl SwapRows for Array2 { +impl SwapRows for Array2 { fn swap_rows(&mut self, idx_a: usize, idx_b: usize) { let (row_a, row_b) = self.multi_slice_mut((s![idx_a, ..], s![idx_b, ..])); Zip::from(row_a).and(row_b).for_each(std::mem::swap); diff --git a/src/utils.rs b/src/utils.rs index 2d0b055..d4741c0 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,16 +1,13 @@ use ndarray::{s, Array1, Array2, Array3, ArrayView2, ArrayView3, ArrayView4, Ix2}; use ndarray_einsum_beta::einsum; -use num_complex::Complex64; -use squaremat::*; - -use crate::{i, r}; - -use std::f64::consts::{E, PI}; +use ndarray_linalg::c64; +use crate::squaremat::*; +use crate::r; use itertools::Itertools; -pub fn trace(arr: ArrayView4) -> Array2 { - let mut out = Array2::::zeros((arr.shape()[2], arr.shape()[3])); +pub fn trace(arr: ArrayView4) -> Array2 { + let mut out = Array2::::zeros((arr.shape()[2], arr.shape()[3])); for i in 0..arr.shape()[2] { for j in 0..arr.shape()[3] { out[(i, j)] = arr @@ -34,7 +31,7 @@ pub fn argsort(v: Vec) -> Vec { .collect() } -pub fn matrix_distance_squared(a: ArrayView2, b: ArrayView2) -> f64 { +pub fn matrix_distance_squared(a: ArrayView2, b: ArrayView2) -> f64 { // 1 - np.abs(np.trace(np.dot(A,B.H))) / A.shape[0] // converted to // 1 - np.abs(np.sum(np.multiply(A,np.conj(B)))) / A.shape[0] @@ -44,9 +41,9 @@ pub fn matrix_distance_squared(a: ArrayView2, b: ArrayView2, - m: ArrayView2, - j: ArrayView3, + u: ArrayView2, + m: ArrayView2, + j: ArrayView3, ) -> (f64, Vec) { let size = u.shape()[0]; let s = u.multiply(&m.conj().view()).sum(); @@ -54,7 +51,7 @@ pub fn matrix_distance_squared_jac( if s == r!(0.0) { return (dsq, vec![std::f64::INFINITY; j.len()]); } - let jus: Vec = j + let jus: Vec = j .outer_iter() .map(|ji| einsum("ij,ij->", &[&u, &ji.conj()]).unwrap().sum()) .collect(); @@ -67,8 +64,8 @@ pub fn matrix_distance_squared_jac( /// Calculates the residuals pub fn matrix_residuals( - a_matrix: &Array2, - b_matrix: &Array2, + a_matrix: &Array2, + b_matrix: &Array2, identity: &Array2, ) -> Vec { let calculated_mat = b_matrix.matmul(a_matrix.conj().t()); @@ -78,9 +75,9 @@ pub fn matrix_residuals( } pub fn matrix_residuals_jac( - u: &Array2, - _m: &Array2, - jacs: &Array3, + u: &Array2, + _m: &Array2, + jacs: &Array3, ) -> Array2 { let u_conj = u.conj(); let size = u.shape()[0]; @@ -92,9 +89,4 @@ pub fn matrix_residuals_jac( row.assign(&data); } out.reversed_axes() -} - -pub fn qft(n: usize) -> Array2 { - let root = r!(E).powc(i!(2f64) * PI / n as f64); - Array2::from_shape_fn((n, n), |(x, y)| root.powf((x * y) as f64)) / (n as f64).sqrt() -} +} \ No newline at end of file From b58b1a7799b528f0b3af2c0fbd88ae0ced8f8e33 Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Sun, 16 Oct 2022 15:29:33 -0700 Subject: [PATCH 02/20] Windows --- Cargo.toml | 20 +++++++++++++++++++- build.rs | 13 +++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 build.rs diff --git a/Cargo.toml b/Cargo.toml index 9146792..86ee588 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ authors = [ publish = false license = "LGPL-2.1 AND BSD-3-Clause" edition = "2021" +build = "build.rs" [features] @@ -45,6 +46,9 @@ ceres = { path="./ceres", features = ["static"] } numpy = "0.17.2" pyo3 = { version = "0.17.2", features = ["extension-module", "abi3-py38"] } +[target.'cfg(target_os = "windows")'.build-dependencies] +vcpkg = "0.2.15" + [lib] name = "bqskitrs" path = "src/lib.rs" @@ -54,4 +58,18 @@ crate-type = ["cdylib"] lto = "fat" codegen-units = 1 opt-level = 3 -debug = true \ No newline at end of file +debug = true + +[package.metadata.vcpkg] +git = "https://github.com/microsoft/vcpkg" +rev = "2022.09.27" + +[package.metadata.vcpkg.target] +x86_64-pc-windows-msvc = { triplet="x64-windows-static-md", install=[ + "ceres", + "eigen3", + "openblas", + "glog", + "gflags", + "clapack", +] } \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..9f160a5 --- /dev/null +++ b/build.rs @@ -0,0 +1,13 @@ +fn main() -> Result<(), std::io::Error> { + // Homebrew/macOS gcc don't add libgfortran to the rpath, + // so we manually go prodding around for it here + if cfg!(target_os = "windows") { + #[cfg(target_os = "windows")] + let _lapack = vcpkg::Config::new() + .target_triplet("x64-windows-static-md") + .find_package("clapack") + .unwrap(); + println!("cargo:rustc-link-lib=static=lapack"); + } + Ok(()) +} \ No newline at end of file From fbf5faa25def44dcefdb7b3347b5ef736d72021d Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Wed, 19 Oct 2022 12:30:00 -0700 Subject: [PATCH 03/20] Building wheels with actions --- .github/workflows/build-wheels.yml | 123 +++++++++++++++++++++++++++++ .github/workflows/main.yml | 89 --------------------- .github/workflows/pre-commit.yml | 33 -------- ceres/ceres-sys/build.rs | 10 ++- pyproject.toml | 7 +- 5 files changed, 133 insertions(+), 129 deletions(-) create mode 100644 .github/workflows/build-wheels.yml delete mode 100644 .github/workflows/main.yml delete mode 100644 .github/workflows/pre-commit.yml diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml new file mode 100644 index 0000000..ffbbec1 --- /dev/null +++ b/.github/workflows/build-wheels.yml @@ -0,0 +1,123 @@ +name: Build Wheels + +on: + release: + types: [published] + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + linux: + name: Manylinux Wheel Build + + runs-on: ubuntu-latest + steps: + - name: Setup Repo + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - name: Build Manylinux Wheels + run: > + docker run -e OPENBLAS_ARGS="DYNAMIC_ARCH=1" --rm -v $(pwd):/io + edyounis/bqskitrs-manylinux:1.0 build --release + --features=openblas --compatibility=manylinux2014 + + - name: Upload Wheel Artifacts + uses: actions/upload-artifact@v3 + with: + name: linux-wheels + path: target/wheels + + windows: + name: Windows Wheel Build + + runs-on: windows-latest + steps: + - name: Setup Repo + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - name: Setup Python + uses: actions/setup-python@v3 + with: + python-version: '3.10' + + - name: Install Python Dependencies + run: pip install -U setuptools wheel maturin + + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + + - name: Install Other Dependencies (Windows) + run: > + cargo install cargo-vcpkg && vcpkg install + ceres:x64-windows-static-md + eigen3:x64-windows-static-md + openblas:x64-windows-static-md + glog:x64-windows-static-md + gflags:x64-windows-static-md + clapack:x64-windows-static-md + && vcpkg integrate install + + - name: Build Windows Wheels + run: maturin build --features="openblas,openblas-src/system" --release + + - name: Upload Wheel Artifacts + uses: actions/upload-artifact@v3 + with: + name: windows-wheels + path: target/wheels + + macos: + name: macOS Wheel Build + + runs-on: macOS-latest + steps: + - name: Setup Repo + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - name: Setup Python + uses: actions/setup-python@v3 + with: + python-version: '3.10' + + - name: Install Python Dependencies + run: pip install -U setuptools wheel maturin + + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + + - name: Install Apple Intel Target + run: rustup target add x86_64-apple-darwin + + - name: Install Apple Silicon Target + run: rustup target add aarch64-apple-darwin + + - name: Install Other Dependencies (macOS) + run: brew install gcc ceres-solver eigen lapack + + - name: Build macOS x86_64 Wheels + run: maturin build --features="accelerate" --release --target x86_64-apple-darwin + + - name: Build macOS aarch64 Wheels + run: maturin build --features="accelerate" --release --target aarch64-apple-darwin + + - name: Upload Wheel Artifacts + uses: actions/upload-artifact@v3 + with: + name: macos-wheels + path: target/wheels diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 7ab74e3..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,89 +0,0 @@ -name: run tests - -on: - push: - paths-ignore: - - '.pre-commit-config.yaml' - branches: - - main - - dev - pull_request: - paths-ignore: - - '.pre-commit-config.yaml' - branches: - - main - - dev -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - python-version: [3.7] - os: [ubuntu-latest, windows-latest, macos-latest] - fail-fast: false - steps: - - name: checkout - uses: actions/checkout@v2 - with: - submodules: true - - name: setup python - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} - - name: Install nightly-2021-11-02 - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly-2021-11-02 - default: true - - if: startsWith(matrix.os, 'ubuntu') - run: sudo apt install libopenblas-dev libgfortran-9-dev libceres-dev - - if: startsWith(matrix.os, 'macOS') - run: brew install ceres-solver eigen lapack - - name: Make space for Windows stuff - run: Remove-Item -Recurse "C:\Program Files\dotnet" && Remove-Item -Recurse C:\Android - if: startsWith(matrix.os, 'Windows') - - if: startsWith(matrix.os, 'Windows') - run: cargo install cargo-vcpkg && vcpkg install ceres:x64-windows-static-md eigen3:x64-windows-static-md openblas:x64-windows-static-md glog:x64-windows-static-md gflags:x64-windows-static-md clapack:x64-windows-static-md && vcpkg integrate install - - name: Build macOS wheels - run: pip install maturin && maturin build --cargo-extra-args="--no-default-features --features python,accelerate,ceres/static,mimalloc/local_dynamic_tls" --release --no-sdist && pip install --no-index --find-links=target/wheels bqskitrs - env: - RUST_BACKTRACE: 1 - if: startsWith(matrix.os, 'macOS') - - name: Build Windows wheels - run: pip install maturin && maturin build --cargo-extra-args="--no-default-features --features python,static,openblas-src/system,mimalloc/local_dynamic_tls" --release --no-sdist && pip install --no-index --find-links=target/wheels bqskitrs - env: - RUST_BACKTRACE: 1 - if: startsWith(matrix.os, 'Windows') - - name: Build Linux wheel - run: pip install . - env: - RUST_BACKTRACE: 1 - if: startsWith(matrix.os, 'ubuntu') - - name: install twine and wheel - run: pip install twine wheel - - name: Remove target/ to prepare for building manylinux wheels - run: rm -rf target - if: startsWith(matrix.os, 'ubuntu') - - name: Build manylinux wheels - run: docker run -e OPENBLAS_ARGS="DYNAMIC_ARCH=1" --rm -v $(pwd):/io ethanhs/maturin-manylinux-2014:0.3 build --cargo-extra-args="--no-default-features --features python,static,mimalloc/local_dynamic_tls" --release --manylinux 2014 --no-sdist --skip-auditwheel - env: - RUST_BACKTRACE: 1 - if: startsWith(matrix.os, 'ubuntu') - - name: upload wheel artifacts - uses: actions/upload-artifact@v1 - with: - name: bqskitrs-wheels-${{matrix.os}} - path: target/wheels - - name: Publish bqskitrs to PyPI - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.pypi_password }} - run: twine upload bqskitrs/target/wheels/* - if: contains(github.event.head_commit.message, '[bqskitrs pypi]') && startsWith(matrix.os, 'macOS') - - name: Remove target/wheels to clear some space - run: rm -rf target/wheels - if: startsWith(matrix.os, 'macOS') - - name: Remove target/wheels to clear some space - run: rmdir /s /q target\wheels - shell: cmd - if: startsWith(matrix.os, 'Windows') diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml deleted file mode 100644 index 8a2c738..0000000 --- a/.github/workflows/pre-commit.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: pre-commit - -on: - pull_request: - push: - branches: [main] - -jobs: - pre-commit: - runs-on: ubuntu-latest - steps: - - name: checkout - uses: actions/checkout@v2 - with: - submodules: true - - name: setup python - uses: actions/setup-python@v1 - with: - python-version: 3.7 - - name: Install nightly-2021-11-02 - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly-2021-11-02 - default: true - components: rustfmt, clippy - - run: sudo apt install libopenblas-dev libgfortran-9-dev libeigen3-dev - - name: set PY - run: echo "name=PY::$(python -c 'import hashlib, sys;print(hashlib.sha256(sys.version.encode()+sys.executable.encode()).hexdigest())')" >> $GITHUB_ENV - - uses: actions/cache@v1 - with: - path: ~/.cache/pre-commit - key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }} - - uses: pre-commit/action@v1.1.0 diff --git a/ceres/ceres-sys/build.rs b/ceres/ceres-sys/build.rs index a89054f..c5849cd 100644 --- a/ceres/ceres-sys/build.rs +++ b/ceres/ceres-sys/build.rs @@ -13,7 +13,7 @@ fn main() { #[cfg(target_os = "windows")] let mut b = cxx_build::bridge("src/solve_silent.rs"); #[cfg(target_os = "windows")] - b.flag_if_supported("/std:c++14") + b.flag_if_supported("/std:c++17") .includes( ceres .include_paths @@ -23,6 +23,7 @@ fn main() { .map(|include| include.to_str().unwrap()), ) .include("src") + .define("GLOG_NO_ABBREVIATED_SEVERITIES", "ON") .compile("autocxx-ceres"); #[cfg(target_os = "windows")] println!("cargo:rerun-if-changed=src/lib.rs"); @@ -34,10 +35,11 @@ fn main() { } else { let ceres = Config::new("ceres-solver") .define("EXPORT_BUILD_DIR", "ON") - .define("NO_THREADS", "ON") + .define("CERES_THREADING_MODEL", "NO_THREADS") .define("BUILD_TESTING", "OFF") .define("BUILD_BENCHMARKS", "OFF") .define("MINIGLOG", "ON") + .define("GFLAGS", "OFF") .define("LAPACK", "OFF") .define("CUSTOM_BLAS", "OFF") .define("SCHUR_SPECIALIZATIONS", "OFF") @@ -65,7 +67,7 @@ fn main() { let targetinclude = std::path::PathBuf::from(format!("{}/include", ceres.display())); // let compiledlib = std::path::PathBuf::from(format!("{}/lib/{}.a", ceres.display(), lib_name)); let mut b = cxx_build::bridge("src/solve_silent.rs"); - b.flag_if_supported("-std=c++14") + b.flag_if_supported("-std=c++17") .flag_if_supported("-Wno-unused-parameter") .include(sysinclude3) .include(sysinclude) @@ -86,7 +88,7 @@ fn main() { let localinclude3 = std::path::PathBuf::from("/usr/local/include/eigen3"); let localinclude = std::path::PathBuf::from("/usr/local/include/eigen"); let mut b = cxx_build::bridge("src/solve_silent.rs"); - b.flag_if_supported("-std=c++14") + b.flag_if_supported("-std=c++17") .flag_if_supported("-Wno-unused-parameter") .include(sysinclude3) .include(sysinclude) diff --git a/pyproject.toml b/pyproject.toml index 3b349ca..ecaa8f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,18 +8,19 @@ build-backend = "maturin" [project] name = "bqskitrs" -version = "0.3.0" +version = "0.3.0.rc1" maintainers = [ {name = "Ethan Smith", email = "ethanhs@lbl.gov"}, {name = "Ed Younis", email = "edyounis@lbl.gov"}, ] license = { file = "LICENSE" } readme = "README.md" -requires-python = ">=3.7" +requires-python = ">=3.8" [project.urls] Source = "https://github.com/bqskit/bqskitrs" [tool.maturin] -manylinux = "off" +compatibility = "off" bindings = "pyo3" +features = ["openblas"] From cebf0b61f4c143aeef81cc3d89e60da3a5060f7a Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Fri, 21 Oct 2022 07:49:37 -0700 Subject: [PATCH 04/20] Qudits and Performance Improvements --- src/ir/circuit.rs | 40 +++--- src/qis/unitary/builder.rs | 247 ++++++++++++++++++------------------- 2 files changed, 137 insertions(+), 150 deletions(-) diff --git a/src/ir/circuit.rs b/src/ir/circuit.rs index 0c8776f..d9684d5 100644 --- a/src/ir/circuit.rs +++ b/src/ir/circuit.rs @@ -19,6 +19,7 @@ pub struct Circuit { pub cycle_boundaries: Vec<(usize, usize)>, pub num_params: usize, pub sendable: bool, + pub dim: usize, } impl Circuit { @@ -42,6 +43,7 @@ impl Circuit { current_cycle += 1; } } + let dim: usize = radixes.iter().product(); Circuit { size, radixes, @@ -50,6 +52,7 @@ impl Circuit { cycle_boundaries, num_params, sendable, + dim, } } @@ -153,20 +156,11 @@ impl Gradient for Circuit { let mut left = UnitaryBuilder::new(self.size, self.radixes.clone()); let mut right = UnitaryBuilder::new(self.size, self.radixes.clone()); let mut full_grads = Vec::with_capacity(num_grads); - // ///////////////////////////////////////////////////////////// - // let grad_dim: usize = self.radixes.iter().product(); - // let mut out_grad = Array3::zeros(( - // num_grads, - // grad_dim, - // grad_dim, - // )); - let mut out_grad = Array3::zeros(( num_grads, - 2usize.pow(self.size as u32), - 2usize.pow(self.size as u32), + self.dim, + self.dim, )); - // ///////////////////////////////////////////////////////////// for (m, location) in matrices.iter().zip(locations.iter()) { right.apply_right(m.view(), location, false); @@ -174,22 +168,22 @@ impl Gradient for Circuit { for (m, location, d_m) in izip!(matrices, locations, grads) { // ///////////////////////////////////////////////////////////// - let perm = calc_permutation_matrix(self.size, (*location).clone()); - let perm_t = perm.t(); - let id = Array2::eye(2usize.pow((self.size - location.len()) as u32)); + // let perm = calc_permutation_matrix(self.size, (*location).clone()); + // let perm_t = perm.t(); + // let id = Array2::eye(2usize.pow((self.size - location.len()) as u32)); // ///////////////////////////////////////////////////////////// - right.apply_left(m.view(), location, true); + right.apply_left(m.conj().t(), location, false); let right_utry = right.get_utry(); - let left_utry = left.get_utry(); + // let left_utry = left.get_utry(); for grad in d_m.outer_iter() { - // let left_grad = left.eval_apply_right(grad.view(), location); - // full_grads.push(right_utry.matmul(left_grad.view())); - let mut full_grad = grad.kron(&id); - full_grad = perm.matmul(full_grad.view()); - full_grad = full_grad.matmul(perm_t); - let right_grad = right_utry.matmul(full_grad.view()); - full_grads.push(right_grad.matmul(left_utry.view())); + let left_grad = left.eval_apply_right(grad.view(), location); + full_grads.push(right_utry.dot(&left_grad.view())); + // let mut full_grad = grad.kron(&id); + // full_grad = perm.matmul(full_grad.view()); + // full_grad = full_grad.matmul(perm_t); + // let right_grad = right_utry.matmul(full_grad.view()); + // full_grads.push(right_grad.matmul(left_utry.view())); } left.apply_right(m.view(), location, false); } diff --git a/src/qis/unitary/builder.rs b/src/qis/unitary/builder.rs index 0295af7..1f7c0f1 100644 --- a/src/qis/unitary/builder.rs +++ b/src/qis/unitary/builder.rs @@ -2,32 +2,39 @@ use ndarray::{Array2, ArrayD, ArrayView2, Ix2}; use ndarray_linalg::c64; use crate::utils::{argsort, trace}; -use crate::squaremat::*; +use itertools::Itertools; /// A type to build unitaries using tensor networks pub struct UnitaryBuilder { - size: usize, + num_qudits: usize, + num_idxs: usize, + dim: usize, + pi: Vec, radixes: Vec, tensor: Option>, - dim: usize, } impl UnitaryBuilder { - pub fn new(size: usize, radixes: Vec) -> Self { + pub fn new(num_qudits: usize, radixes: Vec) -> Self { let dim = radixes.iter().product(); + let num_idxs = num_qudits * 2; + let pi = Vec::from_iter(0..num_qudits*2); let mut tensor = Array2::::eye(dim).into_dyn(); tensor = tensor .into_shape([&radixes[..], &radixes[..]].concat()) .unwrap(); UnitaryBuilder { - size, + num_qudits, + num_idxs, + dim, + pi, radixes, tensor: Some(tensor), - dim, } } - pub fn get_utry(&self) -> Array2 { + pub fn get_utry(&mut self) -> Array2 { + self.permute_idxs(&Vec::from_iter(0..self.num_idxs)); match &self.tensor { Some(t) => t .to_shape((self.dim, self.dim)) @@ -39,162 +46,148 @@ impl UnitaryBuilder { } } + pub fn permute_idxs(&mut self, pi: &Vec) { + let composed: Vec = self.pi.iter() + .enumerate() + .sorted_by(|(_idx_a, a), (_idx_b, b)| a.cmp(b)) + .map(|(idx, _a)| pi[idx]) + .collect(); + self.tensor = Some(self.tensor.take().unwrap().permuted_axes(composed)); + } + pub fn apply_right(&mut self, utry: ArrayView2, location: &[usize], inverse: bool) { + // Permute Tensor Indicies let left_perm = location.iter(); - let mid_perm = (0..self.size).filter(|x| !location.contains(&x)); - let right_perm = (0..self.size).map(|x| x + self.size); - - let left_dim: usize = left_perm.clone().map(|i| self.radixes[*i]).product(); - let unitary = if inverse { - let conj = utry.conj(); - conj.reversed_axes() - } else { - utry.to_owned() - }; + let right_perm = (0..self.num_idxs).filter(|x| !location.contains(&x)); let mut perm = vec![]; perm.extend(left_perm); - perm.extend(mid_perm); perm.extend(right_perm); - - let permuted = self.tensor.take().unwrap().permuted_axes(perm.clone()); - let dim: usize = permuted.shape().iter().product(); - let reshaped = permuted - .to_shape((left_dim, dim / left_dim)) + self.permute_idxs(&perm); + + // Reshape + let owned_tensor = self.tensor.take().unwrap(); + let shape = owned_tensor.shape().clone(); + let left_dim: usize = shape[..location.len()].iter().product(); + let reshaped = owned_tensor + .to_shape((left_dim, self.dim * self.dim / left_dim)) .expect("Cannot reshape tensor to matrix"); - let prod = unitary.matmul(reshaped.view()); - - let radixes = [&self.radixes[..], &self.radixes[..]].concat(); - let shape: Vec = perm.iter().map(|p| radixes[*p]).collect(); + + // Apply Unitary + let prod = utry.dot(&reshaped.view()); let reshape_back = prod - .into_shape(shape) + .to_shape(shape) .expect("Failed to reshape matrix product back"); - self.tensor = Some(reshape_back.permuted_axes(argsort(perm)).into_dyn()); + self.tensor = Some(reshape_back.to_owned()); } pub fn apply_left(&mut self, utry: ArrayView2, location: &[usize], inverse: bool) { - let left_perm = 0..self.size; - let mid_perm = left_perm.clone().filter_map(|x| { - if location.contains(&x) { - None - } else { - Some(x + self.size) - } - }); - let right_perm = location.iter().map(|x| x + self.size); - - let right_dim: usize = right_perm - .clone() - .map(|i| self.radixes[i - self.size]) - .product(); - - let unitary = if inverse { - utry.conj().reversed_axes() - } else { - utry.to_owned() - }; + // Permute Tensor Indicies + let right_perm: Vec = location.iter().map(|x| x + self.num_qudits).collect(); + let left_perm = (0..self.num_idxs).filter(|x| !right_perm.contains(&x)); let mut perm = vec![]; perm.extend(left_perm); - perm.extend(mid_perm); perm.extend(right_perm); - - let permuted = self.tensor.take().unwrap().permuted_axes(perm.clone()); - let dim: usize = permuted.shape().iter().product(); - let reshaped = permuted - .to_shape((dim / right_dim, right_dim)) + self.permute_idxs(&perm); + + // Reshape + let owned_tensor = self.tensor.take().unwrap(); + let shape = owned_tensor.shape().clone(); + let right_dim: usize = shape[shape.len()-location.len()..].iter().product(); + let reshaped = owned_tensor + .to_shape((self.dim * self.dim / right_dim, right_dim)) .expect("Cannot reshape tensor to matrix"); - let prod = reshaped.view().matmul(unitary.view()); - let radixes = [&self.radixes[..], &self.radixes[..]].concat(); - let shape: Vec = perm.iter().map(|p| radixes[*p]).collect(); + + // Apply Unitary + let prod = reshaped.dot(&utry); let reshape_back = prod - .into_shape(shape) + .to_shape(shape) .expect("Failed to reshape matrix product back"); - self.tensor = Some(reshape_back.permuted_axes(argsort(perm)).into_dyn()); + self.tensor = Some(reshape_back.to_owned()); } - pub fn eval_apply_right(&self, m: ArrayView2, location: &[usize]) -> Array2 { + pub fn eval_apply_right(&mut self, m: ArrayView2, location: &[usize]) -> Array2 { + // Permute Tensor Indicies let left_perm = location.iter(); - let mid_perm = (0..self.size).filter(|x| !location.contains(&x)); - let right_perm = (0..self.size).map(|x| x + self.size); - - let left_dim: usize = left_perm.clone().map(|i| self.radixes[*i]).product(); - - let matrix = m.to_owned(); - + let right_perm = (0..self.num_idxs).filter(|x| !location.contains(&x)); let mut perm = vec![]; perm.extend(left_perm); - perm.extend(mid_perm); perm.extend(right_perm); + self.permute_idxs(&perm); - let mut tensor_copy = self.tensor.clone(); - let permuted = tensor_copy.take().unwrap().permuted_axes(perm.clone()); - let dim: usize = permuted.shape().iter().product(); - let reshaped = permuted - .to_shape((left_dim, dim / left_dim)) - .expect("Cannot reshape tensor to matrix"); - let prod = matrix.matmul(reshaped.view()); - - let radixes = [&self.radixes[..], &self.radixes[..]].concat(); - let shape: Vec = perm.iter().map(|p| radixes[*p]).collect(); - let reshape_back = prod - .into_shape(shape) - .expect("Failed to reshape matrix product back"); - let eval_tensor = reshape_back.permuted_axes(argsort(perm)).into_dyn(); - let matrix_dim = self.dim.clone(); - let eval_matrix = eval_tensor - .to_shape((matrix_dim, matrix_dim)) + // Copy and Reshape + let copied_tensor = match &self.tensor { + Some(t) => t.clone(), + None => panic!("Tensor was unexpectedly None."), + }; + let shape = copied_tensor.shape().clone(); + let left_dim: usize = shape[..location.len()].iter().product(); + let reshaped = copied_tensor + .to_shape((left_dim, self.dim * self.dim / left_dim)) .expect("Cannot reshape tensor to matrix"); - eval_matrix.to_owned() + + // Apply matrix and reshuffle back to a unitary + let normal = Vec::from_iter(0..self.num_idxs); + let composed: Vec = self.pi.iter() + .enumerate() + .sorted_by(|(_idx_a, a), (_idx_b, b)| a.cmp(b)) + .map(|(idx, _a)| normal[idx]) + .collect(); + m.dot(&reshaped.view()) + .to_shape(shape) + .unwrap() + .permuted_axes(composed) + .to_shape((self.dim, self.dim)) + .unwrap() + .into_dimensionality::() + .unwrap() + .to_owned() } - pub fn eval_apply_left(&self, m: ArrayView2, location: &[usize]) -> Array2 { - let left_perm = 0..self.size; - let mid_perm = left_perm.clone().filter_map(|x| { - if location.contains(&x) { - None - } else { - Some(x + self.size) - } - }); - let right_perm = location.iter().map(|x| x + self.size); - - let right_dim: usize = right_perm - .clone() - .map(|i| self.radixes[i - self.size]) - .product(); - + pub fn eval_apply_left(&mut self, m: ArrayView2, location: &[usize]) -> Array2 { + // Permute Tensor Indicies + let right_perm: Vec = location.iter().map(|x| x + self.num_qudits).collect(); + let left_perm = (0..self.num_idxs).filter(|x| !right_perm.contains(&x)); let mut perm = vec![]; perm.extend(left_perm); - perm.extend(mid_perm); perm.extend(right_perm); + self.permute_idxs(&perm); - let mut tensor_copy = self.tensor.clone(); - let permuted = tensor_copy.take().unwrap().permuted_axes(perm.clone()); - let dim: usize = permuted.shape().iter().product(); - let reshaped = permuted - .to_shape((dim / right_dim, right_dim)) - .expect("Cannot reshape tensor to matrix"); - let prod = reshaped.view().matmul(m.view()); - - let radixes = [&self.radixes[..], &self.radixes[..]].concat(); - let shape: Vec = perm.iter().map(|p| radixes[*p]).collect(); - let reshape_back = prod - .into_shape(shape) - .expect("Failed to reshape matrix product back"); - let eval_tensor = reshape_back.permuted_axes(argsort(perm)).into_dyn(); - let matrix_dim = self.dim.clone(); - let eval_matrix = eval_tensor - .to_shape((matrix_dim, matrix_dim)) + // Copy and Reshape + let copied_tensor = match &self.tensor { + Some(t) => t.clone(), + None => panic!("Tensor was unexpectedly None."), + }; + let shape = copied_tensor.shape().clone(); + let right_dim: usize = shape[shape.len()-location.len()..].iter().product(); + let reshaped = copied_tensor + .to_shape((self.dim * self.dim / right_dim, right_dim)) .expect("Cannot reshape tensor to matrix"); - eval_matrix.to_owned() + + // Apply Unitary + let normal = Vec::from_iter(0..self.num_idxs); + let composed: Vec = self.pi.iter() + .enumerate() + .sorted_by(|(_idx_a, a), (_idx_b, b)| a.cmp(b)) + .map(|(idx, _a)| normal[idx]) + .collect(); + reshaped.dot(&m) + .to_shape(shape) + .unwrap() + .permuted_axes(composed) + .to_shape((self.dim, self.dim)) + .unwrap() + .into_dimensionality::() + .unwrap() + .to_owned() } pub fn calc_env_matrix(&self, location: &[usize]) -> Array2 { - let mut left_perm: Vec = (0..self.size).filter(|x| !location.contains(x)).collect(); + let mut left_perm: Vec = (0..self.num_qudits).filter(|x| !location.contains(x)).collect(); let left_perm_copy = left_perm.clone(); - let left_extension = left_perm_copy.iter().map(|x| x + self.size); + let left_extension = left_perm_copy.iter().map(|x| x + self.num_qudits); left_perm.extend(left_extension); let mut right_perm = location.to_owned(); - right_perm.extend(location.iter().map(|x| x + self.size)); + right_perm.extend(location.iter().map(|x| x + self.num_qudits)); let mut perm = vec![]; perm.append(&mut left_perm); @@ -205,8 +198,8 @@ impl UnitaryBuilder { }; let reshaped = a .to_shape([ - 2usize.pow(self.size as u32 - location.len() as u32), - 2usize.pow(self.size as u32 - location.len() as u32), + 2usize.pow(self.num_qudits as u32 - location.len() as u32), + 2usize.pow(self.num_qudits as u32 - location.len() as u32), 2usize.pow(location.len() as u32), 2usize.pow(location.len() as u32), ]) From 94debf31277385950633a74896f1c50d441d3048 Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Thu, 27 Oct 2022 06:44:28 -0700 Subject: [PATCH 05/20] Bug Fix and Performance Improvement --- Cargo.toml | 2 +- build.rs | 35 ++++++++++ src/ir/circuit.rs | 70 +++++++++++++------- src/lib.rs | 11 ---- src/qis/unitary/builder.rs | 132 +++++++++++-------------------------- 5 files changed, 121 insertions(+), 129 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 86ee588..6d367d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,4 +72,4 @@ x86_64-pc-windows-msvc = { triplet="x64-windows-static-md", install=[ "glog", "gflags", "clapack", -] } \ No newline at end of file +] } diff --git a/build.rs b/build.rs index 9f160a5..ebfcf99 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,7 @@ +use std::fs; +use std::path::PathBuf; +use std::process::Command; + fn main() -> Result<(), std::io::Error> { // Homebrew/macOS gcc don't add libgfortran to the rpath, // so we manually go prodding around for it here @@ -9,5 +13,36 @@ fn main() -> Result<(), std::io::Error> { .unwrap(); println!("cargo:rustc-link-lib=static=lapack"); } + if cfg!(target_os = "macos") { + // First, we get brew prefix, if installed. Fall back to system gcc. + let prefix = String::from_utf8( + Command::new("brew") + .arg("--prefix") + .output() + .expect("Failed to run brew --prefix") + .stdout, + ) + .unwrap() + .replace('\n', ""); + let mut gcc_dir = PathBuf::new(); + gcc_dir.push(prefix.clone()); + + gcc_dir = fs::read_dir(gcc_dir.join("Cellar/gcc"))? + .map(|res| res.map(|e| e.path())) + .filter_map(Result::ok) + .last() + .expect("No gcc installed?"); + gcc_dir = gcc_dir.join("lib/gcc"); + let version_dir = fs::read_dir(gcc_dir)? + .map(|res| res.map(|e| e.path())) + .filter_map(Result::ok) + .last() + .expect("No directories in prefix?"); + let mut openblas_dir = PathBuf::new(); + openblas_dir.push(prefix); + openblas_dir.push("opt/openblas/lib"); + println!("cargo:rustc-link-search={}", version_dir.to_str().unwrap()); + println!("cargo:rustc-link-search={}", openblas_dir.to_str().unwrap()); + } Ok(()) } \ No newline at end of file diff --git a/src/ir/circuit.rs b/src/ir/circuit.rs index d9684d5..a75cd86 100644 --- a/src/ir/circuit.rs +++ b/src/ir/circuit.rs @@ -1,9 +1,10 @@ use crate::qis::unitary::UnitaryBuilder; use super::gates::{Gate, Gradient, Unitary}; use super::Operation; +use itertools::Itertools; use itertools::izip; -use ndarray::{Array2, Array3}; +use ndarray::{Array2, Array3, ArrayD, ArrayView2, Ix2}; use ndarray_linalg::c64; use crate::squaremat::*; use crate::permutation_matrix::calc_permutation_matrix; @@ -155,41 +156,64 @@ impl Gradient for Circuit { let mut left = UnitaryBuilder::new(self.size, self.radixes.clone()); let mut right = UnitaryBuilder::new(self.size, self.radixes.clone()); - let mut full_grads = Vec::with_capacity(num_grads); let mut out_grad = Array3::zeros(( num_grads, self.dim, self.dim, )); + let mut out_iter = out_grad.outer_iter_mut(); for (m, location) in matrices.iter().zip(locations.iter()) { right.apply_right(m.view(), location, false); } for (m, location, d_m) in izip!(matrices, locations, grads) { - // ///////////////////////////////////////////////////////////// - // let perm = calc_permutation_matrix(self.size, (*location).clone()); - // let perm_t = perm.t(); - // let id = Array2::eye(2usize.pow((self.size - location.len()) as u32)); - // ///////////////////////////////////////////////////////////// - - right.apply_left(m.conj().t(), location, false); - let right_utry = right.get_utry(); - // let left_utry = left.get_utry(); - for grad in d_m.outer_iter() { - let left_grad = left.eval_apply_right(grad.view(), location); - full_grads.push(right_utry.dot(&left_grad.view())); - // let mut full_grad = grad.kron(&id); - // full_grad = perm.matmul(full_grad.view()); - // full_grad = full_grad.matmul(perm_t); - // let right_grad = right_utry.matmul(full_grad.view()); - // full_grads.push(right_grad.matmul(left_utry.view())); - } + // 1. Permute and reshape right tensor for location + let right_perm: Vec = location.iter().map(|x| x + right.num_qudits).collect(); + let left_perm = (0..right.num_idxs).filter(|x| !right_perm.contains(&x)); + let mut perm = vec![]; + perm.extend(left_perm); + perm.extend(right_perm); + right.permute_idxs(perm); + + let owned_tensor = right.tensor.take().unwrap(); + let shape = right.get_current_shape(); + let right_dim: usize = shape[shape.len()-location.len()..].iter().product(); + let reshaped = owned_tensor + .to_shape((right.dim * right.dim / right_dim, right_dim)) + .expect("Cannot reshape tensor to matrix"); + + // Apply inverse gate to the left of the right tensor + let prod = reshaped.dot(&m.conj().t()); + let reshape_back = prod + .into_shape(shape.clone()) + .expect("Failed to reshape matrix product back"); + right.tensor = Some(reshape_back.to_owned()); + + // Store left unitary and then apply gate to left on right + let left_utry = left.get_utry(); left.apply_right(m.view(), location, false); - } - for (mut arr, grad) in out_grad.outer_iter_mut().zip(full_grads) { - arr.assign(&grad); + // Compute un-permute order + let revert: Vec = right.pi.clone().iter() + .enumerate() + .sorted_by(|(_idx_a, a), (_idx_b, b)| a.cmp(b)) + .map(|(idx, _a)| idx) + .collect(); + + // Calculate all gradients + for grad in d_m.outer_iter() { + let right_grad = reshaped.dot(&grad.view()) + .into_shape(shape.clone()) + .expect("Failed to reshape matrix product back") + .permuted_axes(revert.clone()); + let reshaped_grad = right_grad.to_shape((right.dim, right.dim)) + .unwrap() + .into_dimensionality::() + .unwrap(); + let full_grad = reshaped_grad.dot(&left_utry.view()); + out_iter.next().unwrap().assign(&full_grad); + } } (left.get_utry(), out_grad) diff --git a/src/lib.rs b/src/lib.rs index 3298382..055ef8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,17 +22,6 @@ extern crate intel_mkl_src; #[link(name = "openblas")] extern "C" {} -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = CNOTfn{}; - println!("{:?}", result.get_utry(&[0.0])); - } -} - #[macro_export] macro_rules! c { ($re:expr, $im:expr) => { diff --git a/src/qis/unitary/builder.rs b/src/qis/unitary/builder.rs index 1f7c0f1..2e0fb81 100644 --- a/src/qis/unitary/builder.rs +++ b/src/qis/unitary/builder.rs @@ -2,23 +2,24 @@ use ndarray::{Array2, ArrayD, ArrayView2, Ix2}; use ndarray_linalg::c64; use crate::utils::{argsort, trace}; +use crate::squaremat::*; use itertools::Itertools; /// A type to build unitaries using tensor networks pub struct UnitaryBuilder { - num_qudits: usize, - num_idxs: usize, - dim: usize, - pi: Vec, - radixes: Vec, - tensor: Option>, + pub num_qudits: usize, + pub num_idxs: usize, + pub dim: usize, + pub pi: Vec, + pub radixes: Vec, + pub tensor: Option>, } impl UnitaryBuilder { pub fn new(num_qudits: usize, radixes: Vec) -> Self { let dim = radixes.iter().product(); let num_idxs = num_qudits * 2; - let pi = Vec::from_iter(0..num_qudits*2); + let pi = Vec::from_iter(0..num_idxs); let mut tensor = Array2::::eye(dim).into_dyn(); tensor = tensor .into_shape([&radixes[..], &radixes[..]].concat()) @@ -34,7 +35,7 @@ impl UnitaryBuilder { } pub fn get_utry(&mut self) -> Array2 { - self.permute_idxs(&Vec::from_iter(0..self.num_idxs)); + self.reset_idxs(); match &self.tensor { Some(t) => t .to_shape((self.dim, self.dim)) @@ -46,13 +47,30 @@ impl UnitaryBuilder { } } - pub fn permute_idxs(&mut self, pi: &Vec) { - let composed: Vec = self.pi.iter() + pub fn permute_idxs(&mut self, pi: Vec) { + let revert: Vec = self.pi.iter() .enumerate() .sorted_by(|(_idx_a, a), (_idx_b, b)| a.cmp(b)) - .map(|(idx, _a)| pi[idx]) + .map(|(idx, _a)| idx) .collect(); + let composed: Vec = pi.iter().map(|&x| revert[x]).collect(); self.tensor = Some(self.tensor.take().unwrap().permuted_axes(composed)); + self.pi = pi; + } + + pub fn reset_idxs(&mut self) { + self.permute_idxs(Vec::from_iter(0..self.num_idxs)); + } + + pub fn get_current_shape(&self) -> Vec { + self.pi.iter().map( + |&x| + match x { + r if r < self.num_qudits => self.radixes[x], + r if r >= self.num_qudits => self.radixes[x - self.num_qudits], + _ => panic!("Tensor index is invalid."), + } + ).collect() } pub fn apply_right(&mut self, utry: ArrayView2, location: &[usize], inverse: bool) { @@ -62,11 +80,11 @@ impl UnitaryBuilder { let mut perm = vec![]; perm.extend(left_perm); perm.extend(right_perm); - self.permute_idxs(&perm); + self.permute_idxs(perm); // Reshape let owned_tensor = self.tensor.take().unwrap(); - let shape = owned_tensor.shape().clone(); + let shape = self.get_current_shape(); let left_dim: usize = shape[..location.len()].iter().product(); let reshaped = owned_tensor .to_shape((left_dim, self.dim * self.dim / left_dim)) @@ -75,7 +93,7 @@ impl UnitaryBuilder { // Apply Unitary let prod = utry.dot(&reshaped.view()); let reshape_back = prod - .to_shape(shape) + .into_shape(shape) .expect("Failed to reshape matrix product back"); self.tensor = Some(reshape_back.to_owned()); } @@ -87,11 +105,12 @@ impl UnitaryBuilder { let mut perm = vec![]; perm.extend(left_perm); perm.extend(right_perm); - self.permute_idxs(&perm); + self.permute_idxs(perm); // Reshape let owned_tensor = self.tensor.take().unwrap(); - let shape = owned_tensor.shape().clone(); + // let shape = owned_tensor.shape().clone(); + let shape = self.get_current_shape(); let right_dim: usize = shape[shape.len()-location.len()..].iter().product(); let reshaped = owned_tensor .to_shape((self.dim * self.dim / right_dim, right_dim)) @@ -100,88 +119,13 @@ impl UnitaryBuilder { // Apply Unitary let prod = reshaped.dot(&utry); let reshape_back = prod - .to_shape(shape) + .into_shape(shape) .expect("Failed to reshape matrix product back"); self.tensor = Some(reshape_back.to_owned()); } - pub fn eval_apply_right(&mut self, m: ArrayView2, location: &[usize]) -> Array2 { - // Permute Tensor Indicies - let left_perm = location.iter(); - let right_perm = (0..self.num_idxs).filter(|x| !location.contains(&x)); - let mut perm = vec![]; - perm.extend(left_perm); - perm.extend(right_perm); - self.permute_idxs(&perm); - - // Copy and Reshape - let copied_tensor = match &self.tensor { - Some(t) => t.clone(), - None => panic!("Tensor was unexpectedly None."), - }; - let shape = copied_tensor.shape().clone(); - let left_dim: usize = shape[..location.len()].iter().product(); - let reshaped = copied_tensor - .to_shape((left_dim, self.dim * self.dim / left_dim)) - .expect("Cannot reshape tensor to matrix"); - - // Apply matrix and reshuffle back to a unitary - let normal = Vec::from_iter(0..self.num_idxs); - let composed: Vec = self.pi.iter() - .enumerate() - .sorted_by(|(_idx_a, a), (_idx_b, b)| a.cmp(b)) - .map(|(idx, _a)| normal[idx]) - .collect(); - m.dot(&reshaped.view()) - .to_shape(shape) - .unwrap() - .permuted_axes(composed) - .to_shape((self.dim, self.dim)) - .unwrap() - .into_dimensionality::() - .unwrap() - .to_owned() - } - - pub fn eval_apply_left(&mut self, m: ArrayView2, location: &[usize]) -> Array2 { - // Permute Tensor Indicies - let right_perm: Vec = location.iter().map(|x| x + self.num_qudits).collect(); - let left_perm = (0..self.num_idxs).filter(|x| !right_perm.contains(&x)); - let mut perm = vec![]; - perm.extend(left_perm); - perm.extend(right_perm); - self.permute_idxs(&perm); - - // Copy and Reshape - let copied_tensor = match &self.tensor { - Some(t) => t.clone(), - None => panic!("Tensor was unexpectedly None."), - }; - let shape = copied_tensor.shape().clone(); - let right_dim: usize = shape[shape.len()-location.len()..].iter().product(); - let reshaped = copied_tensor - .to_shape((self.dim * self.dim / right_dim, right_dim)) - .expect("Cannot reshape tensor to matrix"); - - // Apply Unitary - let normal = Vec::from_iter(0..self.num_idxs); - let composed: Vec = self.pi.iter() - .enumerate() - .sorted_by(|(_idx_a, a), (_idx_b, b)| a.cmp(b)) - .map(|(idx, _a)| normal[idx]) - .collect(); - reshaped.dot(&m) - .to_shape(shape) - .unwrap() - .permuted_axes(composed) - .to_shape((self.dim, self.dim)) - .unwrap() - .into_dimensionality::() - .unwrap() - .to_owned() - } - - pub fn calc_env_matrix(&self, location: &[usize]) -> Array2 { + pub fn calc_env_matrix(&mut self, location: &[usize]) -> Array2 { + self.reset_idxs(); let mut left_perm: Vec = (0..self.num_qudits).filter(|x| !location.contains(x)).collect(); let left_perm_copy = left_perm.clone(); let left_extension = left_perm_copy.iter().map(|x| x + self.num_qudits); From d1ff23dd239820480f216cb1dcb1238ad4272f11 Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Thu, 27 Oct 2022 06:47:09 -0700 Subject: [PATCH 06/20] Changed actions on --- .github/workflows/build-wheels.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index ffbbec1..c97b5fd 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -1,8 +1,8 @@ name: Build Wheels -on: - release: - types: [published] +on: [push] + # release: + # types: [published] env: CARGO_TERM_COLOR: always From 661647229a80771ef05f11062c151185b9a2bd5f Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Fri, 28 Oct 2022 10:43:30 -0700 Subject: [PATCH 07/20] Bug fix --- src/ir/circuit.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ir/circuit.rs b/src/ir/circuit.rs index a75cd86..3491874 100644 --- a/src/ir/circuit.rs +++ b/src/ir/circuit.rs @@ -186,7 +186,7 @@ impl Gradient for Circuit { // Apply inverse gate to the left of the right tensor let prod = reshaped.dot(&m.conj().t()); let reshape_back = prod - .into_shape(shape.clone()) + .to_shape(shape.clone()) .expect("Failed to reshape matrix product back"); right.tensor = Some(reshape_back.to_owned()); @@ -203,7 +203,7 @@ impl Gradient for Circuit { // Calculate all gradients for grad in d_m.outer_iter() { - let right_grad = reshaped.dot(&grad.view()) + let right_grad = prod.dot(&grad.view()) .into_shape(shape.clone()) .expect("Failed to reshape matrix product back") .permuted_axes(revert.clone()); From ef971da81e4c8a18150088f8a5f53fc2474624e1 Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Fri, 28 Oct 2022 11:53:08 -0700 Subject: [PATCH 08/20] Update --- src/ir/circuit.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/ir/circuit.rs b/src/ir/circuit.rs index 3491874..241c2db 100644 --- a/src/ir/circuit.rs +++ b/src/ir/circuit.rs @@ -168,7 +168,7 @@ impl Gradient for Circuit { } for (m, location, d_m) in izip!(matrices, locations, grads) { - // 1. Permute and reshape right tensor for location + // Permute and reshape right tensor for location let right_perm: Vec = location.iter().map(|x| x + right.num_qudits).collect(); let left_perm = (0..right.num_idxs).filter(|x| !right_perm.contains(&x)); let mut perm = vec![]; @@ -185,10 +185,6 @@ impl Gradient for Circuit { // Apply inverse gate to the left of the right tensor let prod = reshaped.dot(&m.conj().t()); - let reshape_back = prod - .to_shape(shape.clone()) - .expect("Failed to reshape matrix product back"); - right.tensor = Some(reshape_back.to_owned()); // Store left unitary and then apply gate to left on right let left_utry = left.get_utry(); @@ -214,6 +210,12 @@ impl Gradient for Circuit { let full_grad = reshaped_grad.dot(&left_utry.view()); out_iter.next().unwrap().assign(&full_grad); } + + // Reshape the right tensor back + let reshape_back = prod + .into_shape(shape.clone()) + .expect("Failed to reshape matrix product back"); + right.tensor = Some(reshape_back.to_owned()); } (left.get_utry(), out_grad) From 4da9cf7f492da10569312e598a1b66e29aa53ad5 Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Fri, 28 Oct 2022 13:34:05 -0700 Subject: [PATCH 09/20] Update --- src/ir/circuit.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ir/circuit.rs b/src/ir/circuit.rs index 241c2db..2e26175 100644 --- a/src/ir/circuit.rs +++ b/src/ir/circuit.rs @@ -156,12 +156,12 @@ impl Gradient for Circuit { let mut left = UnitaryBuilder::new(self.size, self.radixes.clone()); let mut right = UnitaryBuilder::new(self.size, self.radixes.clone()); + let mut full_grads = Vec::with_capacity(num_grads); let mut out_grad = Array3::zeros(( num_grads, self.dim, self.dim, )); - let mut out_iter = out_grad.outer_iter_mut(); for (m, location) in matrices.iter().zip(locations.iter()) { right.apply_right(m.view(), location, false); @@ -207,8 +207,7 @@ impl Gradient for Circuit { .unwrap() .into_dimensionality::() .unwrap(); - let full_grad = reshaped_grad.dot(&left_utry.view()); - out_iter.next().unwrap().assign(&full_grad); + full_grads.push(reshaped_grad.dot(&left_utry.view())); } // Reshape the right tensor back @@ -218,6 +217,10 @@ impl Gradient for Circuit { right.tensor = Some(reshape_back.to_owned()); } + for (mut arr, grad) in out_grad.outer_iter_mut().zip(full_grads) { + arr.assign(&grad); + } + (left.get_utry(), out_grad) } From 5bba11dc651a06851db96e864c4b3931299728f3 Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Fri, 28 Oct 2022 14:59:40 -0700 Subject: [PATCH 10/20] Fixed Version Tag --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ecaa8f4..8ce6623 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "maturin" [project] name = "bqskitrs" -version = "0.3.0.rc1" +version = "0.3.0rc1" maintainers = [ {name = "Ethan Smith", email = "ethanhs@lbl.gov"}, {name = "Ed Younis", email = "edyounis@lbl.gov"}, From ed71626d7df99b57307588e75bffa27b63d83821 Mon Sep 17 00:00:00 2001 From: Alon Kukliansky Date: Mon, 12 Dec 2022 08:48:18 -0800 Subject: [PATCH 11/20] Bug Fix: not ignoring inverse paramter --- src/qis/unitary/builder.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/qis/unitary/builder.rs b/src/qis/unitary/builder.rs index 2e0fb81..457358a 100644 --- a/src/qis/unitary/builder.rs +++ b/src/qis/unitary/builder.rs @@ -91,7 +91,11 @@ impl UnitaryBuilder { .expect("Cannot reshape tensor to matrix"); // Apply Unitary - let prod = utry.dot(&reshaped.view()); + let prod = if inverse { + utry.conj().reversed_axes().dot(&reshaped.view()) + } else { + utry.dot(&reshaped.view()) + }; let reshape_back = prod .into_shape(shape) .expect("Failed to reshape matrix product back"); @@ -117,7 +121,11 @@ impl UnitaryBuilder { .expect("Cannot reshape tensor to matrix"); // Apply Unitary - let prod = reshaped.dot(&utry); + let prod = if inverse { + reshaped.dot(&utry.conj().reversed_axes()) + } else { + reshaped.dot(&utry) + }; let reshape_back = prod .into_shape(shape) .expect("Failed to reshape matrix product back"); From 504747565ebbd8f19a46d196157bf5eb07d6a30e Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Thu, 5 Jan 2023 22:29:58 -0500 Subject: [PATCH 12/20] Build change --- ceres/ceres-sys/build.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/ceres/ceres-sys/build.rs b/ceres/ceres-sys/build.rs index c5849cd..ac0f9bd 100644 --- a/ceres/ceres-sys/build.rs +++ b/ceres/ceres-sys/build.rs @@ -88,6 +88,16 @@ fn main() { let localinclude3 = std::path::PathBuf::from("/usr/local/include/eigen3"); let localinclude = std::path::PathBuf::from("/usr/local/include/eigen"); let mut b = cxx_build::bridge("src/solve_silent.rs"); + let target = env::var("TARGET").unwrap(); + if target.contains("apple") { + println!("cargo:rustc-link-lib=dylib=c++"); + b.flag_if_supported("-stdlib=libc++"); + } else if target.contains("linux") { + println!("cargo:rustc-link-lib=dylib=stdc++"); + } else if target.contains("win") { + } else { + unimplemented!() + } b.flag_if_supported("-std=c++17") .flag_if_supported("-Wno-unused-parameter") .include(sysinclude3) @@ -100,13 +110,4 @@ fn main() { println!("cargo:rerun-if-changed=src/lib.rs"); println!("cargo:rerun-if-changed=ceres-solver"); } - let target = env::var("TARGET").unwrap(); - if target.contains("apple") { - println!("cargo:rustc-link-lib=dylib=c++"); - } else if target.contains("linux") { - println!("cargo:rustc-link-lib=dylib=stdc++"); - } else if target.contains("win") { - } else { - unimplemented!() - } } From b8ac5157e441cfd6fd89d69edb891deb9dbc96e8 Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Thu, 5 Jan 2023 22:40:04 -0500 Subject: [PATCH 13/20] Update --- ceres/ceres-sys/Cargo.toml | 2 +- ceres/ceres-sys/build.rs | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/ceres/ceres-sys/Cargo.toml b/ceres/ceres-sys/Cargo.toml index a60c9fd..060b137 100644 --- a/ceres/ceres-sys/Cargo.toml +++ b/ceres/ceres-sys/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cxx = "1.0" +cxx = "=1.0.80" [build-dependencies] cxx-build = "1.0.79" diff --git a/ceres/ceres-sys/build.rs b/ceres/ceres-sys/build.rs index ac0f9bd..c5849cd 100644 --- a/ceres/ceres-sys/build.rs +++ b/ceres/ceres-sys/build.rs @@ -88,16 +88,6 @@ fn main() { let localinclude3 = std::path::PathBuf::from("/usr/local/include/eigen3"); let localinclude = std::path::PathBuf::from("/usr/local/include/eigen"); let mut b = cxx_build::bridge("src/solve_silent.rs"); - let target = env::var("TARGET").unwrap(); - if target.contains("apple") { - println!("cargo:rustc-link-lib=dylib=c++"); - b.flag_if_supported("-stdlib=libc++"); - } else if target.contains("linux") { - println!("cargo:rustc-link-lib=dylib=stdc++"); - } else if target.contains("win") { - } else { - unimplemented!() - } b.flag_if_supported("-std=c++17") .flag_if_supported("-Wno-unused-parameter") .include(sysinclude3) @@ -110,4 +100,13 @@ fn main() { println!("cargo:rerun-if-changed=src/lib.rs"); println!("cargo:rerun-if-changed=ceres-solver"); } + let target = env::var("TARGET").unwrap(); + if target.contains("apple") { + println!("cargo:rustc-link-lib=dylib=c++"); + } else if target.contains("linux") { + println!("cargo:rustc-link-lib=dylib=stdc++"); + } else if target.contains("win") { + } else { + unimplemented!() + } } From ccb832369169a31e2e85706c161d62f3c4ab3bec Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Thu, 5 Jan 2023 22:44:16 -0500 Subject: [PATCH 14/20] Update --- ceres/ceres-sys/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ceres/ceres-sys/Cargo.toml b/ceres/ceres-sys/Cargo.toml index 060b137..2c91235 100644 --- a/ceres/ceres-sys/Cargo.toml +++ b/ceres/ceres-sys/Cargo.toml @@ -10,7 +10,7 @@ edition = "2018" cxx = "=1.0.80" [build-dependencies] -cxx-build = "1.0.79" +cxx-build = "=1.0.80" cmake = "0.1.48" [target.'cfg(target_os = "windows")'.build-dependencies] From 1303301c74c7db645390d2893c3b4415a40c93d9 Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Thu, 5 Jan 2023 22:51:05 -0500 Subject: [PATCH 15/20] Update --- .github/workflows/build-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index c97b5fd..f9615ee 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -79,7 +79,7 @@ jobs: macos: name: macOS Wheel Build - runs-on: macOS-latest + runs-on: macOS-12 steps: - name: Setup Repo uses: actions/checkout@v3 From 1b0646a8f088594a28f3965e3541ba476a01ba27 Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Wed, 18 Jan 2023 13:57:19 -0500 Subject: [PATCH 16/20] Update --- .github/workflows/build-wheels.yml | 4 ++-- ceres/ceres-sys/Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index f9615ee..b3b0bf3 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -79,7 +79,7 @@ jobs: macos: name: macOS Wheel Build - runs-on: macOS-12 + runs-on: macOS-11 steps: - name: Setup Repo uses: actions/checkout@v3 @@ -111,7 +111,7 @@ jobs: run: brew install gcc ceres-solver eigen lapack - name: Build macOS x86_64 Wheels - run: maturin build --features="accelerate" --release --target x86_64-apple-darwin + run: MACOSX_DEPLOYMENT_TARGET=11.0 maturin build --features="accelerate" --release --target x86_64-apple-darwin - name: Build macOS aarch64 Wheels run: maturin build --features="accelerate" --release --target aarch64-apple-darwin diff --git a/ceres/ceres-sys/Cargo.toml b/ceres/ceres-sys/Cargo.toml index 2c91235..650452a 100644 --- a/ceres/ceres-sys/Cargo.toml +++ b/ceres/ceres-sys/Cargo.toml @@ -7,10 +7,10 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cxx = "=1.0.80" +cxx = "1.0.80" [build-dependencies] -cxx-build = "=1.0.80" +cxx-build = "1.0.80" cmake = "0.1.48" [target.'cfg(target_os = "windows")'.build-dependencies] From b63f259124972b60292256b3406c702064aac30f Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Wed, 18 Jan 2023 16:39:08 -0500 Subject: [PATCH 17/20] Update --- .github/workflows/build-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index b3b0bf3..f710b58 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -22,7 +22,7 @@ jobs: - name: Build Manylinux Wheels run: > docker run -e OPENBLAS_ARGS="DYNAMIC_ARCH=1" --rm -v $(pwd):/io - edyounis/bqskitrs-manylinux:1.0 build --release + edyounis/bqskitrs-manylinux:1.1 build --release --features=openblas --compatibility=manylinux2014 - name: Upload Wheel Artifacts From fad56ac0320bdd30279124ecf77a5fb8107b5e9e Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Wed, 18 Jan 2023 19:44:46 -0500 Subject: [PATCH 18/20] 0.3.0rc2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8ce6623..0fc1b1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "maturin" [project] name = "bqskitrs" -version = "0.3.0rc1" +version = "0.3.0rc2" maintainers = [ {name = "Ethan Smith", email = "ethanhs@lbl.gov"}, {name = "Ed Younis", email = "edyounis@lbl.gov"}, From d5eef4c67bdc33fef409ce9374d0f46421c86e65 Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Wed, 18 Jan 2023 22:46:32 -0500 Subject: [PATCH 19/20] 0.3.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0fc1b1b..b53dd3a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "maturin" [project] name = "bqskitrs" -version = "0.3.0rc2" +version = "0.3.0" maintainers = [ {name = "Ethan Smith", email = "ethanhs@lbl.gov"}, {name = "Ed Younis", email = "edyounis@lbl.gov"}, From cef5488be9575144552fea054c4bb24dceedd654 Mon Sep 17 00:00:00 2001 From: Ed Younis Date: Thu, 19 Jan 2023 00:47:46 -0500 Subject: [PATCH 20/20] Updated Readme --- README.md | 83 +++++++++++++------------------------------------------ 1 file changed, 19 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 67e7fe0..659088f 100644 --- a/README.md +++ b/README.md @@ -15,77 +15,51 @@ this will use pre-built wheels. Sometimes wheels are not available, so you must ### Linux -First make sure the version of pip you have is at least 20.0.2. -You can check via `pip -V` and upgrade via `python3 -m pip install -U pip`. - -Next, install the dependencies. On Ubuntu 20.04 this is: - -```bash -sudo apt install libopenblas-dev libceres-dev libgfortran-9-dev libeigen3-dev gfortran cmake build-essential -``` - -Next you will want to install Rust: +It's best to build and install the manylinux version with the provided +docker container: ```bash -curl https://sh.rustup.rs -sSf | sh -s -- -y -rustup set profile minimal -rustup toolchain add nightly-2021-11-02 -``` - -Then clone and enter enter the `bqskitrs` directory: - -```bash -git clone https://github.com/BQSKit/bqskitrs.git +git clone https://github.com/BQSKit/bqskitrs.git --recursive cd bqskitrs +docker run -e OPENBLAS_ARGS="DYNAMIC_ARCH=1" --rm -v $(pwd):/io edyounis/bqskitrs-manylinux:1.1 build --release --features=openblas --compatibility=manylinux2014 +pip install --no-index --find-links=target/wheels bqskitrs ``` -Then install the `bqskitrs` package: - -```bash -pip install . -``` - -This will take a while. Once it is done, verify that the installation succeeded by running - -``` -python3 -c 'import bqskitrs' -``` - -It should not print anything out nor give any error. - - ### MacOS - Make sure the version of pip you have is at least 20.0.2. You can check via `pip -V` and upgrade via `python3 -m pip install -U pip`. First, install the dependencies. We use homebrew here, which is what we build the official package against. ``` -brew install gcc eigen lapack +brew install gcc ceres-solver eigen lapack ``` Once that is complete you should then install Rust like as follows: ```bash curl https://sh.rustup.rs -sSf | sh -s -- -y -rustup set profile minimal -rustup toolchain add nightly-2021-11-02 ``` Then clone and enter enter the `bqskitrs` directory: ```bash -git clone https://github.com/BQSKit/bqskitrs.git +git clone https://github.com/BQSKit/bqskitrs.git --recursive cd bqskitrs ``` Then build the wheel (package file) with [maturin](https://github.com/PyO3/maturin): ```bash -pip install maturin -maturin build --cargo-extra-args="--no-default-features --features python,accelerate,ceres/static,mimalloc/local_dynamic_tls" --release --no-sdist +pip install -U setuptools wheel maturin +maturin build --features="accelerate" --release +``` + +If you encounter issues on an intel x86 Mac computer with a message like "rust failed to run custom build command for cxx', you may need to run the following build command instead: + +```bash +MACOSX_DEPLOYMENT_TARGET=11.0 maturin build --features="accelerate" ``` Finally install the wheel that you built: @@ -110,11 +84,7 @@ You can check via `pip -V` and upgrade via `python3 -m pip install -U pip`. Download and install rust via the installer found at https://rustup.rs/. Accept all of the defaults. -Close your shell and open a new one (this updates the enviroment). Then run - -```shell -rustup toolchain add nightly-2021-11-02 -``` +Close your shell and open a new one (this updates the enviroment). Then install `cargo-vcpkg`, which will help install dependencies for us. You can install it via @@ -127,7 +97,7 @@ cargo install cargo-vcpkg Then clone and enter enter the `bqskitrs` directory: ```shell -git clone https://github.com/BQSKit/bqskitrs.git +git clone https://github.com/BQSKit/bqskitrs.git --recursive cd bqskitrs ``` @@ -142,8 +112,8 @@ This will take quite a while. Then build the wheel (package file) with [maturin](https://github.com/PyO3/maturin): ```shell -pip install maturin -maturin build --cargo-extra-args="--no-default-features --features python,static,openblas-src/system,mimalloc/local_dynamic_tls" --release --no-sdist +python -m pip install maturin +python -m maturin build --interpreter $(which python) --features="openblas,openblas-src/system" --release ``` Finally install the wheel that you built: @@ -159,18 +129,3 @@ python3 -c 'import bqskitrs' ``` It should not print anything out nor give any error. - -## Benchmarking - -This crate also supports benchmarking changes. Once you have dependencies installed, -you should install `cargo-criterion` to run benchmarks: - -```bash -cargo install cargo-criterion -``` - -Then you can run a command like the following to benchmark the instantiators: - -```bash -cargo criterion --no-default-features --features openblas-src,blas-src/openblas,openblas-src/system,openblas-src/cblas,openblas-src/lapacke,squaremat/openblas-system,mimalloc -```