From 50417c8401c34ebe47226c682c17415bb6198a16 Mon Sep 17 00:00:00 2001 From: merav-aharoni <46567124+merav-aharoni@users.noreply.github.com> Date: Thu, 1 Aug 2019 18:33:37 +0300 Subject: [PATCH] Partial measure in tensor_network simulation method (#299) * Added support for partial measurement. Still testing * Improved functionality of MPS::apply_measure * Changed sample_measure to also use MPS::apply_measure since it is more efficient * Changed expectation value input to use the string 'Z' rather than the matrix * Added tests for measure in tensor network state --- CHANGELOG.md | 3 + .../tensor_network/matrix_product_state.cpp | 91 ++++++++++++++----- .../tensor_network/matrix_product_state.hpp | 8 +- .../matrix_product_state_tensor.hpp | 1 + .../tensor_network/tensor_network_state.hpp | 36 +++----- .../qasm_simulator/tensor_network_measure.py | 82 +++++++++++++++++ .../qasm_simulator/tensor_network_method.py | 71 +++++++++++++++ .../backends/test_qasm_tensor_network.py | 57 ++---------- 8 files changed, 250 insertions(+), 99 deletions(-) create mode 100644 test/terra/backends/qasm_simulator/tensor_network_measure.py create mode 100644 test/terra/backends/qasm_simulator/tensor_network_method.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 83c180afc8..21f64392fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ Added - Added superop qobj instruction and superoperator matrix utils (\# 289) - Added support for conditional unitary, kraus, superop qobj instructions (\# 291) - Add "validation_threshold" config parameter to Aer backends (\# 290) +- Added support for apply_measure in tensor_network_state. Also changed + sample_measure to use apply_measure (\#299) + - Noise model inserter module diff --git a/src/simulators/tensor_network/matrix_product_state.cpp b/src/simulators/tensor_network/matrix_product_state.cpp index a02b2d7f18..e00c6461ef 100644 --- a/src/simulators/tensor_network/matrix_product_state.cpp +++ b/src/simulators/tensor_network/matrix_product_state.cpp @@ -16,6 +16,7 @@ #include #include "framework/utils.hpp" +#include "framework/matrix.hpp" #include "matrix_product_state.hpp" #include "matrix_product_state_tensor.hpp" @@ -23,6 +24,13 @@ namespace AER { namespace TensorNetworkState { +static const cmatrix_t zero_measure = + AER::Utils::make_matrix({{{1, 0}, {0, 0}}, + {{0, 0}, {0, 0}}}); +static const cmatrix_t one_measure = + AER::Utils::make_matrix({{{0, 0}, {0, 0}}, + {{0, 0}, {1, 0}}}); + //------------------------------------------------------------------------ // local function declarations //------------------------------------------------------------------------ @@ -108,7 +116,6 @@ cmatrix_t mul_matrix_by_lambda(const cmatrix_t &mat, cmatrix_t reshape_matrix(cmatrix_t input_matrix) { vector res(2); - //cmatrix_t temp = AER::Utils::dagger(input_matrix); AER::Utils::split(input_matrix, res[0], res[1], 1); cmatrix_t reshaped_matrix = AER::Utils::concatenate(res[0], res[1], 0); return reshaped_matrix; @@ -267,8 +274,6 @@ void MPS::apply_2_qubit_gate(uint_t index_A, uint_t index_B, Gates gate_type, cm q_reg_[index_A].mul_Gamma_by_left_Lambda(left_lambda); q_reg_[index_B].mul_Gamma_by_right_Lambda(right_lambda); MPS_Tensor temp = MPS_Tensor::contract(q_reg_[index_A], lambda_reg_[index_A], q_reg_[index_B]); - // cout << "after contract, temp = " < &rands) -{ - rvector_t probvector; - probabilities_vector(probvector); - const int_t SHOTS = rands.size(); - reg_t samples = {0}; - uint_t length = probvector.size(); - samples.assign(SHOTS, 0); - for (int_t i = 0; i < SHOTS; ++i) { - double rand = rands[i]; - double p = .0; - uint_t sample; - for (sample = 0; sample < length; ++sample) { - p += probvector[sample]; - if (rand < p) - break; - } - samples[i] = sample; - } - return samples; +reg_t MPS::apply_measure(const reg_t &qubits, + RngEngine &rng) { + reg_t qubits_to_update; + reg_t outcome_vector; + outcome_vector.resize(qubits.size()); + for (uint_t i=0; i0; i--) { + if (lambda_reg_[i-1].size() == 1) + break; // no need to propagate if no entanglement + apply_2_qubit_gate(i-1, i, id, cmatrix_t(1)); + } + + return measurement; } void MPS::initialize_from_statevector(uint_t num_qubits, const cvector_t state_vector) { diff --git a/src/simulators/tensor_network/matrix_product_state.hpp b/src/simulators/tensor_network/matrix_product_state.hpp index dc30ccb03c..25259d4570 100644 --- a/src/simulators/tensor_network/matrix_product_state.hpp +++ b/src/simulators/tensor_network/matrix_product_state.hpp @@ -22,6 +22,8 @@ enum Gates { cx, cz, cu1, swap, su4 // two qubit }; +enum class Direction {RIGHT, LEFT}; + //========================================================================= // MPS class //========================================================================= @@ -191,7 +193,11 @@ class MPS{ return 0; } - reg_t sample_measure(std::vector &rnds); + reg_t apply_measure(const reg_t &qubits, + RngEngine &rng); + + uint_t apply_measure(uint_t qubit, + RngEngine &rng); //---------------------------------------------------------------- // function name: initialize_from_statevector diff --git a/src/simulators/tensor_network/matrix_product_state_tensor.hpp b/src/simulators/tensor_network/matrix_product_state_tensor.hpp index fecdf47e52..15fee7b69e 100644 --- a/src/simulators/tensor_network/matrix_product_state_tensor.hpp +++ b/src/simulators/tensor_network/matrix_product_state_tensor.hpp @@ -76,6 +76,7 @@ class MPS_Tensor // Assignment operator MPS_Tensor& operator=(const MPS_Tensor& rhs){ if (this != &rhs){ + data_.clear(); data_ = rhs.data_; } return *this; diff --git a/src/simulators/tensor_network/tensor_network_state.hpp b/src/simulators/tensor_network/tensor_network_state.hpp index a02bd12970..dcadc50948 100644 --- a/src/simulators/tensor_network/tensor_network_state.hpp +++ b/src/simulators/tensor_network/tensor_network_state.hpp @@ -643,11 +643,10 @@ void State::apply_measure(const reg_t &qubits, const reg_t &cmemory, const reg_t &cregister, RngEngine &rng) { - // Actual measurement outcome - const auto meas = sample_measure_with_prob(qubits, rng); - // Implement measurement update - measure_reset_update(qubits, meas.first, meas.first, meas.second); - const reg_t outcome = Utils::int2reg(meas.first, 2, qubits.size()); + + reg_t outcome = qreg_.apply_measure(qubits, rng); + // measure_reset_update(qubits, meas.first, meas.first, meas.second); + // const reg_t outcome = Utils::int2reg(meas.first, 2, qubits.size()); creg_.store_measure(outcome, cmemory, cregister); } @@ -658,25 +657,16 @@ rvector_t State::measure_probs(const reg_t &qubits) const { std::vector State::sample_measure(const reg_t &qubits, uint_t shots, RngEngine &rng) { + + MPS temp; std::vector all_samples; - all_samples.reserve(shots); - - // Generate flat register for storing - std::vector rnds; - rnds.reserve(shots); - for (uint_t i = 0; i < shots; ++i) - rnds.push_back(rng.rand(0, 1)); - - reg_t allbit_samples = qreg_.sample_measure(rnds); - - for (int_t val : allbit_samples) { - reg_t allbit_sample = Utils::int2reg(val, 2, qreg_.num_qubits()); - reg_t sample; - sample.reserve(qubits.size()); - for (uint_t qubit : qubits) { - sample.push_back(allbit_sample[qubit]); - } - all_samples.push_back(sample); + all_samples.resize(shots); + reg_t single_result; + + for (int_t i=0; i(shots); i++) { + temp.initialize(qreg_); + single_result = temp.apply_measure(qubits, rng); + all_samples[i] = single_result; } return all_samples; diff --git a/test/terra/backends/qasm_simulator/tensor_network_measure.py b/test/terra/backends/qasm_simulator/tensor_network_measure.py new file mode 100644 index 0000000000..93ad8b2abb --- /dev/null +++ b/test/terra/backends/qasm_simulator/tensor_network_measure.py @@ -0,0 +1,82 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2019. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" +Tensor Network Integration Tests +This set of tests is copied from qasm_measure.py, but without some +functionality that has not been supported yet (specifically the 'iden' +operation). +""" + +from test.terra.reference import ref_measure +from qiskit.compiler import assemble +from qiskit.providers.aer import QasmSimulator + +class QasmTensorNetworkMeasureTests: + """QasmSimulator tensor_network measure tests.""" + + SIMULATOR = QasmSimulator() + BACKEND_OPTS = {} + + # --------------------------------------------------------------------- + # Test measure + # --------------------------------------------------------------------- + def test_measure_deterministic_with_sampling(self): + """Test QasmSimulator measure with deterministic counts with sampling""" + shots = 100 + circuits = ref_measure.measure_circuits_deterministic( + allow_sampling=True) + targets = ref_measure.measure_counts_deterministic(shots) + qobj = assemble(circuits, self.SIMULATOR, shots=shots) + result = self.SIMULATOR.run( + qobj, backend_options=self.BACKEND_OPTS).result() + self.is_completed(result) + self.compare_counts(result, circuits, targets, delta=0) + # Test sampling was enabled + for res in result.results: + self.assertIn("measure_sampling", res.metadata) + self.assertEqual(res.metadata["measure_sampling"], True) + + def test_measure_nondeterministic_with_sampling(self): + """Test QasmSimulator measure with non-deterministic counts with sampling""" + shots = 2000 + circuits = ref_measure.measure_circuits_nondeterministic( + allow_sampling=True) + targets = ref_measure.measure_counts_nondeterministic(shots) + qobj = assemble(circuits, self.SIMULATOR, shots=shots) + result = self.SIMULATOR.run( + qobj, backend_options=self.BACKEND_OPTS).result() + self.is_completed(result) + self.compare_counts(result, circuits, targets, delta=0.05 * shots) + # Test sampling was enabled + for res in result.results: + self.assertIn("measure_sampling", res.metadata) + self.assertEqual(res.metadata["measure_sampling"], True) + + # --------------------------------------------------------------------- + # Test multi-qubit measure qobj instruction + # --------------------------------------------------------------------- + def test_measure_nondeterministic_multi_qubit_with_sampling(self): + """Test QasmSimulator measure with non-deterministic counts""" + shots = 2000 + circuits = ref_measure.multiqubit_measure_circuits_nondeterministic( + allow_sampling=True) + targets = ref_measure.multiqubit_measure_counts_nondeterministic(shots) + qobj = assemble(circuits, self.SIMULATOR, shots=shots) + result = self.SIMULATOR.run( + qobj, backend_options=self.BACKEND_OPTS).result() + self.is_completed(result) + self.compare_counts(result, circuits, targets, delta=0.05 * shots) + # Test sampling was enabled + for res in result.results: + self.assertIn("measure_sampling", res.metadata) + self.assertEqual(res.metadata["measure_sampling"], True) + diff --git a/test/terra/backends/qasm_simulator/tensor_network_method.py b/test/terra/backends/qasm_simulator/tensor_network_method.py new file mode 100644 index 0000000000..1cef85ee0d --- /dev/null +++ b/test/terra/backends/qasm_simulator/tensor_network_method.py @@ -0,0 +1,71 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2019. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" +Tensor Network Integration Tests +This set of tests runs all the circuits from test/terra/references/: ref_1q_clifford and ref_2q_clifford, +with the exception of those with the multiplexer gate which is not supported yet. +""" + +import unittest +import logging +import pprint + +from test.terra import common +from test.terra.reference import ref_1q_clifford, ref_2q_clifford + +from qiskit import * +from qiskit.providers.aer import QasmSimulator +#from qiskit.extensions.simulator import Snapshot + +logger = logging.getLogger(__name__) + +class QasmTensorNetworkMethodTests: + """QasmSimulator tensor_network method tests.""" + + BACKEND_OPTS = {"method": "tensor_network"} + + def test_method_deterministic_without_sampling(self): + """TestTensorNetwork method with deterministic counts without sampling""" + deterministic_tests = [(ref_1q_clifford.h_gate_circuits_deterministic, ref_1q_clifford.h_gate_counts_deterministic), + (ref_1q_clifford.x_gate_circuits_deterministic, ref_1q_clifford.x_gate_counts_deterministic), + (ref_1q_clifford.z_gate_circuits_deterministic, ref_1q_clifford.z_gate_counts_deterministic), + (ref_1q_clifford.y_gate_circuits_deterministic, ref_1q_clifford.y_gate_counts_deterministic), + (ref_1q_clifford.s_gate_circuits_deterministic, ref_1q_clifford.s_gate_counts_deterministic), + (ref_1q_clifford.sdg_gate_circuits_deterministic, ref_1q_clifford.sdg_gate_counts_deterministic), + (ref_2q_clifford.cx_gate_circuits_deterministic,ref_2q_clifford.cx_gate_counts_deterministic), + (ref_2q_clifford.cz_gate_circuits_deterministic, ref_2q_clifford.cz_gate_counts_deterministic), + (ref_2q_clifford.swap_gate_circuits_deterministic, ref_2q_clifford.swap_gate_counts_deterministic)] + + determ_test_list = {'list':deterministic_tests, 'shots':100, 'delta':0} + + nondeterministic_tests = [(ref_1q_clifford.h_gate_circuits_nondeterministic, ref_1q_clifford.h_gate_counts_nondeterministic), + (ref_1q_clifford.s_gate_circuits_nondeterministic, ref_1q_clifford.s_gate_counts_nondeterministic), + (ref_1q_clifford.sdg_gate_circuits_nondeterministic, ref_1q_clifford.sdg_gate_counts_nondeterministic), + (ref_2q_clifford.cx_gate_circuits_nondeterministic, ref_2q_clifford.cx_gate_counts_nondeterministic), + (ref_2q_clifford.cz_gate_circuits_nondeterministic, ref_2q_clifford.cz_gate_counts_nondeterministic), + (ref_2q_clifford.swap_gate_circuits_nondeterministic, ref_2q_clifford.swap_gate_counts_nondeterministic)] + + nondeterm_test_list = {'list':nondeterministic_tests, 'shots':2000, 'delta':0.05} + + test_list = [determ_test_list, nondeterm_test_list] + for list in test_list: + for test in list['list']: + delta = list['delta'] + shots = list['shots'] + + circuits = test[0](final_measure=True) + targets = test[1](shots) + job = execute(circuits, QasmSimulator(), backend_options=self.BACKEND_OPTS, shots=shots) + result = job.result() + self.is_completed(result) + self.compare_counts(result, circuits, targets, delta = delta*shots) + diff --git a/test/terra/backends/test_qasm_tensor_network.py b/test/terra/backends/test_qasm_tensor_network.py index 52ff667cf3..b98f7f0003 100644 --- a/test/terra/backends/test_qasm_tensor_network.py +++ b/test/terra/backends/test_qasm_tensor_network.py @@ -9,65 +9,22 @@ # Any modifications or derivative works of this code must retain this # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. + """ Tensor Network Integration Tests -This set of tests runs all the circuits from test/terra/references/: ref_1q_clifford and ref_2q_clifford, -with the exception of those with the multiplexer gate which is not supported yet. """ import unittest -import logging -import pprint - from test.terra import common -from test.terra.reference import ref_1q_clifford, ref_2q_clifford - -from qiskit import * -from qiskit.providers.aer import QasmSimulator -#from qiskit.extensions.simulator import Snapshot - -logger = logging.getLogger(__name__) - -class TestQasmTensorNetworkSimulator(common.QiskitAerTestCase): - """QasmSimulator tensor_network method tests.""" - - - def test_measure_deterministic_without_sampling(self): - """TestTensorNetwork measure with deterministic counts without sampling""" - deterministic_tests = [(ref_1q_clifford.h_gate_circuits_deterministic, ref_1q_clifford.h_gate_counts_deterministic), - (ref_1q_clifford.x_gate_circuits_deterministic, ref_1q_clifford.x_gate_counts_deterministic), - (ref_1q_clifford.z_gate_circuits_deterministic, ref_1q_clifford.z_gate_counts_deterministic), - (ref_1q_clifford.y_gate_circuits_deterministic, ref_1q_clifford.y_gate_counts_deterministic), - (ref_1q_clifford.s_gate_circuits_deterministic, ref_1q_clifford.s_gate_counts_deterministic), - (ref_1q_clifford.sdg_gate_circuits_deterministic, ref_1q_clifford.sdg_gate_counts_deterministic), - (ref_2q_clifford.cx_gate_circuits_deterministic,ref_2q_clifford.cx_gate_counts_deterministic), - (ref_2q_clifford.cz_gate_circuits_deterministic, ref_2q_clifford.cz_gate_counts_deterministic), - (ref_2q_clifford.swap_gate_circuits_deterministic, ref_2q_clifford.swap_gate_counts_deterministic)] - - determ_test_list = {'list':deterministic_tests, 'shots':100, 'delta':0} +from test.terra.backends.qasm_simulator.tensor_network_method import QasmTensorNetworkMethodTests +from test.terra.backends.qasm_simulator.tensor_network_measure import QasmTensorNetworkMeasureTests - nondeterministic_tests = [(ref_1q_clifford.h_gate_circuits_nondeterministic, ref_1q_clifford.h_gate_counts_nondeterministic), - (ref_1q_clifford.s_gate_circuits_nondeterministic, ref_1q_clifford.s_gate_counts_nondeterministic), - (ref_1q_clifford.sdg_gate_circuits_nondeterministic, ref_1q_clifford.sdg_gate_counts_nondeterministic), - (ref_2q_clifford.cx_gate_circuits_nondeterministic, ref_2q_clifford.cx_gate_counts_nondeterministic), - (ref_2q_clifford.cz_gate_circuits_nondeterministic, ref_2q_clifford.cz_gate_counts_nondeterministic), - (ref_2q_clifford.swap_gate_circuits_nondeterministic, ref_2q_clifford.swap_gate_counts_nondeterministic)] - nondeterm_test_list = {'list':nondeterministic_tests, 'shots':2000, 'delta':0.05} +class TestQasmTensorNetworkSimulator(common.QiskitAerTestCase, + QasmTensorNetworkMethodTests, + QasmTensorNetworkMeasureTests): - BACKEND_OPTS_TN = {"method": "tensor_network"} - test_list = [determ_test_list, nondeterm_test_list] - for list in test_list: - for test in list['list']: - delta = list['delta'] - shots = list['shots'] - - circuits = test[0](final_measure=True) - targets = test[1](shots) - job = execute(circuits, QasmSimulator(), backend_options=BACKEND_OPTS_TN, shots=shots) - result = job.result() - self.is_completed(result) - self.compare_counts(result, circuits, targets, delta = delta*shots) + BACKEND_OPTS = {"method": "tensor_network"} if __name__ == '__main__': unittest.main()