Exemple #1
0
    def test_joined_generator(self):
        generator1 = TwoRotsGenerator()

        generator2 = OneRotGenerator()
        grad = op_tree_generator_grad(
            OpTreeGenerator.join(generator1, generator2),
            rad
        )
        grad_lco = LinearCombinationOfOperations({
            _generator((q0, q1)): _coeff for _generator, _coeff in grad.items()
        })

        for _generator, _coeff in grad.items():
            print(_generator.diagram(), _coeff)

        exact_grad_lco = LinearCombinationOfOperations({
            (X(q0), rx(rad).on(q0), rz(rad).on(q1), ry(c * rad).on(q0)): -0.5j,
            (rx(rad).on(q0), Z(q1), rz(rad).on(q1), ry(c * rad).on(q0)): -0.5j,
            (rx(rad).on(q0), rz(rad).on(q1), Y(q0), ry(c * rad).on(q0)): -0.5j * c,
        })

        param_resolver = {
            rad: np.random.rand() * 4 * np.pi,
            c: np.random.rand()
        }

        grad_lco = cirq.resolve_parameters(grad_lco, param_resolver)
        exact_grad_lco = cirq.resolve_parameters(exact_grad_lco, param_resolver)

        test_lco_identical_with_simulator(
            grad_lco,
            exact_grad_lco,
            self
        )
Exemple #2
0
def op_series_grad(
    op_series: typing.Sequence[cirq.Operation], parameter: sympy.Symbol
) -> typing.Union[LinearCombinationOfOperations, GradNotImplemented]:
    grad_dict = LinearCombinationOfOperations({})
    for i, _op in enumerate(op_series):
        _grad = op_grad(typing.cast(cirq.GateOperation, _op), parameter)
        if isinstance(_grad, GradNotImplemented):
            return _grad
        elif _grad == ZERO_OP:
            continue
        else:
            for _grad_op, coeff in _grad.items():
                _grad_op_series = copy.deepcopy(op_series)
                _grad_op_series = (_grad_op_series[:i] + list(_grad_op) +
                                   _grad_op_series[i + 1:])
                grad_dict += LinearCombinationOfOperations(
                    {tuple(_grad_op_series): coeff})

    return grad_dict
Exemple #3
0
    def test_simple_generator(self):
        generator = TwoRotsGenerator()
        grad_lcg = op_tree_generator_grad(generator, rad)
        grad_lco = LinearCombinationOfOperations({
            _generator((q0, q1)): _coeff for _generator, _coeff in grad_lcg.items()
        })

        exact_grad_lco = LinearCombinationOfOperations({
            (X(q0), rx(rad).on(q0), rz(rad).on(q1)): -0.5j,
            (rx(rad).on(q0), Z(q1), rz(rad).on(q1)): -0.5j
        })

        param_resolver = {rad: np.random.rand() * 4 * np.pi}

        grad_lco = cirq.resolve_parameters(grad_lco, param_resolver)
        exact_grad_lco = cirq.resolve_parameters(exact_grad_lco, param_resolver)

        test_lco_identical_with_simulator(
            grad_lco,
            exact_grad_lco,
            self
        )
