Skip to content

Canonicalization

Passes

oqd_core.compiler.analog.passes.canonicalize

analog_operator_canonicalization(model)

This pass runs canonicalization chain for Operators with a verifies for canonicalization.

Parameters:

Name Type Description Default
model VisitableBaseModel
required

Returns:

Name Type Description
model VisitableBaseModel

Operator of Analog level are in canonical form

Assumptions

None

Example
  • for model = X@(Y + Z), output is 1*(X@Y) + 1 * (X@Z)
  • for model = [AnalogGate][oqd_core.interface.analog.operations.AnalogGate](hamiltonian = (A * J)@X), output is [AnalogGate][oqd_core.interface.analog.operations.AnalogGate](hamiltonian = 1 * (X@A)) (where A = Annhiliation(), J = Identity() [Ladder])
Acknowledgement

This code was inspired by Liang.jl.

Source code in oqd-core/src/oqd_core/compiler/analog/passes/canonicalize.py
def analog_operator_canonicalization(model):
    """
    This pass runs canonicalization chain for Operators with a verifies for canonicalization.

    Args:
        model (VisitableBaseModel):

    Returns:
        model (VisitableBaseModel):  [`Operator`][oqd_core.interface.analog.operator.Operator] of Analog level are in canonical form

    Assumptions:
        None

    Example:
        - for model = X@(Y + Z), output is 1*(X@Y) + 1 * (X@Z)
        - for model = [`AnalogGate`][oqd_core.interface.analog.operations.AnalogGate](hamiltonian = (A * J)@X), output is
            [`AnalogGate`][oqd_core.interface.analog.operations.AnalogGate](hamiltonian = 1 * (X@A))
            (where A = Annhiliation(), J = Identity() [Ladder])

    Acknowledgement:
        This code was inspired by [Liang.jl](https://github.com/Roger-luo/Liang.jl/blob/main/src/canonicalize/entry.jl#L8).
    """
    return Chain(
        FixedPoint(dist_chain),
        FixedPoint(Post(ProperOrder())),
        FixedPoint(pauli_chain),
        FixedPoint(Post(GatherPauli())),
        In(VerifyHilberSpaceDim(), reverse=True),
        FixedPoint(normal_order_chain),
        FixedPoint(Post(PruneIdentity())),
        FixedPoint(scale_terms_chain),
        FixedPoint(Post(SortedOrder())),
        math_chain,
        verify_canonicalization,
    )(model=model)

Rewrite Rules

oqd_core.compiler.analog.rewrite.canonicalize

OperatorDistribute

Bases: RewriteRule

RewriteRule which distributes operators of hamiltonians

Parameters:

Name Type Description Default
model VisitableBaseModel
required

Returns:

Name Type Description
model VisitableBaseModel
Assumptions

GatherMathExpr (sometimes)

Example

X@(Y+Z) => X@Y + X@Z

Source code in oqd-core/src/oqd_core/compiler/analog/rewrite/canonicalize.py
class OperatorDistribute(RewriteRule):
    """
    RewriteRule which distributes operators of hamiltonians

    Args:
        model (VisitableBaseModel):

    Returns:
        model (VisitableBaseModel):

    Assumptions:
        [`GatherMathExpr`][oqd_core.compiler.analog.rewrite.canonicalize.GatherMathExpr] (sometimes)

    Example:
        X@(Y+Z) => X@Y + X@Z
    """

    def map_OperatorMul(self, model: OperatorMul):
        if isinstance(model.op1, (OperatorAdd, OperatorSub)):
            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, OperatorSub)):
            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: OperatorKron):
        if isinstance(model.op1, (OperatorAdd, OperatorSub)):
            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, OperatorSub)):
            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: OperatorScalarMul):

        if isinstance(model.op, (OperatorAdd, OperatorSub)):
            return model.op.__class__(
                op1=OperatorScalarMul(op=model.op.op1, expr=model.expr),
                op2=OperatorScalarMul(op=model.op.op2, expr=model.expr),
            )
        return None

    def map_OperatorSub(self, model: OperatorSub):
        return OperatorAdd(
            op1=model.op1,
            op2=OperatorScalarMul(op=model.op2, expr=MathNum(value=-1)),
        )

GatherMathExpr

Bases: RewriteRule

Gathers the math expressions of Operator so that we have math_expr * ( Operator without scalar multiplication)

Parameters:

Name Type Description Default
model VisitableBaseModel

The rule only modifies Operator in Analog level.

required

Returns:

Name Type Description
model VisitableBaseModel
Assumptions

OperatorDistribute (sometimes)

Example

(1 * X) @ (2 * Y) => (1 * 2) => (1 * 2) * (X @ Y)

