# Copyright 2018-2024 Xanadu Quantum Technologies Inc.# Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at# http://www.apache.org/licenses/LICENSE-2.0# Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.# pylint: disable=protected-access"""Contains a utility class ``BooleanFn`` that allows logical compositionof functions with boolean output."""importfunctools# pylint: disable=unnecessary-lambda
[docs]classBooleanFn:r"""Wrapper for simple callables with Boolean output that can be manipulated and combined with bitwise operators. Args: fn (callable): Function to be wrapped. It can accept any number of arguments, and must return a Boolean. **Example** Consider functions that filter numbers to lie within a certain domain. We may wrap them using ``BooleanFn``: .. code-block:: python bigger_than_4 = qml.BooleanFn(lambda x: x > 4) smaller_than_10 = qml.BooleanFn(lambda x: x < 10) is_int = qml.BooleanFn(lambda x: isinstance(x, int)) >>> bigger_than_4(5.2) True >>> smaller_than_10(20.1) False >>> is_int(2.3) False These can then be combined into a single callable using boolean operators, such as ``&`` (logical and): >>> between_4_and_10 = bigger_than_4 & smaller_than_10 >>> between_4_and_10(-3.2) False >>> between_4_and_10(9.9) True >>> between_4_and_10(19.7) False Other supported operators are ``|`` (logical or) and ``~`` (logical not): .. code-block:: python smaller_equal_than_4 = ~bigger_than_4 smaller_than_10_or_int = smaller_than_10 | is_int .. warning:: Note that Python conditional expressions are evaluated from left to right. As a result, the order of composition may matter, even though logical operators such as ``|`` and ``&`` are symmetric. For example: >>> is_int = qml.BooleanFn(lambda x: isinstance(x, int)) >>> has_bit_length_3 = qml.BooleanFn(lambda x: x.bit_length()==3) >>> (is_int & has_bit_length_3)(4) True >>> (is_int & has_bit_length_3)(2.3) False >>> (has_bit_length_3 & is_int)(2.3) AttributeError: 'float' object has no attribute 'bit_length' """def__init__(self,fn,name=None):self.fn=fnself.name=nameorself.fn.__name__functools.update_wrapper(self,fn)def__and__(self,other):returnAnd(self,other)def__or__(self,other):returnOr(self,other)def__xor__(self,other):returnXor(self,other)def__invert__(self):returnNot(self)def__call__(self,*args,**kwargs):returnself.fn(*args,**kwargs)def__repr__(self):returnf"BooleanFn({self.name})"ifnot(self.bitwiseorself.conditional)elseself.name@propertydefbitwise(self):"""Determine whether the wrapped callable performs a bitwise operation or not. This checks for the ``operands`` attribute that should be defined by it."""returnbool(getattr(self,"operands",tuple()))@propertydefconditional(self):"""Determine whether the wrapped callable is for a conditional or not. This checks for the ``condition`` attribute that should be defined by it."""returnbool(getattr(self,"condition",None))
[docs]classAnd(BooleanFn):"""Developer facing class for implemeting bitwise ``AND`` for callables wrapped up with :class:`BooleanFn <pennylane.BooleanFn>`. Args: left (~.BooleanFn): Left operand in the bitwise expression. right (~.BooleanFn): Right operand in the bitwise expression. """def__init__(self,left,right):self.operands=(left,right)ifany(getattr(opr,"condition",None)foroprinself.operands):self.condition=tuple(getattr(opr,"condition",())foroprinself.operands)super().__init__(lambda*args,**kwargs:left(*args,**kwargs)andright(*args,**kwargs),f"And({left.name}, {right.name})",)def__str__(self):returnf"{self.operands[0].name} & {self.operands[1].name}"
[docs]classOr(BooleanFn):"""Developer facing class for implemeting bitwise ``OR`` for callables wrapped up with :class:`BooleanFn <pennylane.BooleanFn>`. Args: left (~.BooleanFn): Left operand in the bitwise expression. right (~.BooleanFn): Right operand in the bitwise expression. """def__init__(self,left,right):self.operands=(left,right)ifany(getattr(opr,"condition",None)foroprinself.operands):self.condition=tuple(getattr(opr,"condition",())foroprinself.operands)super().__init__(lambda*args,**kwargs:left(*args,**kwargs)orright(*args,**kwargs),f"Or({left.name}, {right.name})",)def__str__(self):returnf"{self.operands[0].name} | {self.operands[1].name}"
[docs]classXor(BooleanFn):"""Developer facing class for implemeting bitwise ``XOR`` for callables wrapped up with :class:`BooleanFn <pennylane.BooleanFn>`. Args: left (~.BooleanFn): Left operand in the bitwise expression. right (~.BooleanFn): Right operand in the bitwise expression. """def__init__(self,left,right):self.operands=(left,right)ifany(getattr(opr,"condition",None)foroprinself.operands):self.condition=tuple(getattr(opr,"condition",())foroprinself.operands)super().__init__(lambda*args,**kwargs:left(*args,**kwargs)^right(*args,**kwargs),f"Xor({left.name}, {right.name})",)def__str__(self):returnf"{self.operands[0].name} ^ {self.operands[1].name}"
[docs]classNot(BooleanFn):"""Developer facing class for implemeting bitwise ``NOT`` for callables wrapped up with :class:`BooleanFn <pennylane.BooleanFn>`. Args: left (~.BooleanFn): Left operand in the bitwise expression. """def__init__(self,left):self.operands=(left,)ifany(getattr(opr,"condition",None)foroprinself.operands):self.condition=tuple(getattr(opr,"condition",())foroprinself.operands)super().__init__(lambda*args,**kwargs:notleft(*args,**kwargs),f"Not({left.name})",)def__str__(self):returnf"~{self.operands[0].name}"