Source code for mindquantum.io.qasm.openqasm

# -*- 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.
# ============================================================================
"""openqasm"""
import numpy as np


def _find_qubit_id(cmd):
    """Find qubit id in openqasm cmd"""
    left = []
    right = []
    for i, j in enumerate(cmd):
        if j == '[':
            left.append(i)
        elif j == ']':
            right.append(i)
    if len(left) != len(right):
        raise ValueError(f"Parsing failed for cmd {cmd}")
    idx = []
    for l, r in zip(left, right):
        idx.append(int(cmd[l + 1:r]))
    return idx


def _extr_parameter(cmd):
    """extra parameter for parameterized gate in openqasm cmd"""
    l = cmd.find('(')
    r = cmd.find(')')
    if l == -1 or r == -1:
        raise ValueError(f"no parameter found in cmd {cmd}")
    all_expre = cmd[l + 1:r]
    all_expre = all_expre.split(',')
    out = []
    for expre in all_expre:
        if 'pi' in expre:
            expre = expre.replace('pi', str(np.pi))
        if '*' in expre:
            tmp = expre.split('*')
            if len(tmp) != 2:
                raise ValueError(f"cannot parse cmd {cmd}")
            expre = str(float(tmp[0]) * float(tmp[1]))
        if '/' in expre:
            tmp = expre.split('/')
            if len(tmp) != 2:
                raise ValueError(f"cannot parse cmd {cmd}")
            expre = str(float(tmp[0]) / float(tmp[1]))
        out.append(float(expre))
    return out[0] if len(all_expre) == 1 else out


def u3(theta, psi, lambd, q):
    """decomp u3 gate"""
    from mindquantum import Circuit
    circ = Circuit().rz(psi + 3 * np.pi, q)
    circ.rx(np.pi / 2, q).rz(theta + np.pi, q)
    circ.rx(np.pi / 2, q).rz(lambd, q)
    return circ


def u1(lambd, q):
    """openqasm u1 gate"""
    from mindquantum import Circuit
    return Circuit().rz(lambd, q)


def isgateinstance(gate, gates):
    """Check whether gate is any instance of supported gate type"""
    if isinstance(gates, list):
        gates = (gates, )
    for gate_test in gates:
        for g in gate_test:
            if isinstance(gate, g):
                return True
    return False