Source code in oqd-core/src/oqd_core/compiler/analog/rewrite/canonicalize.py
class GatherMathExpr(RewriteRule):
    """
    Gathers the math expressions of  [`Operator`][oqd_core.interface.analog.operator.Operator] so that we have math_expr * ( [`Operator`][oqd_core.interface.analog.operator.Operator] without scalar multiplication)

    Args:
        model (VisitableBaseModel):
            The rule only modifies [`Operator`][oqd_core.interface.analog.operator.Operator] in Analog level.

    Returns:
        model (VisitableBaseModel):

    Assumptions:
        [`OperatorDistribute`][oqd_core.compiler.analog.rewrite.canonicalize.OperatorDistribute] (sometimes)

    Example:
        (1 * X) @ (2 * Y) => (1 * 2) => (1 * 2) * (X @ Y)
    """

    def map_OperatorScalarMul(self, model: OperatorScalarMul):

        if isinstance(model.op, OperatorScalarMul):
            return model.expr * model.op.expr * model.op.op

        return None

    def map_OperatorMul(self, model: OperatorMul):
        return self._mulkron(model)

    def map_OperatorKron(self, model: OperatorKron):
        return self._mulkron(model)

    def _mulkron(self, model: Union[OperatorMul, OperatorKron]):
        if isinstance(model.op1, OperatorScalarMul) and isinstance(
            model.op2, OperatorScalarMul
        ):
            return (
                model.op1.expr
                * model.op2.expr
                * model.__class__(op1=model.op1.op, op2=model.op2.op)
            )
        if isinstance(model.op1, OperatorScalarMul):
            return model.op1.expr * model.__class__(op1=model.op1.op, op2=model.op2)

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

GatherPauli

Bases: RewriteRule

Gathers ladders and paulis so that we have paulis and then ladders

Parameters:

Name Type Description Default
model VisitableBaseModel

The rule only modifies Operator in Analog level

required

Returns:

Name Type Description
model VisitableBaseModel
Assumptions

GatherMathExpr, OperatorDistribute, ProperOrder Operator

Example

X@A@Y => X@Y@A

Source code in oqd-core/src/oqd_core/compiler/analog/rewrite/canonicalize.py
class GatherPauli(RewriteRule):
    """
    Gathers ladders and paulis so that we have paulis and then ladders

    Args:
        model (VisitableBaseModel):
            The rule only modifies [`Operator`][oqd_core.interface.analog.operator.Operator] in Analog level

    Returns:
        model (VisitableBaseModel):

    Assumptions:
        [`GatherMathExpr`][oqd_core.compiler.analog.rewrite.canonicalize.GatherMathExpr],
        [`OperatorDistribute`][oqd_core.compiler.analog.rewrite.canonicalize.OperatorDistribute],
        [`ProperOrder`][oqd_core.compiler.analog.rewrite.canonicalize.ProperOrder]
        [`Operator`][oqd_core.interface.analog.operator.Operator]

    Example:
        X@A@Y => X@Y@A
    """

    def map_OperatorKron(self, model: OperatorKron):
        if isinstance(model.op2, Pauli):
            if isinstance(model.op1, Ladder):
                return OperatorKron(
                    op1=model.op2,
                    op2=model.op1,
                )
            if isinstance(model.op1, OperatorMul) and isinstance(model.op1.op2, Ladder):
                return OperatorKron(
                    op1=model.op2,
                    op2=model.op1,
                )
            if isinstance(model.op1, OperatorKron) and isinstance(
                model.op1.op2, Union[Ladder, OperatorMul]
            ):
                return OperatorKron(
                    op1=OperatorKron(op1=model.op1.op1, op2=model.op2),
                    op2=model.op1.op2,
                )
        return None

PruneIdentity

Bases: RewriteRule

Removes unnecessary ladder Identities from operators

Parameters:

Name Type Description Default
model VisitableBaseModel
required

Returns:

Name Type Description
model VisitableBaseModel
Assumptions

GatherMathExpr, OperatorDistribute, ProperOrder, GatherPauli, NormalOrder

Example

A*J => A

Source code in oqd-core/src/oqd_core/compiler/analog/rewrite/canonicalize.py
class PruneIdentity(RewriteRule):
    """
    Removes unnecessary ladder Identities from operators

    Args:
        model (VisitableBaseModel):

    Returns:
        model (VisitableBaseModel):

    Assumptions:
        [`GatherMathExpr`][oqd_core.compiler.analog.rewrite.canonicalize.GatherMathExpr],
        [`OperatorDistribute`][oqd_core.compiler.analog.rewrite.canonicalize.OperatorDistribute],
        [`ProperOrder`][oqd_core.compiler.analog.rewrite.canonicalize.ProperOrder],
        [`GatherPauli`][oqd_core.compiler.analog.rewrite.canonicalize.GatherPauli],
        [`NormalOrder`][oqd_core.compiler.analog.rewrite.canonicalize.NormalOrder]

    Example:
        A*J => A
    """

    def map_OperatorMul(self, model: OperatorMul):
        if isinstance(model.op1, (Identity)):
            return model.op2
        if isinstance(model.op2, (Identity)):
            return model.op1
        return None

PauliAlgebra

