Source code for qecc.pred

#!/usr/bin/python
# -*- coding: utf-8 -*-
##
# pred.py: Predicate library for use with stabilizer groups.
##
# © 2012 Christopher E. Granade (cgranade@gmail.com) and
#     Ben Criger (bcriger@gmail.com).
# This file is a part of the QuaEC project.
# Licensed under the AGPL version 3.
##
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
##

## IMPORTS ##
from sys import version_info
if version_info[0] == 3:
    PY3 = True
    from importlib import reload
elif version_info[0] == 2:
    PY3 = False
else:
    raise EnvironmentError("sys.version_info refers to a version of "
        "Python neither 2 nor 3. This is not permitted. "
        "sys.version_info = {}".format(version_info))

import operator as op
import itertools as it

if PY3:
    from . import PauliClass as pc
else:
    import PauliClass as pc

## ALL ##

# The following names will get imported by
# "from pred import *".
__all__ = [
    'Predicate',
    'AllPredicate', 'AnyPredicate',
    'SetMembershipPredicate', 'PauliMembershipPredicate',
    'commutes_with', 'in_group_generated_by'
]

## CLASSES ##

[docs]class Predicate(object): """ Class representing a predicate function on one or more arguments. >>> from qecc import Predicate >>> p = Predicate(lambda x: x > 0) >>> p(1) True >>> p(-1) False Instances can also be constructed by logical operations on existing Predicate instances: >>> q = Predicate(lambda x: x < 3) >>> (p & q)(1) True >>> (p | q)(-1) True >>> (~p)(2) False """ def __init__(self, fn): self.fn = fn def __call__(self, *args, **kwargs): return self.fn(*args, **kwargs)
[docs] def combine(self, other, outer_fn): """ Returns a new :class:`Predicate` that combines this predicate with another predicate using a given function to combine the results. >>> gt_2 = Predicate(lambda x: x > 2) >>> even = Predicate(lambda x: x % 2 == 0) >>> nand = lambda x, y: not (x and y) >>> r = gt_2.combine(even, nand) >>> map(r, range(1,5)) [True, True, True, False] """ def new_predicate(*args, **kwargs): return outer_fn(self(*args, **kwargs), other(*args, **kwargs)) return Predicate(new_predicate)
def __and__(self, other): return AllPredicate(self, other) def __or__(self, other): return AnyPredicate(self, other) def __invert__(self): def not_fn(*args, **kwargs): return not self(*args, **kwargs) return Predicate(not_fn)
class AllPredicate(Predicate): """ Predicate class representing the logical AND of several other predicates. Given two predicates ``p`` and ``q``, >>> p = lambda: True >>> q = lambda: False >>> p_and_q = AllPredicate(p, q) >>> p_and_q () == p () & q () True is equivalent to ``p_and_q = p & q``. """ def __init__(self, *preds): self.preds = preds def __call__(self, *args, **kwargs): return all(p(*args, **kwargs) for p in self.preds) def __and__(self, other): if isinstance(other, AllPredicate): return AllPredicate(*(self.preds + other.preds)) else: return AllPredicate(*(self.preds + (other,))) class AnyPredicate(Predicate): """ Predicate class representing the logical OR of several other predicates. Given two predicates ``p`` and ``q``, >>> p = lambda: True >>> q = lambda: False >>> p_or_q = AnyPredicate(p, q) >>> p_or_q () == p () | q () True is equivalent to ``p_and_q = p | q``. """ def __init__(self, *preds): self.preds = preds def __call__(self, *args, **kwargs): return any(p(*args, **kwargs) for p in self.preds) def __or__(self, other): if isinstance(other, AnyPredicate): return AnyPredicate(*(self.preds + other.preds)) else: return AnyPredicate(*(self.preds + (other,)))
[docs]class SetMembershipPredicate(Predicate): """ Given an iterable ``S``, constructs a predicate that returns ``True`` if and only if its argument is in ``S``. >>> from qecc import SetMembershipPredicate >>> p = SetMembershipPredicate(range(4)) >>> map(p, range(-1, 5)) [False, True, True, True, True, False] """ def __init__(self, S): self.S = set(S) def __call__(self, x): return x in self.S def __repr__(self): return "Predicate: f(x) = True if x in {0}".format(repr(self.S))
[docs]class PauliMembershipPredicate(SetMembershipPredicate): """ Given a set ``S`` of Pauli operators represented as :class:`qecc.Pauli` instances, constructs a predicate that returns ``True`` for a Pauli ``P`` if and only if ``P`` is in ``S``. If the keyword argument ``ignore_phase`` is ``True``, then the comparison to determine whether ``P`` is in ``S`` only considers the operator part of ``P``. """ def __init__(self, S, ignore_phase=True): super(PauliMembershipPredicate, self).__init__( [pc.Pauli(P.op) for P in S] if ignore_phase else S) self.ignore_phase = ignore_phase def __call__(self, P): if self.ignore_phase: P = pc.Pauli(P.op) return P in self.S
## USEFUL PREDICATES ##
[docs]def commutes_with(*paulis): """ Returns a predicate that checks whether a Pauli ``P`` commutes with each of a given list of Pauli operators. """ paulis = list(map(pc.ensure_pauli, paulis)) def pred_fn(P): # Using imap here instead of map allows all() to short-circuit. return all(it.imap(lambda Q: pc.com(P, Q) == 0, paulis)) return Predicate(pred_fn)
[docs]def in_group_generated_by(*paulis): """ Returns a predicate that selects Pauli operators in the group generated by a given list of generators. """ # Warning: This is inefficient for large groups! paulis = list(map(pc.ensure_pauli, paulis)) return PauliMembershipPredicate(pc.from_generators(paulis), ignore_phase=True)
## TEST ## if __name__ == "__main__": p = Predicate(lambda x: x > 0) q = Predicate(lambda x: x < 3) p_and_q = p & q p_or_q = p | q not_p = ~p for test in [2, 4, -1]: print(test, p(test), q(test), p_and_q(test), p_or_q(test), not_p(test)) print(list(filter(p_and_q, list(range(-4,5))))) S = set([1, 2, 3]) in_S = SetMembershipPredicate(S) print(list(map(in_S, list(range(-1, 5))))) print(list(filter( commutes_with('XX', 'ZZ') & ~in_group_generated_by('XX'), pc.pauli_group(2) )))