Skip to content

Commit

Permalink
Merge pull request ScQ-Cloud#119 from chensgit169/master
Browse files Browse the repository at this point in the history
Implement Oracle and customize_gate
  • Loading branch information
chensgit169 committed Dec 2, 2023
2 parents ac37a75 + f229419 commit 49ce98a
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 62 deletions.
25 changes: 5 additions & 20 deletions quafu/circuits/quantum_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,27 +381,12 @@ def wrap_to_gate(self, name: str):
"""
Wrap the circuit to a subclass of QuantumGate, create by metaclass.
"""
from quafu.elements.quantum_gate import customize_gate
from quafu.elements.oracle import customize_gate
from copy import deepcopy

gate_structure = []
qubit_mapping = {q: i for i, q in enumerate(self.used_qubits)}
for gate in self.gates:
if isinstance(gate, QuantumGate):
gate = copy.deepcopy(gate)
# TODO: handel control pos
if isinstance(gate.pos, int):
gate.pos = qubit_mapping[gate.pos]
else:
gate.pos = [qubit_mapping[p] for p in gate.pos]
gate_structure.append(gate)
else:
raise ValueError()

# TODO: error check

customized = customize_gate(cls_name=name.lower(),
qubit_num=len(self.used_qubits),
gate_structure=gate_structure)
# TODO: check validity of instructions
gate_structure = [deepcopy(ins) for ins in self.instructions]
customized = customize_gate(name, gate_structure, self.num)
return customized

def id(self, pos: int) -> "QuantumCircuit":
Expand Down
4 changes: 2 additions & 2 deletions quafu/elements/instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,11 @@ def __init__(self, pos):

@property
def named_pos(self):
return self.named_pos
return super().named_pos

@property
def named_paras(self):
return self.named_paras
return super().named_pos

def __repr__(self):
return f"{self.__class__.__name__}"
Expand Down
133 changes: 133 additions & 0 deletions quafu/elements/oracle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# (C) Copyright 2023 Beijing Academy of Quantum Information Sciences
#
# 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 abc import ABCMeta
from quafu.elements import QuantumGate, Instruction
from typing import Dict, Iterable, List
import copy


class OracleGateMeta(ABCMeta):
"""
Metaclass to create OracleGate CLASS which is its instance.
"""

def __init__(cls, name, bases, attrs):
for attr_name in ['cls_name', 'gate_structure', 'qubit_num']:
assert attr_name in attrs, f"OracleGateMeta: {attr_name} not found in {attrs}."

# TODO: check if instructions inside gate_structure are valid

super().__init__(name, bases, attrs)
cls.name = attrs.__getitem__('cls_name')
cls.gate_structure = attrs.__getitem__('gate_structure')
cls.qubit_num = attrs.__getitem__('qubit_num')


class OracleGate(QuantumGate): # TODO: Can it be related to OracleGateMeta explicitly?
"""
OracleGate is a gate that can be customized by users.
"""
name = None
gate_structure = []
qubit_num = 0

_named_pos = {}
insides = []

def __init__(self, pos: List, paras=None, label: str = None):
"""
Args:
pos: position of the gate
paras: parameters of the gate # TODO: how to set paras?
label: label when draw or plot
"""
if not self.qubit_num == len(pos):
raise ValueError(f"OracleGate: qubit number {self.qubit_num} does not match pos length {len(pos)}.")
super().__init__(pos=pos, paras=paras)

self.__instantiate_gates__()
self.label = label if label is not None else self.name

@property
def matrix(self):
# TODO: this should be finished according to usage in simulation
# to avoid store very large matrix
raise NotImplemented

@property
def named_pos(self) -> Dict:
return {'pos': self.pos}

@property
def named_paras(self) -> Dict:
# TODO: how to manage paras and the names?
return self._named_pos

def to_qasm(self):
# TODO: this is similar to QuantumCircuit.to_qasm
raise NotImplemented

def __instantiate_gates__(self) -> None:
"""
Instantiate the gate structure through coping ins and bit mapping.
"""
bit_mapping = {i: p for i, p in enumerate(self.pos)}

def map_pos(pos):
if isinstance(pos, int):
return bit_mapping[pos]
elif isinstance(pos, Iterable):
return [bit_mapping[p] for p in pos]
else:
raise ValueError

for gate in self.gate_structure:
gate_ = copy.deepcopy(gate)
for key, val in gate.named_pos.items():
setattr(gate_, key, map_pos(val))
setattr(gate_, 'pos', map_pos(gate.pos))
self.insides.append(gate_)


