Skip to content

Canonicalization

oqd_trical.light_matter.compiler.canonicalize

PruneOperator

Bases: RewriteRule

Prunes an Operator AST by removing zeros

Source code in src\oqd_trical\light_matter\compiler\canonicalize.py
class PruneOperator(RewriteRule):
    """Prunes an Operator AST by removing zeros"""

    def map_OperatorAdd(self, model):
        if isinstance(model.op1, PrunedOperator):
            return model.op2
        if isinstance(model.op2, PrunedOperator):
            return model.op1

    def map_OperatorMul(self, model):
        if isinstance(model.op1, PrunedOperator) or isinstance(
            model.op2, PrunedOperator
        ):
            return PrunedOperator()

    def map_OperatorScalarMul(self, model):
        if isinstance(
            model.coeff, WaveCoefficient
        ) and model.coeff.amplitude == MathNum(value=0):
            return PrunedOperator()
        if isinstance(model.op, PrunedOperator):
            return PrunedOperator()

    def map_OperatorKron(self, model):
        if isinstance(model.op1, PrunedOperator) or isinstance(
            model.op2, PrunedOperator
        ):
            return PrunedOperator()

    def map_Displacement(self, model):
        if isinstance(
            model.alpha, WaveCoefficient
        ) and model.alpha.amplitude == MathNum(value=0):
            return Identity(subsystem=model.subsystem)

PruneZeroPowers

Bases: RewriteRule

Prunes a MathExpr AST by MathPow when base is zero

Source code in src\oqd_trical\light_matter\compiler\canonicalize.py
class PruneZeroPowers(RewriteRule):
    """Prunes a MathExpr AST by MathPow when base is zero"""

    def map_MathPow(self, model):
        if model.expr1 == MathNum(value=0):
            return MathNum(value=0)

OperatorDistributivity

Bases: RewriteRule

Implements distributivity of addition over multiplication, kronecker product and scalar multiplication

Source code in src\oqd_trical\light_matter\compiler\canonicalize.py
class OperatorDistributivity(RewriteRule):
    """Implements distributivity of addition over multiplication, kronecker product and scalar multiplication"""

    def map_OperatorMul(self, model):
        if isinstance(model.op1, (OperatorAdd)):
            return model.op1.__class__(
                op1=OperatorMul(op1=model.op1.op1, op2=model.op2),
                op2=OperatorMul(op1=model.op1.op2, op2=model.op2),
            )
        if isinstance(model.op2, (OperatorAdd)):
            return model.op2.__class__(
                op1=OperatorMul(op1=model.op1, op2=model.op2.op1),
                op2=OperatorMul(op1=model.op1, op2=model.op2.op2),
            )
        if isinstance(model.op1, (OperatorKron)) and isinstance(
            model.op2, (OperatorKron)
        ):
            return OperatorKron(
                op1=OperatorMul(op1=model.op1.op1, op2=model.op2.op1),
                op2=OperatorMul(op1=model.op1.op2, op2=model.op2.op2),
            )
        return None

    def map_OperatorKron(self, model):
        if isinstance(model.op1, (OperatorAdd)):
            return model.op1.__class__(
                op1=OperatorKron(op1=model.op1.op1, op2=model.op2),
                op2=OperatorKron(op1=model.op1.op2, op2=model.op2),
            )
        if isinstance(model.op2, (OperatorAdd)):
            return model.op2.__class__(
                op1=OperatorKron(op1=model.op1, op2=model.op2.op1),
                op2=OperatorKron(op1=model.op1, op2=model.op2.op2),
            )
        return None

    def map_OperatorScalarMul(self, model):
        if isinstance(model.op, (OperatorAdd)):
            return model.op.__class__(
                op1=OperatorScalarMul(op=model.op.op1, coeff=model.coeff),
                op2=OperatorScalarMul(op=model.op.op2, coeff=model.coeff),
            )
        return None

    pass

OperatorAssociativity

Bases: RewriteRule

Implements associativity of addition, multiplication and kronecker product