[docs]class OpenQASM: """ Convert a circuit to openqasm format Examples: >>> import numpy as np >>> from mindquantum.io.qasm import OpenQASM >>> from mindquantum.core import Circuit >>> circuit = Circuit().rx(0.3, 0).z(0, 1).zz(np.pi, [0, 1]) >>> openqasm = OpenQASM() >>> circuit_str = openqasm.to_string(circuit) >>> circuit_str[47:60] 'rx(0.3) q[0];' """ def __init__(self): from mindquantum import Circuit self.circuit = Circuit() self.cmds = []
[docs] def to_string(self, circuit, version="2.0"): """ Convert circuit to hiqasm. Args: circuit (Circuit): The quantum circuit you want to translated to openqasm. version (str): The HiQASM version you want to use. Default: '2.0'. Returns: str, The HiQASM format of input circuit. Raises: TypeError: if circuit is not a Circuit. TypeError: if version is not a str. NotImplementedError: if openqasm version not implement. ValueError: if gate not implement in this version. """ from mindquantum import gates as G from mindquantum.core import Circuit if not isinstance(circuit, Circuit): raise TypeError(f"circuit requires Circuit, but get {type(circuit)}.") if not isinstance(version, str): raise TypeError(f"version requires a str, but get {type(version)}") single_np = [G.XGate, G.YGate, G.ZGate] single_p = [G.RX, G.RY, G.RZ, G.PhaseShift] double_np = [G.SWAPGate, G.CNOTGate] double_p = [G.XX, G.YY, G.ZZ] if version == "2.0": self.circuit = circuit self.cmds = [f"OPENQASM {version};", "include \"qelib1.inc\";"] self.cmds.append(f"qreg q[{circuit.n_qubits}];") for gate in self.circuit: if isgateinstance(gate, (single_np, single_p)): if len(gate.ctrl_qubits) > 1: raise ValueError(f"Multiple control for gate {gate} not implement") if isgateinstance(gate, single_np): obj = gate.obj_qubits[0] if gate.ctrl_qubits: ctrl = gate.ctrl_qubits[0] self.cmds.append(f"c{gate.name.lower()} q[{ctrl}],q[{obj}];") else: self.cmds.append(f"{gate.name.lower()} q[{obj}];") else: obj = gate.obj_qubits[0] p = gate.coeff if gate.ctrl_qubits: ctrl = gate.ctrl_qubits[0] self.cmds.append(f"c{gate.name.lower()}({p}) q[{ctrl}],q[{obj}];") else: self.cmds.append(f"{gate.name.lower()}({p}) q[{obj}];") if isgateinstance(gate, (double_np, double_p)): if gate.ctrl_qubits: raise ValueError(f"control two qubits gate {gate} not implement") if isgateinstance(gate, double_np): obj = gate.obj_qubits if isinstance(gate, G.SWAPGate): self.cmds.append(f"cx q[{obj[1]}],q[{obj[0]}];") self.cmds.append(f"cx q[{obj[0]}],q[{obj[1]}];") self.cmds.append(f"cx q[{obj[1]}],q[{obj[0]}];") if isinstance(gate, G.CNOTGate): self.cmds.append(f"cx q[{obj[1]}],q[{obj[0]}];") else: obj = gate.obj_qubits p = gate.coeff self.cmds.append(f"{gate.name.lower()}({p}) q[{obj[0]}],q[{obj[1]}];") else: raise NotImplementedError(f"openqasm version {version} not implement") return '\n'.join(self.cmds)
[docs] def to_file(self, file_name, circuit, version="2.0"): """ Convert a quantum circuit to openqasm format and save in file. Args: file_name (str): The file name you want to save the openqasm file. circuit (Circuit): The Circuit you want to convert. version (str): The version of openqasm. Default: '2.0'. Raises: TypeError: if `file_name` is not a str. TypeError: if `circuit` is not a Circuit. TypeError: if `version` is not a str. """ from mindquantum.core import Circuit if not isinstance(file_name, str): raise TypeError(f'file_name requires a str, but get {type(file_name)}') if not isinstance(circuit, Circuit): raise TypeError(f"circuit requires a Circuit, but get {type(circuit)}") if not isinstance(version, str): raise TypeError(f'version requires a str, but get {type(version)}') cs = self.to_string(circuit, version) with open(file_name, 'w') as f: f.writelines(cs) print(f"write circuit to {file_name} finished!")
[docs] def from_file(self, file_name): """ Read a openqasm file. Args: file_name (str): The path of file that stored quantum circuit in openqasm format. Returns: Circuit, the quantum circuit translated from openqasm file. """ with open(file_name, 'r') as f: cmds = f.readlines() self.cmds, version = self._filter(cmds) if version == '2.0': self._trans_v2(self.cmds) else: raise ValueError(f"OPENQASM {version} not implement yet")
def _filter(self, cmds): """ filter empty cmds and head. """ out = [] version = None for cmd in cmds: cmd = cmd.strip() if not cmd or cmd.startswith('//') or cmd.startswith('include') or cmd.startswith("qreg"): pass elif cmd.startswith('OPENQASM'): version = cmd.split(' ')[-1][:-1] else: out.append(cmd[:-1]) return out, version def _trans_v2(self, cmds): """ trans method for openqasm version 2 """ from mindquantum import Circuit from mindquantum.core.circuit import controlled self.circuit = Circuit() for cmd in cmds: q = _find_qubit_id(cmd) if cmd.startswith("h "): self.circuit.h(q[0]) elif cmd.startswith("x "): self.circuit.x(q[0]) elif cmd.startswith("y "): self.circuit.y(q[0]) elif cmd.startswith("cx "): self.circuit.x(q[1], q[0]) elif cmd.startswith("cz "): self.circuit.z(*q[::-1]) elif cmd.startswith("rz("): self.circuit.rz(_extr_parameter(cmd), q[0]) elif cmd.startswith("ry("): self.circuit.ry(_extr_parameter(cmd), q[0]) elif cmd.startswith("rx("): self.circuit.rx(_extr_parameter(cmd), q[0]) elif cmd.startswith("u3("): self.circuit += u3(*_extr_parameter(cmd), q[0]) elif cmd.startswith("cu1("): self.circuit += controlled(u1(_extr_parameter(cmd), q[1]))(q[0]) else: raise ValueError(f"transfer cmd {cmd} not implement yet!")