def customize_gate(cls_name: str,
gate_structure: List[Instruction],
qubit_num: int,
):
"""
Helper function to create customized gate class
Args:
cls_name: name of the gate class
gate_structure: a list of instruction INSTANCES
qubit_num: number of qubits of the gate (TODO: extract from gate_structure?)
Returns:
customized gate class
Raises:
ValueError: if gate class already exists
"""
if cls_name in QuantumGate.gate_classes:
raise ValueError(f"Gate class {cls_name} already exists.")

attrs = {'cls_name': cls_name,
'gate_structure': gate_structure, # TODO: translate
'qubit_num': qubit_num,
}

customized_cls = OracleGateMeta(cls_name, (OracleGate,), attrs)
assert issubclass(customized_cls, OracleGate)
QuantumGate.register_gate(customized_cls, cls_name)
return customized_cls
74 changes: 37 additions & 37 deletions quafu/elements/quantum_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,43 +147,6 @@ def named_pos(self) -> Dict:
return {'pos': self.pos}


class FixedGate(QuantumGate, ABC):
def __init__(self, pos):
super().__init__(pos=pos, paras=None)

@property
def named_paras(self) -> Dict:
return {}


# class ParaSingleQubitGate(SingleQubitGate, ABC):
# def __init__(self, pos, paras: float):
# if paras is None:
# raise ValueError("`paras` can not be None for ParaSingleQubitGate")
# elif isinstance(paras, int):
# paras = float(paras)
#
# if not isinstance(paras, float):
# raise TypeError(f"`paras` must be float or int for ParaSingleQubitGate, "
# f"instead of {type(paras)}")
# super().__init__(pos, paras=paras)
#
# @property
# def named_paras(self) -> Dict:
# return {'paras': self.paras}

# class FixedMultiQubitGate(MultiQubitGate, ABC):
# def __init__(self, pos: List[int]):
# super().__init__(pos=pos, paras=None)


# class ParaMultiQubitGate(MultiQubitGate, ABC):
# def __init__(self, pos, paras):
# if paras is None:
# raise ValueError("`paras` can not be None for ParaMultiQubitGate")
# super().__init__(pos, paras)


class ControlledGate(MultiQubitGate, ABC):
""" Controlled gate class, where the matrix act non-trivially on target qubits"""

Expand Down Expand Up @@ -238,3 +201,40 @@ def get_targ_matrix(self, reverse_order=False):
@property
def named_pos(self) -> Dict:
return {'ctrls': self.ctrls, 'targs': self.targs}


# class ParaSingleQubitGate(SingleQubitGate, ABC):
# def __init__(self, pos, paras: float):
# if paras is None:
# raise ValueError("`paras` can not be None for ParaSingleQubitGate")
# elif isinstance(paras, int):
# paras = float(paras)
#
# if not isinstance(paras, float):
# raise TypeError(f"`paras` must be float or int for ParaSingleQubitGate, "
# f"instead of {type(paras)}")
# super().__init__(pos, paras=paras)
#
# @property
# def named_paras(self) -> Dict:
# return {'paras': self.paras}

# class FixedMultiQubitGate(MultiQubitGate, ABC):
# def __init__(self, pos: List[int]):
# super().__init__(pos=pos, paras=None)


# class ParaMultiQubitGate(MultiQubitGate, ABC):
# def __init__(self, pos, paras):
# if paras is None:
# raise ValueError("`paras` can not be None for ParaMultiQubitGate")
# super().__init__(pos, paras)


class FixedGate(QuantumGate, ABC):
def __init__(self, pos):
super().__init__(pos=pos, paras=None)

@property
def named_paras(self) -> Dict:
return {}
5 changes: 2 additions & 3 deletions quafu/visualisation/circuitPlot.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,9 @@ def __call__(self,
plt.show()

def _process_ins(self, ins: Instruction, append: bool = True):
name = ins.name
assert name in Instruction.ins_classes, 'If this should occur, please report a bug.'
name = ins.name.lower()
assert name in Instruction.ins_classes, 'Name: %s not registered, if this should occur, please report a bug.' % name

name = name.lower()
_which = slice(np.min(ins.pos), np.max(ins.pos) + 1)
depth = np.max(self.dorders[_which])
paras = ins.paras
Expand Down

0 comments on commit 49ce98a

Please sign in to comment.