Merge pull request #210 from ismaila-at-za-ibm/initiator

New CompositeGate Class for Arbitrary Multi-Qubit Initialization (take 2)
This commit is contained in:
Diego M. Rodríguez 2017-12-26 15:17:39 +01:00 committed by GitHub
commit 78ef075fe8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 873 additions and 2 deletions

View File

@ -0,0 +1,101 @@
# coding=utf-8
# -*- coding: utf-8 -*-
# Copyright 2017 IBM RESEARCH. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# =============================================================================
"""Demo basic optimization: Remove Zero Rotations and Remove Double CNOTs."""
import os
import sys
# We don't know from where the user is running the example,
# so we need a relative position from this file path.
# TODO: Relative imports for intra-package imports are highly discouraged.
# http://stackoverflow.com/a/7506006
sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
from qiskit import QuantumProgram
from qiskit import CompositeGate
from qiskit.extensions.standard.cx import CnotGate
from qiskit.extensions.standard.rx import RXGate
Q_SPECS = {
"name": "Program-tutorial",
"circuits": [{
"name": "Circuit",
"quantum_registers": [{
"name": "qr",
"size": 4
}],
"classical_registers": [{
"name": "cr",
"size": 4
}]}],
}
Q_program = QuantumProgram(specs=Q_SPECS)
circuit = Q_program.get_circuit("Circuit")
quantum_r = Q_program.get_quantum_register("qr")
classical_r = Q_program.get_classical_register('cr')
circuit.h(quantum_r[0])
circuit.rx(0, quantum_r[0])
circuit.cx(quantum_r[0], quantum_r[1])
circuit.cx(quantum_r[0], quantum_r[1])
circuit.h(quantum_r[0])
circuit.cx(quantum_r[0], quantum_r[1])
composite_gate_1 = CompositeGate("composite1", [],
[quantum_r[x] for x in range(4)])
composite_gate_1._attach(CnotGate(quantum_r[0], quantum_r[1]))
circuit._attach(composite_gate_1)
circuit.h(quantum_r[0])
composite_gate_2 = CompositeGate("composite2", [],
[quantum_r[x] for x in range(4)])
composite_gate_2._attach(CnotGate(quantum_r[0], quantum_r[1]))
circuit._attach(composite_gate_2)
circuit.cx(quantum_r[0], quantum_r[1])
circuit.h(quantum_r[0])
composite_gate_3 = CompositeGate("composite3", [],
[quantum_r[x] for x in range(4)])
composite_gate_3._attach(CnotGate(quantum_r[0], quantum_r[1]))
composite_gate_3._attach(CnotGate(quantum_r[0], quantum_r[2]))
circuit._attach(composite_gate_3)
circuit.h(quantum_r[0])
composite_gate_4 = CompositeGate("composite4", [],
[quantum_r[x] for x in range(4)])
composite_gate_4._attach(CnotGate(quantum_r[0], quantum_r[1]))
composite_gate_4._attach(RXGate(0, quantum_r[0]))
composite_gate_4._attach(CnotGate(quantum_r[0], quantum_r[1]))
circuit._attach(composite_gate_4)
print("Removed Zero Rotations: " + str(circuit.remove_zero_rotations()))
print("Removed Double CNOTs: " + str(circuit.remove_double_cnots_once()))
# QASM from a program
QASM_source = Q_program.get_qasm("Circuit")
print(QASM_source)

View File