Bases: RewriteRule

RewriteRule for Pauli algebra operations

Parameters:

Name Type Description Default
model VisitableBaseModel
required

Returns:

Name Type Description
model VisitableBaseModel
Assumptions

GatherMathExpr, OperatorDistribute, ProperOrder

Example

X*Y => iZ

Source code in oqd-core/src/oqd_core/compiler/analog/rewrite/canonicalize.py
class PauliAlgebra(RewriteRule):
    """
    RewriteRule for Pauli algebra operations

    Args:
        model (VisitableBaseModel):

    Returns:
        model (VisitableBaseModel):

    Assumptions:
        [`GatherMathExpr`][oqd_core.compiler.analog.rewrite.canonicalize.GatherMathExpr],
        [`OperatorDistribute`][oqd_core.compiler.analog.rewrite.canonicalize.OperatorDistribute],
        [`ProperOrder`][oqd_core.compiler.analog.rewrite.canonicalize.ProperOrder]

    Example:
        X*Y => iZ
    """

    def map_OperatorMul(self, model: OperatorMul):
        if isinstance(model.op1, Pauli) and isinstance(model.op2, Pauli):
            if isinstance(model.op1, PauliI):
                return model.op2
            if isinstance(model.op2, PauliI):
                return model.op1
            if model.op1 == model.op2:
                return PauliI()
            if isinstance(model.op1, PauliX) and isinstance(model.op2, PauliY):
                return OperatorScalarMul(op=PauliZ(), expr=MathImag())
            if isinstance(model.op1, PauliY) and isinstance(model.op2, PauliZ):
                return OperatorScalarMul(op=PauliX(), expr=MathImag())
            if isinstance(model.op1, PauliZ) and isinstance(model.op2, PauliX):
                return OperatorScalarMul(op=PauliY(), expr=MathImag())
            return OperatorScalarMul(
                op=OperatorMul(op1=model.op2, op2=model.op1),
                expr=MathNum(value=-1),
            )
        return None

NormalOrder

Bases: RewriteRule

Arranges Ladder oeprators in normal order form

Parameters:

Name Type Description Default
model VisitableBaseModel
required

Returns:

Name Type Description
model VisitableBaseModel
Assumptions

GatherMathExpr, OperatorDistribute, ProperOrder, GatherPauli

Example

AC => CA + J

Source code in oqd-core/src/oqd_core/compiler/analog/rewrite/canonicalize.py
class NormalOrder(RewriteRule):
    """
    Arranges Ladder oeprators in normal order form

    Args:
        model (VisitableBaseModel):

    Returns:
        model (VisitableBaseModel):

    Assumptions:
        [`GatherMathExpr`][oqd_core.compiler.analog.rewrite.canonicalize.GatherMathExpr],
        [`OperatorDistribute`][oqd_core.compiler.analog.rewrite.canonicalize.OperatorDistribute],
        [`ProperOrder`][oqd_core.compiler.analog.rewrite.canonicalize.ProperOrder],
        [`GatherPauli`][oqd_core.compiler.analog.rewrite.canonicalize.GatherPauli]

    Example:
        A*C => C*A + J
    """

    def map_OperatorMul(self, model: OperatorMul):
        if isinstance(model.op2, Creation):
            if isinstance(model.op1, Annihilation):
                return OperatorAdd(
                    op1=OperatorMul(op1=model.op2, op2=model.op1), op2=Identity()
                )
            if isinstance(model.op1, Identity):
                return OperatorMul(op1=model.op2, op2=model.op1)
            if isinstance(model.op1, OperatorMul) and isinstance(
                model.op1.op2, (Annihilation, Identity)
            ):
                return OperatorMul(
                    op1=model.op1.op1,
                    op2=OperatorMul(op1=model.op1.op2, op2=model.op2),
                )
        return model

ProperOrder

Bases: RewriteRule

Converts expressions to proper order bracketing. Please see example for clarification.

Parameters:

Name Type Description Default
model VisitableBaseModel
required

Returns:

Name Type Description
model VisitableBaseModel
Assumptions

GatherMathExpr, OperatorDistribute

Example

X @ (Y @ Z) => (X @ Y) @ Z

Source code in oqd-core/src/oqd_core/compiler/analog/rewrite/canonicalize.py
class ProperOrder(RewriteRule):
    """
    Converts expressions to proper order bracketing. Please see example for clarification.

    Args:
        model (VisitableBaseModel):

    Returns:
        model (VisitableBaseModel):

    Assumptions:
        [`GatherMathExpr`][oqd_core.compiler.analog.rewrite.canonicalize.GatherMathExpr],
        [`OperatorDistribute`][oqd_core.compiler.analog.rewrite.canonicalize.OperatorDistribute]

    Example:
        X @ (Y @ Z) =>  (X @ Y) @ Z
    """

    def map_OperatorAdd(self, model: OperatorAdd):
        return self._addmullkron(model=model)

    def map_OperatorMul(self, model: OperatorMul):
        return self._addmullkron(model=model)

    def map_OperatorKron(self, model: OperatorKron):
        return self._addmullkron(model=model)

    def _addmullkron(self, model: Union[OperatorAdd, OperatorMul, OperatorKron]):
        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)