Source code in src\oqd_trical\light_matter\compiler\canonicalize.py
class OperatorAssociativity(RewriteRule):
    """Implements associativity of addition, multiplication and kronecker product"""

    def map_OperatorAdd(self, model):
        return self._map_addmullkron(model=model)

    def map_OperatorMul(self, model):
        return self._map_addmullkron(model=model)

    def map_OperatorKron(self, model):
        return self._map_addmullkron(model=model)

    def _map_addmullkron(self, model):
        if isinstance(model.op2, model.__class__):
            return model.__class__(
                op1=model.__class__(op1=model.op1, op2=model.op2.op1),
                op2=model.op2.op2,
            )
        return model.__class__(op1=model.op1, op2=model.op2)

GatherCoefficient

Bases: RewriteRule

Gathers the coefficients of an operator into a single scalar multiplication for each term

Source code in src\oqd_trical\light_matter\compiler\canonicalize.py
class GatherCoefficient(RewriteRule):
    """Gathers the coefficients of an operator into a single scalar multiplication for each term"""

    def map_OperatorScalarMul(self, model):
        if isinstance(model.op, OperatorScalarMul):
            return (model.coeff * model.op.coeff) * model.op.op

    def map_OperatorMul(self, model):
        return self._map_mulkron(model)

    def map_OperatorKron(self, model):
        return self._map_mulkron(model)

    def _map_mulkron(self, model):
        if isinstance(model.op1, OperatorScalarMul) and isinstance(
            model.op2, OperatorScalarMul
        ):
            return (
                model.op1.coeff
                * model.op2.coeff
                * model.__class__(op1=model.op1.op, op2=model.op2.op)
            )
        if isinstance(model.op1, OperatorScalarMul):
            return model.op1.coeff * model.__class__(op1=model.op1.op, op2=model.op2)

        if isinstance(model.op2, OperatorScalarMul):
            return model.op2.coeff * model.__class__(op1=model.op1, op2=model.op2.op)

CoefficientDistributivity

Bases: RewriteRule

Implements distributivity of addition over multiplication on coefficient

Source code in src\oqd_trical\light_matter\compiler\canonicalize.py
class CoefficientDistributivity(RewriteRule):
    """Implements distributivity of addition over multiplication on coefficient"""

    def map_CoefficientMul(self, model):
        if isinstance(model.coeff1, (CoefficientAdd)):
            return model.coeff1.__class__(
                coeff1=CoefficientMul(coeff1=model.coeff1.coeff1, coeff2=model.coeff2),
                coeff2=CoefficientMul(coeff1=model.coeff1.coeff2, coeff2=model.coeff2),
            )
        if isinstance(model.coeff2, (CoefficientAdd)):
            return model.coeff2.__class__(
                coeff1=CoefficientMul(coeff1=model.coeff1, coeff2=model.coeff2.coeff1),
                coeff2=CoefficientMul(coeff1=model.coeff1, coeff2=model.coeff2.coeff2),
            )

CoefficientAssociativity

Bases: RewriteRule

Implements associativity of addition, multiplication on coefficient

Source code in src\oqd_trical\light_matter\compiler\canonicalize.py
class CoefficientAssociativity(RewriteRule):
    """Implements associativity of addition, multiplication on coefficient"""

    def map_CoefficientAdd(self, model):
        return self._map_addmul(model=model)

    def map_CoefficientMul(self, model):
        return self._map_addmul(model=model)

    def _map_addmul(self, model):
        if isinstance(model.coeff2, model.__class__):
            return model.__class__(
                coeff1=model.__class__(coeff1=model.coeff1, coeff2=model.coeff2.coeff1),
                coeff2=model.coeff2.coeff2,
            )
        return model.__class__(coeff1=model.coeff1, coeff2=model.coeff2)

ScaleTerms

Bases: RewriteRule

Adds a scalar multiplication for each term if it does not exist

