# -*- coding: utf-8 -*-
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2018.
#
# 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.
"""
A two-ways dict to represent a layout.
Layout is the relation between virtual (qu)bits and physical (qu)bits.
Virtual (qu)bits are tuples, e.g. `(QuantumRegister(3, 'qr'), 2)` or simply `qr[2]`.
Physical (qu)bits are integers.
"""
import warnings
from qiskit.circuit.quantumregister import Qubit
from qiskit.transpiler.exceptions import LayoutError
from qiskit.converters import isinstanceint
[docs]class Layout():
"""Two-ways dict to represent a Layout."""
def __init__(self, input_dict=None):
"""construct a Layout from a bijective dictionary, mapping
virtual qubits to physical qubits"""
self._p2v = {}
self._v2p = {}
if input_dict is not None:
if not isinstance(input_dict, dict):
raise LayoutError("Layout constructor takes a dict")
self.from_dict(input_dict)
def __repr__(self):
"""Representation of a Layout"""
str_list = []
for key, val in self._p2v.items():
str_list.append("{k}: {v},".format(k=key, v=val))
if str_list:
str_list[-1] = str_list[-1][:-1]
return "Layout({\n" + "\n".join(str_list) + "\n})"
[docs] def from_dict(self, input_dict):
"""Populates a Layout from a dictionary.
The dictionary must be a bijective mapping between
virtual qubits (tuple) and physical qubits (int).
Args:
input_dict (dict):
e.g.::
{(QuantumRegister(3, 'qr'), 0): 0,
(QuantumRegister(3, 'qr'), 1): 1,
(QuantumRegister(3, 'qr'), 2): 2}
Can be written more concisely as follows:
* virtual to physical::
{qr[0]: 0,
qr[1]: 1,
qr[2]: 2}
* physical to virtual::
{0: qr[0],
1: qr[1],
2: qr[2]}
"""
for key, value in input_dict.items():
virtual, physical = Layout.order_based_on_type(key, value)
self._p2v[physical] = virtual
if virtual is None:
continue
self._v2p[virtual] = physical
[docs] @staticmethod
def order_based_on_type(value1, value2):
"""decides which one is physical/virtual based on the type. Returns (virtual, physical)"""
if isinstanceint(value1) and isinstance(value2, (Qubit, type(None))):
physical = int(value1)
virtual = value2
elif isinstanceint(value2) and isinstance(value1, (Qubit, type(None))):
physical = int(value2)
virtual = value1
else:
raise LayoutError('The map (%s -> %s) has to be a (Bit -> integer)'
' or the other way around.' % (type(value1), type(value2)))
return virtual, physical
[docs] def __getitem__(self, item):
if item in self._p2v:
return self._p2v[item]
if item in self._v2p:
return self._v2p[item]
raise KeyError('The item %s does not exist in the Layout' % (item,))
def __setitem__(self, key, value):
virtual, physical = Layout.order_based_on_type(key, value)
self._set_type_checked_item(virtual, physical)
def _set_type_checked_item(self, virtual, physical):
old = self._v2p.pop(virtual, None)
self._p2v.pop(old, None)
old = self._p2v.pop(physical, None)
self._v2p.pop(old, None)
self._p2v[physical] = virtual
if virtual is not None:
self._v2p[virtual] = physical
def __delitem__(self, key):
if isinstance(key, int):
del self._p2v[key]
del self._v2p[self._p2v[key]]
elif isinstance(key, Qubit):
del self._v2p[key]
del self._p2v[self._v2p[key]]
else:
raise LayoutError('The key to remove should be of the form'
' Qubit or integer) and %s was provided' % (type(key),))
[docs] def __len__(self):
return len(self._p2v)
[docs] def copy(self):
"""Returns a copy of a Layout instance."""
layout_copy = type(self)()
layout_copy._p2v = self._p2v.copy()
layout_copy._v2p = self._v2p.copy()
return layout_copy
[docs] def add(self, virtual_bit, physical_bit=None):
"""
Adds a map element between `bit` and `physical_bit`. If `physical_bit` is not
defined, `bit` will be mapped to a new physical bit (extending the length of the
layout by one.)
Args:
virtual_bit (tuple): A (qu)bit. For example, (QuantumRegister(3, 'qr'), 2).
physical_bit (int): A physical bit. For example, 3.
"""
if physical_bit is None:
physical_candidate = len(self)
while physical_candidate in self._p2v:
physical_candidate += 1
physical_bit = physical_candidate
self[virtual_bit] = physical_bit
[docs] def add_register(self, reg):
"""Adds at the end physical_qubits that map each bit in reg.
Args:
reg (Register): A (qu)bit Register. For example, QuantumRegister(3, 'qr').
"""
for bit in reg:
self.add(bit)
[docs] def get_registers(self):
"""
Returns the registers in the layout [QuantumRegister(2, 'qr0'), QuantumRegister(3, 'qr1')]
Returns:
List: A list of Register in the layout
"""
return {bit.register for bit in self.get_virtual_bits()}
[docs] def get_virtual_bits(self):
"""
Returns the dictionary where the keys are virtual (qu)bits and the
values are physical (qu)bits.
"""
return self._v2p
[docs] def get_physical_bits(self):
"""
Returns the dictionary where the keys are physical (qu)bits and the
values are virtual (qu)bits.
"""
return self._p2v
[docs] def swap(self, left, right):
"""Swaps the map between left and right.
Args:
left (tuple or int): Item to swap with right.
right (tuple or int): Item to swap with left.
Raises:
LayoutError: If left and right have not the same type.
"""
if type(left) is not type(right):
raise LayoutError('The method swap only works with elements of the same type.')
temp = self[left]
self[left] = self[right]
self[right] = temp
[docs] def combine_into_edge_map(self, another_layout):
"""Combines self and another_layout into an "edge map".
For example::
self another_layout resulting edge map
qr_1 -> 0 0 <- q_2 qr_1 -> q_2
qr_2 -> 2 2 <- q_1 qr_2 -> q_1
qr_3 -> 3 3 <- q_0 qr_3 -> q_0
The edge map is used to compose dags via, for example, compose.
Args:
another_layout (Layout): The other layout to combine.
Returns:
dict: A "edge map".
Raises:
LayoutError: another_layout can be bigger than self, but not smaller.
Otherwise, raises.
"""
warnings.warn('combine_into_edge_map is deprecated as of 0.14.0 and '
'will be removed in a future release. Instead '
'reorder_bits() should be used', DeprecationWarning,
stacklevel=2)
edge_map = dict()
for virtual, physical in self.get_virtual_bits().items():
if physical not in another_layout._p2v:
raise LayoutError('The wire_map_from_layouts() method does not support when the'
' other layout (another_layout) is smaller.')
edge_map[virtual] = another_layout[physical]
return edge_map
[docs] def reorder_bits(self, bits):
"""Given an ordered list of bits, reorder them according to this layout.
The list of bits must exactly match the virtual bits in this layout.
Args:
bits (list[Bit]): the bits to reorder.
Returns:
List: ordered bits.
"""
order = [0] * len(bits)
# the i-th bit is now sitting in position j
for i, v in enumerate(bits):
j = self[v]
order[i] = j
return order
[docs] @staticmethod
def generate_trivial_layout(*regs):
"""Creates a trivial ("one-to-one") Layout with the registers in `regs`.
Args:
*regs (Registers): registers to include in the layout.
Returns:
Layout: A layout with all the `regs` in the given order.
"""
layout = Layout()
for reg in regs:
layout.add_register(reg)
return layout
[docs] @staticmethod
def from_intlist(int_list, *qregs):
"""Converts a list of integers to a Layout
mapping virtual qubits (index of the list) to
physical qubits (the list values).
Args:
int_list (list): A list of integers.
*qregs (QuantumRegisters): The quantum registers to apply
the layout to.
Returns:
Layout: The corresponding Layout object.
Raises:
LayoutError: Invalid input layout.
"""
if not all(isinstanceint(i) for i in int_list):
raise LayoutError('Expected a list of ints')
if len(int_list) != len(set(int_list)):
raise LayoutError('Duplicate values not permitted; Layout is bijective.')
num_qubits = sum(reg.size for reg in qregs)
# Check if list is too short to cover all qubits
if len(int_list) < num_qubits:
err_msg = 'Integer list length must equal number of qubits in circuit.'
raise LayoutError(err_msg)
out = Layout()
main_idx = 0
for qreg in qregs:
for idx in range(qreg.size):
out[qreg[idx]] = int_list[main_idx]
main_idx += 1
if main_idx != len(int_list):
for int_item in int_list[main_idx:]:
out[int_item] = None
return out
[docs] @staticmethod
def from_qubit_list(qubit_list):
"""
Populates a Layout from a list containing virtual
qubits, Qubit or None.
Args:
qubit_list (list):
e.g.: [qr[0], None, qr[2], qr[3]]
Returns:
Layout: the corresponding Layout object
Raises:
LayoutError: If the elements are not Qubit or None
"""
out = Layout()
for physical, virtual in enumerate(qubit_list):
if virtual is None:
continue
if isinstance(virtual, Qubit):
if virtual in out._v2p:
raise LayoutError('Duplicate values not permitted; Layout is bijective.')
out[virtual] = physical
else:
raise LayoutError("The list should contain elements of the Bits or NoneTypes")
return out