ScaleTerms

Bases: RewriteRule

Scales operators to ensure consistency

Parameters:

Name Type Description Default
model VisitableBaseModel
required

Returns:

Name Type Description
model VisitableBaseModel
Assumptions

GatherMathExpr, OperatorDistribute, ProperOrder, GatherPauli, NormalOrder, PruneIdentity

Note
Example

X + Y + 2Z => 1X + 1Y + 2Z X@Y => 1*(X@Y)

Source code in oqd-core/src/oqd_core/compiler/analog/rewrite/canonicalize.py
class ScaleTerms(RewriteRule):
    """
    Scales operators to ensure consistency

    Args:
        model (VisitableBaseModel):

    Returns:
        model (VisitableBaseModel):

    Assumptions:
        [`GatherMathExpr`][oqd_core.compiler.analog.rewrite.canonicalize.GatherMathExpr],
        [`OperatorDistribute`][oqd_core.compiler.analog.rewrite.canonicalize.OperatorDistribute],
        [`ProperOrder`][oqd_core.compiler.analog.rewrite.canonicalize.ProperOrder],
        [`GatherPauli`][oqd_core.compiler.analog.rewrite.canonicalize.GatherPauli],
        [`NormalOrder`][oqd_core.compiler.analog.rewrite.canonicalize.NormalOrder],
        [`PruneIdentity`][oqd_core.compiler.analog.rewrite.canonicalize.PruneIdentity]

    Note:
        - Requires [`GatherMathExpr`][oqd_core.compiler.analog.rewrite.canonicalize.GatherMathExpr] right after application of [`ScaleTerms`][oqd_core.compiler.analog.rewrite.canonicalize.ScaleTerms]  for Post walk
        - [`SortedOrder`][oqd_core.compiler.analog.rewrite.canonicalize.SortedOrder] and  [`ScaleTerms`][oqd_core.compiler.analog.rewrite.canonicalize.ScaleTerms] can be run in either order

    Example:
        X + Y + 2*Z => 1*X + 1*Y + 2*Z
        X@Y => 1*(X@Y)
    """

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

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

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

    def map_Operator(self, model: Operator):
        if not self.op_add_root:
            self.op_add_root = True
            if not isinstance(model, Union[OperatorAdd, OperatorScalarMul]):
                return OperatorScalarMul(expr=MathNum(value=1), op=model)

    def map_OperatorAdd(self, model: OperatorAdd):
        self.op_add_root = True
        op1, op2 = model.op1, model.op2
        if not isinstance(model.op1, Union[OperatorScalarMul, OperatorAdd]):
            op1 = OperatorScalarMul(expr=MathNum(value=1), op=model.op1)
        if not isinstance(model.op2, Union[OperatorScalarMul, OperatorAdd]):
            op2 = OperatorScalarMul(expr=MathNum(value=1), op=model.op2)
        return OperatorAdd(op1=op1, op2=op2)

SortedOrder

Bases: RewriteRule

Sorts operators based on TermIndex and collects duplicate terms. Please see example for clarification

Parameters:

Name Type Description Default
model VisitableBaseModel
required

Returns:

Type Description

model (VisitableBaseModel)

Assumptions

GatherMathExpr, OperatorDistribute, ProperOrder, GatherPauli, NormalOrder, PruneIdentity

Note
Example

(X@Y) + (X@I) => (X@I) + (X@Y) X + I + Z + Y => I + X + Y + Z

