"""This module implements the ChangePredicate class."""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from bqskit.passes.control.predicate import PassPredicate
if TYPE_CHECKING:
from bqskit.compiler.passdata import PassData
from bqskit.ir.circuit import Circuit
_logger = logging.getLogger(__name__)
[docs]
class ChangePredicate(PassPredicate):
"""
The ChangePredicate class.
The ChangePredicate returns true if the circuit has changed since the last
call. On the first call, the predicate returns true.
"""
key = 'ChangePredicate_circuit_hash'
[docs]
def get_truth_value(self, circuit: Circuit, data: PassData) -> bool:
"""Call this predicate, see :class:`PassPredicate` for more info."""
# If first call, record data and return true
if self.key not in data:
_logger.debug(f'Could not find {self.key} key.')
data[self.key] = self.get_hash(circuit)
return True
# Otherwise, check for a change and return accordingly
new_hash = self.get_hash(circuit)
if data[self.key] == new_hash:
# TODO: hash collisions?
_logger.debug('Hashes match; no change detected.')
return False
data[self.key] = new_hash
_logger.debug('Hashes do not match; change detected.')
return True
[docs]
def get_hash(self, circuit: Circuit) -> int:
"""Retreive hash associated with `circuit`."""
_logger.debug('Calculating hash for circuit...')
if len(circuit) == 0:
return hash(tuple())
hashes: list[int] = []
for op in circuit:
hashes.append(hash(repr(op)))
# Don't let the hash list grow too large.
if len(hashes) >= 100:
hashes = [hash(tuple(hashes))]
hash_val = hash(tuple(hashes)) if len(hashes) > 1 else hashes[0]
_logger.debug(f'Hash: {hash_val}')
return hash_val