Exemple #4
0
def op_grad(
    operation: cirq.GateOperation, parameter: sympy.Symbol
) -> typing.Union[LinearCombinationOfOperations, GradNotImplemented]:
    if not cirq.is_parameterized(operation):
        return ZERO_OP.copy()

    gate = operation.gate
    qubits = operation.qubits

    if isinstance(gate, cirq.XPowGate):
        # dXPow(e=f(t), s) / dt = i π (s + 1 / 2 - X / 2) * (df(t)/dt) XPow(e=f(t), s)
        # Note that: Rx(θ) = XPowGate(exponent=θ/pi, global_shift=-0.5)
        partial = sympy.diff(gate.exponent, parameter)
        coeff_i = 1.0j * sympy.pi * (gate._global_shift + 0.5)
        coeff_x = -1.0j / 2 * sympy.pi
        if partial == 0:
            return ZERO_OP.copy()
        return LinearCombinationOfOperations({
            (cirq.X.on(*qubits), operation):
            coeff_x * partial,
            (cirq.I.on(*qubits), operation):
            coeff_i * partial
        })

    elif isinstance(gate, cirq.YPowGate):
        # dYPow(e=f(t), s) / dt = i π (s + 1 / 2 - Y / 2) * (df(t)/dt) YPow(e=f(t), s)
        # Note that: Ry(θ) = YPowGate(exponent=θ/pi, global_shift=-0.5)
        partial = sympy.diff(gate.exponent, parameter)
        coeff_i = 1.0j * sympy.pi * (gate._global_shift + 0.5)
        coeff_y = -1.0j / 2 * sympy.pi
        if partial == 0:
            return ZERO_OP.copy()
        return LinearCombinationOfOperations({
            (cirq.Y.on(*qubits), operation):
            coeff_y * partial,
            (cirq.I.on(*qubits), operation):
            coeff_i * partial
        })

    elif isinstance(gate, cirq.ZPowGate):
        # dZPow(e=f(t), s) / dt = i π (s + 1 / 2 - Z / 2) * (df(t)/dt) ZPow(e=f(t), s)
        # Note that: Ry(θ) = ZPowGate(exponent=θ/pi, global_shift=-0.5)
        partial = sympy.diff(gate.exponent, parameter)
        coeff_i = 1.0j * sympy.pi * (gate._global_shift + 0.5)
        coeff_z = -1.0j / 2 * sympy.pi
        if partial == 0:
            return ZERO_OP.copy()
        return LinearCombinationOfOperations({
            (cirq.Z.on(*qubits), operation):
            coeff_z * partial,
            (cirq.I.on(*qubits), operation):
            coeff_i * partial
        })

    elif isinstance(gate, GlobalPhaseGate):
        gate = typing.cast(GlobalPhaseGate, gate)
        # Ph(θ) = exp(i pi θ)
        # dPh(f(θ)) / dθ = [i pi df(θ) / dθ] exp(i pi f(θ))
        #                = [i pi df(θ) / dθ] Ph(f(θ))
        coeff = 1.0j * sympy.diff(gate.rad * sympy.pi, parameter)
        if coeff == 0:
            return ZERO_OP.copy()
        return LinearCombinationOfOperations({(operation, ): coeff})

    elif isinstance(gate, cirq.EigenGate):
        gate = typing.cast(cirq.EigenGate, gate)
        eigenvalues = {v for v, p in gate._eigen_components()}
        if eigenvalues == {0, 1}:
            e = gate.exponent
            s = gate._global_shift
            partial = sympy.diff(e, parameter)

            if partial == 0:
                return ZERO_OP.copy()

            num_qubits = gate.num_qubits()
            gate_e1_s0 = copy.deepcopy(gate)
            gate_e1_s0._exponent = 1.0
            gate_e1_s0._global_shift = 0.0  # Any better solutions?
            coeff = 0.5 * sympy.exp(1.0j * sympy.pi * (1 + s) * e)

            return 1.0j * sympy.pi * LinearCombinationOfOperations(
                {
                    (operation, ): s * partial,
                    (gate_e1_s0.on(*qubits), ): -coeff * partial,
                    (cirq.IdentityGate(num_qubits).on(*qubits), ):
                    coeff * partial
                })

        else:
            return GradNotImplemented(operation)

    elif isinstance(gate, GateBlock):
        gate = typing.cast(GateBlock, gate)

        generator_grad = op_tree_generator_grad(gate._op_generator, parameter)

        if isinstance(generator_grad, GradNotImplemented):
            return generator_grad

        if len(generator_grad) == 0:
            return ZERO_OP.copy()

        _grad = LinearCombinationOfOperations({})
        for generator, coeff in generator_grad.items():
            grad_gate = GateBlock(generator)

            _grad += LinearCombinationOfOperations({
                (grad_gate.on(*qubits), ):
                coeff
            })

            # print(grad_gate.diagram())

        return _grad

    elif isinstance(gate, cirq.ControlledGate):
        gate = typing.cast(cirq.ControlledGate, gate)

        sub_gate_qubits = qubits[gate.num_controls():]
        sub_op_grad = op_grad(gate.sub_gate.on(*sub_gate_qubits), parameter)

        if is_zero_op_or_grad_not_implemented(sub_op_grad):
            return sub_op_grad

        _gate_grad = LinearCombinationOfOperations({})
        op: cirq.GateOperation
        for op_series, coeff in sub_op_grad.items():
            _controlled_op_series = [
                cirq.ControlledGate(
                    op.gate, control_qubits=qubits[:gate.num_controls()]).on(
                        *sub_gate_qubits) for op in op_series
            ]
            _controlled_negative_op_series = copy.deepcopy(
                _controlled_op_series)
            _controlled_negative_op_series.insert(
                0,
                cirq.ControlledGate(
                    GlobalPhaseGate(rad=1),
                    control_qubits=qubits[:gate.num_controls()]).on(
                        *sub_gate_qubits))
            _gate_grad += LinearCombinationOfOperations(
                {
                    tuple(_controlled_op_series): coeff,
                    tuple(_controlled_negative_op_series): -coeff,
                }) / 2.0

        return _gate_grad

    # if `operation` is a basic and indecomposable operation whose grad is
    # not implemented
    elif is_an_indecomposable_operation(operation):
        return GradNotImplemented(operation)

    else:
        op_series = cirq.decompose(
            operation,
            keep=(lambda op: is_a_basic_operation(op) or
                  is_an_indecomposable_operation(op)),
            on_stuck_raise=None)  # type: typing.List[cirq.Operation]

        _grad = op_series_grad(op_series, parameter)
        return _grad
Exemple #5
0
import cirq
import numpy as np
import sympy
from cirq import flatten_op_tree

from paulicirq.gates import GlobalPhaseGate
from paulicirq.gates.gate_block import GateBlock
from paulicirq.gates.universal_gate_set import (is_a_basic_operation,
                                                is_an_indecomposable_operation)
from paulicirq.linear_combinations import (LinearCombinationOfOperations,
                                           LinearSymbolicDict)
from paulicirq.op_tree import OpTreeGenerator
from paulicirq.utils import ToBeTested

ZERO_OP = LinearCombinationOfOperations({})  # constant


class GradNotImplemented:
    __slots__ = ("operation", )

    def __init__(self, operation):
        self.operation = operation


def is_zero_op_or_grad_not_implemented(grad):
    return grad == ZERO_OP or isinstance(grad, GradNotImplemented)


@ToBeTested
def op_grad(