Source code in oqd-core/src/oqd_core/compiler/analog/rewrite/canonicalize.py
class SortedOrder(RewriteRule):
    """
    Sorts operators based on TermIndex and collects duplicate terms.
    Please see example for clarification

    Args:
        model (VisitableBaseModel):

    Returns:
        model (VisitableBaseModel)

    Assumptions:
        [`GatherMathExpr`][oqd_core.compiler.analog.rewrite.canonicalize.GatherMathExpr],
        [`OperatorDistribute`][oqd_core.compiler.analog.rewrite.canonicalize.OperatorDistribute],
        [`ProperOrder`][oqd_core.compiler.analog.rewrite.canonicalize.ProperOrder],
        [`GatherPauli`][oqd_core.compiler.analog.rewrite.canonicalize.GatherPauli],
        [`NormalOrder`][oqd_core.compiler.analog.rewrite.canonicalize.NormalOrder],
        [`PruneIdentity`][oqd_core.compiler.analog.rewrite.canonicalize.PruneIdentity]

    Note:
        - [`SortedOrder`][oqd_core.compiler.analog.rewrite.canonicalize.SortedOrder] and  [`ScaleTerms`][oqd_core.compiler.analog.rewrite.canonicalize.ScaleTerms] can be run in either order

    Example:
        (X@Y) + (X@I) => (X@I) + (X@Y)
        X + I + Z + Y => I + X + Y + Z
    """

    def map_OperatorAdd(self, model: OperatorAdd):
        if isinstance(model.op1, OperatorAdd):
            term1 = analysis_term_index(model.op1.op2)
            term2 = analysis_term_index(model.op2)

            if term1 == term2:
                expr1 = (
                    model.op1.op2.expr
                    if isinstance(model.op1.op2, OperatorScalarMul)
                    else MathNum(value=1)
                )
                expr2 = (
                    model.op2.expr
                    if isinstance(model.op2, OperatorScalarMul)
                    else MathNum(value=1)
                )
                op = (
                    model.op2.op
                    if isinstance(model.op2, OperatorScalarMul)
                    else model.op2
                )
                return OperatorAdd(
                    op1=model.op1.op1,
                    op2=OperatorScalarMul(
                        op=op, expr=MathAdd(expr1=expr1, expr2=expr2)
                    ),
                )

            elif term1 > term2:
                return OperatorAdd(
                    op1=OperatorAdd(op1=model.op1.op1, op2=model.op2),
                    op2=model.op1.op2,
                )

            elif term1 < term2:
                return OperatorAdd(op1=model.op1, op2=model.op2)

        else:
            term1 = analysis_term_index(model.op1)
            term2 = analysis_term_index(model.op2)

            if term1 == term2:
                expr1 = (
                    model.op1.expr
                    if isinstance(model.op1, OperatorScalarMul)
                    else MathNum(value=1)
                )
                expr2 = (
                    model.op2.expr
                    if isinstance(model.op2, OperatorScalarMul)
                    else MathNum(value=1)
                )
                op = (
                    model.op2.op
                    if isinstance(model.op2, OperatorScalarMul)
                    else model.op2
                )
                return OperatorScalarMul(op=op, expr=MathAdd(expr1=expr1, expr2=expr2))

            elif term1 > term2:
                return OperatorAdd(
                    op1=model.op2,
                    op2=model.op1,
                )

            elif term1 < term2:
                return OperatorAdd(op1=model.op1, op2=model.op2)

Verification Rules

oqd_core.compiler.analog.verify.canonicalize

CanVerPauliAlgebra

Bases: RewriteRule

Checks whether there is any incomplete Pauli Algebra computation

Parameters:

Name Type Description Default
model VisitableBaseModel

The rule only verifies Operator in Analog level

required

Returns:

Name Type Description
model VisitableBaseMode

unchanged

Assumptions

GatherMathExpr, OperatorDistribute, ProperOrder

Example
  • X@(Y*Z) => fail
  • X@Y => pass
Source code in oqd-core/src/oqd_core/compiler/analog/verify/canonicalize.py
class CanVerPauliAlgebra(RewriteRule):
    """
    Checks whether there is any incomplete Pauli Algebra computation

    Args:
        model (VisitableBaseModel): The rule only verifies [`Operator`][oqd_core.interface.analog.operator.Operator] in Analog level

    Returns:
        model (VisitableBaseMode): unchanged

    Assumptions:
        [`GatherMathExpr`][oqd_core.compiler.analog.rewrite.canonicalize.GatherMathExpr],
        [`OperatorDistribute`][oqd_core.compiler.analog.rewrite.canonicalize.OperatorDistribute],
        [`ProperOrder`][oqd_core.compiler.analog.rewrite.canonicalize.ProperOrder]

    Example:
        - X@(Y*Z) => fail
        - X@Y => pass
    """

    def map_OperatorMul(self, model: OperatorMul):
        if isinstance(model.op1, Pauli) and isinstance(model.op2, Pauli):
            raise CanonicalFormError("Incomplete Pauli Algebra")
        elif isinstance(model.op1, Pauli) and isinstance(model.op2, Ladder):
            raise CanonicalFormError("Incorrect Ladder and Pauli multiplication")
        elif isinstance(model.op1, Ladder) and isinstance(model.op2, Pauli):
            raise CanonicalFormError("Incorrect Ladder and Pauli multiplication")
        pass

CanVerGatherMathExpr

Bases: RewriteRule

Checks whether all MathExpr have been gathered (i.e. basically checks whether there is any scalar multiplication within a term)

Parameters:

Name Type Description Default
model VisitableBaseModel

The rule only verifies Operator in Analog level

required

Returns:

Name Type Description
model VisitableBaseMode

unchanged

Assumptions

OperatorDistribute OperatorDistribute

Example
  • X@(1*Z) => fail
  • 1*(X@Z) => pass