@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-
# Copyright 2017 IBM RESEARCH. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# =============================================================================
"""
Demo the use of the InitializeGate class to prepare arbitrary pure states.
"""
import math
import os
import sys
# We don't know from where the user is running the example,
# so we need a relative position from this file path.
# TODO: Relative imports for intra-package imports are highly discouraged.
# http://stackoverflow.com/a/7506006
sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
from qiskit import QuantumProgram
import Qconfig
###############################################################
# Make a quantum program for state initialization.
###############################################################
Q_SPECS = {
"name": "Program-tutorial",
"circuits": [{
"name": "initializer_circ",
"quantum_registers": [{
"name": "qr",
"size": 4
}],
"classical_registers": [{
"name": "cr",
"size": 4
}]}],
}
Q_program = QuantumProgram(specs=Q_SPECS)
circuit = Q_program.get_circuit("initializer_circ")
qr = Q_program.get_quantum_register("qr")
cr = Q_program.get_classical_register('cr')
desired_vector = [
1 / math.sqrt(4) * complex(0, 1),
1 / math.sqrt(8) * complex(1, 0),
0,
0,
0,
0,
0,
0,
1 / math.sqrt(8) * complex(1, 0),
1 / math.sqrt(8) * complex(0, 1),
0,
0,
0,
0,
1 / math.sqrt(4) * complex(1, 0),
1 / math.sqrt(8) * complex(1, 0)]
circuit.initialize("QInit", desired_vector, [qr[0], qr[1], qr[2], qr[3]])
circuit.measure(qr[0], cr[0])
circuit.measure(qr[1], cr[1])
circuit.measure(qr[2], cr[2])
circuit.measure(qr[3], cr[3])
QASM_source = Q_program.get_qasm("initializer_circ")
print(QASM_source)
###############################################################
# Set the backend name and coupling map.
###############################################################
device = 'ibmqx2'
coupling_map = {0: [1, 2],
1: [2],
2: [],
3: [2, 4],
4: [2]}
circuits = ['initializer_circ']
shots = 1024
###############################################################
# Set up the API and execute the program.
###############################################################
Q_program.set_api(Qconfig.APItoken, Qconfig.config["url"])
# Desired vector
print("Desired probabilities...")
print(str(list(map(lambda x: format(abs(x * x), '.3f'), desired_vector))))
# Initialize on local simulator
result = Q_program.execute(circuits,
backend='local_qasm_simulator',
wait=2, timeout=240, shots=shots)
print("Probabilities from simulator...[%s]" % result)
n_qubits_qureg = qr.size
counts = result.get_counts("initializer_circ")
qubit_strings = [format(i, '0%sb' % n_qubits_qureg) for
i in range(2 ** n_qubits_qureg)]
print([format(counts.get(s, 0) / shots, '.3f') for
s in qubit_strings])

View File

@ -11,6 +11,7 @@ from ._instructionset import InstructionSet
from ._reset import Reset
from ._measure import Measure
import qiskit.extensions.standard
import qiskit.extensions.quantum_initializer
from ._jobprocessor import JobProcessor
from ._quantumjob import QuantumJob
from ._quantumprogram import QuantumProgram

View File

@ -40,8 +40,9 @@ class Instruction(object):
self.name = name
self.param = []
for p in param:
if not isinstance(p, Basic):
# if item in param not symbolic, make it symbolic
if not isinstance(p, (Basic, complex)):
# If the item in param is not symbolic and not complex (used
# by InitializeGate), make it symbolic.
self.param.append(Number(p))
else:
self.param.append(p)

View File

@ -0,0 +1 @@
from ._initializer import InitializeGate

View File

