Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,7 @@ def _reset_registers(self, num_state_qubits: Optional[int]) -> None:

# add ancillas if required
if len(self.breakpoints) > 1:
num_ancillas = num_state_qubits - 1 + len(self.breakpoints)
if self.contains_zero_breakpoint:
num_ancillas -= 1
num_ancillas = num_state_qubits
qr_ancilla = AncillaRegister(num_ancillas)
self.add_register(qr_ancilla)
else:
Expand All @@ -255,27 +253,22 @@ def _build(self):
self.append(lin_r.to_gate(), qr_state[:] + qr_target)

else:
if self.contains_zero_breakpoint:
i_compare = i - 1
else:
i_compare = i
qr_compare = [qr_ancilla[0]]
qr_helper = qr_ancilla[1:]

# apply Comparator
comp = IntegerComparator(num_state_qubits=self.num_state_qubits, value=point)
qr = qr_state[:] + [qr_ancilla[i_compare]] # add ancilla as compare qubit
qr_remaining_ancilla = qr_ancilla[i_compare + 1:] # take remaining ancillas
qr = qr_state[:] + qr_compare[:] # add ancilla as compare qubit

self.append(comp.to_gate(),
qr[:] + qr_remaining_ancilla[:comp.num_ancillas])
self.append(comp.to_gate(), qr[:] + qr_helper[:comp.num_ancillas])

# apply controlled rotation
lin_r = LinearPauliRotations(num_state_qubits=self.num_state_qubits,
slope=self.mapped_slopes[i],
offset=self.mapped_offsets[i],
basis=self.basis)
self.append(lin_r.to_gate().control(),
[qr_ancilla[i_compare]] + qr_state[:] + qr_target)
qr_compare[:] + qr_state[:] + qr_target)

# uncompute comparator
self.append(comp.to_gate().inverse(),
qr[:] + qr_remaining_ancilla[:comp.num_ancillas])
self.append(comp.to_gate().inverse(), qr[:] + qr_helper[:comp.num_ancillas])
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,15 @@ class PiecewisePolynomialPauliRotations(FunctionalPauliRotations):

f(x) = \begin{cases}
0, x < x_0 \\
\sum_{i=0}^{i=d}a_{j,i} x^i, x_j \leq x < x_{j+1}
\sum_{i=0}^{i=d}a_{j,i}/2 x^i, x_j \leq x < x_{j+1}
\end{cases}

where we implicitly assume :math:`x_{J+1} = 2^n`.

.. note::
Note the :math:`1/2` factor in the coefficients of :math:`f(x)`, this is consistent with
Qiskit's Pauli rotations.

Examples:
>>> from qiskit import QuantumCircuit
>>> from qiskit.circuit.library.arithmetic.piecewise_polynomial_pauli_rotations import\
Expand Down Expand Up @@ -223,7 +227,7 @@ def _check_configuration(self, raise_on_failure: bool = True) -> bool:
raise CircuitError('Not enough qubits in the circuit, need at least '
'{}.'.format(self.num_state_qubits + 1))

if len(self._breakpoints) != len(self.coeffs):
if len(self.breakpoints) != len(self.coeffs):
valid = False
if raise_on_failure:
raise ValueError('Mismatching number of breakpoints and polynomials.')
Expand All @@ -237,7 +241,7 @@ def _reset_registers(self, num_state_qubits: Optional[int]) -> None:
self.qregs = [qr_state, qr_target]

# Calculate number of ancilla qubits required
num_ancillas = num_state_qubits - 1 + len(self.breakpoints)
num_ancillas = num_state_qubits + 1
if self.contains_zero_breakpoint:
num_ancillas -= 1
num_ancillas += max(1, self._degree - 1)
Expand All @@ -249,6 +253,7 @@ def _reset_registers(self, num_state_qubits: Optional[int]) -> None:
self.qregs = []

def _build(self):
# The number of ancilla might have changed, so reset registers
super()._build()

qr_state = self.qubits[:self.num_state_qubits]
Expand All @@ -266,18 +271,14 @@ def _build(self):
poly_r = PolynomialPauliRotations(num_state_qubits=self.num_state_qubits,
coeffs=self.mapped_coeffs[i],
basis=self.basis)
self.append(poly_r.to_gate(), qr_state[:] + qr_target + qr_ancilla_rot[:])
self.append(poly_r.to_gate(), qr_state[:] + qr_target +
qr_ancilla_rot[:poly_r.num_ancillas])

else:
if self.contains_zero_breakpoint:
i_compare = i - 1
else:
i_compare = i

# apply Comparator
comp = IntegerComparator(num_state_qubits=self.num_state_qubits, value=point)
qr_state_full = qr_state[:] + [qr_ancilla[i_compare]] # add compare qubit
qr_remaining_ancilla = qr_ancilla[i_compare + 1:] # take remaining ancillas
qr_state_full = qr_state[:] + [qr_ancilla[0]] # add compare qubit
qr_remaining_ancilla = qr_ancilla[1:] # take remaining ancillas

