Source code for mindquantum.circuit.high_level_ops

# 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.
# ============================================================================
"""High level circuit operators."""

from types import FunctionType, MethodType
import copy

from mindquantum.circuit import Circuit
from mindquantum.parameterresolver import ParameterResolver as PR


def _add_ctrl_qubits(circ, ctrl_qubits):
    """Add control qubits on a circuit."""
    if not isinstance(ctrl_qubits, (int, list)):
        raise TypeError(
            "Require a int or a list of int for ctrl_qubits, but get {}!".
            format(type(ctrl_qubits)))
    if isinstance(ctrl_qubits, int):
        ctrl_qubits = [ctrl_qubits]
    for q in ctrl_qubits:
        if q < 0:
            raise ValueError(
                "ctrl_qubits should not be negative value, but get {}!".format(
                    q))
    circ_out = Circuit()
    ctrl_qubits = set(ctrl_qubits)
    for gate in circ:
        intersection = ctrl_qubits.intersection(set(gate.obj_qubits))
        if intersection:
            raise ValueError(
                f"Qubit {intersection} in ctrl_qubits {ctrl_qubits} already used n obj_qubits of gate {gate}"
            )
        curr_ctrl = set(gate.ctrl_qubits)
        curr_ctrl = list(curr_ctrl.union(ctrl_qubits))
        curr_ctrl.sort()
        new_gate = copy.deepcopy(gate)
        new_gate.ctrl_qubits = curr_ctrl
        new_gate.generate_description()
        circ_out += new_gate
    return circ_out