@ -0,0 +1,452 @@
# -*- coding: utf-8 -*-
# Copyright 2017 IBM RESEARCH. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# =============================================================================
"""
Initialize qubit registers to desired arbitrary state.
"""
import math
import numpy
import scipy
from qiskit import CompositeGate
from qiskit import Gate
from qiskit import QISKitError
from qiskit import QuantumCircuit
from qiskit import Reset
from qiskit.extensions.standard.cx import CnotGate
from qiskit.extensions.standard.ry import RYGate
from qiskit.extensions.standard.rz import RZGate
_EPS = 1e-10 # global variable used to chop very small numbers to zero
class InitializeGate(CompositeGate):
"""Complex amplitude initialization.
Class that implements the (complex amplitude) initialization of some
flexible collection of qubit registers (assuming the qubits are in the
zero state).
Implements a recursive initialization algorithm including optimizations
from "Synthesis of Quantum Logic Circuits" Shende, Bullock, Markov
https://arxiv.org/abs/quant-ph/0406176v5
Additionally implements some extra optimizations: remove zero rotations and
double cnots.`
It inherits from CompositeGate in the same way that the Fredkin (cswap)
gate does. Therefore self.data is the list of gates (in order) that must
be applied to implement this meta-gate.
name = instruction name string
param = list of complex amplitudes
arg = list of qubits
circ = QuantumCircuit or CompositeGate containing this gate
"""
def __init__(self, name, param, arg, circ=None):
"""Create new initialize composite gate."""
num_qubits = math.log2(len(param))
# Check if param is a power of 2
if num_qubits == 0 or not num_qubits.is_integer():
raise QISKitError("Desired vector not a positive power of 2.")
self.num_qubits = int(num_qubits)
# Check if number of desired qubits agrees with available qubits
if len(arg) != self.num_qubits:
raise QISKitError("Number of complex amplitudes do not correspond "
"to the number of qubits.")
# Check if probabilities (amplitudes squared) sum to 1
if not math.isclose(sum(numpy.absolute(param) ** 2), 1.0,
abs_tol=1e-4):
raise QISKitError("Sum of amplitudes-squared does not equal one.")
super(InitializeGate, self).__init__(name, param, arg, circ)
# call to generate the circuit that takes the desired vector to zero
self.gates_to_uncompute()
# remove zero rotations and double cnots
self.optimize_gates()
# invert the circuit to create the desired vector from zero (assuming
# the qubits are in the zero state)
self.inverse()
def nth_qubit_from_least_sig_qubit(self, nth):
"""
Return the qubit that is nth away from the least significant qubit
(LSB), so n=0 corresponds to the LSB.
"""
# if LSB is first (as is the case with the IBM QE) and significance is
# in order:
return self.arg[nth]
# if MSB is first: return self.arg[self.num_qubits - 1 - n]
# equivalent to self.arg[-(n+1)]
# to generalize any mapping could be placed here or even taken from
# the user
def reapply(self, circ):
"""Reapply this gate to the corresponding qubits in circ."""
self._modifiers(circ.initialize(self.param, self.arg))
def gates_to_uncompute(self):
"""
Call to populate the self.data list with gates that takes the
desired vector to zero.
"""
# kick start the peeling loop
remaining_param = self.param
for i in range(self.num_qubits):
# work out which rotations must be done to disentangle the LSB
# qubit (we peel away one qubit at a time)
remaining_param, \
thetas, \
phis = InitializeGate._rotations_to_disentangle(remaining_param)
# perform the required rotations to decouple the LSB qubit (so that
# it can be "factored" out, leaving a
# shorter amplitude vector to peel away)
self._attach(self._multiplex(RZGate, i, phis))
self._attach(self._multiplex(RYGate, i, thetas))
@staticmethod
def _rotations_to_disentangle(local_param):
"""
Static internal method to work out Ry and Rz rotation angles used
to disentangle the LSB qubit.
These rotations make up the block diagonal matrix U (i.e. multiplexor)
that disentangles the LSB.
[[Ry(theta_1).Rz(phi_1) 0 . . 0],
[0 Ry(theta_2).Rz(phi_2) . 0],
.
.
0 0 Ry(theta_2^n).Rz(phi_2^n)]]
"""
remaining_vector = []
thetas = []
phis = []
param_len = len(local_param)
for i in range(param_len // 2):
# Ry and Rz rotations to move bloch vector from 0 to "imaginary"
# qubit
# (imagine a qubit state signified by the amplitudes at index 2*i
# and 2*(i+1), corresponding to the select qubits of the
# multiplexor being in state |i>)
remains,\
add_theta,\
add_phi = InitializeGate._bloch_angles(
local_param[2*i : 2*(i + 1)])
remaining_vector.append(remains)
# rotations for all imaginary qubits of the full vector
# to move from where it is to zero, hence the negative sign
thetas.append(-add_theta)
phis.append(-add_phi)
return remaining_vector, thetas, phis
@staticmethod
def _bloch_angles(pair_of_complex):
"""
Static internal method to work out rotation to create the passed in
qubit from the zero vector.
"""
[a_complex, b_complex] = pair_of_complex
# Force a and b to be complex, as otherwise numpy.angle might fail.
a_complex = complex(a_complex)
b_complex = complex(b_complex)
mag_a = numpy.absolute(a_complex)
final_r = float(numpy.sqrt(mag_a ** 2 + numpy.absolute(b_complex) ** 2))
if final_r < _EPS:
theta = 0
phi = 0
final_r = 0
final_t = 0
else:
theta = float(2 * numpy.arccos(mag_a / final_r))
a_arg = numpy.angle(a_complex)
b_arg = numpy.angle(b_complex)
final_t = a_arg + b_arg
phi = b_arg - a_arg
return final_r * numpy.exp(1.J * final_t/2), theta, phi
def _multiplex(self, bottom_gate, bottom_qubit_index, list_of_angles):
"""
Internal recursive method to create gates to perform rotations on the
imaginary qubits: works by rotating LSB (and hence ALL imaginary
qubits) by combo angle and then flipping sign (by flipping the bit,
hence moving the complex amplitudes) of half the imaginary qubits
(CNOT) followed by another combo angle on LSB, therefore executing
conditional (on MSB) rotations, thereby disentangling LSB.
"""
list_len = len(list_of_angles)
target_qubit = self.nth_qubit_from_least_sig_qubit(bottom_qubit_index)
# Case of no multiplexing = base case for recursion
if list_len == 1:
return bottom_gate(list_of_angles[0], target_qubit)
local_num_qubits = int(math.log2(list_len)) + 1
control_qubit = self.nth_qubit_from_least_sig_qubit(
local_num_qubits - 1 + bottom_qubit_index)
# calc angle weights, assuming recursion (that is the lower-level
# requested angles have been correctly implemented by recursion
angle_weight = scipy.kron([[0.5, 0.5], [0.5, -0.5]],
numpy.identity(2 ** (local_num_qubits - 2)))
# calc the combo angles
list_of_angles = (angle_weight * numpy.matrix(
list_of_angles).transpose()).reshape(-1).tolist()[0]
combine_composite_gates = CompositeGate(
"multiplex" + local_num_qubits.__str__(), [], self.arg)
# recursive step on half the angles fulfilling the above assumption
combine_composite_gates._attach(
self._multiplex(bottom_gate, bottom_qubit_index,
list_of_angles[0:(list_len // 2)]))
# combine_composite_gates.cx(control_qubit,target_qubit) -> does not
# work as expected because checks circuit
# so attach CNOT as follows, thereby flipping the LSB qubit
combine_composite_gates._attach(CnotGate(control_qubit, target_qubit))
# implement extra efficiency from the paper of cancelling adjacent
# CNOTs (by leaving out last CNOT and reversing (NOT inverting) the
# second lower-level multiplex)
sub_gate = self._multiplex(
bottom_gate, bottom_qubit_index, list_of_angles[(list_len // 2):])
if isinstance(sub_gate, CompositeGate):
combine_composite_gates._attach(sub_gate.reverse())
else:
combine_composite_gates._attach(sub_gate)
# outer multiplex keeps final CNOT, because no adjacent CNOT to cancel
# with
if self.num_qubits == local_num_qubits + bottom_qubit_index:
combine_composite_gates._attach(CnotGate(control_qubit,
target_qubit))
return combine_composite_gates
@staticmethod
def chop_num(numb):
"""
Set very small numbers (as defined by global variable _EPS) to zero.
"""
return 0 if abs(numb) < _EPS else numb
# ###############################################################
# Add needed functionality to other classes (it feels
# weird following the QISKit convention of adding functionality to other
# classes like this ;),
# TODO: multiple inheritance might be better?)
def reverse(self):
"""
Reverse (recursively) the sub-gates of this CompositeGate. Note this does
not invert the gates!
"""
new_data = []
for gate in reversed(self.data):
if isinstance(gate, CompositeGate):
new_data.append(gate.reverse())
else:
new_data.append(gate)
self.data = new_data
# not just a high-level reverse:
# self.data = [gate for gate in reversed(self.data)]
return self
QuantumCircuit.reverse = reverse
CompositeGate.reverse = reverse
def optimize_gates(self):
"""Remove Zero rotations and Double CNOTS."""
self.remove_zero_rotations()
while self.remove_double_cnots_once():
pass
QuantumCircuit.optimize_gates = optimize_gates
CompositeGate.optimize_gates = optimize_gates
def remove_zero_rotations(self):
"""
Remove Zero Rotations by looking (recursively) at rotation gates at the
leaf ends.
"""
# Removed at least one zero rotation.
zero_rotation_removed = False
new_data = []
for gate in self.data:
if isinstance(gate, CompositeGate):
zero_rotation_removed |= gate.remove_zero_rotations()
if gate.data:
new_data.append(gate)
else:
if ((not isinstance(gate, Gate)) or
(not (gate.name == "rz" or gate.name == "ry" or
gate.name == "rx") or
(InitializeGate.chop_num(gate.param[0]) != 0))):
new_data.append(gate)
else:
zero_rotation_removed = True
self.data = new_data
return zero_rotation_removed
QuantumCircuit.remove_zero_rotations = remove_zero_rotations
CompositeGate.remove_zero_rotations = remove_zero_rotations
def number_atomic_gates(self):
"""Count the number of leaf gates. """
num = 0
for gate in self.data:
if isinstance(gate, CompositeGate):
num += gate.number_atomic_gates()
else:
if isinstance(gate, Gate):
num += 1
return num
QuantumCircuit.number_atomic_gates = number_atomic_gates
CompositeGate.number_atomic_gates = number_atomic_gates
def remove_double_cnots_once(self):
"""
Remove Double CNOTS paying attention that gates may be neighbours across
Composite Gate boundaries.
"""
num_high_level_gates = len(self.data)
if num_high_level_gates == 0:
return False
else:
if num_high_level_gates == 1 and isinstance(self.data[0],
CompositeGate):
return self.data[0].remove_double_cnots_once()
# Removed at least one double cnot.
double_cnot_removed = False
# last gate might be composite
if isinstance(self.data[num_high_level_gates - 1], CompositeGate):
double_cnot_removed = \
double_cnot_removed or\
self.data[num_high_level_gates - 1].remove_double_cnots_once()
# don't start with last gate, using reversed so that can del on the go
for i in reversed(range(num_high_level_gates - 1)):
if isinstance(self.data[i], CompositeGate):
double_cnot_removed =\
double_cnot_removed \
or self.data[i].remove_double_cnots_once()
left_gate_host = self.data[i].last_atomic_gate_host()
left_gate_index = -1
# TODO: consider adding if semantics needed:
# to remove empty composite gates
# if left_gate_host == None: del self.data[i]
else:
left_gate_host = self.data
left_gate_index = i
if ((left_gate_host is not None) and
left_gate_host[left_gate_index].name == "cx"):
if isinstance(self.data[i + 1], CompositeGate):
right_gate_host = self.data[i + 1].first_atomic_gate_host()
right_gate_index = 0
else:
right_gate_host = self.data
right_gate_index = i + 1
if (right_gate_host is not None) \
and right_gate_host[right_gate_index].name == "cx" \
and (left_gate_host[left_gate_index].arg ==
right_gate_host[right_gate_index].arg):
del right_gate_host[right_gate_index]
del left_gate_host[left_gate_index]
double_cnot_removed = True
return double_cnot_removed
QuantumCircuit.remove_double_cnots_once = remove_double_cnots_once
CompositeGate.remove_double_cnots_once = remove_double_cnots_once
def first_atomic_gate_host(self):
"""Return the host list of the leaf gate on the left edge."""
if self.data:
if isinstance(self.data[0], CompositeGate):
return self.data[0].first_atomic_gate_host()
return self.data
return None
QuantumCircuit.first_atomic_gate_host = first_atomic_gate_host
CompositeGate.first_atomic_gate_host = first_atomic_gate_host
def last_atomic_gate_host(self):
"""Return the host list of the leaf gate on the right edge."""
if self.data:
if isinstance(self.data[-1], CompositeGate):
return self.data[-1].last_atomic_gate_host()
return self.data
return None
QuantumCircuit.last_atomic_gate_host = last_atomic_gate_host
CompositeGate.last_atomic_gate_host = last_atomic_gate_host
def initialize(self, name, params, qubits):
"""Apply initialize to circuit."""
self._check_dups(qubits)
for i in qubits:
self._check_qubit(i)
self._attach(Reset(i, self))
# TODO: avoid explicit reset if compiler determines a |0> state
return self._attach(InitializeGate(name, params, qubits, self))
QuantumCircuit.initialize = initialize
CompositeGate.initialize = initialize

View File

@ -0,0 +1,196 @@
# -*- coding: utf-8 -*-
# pylint: disable=invalid-name,missing-docstring
# Copyright 2017 IBM RESEARCH. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# =============================================================================
"""
InitializeGate (CompositeGate instance) test.
"""
import math
import unittest
from qiskit import QISKitError
from qiskit import QuantumProgram
from qiskit.tools.qi.qi import state_fidelity
from .common import QiskitTestCase
class TestInitialize(QiskitTestCase):
"""QISKIT InitializeGate tests."""
_desired_fidelity = 0.99
def test_uniform_superposition(self):
desired_vector = [0.5, 0.5, 0.5, 0.5]
qp = QuantumProgram()
qr = qp.create_quantum_register("qr", 2)
cr = qp.create_classical_register("cr", 2)
qc = qp.create_circuit("qc", [qr], [cr])
qc.initialize("QInit", desired_vector, [qr[0], qr[1]])
result = qp.execute(["qc"], backend='local_qasm_simulator', shots=1)
quantum_state = result.get_data("qc")['quantum_state']
fidelity = state_fidelity(quantum_state, desired_vector)
self.assertGreater(
fidelity, self._desired_fidelity,
"Initializer has low fidelity {0:.2g}.".format(fidelity))
def test_deterministic_state(self):
desired_vector = [0, 1, 0, 0]
qp = QuantumProgram()
qr = qp.create_quantum_register("qr", 2)
cr = qp.create_classical_register("cr", 2)
qc = qp.create_circuit("qc", [qr], [cr])
qc.initialize("QInit", desired_vector, [qr[0], qr[1]])
result = qp.execute(["qc"], backend='local_qasm_simulator', shots=1)
quantum_state = result.get_data("qc")['quantum_state']
fidelity = state_fidelity(quantum_state, desired_vector)
self.assertGreater(
fidelity, self._desired_fidelity,
"Initializer has low fidelity {0:.2g}.".format(fidelity))
def test_bell_state(self):
desired_vector = [1/math.sqrt(2), 0, 0, 1/math.sqrt(2)]
qp = QuantumProgram()
qr = qp.create_quantum_register("qr", 2)
cr = qp.create_classical_register("cr", 2)
qc = qp.create_circuit("qc", [qr], [cr])
qc.initialize("QInit", desired_vector, [qr[0], qr[1]])
result = qp.execute(["qc"], backend='local_qasm_simulator', shots=1)
quantum_state = result.get_data("qc")['quantum_state']
fidelity = state_fidelity(quantum_state, desired_vector)
self.assertGreater(
fidelity, self._desired_fidelity,
"Initializer has low fidelity {0:.2g}.".format(fidelity))
def test_ghz_state(self):
desired_vector = [1/math.sqrt(2), 0, 0, 0, 0, 0, 0, 1/math.sqrt(2)]
qp = QuantumProgram()
qr = qp.create_quantum_register("qr", 3)
cr = qp.create_classical_register("cr", 3)
qc = qp.create_circuit("qc", [qr], [cr])
qc.initialize("QInit", desired_vector, [qr[0], qr[1], qr[2]])
result = qp.execute(["qc"], backend='local_qasm_simulator', shots=1)
quantum_state = result.get_data("qc")['quantum_state']
fidelity = state_fidelity(quantum_state, desired_vector)
self.assertGreater(
fidelity, self._desired_fidelity,
"Initializer has low fidelity {0:.2g}.".format(fidelity))
def test_single_qubit(self):
desired_vector = [1/math.sqrt(3), math.sqrt(2)/math.sqrt(3)]
qp = QuantumProgram()
qr = qp.create_quantum_register("qr", 1)
cr = qp.create_classical_register("cr", 1)
qc = qp.create_circuit("qc", [qr], [cr])
qc.initialize("QInit", desired_vector, [qr[0]])
result = qp.execute(["qc"], backend='local_qasm_simulator', shots=1)
quantum_state = result.get_data("qc")['quantum_state']
fidelity = state_fidelity(quantum_state, desired_vector)
self.assertGreater(
fidelity, self._desired_fidelity,
"Initializer has low fidelity {0:.2g}.".format(fidelity))
def test_random_3qubit(self):
desired_vector = [
1 / math.sqrt(16) * complex(0, 1),
1 / math.sqrt(8) * complex(1, 0),
1 / math.sqrt(16) * complex(1, 1),
0,
0,
1 / math.sqrt(8) * complex(1, 2),
1 / math.sqrt(16) * complex(1, 0),
0]
qp = QuantumProgram()
qr = qp.create_quantum_register("qr", 3)
cr = qp.create_classical_register("cr", 1)
qc = qp.create_circuit("qc", [qr], [cr])
qc.initialize("QInit", desired_vector, [qr[0], qr[1], qr[2]])
result = qp.execute(["qc"], backend='local_qasm_simulator', shots=1)
quantum_state = result.get_data("qc")['quantum_state']
fidelity = state_fidelity(quantum_state, desired_vector)
self.assertGreater(
fidelity, self._desired_fidelity,
"Initializer has low fidelity {0:.2g}.".format(fidelity))
def test_random_4qubit(self):
desired_vector = [
1 / math.sqrt(4) * complex(0, 1),
1 / math.sqrt(8) * complex(1, 0),
0,
0,
0,
0,
0,
0,
1 / math.sqrt(8) * complex(1, 0),
1 / math.sqrt(8) * complex(0, 1),
0,
0,
0,
0,
1 / math.sqrt(4) * complex(1, 0),
1 / math.sqrt(8) * complex(1, 0)]
qp = QuantumProgram()
qr = qp.create_quantum_register("qr", 4)
cr = qp.create_classical_register("cr", 4)
qc = qp.create_circuit("qc", [qr], [cr])
qc.initialize("QInit", desired_vector, [qr[0], qr[1], qr[2], qr[3]])
result = qp.execute(["qc"], backend='local_qasm_simulator', shots=1)
quantum_state = result.get_data("qc")['quantum_state']
fidelity = state_fidelity(quantum_state, desired_vector)
self.assertGreater(
fidelity, self._desired_fidelity,
"Initializer has low fidelity {0:.2g}.".format(fidelity))
def test_malformed_amplitudes(self):
desired_vector = [1/math.sqrt(3), math.sqrt(2)/math.sqrt(3), 0]
qp = QuantumProgram()
qr = qp.create_quantum_register("qr", 2)
cr = qp.create_classical_register("cr", 2)
qc = qp.create_circuit("qc", [qr], [cr])
self.assertRaises(
QISKitError,
qc.initialize, "QInit", desired_vector, [qr[0], qr[1]])
def test_non_unit_probability(self):
desired_vector = [1, 1]
qp = QuantumProgram()
qr = qp.create_quantum_register("qr", 2)
cr = qp.create_classical_register("cr", 2)
qc = qp.create_circuit("qc", [qr], [cr])
self.assertRaises(
QISKitError,
qc.initialize, "QInit", desired_vector, [qr[0], qr[1]])
def test_initialize_middle_circuit(self):
desired_vector = [0.5, 0.5, 0.5, 0.5]
qp = QuantumProgram()
qr = qp.create_quantum_register("qr", 2)
cr = qp.create_classical_register("cr", 2)
qc = qp.create_circuit("qc", [qr], [cr])
qc.h(qr[0])
qc.cx(qr[0], qr[1])
qc.initialize("QInit", desired_vector, [qr[0], qr[1]])
result = qp.execute(["qc"], backend='local_qasm_simulator', shots=1)
quantum_state = result.get_data("qc")['quantum_state']
fidelity = state_fidelity(quantum_state, desired_vector)
self.assertGreater(
fidelity, self._desired_fidelity,
"Initializer has low fidelity {0:.2g}.".format(fidelity))
if __name__ == '__main__':
unittest.main()