def decode(self, code, syndrome, **kwargs): """See :meth:`qecsim.model.Decoder.decode`""" # prepare recovery recovery_pauli = code.new_pauli() # ask code for plaquette_indices plaquette_indices = code.syndrome_to_plaquette_indices(syndrome) # for each lattice for lattice in (code.PRIMAL_INDEX, code.DUAL_INDEX): # prepare lattice graph l_graph = gt.SimpleGraph() # select lattice plaquettes l_plaquette_indices = [(la, r, c) for la, r, c in plaquette_indices if la == lattice] # add weighted edges to lattice graph for a_index, b_index in itertools.combinations( l_plaquette_indices, 2): # add edge with taxi-cab distance between a and b l_graph.add_edge(a_index, b_index, self.distance(code, a_index, b_index)) # find MWPM edges {(a, b), (c, d), ...} l_mates = gt.mwpm(l_graph) # iterate edges for a_index, b_index in l_mates: # add path to recover recovery_pauli.path(a_index, b_index) # return recover as bsf return recovery_pauli.to_bsf()
def _matching(cls, graph): """Matching (minimum weight perfect matching) over graph. :param graph: Graph of weighted edges between nodes, as {(a_node, b_node): weight, ...}. :type graph: dict of (object, object) edges to float weights. :return: Matches between nodes as (a_node, b_node). :rtype: set of (object, object) """ return gt.mwpm(graph) # option to switch to networkx or blossom5 here
def _matching(cls, graphs): """Combined matching (minimum weight perfect matching) over given graphs. :param graphs: List of graphs of weighted edges between nodes, as {(a_node, b_node): weight, ...}. :type graphs: list of dict of (object, object) edges to float weights. :return: Matches between nodes as (a_node, b_node). :rtype: set of (object, object) """ matches = set() for graph in graphs: matches.update(gt.mwpm(graph)) # option to switch to networkx or blossom5 here del graph # release heavy object return matches
def decode(self, code, syndrome, **kwargs): """See :meth:`qecsim.model.Decoder.decode`""" # prepare recovery recovery_pauli = code.new_pauli() # get syndrome indices syndrome_indices = code.syndrome_to_plaquette_indices(syndrome) # split indices into primal and dual primal_indices = [i for i in syndrome_indices if code.is_primal(i)] dual_indices = [i for i in syndrome_indices if code.is_dual(i)] # extra virual indices are deliberately well off-boundary to be separate from nearest virtual indices primal_extra_vindex = (-9, -10) dual_extra_vindex = (-10, -9) # for each type of indices and extra virtual index for indices, extra_vindex in (primal_indices, primal_extra_vindex), (dual_indices, dual_extra_vindex): # prepare graph graph = gt.SimpleGraph() # prepare virtual nodes vindices = set() # add weighted edges between nodes and virtual nodes for index in indices: vindex = code.virtual_plaquette_index(index) vindices.add(vindex) distance = self.distance(code, index, vindex) graph.add_edge(index, vindex, distance) # add extra virtual node if odd number of total nodes if (len(indices) + len(vindices)) % 2: vindices.add(extra_vindex) # add weighted edges to graph between all (non-virtual) nodes for a_index, b_index in itertools.combinations(indices, 2): distance = self.distance(code, a_index, b_index) graph.add_edge(a_index, b_index, distance) # add zero weight edges between all virtual nodes for a_index, b_index in itertools.combinations(vindices, 2): graph.add_edge(a_index, b_index, 0) # find MWPM edges {(a, b), (c, d), ...} mates = gt.mwpm(graph) # iterate edges for a_index, b_index in mates: # add path to recover recovery_pauli.path(a_index, b_index) # return recover as bsf return recovery_pauli.to_bsf()
def mwpm(self, matched_indices, syndrome_indices, factor=3, initial=1, box_shape='t', distance_algorithm=4): """ Minimum-weight perfect matching of syndrome indices over a background of matched dual syndrome indices. Notes: * The background is set according to :meth:`set_background`. * A graph of the unmatched foreground indices is created, with appropriate virtual indices, and with edge weights given by :meth:`distance`. * A standard minimum-weight perfect matching is found in the graph. :param matched_indices: Matched pairs of background syndrome indices (dual to foreground). :type matched_indices: frozenset of 2-tuples of 2-tuple of int :param syndrome_indices: Unmatched foreground syndrome indices. :type syndrome_indices: frozenset of 2-tuple of int :param factor: Multiplication factor. (default=3) :type factor: int or float :param initial: Initial edge weight. (default=1) :type initial: int or float :param box_shape: Shape of background boxes. (default='t', 't'=tight, 'r'=rounded, 'f'=fitted, 'l'=loose) :type box_shape: str :param distance_algorithm: Distance algorithm. (default=4, 1=v+h, 2=min(v+h,h+v), 4=min(v+h,h+v,v+h+v,h+v+h) :type distance_algorithm: int :return: Minimum-weight perfect matching of foreground syndrome indices. :rtype: frozenset of 2-tuples of 2-tuple of int """ # set grid background self.set_background(matched_indices, factor=factor, initial=initial, box_shape=box_shape) # prepare graph graph = gt.SimpleGraph() # create lists of nodes and corresponding vnodes # NOTE: encapsulate indices in node objects that implement object reference equality since we may pass # multiple virtual plaquettes with the same index for matching. nodes, vnodes = [], [] for index in syndrome_indices: nodes.append(self._Node(index)) vnodes.append( self._Node(self._code.virtual_plaquette_index(index))) # add weighted edges to graph for a_node, b_node in itertools.chain( itertools.combinations(nodes, 2), # all nodes to all nodes itertools.combinations(vnodes, 2), # all vnodes to all vnodes zip(nodes, vnodes)): # each node to corresponding vnode # find weighted taxi-cab distance between a and b distance = self.distance(a_node.index, b_node.index, algorithm=distance_algorithm) # add edge with weight=distance graph.add_edge(a_node, b_node, distance) # find MWPM edges {(a, b), (c, d), ...} mates = gt.mwpm(graph) # convert to frozenset of sorted tuples {(a_index, b_index), ...}, removing matches if both indices virtual matches = frozenset( tuple(sorted((a.index, b.index))) for a, b in mates if self._code.is_in_bounds(a.index) or self._code.is_in_bounds(b.index)) return matches
def test_mwpm(): graph = {('b', 'c'): 10, ('b', 'd'): 25, ('a', 'c'): 56, ('a', 'b'): 15, ('c', 'd'): 6} mates = gt.mwpm(graph) sorted_mates = {tuple(sorted(match)) for match in mates} expected = {('a', 'b'), ('c', 'd')} assert sorted_mates == expected