[docs]def controlled(circuit_fn): """ Add control qubits on a quantum circuit or a quantum operator (a function that can generate a quantum circuit) Args: circuit_fn (Union[Circuit, FunctionType, MethodType]): A quantum circuit, or a function that can generate a quantum circuit. Examples: >>> from mindquantum.circuit import qft, controlled >>> u1 = qft([0, 1]) >>> u2 = controlled(u1) >>> u3 = controlled(qft) >>> u3 = u3(2, [0, 1]) >>> u2(2) H(0 <-: 2) PS(1.571|0 <-: 1 2) H(1 <-: 2) SWAP(0 1 <-: 2) >>> u3 H(0 <-: 2) PS(1.571|0 <-: 1 2) H(1 <-: 2) SWAP(0 1 <-: 2) """ if isinstance(circuit_fn, (FunctionType, MethodType)): def wrapper(ctrl_qubits, *arg, **keywords): circ = circuit_fn(*arg, **keywords) if not isinstance(circ, Circuit): return controlled(circ) return _add_ctrl_qubits(circ, ctrl_qubits) return wrapper if isinstance(circuit_fn, Circuit): return lambda ctrl_qubits: _add_ctrl_qubits(circuit_fn, ctrl_qubits) raise TypeError( "Input need a circuit or a function that can generate a circuit.")
[docs]def dagger(circuit_fn): """ Get the hermitian dagger of a quantum circuit or a quantum operator (a function that can generate a quantum circuit) Args: circuit_fn (Union[Circuit, FunctionType, MethodType]): A quantum circuit, or a function that can generate a quantum circuit. Examples: >>> from mindquantum.circuit import qft, dagger >>> u1 = qft([0, 1]) >>> u2 = dagger(u1) >>> u3 = dagger(qft) >>> u3 = u3([0, 1]) >>> u2 SWAP(0 1) H(1) PS(-1.571|0 <-: 1) H(0) >>> u3 SWAP(0 1) H(1) PS(-1.571|0 <-: 1) H(0) """ if isinstance(circuit_fn, (FunctionType, MethodType)): def wrapper(*arg, **keywords): circ = circuit_fn(*arg, **keywords) if not isinstance(circ, Circuit): return dagger(circ) return circ.hermitian return wrapper if isinstance(circuit_fn, Circuit): return circuit_fn.hermitian raise TypeError( "Input need a circuit or a function that can generate a circuit.")
def _apply_circuit(circ, qubits): """Apply a circuit to other different qubits.""" old_qubits = set([]) for g in circ: old_qubits.update(g.obj_qubits) old_qubits.update(g.ctrl_qubits) old_qubits = list(old_qubits) old_qubits.sort() if len(old_qubits) != len(qubits): raise ValueError( f"Can not apply a {len(old_qubits)} qubits unit to {len(qubits)} qubits circuit." ) qubits_map = dict(zip(old_qubits, qubits)) out = Circuit() for g in circ: g = copy.deepcopy(g) g.obj_qubits = [qubits_map[i] for i in g.obj_qubits] g.ctrl_qubits = [qubits_map[i] for i in g.ctrl_qubits] g.generate_description() out += g return out
[docs]def apply(circuit_fn, qubits): """ Apply a quantum circuit or a quantum operator (a function that can generate a quantum circuit) to different qubits. Args: circuit_fn (Union[Circuit, FunctionType, MethodType]): A quantum circuit, or a function that can generate a quantum circuit. qubits (list[int]): The new qubits that you want to apply. Examples: >>> from mindquantum.circuit import qft, apply >>> u1 = qft([0, 1]) >>> u2 = apply(u1, [1, 2]) >>> u3 = apply(qft, [1, 2]) >>> u3 = u3([0, 1]) >>> u2 H(1) PS(1.571|1 <-: 2) H(2) SWAP(1 2) >>> u3 H(1) PS(1.571|1 <-: 2) H(2) SWAP(1 2) """ if not isinstance(qubits, list): raise TypeError(f"New qubits need a list, but get {type(qubits)}!") if len(qubits) > 1: for index, q in enumerate(qubits[1:]): if q < 0 or qubits[index] < 0: raise ValueError(f"Qubit index can not negative!") if q <= qubits[index]: raise ValueError(f"Qubits should be in ascending order!") if isinstance(circuit_fn, (FunctionType, MethodType)): def wrapper(*arg, **keywords): circ = circuit_fn(*arg, **keywords) if not isinstance(circ, Circuit): return apply(circ, qubits) return _apply_circuit(circ, qubits) return wrapper if isinstance(circuit_fn, Circuit): return _apply_circuit(circuit_fn, qubits) raise TypeError( "Input need a circuit or a function that can generate a circuit.")
def _add_prefix(circ, prefix): """Add prefix to every parameters in circuit.""" out = Circuit() for g in circ: g = copy.deepcopy(g) if g.isparameter: pr = PR() for k, v in g.coeff.items(): pr[f'{prefix}_{k}'] = v g.coeff = pr g.generate_description() out += g return out
[docs]def add_prefix(circuit_fn, prefix): """ Add a prefix on the parameter of a parameterized quantum circuit or a parameterized quantum operator (a function that can generate a parameterized quantum circuit). Args: circuit_fn (Union[Circuit, FunctionType, MethodType]): A quantum circuit, or a function that can generate a quantum circuit. prefix (str): The prefix you want to add to every parameters. Examples: >>> from mindquantum.circuit import qft, add_prefix >>> from mindquantum import RX, H, Circuit >>> u = lambda qubit: Circuit([H.on(0), RX('a').on(qubit)]) >>> u1 = u(0) >>> u2 = add_prefix(u1, 'ansatz') >>> u3 = add_prefix(u, 'ansatz') >>> u3 = u3(0) >>> u2 H(0) RX(ansatz_a|0) >>> u3 H(0) RX(ansatz_a|0) """ if not isinstance(prefix, str): raise TypeError(f"prefix need string, but get {type(prefix)}") if isinstance(circuit_fn, (FunctionType, MethodType)): def wrapper(*arg, **keywords): circ = circuit_fn(*arg, **keywords) if not isinstance(circ, Circuit): return add_prefix(circ, prefix) return _add_prefix(circ, prefix) return wrapper if isinstance(circuit_fn, Circuit): return _add_prefix(circuit_fn, prefix) raise TypeError( "Input need a circuit or a function that can generate a circuit.")
def _change_param_name(circ, name_map): """Change the parameter of circuit according to the name map.""" out = Circuit() for g in circ: g = copy.deepcopy(g) if g.isparameter: pr = PR() for k, v in g.coeff.items(): if k not in name_map: raise KeyError(f"Original parameter {k} not in name_map!") pr[name_map[k]] = v g.coeff = pr g.generate_description() out += g return out
[docs]def change_param_name(circuit_fn, name_map): """ Change the parameter name of a parameterized quantum circuit or a parameterized quantum operator (a function that can generate a parameterized quantum circuit). Args: circuit_fn (Union[Circuit, FunctionType, MethodType]): A quantum circuit, or a function that can generate a quantum circuit. name_map (dict): The parameter name mapping dict. Examples: >>> from mindquantum.circuit import qft, change_param_name >>> from mindquantum import RX, H, Circuit >>> u = lambda qubit: Circuit([H.on(0), RX('a').on(qubit)]) >>> u1 = u(0) >>> u2 = change_param_name(u1, {'a': 'b'}) >>> u3 = change_param_name(u, {'a': 'b'}) >>> u3 = u3(0) >>> u2 H(0) RX(b|0) >>> u3 H(0) RX(b|0) """ if not isinstance(name_map, dict): raise TypeError( f"Parameters name map need map, but get {type(name_map)}") for k, v in name_map.items(): if not isinstance(k, str): raise ValueError( f"Parameter need a string, but get {k}, which is {type(k)}") if not isinstance(v, str): raise ValueError( f"Parameter need a string, but get {v}, which is {type(v)}") if isinstance(circuit_fn, (FunctionType, MethodType)): def wrapper(*arg, **keywords): circ = circuit_fn(*arg, **keywords) if not isinstance(circ, Circuit): return change_param_name(circ, name_map) return _change_param_name(circ, name_map) return wrapper if isinstance(circuit_fn, Circuit): return _change_param_name(circuit_fn, name_map) raise TypeError( "Input need a circuit or a function that can generate a circuit.")
C = controlled D = dagger A = apply AP = add_prefix CPN = change_param_name