Source code for mindquantum.framework.operations

# -*- coding: utf-8 -*-
# Copyright 2021 Huawei Technologies Co., Ltd
#
# 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.
# ============================================================================
"""Mindspore quantum simulator operator."""
import numpy as np
import mindspore as ms
from mindspore import context
from mindspore.ops import operations as P
import mindspore.nn as nn
from mindspore.ops.primitive import constexpr
from mindquantum.simulator import GradOpsWrapper


@constexpr
def check_enc_input_shape(data, x, enc_len):
    if not isinstance(data, ms.Tensor):
        raise TypeError(f"Encoder parameter requires a Tensor but get {type(data)}")
    if len(x) != 2 or x[1] != enc_len:
        raise ValueError(f'Encoder data requires a two dimension Tensor with second' +
                         f' dimension should be {enc_len}, but get shape {x}')


@constexpr
def check_ans_input_shape(data, x, ans_len):
    if not isinstance(data, ms.Tensor):
        raise TypeError(f"Ansatz parameter requires a Tensor but get {type(data)}")
    if len(x) != 1 or x[0] != ans_len:
        raise ValueError(f'Ansatz data requires a one dimension Tensor with shape {ans_len} ' + f'but get {x}')


[docs]class MQOps(nn.Cell): """ MindQuantum operator that get the expectation of a hamiltonian on a quantum state evaluated by a parameterized quantum circuit (PQC). This PQC should contains a encoder circuit and an ansatz circuit. This ops is `PYNATIVE_MODE` supported only. Args: expectation_with_grad (GradOpsWrapper): a grad ops that receive encoder data and ansatz data and return the expectation value and gradient value of parameters respect to expectation. Inputs: - **enc_data** (Tensor) - Tensor of encoder data with shape :math:`(N, M)` that you want to encode into quantum state, where :math:`N` means the batch size and :math:`M` means the number of encoder parameters. - **ans_data** (Tensor) - Tensor with shape :math:`N` for ansatz circuit, where :math:`N` means the number of ansatz parameters. Outputs: Tensor, The expectation value of the hamiltonian. Supported Platforms: ``GPU``, ``CPU`` Examples: >>> import numpy as np >>> from mindquantum import Circuit, Hamiltonian, QubitOperator >>> from mindquantum import Simulator, MQOps >>> import mindspore as ms >>> ms.context.set_context(mode=ms.context.PYNATIVE_MODE, device_target="CPU") >>> enc = Circuit().ry('a', 0) >>> ans = Circuit().h(0).rx('b', 0) >>> ham = Hamiltonian(QubitOperator('Z0')) >>> sim = Simulator('projectq', 1) >>> grad_ops = sim.get_expectation_with_grad(ham, enc+ans, ... encoder_params_name=['a'], ... ansatz_params_name=['b']) >>> enc_data = np.array([[0.1]]) >>> ans_data = np.array([0.2]) >>> f, g_enc, g_ans = grad_ops(enc_data, ans_data) >>> f array([[0.0978434+0.j]]) >>> net = MQOps(grad_ops) >>> f_ms = net(ms.Tensor(enc_data), ms.Tensor(ans_data)) >>> f_ms Tensor(shape=[1, 1], dtype=Float32, value= [[ 9.78433937e-02]]) """ def __init__(self, expectation_with_grad): super(MQOps, self).__init__() _mode_check(self) _check_grad_ops(expectation_with_grad) self.expectation_with_grad = expectation_with_grad self.shape_ops = P.Shape() def extend_repr(self): return self.expectation_with_grad.str def construct(self, enc_data, ans_data): check_enc_input_shape(enc_data, self.shape_ops(enc_data), len(self.expectation_with_grad.encoder_params_name)) check_ans_input_shape(ans_data, self.shape_ops(ans_data), len(self.expectation_with_grad.ansatz_params_name)) enc_data = enc_data.asnumpy() ans_data = ans_data.asnumpy() f, g_enc, g_ans = self.expectation_with_grad(enc_data, ans_data) f = ms.Tensor(np.real(f), dtype=ms.float32) self.g_enc = np.real(g_enc) self.g_ans = np.real(g_ans) return f def bprop(self, enc_data, ans_data, out, dout): dout = dout.asnumpy() enc_grad = np.einsum('smp,sm->sp', self.g_enc, dout) ans_grad = np.einsum('smp,sm->p', self.g_ans, dout) return ms.Tensor(enc_grad, dtype=ms.float32), ms.Tensor(ans_grad, dtype=ms.float32)
[docs]class MQN2Ops(nn.Cell): r""" MindQuantum operator that get the square of absolute value of expectation of a hamiltonian on a quantum state evaluated by a parameterized quantum circuit (PQC). This PQC should contains a encoder circuit and an ansatz circuit. This ops is `PYNATIVE_MODE` supported only. .. math:: O = \left|\left<\varphi\right| U^\dagger_l H U_r\left|\psi\right>\right|^2 Args: expectation_with_grad (GradOpsWrapper): a grad ops that receive encoder data and ansatz data and return the square of absolute value of expectation value and gradient value of parameters respect to expectation. Inputs: - **enc_data** (Tensor) - Tensor of encoder data with shape :math:`(N, M)` that you want to encode into quantum state, where :math:`N` means the batch size and :math:`M` means the number of encoder parameters. - **ans_data** (Tensor) - Tensor with shape :math:`N` for ansatz circuit, where :math:`N` means the number of ansatz parameters. Outputs: Tensor, The square of absolute value of expectation value of the hamiltonian. Supported Platforms: ``GPU``, ``CPU`` Examples: >>> import numpy as np >>> from mindquantum import Circuit, Hamiltonian, QubitOperator >>> from mindquantum import Simulator, MQN2Ops >>> import mindspore as ms >>> ms.context.set_context(mode=ms.context.PYNATIVE_MODE, device_target="CPU") >>> enc = Circuit().ry('a', 0) >>> ans = Circuit().h(0).rx('b', 0) >>> ham = Hamiltonian(QubitOperator('Z0')) >>> sim = Simulator('projectq', 1) >>> grad_ops = sim.get_expectation_with_grad(ham, enc+ans, ... encoder_params_name=['a'], ... ansatz_params_name=['b']) >>> enc_data = np.array([[0.1]]) >>> ans_data = np.array([0.2]) >>> f, g_enc, g_ans = grad_ops(enc_data, ans_data) >>> np.abs(f) ** 2 array([[0.00957333]]) >>> net = MQN2Ops(grad_ops) >>> f_ms = net(ms.Tensor(enc_data), ms.Tensor(ans_data)) >>> f_ms Tensor(shape=[1, 1], dtype=Float32, value= [[ 9.57333017e-03]]) """ def __init__(self, expectation_with_grad): super(MQN2Ops, self).__init__() _mode_check(self) _check_grad_ops(expectation_with_grad) self.expectation_with_grad = expectation_with_grad self.shape_ops = P.Shape() def extend_repr(self): return self.expectation_with_grad.str def construct(self, enc_data, ans_data): check_enc_input_shape(enc_data, self.shape_ops(enc_data), len(self.expectation_with_grad.encoder_params_name)) check_ans_input_shape(ans_data, self.shape_ops(ans_data), len(self.expectation_with_grad.ansatz_params_name)) enc_data = enc_data.asnumpy() ans_data = ans_data.asnumpy() f, g_enc, g_ans = self.expectation_with_grad(enc_data, ans_data) self.f = f f = ms.Tensor(np.abs(f)**2, dtype=ms.float32) self.g_enc = g_enc self.g_ans = g_ans return f def bprop(self, enc_data, ans_data, out, dout): dout = dout.asnumpy() enc_grad = 2 * np.real(np.einsum('smp,sm,sm->sp', self.g_enc, dout, np.conj(self.f))) ans_grad = 2 * np.real(np.einsum('smp,sm,sm->p', self.g_ans, dout, np.conj(self.f))) return ms.Tensor(enc_grad, dtype=ms.float32), ms.Tensor(ans_grad, dtype=ms.float32)
[docs]class MQAnsatzOnlyOps(nn.Cell): r""" MindQuantum operator that get the expectation of a hamiltonian on a quantum state evaluated by a parameterized quantum circuit (PQC). This PQC should contains an ansatz circuit only. This ops is `PYNATIVE_MODE` supported only. Args: expectation_with_grad (GradOpsWrapper): a grad ops that receive encoder data and ansatz data and return the expectation value and gradient value of parameters respect to expectation. Inputs: - **ans_data** (Tensor) - Tensor with shape :math:`N` for ansatz circuit, where :math:`N` means the number of ansatz parameters. Outputs: Tensor, The expectation value of the hamiltonian. Supported Platforms: ``GPU``, ``CPU`` Examples: >>> import numpy as np >>> from mindquantum import Circuit, Hamiltonian, QubitOperator >>> from mindquantum import Simulator, MQAnsatzOnlyOps >>> import mindspore as ms >>> ms.context.set_context(mode=ms.context.PYNATIVE_MODE, device_target="CPU") >>> circ = Circuit().ry('a', 0).h(0).rx('b', 0) >>> ham = Hamiltonian(QubitOperator('Z0')) >>> sim = Simulator('projectq', 1) >>> grad_ops = sim.get_expectation_with_grad(ham, circ) >>> data = np.array([0.1, 0.2]) >>> f, g = grad_ops(data) >>> f array([[0.0978434+0.j]]) >>> net = MQAnsatzOnlyOps(grad_ops) >>> f_ms = net(ms.Tensor(data)) >>> f_ms Tensor(shape=[1], dtype=Float32, value= [ 9.78433937e-02]) """ def __init__(self, expectation_with_grad): super(MQAnsatzOnlyOps, self).__init__() _mode_check(self) _check_grad_ops(expectation_with_grad) self.expectation_with_grad = expectation_with_grad self.shape_ops = P.Shape() def extend_repr(self): return self.expectation_with_grad.str def construct(self, x): check_ans_input_shape(x, self.shape_ops(x), len(self.expectation_with_grad.ansatz_params_name)) x = x.asnumpy() f, g = self.expectation_with_grad(x) f = ms.Tensor(np.real(f[0]), dtype=ms.float32) self.g = np.real(g[0]) return f def bprop(self, x, out, dout): dout = dout.asnumpy() grad = dout @ self.g return ms.Tensor(grad, dtype=ms.float32)
[docs]class MQN2AnsatzOnlyOps(nn.Cell): r""" MindQuantum operator that get the square of absolute value of expectation of a hamiltonian on a quantum state evaluated by a parameterized quantum circuit (PQC). This PQC should contains an ansatz circuit only. This ops is `PYNATIVE_MODE` supported only. Args: expectation_with_grad (GradOpsWrapper): a grad ops that receive encoder data and ansatz data and return the square of absolute value of expectation value and gradient value of parameters respect to expectation. Inputs: - **ans_data** (Tensor) - Tensor with shape :math:`N` for ansatz circuit, where :math:`N` means the number of ansatz parameters. Outputs: Tensor, The square of absolute value of expectation value of the hamiltonian. Supported Platforms: ``GPU``, ``CPU`` Examples: >>> import numpy as np >>> from mindquantum import Circuit, Hamiltonian, QubitOperator >>> from mindquantum import Simulator, MQN2AnsatzOnlyOps >>> import mindspore as ms >>> ms.context.set_context(mode=ms.context.PYNATIVE_MODE, device_target="CPU") >>> circ = Circuit().ry('a', 0).h(0).rx('b', 0) >>> ham = Hamiltonian(QubitOperator('Z0')) >>> sim = Simulator('projectq', 1) >>> grad_ops = sim.get_expectation_with_grad(ham, circ) >>> data = np.array([0.1, 0.2]) >>> f, g = grad_ops(data) >>> np.abs(f) ** 2 array([[0.00957333]]) >>> net = MQN2AnsatzOnlyOps(grad_ops) >>> f_ms = net(ms.Tensor(data)) >>> f_ms Tensor(shape=[1], dtype=Float32, value= [ 9.57333017e-03]) """ def __init__(self, expectation_with_grad): super(MQN2AnsatzOnlyOps, self).__init__() _mode_check(self) _check_grad_ops(expectation_with_grad) self.expectation_with_grad = expectation_with_grad self.shape_ops = P.Shape() def extend_repr(self): return self.expectation_with_grad.str def construct(self, x): check_ans_input_shape(x, self.shape_ops(x), len(self.expectation_with_grad.ansatz_params_name)) x = x.asnumpy() f, g = self.expectation_with_grad(x) self.f = f[0] f = ms.Tensor(np.abs(f[0])**2, dtype=ms.float32) self.g = g[0] return f def bprop(self, x, out, dout): dout = dout.asnumpy() grad = 2 * np.real(np.einsum('m,m,mp->p', np.conj(self.f), dout, self.g)) return ms.Tensor(grad, dtype=ms.float32)
[docs]class MQEncoderOnlyOps(nn.Cell): r""" MindQuantum operator that get the expectation of a hamiltonian on a quantum state evaluated by a parameterized quantum circuit (PQC). This PQC should contains a encoder circuit only. This ops is `PYNATIVE_MODE` supported only. Args: expectation_with_grad (GradOpsWrapper): a grad ops that receive encoder data and ansatz data and return the expectation value and gradient value of parameters respect to expectation. Inputs: - **enc_data** (Tensor) - Tensor of encoder data with shape :math:`(N, M)` that you want to encode into quantum state, where :math:`N` means the batch size and :math:`M` means the number of encoder parameters. Outputs: Tensor, The expectation value of the hamiltonian. Supported Platforms: ``GPU``, ``CPU`` Examples: >>> import numpy as np >>> from mindquantum import Circuit, Hamiltonian, QubitOperator >>> from mindquantum import Simulator, MQEncoderOnlyOps >>> import mindspore as ms >>> ms.context.set_context(mode=ms.context.PYNATIVE_MODE, device_target="CPU") >>> circ = Circuit().ry('a', 0).h(0).rx('b', 0) >>> ham = Hamiltonian(QubitOperator('Z0')) >>> sim = Simulator('projectq', 1) >>> grad_ops = sim.get_expectation_with_grad(ham, circ, encoder_params_name=circ.params_name) >>> data = np.array([[0.1, 0.2], [0.3, 0.4]]) >>> f, g = grad_ops(data) >>> f array([[0.0978434 +0.j], [0.27219214+0.j]]) >>> net = MQEncoderOnlyOps(grad_ops) >>> f_ms = net(ms.Tensor(data)) >>> f_ms Tensor(shape=[2, 1], dtype=Float32, value= [[ 9.78433937e-02], [ 2.72192121e-01]]) """ def __init__(self, expectation_with_grad): super(MQEncoderOnlyOps, self).__init__() _mode_check(self) _check_grad_ops(expectation_with_grad) self.expectation_with_grad = expectation_with_grad self.shape_ops = P.Shape() def extend_repr(self): return self.expectation_with_grad.str def construct(self, x): check_enc_input_shape(x, self.shape_ops(x), len(self.expectation_with_grad.encoder_params_name)) x = x.asnumpy() f, g = self.expectation_with_grad(x) f = ms.Tensor(np.real(f), dtype=ms.float32) self.g = np.real(g) return f def bprop(self, x, out, dout): dout = dout.asnumpy() grad = np.einsum('smp,sm->sp', self.g, dout) return ms.Tensor(grad, dtype=ms.float32)
[docs]class MQN2EncoderOnlyOps(nn.Cell): r""" MindQuantum operator that get the square of absolute value of expectation of a hamiltonian on a quantum state evaluated by a parameterized quantum circuit (PQC). This PQC should contains a encoder circuit only. This ops is `PYNATIVE_MODE` supported only. Args: expectation_with_grad (GradOpsWrapper): a grad ops that receive encoder data and ansatz data and return the square of absolute value of expectation value and gradient value of parameters respect to expectation. Inputs: - **ans_data** (Tensor) - Tensor with shape :math:`N` for ansatz circuit, where :math:`N` means the number of ansatz parameters. Outputs: Tensor, The square of absolute value of expectation value of the hamiltonian. Supported Platforms: ``GPU``, ``CPU`` Examples: >>> import numpy as np >>> from mindquantum import Circuit, Hamiltonian, QubitOperator >>> from mindquantum import Simulator, MQN2EncoderOnlyOps >>> import mindspore as ms >>> ms.context.set_context(mode=ms.context.PYNATIVE_MODE, device_target="CPU") >>> circ = Circuit().ry('a', 0).h(0).rx('b', 0) >>> ham = Hamiltonian(QubitOperator('Z0')) >>> sim = Simulator('projectq', 1) >>> grad_ops = sim.get_expectation_with_grad(ham, circ, encoder_params_name=circ.params_name) >>> data = np.array([[0.1, 0.2], [0.3, 0.4]]) >>> f, g = grad_ops(data) >>> np.abs(f) ** 2 array([[0.00957333], [0.07408856]]) >>> net = MQN2EncoderOnlyOps(grad_ops) >>> f_ms = net(ms.Tensor(data)) >>> f_ms Tensor(shape=[2, 1], dtype=Float32, value= [[ 9.57333017e-03], [ 7.40885586e-02]]) """ def __init__(self, expectation_with_grad): super(MQN2EncoderOnlyOps, self).__init__() _mode_check(self) _check_grad_ops(expectation_with_grad) self.expectation_with_grad = expectation_with_grad self.shape_ops = P.Shape() def extend_repr(self): return self.expectation_with_grad.str def construct(self, x): check_enc_input_shape(x, self.shape_ops(x), len(self.expectation_with_grad.encoder_params_name)) x = x.asnumpy() f, g = self.expectation_with_grad(x) self.f = f f = ms.Tensor(np.abs(f)**2, dtype=ms.float32) self.g = g return f def bprop(self, x, out, dout): dout = dout.asnumpy() grad = 2 * np.real(np.einsum('smp,sm,sm->sp', self.g, dout, np.conj(self.f))) return ms.Tensor(grad, dtype=ms.float32)
def _mode_check(self): if context.get_context('mode') != context.PYNATIVE_MODE: raise RuntimeError(f'{self.__class__} is `PYNATIVE_MODE` supported only. Run command below to set context\n\ import mindspore as ms\n\ ms.context.set_context(mode=ms.context.PYNATIVE_MODE, device_target="CPU")') def _check_grad_ops(expectation_with_grad): if not isinstance(expectation_with_grad, GradOpsWrapper): raise TypeError(f'expectation_with_grad requires a GradOpsWrapper, but get {type(expectation_with_grad)}')