Source code in oqd-core/src/oqd_core/compiler/analog/verify/canonicalize.py
class CanVerGatherMathExpr(RewriteRule):
    """
    Checks whether all MathExpr have been gathered (i.e. basically checks whether
    there is any scalar multiplication within a term)

    Args:
        model (VisitableBaseModel): The rule only verifies [`Operator`][oqd_core.interface.analog.operator.Operator] in Analog level

    Returns:
        model (VisitableBaseMode): unchanged

    Assumptions:
        OperatorDistribute
        [`OperatorDistribute`][oqd_core.compiler.analog.rewrite.canonicalize.OperatorDistribute]

    Example:
        - X@(1*Z) => fail
        - 1*(X@Z) => pass
    """

    def map_OperatorMul(self, model: OperatorMul):
        return self._mulkron(model)

    def map_OperatorKron(self, model: OperatorKron):
        return self._mulkron(model)

    def _mulkron(self, model: Union[OperatorMul, OperatorKron]):
        if isinstance(model.op1, OperatorScalarMul) or isinstance(
            model.op2, OperatorScalarMul
        ):
            raise CanonicalFormError("Incomplete Gather Math Expression")
        return None

    def map_OperatorScalarMul(self, model: OperatorScalarMul):
        if isinstance(model.op, OperatorScalarMul):
            raise CanonicalFormError(
                "Incomplete scalar multiplications after GatherMathExpression"
            )
        return None

CanVerOperatorDistribute

Bases: RewriteRule

Checks for incomplete distribution of Operators

Parameters:

Name Type Description Default
model VisitableBaseModel

The rule only verifies Operator in Analog level

required

Returns:

Name Type Description
model VisitableBaseMode

unchanged

Assumptions

None

Example
  • X@(Y+Z) => fail
  • X@Y + X@Z => pass
Source code in oqd-core/src/oqd_core/compiler/analog/verify/canonicalize.py
class CanVerOperatorDistribute(RewriteRule):
    """
    Checks for incomplete distribution of Operators

    Args:
        model (VisitableBaseModel): The rule only verifies [`Operator`][oqd_core.interface.analog.operator.Operator] in Analog level

    Returns:
        model (VisitableBaseMode): unchanged

    Assumptions:
        None

    Example:
        - X@(Y+Z) => fail
        - X@Y + X@Z => pass
    """

    def __init__(self):
        super().__init__()
        self.allowed_ops = Union[
            OperatorTerminal,
            Ladder,
            OperatorMul,
            OperatorScalarMul,
            OperatorKron,
        ]

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

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

    def _OperatorMulKron(self, model: Union[OperatorMul, OperatorKron]):
        if (
            isinstance(model, OperatorMul)
            and isinstance(model.op1, OperatorKron)
            and isinstance(model.op2, OperatorKron)
        ):
            raise CanonicalFormError(
                "Incomplete Operator Distribution (multiplication of OperatorKron present)"
            )
        elif not (
            isinstance(model.op1, self.allowed_ops)
            and isinstance(model.op2, self.allowed_ops)
        ):
            raise CanonicalFormError("Incomplete Operator Distribution")

        pass

    def map_OperatorScalarMul(self, model: OperatorScalarMul):
        if not (isinstance(model.op, self.allowed_ops)):
            raise CanonicalFormError(
                "Scalar multiplication of operators not simplified fully"
            )
        pass

    def map_OperatorSub(self, model: OperatorSub):
        if isinstance(model, OperatorSub):
            raise CanonicalFormError("Subtraction of terms present")
        pass

CanVerProperOrder

Bases: RewriteRule

Checks whether all Operators are ProperOrdered according to how they are bracketed Please see example for clarification

Parameters:

Name Type Description Default
model VisitableBaseModel

The rule only verifies Operator in Analog level

required

Returns:

Name Type Description
model VisitableBaseMode

unchanged

Assumptions

None

Example
  • X@(Y@Z) => fail
  • (X@Y)@Z => pass
Source code in oqd-core/src/oqd_core/compiler/analog/verify/canonicalize.py
class CanVerProperOrder(RewriteRule):
    """
    Checks whether all Operators are ProperOrdered according to how they are bracketed
    Please see example for clarification

    Args:
        model (VisitableBaseModel): The rule only verifies [`Operator`][oqd_core.interface.analog.operator.Operator] in Analog level

    Returns:
        model (VisitableBaseMode): unchanged

    Assumptions:
        None

    Example:
        - X@(Y@Z) => fail
        - (X@Y)@Z => pass
    """

    def map_OperatorAdd(self, model: OperatorAdd):
        self._OperatorAddMulKron(model)
        pass

    def map_OperatorMul(self, model: OperatorMul):
        self._OperatorAddMulKron(model)
        pass

    def map_OperatorKron(self, model: OperatorKron):
        self._OperatorAddMulKron(model)
        pass

    def _OperatorAddMulKron(self, model: Union[OperatorAdd, OperatorMul, OperatorKron]):
        if isinstance(model.op2, model.__class__):
            raise CanonicalFormError("Incorrect Proper Ordering")
        pass

    def map_OperatorScalarMul(self, model: OperatorScalarMul):
        if isinstance(model.op, model.__class__):
            raise CanonicalFormError(
                "Incorrect Proper Ordering (for scalar multiplication)"
            )
        pass

