Source code for qiskit.circuit.library.arithmetic.weighted_adder

# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2020.
#
# 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.

# pylint: disable=no-member

"""Compute the weighted sum of qubit states."""

from typing import List, Optional
import numpy as np

from qiskit.circuit import QuantumRegister

from ..blueprintcircuit import BlueprintCircuit


[docs]class WeightedAdder(BlueprintCircuit): r"""A circuit to compute the weighted sum of qubit registers. Given :math:`n` qubit basis states :math:`q_0, \ldots, q_{n-1} \in \{0, 1\}` and non-negative integer weights :math:`\lambda_0, \ldots, \lambda_{n-1}`, this circuit performs the operation .. math:: |q_0 \ldots q_{n-1}\rangle |0\rangle_s \mapsto |q_0 \ldots q_{n-1}\rangle |\sum_{j=0}^{n-1} \lambda_j q_j\rangle_s where :math:`s` is the number of sum qubits required. This can be computed as .. math:: s = 1 + \left\lfloor \log_2\left( \sum_{j=0}^{n-1} \lambda_j \right) \right\rfloor or :math:`s = 1` if the sum of the weights is 0 (then the expression in the logarithm is invalid). For qubits in a circuit diagram, the first weight applies to the upper-most qubit. For an example where the state of 4 qubits is added into a sum register, the circuit can be schematically drawn as .. parsed-literal:: ┌────────┐ state_0: ┤0 ├ | state_0 * weights[0] │ │ | state_1: ┤1 ├ | + state_1 * weights[1] │ │ | state_2: ┤2 ├ | + state_2 * weights[2] │ │ | state_3: ┤3 ├ | + state_3 * weights[3] │ │ sum_0: ┤4 ├ | │ Adder │ | sum_1: ┤5 ├ | = sum_0 * 2^0 + sum_1 * 2^1 + sum_2 * 2^2 │ │ | sum_2: ┤6 ├ | │ │ carry_0: ┤7 ├ │ │ carry_1: ┤8 ├ │ │ control_0: ┤9 ├ └────────┘ """ def __init__(self, num_state_qubits: Optional[int] = None, weights: Optional[List[int]] = None, name: str = 'adder') -> None: """Computes the weighted sum controlled by state qubits. Args: num_state_qubits: The number of state qubits. weights: List of weights, one for each state qubit. If none are provided they default to 1 for every qubit. name: The name of the circuit. """ super().__init__(name=name) self._weights = None self._num_state_qubits = None self.weights = weights self.num_state_qubits = num_state_qubits @property def num_sum_qubits(self) -> int: """The number of sum qubits in the circuit. Returns: The number of qubits needed to represent the weighted sum of the qubits. """ if sum(self.weights) > 0: return int(np.floor(np.log2(sum(self.weights))) + 1) return 1 @property def weights(self) -> List[int]: """The weights for the qubit states. Returns: The weight for the qubit states. """ if self._weights: return self._weights if self.num_state_qubits: return [1] * self.num_state_qubits return None @weights.setter def weights(self, weights: List[int]) -> None: """Set the weights for summing the qubit states. Args: weights: The new weights. Raises: ValueError: If not all weights are close to an integer. """ if weights: for i, weight in enumerate(weights): if not np.isclose(weight, np.round(weight)): raise ValueError('Non-integer weights are not supported!') weights[i] = np.round(weight) self._invalidate() self._weights = weights self._reset_registers() @property def num_state_qubits(self) -> int: """The number of qubits to be summed. Returns: The number of state qubits. """ return self._num_state_qubits @num_state_qubits.setter def num_state_qubits(self, num_state_qubits: int) -> None: """Set the number of state qubits. Args: num_state_qubits: The new number of state qubits. """ if self._num_state_qubits is None or num_state_qubits != self._num_state_qubits: self._invalidate() self._num_state_qubits = num_state_qubits self._reset_registers() def _reset_registers(self): if self.num_state_qubits: qr_state = QuantumRegister(self.num_state_qubits, name='state') qr_sum = QuantumRegister(self.num_sum_qubits, name='sum') self.qregs = [qr_state, qr_sum] if self.num_carry_qubits > 0: qr_carry = QuantumRegister(self.num_carry_qubits, name='carry') self.qregs += [qr_carry] if self.num_control_qubits > 0: qr_control = QuantumRegister(self.num_control_qubits, name='control') self.qregs += [qr_control] else: self.qregs = [] @property def num_carry_qubits(self) -> int: """The number of carry qubits required to compute the sum. Note that this is not necessarily equal to the number of ancilla qubits, these can be queried using ``num_ancilla_qubits``. Returns: The number of carry qubits required to compute the sum. """ return self.num_sum_qubits - 1 @property def num_control_qubits(self) -> int: """The number of additional control qubits required. Note that the total number of ancilla qubits can be obtained by calling the method ``num_ancilla_qubits``. Returns: The number of additional control qubits required (0 or 1). """ return int(self.num_sum_qubits > 2) @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 def _check_configuration(self, raise_on_failure=True): valid = True if self._num_state_qubits is None: valid = False if raise_on_failure: raise AttributeError('The number of state qubits has not been set.') if self._num_state_qubits != len(self.weights): valid = False if raise_on_failure: raise ValueError('Mismatching number of state qubits and weights.') return valid def _build(self): super()._build() num_result_qubits = self.num_state_qubits + self.num_sum_qubits qr_state = self.qubits[:self.num_state_qubits] qr_sum = self.qubits[self.num_state_qubits:num_result_qubits] qr_carry = self.qubits[num_result_qubits:num_result_qubits + self.num_carry_qubits] qr_control = self.qubits[num_result_qubits + self.num_carry_qubits:] # loop over state qubits and corresponding weights for i, weight in enumerate(self.weights): # only act if non-trivial weight if np.isclose(weight, 0): continue # get state control qubit q_state = qr_state[i] # get bit representation of current weight weight_binary = '{0:b}'.format(int(weight)).rjust(self.num_sum_qubits, '0')[::-1] # loop over bits of current weight and add them to sum and carry registers for j, bit in enumerate(weight_binary): if bit == '1': if self.num_sum_qubits == 1: self.cx(q_state, qr_sum[j]) elif j == 0: # compute (q_sum[0] + 1) into (q_sum[0], q_carry[0]) # - controlled by q_state[i] self.ccx(q_state, qr_sum[j], qr_carry[j]) self.cx(q_state, qr_sum[j]) elif j == self.num_sum_qubits - 1: # compute (q_sum[j] + q_carry[j-1] + 1) into (q_sum[j]) # - controlled by q_state[i] / last qubit, # no carry needed by construction self.cx(q_state, qr_sum[j]) self.ccx(q_state, qr_carry[j - 1], qr_sum[j]) else: # compute (q_sum[j] + q_carry[j-1] + 1) into (q_sum[j], q_carry[j]) # - 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.cx(q_state, qr_carry[j]) self.x(qr_sum[j]) self.x(qr_carry[j - 1]) self.cx(q_state, qr_sum[j]) self.ccx(q_state, qr_carry[j - 1], qr_sum[j]) else: if self.num_sum_qubits == 1: pass # nothing to do, since nothing to add elif j == 0: pass # nothing to do, since nothing to add elif j == self.num_sum_qubits-1: # compute (q_sum[j] + q_carry[j-1]) into (q_sum[j]) # - controlled by q_state[i] / last qubit, # no carry needed by construction self.ccx(q_state, qr_carry[j - 1], qr_sum[j]) 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.ccx(q_state, qr_carry[j - 1], qr_sum[j]) # uncompute carry qubits for j in reversed(range(len(weight_binary))): bit = weight_binary[j] if bit == '1': if self.num_sum_qubits == 1: pass elif j == 0: self.x(qr_sum[j]) self.ccx(q_state, qr_sum[j], qr_carry[j]) self.x(qr_sum[j]) elif j == self.num_sum_qubits - 1: 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.cx(q_state, qr_carry[j]) self.x(qr_carry[j - 1]) else: if self.num_sum_qubits == 1: pass elif j == 0: pass elif j == self.num_sum_qubits - 1: pass else: # 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.x(qr_sum[j])