Source code in src\oqd_trical\light_matter\compiler\canonicalize.py
class ScaleTerms(RewriteRule):
    """Adds a scalar multiplication for each term if it does not exist"""

    def __init__(self):
        super().__init__()
        self.op_add_root = False

    def map_AtomicEmulatorGate(self, model):
        self.op_add_root = False

    def map_OperatorAdd(self, model):
        self.op_add_root = True
        if isinstance(model.op1, Union[OperatorAdd, OperatorScalarMul]):
            op1 = model.op1
        else:
            op1 = ConstantCoefficient(value=1) * model.op1
        if isinstance(model.op2, OperatorScalarMul):
            op2 = model.op2
        else:
            op2 = ConstantCoefficient(value=1) * model.op2
        return op1 + op2

    def map_OperatorScalarMul(self, model):
        self.op_add_root = True
        pass

    def map_Operator(self, model):
        if not self.op_add_root:
            self.op_add_root = True
            return ConstantCoefficient(value=1) * model

_CombineTermsHelper

Bases: RewriteRule

Helper for combining terms of the same operator by combining their coefficients

Source code in src\oqd_trical\light_matter\compiler\canonicalize.py
class _CombineTermsHelper(RewriteRule):
    """Helper for combining terms of the same operator by combining their coefficients"""

    def __init__(self):
        super().__init__()

        self.operators = []

    @property
    def coefficients(self):
        return [o[0] for o in self.operators]

    @property
    def terms(self):
        return [o[1] for o in self.operators]

    def emit(self):
        if self.operators == []:
            return PrunedOperator()
        return reduce(
            lambda op1, op2: op1 + op2,
            [o[0] * o[1] for o in self.operators],
        )

    def map_OperatorScalarMul(self, model):
        if model.op in self.terms:
            i = self.terms.index(model.op)
            self.operators[i] = (model.coeff + self.coefficients[i], model.op)
        else:
            self.operators.append((model.coeff, model.op))

CombineTerms

Bases: RewriteRule

Combines terms of the same operator by combining their coefficients

Source code in src\oqd_trical\light_matter\compiler\canonicalize.py
class CombineTerms(RewriteRule):
    """Combines terms of the same operator by combining their coefficients"""

    def map_AtomicEmulatorCircuit(self, model):
        return model.__class__(frame=model.frame, sequence=model.sequence)

    def map_AtomicEmulatorGate(self, model):
        combiner = _CombineTermsHelper()
        Pre(combiner)(model)

        return model.__class__(hamiltonian=combiner.emit(), duration=model.duration)

PartitionMathExpr

Bases: RewriteRule

This separates real and complex portions of [MathExpr][oqd_core.interface.math.MathExpr] objects.

Parameters:

Name Type Description Default
model MathExpr

The rule only acts on [MathExpr][oqd_core.interface.math.MathExpr] objects.

required

Returns:

Name Type Description
model MathExpr
Assumptions

[DistributeMathExpr][oqd_core.compiler.math.rules.DistributeMathExpr], [ProperOrderMathExpr][oqd_core.compiler.math.rules.ProperOrderMathExpr]

Example
  • MathStr(string = '1 + 1j + 2') => MathStr(string = '1 + 2 + 1j')
  • MathStr(string = '1 * 1j * 2') => MathStr(string = '1j * 1 * 2')
