Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed issue 4526 by adding InitializeGate #5179

Closed
wants to merge 10 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ def __init__(self,
if upto_diag:
self.isometry(np.sqrt(normalized_probabilities), self.qubits, None)
else:
from qiskit.extensions import Initialize # pylint: disable=cyclic-import
initialize = Initialize(np.sqrt(normalized_probabilities))
from qiskit.extensions import InitializeGate # pylint: disable=cyclic-import
initialize = InitializeGate(np.sqrt(normalized_probabilities))
circuit = initialize.gates_to_uncompute().inverse()
self.compose(circuit, inplace=True)

Expand Down
4 changes: 2 additions & 2 deletions qiskit/circuit/library/probability_distributions/normal.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,8 @@ def __init__(self,
if upto_diag:
self.isometry(np.sqrt(normalized_probabilities), self.qubits, None)
else:
from qiskit.extensions import Initialize # pylint: disable=cyclic-import
initialize = Initialize(np.sqrt(normalized_probabilities))
from qiskit.extensions import InitializeGate # pylint: disable=cyclic-import
initialize = InitializeGate(np.sqrt(normalized_probabilities))
circuit = initialize.gates_to_uncompute().inverse()
self.compose(circuit, inplace=True)

Expand Down
2 changes: 1 addition & 1 deletion qiskit/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
from qiskit.circuit.library.standard_gates import *
from qiskit.circuit.barrier import Barrier

from .quantum_initializer.initializer import Initialize
from .quantum_initializer.initializer import Initialize, InitializeGate
from .unitary import UnitaryGate
from .hamiltonian_gate import HamiltonianGate
from .simulator import Snapshot
105 changes: 94 additions & 11 deletions qiskit/extensions/quantum_initializer/initializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from qiskit.circuit import QuantumCircuit
from qiskit.circuit import QuantumRegister
from qiskit.circuit import Instruction
from qiskit.circuit.gate import Gate
from qiskit.circuit.exceptions import CircuitError
from qiskit.circuit.library.standard_gates.x import CXGate, XGate
from qiskit.circuit.library.standard_gates.h import HGate
Expand All @@ -32,17 +33,15 @@
_EPS = 1e-10 # global variable used to chop very small numbers to zero


class Initialize(Instruction):
class InitializeGate(Gate):
"""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).
Note that Initialize is an Instruction and not a Gate since it contains a reset instruction,
which is not unitary.
"""

def __init__(self, params):
def __init__(self, params, label=None):
"""Create new initialize composite.

params (str or list):
Expand All @@ -56,13 +55,20 @@ def __init__(self, params):
if isinstance(params, str):
self._fromlabel = True
num_qubits = len(params)
elif isinstance(params, list) and isinstance(params[0], str):
self._fromlabel = True
params = "".join(params)
num_qubits = len(params)
else:
self._fromlabel = False
num_qubits = math.log2(len(params))
try:
num_qubits = math.log2(len(params))
except (ValueError, TypeError):
raise QiskitError("Given params are not a statevector")

# Check if param is a power of 2
if num_qubits == 0 or not num_qubits.is_integer():
raise QiskitError("Desired statevector length not a positive power of 2.")
raise QiskitError("Desired statevector length is not a power of 2.")

# Check if probabilities (amplitudes squared) sum to 1
if not math.isclose(sum(np.absolute(params) ** 2), 1.0,
Expand All @@ -71,7 +77,7 @@ def __init__(self, params):

num_qubits = int(num_qubits)

super().__init__("initialize", num_qubits, 0, params)
super().__init__('init', int(num_qubits), params, label=label)

def _define(self):
self.definition = self._define_fromlabel() if self._fromlabel else self._define_synthesis()
Expand Down Expand Up @@ -118,8 +124,6 @@ def _define_synthesis(self):

q = QuantumRegister(self.num_qubits, 'q')
initialize_circuit = QuantumCircuit(q, name='init_def')
for qubit in q:
initialize_circuit.append(Reset(), [qubit])
initialize_circuit.append(initialize_instr, q[:])

return initialize_circuit
Expand All @@ -141,7 +145,7 @@ def gates_to_uncompute(self):
# qubit (we peel away one qubit at a time)
(remaining_param,
thetas,
phis) = Initialize._rotations_to_disentangle(remaining_param)
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)
Expand Down Expand Up @@ -188,7 +192,7 @@ def _rotations_to_disentangle(local_param):
# multiplexor being in state |i>)
(remains,
add_theta,
add_phi) = Initialize._bloch_angles(local_param[2 * i: 2 * (i + 1)])
add_phi) = InitializeGate._bloch_angles(local_param[2 * i: 2 * (i + 1)])

remaining_vector.append(remains)

Expand Down Expand Up @@ -315,6 +319,85 @@ def validate_parameter(self, parameter):
"{1}".format(type(parameter), self.name))


class Initialize(Instruction):
"""Apply reset then the complex amplitude initialization.
"""

def __init__(self, params):
"""Create new initialize composite.

params (list): vector of complex amplitudes to initialize to
"""
if isinstance(params, str):
self._fromlabel = True
num_qubits = len(params)
else:
self._fromlabel = False
try:
num_qubits = math.log2(len(params))
except (ValueError, TypeError):
raise QiskitError("Given params are not a statevector")

# Check if param is a power of 2
if num_qubits == 0 or not num_qubits.is_integer():
raise QiskitError("Desired statevector length not a positive power of 2.")

# Check if probabilities (amplitudes squared) sum to 1
if not math.isclose(sum(np.absolute(params) ** 2), 1.0,
abs_tol=_EPS):
raise QiskitError("Sum of amplitudes-squared does not equal one.")

num_qubits = int(num_qubits)

super().__init__("initialize", num_qubits, 0, params)

def _define(self):
"""Calculate a subcircuit that implements this initialization

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.
"""
Comment on lines +355 to +363
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment can be removed, it applies to InitializeGate since the decomposition is computed there.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is now removed

q = QuantumRegister(self.num_qubits, 'q')
initialize_circuit = QuantumCircuit(q, name='init_def')
for qubit in q:
initialize_circuit.append(Reset(), [qubit])
initialize_circuit.append(InitializeGate(self.params), [q[:]])

self.definition = initialize_circuit

def broadcast_arguments(self, qargs, cargs):
flat_qargs = [qarg for sublist in qargs for qarg in sublist]

if self.num_qubits != len(flat_qargs):
raise QiskitError("Initialize parameter vector has %d elements, therefore expects %s "
"qubits. However, %s were provided." %
(2**self.num_qubits, self.num_qubits, len(flat_qargs)))
yield flat_qargs, []

def validate_parameter(self, parameter):
"""Initialize instruction parameter can be str, int, float, and complex."""

# Initialize instruction parameter can be str
if self._fromlabel:
if parameter in ['0', '1', '+', '-', 'l', 'r']:
return parameter
raise CircuitError("invalid param label {0} for instruction {1}. Label should be "
"0, 1, +, -, l, or r ".format(type(parameter), self.name))

# Initialize instruction parameter can be int, float, and complex.
if isinstance(parameter, (int, float, complex)):
return complex(parameter)
elif isinstance(parameter, np.number):
return complex(parameter.item())
else:
raise CircuitError("invalid param type {0} for instruction "
"{1}".format(type(parameter), self.name))


def initialize(self, params, qubits):
"""Apply initialize to circuit."""
if not isinstance(qubits, list):
Expand Down
2 changes: 1 addition & 1 deletion test/python/circuit/test_gate_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def setUpClass(cls):
exclude = {'ControlledGate', 'DiagonalGate', 'UCGate', 'MCGupDiag',
'MCU1Gate', 'UnitaryGate', 'HamiltonianGate', 'MCPhaseGate',
'UCPauliRotGate', 'SingleQubitUnitary', 'MCXGate',
'VariadicZeroParamGate', 'ClassicalFunction'}
'VariadicZeroParamGate', 'ClassicalFunction', 'InitializeGate'}
cls._gate_classes = []
for aclass in class_list:
if aclass.__name__ not in exclude:
Expand Down