Skip to content

Commit

Permalink
Partial measure in tensor_network simulation method (Qiskit#299)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
merav-aharoni authored and atilag committed Aug 1, 2019
1 parent 8d0fe29 commit 50417c8
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 99 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
91 changes: 66 additions & 25 deletions src/simulators/tensor_network/matrix_product_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,21 @@
#include <iostream>

#include "framework/utils.hpp"
#include "framework/matrix.hpp"

#include "matrix_product_state.hpp"
#include "matrix_product_state_tensor.hpp"

namespace AER {
namespace TensorNetworkState {

static const cmatrix_t zero_measure =
AER::Utils::make_matrix<complex_t>({{{1, 0}, {0, 0}},
{{0, 0}, {0, 0}}});
static const cmatrix_t one_measure =
AER::Utils::make_matrix<complex_t>({{{0, 0}, {0, 0}},
{{0, 0}, {1, 0}}});

//------------------------------------------------------------------------
// local function declarations
//------------------------------------------------------------------------
Expand Down Expand Up @@ -108,7 +116,6 @@ cmatrix_t mul_matrix_by_lambda(const cmatrix_t &mat,

cmatrix_t reshape_matrix(cmatrix_t input_matrix) {
vector<cmatrix_t> 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;
Expand Down Expand Up @@ -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 = " <<endl;
// temp.print(cout);

switch (gate_type) {
case cx:
Expand All @@ -277,11 +282,13 @@ void MPS::apply_2_qubit_gate(uint_t index_A, uint_t index_B, Gates gate_type, cm
case cz:
temp.apply_cz();
break;
case id:
break;
case cu1:
{
cmatrix_t Zeros = AER::Utils::Matrix::I-AER::Utils::Matrix::I;
cmatrix_t temp1 = AER::Utils::concatenate(AER::Utils::Matrix::I, Zeros , 1),
temp2 = AER::Utils::concatenate(Zeros, mat, 1);
temp2 = AER::Utils::concatenate(Zeros, mat, 1);
cmatrix_t cu = AER::Utils::concatenate(temp1, temp2 ,0) ;
temp.apply_matrix(cu);
break;
Expand Down Expand Up @@ -486,27 +493,61 @@ void MPS::probabilities_vector(rvector_t& probvector) const
}
}

// for now supporting only the fully vector (all qubits)
reg_t MPS::sample_measure(std::vector<double> &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; i<qubits.size(); i++) {
outcome_vector[i] = apply_measure(qubits[i], rng);
}
return outcome_vector;
}

uint_t MPS::apply_measure(uint_t qubit,
RngEngine &rng) {
reg_t qubits_to_update;
qubits_to_update.push_back(qubit);

// step 1 - measure qubit 0 in Z basis
double exp_val = expectation_value(qubits_to_update, "Z");

// step 2 - compute probability for 0 or 1 result
double prob0 = (1 + exp_val ) / 2;
double prob1 = 1 - prob0;

// step 3 - randomly choose a measurement value for qubit 0
double rnd = rng.rand(0, 1);
uint_t measurement;
cmatrix_t measurement_matrix(4);

if (rnd < prob0) {
measurement = 0;
measurement_matrix = zero_measure;
measurement_matrix = measurement_matrix * (1 / sqrt(prob0));
} else {
measurement = 1;
measurement_matrix = one_measure;
measurement_matrix = measurement_matrix * (1 / sqrt(prob1));
}

apply_matrix(qubits_to_update, measurement_matrix);

// step 4 - propagate the changes to all qubits to the right
for (uint_t i=qubit; i<num_qubits_-1; i++) {
if (lambda_reg_[i].size() == 1)
break; // no need to propagate if no entanglement
apply_2_qubit_gate(i, i+1, id, cmatrix_t(1));
}

// and propagate the changes to all qubits to the left
for (int_t i=qubit; i>0; 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) {
Expand Down
8 changes: 7 additions & 1 deletion src/simulators/tensor_network/matrix_product_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ enum Gates {
cx, cz, cu1, swap, su4 // two qubit
};

enum class Direction {RIGHT, LEFT};

//=========================================================================
// MPS class
//=========================================================================
Expand Down Expand Up @@ -191,7 +193,11 @@ class MPS{
return 0;
}

reg_t sample_measure(std::vector<double> &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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
36 changes: 13 additions & 23 deletions src/simulators/tensor_network/tensor_network_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -658,25 +657,16 @@ rvector_t State::measure_probs(const reg_t &qubits) const {
std::vector<reg_t> State::sample_measure(const reg_t &qubits,
uint_t shots,
RngEngine &rng) {

MPS temp;
std::vector<reg_t> all_samples;
all_samples.reserve(shots);

// Generate flat register for storing
std::vector<double> 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<static_cast<int_t>(shots); i++) {
temp.initialize(qreg_);
single_result = temp.apply_measure(qubits, rng);
all_samples[i] = single_result;
}

return all_samples;
Expand Down
82 changes: 82 additions & 0 deletions test/terra/backends/qasm_simulator/tensor_network_measure.py
Original file line number Diff line number Diff line change
@@ -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)

71 changes: 71 additions & 0 deletions test/terra/backends/qasm_simulator/tensor_network_method.py
Original file line number Diff line number Diff line change
@@ -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)

Loading

0 comments on commit 50417c8

Please sign in to comment.