Source code in src\oqd_trical\light_matter\compiler\canonicalize.py
class PartitionMathExpr(RewriteRule):
    """
    This separates real and complex portions of [`MathExpr`][oqd_core.interface.math.MathExpr] objects.

    Args:
        model (MathExpr): The rule only acts on [`MathExpr`][oqd_core.interface.math.MathExpr] objects.

    Returns:
        model (MathExpr):

    Assumptions:
        [`DistributeMathExpr`][oqd_core.compiler.math.rules.DistributeMathExpr],
        [`ProperOrderMathExpr`][oqd_core.compiler.math.rules.ProperOrderMathExpr]

    Example:
        - MathStr(string = '1 + 1j + 2') => MathStr(string = '1 + 2 + 1j')
        - MathStr(string = '1 * 1j * 2') => MathStr(string = '1j * 1 * 2')
    """

    def map_MathAdd(self, model):
        priority = dict(
            MathImag=5, MathNum=4, MathVar=3, MathFunc=2, MathPow=1, MathMul=0
        )

        if isinstance(
            model.expr2, (MathImag, MathNum, MathVar, MathFunc, MathPow, MathMul)
        ):
            if isinstance(model.expr1, MathAdd):
                if (
                    priority[model.expr2.__class__.__name__]
                    > priority[model.expr1.expr2.__class__.__name__]
                ):
                    return MathAdd(
                        expr1=MathAdd(expr1=model.expr1.expr1, expr2=model.expr2),
                        expr2=model.expr1.expr2,
                    )
            else:
                if (
                    priority[model.expr2.__class__.__name__]
                    > priority[model.expr1.__class__.__name__]
                ):
                    return MathAdd(
                        expr1=model.expr2,
                        expr2=model.expr1,
                    )

    def map_MathMul(self, model: MathMul):
        priority = dict(MathImag=4, MathNum=3, MathVar=2, MathFunc=1, MathPow=0)

        if isinstance(model.expr2, (MathImag, MathNum, MathVar, MathFunc, MathPow)):
            if isinstance(model.expr1, MathMul):
                if (
                    priority[model.expr2.__class__.__name__]
                    > priority[model.expr1.expr2.__class__.__name__]
                ):
                    return MathMul(
                        expr1=MathMul(expr1=model.expr1.expr1, expr2=model.expr2),
                        expr2=model.expr1.expr2,
                    )
            else:
                if (
                    priority[model.expr2.__class__.__name__]
                    > priority[model.expr1.__class__.__name__]
                ):
                    return MathMul(
                        expr1=model.expr2,
                        expr2=model.expr1,
                    )

canonicalize_math_factory()

Creates a new instance of the canonicalization pass for math expressions

Source code in src\oqd_trical\light_matter\compiler\canonicalize.py
def canonicalize_math_factory():
    """Creates a new instance of the canonicalization pass for math expressions"""
    return Chain(
        FixedPoint(
            Post(
                Chain(
                    PruneMathExpr(),
                    PruneZeroPowers(),
                    SimplifyMathExpr(),
                    DistributeMathExpr(),
                    ProperOrderMathExpr(),
                )
            )
        ),
        FixedPoint(Post(PartitionMathExpr())),
    )

canonicalize_coefficient_factory()

Creates a new instance of the canonicalization pass for coefficients

Source code in src\oqd_trical\light_matter\compiler\canonicalize.py
def canonicalize_coefficient_factory():
    """Creates a new instance of the canonicalization pass for coefficients"""
    return Chain(
        FixedPoint(
            Post(
                Chain(
                    PruneCoefficient(),
                    CoefficientDistributivity(),
                    CombineCoefficient(),
                    CoefficientAssociativity(),
                )
            )
        ),
        FixedPoint(Post(Chain(SortCoefficient(), CombineCoefficient()))),
    )

canonicalize_operator_factory()

Creates a new instance of the canonicalization pass for operators

Source code in src\oqd_trical\light_matter\compiler\canonicalize.py
def canonicalize_operator_factory():
    """Creates a new instance of the canonicalization pass for operators"""
    return FixedPoint(
        Post(
            Chain(
                OperatorDistributivity(),
                GatherCoefficient(),
                OperatorAssociativity(),
            )
        )
    )

canonicalize_emulator_circuit_factory()

Creates a new instance of the canonicalization pass for AtomicEmulatorCircuit

Source code in src\oqd_trical\light_matter\compiler\canonicalize.py
def canonicalize_emulator_circuit_factory():
    """Creates a new instance of the canonicalization pass for AtomicEmulatorCircuit"""
    return Chain(
        canonicalize_operator_factory(),
        canonicalize_coefficient_factory(),
        canonicalize_math_factory(),
        Post(PruneOperator()),
        Pre(ScaleTerms()),
        Post(CombineTerms()),
        canonicalize_coefficient_factory(),
        canonicalize_math_factory(),
        Post(PruneOperator()),
    )