#
# Copyright © 2022-2023 University of Strasbourg. 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.
#
from typing import List, Tuple
import mimiqcircuits as mc
[docs]
class GateDecl:
"""
Simple declaration of gates using the @gatedecl decorator.
Examples:
**First way**
>>> from symengine import symbols
>>> from mimiqcircuits import *
Define symbols for the gate arguments
>>> x, y = symbols('x y')
Declare a gate using the @gatedecl decorator
>>> @gatedecl("ansatz")
... def ansatz(x):
... insts = [
... Instruction(GateX(), (1,)),
... Instruction(GateRX(x), (2,))
... ]
... return insts
Create a GateDecl object using the decorator
>>> ansatz(x)
gate ansatz(x) =
├── X @ q[1]
└── RX(x) @ q[2]
<BLANKLINE>
>>> ansatz(y)
gate ansatz(y) =
├── X @ q[1]
└── RX(y) @ q[2]
<BLANKLINE>
Decompose the GateCall into a quantum circuit
>>> GateCall(ansatz(x), (1.57,)).decompose()
3-qubit circuit with 2 instructions:
├── X @ q[1]
└── RX(1.57) @ q[2]
<BLANKLINE>
>>> GateCall(ansatz(y), (1.57,)).decompose()
3-qubit circuit with 2 instructions:
├── X @ q[1]
└── RX(1.57) @ q[2]
<BLANKLINE>
**Second Way**
>>> from symengine import *
>>> from mimiqcircuits import *
Define symbols for the gate arguments
>>> x, y = symbols('x y')
Create a GateDecl object using the GateDecl class
>>> gate_decl = GateDecl("ansatz", ('x','y'), [Instruction(GateXXplusYY(x,y), (1,2)), Instruction(GateRX(x),(2,))])
>>> GateCall(gate_decl, (2,4))
ansatz(2, 4)
Decompose the GateCall into a quantum circuit
>>> GateCall(gate_decl, (2,4)).decompose()
3-qubit circuit with 2 instructions:
├── XXplusYY(2, 4) @ q[1,2]
└── RX(2) @ q[2]
<BLANKLINE>
Add to Circuit
>>> g = GateCall(gate_decl, (2,4))
>>> c = Circuit()
>>> c.push(g,10,22)
23-qubit circuit with 1 instructions:
└── ansatz(2, 4) @ q[10,22]
<BLANKLINE>
"""
_num_qubits = None
_num_bits = None
_name = "GateDecl"
def __init__(
self, name: str, arguments: Tuple[str, ...], instructions: List[mc.Instruction]
):
if not all(isinstance(arg, str) for arg in arguments):
raise TypeError("All GateDecl arguments must be strings.")
if not instructions:
raise ValueError("GateDecl instructions cannot be empty.")
unique_qubits = set()
for inst in instructions:
unique_qubits.update(inst.get_qubits())
nq = len(unique_qubits)
np = len(arguments)
super().__init__()
self.name = name
self.arguments = arguments
self.instructions = instructions
self.num_qubits = nq
self.num_params = np
def __str__(self):
result = f"gate {self.name}({', '.join(map(str, self.arguments))}) =\n"
for i, inst in enumerate(self.instructions):
if i == len(self.instructions) - 1:
result += f"└── {inst}\n"
else:
result += f"├── {inst}\n"
return result
def __repr__(self):
return str(self)
[docs]
class GateCall(mc.Gate):
"""GateCall"""
_name = "GateCall"
_num_bits = 0
_num_qubits = None
def __init__(self, decl: GateDecl, args: Tuple[float, ...]):
if len(args) != len(decl.arguments):
raise ValueError("Wrong number of arguments for GateCall.")
self._num_qubits = decl.num_qubits
self._decl = decl
self._args = args
def _matrix(self):
pass
def _decompose(self, circ, qubits, bits):
d = dict(zip(self._decl.arguments, self._args))
for inst in self._decl.instructions:
op = inst.operation.evaluate(d)
qubits = [i for i in inst.get_qubits()]
bits = [i for i in inst.get_bits()]
circ.push(op, *qubits, *bits)
return circ
def __str__(self):
return f"{self._decl.name}({', '.join(map(str, self._args))})"
def __repr__(self):
return str(self)
[docs]
def evaluate(self, d):
new_args = [
arg.subs(d) if not isinstance(arg, (int, float)) else arg
for arg in self._args
]
return type(self)(self._decl, tuple(new_args))
[docs]
def gatedecl(name):
def decorator(func):
def wrapper(*args, **kwargs):
instructions = func(*args, **kwargs)
arguments = [str(arg) for arg in args] + [
f"{key}={value}" for key, value in kwargs.items()
]
return GateDecl(name, arguments, instructions)
return wrapper
return decorator
__all__ = ["GateCall", "GateDecl", "gatedecl"]