CanVerPruneIdentity

Bases: RewriteRule

Checks if there is any ladder Identity present in ladder multiplication

Parameters:

Name Type Description Default
model VisitableBaseModel

The rule only verifies Operator in Analog level

required

Returns:

Name Type Description
model VisitableBaseMode

unchanged

Assumptions

OperatorDistribute OperatorDistribute

Example
  • AJC => fail
  • A*C => pass
Source code in oqd-core/src/oqd_core/compiler/analog/verify/canonicalize.py
class CanVerPruneIdentity(RewriteRule):
    """
    Checks if there is any ladder Identity present in ladder multiplication

    Args:
        model (VisitableBaseModel): The rule only verifies [`Operator`][oqd_core.interface.analog.operator.Operator] in Analog level

    Returns:
        model (VisitableBaseMode): unchanged

    Assumptions:
        OperatorDistribute
        [`OperatorDistribute`][oqd_core.compiler.analog.rewrite.canonicalize.OperatorDistribute]

    Example:
        - A*J*C => fail
        - A*C => pass
    """

    def map_OperatorMul(self, model: OperatorMul):
        if isinstance(model.op1, Identity) or isinstance(model.op2, Identity):
            raise CanonicalFormError("Prune Identity is not complete")
        pass

CanVerGatherPauli

Bases: RewriteRule

Checks whether pauli and ladder have been separated.

Parameters:

Name Type Description Default
model VisitableBaseModel

The rule only verifies Operator in Analog level

required

Returns:

Name Type Description
model VisitableBaseMode

unchanged

Assumptions

GatherMathExpr, OperatorDistribute, ProperOrder, PauliAlgebra

Example
  • X@A@Y => fail
  • X@Y@A => pass
Source code in oqd-core/src/oqd_core/compiler/analog/verify/canonicalize.py
class CanVerGatherPauli(RewriteRule):
    """
    Checks whether pauli and ladder have been separated.

    Args:
        model (VisitableBaseModel): The rule only verifies [`Operator`][oqd_core.interface.analog.operator.Operator] in Analog level

    Returns:
        model (VisitableBaseMode): unchanged

    Assumptions:
        [`GatherMathExpr`][oqd_core.compiler.analog.rewrite.canonicalize.GatherMathExpr],
        [`OperatorDistribute`][oqd_core.compiler.analog.rewrite.canonicalize.OperatorDistribute],
        [`ProperOrder`][oqd_core.compiler.analog.rewrite.canonicalize.ProperOrder],
        [`PauliAlgebra`][oqd_core.compiler.analog.rewrite.canonicalize.PauliAlgebra]

    Example:
        - X@A@Y => fail
        - X@Y@A => pass
    """

    def map_OperatorKron(self, model: OperatorKron):
        if isinstance(model.op2, Pauli):
            if isinstance(model.op1, (Ladder, OperatorMul)):
                raise CanonicalFormError("Incorrect GatherPauli")
            if isinstance(model.op1, OperatorKron):
                if isinstance(model.op1.op2, (Ladder, OperatorMul)):
                    raise CanonicalFormError("Incorrect GatherPauli")
        pass

CanVerNormalOrder

Bases: RewriteRule

Checks whether the ladder operations are in normal order

Parameters:

Name Type Description Default
model VisitableBaseModel

The rule only verifies Operator in Analog level

required

Returns:

Name Type Description
model VisitableBaseMode

unchanged

Assumptions

OperatorDistribute, GatherMathExpr, ProperOrder, PauliAlgebra, PruneIdentity GatherMathExpr, OperatorDistribute, ProperOrder, PauliAlgebra, PruneIdentity

Example
  • A*C => fail
  • C*A => pass
Source code in oqd-core/src/oqd_core/compiler/analog/verify/canonicalize.py
class CanVerNormalOrder(RewriteRule):
    """
    Checks whether the ladder operations are in normal order

    Args:
        model (VisitableBaseModel): The rule only verifies [`Operator`][oqd_core.interface.analog.operator.Operator] in Analog level

    Returns:
        model (VisitableBaseMode): unchanged

    Assumptions:
        OperatorDistribute, GatherMathExpr, ProperOrder, PauliAlgebra, PruneIdentity
        [`GatherMathExpr`][oqd_core.compiler.analog.rewrite.canonicalize.GatherMathExpr],
        [`OperatorDistribute`][oqd_core.compiler.analog.rewrite.canonicalize.OperatorDistribute],
        [`ProperOrder`][oqd_core.compiler.analog.rewrite.canonicalize.ProperOrder],
        [`PauliAlgebra`][oqd_core.compiler.analog.rewrite.canonicalize.PauliAlgebra],
        [`PruneIdentity`][oqd_core.compiler.analog.rewrite.canonicalize.PruneIdentity]


    Example:
        - A*C => fail
        - C*A => pass
    """

    def map_OperatorMul(self, model: OperatorMul):
        if isinstance(model.op2, Creation):
            if isinstance(model.op1, Annihilation):
                raise CanonicalFormError("Incorrect NormalOrder")
            if isinstance(model.op1, OperatorMul):
                if isinstance(model.op1.op2, Annihilation):
                    raise CanonicalFormError("Incorrect NormalOrder")
        pass

