Source code for mimiqcircuits.operations.operators.projectors

#
# Copyright © 2022-2023 University of Strasbourg. All Rights Reserved.
# Copyright © 2032-2024 QPerfect. All Rights Reserved.
#
# 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.
#

import symengine as se
import mimiqcircuits as mc


def to_subscript(number):
    subscript_map = str.maketrans("0123456789", "₀₁₂₃₄₅₆₇₈₉")
    return str(number).translate(subscript_map)


[docs] class Projector0(mc.AbstractOperator): r"""One-qubit operator corresponding to a projection onto :math:`|0\rangle`. **Matrix Representation** .. math:: \begin{pmatrix} a & 0 \\ 0 & 0 \end{pmatrix} This matrix is parametrized by `a` to allow for phases/rescaling. Equivalent to :class:`DiagonalOp(a, 0)`. The parameter `a` is optional and is set to 1 by default. See Also: :class:`Projector1`, :class:`DiagonalOp` Parameters: a (complex, optional): The top-left entry of the matrix. Defaults to 1. Examples: >>> from mimiqcircuits import * >>> Projector0() P₀(1) >>> Projector0(0.5) P₀(0.5) >>> c = Circuit() >>> c.push(ExpectationValue(Projector0()), 1, 1) 2-qubit circuit with 1 instructions: └── ⟨P₀(1)⟩ @ q[1], z[1] <BLANKLINE> """ _name = "Projector0" _num_qubits = 1 _parnames = () _qregsizes = [1] def __init__(self, a=1): self.a = a super().__init__() self._parnames = ("a",) def _matrix(self): return se.Matrix([[self.a, 0], [0, 0]]) @property def parnames(self): return self._parnames def __str__(self): return f"P{to_subscript(0)}({self.a})"
[docs] def rescale(self, scale): return Projector0(self.a * scale)
[docs] def rescale_inplace(self, scale): self.a *= scale return self
[docs] def opsquared(self): return Projector0(abs(self.a) ** 2)
[docs] class Projector1(mc.AbstractOperator): r"""One-qubit operator corresponding to a projection onto :math:`|1\rangle`. **Matrix Representation** .. math:: \begin{pmatrix} 0 & 0 \\ 0 & a \end{pmatrix} This matrix is parametrized by `a` to allow for phases/rescaling. Equivalent to :class:`DiagonalOp(0, a)`. The parameter `a` is optional and is set to 1 by default. See Also: :class:`Projector0`, :class:`DiagonalOp` Parameters: a (complex, optional): The bottom-right entry of the matrix. Defaults to 1. Examples: >>> from mimiqcircuits import * >>> Projector1() P₁(1) >>> Projector1(0.5) P₁(0.5) >>> c = Circuit() >>> c.push(ExpectationValue(Projector1()), 1, 1) 2-qubit circuit with 1 instructions: └── ⟨P₁(1)⟩ @ q[1], z[1] <BLANKLINE> """ _name = "Projector1" _num_qubits = 1 _parnames = () _qregsizes = [1] def __init__(self, a=1): self.a = a super().__init__() self._parnames = ("a",) def _matrix(self): return se.Matrix([[0, 0], [0, self.a]]) @property def parnames(self): return self._parnames def __str__(self): return f"P{to_subscript(1)}({self.a})"
[docs] def rescale(self, scale): return Projector1(self.a * scale)
[docs] def rescale_inplace(self, scale): self.a *= scale return self
[docs] def opsquared(self): return Projector1(abs(self.a) ** 2)
[docs] class ProjectorX0(mc.AbstractOperator): r"""One-qubit operator corresponding to a projection onto :math:`|+\rangle`. **Matrix Representation** .. math:: \frac{a}{2} \begin{pmatrix} 1 & 1 \\ 1 & 1 \end{pmatrix} This matrix is parametrized by `a` to allow for phases/rescaling. The parameter `a` is optional and is set to 1 by default. See Also: :class:`ProjectorX1` Parameters: a (complex, optional): Scaling factor for the matrix. Defaults to 1. Examples: >>> from mimiqcircuits import * >>> ProjectorX0() PX₀(1) >>> ProjectorX0(0.5) PX₀(0.5) >>> c = Circuit() >>> c.push(ExpectationValue(ProjectorX0()), 1, 1) 2-qubit circuit with 1 instructions: └── ⟨PX₀(1)⟩ @ q[1], z[1] <BLANKLINE> """ _name = "PX0" _num_qubits = 1 _parnames = () _qregsizes = [1] def __init__(self, a=1): self.a = a super().__init__() self._parnames = ("a",) def _matrix(self): return self.a / 2 * se.Matrix([[1, 1], [1, 1]]) @property def parnames(self): return self._parnames def __str__(self): return f"PX{to_subscript(0)}({self.a})"
[docs] def rescale(self, scale): return ProjectorX0(self.a * scale)
[docs] def rescale_inplace(self, scale): self.a *= scale return self
[docs] def opsquared(self): return ProjectorX0(abs(self.a) ** 2)
[docs] class ProjectorX1(mc.AbstractOperator): r"""One-qubit operator corresponding to a projection onto :math:`|-\rangle`. **Matrix Representation** .. math:: \frac{a}{2} \begin{pmatrix} 1 & -1 \\ -1 & 1 \end{pmatrix} This matrix is parametrized by `a` to allow for phases/rescaling. The parameter `a` is optional and is set to 1 by default. See Also: :class:`ProjectorX0` Parameters: a (complex, optional): Scaling factor for the matrix. Defaults to 1. Examples: >>> from mimiqcircuits import * >>> ProjectorX1() PX₁(1) >>> ProjectorX1(0.5) PX₁(0.5) >>> c = Circuit() >>> c.push(ExpectationValue(ProjectorX1()), 1, 1) 2-qubit circuit with 1 instructions: └── ⟨PX₁(1)⟩ @ q[1], z[1] <BLANKLINE> """ _name = "PX1" _num_qubits = 1 _parnames = () _qregsizes = [1] def __init__(self, a=1): self.a = a super().__init__() self._parnames = ("a",) def _matrix(self): return self.a / 2 * se.Matrix([[1, -1], [-1, 1]]) @property def parnames(self): return self._parnames def __str__(self): return f"PX{to_subscript(1)}({self.a})"
[docs] def rescale(self, scale): return ProjectorX1(self.a * scale)
[docs] def rescale_inplace(self, scale): self.a *= scale return self
[docs] def opsquared(self): return ProjectorX1(abs(self.a) ** 2)
[docs] class ProjectorY0(mc.AbstractOperator): r"""One-qubit operator corresponding to a projection onto :math:`|y+\rangle`. **Matrix Representation** .. math:: \frac{a}{2} \begin{pmatrix} 1 & -i \\ i & 1 \end{pmatrix} This matrix is parametrized by `a` to allow for phases/rescaling. The parameter `a` is optional and is set to 1 by default. See Also: :class:`ProjectorY1` Parameters: a (complex, optional): Scaling factor for the matrix. Defaults to 1. Examples: >>> from mimiqcircuits import * >>> ProjectorY0() PY₀(1) >>> ProjectorY0(0.5) PY₀(0.5) >>> c = Circuit() >>> c.push(ExpectationValue(ProjectorY0()), 1, 1) 2-qubit circuit with 1 instructions: └── ⟨PY₀(1)⟩ @ q[1], z[1] <BLANKLINE> """ _name = "PY0" _num_qubits = 1 _parnames = () _qregsizes = [1] def __init__(self, a=1): self.a = a super().__init__() self._parnames = ("a",) def _matrix(self): return self.a / 2 * se.Matrix([[1, -1j], [1j, 1]]) @property def parnames(self): return self._parnames def __str__(self): return f"PY{to_subscript(0)}({self.a})"
[docs] def rescale(self, scale): return ProjectorY0(self.a * scale)
[docs] def rescale_inplace(self, scale): self.a *= scale return self
[docs] def opsquared(self): return ProjectorY0(abs(self.a) ** 2)
[docs] class ProjectorY1(mc.AbstractOperator): r"""One-qubit operator corresponding to a projection onto :math:`|y-\rangle`. **Matrix Representation** .. math:: \frac{a}{2} \begin{pmatrix} 1 & i \\ -i & 1 \end{pmatrix} This matrix is parametrized by `a` to allow for phases/rescaling. The parameter `a` is optional and is set to 1 by default. See Also: :class:`ProjectorY0` Parameters: a (complex, optional): Scaling factor for the matrix. Defaults to 1. Examples: >>> from mimiqcircuits import * >>> ProjectorY1() PY₁(1) >>> ProjectorY1(0.5) PY₁(0.5) >>> c = Circuit() >>> c.push(ExpectationValue(ProjectorY1()), 1, 1) 2-qubit circuit with 1 instructions: └── ⟨PY₁(1)⟩ @ q[1], z[1] <BLANKLINE> """ _name = "PY1" _num_qubits = 1 _parnames = () _qregsizes = [1] def __init__(self, a=1): self.a = a super().__init__() self._parnames = ("a",) def _matrix(self): return self.a / 2 * se.Matrix([[1, 1j], [-1j, 1]]) @property def parnames(self): return self._parnames def __str__(self): return f"PY{to_subscript(1)}({self.a})"
[docs] def rescale(self, scale): return ProjectorY1(self.a * scale)
[docs] def rescale_inplace(self, scale): self.a *= scale return self
[docs] def opsquared(self): return ProjectorY1(abs(self.a) ** 2)
[docs] class Projector00(mc.AbstractOperator): r"""Two-qubit operator corresponding to a projection onto :math:`|00\rangle`. **Matrix Representation** .. math:: \begin{pmatrix} a & 0 & 0 & 0\\ 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 \end{pmatrix} This matrix is parametrized by `a` to allow for phases/rescaling. The parameter `a` is optional and is set to 1 by default. See Also: :class:`Projector01`, :class:`Projector10`, :class:`Projector11` Parameters: a (complex, optional): Scaling factor for the matrix. Defaults to 1. Examples: >>> from mimiqcircuits import * >>> Projector00() P₀₀(1) >>> Projector00(0.5) P₀₀(0.5) >>> c = Circuit() >>> c.push(ExpectationValue(Projector00()), 1, 2, 1) 3-qubit circuit with 1 instructions: └── ⟨P₀₀(1)⟩ @ q[1,2], z[1] <BLANKLINE> """ _name = "Projector00" _num_qubits = 2 _parnames = () _qregsizes = [2] def __init__(self, a=1): self.a = a super().__init__() self._parnames = ("a",) def _matrix(self): return se.Matrix([[self.a, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]) @property def parnames(self): return self._parnames def __str__(self): return f"P{to_subscript(0)}{to_subscript(0)}({self.a})"
[docs] def rescale(self, scale): return Projector00(self.a * scale)
[docs] def rescale_inplace(self, scale): self.a *= scale return self
[docs] def opsquared(self): return Projector00(abs(self.a) ** 2)
[docs] class Projector01(mc.AbstractOperator): r"""Two-qubit operator corresponding to a projection onto :math:`|01\rangle`. **Matrix Representation** .. math:: \begin{pmatrix} 0 & 0 & 0 & 0\\ 0 & a & 0 & 0\\ 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 \end{pmatrix} This matrix is parametrized by `a` to allow for phases/rescaling. The parameter `a` is optional and is set to 1 by default. See Also: :class:`Projector00`, :class:`Projector10`, :class:`Projector11` Parameters: a (complex, optional): Scaling factor for the matrix. Defaults to 1. Examples: >>> from mimiqcircuits import * >>> Projector01() P₀₁(1) >>> Projector01(0.5) P₀₁(0.5) >>> c = Circuit() >>> c.push(ExpectationValue(Projector01()), 1, 2, 1) 3-qubit circuit with 1 instructions: └── ⟨P₀₁(1)⟩ @ q[1,2], z[1] <BLANKLINE> """ _name = "Projector01" _num_qubits = 2 _parnames = () _qregsizes = [2] def __init__(self, a=1): self.a = a super().__init__() self._parnames = ("a",) def _matrix(self): return se.Matrix([[0, 0, 0, 0], [0, self.a, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]) @property def parnames(self): return self._parnames def __str__(self): return f"P{to_subscript(0)}{to_subscript(1)}({self.a})"
[docs] def rescale(self, scale): return Projector01(self.a * scale)
[docs] def rescale_inplace(self, scale): self.a *= scale return self
[docs] def opsquared(self): return Projector01(abs(self.a) ** 2)
[docs] class Projector10(mc.AbstractOperator): r"""Two-qubit operator corresponding to a projection onto :math:`|10\rangle`. **Matrix Representation** .. math:: \begin{pmatrix} 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0\\ 0 & 0 & a & 0\\ 0 & 0 & 0 & 0 \end{pmatrix} This matrix is parametrized by `a` to allow for phases/rescaling. The parameter `a` is optional and is set to 1 by default. See Also: :class:`Projector00`, :class:`Projector01`, :class:`Projector11` Parameters: a (complex, optional): Scaling factor for the matrix. Defaults to 1. Examples: >>> from mimiqcircuits import * >>> Projector10() P₁₀(1) >>> Projector10(0.5) P₁₀(0.5) >>> c = Circuit() >>> c.push(ExpectationValue(Projector10()), 1, 2, 1) 3-qubit circuit with 1 instructions: └── ⟨P₁₀(1)⟩ @ q[1,2], z[1] <BLANKLINE> """ _name = "Projector10" _num_qubits = 2 _parnames = () _qregsizes = [2] def __init__(self, a=1): self.a = a super().__init__() self._parnames = ("a",) def _matrix(self): return se.Matrix([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, self.a, 0], [0, 0, 0, 0]]) @property def parnames(self): return self._parnames def __str__(self): return f"P{to_subscript(1)}{to_subscript(0)}({self.a})"
[docs] def rescale(self, scale): return Projector10(self.a * scale)
[docs] def rescale_inplace(self, scale): self.a *= scale return self
[docs] def opsquared(self): return Projector10(abs(self.a) ** 2)
[docs] class Projector11(mc.AbstractOperator): r"""Two-qubit operator corresponding to a projection onto :math:`|11\rangle`. **Matrix Representation** .. math:: \begin{pmatrix} 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0\\ 0 & 0 & 0 & a \end{pmatrix} This matrix is parametrized by `a` to allow for phases/rescaling. The parameter `a` is optional and is set to 1 by default. See Also: :class:`Projector00`, :class:`Projector01`, :class:`Projector10` Parameters: a (complex, optional): Scaling factor for the matrix. Defaults to 1. Examples: >>> from mimiqcircuits import * >>> Projector11() P₁₁(1) >>> Projector11(0.5) P₁₁(0.5) >>> c = Circuit() >>> c.push(ExpectationValue(Projector11()), 1, 2, 1) 3-qubit circuit with 1 instructions: └── ⟨P₁₁(1)⟩ @ q[1,2], z[1] <BLANKLINE> """ _name = "Projector11" _num_qubits = 2 _parnames = () _qregsizes = [2] def __init__(self, a=1): self.a = a super().__init__() self._parnames = ("a",) def _matrix(self): return se.Matrix([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, self.a]]) @property def parnames(self): return self._parnames def __str__(self): return f"P{to_subscript(1)}{to_subscript(1)}({self.a})"
[docs] def rescale(self, scale): return Projector11(self.a * scale)
[docs] def rescale_inplace(self, scale): self.a *= scale return self
[docs] def opsquared(self): return Projector11(abs(self.a) ** 2)
[docs] class ProjectorZ0(mc.AbstractOperator): r"""ProjectorZ0() Alias for :class:`Projector0` """ def __new__(self): return Projector0()
[docs] class ProjectorZ1(mc.AbstractOperator): r"""ProjectorZ1() Alias for :class:`Projector1` """ def __new__(self): return Projector1()
__all__ = [ "Projector0", "Projector00", "Projector01", "Projector1", "Projector10", "Projector11", "ProjectorY1", "ProjectorY0", "ProjectorX1", "ProjectorX0", "ProjectorZ1", "ProjectorZ0" ]