Source code for acqdp.tensor_network.order_finder
import sys
from acqdp.tensor_network.local_optimizer import defaultOrderResolver
if sys.version_info < (3, 0):
sys.stdout.write("Sorry, requires Python 3.x, not Python 2.x\n")
sys.exit(1)
[docs]class OrderFinder:
"""
:class:`OrderFinder` class is dedicated to finding a contraction scheme corresponding to a given tensor network structure.
The main method of an :class:`OrderFinder` is `find_order`, which takes a `TensorNetwork` as input, and yields a
generator of `ContractionScheme`.The base class offers preliminary order finding schemes. For more advanced
hypergraph-based approach, use :class:`KHPOrderFinder`. For finding contraction schemes with sliced edges,
use :class:`SlicedOrderFinder`.
:ivar order_method: 'default' order contracts the tensor one by one by order; 'vertical' order contracts the tensor based
on the vertical ordering. When the tensor network is from a quantum circuit, the vertical order corresponds to the
tensor network contraction where the tensors connected to a qubit is first contracted together and then merged to a
main one according to a given order of the qubits.
"""
def __init__(self,
order_method='default',
**kwargs):
self.order_method = order_method
[docs] def find_order(self, tn, **kwargs):
"""Find a contraction scheme for the input tensor network subject to the constraints given in the
:class:`OrderFinder`.
:param tn: A tensor network for which the contraction scheme is to be determined.
:type tn: :class:`TensorNetwork`
:yields: A :class:`ContractionScheme` containing the pairwise contraction order, a list of edges to be sliced, and
optionally the total contraction cost.
"""
tn = tn._expand_and_delta()
if self.order_method == 'default':
a = lambda b: b # noqa: E731
elif self.order_method == 'vertical':
qubit_order = kwargs.get('qubit_order', sorted(set(b[1] for b in tn.nodes_by_name)))
a = lambda b: (qubit_order.index(b[1]), b[0], b[2:]) # noqa: E731
else:
raise ValueError("order method not implemented")
try:
nodes_list = sorted(tn.nodes_by_name, key=a)
except TypeError:
nodes_list = sorted(tn.nodes_by_name, key=lambda x: str(x))
o = []
if len(nodes_list) > 1:
k = nodes_list[0]
for i in range(len(nodes_list) - 1):
new_k = ('#', i)
o.append([[nodes_list[i + 1], k], new_k])
k = new_k
if len(o) > 0:
o[-1][-1] = '#'
res = defaultOrderResolver.order_to_contraction_scheme(tn, o)
while True:
yield res
[docs]class SlicedOrderFinder(OrderFinder):
"""
:class: `SlicedOrderFinder` finds a sliced contraction scheme based on unsliced contraction schemes found by its base
order finder.
:ivar base_order_finder: The base order finder of the sliced order finder, from which the `SlicedOrderFinder` fetches
contraction schemes and do slicing on it.
:ivar slicer: The slicing algorithm acting upon the contraction schemes.
:ivar num_candidates: Number of unsliced contraction schemes to feed to the slicer at a time. Set to 20 by default.
"""
def __init__(self,
base_order_finder={'order_finder_name': 'khp'},
slicer={'slicer_name': 'default'},
num_candidates=20,
**kwargs):
self.base_order_finder = base_order_finder
self.num_candidates = num_candidates
from acqdp.tensor_network.order_finder import get_order_finder
from acqdp.tensor_network.slicer import get_slicer
self.base_order_finder = get_order_finder(**base_order_finder)
self.slicer = get_slicer(**slicer)
def find_order(self, tn, **kwargs):
tn = tn._expand_and_delta()
order_gen = self.base_order_finder.find_order(graph=tn)
next(order_gen)
while True:
res = self.slicer.slice(tn, order_gen)
yield res
def get_order_finder(**kwargs):
from acqdp.tensor_network.kahypar_order_finder import KHPOrderFinder
order_finders = {
'khp': KHPOrderFinder,
'default': OrderFinder,
'sliced': SlicedOrderFinder
}
order_finder_name = kwargs.get('order_finder_name', 'default')
return (order_finders[order_finder_name])(
**kwargs.get('order_finder_params', {}))