CanVerSortedOrder

Bases: RewriteRule

Checks whether operators are in sorted order according to TermIndex. Please see example for further clarification

Parameters:

Name Type Description Default
model VisitableBaseModel

The rule only verifies Operator in Analog level

required

Returns:

Name Type Description
model VisitableBaseMode

unchanged

Assumptions

GatherMathExpr, OperatorDistribute, ProperOrder, GatherPauli, NormalOrder, PruneIdentity

Example
  • X + I => fail
  • I + X => pass
Source code in oqd-core/src/oqd_core/compiler/analog/verify/canonicalize.py
class CanVerSortedOrder(RewriteRule):
    """
    Checks whether operators are in sorted order according to TermIndex.
    Please see example for further clarification

    Args:
        model (VisitableBaseModel): The rule only verifies [`Operator`][oqd_core.interface.analog.operator.Operator] in Analog level

    Returns:
        model (VisitableBaseMode): unchanged

    Assumptions:
        [`GatherMathExpr`][oqd_core.compiler.analog.rewrite.canonicalize.GatherMathExpr],
        [`OperatorDistribute`][oqd_core.compiler.analog.rewrite.canonicalize.OperatorDistribute],
        [`ProperOrder`][oqd_core.compiler.analog.rewrite.canonicalize.ProperOrder],
        [`GatherPauli`][oqd_core.compiler.analog.rewrite.canonicalize.GatherPauli],
        [`NormalOrder`][oqd_core.compiler.analog.rewrite.canonicalize.NormalOrder],
        [`PruneIdentity`][oqd_core.compiler.analog.rewrite.canonicalize.PruneIdentity]

    Example:
        - X + I => fail
        - I + X => pass
    """

    def map_OperatorAdd(self, model: OperatorAdd):
        term2 = analysis_term_index(model.op2)
        if isinstance(model.op1, OperatorAdd):
            term1 = analysis_term_index(model.op1.op2)
        else:
            term1 = analysis_term_index(model.op1)
        if term1 > term2:
            raise CanonicalFormError("Terms are not in sorted order")
        elif term1 == term2:
            raise CanonicalFormError("Duplicate terms present")
        pass

CanVerScaleTerm

Bases: RewriteRule

Checks whether all terms have a scalar multiplication.

Parameters:

Name Type Description Default
model VisitableBaseModel

The rule only verifies Operator in Analog level

required

Returns:

Name Type Description
model VisitableBaseMode

unchanged

Assumptions

GatherMathExpr, OperatorDistribute, ProperOrder, GatherPauli

Example
  • X + 2*Y => fail
  • 1X + 2Y => pass
Source code in oqd-core/src/oqd_core/compiler/analog/verify/canonicalize.py
class CanVerScaleTerm(RewriteRule):
    """
    Checks whether all terms have a scalar multiplication.

    Args:
        model (VisitableBaseModel): The rule only verifies [`Operator`][oqd_core.interface.analog.operator.Operator] in Analog level

    Returns:
        model (VisitableBaseMode): unchanged

    Assumptions:
        [`GatherMathExpr`][oqd_core.compiler.analog.rewrite.canonicalize.GatherMathExpr],
        [`OperatorDistribute`][oqd_core.compiler.analog.rewrite.canonicalize.OperatorDistribute],
        [`ProperOrder`][oqd_core.compiler.analog.rewrite.canonicalize.ProperOrder],
        [`GatherPauli`][oqd_core.compiler.analog.rewrite.canonicalize.GatherPauli]

    Example:
        - X + 2*Y => fail
        - 1*X + 2*Y => pass
    """

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

    def map_AnalogGate(self, model):
        self._single_term_scaling_needed = False

    def map_Expectation(self, model):
        self._single_term_scaling_needed = False

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

    def map_OperatorMul(self, model: OperatorMul):
        if not self._single_term_scaling_needed:
            raise CanonicalFormError("Single term operator has not been scaled")

    def map_OperatorKron(self, model: OperatorKron):
        if not self._single_term_scaling_needed:
            raise CanonicalFormError("Single term operator has not been scaled")

    def map_OperatorTerminal(self, model: OperatorKron):
        if not self._single_term_scaling_needed:
            raise CanonicalFormError("Single term operator has not been scaled")

    def map_OperatorAdd(self, model: OperatorAdd):
        self._single_term_scaling_needed = True
        if isinstance(model.op2, OperatorScalarMul) and isinstance(
            model.op1, Union[OperatorScalarMul, OperatorAdd]
        ):
            pass
        else:
            raise CanonicalFormError(
                "some operators between addition are not scaled properly"
            )