self.append(comp.to_gate(),
qr_state_full[:] + qr_remaining_ancilla[:comp.num_ancillas])
Expand All @@ -287,7 +288,8 @@ def _build(self):
coeffs=self.mapped_coeffs[i],
basis=self.basis)
self.append(poly_r.to_gate().control(),
[qr_ancilla[i_compare]] + qr_state[:] + qr_target + qr_ancilla_rot[:])
[qr_ancilla[0]] + qr_state[:] + qr_target +
qr_ancilla_rot[:poly_r.num_ancillas])

# uncompute comparator
self.append(comp.to_gate().inverse(),
Expand Down
17 changes: 12 additions & 5 deletions qiskit/circuit/library/arithmetic/polynomial_pauli_rotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,16 @@ def _reset_registers(self, num_state_qubits):
# set new register of appropriate size
qr_state = QuantumRegister(num_state_qubits, name='state')
qr_target = QuantumRegister(1, name='target')
num_ancillas = max(1, self.degree - 1)
qr_ancilla = AncillaRegister(num_ancillas)
self.qregs = [qr_state, qr_target, qr_ancilla]
num_ancillas = min(self.degree - 1, num_state_qubits - 2)

self.qregs = [qr_state, qr_target]

if num_ancillas > 0:
qr_ancilla = AncillaRegister(num_ancillas)
self.qregs.append(qr_ancilla)
else:
qr_ancilla = []

self._qubits = qr_state[:] + qr_target[:] + qr_ancilla[:]
self._ancillas = qr_ancilla[:]
else:
Expand Down Expand Up @@ -338,11 +345,11 @@ def _build(self):
# apply controlled rotations
if len(qr_control) > 1:
if self.basis == 'x':
self.mcrx(rotation_coeffs[c], qr_control, qr_target, qr_ancilla)
self.mcrx(rotation_coeffs[c], qr_control, qr_target)
elif self.basis == 'y':
self.mcry(rotation_coeffs[c], qr_control, qr_target, qr_ancilla)
else:
self.mcrz(rotation_coeffs[c], qr_control, qr_target, qr_ancilla)
self.mcrz(rotation_coeffs[c], qr_control, qr_target)

elif len(qr_control) == 1:
if self.basis == 'x':
Expand Down
42 changes: 29 additions & 13 deletions qiskit/circuit/library/arithmetic/weighted_adder.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
"""Compute the weighted sum of qubit states."""

from typing import List, Optional
import warnings
import numpy as np

from qiskit.circuit import QuantumRegister
from qiskit.circuit import QuantumRegister, AncillaRegister

from ..blueprintcircuit import BlueprintCircuit

Expand Down Expand Up @@ -162,15 +163,25 @@ def _reset_registers(self):
qr_state = QuantumRegister(self.num_state_qubits, name='state')
qr_sum = QuantumRegister(self.num_sum_qubits, name='sum')
self.qregs = [qr_state, qr_sum]
self._qubits = qr_state[:] + qr_sum[:]
self._ancillas = []

if self.num_carry_qubits > 0:
qr_carry = QuantumRegister(self.num_carry_qubits, name='carry')
qr_carry = AncillaRegister(self.num_carry_qubits, name='carry')
self.qregs += [qr_carry]
self._qubits += qr_carry[:]
self._ancillas += qr_carry[:]

if self.num_control_qubits > 0:
qr_control = QuantumRegister(self.num_control_qubits, name='control')
qr_control = AncillaRegister(self.num_control_qubits, name='control')
self.qregs += [qr_control]
self._qubits += qr_control[:]
self._ancillas += qr_control[:]

else:
self.qregs = []
self._qubits = []
self._ancillas = []

@property
def num_carry_qubits(self) -> int:
Expand Down Expand Up @@ -198,12 +209,13 @@ def num_control_qubits(self) -> int:

@property
def num_ancilla_qubits(self) -> int:
"""The number of ancilla qubits required to implement the weighted sum.

Returns:
The number of ancilla qubits in the circuit.
"""
return self.num_carry_qubits + self.num_control_qubits
"""Deprecated. Use num_ancillas instead."""
warnings.warn('The WeightedAdder.num_ancilla_qubits property is deprecated '
'as of 0.17.0. It will be removed no earlier than 3 months after the release '
'date. You should use the num_ancillas property instead.',
DeprecationWarning, stacklevel=2)
return self.num_control_qubits + self.num_carry_qubits
# return self.num_ancillas

def _check_configuration(self, raise_on_failure=True):
valid = True
Expand Down Expand Up @@ -262,7 +274,8 @@ def _build(self):
# - controlled by q_state[i]
self.x(qr_sum[j])
self.x(qr_carry[j - 1])
self.mct([q_state, qr_sum[j], qr_carry[j - 1]], qr_carry[j], qr_control)
self.mct([q_state, qr_sum[j], qr_carry[j - 1]], qr_carry[j], qr_control,
mode='v-chain')
self.cx(q_state, qr_carry[j])
self.x(qr_sum[j])
self.x(qr_carry[j - 1])
Expand All @@ -281,7 +294,8 @@ def _build(self):
else:
# compute (q_sum[j] + q_carry[j-1]) into (q_sum[j], q_carry[j])
# - controlled by q_state[i]
self.mct([q_state, qr_sum[j], qr_carry[j - 1]], qr_carry[j], qr_control)
self.mcx([q_state, qr_sum[j], qr_carry[j - 1]], qr_carry[j], qr_control,
mode='v-chain')
self.ccx(q_state, qr_carry[j - 1], qr_sum[j])

# uncompute carry qubits
Expand All @@ -298,7 +312,8 @@ def _build(self):
pass
else:
self.x(qr_carry[j - 1])
self.mct([q_state, qr_sum[j], qr_carry[j - 1]], qr_carry[j], qr_control)
self.mcx([q_state, qr_sum[j], qr_carry[j - 1]], qr_carry[j], qr_control,
mode='v-chain')
self.cx(q_state, qr_carry[j])
self.x(qr_carry[j - 1])
else:
Expand All @@ -312,5 +327,6 @@ def _build(self):
# compute (q_sum[j] + q_carry[j-1]) into (q_sum[j], q_carry[j])
# - controlled by q_state[i]
self.x(qr_sum[j])
self.mct([q_state, qr_sum[j], qr_carry[j - 1]], qr_carry[j], qr_control)
self.mcx([q_state, qr_sum[j], qr_carry[j - 1]], qr_carry[j], qr_control,
mode='v-chain')
self.x(qr_sum[j])
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
deprecations:
- |
Deprecate the :meth:`qiskit.circuit.library.WeightedAdder.num_ancilla_qubits`
method in favor of :meth:`qiskit.circuit.library.WeightedAdder.num_ancillas` for
consistency among all circuits.
fixes:
- |
Improve the allocation of helper qubits in `PolynomialPauliRotations`,
`PiecewiseLinearPauliRotations` and use the allocated helper qubits in the MCX gate
in `WeightedAdder` (which have been allocated before but not used).
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ class TestFunctionalPauliRotations(QiskitTestCase):

def assertFunctionIsCorrect(self, function_circuit, reference):
"""Assert that ``function_circuit`` implements the reference function ``reference``."""
num_state_qubits = function_circuit.num_state_qubits
num_ancilla_qubits = function_circuit.num_ancilla_qubits
num_state_qubits = function_circuit.num_qubits - function_circuit.num_ancillas - 1
num_ancilla_qubits = function_circuit.num_ancillas
circuit = QuantumCircuit(num_state_qubits + 1 + num_ancilla_qubits)
circuit.h(list(range(num_state_qubits)))
circuit.append(function_circuit.to_instruction(), list(range(circuit.num_qubits)))
Expand Down Expand Up @@ -61,6 +61,7 @@ def assertFunctionIsCorrect(self, function_circuit, reference):

@data(([1, 0.1], 3),
([0, 0.4, 2], 2),
([1, 0.5, 0.2, -0.2, 0.4, 2.5], 5),
)
@unpack
def test_polynomial_function(self, coeffs, num_state_qubits):
Expand Down
4 changes: 2 additions & 2 deletions test/python/circuit/library/test_weighted_adder.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def assertSummationIsCorrect(self, adder):

probabilities = defaultdict(float)
for i, statevector_amplitude in enumerate(statevector):
i = bin(i)[2:].zfill(circuit.num_qubits)[adder.num_ancilla_qubits:]
i = bin(i)[2:].zfill(circuit.num_qubits)[adder.num_ancillas:]
probabilities[i] += np.real(np.abs(statevector_amplitude) ** 2)

expectations = defaultdict(float)
Expand All @@ -54,7 +54,7 @@ def assertSummationIsCorrect(self, adder):
for state, probability in probabilities.items():
self.assertAlmostEqual(probability, expectations[state])

@data([0], [1, 2, 1], [4])
@data([0], [1, 2, 1], [4], [1, 2, 1, 1, 4])
def test_summation(self, weights):
"""Test the weighted adder on some examples."""
adder = WeightedAdder(len(weights), weights)
Expand Down
4 changes: 2 additions & 2 deletions test/python/circuit/test_piecewise_polynomial.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@


@ddt
class TestFunctionalPauliRotations(QiskitTestCase):
"""Test the functional Pauli rotations."""
class TestPiecewisePolynomialRotations(QiskitTestCase):
"""Test the piecewise polynomial Pauli rotations."""

def assertFunctionIsCorrect(self, function_circuit, reference):
"""Assert that ``function_circuit`` implements the reference function ``reference``."""
Expand Down