diff --git a/qiskit/assembler/assemble_schedules.py b/qiskit/assembler/assemble_schedules.py index d5ced498a03b..0c9cf634d81a 100644 --- a/qiskit/assembler/assemble_schedules.py +++ b/qiskit/assembler/assemble_schedules.py @@ -13,213 +13,221 @@ # that they have been altered from the originals. """Assemble function for converting a list of circuits into a qobj.""" +from typing import Any, Dict, List, Tuple from collections import defaultdict from qiskit.exceptions import QiskitError -from qiskit.pulse.commands import (PulseInstruction, AcquireInstruction, +from qiskit.pulse import Schedule +from qiskit.pulse.commands import (Command, PulseInstruction, Acquire, AcquireInstruction, DelayInstruction, SamplePulse, ParametricInstruction) -from qiskit.qobj import (PulseQobj, QobjExperimentHeader, +from qiskit.qobj import (PulseQobj, QobjHeader, QobjExperimentHeader, PulseQobjInstruction, PulseQobjExperimentConfig, PulseQobjExperiment, PulseQobjConfig, PulseLibraryItem) from qiskit.qobj.converters import InstructionToQobjConverter, LoConfigConverter from qiskit.qobj.converters.pulse_instruction import ParametricPulseShapes from qiskit.qobj.utils import MeasLevel, MeasReturnType +from .run_config import RunConfig -def assemble_schedules(schedules, qobj_id, qobj_header, run_config): + +def assemble_schedules(schedules: List[Schedule], + qobj_id: int, + qobj_header: QobjHeader, + run_config: RunConfig) -> PulseQobj: """Assembles a list of schedules into a qobj that can be run on the backend. Args: - schedules (list[Schedule]): schedules to assemble - qobj_id (int): identifier for the generated qobj - qobj_header (QobjHeader): header to pass to the results - run_config (RunConfig): configuration of the runtime environment + schedules: Schedules to assemble. + qobj_id: Identifier for the generated qobj. + qobj_header: Header to pass to the results. + run_config: Configuration of the runtime environment. + Returns: - PulseQobj: the Qobj to be run on the backends + The Qobj to be run on the backends. + Raises: - QiskitError: when invalid schedules or configs are provided + QiskitError: when frequency settings are not supplied. """ - if hasattr(run_config, 'instruction_converter'): - instruction_converter = run_config.instruction_converter - else: - instruction_converter = InstructionToQobjConverter - - qobj_config = run_config.to_dict() - - qubit_lo_freq = qobj_config.get('qubit_lo_freq', None) - if qubit_lo_freq is None: + if not hasattr(run_config, 'qubit_lo_freq'): raise QiskitError('qubit_lo_freq must be supplied.') - - meas_lo_freq = qobj_config.get('meas_lo_freq', None) - if meas_lo_freq is None: + if not hasattr(run_config, 'meas_lo_freq'): raise QiskitError('meas_lo_freq must be supplied.') - meas_map = qobj_config.pop('meas_map', None) + lo_converter = LoConfigConverter(PulseQobjExperimentConfig, + **run_config.to_dict()) + experiments, experiment_config = _assemble_experiments(schedules, + lo_converter, + run_config) + qobj_config = _assemble_config(lo_converter, experiment_config, run_config) + + return PulseQobj(experiments=experiments, + qobj_id=qobj_id, + header=qobj_header, + config=qobj_config) + + +def _assemble_experiments( + schedules: List[Schedule], + lo_converter: LoConfigConverter, + run_config: RunConfig +) -> Tuple[List[PulseQobjExperiment], Dict[str, Any]]: + """Assembles a list of schedules into PulseQobjExperiments, and returns related metadata that + will be assembled into the Qobj configuration. - # convert enums to serialized values - meas_return = qobj_config.get('meas_return', 'avg') - if isinstance(meas_return, MeasReturnType): - qobj_config['meas_return'] = meas_return.value + Args: + schedules: Schedules to assemble. + lo_converter: The configured frequency converter and validator. + run_config: Configuration of the runtime environment. - meas_level = qobj_config.get('meas_return', 2) - if isinstance(meas_level, MeasLevel): - qobj_config['meas_level'] = meas_level.value + Returns: + The list of assembled experiments, and the dictionary of related experiment config. - instruction_converter = instruction_converter(PulseQobjInstruction, **qobj_config) + Raises: + QiskitError: when frequency settings are not compatible with the experiments. + """ + freq_configs = [lo_converter(lo_dict) for lo_dict in getattr(run_config, 'schedule_los', [])] - qubit_lo_range = qobj_config.pop('qubit_lo_range', None) - meas_lo_range = qobj_config.pop('meas_lo_range', None) - lo_converter = LoConfigConverter(PulseQobjExperimentConfig, - qubit_lo_range=qubit_lo_range, - meas_lo_range=meas_lo_range, - **qobj_config) + if len(schedules) > 1 and len(freq_configs) not in [0, 1, len(schedules)]: + raise QiskitError('Invalid frequency setting is specified. If the frequency is specified, ' + 'it should be configured the same for all schedules, configured for each ' + 'schedule, or a list of frequencies should be provided for a single ' + 'frequency sweep schedule.') - memory_slot_size = 0 + instruction_converter = getattr(run_config, 'instruction_converter', InstructionToQobjConverter) + instruction_converter = instruction_converter(PulseQobjInstruction, **run_config.to_dict()) - # Pack everything into the Qobj - qobj_schedules = [] user_pulselib = {} + experiments = [] for idx, schedule in enumerate(schedules): - # instructions - max_memory_slot = 0 - qobj_instructions = [] - acquire_instruction_map = defaultdict(list) - - # Instructions are returned as tuple of shifted time and instruction - for shift, instruction in schedule.instructions: - # TODO: support conditional gate - - if isinstance(instruction, ParametricInstruction): - pulse_shape = ParametricPulseShapes(type(instruction.command)).name - if pulse_shape not in run_config.parametric_pulses: - # Convert to SamplePulse if the backend does not support it - instruction = PulseInstruction(instruction.command.get_sample_pulse(), - instruction.channels[0], - name=instruction.name) - - if isinstance(instruction, PulseInstruction): - name = instruction.command.name - if name in user_pulselib and instruction.command != user_pulselib[name]: - name = "{0}-{1:x}".format(name, hash(instruction.command.samples.tostring())) - instruction = PulseInstruction( - command=SamplePulse(name=name, samples=instruction.command.samples), - name=instruction.name, - channel=instruction.channels[0]) - # add samples to pulse library - user_pulselib[name] = instruction.command - - if isinstance(instruction, AcquireInstruction): - max_memory_slot = max(max_memory_slot, - *[slot.index for slot in instruction.mem_slots]) - # Acquires have a single AcquireChannel per inst, but we have to bundle them - # together into the Qobj as one instruction with many channels - acquire_instruction_map[(shift, instruction.command)].append(instruction) - continue - - if isinstance(instruction, DelayInstruction): - # delay instructions are ignored as timing is explicit within qobj - continue - - qobj_instructions.append(instruction_converter(shift, instruction)) - - if acquire_instruction_map: - if meas_map: - _validate_meas_map(acquire_instruction_map, meas_map) - for (shift, _), instructions in acquire_instruction_map.items(): - qubits, mem_slots, reg_slots = _bundle_channel_indices(instructions) - qobj_instructions.append( - instruction_converter.convert_single_acquires( - shift, instructions[0], - qubits=qubits, memory_slot=mem_slots, register_slot=reg_slots)) - - # memory slot size is memory slot index + 1 because index starts from zero - exp_memory_slot_size = max_memory_slot + 1 - memory_slot_size = max(memory_slot_size, exp_memory_slot_size) - - # experiment header + qobj_instructions, user_pulses, max_memory_slot = _assemble_instructions( + schedule, + instruction_converter, + run_config) + user_pulselib.update(user_pulses) + # TODO: add other experimental header items (see circuit assembler) qobj_experiment_header = QobjExperimentHeader( - memory_slots=exp_memory_slot_size, - name=schedule.name or 'Experiment-%d' % idx - ) + memory_slots=max_memory_slot + 1, # Memory slots are 0 indexed + name=schedule.name or 'Experiment-%d' % idx) + + experiment = PulseQobjExperiment( + header=qobj_experiment_header, + instructions=qobj_instructions) + if freq_configs: + # This handles the cases where one frequency setting applies to all experiments and + # where each experiment has a different frequency + freq_idx = idx if len(freq_configs) != 1 else 0 + experiment.config = freq_configs[freq_idx] + + experiments.append(experiment) + + # Frequency sweep + if freq_configs and len(experiments) == 1: + experiment = experiments[0] + experiments = [] + for freq_config in freq_configs: + experiments.append(PulseQobjExperiment( + header=experiment.header, + instructions=experiment.instructions, + config=freq_config)) - qobj_schedules.append({ - 'header': qobj_experiment_header, - 'instructions': qobj_instructions - }) + # Top level Qobj configuration + experiment_config = { + 'pulse_library': [PulseLibraryItem(name=pulse.name, samples=pulse.samples) + for pulse in user_pulselib.values()], + 'memory_slots': max([exp.header.memory_slots for exp in experiments]) + } - # set number of memoryslots - qobj_config['memory_slots'] = memory_slot_size + return experiments, experiment_config - # setup pulse_library - qobj_config['pulse_library'] = [PulseLibraryItem(name=pulse.name, samples=pulse.samples) - for pulse in user_pulselib.values()] - # convert lo frequencies to GHz - qobj_config['qubit_lo_freq'] = [freq/1e9 for freq in qubit_lo_freq] - qobj_config['meas_lo_freq'] = [freq/1e9 for freq in meas_lo_freq] +def _assemble_instructions( + schedule: Schedule, + instruction_converter: InstructionToQobjConverter, + run_config: RunConfig +) -> Tuple[List[PulseQobjInstruction], Dict[str, Command], int]: + """Assembles the instructions in a schedule into a list of PulseQobjInstructions and returns + related metadata that will be assembled into the Qobj configuration. - # create qobj experiment field - experiments = [] - schedule_los = qobj_config.pop('schedule_los', []) - - if len(schedule_los) == 1: - lo_dict = schedule_los[0] - # update global config - q_los = lo_converter.get_qubit_los(lo_dict) - if q_los: - qobj_config['qubit_lo_freq'] = [freq/1e9 for freq in q_los] - m_los = lo_converter.get_meas_los(lo_dict) - if m_los: - qobj_config['meas_lo_freq'] = [freq/1e9 for freq in m_los] - - if schedule_los: - # multiple frequency setups - if len(qobj_schedules) == 1: - # frequency sweep - for lo_dict in schedule_los: - experiments.append(PulseQobjExperiment( - instructions=qobj_schedules[0]['instructions'], - header=qobj_schedules[0]['header'], - config=lo_converter(lo_dict) - )) - elif len(qobj_schedules) == len(schedule_los): - # n:n setup - for lo_dict, schedule in zip(schedule_los, qobj_schedules): - experiments.append(PulseQobjExperiment( - instructions=schedule['instructions'], - header=schedule['header'], - config=lo_converter(lo_dict) - )) - else: - raise QiskitError('Invalid LO setting is specified. ' - 'The LO should be configured for each schedule, or ' - 'single setup for all schedules (unique), or ' - 'multiple setups for a single schedule (frequency sweep),' - 'or no LO configured at all.') - else: - # unique frequency setup - for schedule in qobj_schedules: - experiments.append(PulseQobjExperiment( - instructions=schedule['instructions'], - header=schedule['header'], - )) + Args: + schedule: Schedule to assemble. + instruction_converter: A converter instance which can convert PulseInstructions to + PulseQobjInstructions. + run_config: Configuration of the runtime environment. - qobj_config = PulseQobjConfig(**qobj_config) + Returns: + A list of converted instructions, the user pulse library dictionary (from pulse name to + pulse command), and the maximum number of readout memory slots used by this Schedule. + """ + max_memory_slot = 0 + qobj_instructions = [] + user_pulselib = {} - return PulseQobj(qobj_id=qobj_id, - config=qobj_config, - experiments=experiments, - header=qobj_header) + acquire_instruction_map = defaultdict(list) + for time, instruction in schedule.instructions: + + if isinstance(instruction, ParametricInstruction): + pulse_shape = ParametricPulseShapes(type(instruction.command)).name + if pulse_shape not in run_config.parametric_pulses: + # Convert to SamplePulse if the backend does not support it + instruction = PulseInstruction(instruction.command.get_sample_pulse(), + instruction.channels[0], + name=instruction.name) + + if isinstance(instruction, PulseInstruction): + name = instruction.command.name + if name in user_pulselib and instruction.command != user_pulselib[name]: + name = "{0}-{1:x}".format(name, hash(instruction.command.samples.tostring())) + instruction = PulseInstruction( + command=SamplePulse(name=name, samples=instruction.command.samples), + name=instruction.name, + channel=instruction.channels[0]) + # add samples to pulse library + user_pulselib[name] = instruction.command + + if isinstance(instruction, AcquireInstruction): + max_memory_slot = max(max_memory_slot, + *[slot.index for slot in instruction.mem_slots]) + # Acquires have a single AcquireChannel per inst, but we have to bundle them + # together into the Qobj as one instruction with many channels + acquire_instruction_map[(time, instruction.command)].append(instruction) + continue + + if isinstance(instruction, DelayInstruction): + # delay instructions are ignored as timing is explicit within qobj + continue + + qobj_instructions.append(instruction_converter(time, instruction)) + + if acquire_instruction_map: + if hasattr(run_config, 'meas_map'): + _validate_meas_map(acquire_instruction_map, run_config.meas_map) + for (time, _), instructions in acquire_instruction_map.items(): + qubits, mem_slots, reg_slots = _bundle_channel_indices(instructions) + qobj_instructions.append( + instruction_converter.convert_single_acquires( + time, instructions[0], + qubits=qubits, memory_slot=mem_slots, register_slot=reg_slots)) + + return qobj_instructions, user_pulselib, max_memory_slot + + +def _validate_meas_map(instruction_map: Dict[Tuple[int, Acquire], List[AcquireInstruction]], + meas_map: List[List[int]]) -> None: + """Validate all qubits tied in ``meas_map`` are to be acquired. + Args: + instruction_map: A dictionary grouping AcquireInstructions according to their start time + and the command features (notably, their duration). + meas_map: List of groups of qubits that must be acquired together. -def _validate_meas_map(instruction_map, meas_map): - """Validate all qubits tied in meas_map are to be acquired.""" + Raises: + QiskitError: If the instructions do not satisfy the measurement map. + """ meas_map_sets = [set(m) for m in meas_map] # Check each acquisition time individually for _, instructions in instruction_map.items(): - measured_qubits = set() for inst in instructions: measured_qubits.update([acq.index for acq in inst.acquires]) @@ -231,9 +239,18 @@ def _validate_meas_map(instruction_map, meas_map): 'in measurement map: {1}'.format(measured_qubits, meas_set)) -def _bundle_channel_indices(instructions): +def _bundle_channel_indices( + instructions: List[AcquireInstruction] +) -> Tuple[List[int], List[int], List[int]]: """From the list of AcquireInstructions, bundle the indices of the acquire channels, - memory slots, and register slots into a 3-tuple of lists.""" + memory slots, and register slots into a 3-tuple of lists. + + Args: + instructions: A list of AcquireInstructions to be bundled. + + Returns: + The qubit indices, the memory slot indices, and register slot indices from instructions. + """ qubits = [] mem_slots = [] reg_slots = [] @@ -242,3 +259,52 @@ def _bundle_channel_indices(instructions): mem_slots.extend(mem_slot.index for mem_slot in inst.mem_slots) reg_slots.extend(reg.index for reg in inst.reg_slots) return qubits, mem_slots, reg_slots + + +def _assemble_config(lo_converter: LoConfigConverter, + experiment_config: Dict[str, Any], + run_config: RunConfig) -> PulseQobjConfig: + """Assembles the QobjConfiguration from experimental config and runtime config. + + Args: + lo_converter: The configured frequency converter and validator. + experiment_config: Schedules to assemble. + run_config: Configuration of the runtime environment. + + Returns: + The assembled PulseQobjConfig. + """ + qobj_config = run_config.to_dict() + qobj_config.update(experiment_config) + + # Run config not needed in qobj config + qobj_config.pop('meas_map', None) + qobj_config.pop('qubit_lo_range', None) + qobj_config.pop('meas_lo_range', None) + + # convert enums to serialized values + meas_return = qobj_config.get('meas_return', 'avg') + if isinstance(meas_return, MeasReturnType): + qobj_config['meas_return'] = meas_return.value + + meas_level = qobj_config.get('meas_level', 2) + if isinstance(meas_level, MeasLevel): + qobj_config['meas_level'] = meas_level.value + + # convert lo frequencies to Hz + qobj_config['qubit_lo_freq'] = [freq / 1e9 for freq in qobj_config['qubit_lo_freq']] + qobj_config['meas_lo_freq'] = [freq / 1e9 for freq in qobj_config['meas_lo_freq']] + + # frequency sweep config + schedule_los = qobj_config.pop('schedule_los', []) + if len(schedule_los) == 1: + lo_dict = schedule_los[0] + q_los = lo_converter.get_qubit_los(lo_dict) + # Hz -> GHz + if q_los: + qobj_config['qubit_lo_freq'] = [freq / 1e9 for freq in q_los] + m_los = lo_converter.get_meas_los(lo_dict) + if m_los: + qobj_config['meas_lo_freq'] = [freq / 1e9 for freq in m_los] + + return PulseQobjConfig(**qobj_config) diff --git a/qiskit/assembler/disassemble.py b/qiskit/assembler/disassemble.py index 3b66c559a1eb..132ebd30a216 100644 --- a/qiskit/assembler/disassemble.py +++ b/qiskit/assembler/disassemble.py @@ -50,8 +50,6 @@ def _experiments_to_circuits(qobj): conditional = {} for i in x.instructions: name = i.name - if i.name == 'id': - name = 'iden' qubits = [] params = getattr(i, 'params', []) try: diff --git a/qiskit/circuit/__init__.py b/qiskit/circuit/__init__.py index 6003ffbd20dd..72cde06fe2af 100644 --- a/qiskit/circuit/__init__.py +++ b/qiskit/circuit/__init__.py @@ -54,11 +54,19 @@ ParameterVector ParameterExpression +Random Circuits +=============== + +.. autosummary:: + :toctree: ../stubs/ + + random.random_circuit """ from .quantumcircuit import QuantumCircuit from .classicalregister import ClassicalRegister, Clbit from .quantumregister import QuantumRegister, Qubit from .gate import Gate +# pylint: disable=cyclic-import from .controlledgate import ControlledGate from .instruction import Instruction from .instructionset import InstructionSet diff --git a/qiskit/circuit/add_control.py b/qiskit/circuit/add_control.py index ae85a063b62e..e3a47731febe 100644 --- a/qiskit/circuit/add_control.py +++ b/qiskit/circuit/add_control.py @@ -14,22 +14,39 @@ """ Add control to operation if supported. """ -from qiskit import QiskitError +from typing import Union, Optional + +from qiskit.circuit.exceptions import CircuitError from qiskit.extensions import UnitaryGate +from . import ControlledGate, Gate, QuantumRegister, QuantumCircuit + + +def add_control(operation: Union[Gate, ControlledGate], + num_ctrl_qubits: int, + label: Union[str, None], + ctrl_state: Union[int, str, None]) -> ControlledGate: + """For standard gates, if the controlled version already exists in the + library, it will be returned (e.g. XGate.control() = CnotGate(). + For more generic gates, this method implements the controlled + version by first decomposing into the ['u1', 'u3', 'cx'] basis, then + controlling each gate in the decomposition. -def add_control(operation, num_ctrl_qubits, label): - """Add num_ctrl_qubits controls to operation + Open controls are implemented by conjugating the control line with + X gates. Adds num_ctrl_qubits controls to operation. Args: - operation (Gate or ControlledGate): operation to add control to. - num_ctrl_qubits (int): number of controls to add to gate (default=1) - label (str): optional gate label + operation: Operation for which control will be added. + num_ctrl_qubits: The number of controls to add to gate (default=1). + label: Optional gate label. + ctrl_state (int or str or None): The control state in decimal or as + a bitstring (e.g. '111'). If specified as a bitstring the length + must equal num_ctrl_qubits, MSB on left. If None, use + 2**num_ctrl_qubits-1. Returns: - ControlledGate: controlled version of gate. This default algorithm - uses num_ctrl_qubits-1 ancillae qubits so returns a gate of size - num_qubits + 2*num_ctrl_qubits - 1. + Controlled version of gate. + """ import qiskit.extensions.standard as standard if isinstance(operation, standard.RZGate) or operation.name == 'rz': @@ -37,34 +54,39 @@ def add_control(operation, num_ctrl_qubits, label): # the condition matching 'name' above is to catch a test case, # 'TestControlledGate.test_rotation_gates', where the rz gate # gets converted to a circuit before becoming a generic Gate object. - cgate = standard.CrzGate(*operation.params) + cgate = standard.CRZGate(*operation.params) return cgate.control(num_ctrl_qubits - 1) if isinstance(operation, UnitaryGate): # attempt decomposition operation._define() - return control(operation, num_ctrl_qubits=num_ctrl_qubits, label=label) + return control(operation, num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) -def control(operation, num_ctrl_qubits=1, label=None): +def control(operation: Union[Gate, ControlledGate], + num_ctrl_qubits: Optional[int] = 1, + label: Optional[Union[None, str]] = None, + ctrl_state: Optional[Union[None, int, str]] = None) -> ControlledGate: """Return controlled version of gate using controlled rotations Args: - operation (Gate or Controlledgate): gate to create ControlledGate from - num_ctrl_qubits (int): number of controls to add to gate (default=1) - label (str): optional gate label + operation: gate to create ControlledGate from + num_ctrl_qubits: number of controls to add to gate (default=1) + label: optional gate label + ctrl_state: The control state in decimal or as + a bitstring (e.g. '111'). If specified as a bitstring the length + must equal num_ctrl_qubits, MSB on left. If None, use + 2**num_ctrl_qubits-1. + Returns: - ControlledGate: controlled version of gate. This default algorithm - uses num_ctrl_qubits-1 ancillae qubits so returns a gate of size - num_qubits + 2*num_ctrl_qubits - 1. + Controlled version of gate. Raises: - QiskitError: gate contains non-gate in definitionl + CircuitError: gate contains non-gate in definition """ from math import pi # pylint: disable=cyclic-import import qiskit.circuit.controlledgate as controlledgate - from qiskit.circuit.quantumregister import QuantumRegister - from qiskit.circuit.quantumcircuit import QuantumCircuit # pylint: disable=unused-import import qiskit.extensions.standard.multi_control_rotation_gates import qiskit.extensions.standard.multi_control_toffoli_gate @@ -73,7 +95,6 @@ def control(operation, num_ctrl_qubits=1, label=None): q_control = QuantumRegister(num_ctrl_qubits, name='control') q_target = QuantumRegister(operation.num_qubits, name='target') q_ancillae = None # TODO: add - qc = QuantumCircuit(q_control, q_target) if operation.name == 'x' or ( @@ -98,7 +119,7 @@ def control(operation, num_ctrl_qubits=1, label=None): for rule in bgate.definition: if rule[0].name == 'u3': theta, phi, lamb = rule[0].params - if phi == -pi/2 and lamb == pi/2: + if phi == -pi / 2 and lamb == pi / 2: qc.mcrx(theta, q_control, q_target[rule[1][0].index], use_basis_gates=True) elif phi == 0 and lamb == 0: @@ -122,7 +143,7 @@ def control(operation, num_ctrl_qubits=1, label=None): None, mode='noancilla') else: - raise QiskitError('gate contains non-controllable instructions') + raise CircuitError('gate contains non-controllable instructions') instr = qc.to_instruction() if isinstance(operation, controlledgate.ControlledGate): new_num_ctrl_qubits = num_ctrl_qubits + operation.num_ctrl_qubits @@ -146,20 +167,19 @@ def control(operation, num_ctrl_qubits=1, label=None): operation.params, label=label, num_ctrl_qubits=new_num_ctrl_qubits, - definition=instr.definition) + definition=instr.definition, + ctrl_state=ctrl_state) cgate.base_gate = base_gate return cgate def _gate_to_circuit(operation): - from qiskit.circuit.quantumcircuit import QuantumCircuit - from qiskit.circuit.quantumregister import QuantumRegister qr = QuantumRegister(operation.num_qubits) qc = QuantumCircuit(qr, name=operation.name) if hasattr(operation, 'definition') and operation.definition: for rule in operation.definition: if rule[0].name in {'id', 'barrier', 'measure', 'snapshot'}: - raise QiskitError('Cannot make controlled gate with {} instruction'.format( + raise CircuitError('Cannot make controlled gate with {} instruction'.format( rule[0].name)) qc.append(rule[0], qargs=[qr[bit.index] for bit in rule[1]], cargs=[]) else: diff --git a/qiskit/circuit/controlledgate.py b/qiskit/circuit/controlledgate.py index 5d67261d1b6d..6f97eef1870b 100644 --- a/qiskit/circuit/controlledgate.py +++ b/qiskit/circuit/controlledgate.py @@ -15,17 +15,21 @@ """ Controlled unitary gate. """ - from qiskit.circuit.exceptions import CircuitError from .gate import Gate +from . import QuantumRegister class ControlledGate(Gate): """Controlled unitary gate.""" def __init__(self, name, num_qubits, params, label=None, num_ctrl_qubits=1, - definition=None): - """Create a new gate. + definition=None, ctrl_state=None): + """Create a controlled gate. + + Attributes: + num_ctrl_qubits (int): The number of control qubits. + ctrl_state (int): The control state in decimal notation. Args: name (str): The Qobj name of the gate. @@ -34,8 +38,13 @@ def __init__(self, name, num_qubits, params, label=None, num_ctrl_qubits=1, label (str or None): An optional label for the gate [Default: None] num_ctrl_qubits (int): Number of control qubits. definition (list): list of gate rules for implementing this gate. + ctrl_state (int or str or None): The control state in decimal or as + a bitstring (e.g. '111'). If specified as a bitstring the length + must equal num_ctrl_qubits, MSB on left. If None, use + 2**num_ctrl_qubits-1. Raises: CircuitError: num_ctrl_qubits >= num_qubits + CircuitError: ctrl_state < 0 or ctrl_state > 2**num_ctrl_qubits. """ super().__init__(name, num_qubits, params, label=label) if num_ctrl_qubits < num_qubits: @@ -50,14 +59,76 @@ def __init__(self, name, num_qubits, params, label=None, num_ctrl_qubits=1, self.base_gate = base_gate.base_gate else: self.base_gate = base_gate + self._ctrl_state = None + self.ctrl_state = ctrl_state + + @property + def definition(self): + """Return definition in terms of other basic gates. If the gate has + open controls, as determined from `self.ctrl_state`, the returned + definition is conjugated with X.""" + if not self._definition: + self._define() + # pylint: disable=cyclic-import + from qiskit.extensions.standard import XGate, CnotGate + bit_ctrl_state = bin(self.ctrl_state)[2:].zfill(self.num_ctrl_qubits) + # hacky way to get register assuming single register + if self._definition: + qreg = self._definition[0][1][0].register + elif isinstance(self, CnotGate): + qreg = QuantumRegister(self.num_qubits, 'q') + self._definition = [(self, [qreg[0], qreg[1]], [])] + open_rules = [] + for qind, val in enumerate(bit_ctrl_state[::-1]): + if val == '0': + open_rules.append([XGate(), [qreg[qind]], []]) + return open_rules + self._definition + open_rules + + @definition.setter + def definition(self, excited_def): + """Set controlled gate definition with closed controls.""" + super(Gate, self.__class__).definition.fset(self, excited_def) + + @property + def ctrl_state(self): + """Return the control state of the gate as a decimal integer.""" + return self._ctrl_state + + @ctrl_state.setter + def ctrl_state(self, ctrl_state): + """Set the control state of this gate. + + Args: + ctrl_state (int or str or None): The control state of the gate. + + Raises: + CircuitError: ctrl_state is invalid. + """ + if isinstance(ctrl_state, str): + try: + assert len(ctrl_state) == self.num_ctrl_qubits + ctrl_state = int(ctrl_state, 2) + except ValueError: + raise CircuitError('invalid control bit string: ' + ctrl_state) + except AssertionError: + raise CircuitError('invalid control bit string: length != ' + 'num_ctrl_qubits') + if isinstance(ctrl_state, int): + if 0 <= ctrl_state < 2**self.num_ctrl_qubits: + self._ctrl_state = ctrl_state + else: + raise CircuitError('invalid control state specification') + elif ctrl_state is None: + self._ctrl_state = 2**self.num_ctrl_qubits - 1 + else: + raise CircuitError('invalid control state specification') def __eq__(self, other): if not isinstance(other, ControlledGate): return False else: return (other.num_ctrl_qubits == self.num_ctrl_qubits and - self.base_gate == other.base_gate and - super().__eq__(other)) + self.base_gate == other.base_gate) def inverse(self): """Invert this gate by calling inverse on the base gate.""" diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index a8095fded48c..618435474f23 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -106,24 +106,24 @@ def label(self, name): else: raise TypeError('label expects a string or None') - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Return controlled version of gate Args: num_ctrl_qubits (int): number of controls to add to gate (default=1) - label (str): optional gate label + label (str or None): optional gate label + ctrl_state (int or str or None): The control state in decimal or as + a bitstring (e.g. '111'). If None, use 2**num_ctrl_qubits-1. Returns: - ControlledGate: controlled version of gate. This default algorithm - uses num_ctrl_qubits-1 ancillae qubits so returns a gate of size - num_qubits + 2*num_ctrl_qubits - 1. + ControlledGate: controlled version of gate. Raises: - QiskitError: unrecognized mode + QiskitError: unrecognized mode or invalid ctrl_state """ # pylint: disable=cyclic-import from .add_control import add_control - return add_control(self, num_ctrl_qubits, label) + return add_control(self, num_ctrl_qubits, label, ctrl_state) @staticmethod def _broadcast_single_argument(qarg): diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py new file mode 100644 index 000000000000..701956717ebb --- /dev/null +++ b/qiskit/circuit/library/__init__.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Module for builtin library of circuits.""" + +from .boolean_logic import Permutation, XOR, InnerProduct diff --git a/qiskit/circuit/library/boolean_logic.py b/qiskit/circuit/library/boolean_logic.py new file mode 100644 index 000000000000..d6377d438304 --- /dev/null +++ b/qiskit/circuit/library/boolean_logic.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +# pylint: disable=no-member + +"""Implementations of boolean logic quantum circuits.""" + +from typing import List, Optional + +import numpy as np +from qiskit.circuit import QuantumRegister, QuantumCircuit +from qiskit.circuit.exceptions import CircuitError + + +class Permutation(QuantumCircuit): + """An n_qubit circuit that permutes qubits.""" + + def __init__(self, + n_qubits: int, + pattern: Optional[List[int]] = None, + seed: Optional[int] = None) -> QuantumCircuit: + """Return an n_qubit permutation circuit implemented using SWAPs. + + Args: + n_qubits: circuit width. + pattern: permutation pattern. If None, permute randomly. + seed: random seed in case a random permutation is requested. + + Returns: + A permutation circuit. + + Raises: + CircuitError: if permutation pattern is malformed. + """ + super().__init__(n_qubits, name="permutation") + + if pattern is not None: + if sorted(pattern) != list(range(n_qubits)): + raise CircuitError("Permutation pattern must be some " + "ordering of 0..n_qubits-1 in a list.") + pattern = np.array(pattern) + else: + rng = np.random.RandomState(seed) + pattern = np.arange(n_qubits) + rng.shuffle(pattern) + + for i in range(n_qubits): + if (pattern[i] != -1) and (pattern[i] != i): + self.swap(i, int(pattern[i])) + pattern[pattern[i]] = -1 + + +class XOR(QuantumCircuit): + """An n_qubit circuit for bitwise xor-ing the input with some integer ``amount``. + + The ``amount`` is xor-ed in bitstring form with the input. + + This circuit can also represent addition by ``amount`` over the finite field GF(2). + """ + + def __init__(self, + n_qubits: int, + amount: Optional[int] = None, + seed: Optional[int] = None) -> QuantumCircuit: + """Return a circuit implementing bitwise xor. + + Args: + n_qubits: the width of circuit. + amount: the xor amount in decimal form. + seed: random seed in case a random xor is requested. + + Returns: + A circuit for bitwise XOR. + + Raises: + CircuitError: if the xor bitstring exceeds available qubits. + """ + super().__init__(n_qubits, name="xor") + + if amount is not None: + if len(bin(amount)[2:]) > n_qubits: + raise CircuitError("Bits in 'amount' exceed circuit width") + else: + rng = np.random.RandomState(seed) + amount = rng.randint(0, 2**n_qubits) + + for i in range(n_qubits): + bit = amount & 1 + amount = amount >> 1 + if bit == 1: + self.x(i) + + +class InnerProduct(QuantumCircuit): + """An n_qubit circuit that computes the inner product of two registers.""" + + def __init__(self, n_qubits: int) -> QuantumCircuit: + """Return a circuit to compute the inner product of 2 n-qubit registers. + + This implementation uses CZ gates. + + Args: + n_qubits: width of top and bottom registers (half total circuit width) + + Returns: + A circuit computing inner product of two registers. + """ + qr_a = QuantumRegister(n_qubits) + qr_b = QuantumRegister(n_qubits) + super().__init__(qr_a, qr_b, name="inner_product") + + for i in range(n_qubits): + self.cz(qr_a[i], qr_b[i]) diff --git a/qiskit/circuit/random/utils.py b/qiskit/circuit/random/utils.py index 71cad691a88b..e969c0a9a1f3 100644 --- a/qiskit/circuit/random/utils.py +++ b/qiskit/circuit/random/utils.py @@ -18,12 +18,12 @@ from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.circuit import Reset -from qiskit.extensions import (IdGate, U1Gate, U2Gate, U3Gate, XGate, +from qiskit.extensions import (IGate, U1Gate, U2Gate, U3Gate, XGate, YGate, ZGate, HGate, SGate, SdgGate, TGate, - TdgGate, RXGate, RYGate, RZGate, CnotGate, - CyGate, CzGate, CHGate, CrzGate, Cu1Gate, - Cu3Gate, SwapGate, RZZGate, - ToffoliGate, FredkinGate) + TdgGate, RXGate, RYGate, RZGate, CXGate, + CYGate, CZGate, CHGate, CRZGate, CU1Gate, + CU3Gate, SwapGate, RZZGate, + CCXGate, CSwapGate) from qiskit.circuit.exceptions import CircuitError @@ -31,6 +31,16 @@ def random_circuit(n_qubits, depth, max_operands=3, measure=False, conditional=False, reset=False, seed=None): """Generate random circuit of arbitrary size and form. + This function will generate a random circuit by randomly selecting gates + from the set of standard gates in :mod:`qiskit.extensions`. For example: + + .. jupyter-execute:: + + from qiskit.circuit.random import random_circuit + + circ = random_circuit(2, 2, measure=True) + circ.draw(output='mpl') + Args: n_qubits (int): number of quantum wires depth (int): layers of operations (i.e. critical path length) @@ -49,14 +59,14 @@ def random_circuit(n_qubits, depth, max_operands=3, measure=False, if max_operands < 1 or max_operands > 3: raise CircuitError("max_operands must be between 1 and 3") - one_q_ops = [IdGate, U1Gate, U2Gate, U3Gate, XGate, YGate, ZGate, + one_q_ops = [IGate, U1Gate, U2Gate, U3Gate, XGate, YGate, ZGate, HGate, SGate, SdgGate, TGate, TdgGate, RXGate, RYGate, RZGate] - one_param = [U1Gate, RXGate, RYGate, RZGate, RZZGate, Cu1Gate, CrzGate] + one_param = [U1Gate, RXGate, RYGate, RZGate, RZZGate, CU1Gate, CRZGate] two_param = [U2Gate] - three_param = [U3Gate, Cu3Gate] - two_q_ops = [CnotGate, CyGate, CzGate, CHGate, CrzGate, - Cu1Gate, Cu3Gate, SwapGate, RZZGate] - three_q_ops = [ToffoliGate, FredkinGate] + three_param = [U3Gate, CU3Gate] + two_q_ops = [CXGate, CYGate, CZGate, CHGate, CRZGate, + CU1Gate, CU3Gate, SwapGate, RZZGate] + three_q_ops = [CCXGate, CSwapGate] qr = QuantumRegister(n_qubits, 'q') qc = QuantumCircuit(n_qubits) @@ -96,7 +106,7 @@ def random_circuit(n_qubits, depth, max_operands=3, measure=False, num_angles = 3 else: num_angles = 0 - angles = [rng.uniform(0, 2*np.pi) for x in range(num_angles)] + angles = [rng.uniform(0, 2 * np.pi) for x in range(num_angles)] register_operands = [qr[i] for i in operands] op = operation(*angles) diff --git a/qiskit/converters/ast_to_dag.py b/qiskit/converters/ast_to_dag.py index 3e07aec68f7c..a8dc17ee96fd 100644 --- a/qiskit/converters/ast_to_dag.py +++ b/qiskit/converters/ast_to_dag.py @@ -25,14 +25,14 @@ from qiskit.circuit.measure import Measure from qiskit.circuit.reset import Reset from qiskit.extensions.standard.barrier import Barrier -from qiskit.extensions.standard.x import ToffoliGate -from qiskit.extensions.standard.swap import FredkinGate -from qiskit.extensions.standard.x import CnotGate -from qiskit.extensions.standard.y import CyGate -from qiskit.extensions.standard.z import CzGate +from qiskit.extensions.standard.x import CCXGate +from qiskit.extensions.standard.swap import CSwapGate +from qiskit.extensions.standard.x import CXGate +from qiskit.extensions.standard.y import CYGate +from qiskit.extensions.standard.z import CZGate from qiskit.extensions.standard.swap import SwapGate from qiskit.extensions.standard.h import HGate -from qiskit.extensions.standard.iden import IdGate +from qiskit.extensions.standard.i import IGate from qiskit.extensions.standard.s import SGate from qiskit.extensions.standard.s import SdgGate from qiskit.extensions.standard.t import TGate @@ -46,14 +46,14 @@ from qiskit.extensions.standard.rx import RXGate from qiskit.extensions.standard.ry import RYGate from qiskit.extensions.standard.rz import RZGate -from qiskit.extensions.standard.u1 import Cu1Gate -from qiskit.extensions.standard.h import CHGate -from qiskit.extensions.standard.rx import CrxGate -from qiskit.extensions.standard.ry import CryGate -from qiskit.extensions.standard.rz import CrzGate -from qiskit.extensions.standard.u3 import Cu3Gate from qiskit.extensions.standard.rxx import RXXGate from qiskit.extensions.standard.rzz import RZZGate +from qiskit.extensions.standard.u1 import CU1Gate +from qiskit.extensions.standard.u3 import CU3Gate +from qiskit.extensions.standard.h import CHGate +from qiskit.extensions.standard.rx import CRXGate +from qiskit.extensions.standard.ry import CRYGate +from qiskit.extensions.standard.rz import CRZGate def ast_to_dag(ast): @@ -113,19 +113,19 @@ class AstInterpreter: "ry": RYGate, "rz": RZGate, "rzz": RZZGate, - "id": IdGate, + "id": IGate, "h": HGate, - "cx": CnotGate, - "cy": CyGate, - "cz": CzGate, + "cx": CXGate, + "cy": CYGate, + "cz": CZGate, "ch": CHGate, - "crx": CrxGate, - "cry": CryGate, - "crz": CrzGate, - "cu1": Cu1Gate, - "cu3": Cu3Gate, - "ccx": ToffoliGate, - "cswap": FredkinGate} + "crx": CRXGate, + "cry": CRYGate, + "crz": CRZGate, + "cu1": CU1Gate, + "cu3": CU3Gate, + "ccx": CCXGate, + "cswap": CSwapGate} def __init__(self, dag): """Initialize interpreter's data.""" @@ -239,11 +239,11 @@ def _process_cnot(self, node): maxidx = max([len(id0), len(id1)]) for idx in range(maxidx): if len(id0) > 1 and len(id1) > 1: - self.dag.apply_operation_back(CnotGate(), [id0[idx], id1[idx]], [], self.condition) + self.dag.apply_operation_back(CXGate(), [id0[idx], id1[idx]], [], self.condition) elif len(id0) > 1: - self.dag.apply_operation_back(CnotGate(), [id0[idx], id1[0]], [], self.condition) + self.dag.apply_operation_back(CXGate(), [id0[idx], id1[0]], [], self.condition) else: - self.dag.apply_operation_back(CnotGate(), [id0[0], id1[idx]], [], self.condition) + self.dag.apply_operation_back(CXGate(), [id0[0], id1[idx]], [], self.condition) def _process_measure(self, node): """Process a measurement node.""" diff --git a/qiskit/converters/dag_to_circuit.py b/qiskit/converters/dag_to_circuit.py index 7bd1e161d018..f6cf4e90460c 100644 --- a/qiskit/converters/dag_to_circuit.py +++ b/qiskit/converters/dag_to_circuit.py @@ -31,7 +31,7 @@ def dag_to_circuit(dag): from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.dagcircuit import DAGCircuit from qiskit.converters import circuit_to_dag - from qiskit.extensions.standard import CHGate, U2Gate, CnotGate + from qiskit.extensions.standard import CHGate, U2Gate, CXGate from qiskit.converters import dag_to_circuit %matplotlib inline diff --git a/qiskit/extensions/__init__.py b/qiskit/extensions/__init__.py index 1eedc7b96840..e77596231d0b 100644 --- a/qiskit/extensions/__init__.py +++ b/qiskit/extensions/__init__.py @@ -26,19 +26,19 @@ :toctree: ../stubs/ Barrier - ToffoliGate + CCXGate CHGate - CrxGate - CryGate - CrzGate - FredkinGate - Cu1Gate - Cu3Gate - CnotGate - CyGate - CzGate + CRXGate + CRYGate + CRZGate + CSwapGate + CU1Gate + CU3Gate + CXGate + CYGate + CZGate HGate - IdGate + IGate MSGate RXGate RXXGate diff --git a/qiskit/extensions/quantum_initializer/__init__.py b/qiskit/extensions/quantum_initializer/__init__.py index 5aab84a27772..8b1b3f2b71d6 100644 --- a/qiskit/extensions/quantum_initializer/__init__.py +++ b/qiskit/extensions/quantum_initializer/__init__.py @@ -15,9 +15,9 @@ """Initialize qubit registers to desired arbitrary state.""" from .squ import SingleQubitUnitary -from .ucz import UCZ -from .ucy import UCY -from .ucx import UCX -from .diag import DiagGate -from .ucg import UCG +from .ucrz import UCRZGate +from .ucry import UCRYGate +from .ucrx import UCRXGate +from .diagonal import DiagonalGate +from .uc import UCGate from .isometry import Isometry diff --git a/qiskit/extensions/quantum_initializer/diag.py b/qiskit/extensions/quantum_initializer/diag.py index 296349974673..f8cae74dad7f 100644 --- a/qiskit/extensions/quantum_initializer/diag.py +++ b/qiskit/extensions/quantum_initializer/diag.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2019. +# (C) Copyright IBM 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -12,132 +12,14 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -# The structure of the code is based on Emanuel Malvetti's semester thesis at ETH in 2018, -# which was supervised by Raban Iten and Prof. Renato Renner. +"""The diagonal gate. -# pylint: disable=missing-param-doc -# pylint: disable=missing-type-doc - -""" -Decomposes a diagonal matrix into elementary gates using the method described in Theorem 7 in -"Synthesis of Quantum Logic Circuits" by Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf). +This module is deprecated, see diagonal.py """ -import cmath -import math - -import numpy as np - -from qiskit.circuit import Gate -from qiskit.circuit.quantumcircuit import QuantumCircuit, QuantumRegister -from qiskit.exceptions import QiskitError - -_EPS = 1e-10 # global variable used to chop very small numbers to zero - - -class DiagGate(Gate): - """ - diag = list of the 2^k diagonal entries (for a diagonal gate on k qubits). Must contain at - least two entries. - """ - - def __init__(self, diag): - """Check types""" - # Check if diag has type "list" - if not isinstance(diag, list): - raise QiskitError("The diagonal entries are not provided in a list.") - # Check if the right number of diagonal entries is provided and if the diagonal entries - # have absolute value one. - num_action_qubits = math.log2(len(diag)) - if num_action_qubits < 1 or not num_action_qubits.is_integer(): - raise QiskitError("The number of diagonal entries is not a positive power of 2.") - for z in diag: - try: - complex(z) - except TypeError: - raise QiskitError("Not all of the diagonal entries can be converted to " - "complex numbers.") - if not np.abs(z) - 1 < _EPS: - raise QiskitError("A diagonal entry has not absolute value one.") - # Create new gate. - super().__init__("diag", int(num_action_qubits), diag) - - def _define(self): - diag_circuit = self._dec_diag() - gate = diag_circuit.to_instruction() - q = QuantumRegister(self.num_qubits) - diag_circuit = QuantumCircuit(q) - diag_circuit.append(gate, q[:]) - self.definition = diag_circuit.data - - def _dec_diag(self): - """ - Call to create a circuit implementing the diagonal gate. - """ - q = QuantumRegister(self.num_qubits) - circuit = QuantumCircuit(q) - # Since the diagonal is a unitary, all its entries have absolute value one and the diagonal - # is fully specified by the phases of its entries - diag_phases = [cmath.phase(z) for z in self.params] - n = len(self.params) - while n >= 2: - angles_rz = [] - for i in range(0, n, 2): - diag_phases[i // 2], rz_angle = _extract_rz(diag_phases[i], diag_phases[i + 1]) - angles_rz.append(rz_angle) - num_act_qubits = int(np.log2(n)) - contr_qubits = q[self.num_qubits - num_act_qubits + 1:self.num_qubits] - target_qubit = q[self.num_qubits - num_act_qubits] - circuit.ucz(angles_rz, contr_qubits, target_qubit) - n //= 2 - return circuit - - -# extract a Rz rotation (angle given by first output) such that exp(j*phase)*Rz(z_angle) -# is equal to the diagonal matrix with entires exp(1j*ph1) and exp(1j*ph2) -def _extract_rz(phi1, phi2): - phase = (phi1 + phi2) / 2.0 - z_angle = phi2 - phi1 - return phase, z_angle - - -def diag_gate(self, diag, qubit): - """Attach a diagonal gate to a circuit. - - The decomposition is based on Theorem 7 given in "Synthesis of Quantum Logic Circuits" by - Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf). - - Args: - diag (list): list of the 2^k diagonal entries (for a diagonal gate on k qubits). - Must contain at least two entries - qubit (QuantumRegister|list): list of k qubits the diagonal is - acting on (the order of the qubits specifies the computational basis in which the - diagonal gate is provided: the first element in diag acts on the state where all - the qubits in q are in the state 0, the second entry acts on the state where all - the qubits q[1],...,q[k-1] are in the state zero and q[0] is in the state 1, - and so on) - - Returns: - QuantumCircuit: the diagonal gate which was attached to the circuit. - - Raises: - QiskitError: if the list of the diagonal entries or the qubit list is in bad format; - if the number of diagonal entries is not 2^k, where k denotes the number of qubits - """ - - if isinstance(qubit, QuantumRegister): - qubit = qubit[:] - # Check if q has type "list" - if not isinstance(qubit, list): - raise QiskitError("The qubits must be provided as a list " - "(also if there is only one qubit).") - # Check if diag has type "list" - if not isinstance(diag, list): - raise QiskitError("The diagonal entries are not provided in a list.") - num_action_qubits = math.log2(len(diag)) - if not len(qubit) == num_action_qubits: - raise QiskitError("The number of diagonal entries does not correspond to" - " the number of qubits.") - return self.append(DiagGate(diag), qubit) +import warnings +# pylint: disable=unused-import +from qiskit.extensions.quantum_initializer.diagonal import DiagGate, diag_gate -QuantumCircuit.diag_gate = diag_gate +warnings.warn('This module is deprecated. The DiagonalGate/DiagGate is now in diagonal.py', + category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/quantum_initializer/diagonal.py b/qiskit/extensions/quantum_initializer/diagonal.py new file mode 100644 index 000000000000..1083ffd36946 --- /dev/null +++ b/qiskit/extensions/quantum_initializer/diagonal.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +# The structure of the code is based on Emanuel Malvetti's semester thesis at ETH in 2018, +# which was supervised by Raban Iten and Prof. Renato Renner. + +# pylint: disable=missing-param-doc +# pylint: disable=missing-type-doc + +""" +Decomposes a diagonal matrix into elementary gates using the method described in Theorem 7 in +"Synthesis of Quantum Logic Circuits" by Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf). +""" +import cmath +import math + +import numpy as np + +from qiskit.circuit import Gate +from qiskit.circuit.quantumcircuit import QuantumCircuit, QuantumRegister +from qiskit.exceptions import QiskitError + +_EPS = 1e-10 # global variable used to chop very small numbers to zero + + +class DiagonalMeta(type): + """A metaclass to ensure that DiagonalGate and DiagGate are of the same type. + + Can be removed when DiagGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {DiagonalGate, DiagGate} # pylint: disable=unidiomatic-typecheck + + +class DiagonalGate(Gate, metaclass=DiagonalMeta): + """ + diag = list of the 2^k diagonal entries (for a diagonal gate on k qubits). Must contain at + least two entries. + """ + + def __init__(self, diag): + """Check types""" + # Check if diag has type "list" + if not isinstance(diag, list): + raise QiskitError("The diagonal entries are not provided in a list.") + # Check if the right number of diagonal entries is provided and if the diagonal entries + # have absolute value one. + num_action_qubits = math.log2(len(diag)) + if num_action_qubits < 1 or not num_action_qubits.is_integer(): + raise QiskitError("The number of diagonal entries is not a positive power of 2.") + for z in diag: + try: + complex(z) + except TypeError: + raise QiskitError("Not all of the diagonal entries can be converted to " + "complex numbers.") + if not np.abs(z) - 1 < _EPS: + raise QiskitError("A diagonal entry has not absolute value one.") + # Create new gate. + super().__init__("diagonal", int(num_action_qubits), diag) + + def _define(self): + diag_circuit = self._dec_diag() + gate = diag_circuit.to_instruction() + q = QuantumRegister(self.num_qubits) + diag_circuit = QuantumCircuit(q) + diag_circuit.append(gate, q[:]) + self.definition = diag_circuit.data + + def _dec_diag(self): + """ + Call to create a circuit implementing the diagonal gate. + """ + q = QuantumRegister(self.num_qubits) + circuit = QuantumCircuit(q) + # Since the diagonal is a unitary, all its entries have absolute value one and the diagonal + # is fully specified by the phases of its entries + diag_phases = [cmath.phase(z) for z in self.params] + n = len(self.params) + while n >= 2: + angles_rz = [] + for i in range(0, n, 2): + diag_phases[i // 2], rz_angle = _extract_rz(diag_phases[i], diag_phases[i + 1]) + angles_rz.append(rz_angle) + num_act_qubits = int(np.log2(n)) + contr_qubits = q[self.num_qubits - num_act_qubits + 1:self.num_qubits] + target_qubit = q[self.num_qubits - num_act_qubits] + circuit.ucrz(angles_rz, contr_qubits, target_qubit) + n //= 2 + return circuit + + +# extract a Rz rotation (angle given by first output) such that exp(j*phase)*Rz(z_angle) +# is equal to the diagonal matrix with entires exp(1j*ph1) and exp(1j*ph2) +def _extract_rz(phi1, phi2): + phase = (phi1 + phi2) / 2.0 + z_angle = phi2 - phi1 + return phase, z_angle + + +def diagonal(self, diag, qubit): + """Attach a diagonal gate to a circuit. + + The decomposition is based on Theorem 7 given in "Synthesis of Quantum Logic Circuits" by + Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf). + + Args: + diag (list): list of the 2^k diagonal entries (for a diagonal gate on k qubits). + Must contain at least two entries + qubit (QuantumRegister|list): list of k qubits the diagonal is + acting on (the order of the qubits specifies the computational basis in which the + diagonal gate is provided: the first element in diag acts on the state where all + the qubits in q are in the state 0, the second entry acts on the state where all + the qubits q[1],...,q[k-1] are in the state zero and q[0] is in the state 1, + and so on) + + Returns: + QuantumCircuit: the diagonal gate which was attached to the circuit. + + Raises: + QiskitError: if the list of the diagonal entries or the qubit list is in bad format; + if the number of diagonal entries is not 2^k, where k denotes the number of qubits + """ + + if isinstance(qubit, QuantumRegister): + qubit = qubit[:] + # Check if q has type "list" + if not isinstance(qubit, list): + raise QiskitError("The qubits must be provided as a list " + "(also if there is only one qubit).") + # Check if diag has type "list" + if not isinstance(diag, list): + raise QiskitError("The diagonal entries are not provided in a list.") + num_action_qubits = math.log2(len(diag)) + if not len(qubit) == num_action_qubits: + raise QiskitError("The number of diagonal entries does not correspond to" + " the number of qubits.") + return self.append(DiagonalGate(diag), qubit) + + +class DiagGate(DiagonalGate, metaclass=DiagonalMeta): + """The deprecated DiagonalGate class.""" + + def __init__(self, diag): + import warnings + warnings.warn('The class DiagGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class DiagonalGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(diag) + + +def diag_gate(self, diag, qubit): + """Deprecated version of QuantumCircuit.diagonal.""" + import warnings + warnings.warn('The QuantumCircuit.diag_gate() method is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the QuantumCircuit.diagonal() method instead.', + DeprecationWarning, stacklevel=2) + return diagonal(self, diag, qubit) + + +QuantumCircuit.diagonal = diagonal +QuantumCircuit.diag_gate = diag_gate # deprecated diff --git a/qiskit/extensions/quantum_initializer/initializer.py b/qiskit/extensions/quantum_initializer/initializer.py index b728ecd50ce9..6047121457bf 100644 --- a/qiskit/extensions/quantum_initializer/initializer.py +++ b/qiskit/extensions/quantum_initializer/initializer.py @@ -23,7 +23,7 @@ from qiskit.circuit import QuantumCircuit from qiskit.circuit import QuantumRegister from qiskit.circuit import Instruction -from qiskit.extensions.standard.x import CnotGate +from qiskit.extensions.standard.x import CXGate from qiskit.extensions.standard.ry import RYGate from qiskit.extensions.standard.rz import RZGate from qiskit.circuit.reset import Reset @@ -37,6 +37,8 @@ class Initialize(Instruction): Class that implements the (complex amplitude) initialization of some flexible collection of qubit registers (assuming the qubits are in the zero state). + Note that Initialize is an Instruction and not a Gate since it contains a reset instruction, + which is not unitary. """ def __init__(self, params): @@ -221,7 +223,7 @@ def _multiplex(self, target_gate, list_of_angles): circuit.append(multiplex_1.to_instruction(), q[0:-1]) # attach CNOT as follows, thereby flipping the LSB qubit - circuit.append(CnotGate(), [msb, lsb]) + circuit.append(CXGate(), [msb, lsb]) # implement extra efficiency from the paper of cancelling adjacent # CNOTs (by leaving out last CNOT and reversing (NOT inverting) the @@ -233,7 +235,7 @@ def _multiplex(self, target_gate, list_of_angles): circuit.append(multiplex_2.to_instruction(), q[0:-1]) # attach a final CNOT - circuit.append(CnotGate(), [msb, lsb]) + circuit.append(CXGate(), [msb, lsb]) return circuit diff --git a/qiskit/extensions/quantum_initializer/isometry.py b/qiskit/extensions/quantum_initializer/isometry.py index 89336dbf1629..08052daa9eac 100644 --- a/qiskit/extensions/quantum_initializer/isometry.py +++ b/qiskit/extensions/quantum_initializer/isometry.py @@ -30,7 +30,7 @@ from qiskit.circuit import QuantumCircuit from qiskit.exceptions import QiskitError from qiskit.quantum_info.operators.predicates import is_isometry -from qiskit.extensions.quantum_initializer.ucg import UCG +from qiskit.extensions.quantum_initializer.uc import UCGate from qiskit.extensions.quantum_initializer.mcg_up_to_diagonal import MCGupDiag _EPS = 1e-10 # global variable used to chop very small numbers to zero @@ -93,7 +93,7 @@ def __init__(self, isometry, num_ancillas_zero, num_ancillas_dirty): num_qubits = int(n) + num_ancillas_zero + num_ancillas_dirty - super().__init__("iso", num_qubits, 0, [isometry]) + super().__init__("isometry", num_qubits, 0, [isometry]) def _define(self): # call to generate the circuit that takes the isometry to the first 2^m columns @@ -109,7 +109,7 @@ def _define(self): if num_gates == 0: # ToDo: improve handling of empty circuit, such that the following line # ToDo: is not required. - iso_circuit.iden(q[0]) + iso_circuit.i(q[0]) else: iso_circuit.append(gate, q[:]) self.definition = iso_circuit.data @@ -139,7 +139,7 @@ def _gates_to_uncompute(self): # remove first column (which is now stored in diag) remaining_isometry = remaining_isometry[:, 1:] if len(diag) > 1 and not _diag_is_identity_up_to_global_phase(diag): - circuit.diag_gate(np.conj(diag).tolist(), q_input) + circuit.diagonal(np.conj(diag).tolist(), q_input) return circuit def _decompose_column(self, circuit, q, diag, remaining_isometry, column_index): @@ -163,7 +163,7 @@ def _disentangle(self, circuit, q, diag, remaining_isometry, column_index, s): v = remaining_isometry n = int(np.log2(self.params[0].shape[0])) - # MCG to set one entry to zero (preparation for disentangling with UCG): + # MCG to set one entry to zero (preparation for disentangling with UCGate): index1 = 2 * _a(k, s + 1) * 2 ** s + _b(k, s + 1) index2 = (2 * _a(k, s + 1) + 1) * 2 ** s + _b(k, s + 1) target_label = n - s - 1 @@ -183,17 +183,17 @@ def _disentangle(self, circuit, q, diag, remaining_isometry, column_index, s): # update the diag according to the applied diagonal gate _apply_diagonal_gate_to_diag(diag, control_labels + [target_label], diag_mcg_inverse, n) - # UCG to disentangle a qubit: - # Find the UCG, decompose it and apply it to the remaining isometry + # UCGate to disentangle a qubit: + # Find the UCGate, decompose it and apply it to the remaining isometry single_qubit_gates = self._find_squs_for_disentangling(v, k, s) if not _ucg_is_identity_up_to_global_phase(single_qubit_gates): control_labels = list(range(target_label)) diagonal_ucg = self._append_ucg_up_to_diagonal(circuit, q, single_qubit_gates, control_labels, target_label) - # merge the diagonal into the UCG for efficient application of both together + # merge the diagonal into the UCGate for efficient application of both together diagonal_ucg_inverse = np.conj(diagonal_ucg).tolist() - single_qubit_gates = _merge_UCG_and_diag(single_qubit_gates, diagonal_ucg_inverse) - # apply the UCG (with the merged diagonal gate) to the remaining isometry + single_qubit_gates = _merge_UCGate_and_diag(single_qubit_gates, diagonal_ucg_inverse) + # apply the UCGate (with the merged diagonal gate) to the remaining isometry _apply_ucg(v, len(control_labels), single_qubit_gates) # update the diag according to the applied diagonal gate _apply_diagonal_gate_to_diag(diag, control_labels + [target_label], @@ -202,7 +202,7 @@ def _disentangle(self, circuit, q, diag, remaining_isometry, column_index, s): # diag_inv = np.conj(diag).tolist() # _apply_diagonal_gate(v, control_labels + [target_label], diag_inv) - # This method finds the single-qubit gates for a UCG to disentangle a qubit: + # This method finds the single-qubit gates for a UCGate to disentangle a qubit: # we consider the n-qubit state v[:,0] starting with k zeros (in the computational basis). # The qubit with label n-s-1 is disentangled into the basis state k_s(k,s). def _find_squs_for_disentangling(self, v, k, s): @@ -218,17 +218,17 @@ def _find_squs_for_disentangling(self, v, k, s): for l in range(i_start, 2 ** (n - s - 1))] return id_list + squs - # Append a UCG up to diagonal to the circuit circ. + # Append a UCGate up to diagonal to the circuit circ. def _append_ucg_up_to_diagonal(self, circ, q, single_qubit_gates, control_labels, target_label): (q_input, q_ancillas_for_output, q_ancillas_zero, q_ancillas_dirty) = \ self._define_qubit_role(q) n = int(np.log2(self.params[0].shape[0])) qubits = q_input + q_ancillas_for_output # Note that we have to reverse the control labels, since controls are provided by - # increasing qubit number toa UCG by convention + # increasing qubit number toa UCGate by convention control_qubits = _reverse_qubit_oder(_get_qubits_by_label(control_labels, qubits, n)) target_qubit = _get_qubits_by_label([target_label], qubits, n)[0] - ucg = UCG(single_qubit_gates, up_to_diagonal=True) + ucg = UCGate(single_qubit_gates, up_to_diagonal=True) circ.append(ucg, [target_qubit] + control_qubits) return ucg._get_diagonal() @@ -283,8 +283,8 @@ def _reverse_qubit_state(state, basis_state): # Methods for applying gates to matrices (should be moved to Qiskit AER) # Input: matrix m with 2^n rows (and arbitrary many columns). Think of the columns as states -# on n qubits. The method applies a uniformly controlled gate (UCG) to all the columns, where -# the UCG is specified by the inputs k and single_qubit_gates: +# on n qubits. The method applies a uniformly controlled gate (UCGate) to all the columns, where +# the UCGate is specified by the inputs k and single_qubit_gates: # k = number of controls. We assume that the controls are on the k most significant qubits # (and the target is on the (k+1)th significant qubit) @@ -440,10 +440,10 @@ def _get_binary_rep_as_list(n, num_digits): return binary[-num_digits:] -# absorb a diagonal gate into a UCG +# absorb a diagonal gate into a UCGate -def _merge_UCG_and_diag(single_qubit_gates, diag): +def _merge_UCGate_and_diag(single_qubit_gates, diag): for (i, gate) in enumerate(single_qubit_gates): single_qubit_gates[i] = \ np.array([[diag[2 * i], 0.], [0., diag[2 * i + 1]]]).dot(gate) @@ -553,4 +553,6 @@ def iso(self, isometry, q_input, q_ancillas_for_output, q_ancillas_zero=None, q_input + q_ancillas_for_output + q_ancillas_zero + q_ancillas_dirty) +# support both QuantumCircuit.iso and QuantumCircuit.isometry QuantumCircuit.iso = iso +QuantumCircuit.isometry = iso diff --git a/qiskit/extensions/quantum_initializer/mcg_up_to_diagonal.py b/qiskit/extensions/quantum_initializer/mcg_up_to_diagonal.py index 46524d2414f4..01f71829eacf 100644 --- a/qiskit/extensions/quantum_initializer/mcg_up_to_diagonal.py +++ b/qiskit/extensions/quantum_initializer/mcg_up_to_diagonal.py @@ -29,7 +29,7 @@ from qiskit.circuit.quantumcircuit import QuantumRegister, QuantumCircuit from qiskit.quantum_info.operators.predicates import is_isometry from qiskit.exceptions import QiskitError -from qiskit.extensions.quantum_initializer.ucg import UCG +from qiskit.extensions.quantum_initializer.uc import UCGate _EPS = 1e-10 # global variable used to chop very small numbers to zero @@ -94,14 +94,14 @@ def _dec_mcg_up_diag(self): circuit = QuantumCircuit(q) (q_target, q_controls, q_ancillas_zero, q_ancillas_dirty) = self._define_qubit_role(q) # ToDo: Keep this threshold updated such that the lowest gate count is achieved: - # ToDo: we implement the MCG with a UCG up to diagonal if the number of controls is + # ToDo: we implement the MCG with a UCGate up to diagonal if the number of controls is # ToDo: smaller than the threshold. threshold = float("inf") if self.num_controls < threshold: - # Implement the MCG as a UCG (up to diagonal) + # Implement the MCG as a UCGate (up to diagonal) gate_list = [np.eye(2, 2) for i in range(2 ** self.num_controls)] gate_list[-1] = self.params[0] - ucg = UCG(gate_list, up_to_diagonal=True) + ucg = UCGate(gate_list, up_to_diagonal=True) circuit.append(ucg, [q_target] + q_controls) diag = ucg._get_diagonal() # else: diff --git a/qiskit/extensions/quantum_initializer/squ.py b/qiskit/extensions/quantum_initializer/squ.py index 820e34192d4e..e5cc7901c7c6 100644 --- a/qiskit/extensions/quantum_initializer/squ.py +++ b/qiskit/extensions/quantum_initializer/squ.py @@ -99,7 +99,7 @@ def _dec_single_qubit_unitary(self): circuit.rz(c, q[0]) is_identity = False if is_identity: - circuit.iden(q[0]) + circuit.i(q[0]) return circuit, diag def _zyz_dec(self): diff --git a/qiskit/extensions/quantum_initializer/uc.py b/qiskit/extensions/quantum_initializer/uc.py new file mode 100644 index 000000000000..f4501d673f37 --- /dev/null +++ b/qiskit/extensions/quantum_initializer/uc.py @@ -0,0 +1,347 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +# The structure of the code is based on Emanuel Malvetti's semester thesis at +# ETH in 2018, which was supervised by Raban Iten and Prof. Renato Renner. + +# pylint: disable=invalid-name +# pylint: disable=missing-param-doc +# pylint: disable=missing-type-doc + +""" +Uniformly controlled gates (also called multiplexed gates). + +These gates can have several control qubits and a single target qubit. +If the k control qubits are in the state |i> (in the computational basis), +a single-qubit unitary U_i is applied to the target qubit. + +This gate is represented by a block-diagonal matrix, where each block is a +2x2 unitary: + + [[U_0, 0, ...., 0], + [0, U_1, ...., 0], + . + . + [0, 0, ...., U_(2^k-1)]] +""" + +import cmath +import math + +import numpy as np + +from qiskit.circuit.gate import Gate +from qiskit.extensions.standard.h import HGate +from qiskit.quantum_info.operators.predicates import is_unitary_matrix +from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.exceptions import QiskitError +from qiskit.quantum_info.synthesis import euler_angles_1q + +_EPS = 1e-10 # global variable used to chop very small numbers to zero + + +class UCMeta(type): + """A metaclass to ensure that UCGate and UCG are of the same type. + + Can be removed when UCGG gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {UCGate, UCG} # pylint: disable=unidiomatic-typecheck + + +class UCGate(Gate, metaclass=UCMeta): + """Uniformly controlled gate (also called multiplexed gate). + The decomposition is based on: https://arxiv.org/pdf/quant-ph/0410066.pdf. + """ + + def __init__(self, gate_list, up_to_diagonal=False): + """UCGate Gate initializer. + + Args: + gate_list (list[ndarray]): list of two qubit unitaries [U_0,...,U_{2^k-1}], + where each single-qubit unitary U_i is given as a 2*2 numpy array. + + up_to_diagonal (bool): determines if the gate is implemented up to a diagonal. + or if it is decomposed completely (default: False). + If the UCGate u is decomposed up to a diagonal d, this means that the circuit + implements a unitary u' such that d.u'=u. + + Raises: + QiskitError: in case of bad input to the constructor + """ + # check input format + if not isinstance(gate_list, list): + raise QiskitError("The single-qubit unitaries are not provided in a list.") + for gate in gate_list: + if not gate.shape == (2, 2): + raise QiskitError("The dimension of a controlled gate is not equal to (2,2).") + if not gate_list: + raise QiskitError("The gate list cannot be empty.") + + # Check if number of gates in gate_list is a positive power of two + num_contr = math.log2(len(gate_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError("The number of controlled single-qubit gates is not a " + "non-negative power of 2.") + + # Check if the single-qubit gates are unitaries + for gate in gate_list: + if not is_unitary_matrix(gate, _EPS): + raise QiskitError("A controlled gate is not unitary.") + + # Create new gate. + super().__init__("multiplexer", int(num_contr) + 1, gate_list) + self.up_to_diagonal = up_to_diagonal + + def _get_diagonal(self): + # Important: for a control list q_controls = [q[0],...,q_[k-1]] the + # diagonal gate is provided in the computational basis of the qubits + # q[k-1],...,q[0],q_target, decreasingly ordered with respect to the + # significance of the qubit in the computational basis + _, diag = self._dec_ucg() + return diag + + def _define(self): + ucg_circuit, _ = self._dec_ucg() + self.definition = ucg_circuit.data + + def _dec_ucg(self): + """ + Call to create a circuit that implements the uniformly controlled gate. If + up_to_diagonal=True, the circuit implements the gate up to a diagonal gate and + the diagonal gate is also returned. + """ + diag = np.ones(2 ** self.num_qubits).tolist() + q = QuantumRegister(self.num_qubits) + q_controls = q[1:] + q_target = q[0] + circuit = QuantumCircuit(q) + # If there is no control, we use the ZYZ decomposition + if not q_controls: + theta, phi, lamb = euler_angles_1q(self.params[0]) + circuit.u3(theta, phi, lamb, q) + return circuit, diag + # If there is at least one control, first, + # we find the single qubit gates of the decomposition. + (single_qubit_gates, diag) = self._dec_ucg_help() + # Now, it is easy to place the C-NOT gates and some Hadamards and Rz(pi/2) gates + # (which are absorbed into the single-qubit unitaries) to get back the full decomposition. + for i, gate in enumerate(single_qubit_gates): + # Absorb Hadamards and Rz(pi/2) gates + if i == 0: + squ = HGate().to_matrix().dot(gate) + elif i == len(single_qubit_gates) - 1: + squ = gate.dot(UCGate._rz(np.pi / 2)).dot(HGate().to_matrix()) + else: + squ = HGate().to_matrix().dot(gate.dot(UCGate._rz(np.pi / 2))).dot( + HGate().to_matrix()) + # Add single-qubit gate + circuit.squ(squ, q_target) + # The number of the control qubit is given by the number of zeros at the end + # of the binary representation of (i+1) + binary_rep = np.binary_repr(i + 1) + num_trailing_zeros = len(binary_rep) - len(binary_rep.rstrip('0')) + q_contr_index = num_trailing_zeros + # Add C-NOT gate + if not i == len(single_qubit_gates) - 1: + circuit.cx(q_controls[q_contr_index], q_target) + if not self.up_to_diagonal: + # Important: the diagonal gate is given in the computational basis of the qubits + # q[k-1],...,q[0],q_target (ordered with decreasing significance), + # where q[i] are the control qubits and t denotes the target qubit. + circuit.diagonal(diag.tolist(), q) + return circuit, diag + + def _dec_ucg_help(self): + """ + This method finds the single qubit gate arising in the decomposition of UCGates given in + https://arxiv.org/pdf/quant-ph/0410066.pdf. + """ + single_qubit_gates = [gate.astype(complex) for gate in self.params] + diag = np.ones(2 ** self.num_qubits, dtype=complex) + num_contr = self.num_qubits - 1 + for dec_step in range(num_contr): + num_ucgs = 2 ** dec_step + # The decomposition works recursively and the following loop goes over the different + # UCGates that arise in the decomposition + for ucg_index in range(num_ucgs): + len_ucg = 2 ** (num_contr - dec_step) + for i in range(int(len_ucg / 2)): + shift = ucg_index * len_ucg + a = single_qubit_gates[shift + i] + b = single_qubit_gates[shift + len_ucg // 2 + i] + # Apply the decomposition for UCGates given in equation (3) in + # https://arxiv.org/pdf/quant-ph/0410066.pdf + # to demultiplex one control of all the num_ucgs uniformly-controlled gates + # with log2(len_ucg) uniform controls + v, u, r = self._demultiplex_single_uc(a, b) + # replace the single-qubit gates with v,u (the already existing ones + # are not needed any more) + single_qubit_gates[shift + i] = v + single_qubit_gates[shift + len_ucg // 2 + i] = u + # Now we decompose the gates D as described in Figure 4 in + # https://arxiv.org/pdf/quant-ph/0410066.pdf and merge some of the gates + # into the UCGates and the diagonal at the end of the circuit + + # Remark: The Rz(pi/2) rotation acting on the target qubit and the Hadamard + # gates arising in the decomposition of D are ignored for the moment (they will + # be added together with the C-NOT gates at the end of the decomposition + # (in the method dec_ucg())) + if ucg_index < num_ucgs - 1: + # Absorb the Rz(pi/2) rotation on the control into the UC-Rz gate and + # merge the UC-Rz rotation with the following UCGate, + # which hasn't been decomposed yet. + k = shift + len_ucg + i + single_qubit_gates[k] = \ + single_qubit_gates[k].dot(UCGate._ct(r)) * \ + UCGate._rz(np.pi / 2).item((0, 0)) + k = k + len_ucg // 2 + single_qubit_gates[k] = \ + single_qubit_gates[k].dot(r) * UCGate._rz(np.pi / 2).item((1, 1)) + else: + # Absorb the Rz(pi/2) rotation on the control into the UC-Rz gate and merge + # the trailing UC-Rz rotation into a diagonal gate at the end of the circuit + for ucg_index_2 in range(num_ucgs): + shift_2 = ucg_index_2 * len_ucg + k = 2 * (i + shift_2) + diag[k] = diag[k] * UCGate._ct(r).item((0, 0)) * \ + UCGate._rz(np.pi / 2).item((0, 0)) + diag[k + 1] = diag[k + 1] * UCGate._ct(r).item((1, 1)) * UCGate._rz( + np.pi / 2).item((0, 0)) + k = len_ucg + k + diag[k] *= r.item((0, 0)) * UCGate._rz(np.pi / 2).item((1, 1)) + diag[k + 1] *= r.item((1, 1)) * UCGate._rz(np.pi / 2).item((1, 1)) + return single_qubit_gates, diag + + def _demultiplex_single_uc(self, a, b): + """ + This method implements the decomposition given in equation (3) in + https://arxiv.org/pdf/quant-ph/0410066.pdf. + The decomposition is used recursively to decompose uniformly controlled gates. + a,b = single qubit unitaries + v,u,r = outcome of the decomposition given in the reference mentioned above + (see there for the details). + """ + # The notation is chosen as in https://arxiv.org/pdf/quant-ph/0410066.pdf. + x = a.dot(UCGate._ct(b)) + det_x = np.linalg.det(x) + x11 = x.item((0, 0)) / cmath.sqrt(det_x) + phi = cmath.phase(det_x) + r1 = cmath.exp(1j / 2 * (np.pi / 2 - phi / 2 - cmath.phase(x11))) + r2 = cmath.exp(1j / 2 * (np.pi / 2 - phi / 2 + cmath.phase(x11) + np.pi)) + r = np.array([[r1, 0], [0, r2]], dtype=complex) + d, u = np.linalg.eig(r.dot(x).dot(r)) + # If d is not equal to diag(i,-i), then we put it into this "standard" form + # (see eq. (13) in https://arxiv.org/pdf/quant-ph/0410066.pdf) by interchanging + # the eigenvalues and eigenvectors. + if abs(d[0] + 1j) < _EPS: + d = np.flip(d, 0) + u = np.flip(u, 1) + d = np.diag(np.sqrt(d)) + v = d.dot(UCGate._ct(u)).dot(UCGate._ct(r)).dot(b) + return v, u, r + + @staticmethod + def _ct(m): + return np.transpose(np.conjugate(m)) + + @staticmethod + def _rz(alpha): + return np.array([[np.exp(1j * alpha / 2), 0], [0, np.exp(-1j * alpha / 2)]]) + + +def uc(self, gate_list, q_controls, q_target, up_to_diagonal=False): + """Attach a uniformly controlled gates (also called multiplexed gates) to a circuit. + + The decomposition was introduced by Bergholm et al. in + https://arxiv.org/pdf/quant-ph/0410066.pdf. + + Args: + gate_list (list[ndarray]): list of two qubit unitaries [U_0,...,U_{2^k-1}], + where each single-qubit unitary U_i is a given as a 2*2 array + q_controls (QuantumRegister|list[(QuantumRegister,int)]): list of k control qubits. + The qubits are ordered according to their significance in the computational basis. + For example if q_controls=[q[1],q[2]] (with q = QuantumRegister(2)), + the unitary U_0 is performed if q[1] and q[2] are in the state zero, U_1 is + performed if q[2] is in the state zero and q[1] is in the state one, and so on + q_target (QuantumRegister|(QuantumRegister,int)): target qubit, where we act on with + the single-qubit gates. + up_to_diagonal (bool): If set to True, the uniformly controlled gate is decomposed up + to a diagonal gate, i.e. a unitary u' is implemented such that there exists a + diagonal gate d with u = d.dot(u'), where the unitary u describes the uniformly + controlled gate + + Returns: + QuantumCircuit: the uniformly controlled gate is attached to the circuit. + + Raises: + QiskitError: if the list number of control qubits does not correspond to the provided + number of single-qubit unitaries; if an input is of the wrong type + """ + + if isinstance(q_controls, QuantumRegister): + q_controls = q_controls[:] + if isinstance(q_target, QuantumRegister): + q_target = q_target[:] + if len(q_target) == 1: + q_target = q_target[0] + else: + raise QiskitError("The target qubit is a QuantumRegister containing more than" + " one qubit.") + # Check if q_controls has type "list" + if not isinstance(q_controls, list): + raise QiskitError("The control qubits must be provided as a list" + " (also if there is only one control qubit).") + # Check if gate_list has type "list" + if not isinstance(gate_list, list): + raise QiskitError("The single-qubit unitaries are not provided in a list.") + # Check if number of gates in gate_list is a positive power of two + num_contr = math.log2(len(gate_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError("The number of controlled single-qubit gates is not a non negative" + " power of 2.") + # Check if number of control qubits does correspond to the number of single-qubit rotations + if num_contr != len(q_controls): + raise QiskitError("Number of controlled gates does not correspond to the number of" + " control qubits.") + return self.append(UCGate(gate_list, up_to_diagonal), [q_target] + q_controls) + + +class UCG(UCGate, metaclass=UCMeta): + """The deprecated UCGate class.""" + + def __init__(self, gate_list, up_to_diagonal=False): + import warnings + warnings.warn('The class UCG is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class UCGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(gate_list, up_to_diagonal) + + +def ucg(self, angle_list, q_controls, q_target, up_to_diagonal=False): + """Deprecated version of uc.""" + + import warnings + warnings.warn('The QuantumCircuit.ucg() method is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the QuantumCircuit.uc() method instead.', + DeprecationWarning, stacklevel=2) + return uc(self, angle_list, q_controls, q_target, up_to_diagonal) + + +QuantumCircuit.uc = uc +QuantumCircuit.ucg = ucg # deprecated, but still supported diff --git a/qiskit/extensions/quantum_initializer/uc_pauli_rot.py b/qiskit/extensions/quantum_initializer/uc_pauli_rot.py new file mode 100644 index 000000000000..83cb1f923338 --- /dev/null +++ b/qiskit/extensions/quantum_initializer/uc_pauli_rot.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +# The structure of the code is based on Emanuel Malvetti's semester thesis at ETH in 2018, +# which was supervised by Raban Iten and Prof. Renato Renner. + +""" +(Abstract) base class for uniformly controlled (also called multiplexed) single-qubit rotations R_t. +This class provides a basis for the decomposition of uniformly controlled R_x,R_y and R_z gates +(i.e., for t=x,y,z). These gates can have several control qubits and a single target qubit. +If the k control qubits are in the state ket(i) (in the computational bases), +a single-qubit rotation R_t(a_i) is applied to the target qubit for a (real) angle a_i. +""" + +import math + +import numpy as np + +from qiskit.circuit import Gate, QuantumCircuit +from qiskit.circuit.quantumcircuit import QuantumRegister +from qiskit.exceptions import QiskitError + +_EPS = 1e-10 # global variable used to chop very small numbers to zero + + +class UCPauliRotMeta(type): + """A metaclass to ensure that UCPauliRotGate and UCRot are of the same type. + + Can be removed when UCRot gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {UCPauliRotGate, UCRot} # pylint: disable=unidiomatic-typecheck + + +class UCPauliRotGate(Gate, metaclass=UCPauliRotMeta): + """ + Uniformly controlled rotations (also called multiplexed rotations). + The decomposition is based on 'Synthesis of Quantum Logic Circuits' + by Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf) + + Input: + angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}]. Must have at least one entry. + + rot_axis = rotation axis for the single qubit rotations + (currently, 'X', 'Y' and 'Z' are supported) + """ + + def __init__(self, angle_list, rot_axis): + self.rot_axes = rot_axis + # Check if angle_list has type "list" + if not isinstance(angle_list, list): + raise QiskitError('The angles are not provided in a list.') + # Check if the angles in angle_list are real numbers + for angle in angle_list: + try: + float(angle) + except TypeError: + raise QiskitError( + 'An angle cannot be converted to type float (real angles are expected).') + num_contr = math.log2(len(angle_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError( + 'The number of controlled rotation gates is not a non-negative power of 2.') + if rot_axis not in ('X', 'Y', 'Z'): + raise QiskitError('Rotation axis is not supported.') + # Create new gate. + num_qubits = int(num_contr) + 1 + super().__init__('ucr' + rot_axis.lower(), num_qubits, angle_list) + + def _define(self): + ucr_circuit = self._dec_ucrot() + gate_num = len(ucr_circuit.data) + gate = ucr_circuit.to_instruction() + q = QuantumRegister(self.num_qubits) + ucr_circuit = QuantumCircuit(q) + if gate_num == 0: + # ToDo: if we would not add the identity here, this would lead to troubles + # ToDo: simulating the circuit afterwards. + # this should probably be fixed in the behavior of QuantumCircuit. + ucr_circuit.i(q[0]) + else: + ucr_circuit.append(gate, q[:]) + self.definition = ucr_circuit.data + + def _dec_ucrot(self): + """ + Finds a decomposition of a UC rotation gate into elementary gates + (C-NOTs and single-qubit rotations). + """ + q = QuantumRegister(self.num_qubits) + circuit = QuantumCircuit(q) + q_target = q[0] + q_controls = q[1:] + if not q_controls: # equivalent to: if len(q_controls) == 0 + if self.rot_axes == 'X': + if np.abs(self.params[0]) > _EPS: + circuit.rx(self.params[0], q_target) + if self.rot_axes == 'Y': + if np.abs(self.params[0]) > _EPS: + circuit.ry(self.params[0], q_target) + if self.rot_axes == 'Z': + if np.abs(self.params[0]) > _EPS: + circuit.rz(self.params[0], q_target) + else: + # First, we find the rotation angles of the single-qubit rotations acting + # on the target qubit + angles = self.params.copy() + UCPauliRotGate._dec_uc_rotations(angles, 0, len(angles), False) + # Now, it is easy to place the C-NOT gates to get back the full decomposition. + for (i, angle) in enumerate(angles): + if self.rot_axes == 'X': + if np.abs(angle) > _EPS: + circuit.rx(angle, q_target) + if self.rot_axes == 'Y': + if np.abs(angle) > _EPS: + circuit.ry(angle, q_target) + if self.rot_axes == 'Z': + if np.abs(angle) > _EPS: + circuit.rz(angle, q_target) + # Determine the index of the qubit we want to control the C-NOT gate. + # Note that it corresponds + # to the number of trailing zeros in the binary representation of i+1 + if not i == len(angles) - 1: + binary_rep = np.binary_repr(i + 1) + q_contr_index = len(binary_rep) - \ + len(binary_rep.rstrip('0')) + else: + # Handle special case: + q_contr_index = len(q_controls) - 1 + # For X rotations, we have to additionally place some Ry gates around the + # C-NOT gates. They change the basis of the NOT operation, such that the + # decomposition of for uniformly controlled X rotations works correctly by symmetry + # with the decomposition of uniformly controlled Z or Y rotations + if self.rot_axes == 'X': + circuit.ry(np.pi / 2, q_target) + circuit.cx(q_controls[q_contr_index], q_target) + if self.rot_axes == 'X': + circuit.ry(-np.pi / 2, q_target) + return circuit + + @staticmethod + def _dec_uc_rotations(angles, start_index, end_index, reversed_dec): + """ + Calculates rotation angles for a uniformly controlled R_t gate with a C-NOT gate at + the end of the circuit. The rotation angles of the gate R_t are stored in + angles[start_index:end_index]. If reversed_dec == True, it decomposes the gate such that + there is a C-NOT gate at the start of the circuit (in fact, the circuit topology for + the reversed decomposition is the reversed one of the original decomposition) + """ + interval_len_half = (end_index - start_index) // 2 + for i in range(start_index, start_index + interval_len_half): + if not reversed_dec: + angles[i], angles[i + interval_len_half] = \ + UCPauliRotGate._update_angles( + angles[i], angles[i + interval_len_half]) + else: + angles[i + interval_len_half], angles[i] = \ + UCPauliRotGate._update_angles( + angles[i], angles[i + interval_len_half]) + if interval_len_half <= 1: + return + else: + UCPauliRotGate._dec_uc_rotations(angles, start_index, start_index + interval_len_half, + False) + UCPauliRotGate._dec_uc_rotations(angles, start_index + interval_len_half, end_index, + True) + + @staticmethod + def _update_angles(angle1, angle2): + """Calculate the new rotation angles according to Shende's decomposition.""" + return (angle1 + angle2) / 2.0, (angle1 - angle2) / 2.0 + + +class UCRot(UCPauliRotGate, metaclass=UCPauliRotMeta): + """The deprecated DiagonalGate class.""" + + def __init__(self, angle_list, rot_axis): + import warnings + warnings.warn('The class UCRot is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class UCPauliRotGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(angle_list, rot_axis) diff --git a/qiskit/extensions/quantum_initializer/ucg.py b/qiskit/extensions/quantum_initializer/ucg.py index 80f0737aa227..980d54013b5e 100644 --- a/qiskit/extensions/quantum_initializer/ucg.py +++ b/qiskit/extensions/quantum_initializer/ucg.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2019. +# (C) Copyright IBM 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -12,300 +12,14 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -# The structure of the code is based on Emanuel Malvetti's semester thesis at -# ETH in 2018, which was supervised by Raban Iten and Prof. Renato Renner. +"""The uniformly controlled gate. -# pylint: disable=invalid-name -# pylint: disable=missing-param-doc -# pylint: disable=missing-type-doc - -""" -Uniformly controlled gates (also called multiplexed gates). - -These gates can have several control qubits and a single target qubit. -If the k control qubits are in the state |i> (in the computational basis), -a single-qubit unitary U_i is applied to the target qubit. - -This gate is represented by a block-diagonal matrix, where each block is a -2x2 unitary: - - [[U_0, 0, ...., 0], - [0, U_1, ...., 0], - . - . - [0, 0, ...., U_(2^k-1)]] +This module is deprecated, see uc.py. """ -import cmath -import math - -import numpy as np - -from qiskit.circuit.gate import Gate -from qiskit.extensions.standard.h import HGate -from qiskit.quantum_info.operators.predicates import is_unitary_matrix -from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.exceptions import QiskitError -from qiskit.quantum_info.synthesis import euler_angles_1q - -_EPS = 1e-10 # global variable used to chop very small numbers to zero - - -class UCG(Gate): - """Uniformly controlled gate (also called multiplexed gate). - The decomposition is based on: https://arxiv.org/pdf/quant-ph/0410066.pdf. - """ - - def __init__(self, gate_list, up_to_diagonal=False): - """UCG Gate initializer. - - Args: - gate_list (list[ndarray]): list of two qubit unitaries [U_0,...,U_{2^k-1}], - where each single-qubit unitary U_i is given as a 2*2 numpy array. - - up_to_diagonal (bool): determines if the gate is implemented up to a diagonal. - or if it is decomposed completely (default: False). - If the UCG u is decomposed up to a diagonal d, this means that the circuit - implements a unitary u' such that d.u'=u. - - Raises: - QiskitError: in case of bad input to the constructor - """ - # check input format - if not isinstance(gate_list, list): - raise QiskitError("The single-qubit unitaries are not provided in a list.") - for gate in gate_list: - if not gate.shape == (2, 2): - raise QiskitError("The dimension of a controlled gate is not equal to (2,2).") - if not gate_list: - raise QiskitError("The gate list cannot be empty.") - - # Check if number of gates in gate_list is a positive power of two - num_contr = math.log2(len(gate_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError("The number of controlled single-qubit gates is not a " - "non-negative power of 2.") - - # Check if the single-qubit gates are unitaries - for gate in gate_list: - if not is_unitary_matrix(gate, _EPS): - raise QiskitError("A controlled gate is not unitary.") - - # Create new gate. - super().__init__("multiplexer", int(num_contr) + 1, gate_list) - self.up_to_diagonal = up_to_diagonal - - def _get_diagonal(self): - # Important: for a control list q_controls = [q[0],...,q_[k-1]] the - # diagonal gate is provided in the computational basis of the qubits - # q[k-1],...,q[0],q_target, decreasingly ordered with respect to the - # significance of the qubit in the computational basis - _, diag = self._dec_ucg() - return diag - - def _define(self): - ucg_circuit, _ = self._dec_ucg() - self.definition = ucg_circuit.data - - def _dec_ucg(self): - """ - Call to create a circuit that implements the uniformly controlled gate. If - up_to_diagonal=True, the circuit implements the gate up to a diagonal gate and - the diagonal gate is also returned. - """ - diag = np.ones(2 ** self.num_qubits).tolist() - q = QuantumRegister(self.num_qubits) - q_controls = q[1:] - q_target = q[0] - circuit = QuantumCircuit(q) - # If there is no control, we use the ZYZ decomposition - if not q_controls: - theta, phi, lamb = euler_angles_1q(self.params[0]) - circuit.u3(theta, phi, lamb, q) - return circuit, diag - # If there is at least one control, first, - # we find the single qubit gates of the decomposition. - (single_qubit_gates, diag) = self._dec_ucg_help() - # Now, it is easy to place the C-NOT gates and some Hadamards and Rz(pi/2) gates - # (which are absorbed into the single-qubit unitaries) to get back the full decomposition. - for i, gate in enumerate(single_qubit_gates): - # Absorb Hadamards and Rz(pi/2) gates - if i == 0: - squ = HGate().to_matrix().dot(gate) - elif i == len(single_qubit_gates) - 1: - squ = gate.dot(UCG._rz(np.pi / 2)).dot(HGate().to_matrix()) - else: - squ = HGate().to_matrix().dot(gate.dot(UCG._rz(np.pi / 2))).dot(HGate().to_matrix()) - # Add single-qubit gate - circuit.squ(squ, q_target) - # The number of the control qubit is given by the number of zeros at the end - # of the binary representation of (i+1) - binary_rep = np.binary_repr(i + 1) - num_trailing_zeros = len(binary_rep) - len(binary_rep.rstrip('0')) - q_contr_index = num_trailing_zeros - # Add C-NOT gate - if not i == len(single_qubit_gates) - 1: - circuit.cx(q_controls[q_contr_index], q_target) - if not self.up_to_diagonal: - # Important: the diagonal gate is given in the computational basis of the qubits - # q[k-1],...,q[0],q_target (ordered with decreasing significance), - # where q[i] are the control qubits and t denotes the target qubit. - circuit.diag_gate(diag.tolist(), q) - return circuit, diag - - def _dec_ucg_help(self): - """ - This method finds the single qubit gate arising in the decomposition of UCGs given in - https://arxiv.org/pdf/quant-ph/0410066.pdf. - """ - single_qubit_gates = [gate.astype(complex) for gate in self.params] - diag = np.ones(2 ** self.num_qubits, dtype=complex) - num_contr = self.num_qubits - 1 - for dec_step in range(num_contr): - num_ucgs = 2 ** dec_step - # The decomposition works recursively and the following loop goes over the different - # UCGs that arise in the decomposition - for ucg_index in range(num_ucgs): - len_ucg = 2 ** (num_contr - dec_step) - for i in range(int(len_ucg / 2)): - shift = ucg_index * len_ucg - a = single_qubit_gates[shift + i] - b = single_qubit_gates[shift + len_ucg // 2 + i] - # Apply the decomposition for UCGs given in equation (3) in - # https://arxiv.org/pdf/quant-ph/0410066.pdf - # to demultiplex one control of all the num_ucgs uniformly-controlled gates - # with log2(len_ucg) uniform controls - v, u, r = self._demultiplex_single_uc(a, b) - # replace the single-qubit gates with v,u (the already existing ones - # are not needed any more) - single_qubit_gates[shift + i] = v - single_qubit_gates[shift + len_ucg // 2 + i] = u - # Now we decompose the gates D as described in Figure 4 in - # https://arxiv.org/pdf/quant-ph/0410066.pdf and merge some of the gates - # into the UCGs and the diagonal at the end of the circuit - - # Remark: The Rz(pi/2) rotation acting on the target qubit and the Hadamard - # gates arising in the decomposition of D are ignored for the moment (they will - # be added together with the C-NOT gates at the end of the decomposition - # (in the method dec_ucg())) - if ucg_index < num_ucgs - 1: - # Absorb the Rz(pi/2) rotation on the control into the UC-Rz gate and - # merge the UC-Rz rotation with the following UCG, - # which hasn't been decomposed yet. - k = shift + len_ucg + i - single_qubit_gates[k] = \ - single_qubit_gates[k].dot(UCG._ct(r)) * UCG._rz(np.pi / 2).item((0, 0)) - k = k + len_ucg // 2 - single_qubit_gates[k] = \ - single_qubit_gates[k].dot(r) * UCG._rz(np.pi / 2).item((1, 1)) - else: - # Absorb the Rz(pi/2) rotation on the control into the UC-Rz gate and merge - # the trailing UC-Rz rotation into a diagonal gate at the end of the circuit - for ucg_index_2 in range(num_ucgs): - shift_2 = ucg_index_2 * len_ucg - k = 2 * (i + shift_2) - diag[k] = diag[k] * UCG._ct(r).item((0, 0)) * UCG._rz(np.pi / 2).item( - (0, 0)) - diag[k + 1] = diag[k + 1] * UCG._ct(r).item((1, 1)) * UCG._rz( - np.pi / 2).item((0, 0)) - k = len_ucg + k - diag[k] *= r.item((0, 0)) * UCG._rz(np.pi / 2).item((1, 1)) - diag[k + 1] *= r.item((1, 1)) * UCG._rz(np.pi / 2).item((1, 1)) - return single_qubit_gates, diag - - def _demultiplex_single_uc(self, a, b): - """ - This method implements the decomposition given in equation (3) in - https://arxiv.org/pdf/quant-ph/0410066.pdf. - The decomposition is used recursively to decompose uniformly controlled gates. - a,b = single qubit unitaries - v,u,r = outcome of the decomposition given in the reference mentioned above - (see there for the details). - """ - # The notation is chosen as in https://arxiv.org/pdf/quant-ph/0410066.pdf. - x = a.dot(UCG._ct(b)) - det_x = np.linalg.det(x) - x11 = x.item((0, 0)) / cmath.sqrt(det_x) - phi = cmath.phase(det_x) - r1 = cmath.exp(1j / 2 * (np.pi / 2 - phi / 2 - cmath.phase(x11))) - r2 = cmath.exp(1j / 2 * (np.pi / 2 - phi / 2 + cmath.phase(x11) + np.pi)) - r = np.array([[r1, 0], [0, r2]], dtype=complex) - d, u = np.linalg.eig(r.dot(x).dot(r)) - # If d is not equal to diag(i,-i), then we put it into this "standard" form - # (see eq. (13) in https://arxiv.org/pdf/quant-ph/0410066.pdf) by interchanging - # the eigenvalues and eigenvectors. - if abs(d[0] + 1j) < _EPS: - d = np.flip(d, 0) - u = np.flip(u, 1) - d = np.diag(np.sqrt(d)) - v = d.dot(UCG._ct(u)).dot(UCG._ct(r)).dot(b) - return v, u, r - - @staticmethod - def _ct(m): - return np.transpose(np.conjugate(m)) - - @staticmethod - def _rz(alpha): - return np.array([[np.exp(1j * alpha / 2), 0], [0, np.exp(-1j * alpha / 2)]]) - - -def ucg(self, gate_list, q_controls, q_target, up_to_diagonal=False): - """Attach a uniformly controlled gates (also called multiplexed gates) to a circuit. - - The decomposition was introduced by Bergholm et al. in - https://arxiv.org/pdf/quant-ph/0410066.pdf. - - Args: - gate_list (list[ndarray]): list of two qubit unitaries [U_0,...,U_{2^k-1}], - where each single-qubit unitary U_i is a given as a 2*2 array - q_controls (QuantumRegister|list[(QuantumRegister,int)]): list of k control qubits. - The qubits are ordered according to their significance in the computational basis. - For example if q_controls=[q[1],q[2]] (with q = QuantumRegister(2)), - the unitary U_0 is performed if q[1] and q[2] are in the state zero, U_1 is - performed if q[2] is in the state zero and q[1] is in the state one, and so on - q_target (QuantumRegister|(QuantumRegister,int)): target qubit, where we act on with - the single-qubit gates. - up_to_diagonal (bool): If set to True, the uniformly controlled gate is decomposed up - to a diagonal gate, i.e. a unitary u' is implemented such that there exists a - diagonal gate d with u = d.dot(u'), where the unitary u describes the uniformly - controlled gate - - Returns: - QuantumCircuit: the uniformly controlled gate is attached to the circuit. - - Raises: - QiskitError: if the list number of control qubits does not correspond to the provided - number of single-qubit unitaries; if an input is of the wrong type - """ - - if isinstance(q_controls, QuantumRegister): - q_controls = q_controls[:] - if isinstance(q_target, QuantumRegister): - q_target = q_target[:] - if len(q_target) == 1: - q_target = q_target[0] - else: - raise QiskitError("The target qubit is a QuantumRegister containing more than" - " one qubit.") - # Check if q_controls has type "list" - if not isinstance(q_controls, list): - raise QiskitError("The control qubits must be provided as a list" - " (also if there is only one control qubit).") - # Check if gate_list has type "list" - if not isinstance(gate_list, list): - raise QiskitError("The single-qubit unitaries are not provided in a list.") - # Check if number of gates in gate_list is a positive power of two - num_contr = math.log2(len(gate_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError("The number of controlled single-qubit gates is not a non negative" - " power of 2.") - # Check if number of control qubits does correspond to the number of single-qubit rotations - if num_contr != len(q_controls): - raise QiskitError("Number of controlled gates does not correspond to the number of" - " control qubits.") - return self.append(UCG(gate_list, up_to_diagonal), [q_target] + q_controls) - +import warnings +# pylint: disable=unused-import +from qiskit.extensions.quantum_initializer.uc import UCG, ucg -QuantumCircuit.ucg = ucg +warnings.warn('This module is deprecated. The UCGate/UCG can now be found in uc.py', + category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/quantum_initializer/ucrot.py b/qiskit/extensions/quantum_initializer/ucrot.py index 81cff4c4bfe9..514a86f7d471 100644 --- a/qiskit/extensions/quantum_initializer/ucrot.py +++ b/qiskit/extensions/quantum_initializer/ucrot.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2019. +# (C) Copyright IBM 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -12,157 +12,14 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -# The structure of the code is based on Emanuel Malvetti's semester thesis at ETH in 2018, -# which was supervised by Raban Iten and Prof. Renato Renner. +"""The uniformly controlled Pauli rotation gate. +This module is deprecated, see uc_pauli_rot.py """ -(Abstract) base class for uniformly controlled (also called multiplexed) single-qubit rotations R_t. -This class provides a basis for the decomposition of uniformly controlled R_x,R_y and R_z gates -(i.e., for t=x,y,z). These gates can have several control qubits and a single target qubit. -If the k control qubits are in the state ket(i) (in the computational bases), -a single-qubit rotation R_t(a_i) is applied to the target qubit for a (real) angle a_i. -""" - -import math - -import numpy as np - -from qiskit.circuit import Gate, QuantumCircuit -from qiskit.circuit.quantumcircuit import QuantumRegister -from qiskit.exceptions import QiskitError - -_EPS = 1e-10 # global variable used to chop very small numbers to zero - - -class UCRot(Gate): - """ - Uniformly controlled rotations (also called multiplexed rotations). - The decomposition is based on 'Synthesis of Quantum Logic Circuits' - by Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf) - - Input: - angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}]. Must have at least one entry. - - rot_axis = rotation axis for the single qubit rotations - (currently, "X","Y" and "Z" are supported) - """ - - def __init__(self, angle_list, rot_axis): - self.rot_axes = rot_axis - # Check if angle_list has type "list" - if not isinstance(angle_list, list): - raise QiskitError("The angles are not provided in a list.") - # Check if the angles in angle_list are real numbers - for angle in angle_list: - try: - float(angle) - except TypeError: - raise QiskitError( - "An angle cannot be converted to type float (real angles are expected).") - num_contr = math.log2(len(angle_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError( - "The number of controlled rotation gates is not a non-negative power of 2.") - if rot_axis not in ("X", "Y", "Z"): - raise QiskitError("Rotation axis is not supported.") - # Create new gate. - num_qubits = int(num_contr) + 1 - super().__init__("ucrot" + rot_axis, num_qubits, angle_list) - - def _define(self): - ucr_circuit = self._dec_ucrot() - gate_num = len(ucr_circuit.data) - gate = ucr_circuit.to_instruction() - q = QuantumRegister(self.num_qubits) - ucr_circuit = QuantumCircuit(q) - if gate_num == 0: - # ToDo: if we would not add the identity here, this would lead to troubles - # ToDo: simulating the circuit afterwards. - # this should probably be fixed in the behaviour of QuantumCircuit. - ucr_circuit.iden(q[0]) - else: - ucr_circuit.append(gate, q[:]) - self.definition = ucr_circuit.data - - def _dec_ucrot(self): - """ - finds a decomposition of a UC rotation gate into elementary gates - (C-NOTs and single-qubit rotations). - """ - q = QuantumRegister(self.num_qubits) - circuit = QuantumCircuit(q) - q_target = q[0] - q_controls = q[1:] - if not q_controls: # equivalent to: if len(q_controls) == 0 - if self.rot_axes == "X": - if np.abs(self.params[0]) > _EPS: - circuit.rx(self.params[0], q_target) - if self.rot_axes == "Y": - if np.abs(self.params[0]) > _EPS: - circuit.ry(self.params[0], q_target) - if self.rot_axes == "Z": - if np.abs(self.params[0]) > _EPS: - circuit.rz(self.params[0], q_target) - else: - # First, we find the rotation angles of the single-qubit rotations acting - # on the target qubit - angles = self.params.copy() - UCRot._dec_uc_rotations(angles, 0, len(angles), False) - # Now, it is easy to place the C-NOT gates to get back the full decomposition.s - for (i, angle) in enumerate(angles): - if self.rot_axes == "X": - if np.abs(angle) > _EPS: - circuit.rx(angle, q_target) - if self.rot_axes == "Y": - if np.abs(angle) > _EPS: - circuit.ry(angle, q_target) - if self.rot_axes == "Z": - if np.abs(angle) > _EPS: - circuit.rz(angle, q_target) - # Determine the index of the qubit we want to control the C-NOT gate. - # Note that it corresponds - # to the number of trailing zeros in the binary representation of i+1 - if not i == len(angles) - 1: - binary_rep = np.binary_repr(i + 1) - q_contr_index = len(binary_rep) - len(binary_rep.rstrip('0')) - else: - # Handle special case: - q_contr_index = len(q_controls) - 1 - # For X rotations, we have to additionally place some Ry gates around the - # C-NOT gates. They change the basis of the NOT operation, such that the - # decomposition of for uniformly controlled X rotations works correctly by symmetry - # with the decomposition of uniformly controlled Z or Y rotations - if self.rot_axes == "X": - circuit.ry(np.pi / 2, q_target) - circuit.cx(q_controls[q_contr_index], q_target) - if self.rot_axes == "X": - circuit.ry(-np.pi / 2, q_target) - return circuit - @staticmethod - def _dec_uc_rotations(angles, start_index, end_index, reversedDec): - """ - Calculates rotation angles for a uniformly controlled R_t gate with a C-NOT gate at - the end of the circuit. The rotation angles of the gate R_t are stored in - angles[start_index:end_index]. If reversed == True, it decomposes the gate such that - there is a C-NOT gate at the start of the circuit (in fact, the circuit topology for - the reversed decomposition is the reversed one of the original decomposition) - """ - interval_len_half = (end_index - start_index) // 2 - for i in range(start_index, start_index + interval_len_half): - if not reversedDec: - angles[i], angles[i + interval_len_half] = UCRot._update_angles(angles[i], angles[ - i + interval_len_half]) - else: - angles[i + interval_len_half], angles[i] = UCRot._update_angles(angles[i], angles[ - i + interval_len_half]) - if interval_len_half <= 1: - return - else: - UCRot._dec_uc_rotations(angles, start_index, start_index + interval_len_half, False) - UCRot._dec_uc_rotations(angles, start_index + interval_len_half, end_index, True) +import warnings +# pylint: disable=unused-import +from qiskit.extensions.quantum_initializer.uc_pauli_rot import UCRot - @staticmethod - def _update_angles(angle1, angle2): - """Calculate the new rotation angles according to Shende's decomposition""" - return (angle1 + angle2) / 2.0, (angle1 - angle2) / 2.0 +warnings.warn('This module is deprecated. The UCPauliRotGate/UCRot is now in uc_pauli_rot.py', + category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/quantum_initializer/ucrx.py b/qiskit/extensions/quantum_initializer/ucrx.py new file mode 100644 index 000000000000..f6893db4d2fe --- /dev/null +++ b/qiskit/extensions/quantum_initializer/ucrx.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +# pylint: disable=missing-param-doc +# pylint: disable=missing-type-doc + +""" +Implementation of the abstract class UCPauliRotGate for uniformly controlled +(also called multiplexed) single-qubit rotations around the X-axes +(i.e., uniformly controlled R_x rotations). +These gates can have several control qubits and a single target qubit. +If the k control qubits are in the state ket(i) (in the computational bases), +a single-qubit rotation R_x(a_i) is applied to the target qubit. +""" +import math + +from qiskit import QuantumRegister, QiskitError +from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.extensions.quantum_initializer.uc_pauli_rot import UCPauliRotGate, UCPauliRotMeta + + +class UCRXMeta(UCPauliRotMeta): + """A metaclass to ensure that UCRXGate and UCX are of the same type. + + Can be removed when UCX gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {UCRXGate, UCX} # pylint: disable=unidiomatic-typecheck + + +class UCRXGate(UCPauliRotGate, metaclass=UCRXMeta): + """ + Uniformly controlled rotations (also called multiplexed rotations). + The decomposition is based on + 'Synthesis of Quantum Logic Circuits' by V. Shende et al. + (https://arxiv.org/pdf/quant-ph/0406176.pdf) + + Input: + angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}] + """ + + def __init__(self, angle_list): + super().__init__(angle_list, "X") + + +def ucrx(self, angle_list, q_controls, q_target): + """Attach a uniformly controlled (also called multiplexed) Rx rotation gate to a circuit. + + The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. + + Args: + angle_list (list): list of (real) rotation angles [a_0,...,a_{2^k-1}] + q_controls (QuantumRegister|list): list of k control qubits + (or empty list if no controls). The control qubits are ordered according to their + significance in increasing order: For example if q_controls=[q[1],q[2]] + (with q = QuantumRegister(2)), the rotation Rx(a_0)is performed if q[1] and q[2] + are in the state zero, the rotation Rx(a_1) is performed if q[1] is in the state + one and q[2] is in the state zero, and so on + q_target (QuantumRegister|Qubit): target qubit, where we act on with + the single-qubit rotation gates + + Returns: + QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. + + Raises: + QiskitError: if the list number of control qubits does not correspond to the provided + number of single-qubit unitaries; if an input is of the wrong type + """ + + if isinstance(q_controls, QuantumRegister): + q_controls = q_controls[:] + if isinstance(q_target, QuantumRegister): + q_target = q_target[:] + if len(q_target) == 1: + q_target = q_target[0] + else: + raise QiskitError("The target qubit is a QuantumRegister containing more" + " than one qubits.") + # Check if q_controls has type "list" + if not isinstance(angle_list, list): + raise QiskitError("The angles must be provided as a list.") + num_contr = math.log2(len(angle_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError("The number of controlled rotation gates is not a non-negative" + " power of 2.") + # Check if number of control qubits does correspond to the number of rotations + if num_contr != len(q_controls): + raise QiskitError("Number of controlled rotations does not correspond to the number of" + " control-qubits.") + return self.append(UCRXGate(angle_list), [q_target] + q_controls, []) + + +class UCX(UCRXGate, metaclass=UCRXMeta): + """The deprecated UCRXGate class.""" + + def __init__(self, angle_list): + import warnings + warnings.warn('The class UCX is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class UCRXGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(angle_list) + + +def ucx(self, angle_list, q_controls, q_target): + """Deprecated version of ucrx.""" + import warnings + warnings.warn('The QuantumCircuit. ucx() method is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the QuantumCircuit. ucrx() method instead.', + DeprecationWarning, stacklevel=2) + return ucrx(self, angle_list, q_controls, q_target) + + +QuantumCircuit.ucrx = ucrx +QuantumCircuit.ucx = ucx # deprecated, but still supported diff --git a/qiskit/extensions/quantum_initializer/ucry.py b/qiskit/extensions/quantum_initializer/ucry.py new file mode 100644 index 000000000000..280bbad20dd6 --- /dev/null +++ b/qiskit/extensions/quantum_initializer/ucry.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Implementation of the abstract class UCPauliRotGate for uniformly controlled +(also called multiplexed) single-qubit rotations +around the Y-axes (i.e., uniformly controlled R_y rotations). +These gates can have several control qubits and a single target qubit. +If the k control qubits are in the state ket(i) (in the computational bases), +a single-qubit rotation R_y(a_i) is applied to the target qubit. +""" +import math + +from qiskit import QuantumRegister, QiskitError +from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.extensions.quantum_initializer.uc_pauli_rot import UCPauliRotGate, UCPauliRotMeta + + +class UCRYMeta(UCPauliRotMeta): + """A metaclass to ensure that UCRYGate and UCY are of the same type. + + Can be removed when UCY gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {UCRYGate, UCY} # pylint: disable=unidiomatic-typecheck + + +class UCRYGate(UCPauliRotGate, metaclass=UCRYMeta): + """ + Uniformly controlled rotations (also called multiplexed rotations). + The decomposition is based on + 'Synthesis of Quantum Logic Circuits' by V. Shende et al. + (https://arxiv.org/pdf/quant-ph/0406176.pdf) + + Input: + angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}] + """ + + def __init__(self, angle_list): + super().__init__(angle_list, "Y") + + +def ucry(self, angle_list, q_controls, q_target): + """Attach a uniformly controlled (also called multiplexed) Ry rotation gate to a circuit. + + The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. + + Args: + angle_list (list[numbers): list of (real) rotation angles [a_0,...,a_{2^k-1}] + q_controls (QuantumRegister|list[Qubit]): list of k control qubits + (or empty list if no controls). The control qubits are ordered according to their + significance in increasing order: For example if q_controls=[q[1],q[2]] + (with q = QuantumRegister(2)), the rotation Ry(a_0)is performed if q[1] and q[2] + are in the state zero, the rotation Ry(a_1) is performed if q[1] is in the state + one and q[2] is in the state zero, and so on + q_target (QuantumRegister|Qubit): target qubit, where we act on with + the single-qubit rotation gates + + Returns: + QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. + + Raises: + QiskitError: if the list number of control qubits does not correspond to the provided + number of single-qubit unitaries; if an input is of the wrong type + """ + + if isinstance(q_controls, QuantumRegister): + q_controls = q_controls[:] + if isinstance(q_target, QuantumRegister): + q_target = q_target[:] + if len(q_target) == 1: + q_target = q_target[0] + else: + raise QiskitError("The target qubit is a QuantumRegister containing" + " more than one qubits.") + # Check if q_controls has type "list" + if not isinstance(angle_list, list): + raise QiskitError("The angles must be provided as a list.") + num_contr = math.log2(len(angle_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError("The number of controlled rotation gates is not" + " a non-negative power of 2.") + # Check if number of control qubits does correspond to the number of rotations + if num_contr != len(q_controls): + raise QiskitError("Number of controlled rotations does not correspond to" + " the number of control-qubits.") + return self.append(UCRYGate(angle_list), [q_target] + q_controls, []) + + +class UCY(UCRYGate, metaclass=UCRYMeta): + """The deprecated UCRYGate class.""" + + def __init__(self, angle_list): + import warnings + warnings.warn('The class UCY is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class UCRYGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(angle_list) + + +def ucy(self, angle_list, q_controls, q_target): + """Deprecated version of ucry.""" + import warnings + warnings.warn('The QuantumCircuit ucy() method is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the QuantumCircuit ucry() method instead.', + DeprecationWarning, stacklevel=2) + return ucry(self, angle_list, q_controls, q_target) + + +QuantumCircuit.ucry = ucry +QuantumCircuit.ucy = ucy # deprecated, but still supported diff --git a/qiskit/extensions/quantum_initializer/ucrz.py b/qiskit/extensions/quantum_initializer/ucrz.py new file mode 100644 index 000000000000..cfdd7e684629 --- /dev/null +++ b/qiskit/extensions/quantum_initializer/ucrz.py @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Implementation of the abstract class UCPauliRotGate for uniformly controlled +(also called multiplexed) single-qubit rotations +around the Z-axes (i.e., uniformly controlled R_z rotations). +These gates can have several control qubits and a single target qubit. +If the k control qubits are in the state ket(i) (in the computational bases), +a single-qubit rotation R_z(a_i) is applied to the target qubit. +""" +import math + +from qiskit import QuantumRegister, QiskitError +from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.extensions.quantum_initializer.uc_pauli_rot import UCPauliRotGate, UCPauliRotMeta + + +class UCRZMeta(UCPauliRotMeta): + """A metaclass to ensure that UCRZGate and UCZ are of the same type. + + Can be removed when UCZ gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {UCRZGate, UCZ} # pylint: disable=unidiomatic-typecheck + + +class UCRZGate(UCPauliRotGate, metaclass=UCRZMeta): + """ + Uniformly controlled rotations (also called multiplexed rotations). + The decomposition is based on + 'Synthesis of Quantum Logic Circuits' by V. Shende et al. + (https://arxiv.org/pdf/quant-ph/0406176.pdf) + + Input: + angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}] + """ + + def __init__(self, angle_list): + super().__init__(angle_list, "Z") + + +def ucrz(self, angle_list, q_controls, q_target): + """Attach a uniformly controlled (also called multiplexed gates) Rz rotation gate to a circuit. + + The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. + + Args: + angle_list (list[numbers): list of (real) rotation angles [a_0,...,a_{2^k-1}] + q_controls (QuantumRegister|list[Qubit]): list of k control qubits + (or empty list if no controls). The control qubits are ordered according to their + significance in increasing order: For example if q_controls=[q[1],q[2]] + (with q = QuantumRegister(2)), the rotation Rz(a_0)is performed if q[1] and q[2] + are in the state zero, the rotation Rz(a_1) is performed if q[1] is in + the state one and q[2] is in the state zero, and so on + q_target (QuantumRegister|Qubit): target qubit, where we act on with + the single-qubit rotation gates + + Returns: + QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. + + Raises: + QiskitError: if the list number of control qubits does not correspond to + the provided number of single-qubit unitaries; if an input is of + the wrong type + """ + + if isinstance(q_controls, QuantumRegister): + q_controls = q_controls[:] + if isinstance(q_target, QuantumRegister): + q_target = q_target[:] + if len(q_target) == 1: + q_target = q_target[0] + else: + raise QiskitError("The target qubit is a QuantumRegister containing" + " more than one qubits.") + # Check if q_controls has type "list" + if not isinstance(angle_list, list): + raise QiskitError("The angles must be provided as a list.") + num_contr = math.log2(len(angle_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError("The number of controlled rotation gates is not a non-negative" + " power of 2.") + # Check if number of control qubits does correspond to the number of rotations + if num_contr != len(q_controls): + raise QiskitError("Number of controlled rotations does not correspond to the number" + " of control-qubits.") + return self.append(UCRZGate(angle_list), [q_target] + q_controls, []) + + +class UCZ(UCRZGate, metaclass=UCRZMeta): + """The deprecated UCRZGate class.""" + + def __init__(self, angle_list): + import warnings + warnings.warn('The class UCZ is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class UCRZGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(angle_list) + + +def ucz(self, angle_list, q_controls, q_target): + """Deprecated version of ucrz.""" + import warnings + warnings.warn('The QuantumCircuit.ucz() method is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the QuantumCircuit.ucrz() method instead.', + DeprecationWarning, stacklevel=2) + return ucrz(self, angle_list, q_controls, q_target) + + +QuantumCircuit.ucrz = ucrz +QuantumCircuit.ucz = ucz # deprecated, but still supported diff --git a/qiskit/extensions/quantum_initializer/ucx.py b/qiskit/extensions/quantum_initializer/ucx.py index 3dbc3a12eae3..9da13eda5d28 100644 --- a/qiskit/extensions/quantum_initializer/ucx.py +++ b/qiskit/extensions/quantum_initializer/ucx.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2019. +# (C) Copyright IBM 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -12,84 +12,14 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -# pylint: disable=missing-param-doc -# pylint: disable=missing-type-doc +"""The uniformly controlled RX gate. +This module is deprecated, see ucrx.py """ -Implementation of the abstract class UCRot for uniformly controlled -(also called multiplexed) single-qubit rotations around the X-axes -(i.e., uniformly controlled R_x rotations). -These gates can have several control qubits and a single target qubit. -If the k control qubits are in the state ket(i) (in the computational bases), -a single-qubit rotation R_x(a_i) is applied to the target qubit. -""" -import math - -from qiskit import QuantumRegister, QiskitError -from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.extensions.quantum_initializer.ucrot import UCRot - - -class UCX(UCRot): - """ - Uniformly controlled rotations (also called multiplexed rotations). - The decomposition is based on - 'Synthesis of Quantum Logic Circuits' by V. Shende et al. - (https://arxiv.org/pdf/quant-ph/0406176.pdf) - - Input: - angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}] - """ - - def __init__(self, angle_list): - super().__init__(angle_list, "X") - - -def ucx(self, angle_list, q_controls, q_target): - """Attach a uniformly controlled (also called multiplexed) Rx rotation gate to a circuit. - - The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. - - Args: - angle_list (list): list of (real) rotation angles [a_0,...,a_{2^k-1}] - q_controls (QuantumRegister|list): list of k control qubits - (or empty list if no controls). The control qubits are ordered according to their - significance in increasing order: For example if q_controls=[q[1],q[2]] - (with q = QuantumRegister(2)), the rotation Rx(a_0)is performed if q[1] and q[2] - are in the state zero, the rotation Rx(a_1) is performed if q[1] is in the state - one and q[2] is in the state zero, and so on - q_target (QuantumRegister|Qubit): target qubit, where we act on with - the single-qubit rotation gates - - Returns: - QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. - - Raises: - QiskitError: if the list number of control qubits does not correspond to the provided - number of single-qubit unitaries; if an input is of the wrong type - """ - - if isinstance(q_controls, QuantumRegister): - q_controls = q_controls[:] - if isinstance(q_target, QuantumRegister): - q_target = q_target[:] - if len(q_target) == 1: - q_target = q_target[0] - else: - raise QiskitError("The target qubit is a QuantumRegister containing more" - " than one qubits.") - # Check if q_controls has type "list" - if not isinstance(angle_list, list): - raise QiskitError("The angles must be provided as a list.") - num_contr = math.log2(len(angle_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError("The number of controlled rotation gates is not a non-negative" - " power of 2.") - # Check if number of control qubits does correspond to the number of rotations - if num_contr != len(q_controls): - raise QiskitError("Number of controlled rotations does not correspond to the number of" - " control-qubits.") - return self.append(UCX(angle_list), [q_target] + q_controls, []) +import warnings +# pylint: disable=unused-import +from qiskit.extensions.quantum_initializer.ucrx import UCX, ucx -QuantumCircuit.ucx = ucx +warnings.warn('This module is deprecated. The UCRXGate/UCX can now be found in ucrx.py', + category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/quantum_initializer/ucy.py b/qiskit/extensions/quantum_initializer/ucy.py index 1c3546cd3675..f993508bcd46 100644 --- a/qiskit/extensions/quantum_initializer/ucy.py +++ b/qiskit/extensions/quantum_initializer/ucy.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2019. +# (C) Copyright IBM 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -12,81 +12,14 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" -Implementation of the abstract class UCRot for uniformly controlled -(also called multiplexed) single-qubit rotations -around the Y-axes (i.e., uniformly controlled R_y rotations). -These gates can have several control qubits and a single target qubit. -If the k control qubits are in the state ket(i) (in the computational bases), -a single-qubit rotation R_y(a_i) is applied to the target qubit. -""" -import math - -from qiskit import QuantumRegister, QiskitError -from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.extensions.quantum_initializer.ucrot import UCRot - - -class UCY(UCRot): - """ - Uniformly controlled rotations (also called multiplexed rotations). - The decomposition is based on - 'Synthesis of Quantum Logic Circuits' by V. Shende et al. - (https://arxiv.org/pdf/quant-ph/0406176.pdf) - - Input: - angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}] - """ - - def __init__(self, angle_list): - super().__init__(angle_list, "Y") +"""The uniformly controlled RY gate. +This module is deprecated, see ucry.py +""" -def ucy(self, angle_list, q_controls, q_target): - """Attach a uniformly controlled (also called multiplexed) Ry rotation gate to a circuit. - - The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. - - Args: - angle_list (list[numbers): list of (real) rotation angles [a_0,...,a_{2^k-1}] - q_controls (QuantumRegister|list[Qubit]): list of k control qubits - (or empty list if no controls). The control qubits are ordered according to their - significance in increasing order: For example if q_controls=[q[1],q[2]] - (with q = QuantumRegister(2)), the rotation Ry(a_0)is performed if q[1] and q[2] - are in the state zero, the rotation Ry(a_1) is performed if q[1] is in the state - one and q[2] is in the state zero, and so on - q_target (QuantumRegister|Qubit): target qubit, where we act on with - the single-qubit rotation gates - - Returns: - QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. - - Raises: - QiskitError: if the list number of control qubits does not correspond to the provided - number of single-qubit unitaries; if an input is of the wrong type - """ - - if isinstance(q_controls, QuantumRegister): - q_controls = q_controls[:] - if isinstance(q_target, QuantumRegister): - q_target = q_target[:] - if len(q_target) == 1: - q_target = q_target[0] - else: - raise QiskitError("The target qubit is a QuantumRegister containing" - " more than one qubits.") - # Check if q_controls has type "list" - if not isinstance(angle_list, list): - raise QiskitError("The angles must be provided as a list.") - num_contr = math.log2(len(angle_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError("The number of controlled rotation gates is not" - " a non-negative power of 2.") - # Check if number of control qubits does correspond to the number of rotations - if num_contr != len(q_controls): - raise QiskitError("Number of controlled rotations does not correspond to" - " the number of control-qubits.") - return self.append(UCY(angle_list), [q_target] + q_controls, []) - +import warnings +# pylint: disable=unused-import +from qiskit.extensions.quantum_initializer.ucry import UCY, ucy -QuantumCircuit.ucy = ucy +warnings.warn('This module is deprecated. The UCRYGate/UCY can now be found in ucry.py', + category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/quantum_initializer/ucz.py b/qiskit/extensions/quantum_initializer/ucz.py index 132ef2967f43..b3ca60da5ee1 100644 --- a/qiskit/extensions/quantum_initializer/ucz.py +++ b/qiskit/extensions/quantum_initializer/ucz.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2019. +# (C) Copyright IBM 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -12,82 +12,14 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" -Implementation of the abstract class UCRot for uniformly controlled -(also called multiplexed) single-qubit rotations -around the Z-axes (i.e., uniformly controlled R_z rotations). -These gates can have several control qubits and a single target qubit. -If the k control qubits are in the state ket(i) (in the computational bases), -a single-qubit rotation R_z(a_i) is applied to the target qubit. -""" -import math - -from qiskit import QuantumRegister, QiskitError -from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.extensions.quantum_initializer.ucrot import UCRot - - -class UCZ(UCRot): - """ - Uniformly controlled rotations (also called multiplexed rotations). - The decomposition is based on - 'Synthesis of Quantum Logic Circuits' by V. Shende et al. - (https://arxiv.org/pdf/quant-ph/0406176.pdf) - - Input: - angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}] - """ - - def __init__(self, angle_list): - super().__init__(angle_list, "Z") +"""The uniformly controlled RZ gate. +This module is deprecated, see ucrz.py +""" -def ucz(self, angle_list, q_controls, q_target): - """Attach a uniformly controlled (also called multiplexed gates) Rz rotation gate to a circuit. - - The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. - - Args: - angle_list (list[numbers): list of (real) rotation angles [a_0,...,a_{2^k-1}] - q_controls (QuantumRegister|list[Qubit]): list of k control qubits - (or empty list if no controls). The control qubits are ordered according to their - significance in increasing order: For example if q_controls=[q[1],q[2]] - (with q = QuantumRegister(2)), the rotation Rz(a_0)is performed if q[1] and q[2] - are in the state zero, the rotation Rz(a_1) is performed if q[1] is in - the state one and q[2] is in the state zero, and so on - q_target (QuantumRegister|Qubit): target qubit, where we act on with - the single-qubit rotation gates - - Returns: - QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. - - Raises: - QiskitError: if the list number of control qubits does not correspond to - the provided number of single-qubit unitaries; if an input is of - the wrong type - """ - - if isinstance(q_controls, QuantumRegister): - q_controls = q_controls[:] - if isinstance(q_target, QuantumRegister): - q_target = q_target[:] - if len(q_target) == 1: - q_target = q_target[0] - else: - raise QiskitError("The target qubit is a QuantumRegister containing" - " more than one qubits.") - # Check if q_controls has type "list" - if not isinstance(angle_list, list): - raise QiskitError("The angles must be provided as a list.") - num_contr = math.log2(len(angle_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError("The number of controlled rotation gates is not a non-negative" - " power of 2.") - # Check if number of control qubits does correspond to the number of rotations - if num_contr != len(q_controls): - raise QiskitError("Number of controlled rotations does not correspond to the number" - " of control-qubits.") - return self.append(UCZ(angle_list), [q_target] + q_controls, []) - +import warnings +# pylint: disable=unused-import +from qiskit.extensions.quantum_initializer.ucrz import UCZ, ucz -QuantumCircuit.ucz = ucz +warnings.warn('This module is deprecated. The UCRZGate/UCZ can now be found in ucrz.py', + category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/standard/__init__.py b/qiskit/extensions/standard/__init__.py index 863d3821ad04..c5231f02a2e7 100644 --- a/qiskit/extensions/standard/__init__.py +++ b/qiskit/extensions/standard/__init__.py @@ -15,22 +15,35 @@ """Standard gates.""" from .barrier import Barrier from .h import HGate, CHGate -from .iden import IdGate +from .i import IGate from .ms import MSGate from .r import RGate from .rccx import RCCXGate from .rcccx import RCCCXGate -from .rx import RXGate, CrxGate +from .rx import RXGate, CRXGate from .rxx import RXXGate -from .ry import RYGate, CryGate -from .rz import RZGate, CrzGate +from .ry import RYGate, CRYGate +from .rz import RZGate, CRZGate from .rzz import RZZGate from .s import SGate, SdgGate -from .swap import SwapGate, FredkinGate +from .swap import SwapGate, CSwapGate from .t import TGate, TdgGate -from .u1 import U1Gate, Cu1Gate +from .u1 import U1Gate, CU1Gate from .u2 import U2Gate -from .u3 import U3Gate, Cu3Gate -from .x import XGate, CnotGate, ToffoliGate -from .y import YGate, CyGate -from .z import ZGate, CzGate +from .u3 import U3Gate, CU3Gate +from .x import XGate, CXGate, CCXGate +from .y import YGate, CYGate +from .z import ZGate, CZGate + +# deprecated gates, to be removed +from .i import IdGate +from .x import ToffoliGate +from .swap import FredkinGate +from .x import CnotGate +from .y import CyGate +from .z import CzGate +from .u1 import Cu1Gate +from .u3 import Cu3Gate +from .rx import CrxGate +from .ry import CryGate +from .rz import CrzGate diff --git a/qiskit/extensions/standard/ccx.py b/qiskit/extensions/standard/ccx.py index 685d996265af..816c11f36339 100644 --- a/qiskit/extensions/standard/ccx.py +++ b/qiskit/extensions/standard/ccx.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -Toffoli gate. Controlled-Controlled-X. +Controlled-Controlled-X (or Toffoli) Gate. """ import warnings diff --git a/qiskit/extensions/standard/ch.py b/qiskit/extensions/standard/ch.py index c6223843cac0..19bd446ef99e 100644 --- a/qiskit/extensions/standard/ch.py +++ b/qiskit/extensions/standard/ch.py @@ -13,11 +13,11 @@ # that they have been altered from the originals. """ -controlled-H gate. +Controlled-H gate. """ import warnings # pylint: disable=unused-import -from qiskit.extensions.standard.h import HGate, ch +from qiskit.extensions.standard.h import CHGate, ch warnings.warn('This module is deprecated. The CHGate can now be found in h.py', category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/standard/crx.py b/qiskit/extensions/standard/crx.py index cc8b4f373e87..5155fa62e524 100644 --- a/qiskit/extensions/standard/crx.py +++ b/qiskit/extensions/standard/crx.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-rx gate. +Controlled-rx gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/cry.py b/qiskit/extensions/standard/cry.py index 91d0ad400934..610fdee92010 100644 --- a/qiskit/extensions/standard/cry.py +++ b/qiskit/extensions/standard/cry.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-ry gate. +Controlled-ry gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/crz.py b/qiskit/extensions/standard/crz.py index 4b582257ffc5..001304f21802 100644 --- a/qiskit/extensions/standard/crz.py +++ b/qiskit/extensions/standard/crz.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-rz gate. +Controlled-rz gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/cswap.py b/qiskit/extensions/standard/cswap.py index 48e4d9ee7cd4..9a39550ee305 100644 --- a/qiskit/extensions/standard/cswap.py +++ b/qiskit/extensions/standard/cswap.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -Fredkin gate. Controlled-SWAP. +Controlled-Swap gate or Fredkin gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/cu1.py b/qiskit/extensions/standard/cu1.py index 0f671409ee82..68acbec0eb01 100644 --- a/qiskit/extensions/standard/cu1.py +++ b/qiskit/extensions/standard/cu1.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-u1 gate. +Controlled-u1 gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/cu3.py b/qiskit/extensions/standard/cu3.py index ecc5504b18a3..4b7d9fd1a08f 100644 --- a/qiskit/extensions/standard/cu3.py +++ b/qiskit/extensions/standard/cu3.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-u3 gate. +Controlled-u3 gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/cx.py b/qiskit/extensions/standard/cx.py index 0562ad745fb6..121647049828 100644 --- a/qiskit/extensions/standard/cx.py +++ b/qiskit/extensions/standard/cx.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-NOT gate. +Controlled-not gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/cy.py b/qiskit/extensions/standard/cy.py index 98157d4b7032..c2c2574990db 100644 --- a/qiskit/extensions/standard/cy.py +++ b/qiskit/extensions/standard/cy.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-Y gate. +Controlled-Y gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/cz.py b/qiskit/extensions/standard/cz.py index 37be8f528c2f..12ce11bbaee1 100644 --- a/qiskit/extensions/standard/cz.py +++ b/qiskit/extensions/standard/cz.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -controlled-Phase gate. +Controlled-Phase gate. """ import warnings # pylint: disable=unused-import diff --git a/qiskit/extensions/standard/h.py b/qiskit/extensions/standard/h.py index 002bca37b813..b6fc2460ee7b 100644 --- a/qiskit/extensions/standard/h.py +++ b/qiskit/extensions/standard/h.py @@ -20,6 +20,8 @@ from qiskit.circuit import QuantumCircuit from qiskit.circuit import QuantumRegister from qiskit.circuit.controlledgate import ControlledGate +from qiskit.extensions.standard.t import TGate, TdgGate +from qiskit.extensions.standard.s import SGate, SdgGate from qiskit.qasm import pi from qiskit.util import deprecate_arguments @@ -30,7 +32,7 @@ class HGate(Gate): def __init__(self, label=None): """Create new Hadamard gate.""" - super().__init__("h", 1, [], label=label) + super().__init__('h', 1, [], label=label) def _define(self): """ @@ -38,7 +40,7 @@ def _define(self): """ from qiskit.extensions.standard.u2 import U2Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (U2Gate(0, pi), [q[0]], []) ] @@ -46,19 +48,23 @@ def _define(self): definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return CHGate() - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CHGate() + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate.""" @@ -102,11 +108,11 @@ def h(self, qubit, *, q=None): # pylint: disable=invalid-name,unused-argument class CHGate(ControlledGate): - """controlled-H gate.""" + """The controlled-H gate.""" def __init__(self): """Create new CH gate.""" - super().__init__("ch", 2, [], num_ctrl_qubits=1) + super().__init__('ch', 2, [], num_ctrl_qubits=1) self.base_gate = HGate() def _define(self): @@ -121,16 +127,14 @@ def _define(self): sdg b; } """ - from qiskit.extensions.standard.s import SGate, SdgGate - from qiskit.extensions.standard.t import TGate, TdgGate - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ (SGate(), [q[1]], []), (HGate(), [q[1]], []), (TGate(), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (TdgGate(), [q[1]], []), (HGate(), [q[1]], []), (SdgGate(), [q[1]], []) @@ -144,11 +148,12 @@ def inverse(self): return CHGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the Ch gate.""" + """Return a numpy.array for the CH gate.""" return numpy.array([[1, 0, 0, 0], - [0, 1/numpy.sqrt(2), 0, 1/numpy.sqrt(2)], + [0, 1 / numpy.sqrt(2), 0, 1 / numpy.sqrt(2)], [0, 0, 1, 0], - [0, 1/numpy.sqrt(2), 0, -1/numpy.sqrt(2)]], dtype=complex) + [0, 1 / numpy.sqrt(2), 0, -1 / numpy.sqrt(2)]], + dtype=complex) @deprecate_arguments({'ctl': 'control_qubit', 'tgt': 'target_qubit'}) diff --git a/qiskit/extensions/standard/i.py b/qiskit/extensions/standard/i.py new file mode 100644 index 000000000000..e14a00edd2c6 --- /dev/null +++ b/qiskit/extensions/standard/i.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Identity gate. +""" +import warnings +import numpy +from qiskit.circuit import Gate +from qiskit.circuit import QuantumCircuit +from qiskit.util import deprecate_arguments + + +class IMeta(type): + """A metaclass to ensure that Id and I are of the same type. + + Can be removed when IdGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {IGate, IdGate} # pylint: disable=unidiomatic-typecheck + + +class IGate(Gate, metaclass=IMeta): + """Identity gate. + + Identity gate corresponds to a single-qubit gate wait cycle, + and should not be optimized or unrolled (it is an opaque gate). + """ + + def __init__(self, label=None): + """Create new Identity gate.""" + super().__init__('id', 1, [], label=label) + + def inverse(self): + """Invert this gate.""" + return IGate() # self-inverse + + def to_matrix(self): + """Return a numpy.array for the identity gate.""" + return numpy.array([[1, 0], + [0, 1]], dtype=complex) + + +class IdGate(IGate, metaclass=IMeta): + """The deprecated IGate class.""" + + def __init__(self): + warnings.warn('The class IdGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class IGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__() + + +@deprecate_arguments({'q': 'qubit'}) +def i(self, qubit, *, q=None): # pylint: disable=unused-argument + """Apply Identity to to a specified qubit (qubit). + + The Identity gate ensures that nothing is applied to a qubit for one unit + of gate time. It leaves the quantum states |0> and |1> unchanged. + The Identity gate should not be optimized or unrolled (it is an opaque gate). + + Examples: + + Circuit Representation: + + .. jupyter-execute:: + + from qiskit import QuantumCircuit + + circuit = QuantumCircuit(1) + circuit.id(0) # or circuit.i(0) + circuit.draw() + + Matrix Representation: + + .. jupyter-execute:: + + from qiskit.extensions.standard.i import IGate + IGate().to_matrix() + """ + return self.append(IGate(), [qubit], []) + + +@deprecate_arguments({'q': 'qubit'}) +def iden(self, qubit, *, q=None): # pylint: disable=unused-argument + """Deprecated identity gate.""" + warnings.warn('The QuantumCircuit.iden() method is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the QuantumCircuit.i() method instead.', + DeprecationWarning, stacklevel=2) + return self.append(IGate(), [qubit], []) + + +# support both i and id as methods of QuantumCircuit +QuantumCircuit.i = i +QuantumCircuit.id = i + +QuantumCircuit.iden = iden # deprecated, remove once IdGate is removed diff --git a/qiskit/extensions/standard/iden.py b/qiskit/extensions/standard/iden.py index bfcafe17d473..d40485931bd6 100644 --- a/qiskit/extensions/standard/iden.py +++ b/qiskit/extensions/standard/iden.py @@ -15,61 +15,9 @@ """ Identity gate. """ -import numpy -from qiskit.circuit import Gate -from qiskit.circuit import QuantumCircuit -from qiskit.util import deprecate_arguments +import warnings +# pylint: disable=unused-import +from qiskit.extensions.standard.i import IdGate, iden - -class IdGate(Gate): - """Identity gate. - - Identity gate corresponds to a single-qubit gate wait cycle, - and should not be optimized or unrolled (it is an opaque gate). - """ - - def __init__(self, label=None): - """Create new Identity gate.""" - super().__init__("id", 1, [], label=label) - - def inverse(self): - """Invert this gate.""" - return IdGate() # self-inverse - - def to_matrix(self): - """Return a Numpy.array for the Id gate.""" - return numpy.array([[1, 0], - [0, 1]], dtype=complex) - - -@deprecate_arguments({'q': 'qubit'}) -def iden(self, qubit, *, q=None): # pylint: disable=unused-argument - """Apply Identity to to a specified qubit (qubit). - - The Identity gate ensures that nothing is applied to a qubit for one unit - of gate time. It leaves the quantum states |0> and |1> unchanged. - The Identity gate should not be optimized or unrolled (it is an opaque gate). - - Examples: - - Circuit Representation: - - .. jupyter-execute:: - - from qiskit import QuantumCircuit - - circuit = QuantumCircuit(1) - circuit.iden(0) - circuit.draw() - - Matrix Representation: - - .. jupyter-execute:: - - from qiskit.extensions.standard.iden import IdGate - IdGate().to_matrix() - """ - return self.append(IdGate(), [qubit], []) - - -QuantumCircuit.iden = iden +warnings.warn('This module is deprecated. The IdGate can now be found in i.py', + category=DeprecationWarning, stacklevel=2) diff --git a/qiskit/extensions/standard/ms.py b/qiskit/extensions/standard/ms.py index 161704ce1f45..8c626dfc75b3 100755 --- a/qiskit/extensions/standard/ms.py +++ b/qiskit/extensions/standard/ms.py @@ -35,15 +35,15 @@ class MSGate(Gate): def __init__(self, n_qubits, theta): """Create new MS gate.""" - super().__init__("ms", n_qubits, [theta]) + super().__init__('ms', n_qubits, [theta]) def _define(self): from qiskit.extensions.standard.rxx import RXXGate definition = [] - q = QuantumRegister(self.num_qubits, "q") + q = QuantumRegister(self.num_qubits, 'q') rule = [] for i in range(self.num_qubits): - for j in range(i+1, self.num_qubits): + for j in range(i + 1, self.num_qubits): rule += [(RXXGate(self.params[0]), [q[i], q[j]], [])] for inst in rule: diff --git a/qiskit/extensions/standard/r.py b/qiskit/extensions/standard/r.py index 4e189d23b43b..191444f7c390 100644 --- a/qiskit/extensions/standard/r.py +++ b/qiskit/extensions/standard/r.py @@ -29,7 +29,7 @@ class RGate(Gate): def __init__(self, theta, phi): """Create new r single-qubit gate.""" - super().__init__("r", 1, [theta, phi]) + super().__init__('r', 1, [theta, phi]) def _define(self): """ @@ -37,7 +37,7 @@ def _define(self): """ from qiskit.extensions.standard.u3 import U3Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') theta = self.params[0] phi = self.params[1] rule = [ @@ -55,7 +55,7 @@ def inverse(self): return RGate(-self.params[0], self.params[1]) def to_matrix(self): - """Return a Numpy.array for the R gate.""" + """Return a numpy.array for the R gate.""" cos = math.cos(self.params[0] / 2) sin = math.sin(self.params[0] / 2) exp_m = numpy.exp(-1j * self.params[1]) diff --git a/qiskit/extensions/standard/rcccx.py b/qiskit/extensions/standard/rcccx.py index b91917a86639..1fb481e07167 100644 --- a/qiskit/extensions/standard/rcccx.py +++ b/qiskit/extensions/standard/rcccx.py @@ -19,7 +19,7 @@ from qiskit.circuit import QuantumCircuit, Gate, QuantumRegister from qiskit.extensions.standard.u1 import U1Gate from qiskit.extensions.standard.u2 import U2Gate -from qiskit.extensions.standard.x import CnotGate +from qiskit.extensions.standard.x import CXGate from qiskit.qasm import pi @@ -67,20 +67,20 @@ def _define(self): rule = [ (U2Gate(0, pi), [q[3]], []), # H gate (U1Gate(pi / 4), [q[3]], []), # T gate - (CnotGate(), [q[2], q[3]], []), + (CXGate(), [q[2], q[3]], []), (U1Gate(-pi / 4), [q[3]], []), # inverse T gate (U2Gate(0, pi), [q[3]], []), - (CnotGate(), [q[0], q[3]], []), + (CXGate(), [q[0], q[3]], []), (U1Gate(pi / 4), [q[3]], []), - (CnotGate(), [q[1], q[3]], []), + (CXGate(), [q[1], q[3]], []), (U1Gate(-pi / 4), [q[3]], []), - (CnotGate(), [q[0], q[3]], []), + (CXGate(), [q[0], q[3]], []), (U1Gate(pi / 4), [q[3]], []), - (CnotGate(), [q[1], q[3]], []), + (CXGate(), [q[1], q[3]], []), (U1Gate(-pi / 4), [q[3]], []), (U2Gate(0, pi), [q[3]], []), (U1Gate(pi / 4), [q[3]], []), - (CnotGate(), [q[2], q[3]], []), + (CXGate(), [q[2], q[3]], []), (U1Gate(-pi / 4), [q[3]], []), (U2Gate(0, pi), [q[3]], []), ] diff --git a/qiskit/extensions/standard/rccx.py b/qiskit/extensions/standard/rccx.py index 1ee5f58c7364..7835b80c138a 100644 --- a/qiskit/extensions/standard/rccx.py +++ b/qiskit/extensions/standard/rccx.py @@ -19,7 +19,7 @@ from qiskit.circuit import QuantumCircuit, Gate, QuantumRegister from qiskit.extensions.standard.u1 import U1Gate from qiskit.extensions.standard.u2 import U2Gate -from qiskit.extensions.standard.x import CnotGate +from qiskit.extensions.standard.x import CXGate from qiskit.qasm import pi @@ -59,11 +59,11 @@ def _define(self): rule = [ (U2Gate(0, pi), [q[2]], []), # H gate (U1Gate(pi / 4), [q[2]], []), # T gate - (CnotGate(), [q[1], q[2]], []), + (CXGate(), [q[1], q[2]], []), (U1Gate(-pi / 4), [q[2]], []), # inverse T gate - (CnotGate(), [q[0], q[2]], []), + (CXGate(), [q[0], q[2]], []), (U1Gate(pi / 4), [q[2]], []), - (CnotGate(), [q[1], q[2]], []), + (CXGate(), [q[1], q[2]], []), (U1Gate(-pi / 4), [q[2]], []), # inverse T gate (U2Gate(0, pi), [q[2]], []), # H gate ] diff --git a/qiskit/extensions/standard/rx.py b/qiskit/extensions/standard/rx.py index c14335e654a8..801bc27eef75 100644 --- a/qiskit/extensions/standard/rx.py +++ b/qiskit/extensions/standard/rx.py @@ -17,8 +17,8 @@ """ import math import numpy -from qiskit.circuit import ControlledGate from qiskit.circuit import Gate +from qiskit.circuit import ControlledGate from qiskit.circuit import QuantumCircuit from qiskit.circuit import QuantumRegister from qiskit.qasm import pi @@ -26,11 +26,11 @@ class RXGate(Gate): - """rotation around the x-axis.""" + """The rotation around the x-axis.""" def __init__(self, theta): """Create new rx single qubit gate.""" - super().__init__("rx", 1, [theta]) + super().__init__('rx', 1, [theta]) def _define(self): """ @@ -38,7 +38,7 @@ def _define(self): """ from qiskit.extensions.standard.r import RGate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (RGate(self.params[0], 0), [q[0]], []) ] @@ -46,19 +46,23 @@ def _define(self): definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return CrxGate(self.params[0]) - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CRXGate(self.params[0]) + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate. @@ -68,7 +72,7 @@ def inverse(self): return RXGate(-self.params[0]) def to_matrix(self): - """Return a Numpy.array for the RX gate.""" + """Return a numpy.array for the RX gate.""" cos = math.cos(self.params[0] / 2) sin = math.sin(self.params[0] / 2) return numpy.array([[cos, -1j * sin], @@ -108,8 +112,18 @@ def rx(self, theta, qubit, *, q=None): # pylint: disable=invalid-name,unused-ar QuantumCircuit.rx = rx -class CrxGate(ControlledGate): - """controlled-rx gate.""" +class CRXMeta(type): + """A metaclass to ensure that CrxGate and CRXGate are of the same type. + + Can be removed when CrxGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CRXGate, CrxGate} # pylint: disable=unidiomatic-typecheck + + +class CRXGate(ControlledGate, metaclass=CRXMeta): + """The controlled-rx gate.""" def __init__(self, theta): """Create new crx gate.""" @@ -128,14 +142,14 @@ def _define(self): """ from qiskit.extensions.standard.u1 import U1Gate from qiskit.extensions.standard.u3 import U3Gate - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] q = QuantumRegister(2, 'q') rule = [ (U1Gate(pi / 2), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U3Gate(-self.params[0] / 2, 0, 0), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U3Gate(self.params[0] / 2, -pi / 2, 0), [q[1]], []) ] for inst in rule: @@ -144,7 +158,19 @@ def _define(self): def inverse(self): """Invert this gate.""" - return CrxGate(-self.params[0]) + return CRXGate(-self.params[0]) + + +class CrxGate(CRXGate, metaclass=CRXMeta): + """The deprecated CRXGate class.""" + + def __init__(self, theta): + import warnings + warnings.warn('The class CrxGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CRXGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(theta) @deprecate_arguments({'ctl': 'control_qubit', @@ -152,7 +178,7 @@ def inverse(self): def crx(self, theta, control_qubit, target_qubit, *, ctl=None, tgt=None): # pylint: disable=unused-argument """Apply crx from ctl to tgt with angle theta.""" - return self.append(CrxGate(theta), [control_qubit, target_qubit], []) + return self.append(CRXGate(theta), [control_qubit, target_qubit], []) QuantumCircuit.crx = crx diff --git a/qiskit/extensions/standard/rxx.py b/qiskit/extensions/standard/rxx.py index 5c5dcf726956..853eada75cb3 100644 --- a/qiskit/extensions/standard/rxx.py +++ b/qiskit/extensions/standard/rxx.py @@ -29,24 +29,24 @@ class RXXGate(Gate): def __init__(self, theta): """Create new rxx gate.""" - super().__init__("rxx", 2, [theta]) + super().__init__('rxx', 2, [theta]) def _define(self): """Calculate a subcircuit that implements this unitary.""" - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate from qiskit.extensions.standard.u1 import U1Gate from qiskit.extensions.standard.u2 import U2Gate from qiskit.extensions.standard.u3 import U3Gate from qiskit.extensions.standard.h import HGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') theta = self.params[0] rule = [ (U3Gate(np.pi / 2, theta, 0), [q[0]], []), (HGate(), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U1Gate(-theta), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (HGate(), [q[1]], []), (U2Gate(-np.pi, np.pi - theta), [q[0]], []), ] @@ -76,5 +76,4 @@ def rxx(self, theta, qubit1, qubit2): return self.append(RXXGate(theta), [qubit1, qubit2], []) -# Add to QuantumCircuit class QuantumCircuit.rxx = rxx diff --git a/qiskit/extensions/standard/ry.py b/qiskit/extensions/standard/ry.py index f9d86892e47c..a76668f132f1 100644 --- a/qiskit/extensions/standard/ry.py +++ b/qiskit/extensions/standard/ry.py @@ -26,11 +26,11 @@ class RYGate(Gate): - """rotation around the y-axis.""" + """The rotation around the y-axis.""" def __init__(self, theta): - """Create new ry single qubit gate.""" - super().__init__("ry", 1, [theta]) + """Create new RY single qubit gate.""" + super().__init__('ry', 1, [theta]) def _define(self): """ @@ -38,27 +38,31 @@ def _define(self): """ from qiskit.extensions.standard.r import RGate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ - (RGate(self.params[0], pi/2), [q[0]], []) + (RGate(self.params[0], pi / 2), [q[0]], []) ] for inst in rule: definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return CryGate(self.params[0]) - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CRYGate(self.params[0]) + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate. @@ -68,7 +72,7 @@ def inverse(self): return RYGate(-self.params[0]) def to_matrix(self): - """Return a Numpy.array for the RY gate.""" + """Return a numpy.array for the RY gate.""" cos = math.cos(self.params[0] / 2) sin = math.sin(self.params[0] / 2) return numpy.array([[cos, -sin], @@ -108,12 +112,22 @@ def ry(self, theta, qubit, *, q=None): # pylint: disable=invalid-name,unused-ar QuantumCircuit.ry = ry -class CryGate(ControlledGate): - """controlled-ry gate.""" +class CRYMeta(type): + """A metaclass to ensure that CryGate and CRYGate are of the same type. + + Can be removed when CryGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CRYGate, CryGate} # pylint: disable=unidiomatic-typecheck + + +class CRYGate(ControlledGate, metaclass=CRYMeta): + """The controlled-ry gate.""" def __init__(self, theta): """Create new cry gate.""" - super().__init__("cry", 2, [theta], num_ctrl_qubits=1) + super().__init__('cry', 2, [theta], num_ctrl_qubits=1) self.base_gate = RYGate(theta) def _define(self): @@ -124,15 +138,15 @@ def _define(self): } """ - from qiskit.extensions.standard.x import CnotGate from qiskit.extensions.standard.u3 import U3Gate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ (U3Gate(self.params[0] / 2, 0, 0), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U3Gate(-self.params[0] / 2, 0, 0), [q[1]], []), - (CnotGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []) ] for inst in rule: definition.append(inst) @@ -140,7 +154,19 @@ def _define(self): def inverse(self): """Invert this gate.""" - return CryGate(-self.params[0]) + return CRYGate(-self.params[0]) + + +class CryGate(CRYGate, metaclass=CRYMeta): + """The deprecated CRYGate class.""" + + def __init__(self, theta): + import warnings + warnings.warn('The class CryGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CRYGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(theta) @deprecate_arguments({'ctl': 'control_qubit', @@ -148,7 +174,7 @@ def inverse(self): def cry(self, theta, control_qubit, target_qubit, *, ctl=None, tgt=None): # pylint: disable=unused-argument """Apply cry from ctl to tgt with angle theta.""" - return self.append(CryGate(theta), [control_qubit, target_qubit], []) + return self.append(CRYGate(theta), [control_qubit, target_qubit], []) QuantumCircuit.cry = cry diff --git a/qiskit/extensions/standard/rz.py b/qiskit/extensions/standard/rz.py index f215c276a9a5..74bdb120cab6 100644 --- a/qiskit/extensions/standard/rz.py +++ b/qiskit/extensions/standard/rz.py @@ -23,11 +23,11 @@ class RZGate(Gate): - """rotation around the z-axis.""" + """The rotation around the z-axis.""" def __init__(self, phi): - """Create new rz single qubit gate.""" - super().__init__("rz", 1, [phi]) + """Create new RZ single qubit gate.""" + super().__init__('rz', 1, [phi]) def _define(self): """ @@ -35,7 +35,7 @@ def _define(self): """ from qiskit.extensions.standard.u1 import U1Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (U1Gate(self.params[0]), [q[0]], []) ] @@ -43,19 +43,23 @@ def _define(self): definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return CrzGate(self.params[0]) - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CRZGate(self.params[0]) + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate. @@ -90,12 +94,22 @@ def rz(self, phi, qubit, *, q=None): # pylint: disable=invalid-name,unused-argu QuantumCircuit.rz = rz -class CrzGate(ControlledGate): - """controlled-rz gate.""" +class CRZMeta(type): + """A metaclass to ensure that CrzGate and CRZGate are of the same type. + + Can be removed when CrzGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CRZGate, CrzGate} # pylint: disable=unidiomatic-typecheck + + +class CRZGate(ControlledGate, metaclass=CRZMeta): + """The controlled-rz gate.""" def __init__(self, theta): """Create new crz gate.""" - super().__init__("crz", 2, [theta], num_ctrl_qubits=1) + super().__init__('crz', 2, [theta], num_ctrl_qubits=1) self.base_gate = RZGate(theta) def _define(self): @@ -105,15 +119,15 @@ def _define(self): u1(-lambda/2) b; cx a,b; } """ - from qiskit.extensions.standard.x import CnotGate from qiskit.extensions.standard.u1 import U1Gate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ (U1Gate(self.params[0] / 2), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U1Gate(-self.params[0] / 2), [q[1]], []), - (CnotGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []) ] for inst in rule: definition.append(inst) @@ -121,7 +135,19 @@ def _define(self): def inverse(self): """Invert this gate.""" - return CrzGate(-self.params[0]) + return CRZGate(-self.params[0]) + + +class CrzGate(CRZGate, metaclass=CRZMeta): + """The deprecated CRZGate class.""" + + def __init__(self, theta): + import warnings + warnings.warn('The class CrzGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CRZGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(theta) @deprecate_arguments({'ctl': 'control_qubit', 'tgt': 'target_qubit'}) @@ -144,7 +170,7 @@ def crz(self, theta, control_qubit, target_qubit, circuit.crz(theta,0,1) circuit.draw() """ - return self.append(CrzGate(theta), [control_qubit, target_qubit], []) + return self.append(CRZGate(theta), [control_qubit, target_qubit], []) QuantumCircuit.crz = crz diff --git a/qiskit/extensions/standard/rzz.py b/qiskit/extensions/standard/rzz.py index 717fb264dc96..00f7e322754a 100644 --- a/qiskit/extensions/standard/rzz.py +++ b/qiskit/extensions/standard/rzz.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -two-qubit ZZ-rotation gate. +Two-qubit ZZ-rotation gate. """ from qiskit.circuit import Gate from qiskit.circuit import QuantumCircuit @@ -21,24 +21,24 @@ class RZZGate(Gate): - """Two-qubit ZZ-rotation gate.""" + """The two-qubit ZZ-rotation gate.""" def __init__(self, theta): """Create new rzz gate.""" - super().__init__("rzz", 2, [theta]) + super().__init__('rzz', 2, [theta]) def _define(self): """ gate rzz(theta) a, b { cx a, b; u1(theta) b; cx a, b; } """ from qiskit.extensions.standard.u1 import U1Gate - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U1Gate(self.params[0]), [q[1]], []), - (CnotGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []) ] for inst in rule: definition.append(inst) diff --git a/qiskit/extensions/standard/s.py b/qiskit/extensions/standard/s.py index cea199c41a22..7dc61269fe37 100644 --- a/qiskit/extensions/standard/s.py +++ b/qiskit/extensions/standard/s.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -S=diag(1,i) Clifford phase gate or its inverse. +The S gate (Clifford phase gate) and its inverse. """ import numpy from qiskit.circuit import Gate @@ -24,11 +24,11 @@ class SGate(Gate): - """S=diag(1,i) Clifford phase gate.""" + """The S gate, also called Clifford phase gate.""" def __init__(self, label=None): - """Create new S gate.""" - super().__init__("s", 1, [], label=label) + """Create a new S gate.""" + super().__init__('s', 1, [], label=label) def _define(self): """ @@ -36,7 +36,7 @@ def _define(self): """ from qiskit.extensions.standard.u1 import U1Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (U1Gate(pi / 2), [q[0]], []) ] @@ -49,7 +49,7 @@ def inverse(self): return SdgGate() def to_matrix(self): - """Return a Numpy.array for the S gate.""" + """Return a numpy.array for the S gate.""" return numpy.array([[1, 0], [0, 1j]], dtype=complex) @@ -58,8 +58,8 @@ class SdgGate(Gate): """Sdg=diag(1,-i) Clifford adjoint phase gate.""" def __init__(self, label=None): - """Create new Sdg gate.""" - super().__init__("sdg", 1, [], label=label) + """Create a new Sdg gate.""" + super().__init__('sdg', 1, [], label=label) def _define(self): """ @@ -67,7 +67,7 @@ def _define(self): """ from qiskit.extensions.standard.u1 import U1Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (U1Gate(-pi / 2), [q[0]], []) ] @@ -80,7 +80,7 @@ def inverse(self): return SGate() def to_matrix(self): - """Return a Numpy.array for the Sdg gate.""" + """Return a numpy.array for the Sdg gate.""" return numpy.array([[1, 0], [0, -1j]], dtype=complex) diff --git a/qiskit/extensions/standard/swap.py b/qiskit/extensions/standard/swap.py index 1cfce87a4840..5512eb4e82a8 100644 --- a/qiskit/extensions/standard/swap.py +++ b/qiskit/extensions/standard/swap.py @@ -13,7 +13,7 @@ # that they have been altered from the originals. """ -SWAP gate. +Swap gate. """ import numpy from qiskit.circuit import ControlledGate @@ -28,44 +28,48 @@ class SwapGate(Gate): def __init__(self): """Create new SWAP gate.""" - super().__init__("swap", 2, []) + super().__init__('swap', 2, []) def _define(self): """ gate swap a,b { cx a,b; cx b,a; cx a,b; } """ - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ - (CnotGate(), [q[0], q[1]], []), - (CnotGate(), [q[1], q[0]], []), - (CnotGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []), + (CXGate(), [q[1], q[0]], []), + (CXGate(), [q[0], q[1]], []) ] for inst in rule: definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return FredkinGate() - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CSwapGate() + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate.""" return SwapGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the Swap gate.""" + """Return a numpy.array for the SWAP gate.""" return numpy.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], @@ -101,12 +105,22 @@ def swap(self, qubit1, qubit2): QuantumCircuit.swap = swap -class FredkinGate(ControlledGate): - """Fredkin gate.""" +class CSwapMeta(type): + """A Metaclass to ensure that CSwapGate and FredkinGate are of the same type. + + Can be removed when FredkinGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CSwapGate, FredkinGate} # pylint: disable=unidiomatic-typecheck + + +class CSwapGate(ControlledGate, metaclass=CSwapMeta): + """The controlled-swap gate, also called Fredkin gate.""" def __init__(self): - """Create new Fredkin gate.""" - super().__init__("cswap", 3, [], num_ctrl_qubits=1) + """Create new CSWAP gate.""" + super().__init__('cswap', 3, [], num_ctrl_qubits=1) self.base_gate = SwapGate() def _define(self): @@ -117,13 +131,14 @@ def _define(self): cx c,b; } """ - from qiskit.extensions.standard.x import CnotGate, ToffoliGate + from qiskit.extensions.standard.x import CXGate + from qiskit.extensions.standard.x import CCXGate definition = [] - q = QuantumRegister(3, "q") + q = QuantumRegister(3, 'q') rule = [ - (CnotGate(), [q[2], q[1]], []), - (ToffoliGate(), [q[0], q[1], q[2]], []), - (CnotGate(), [q[2], q[1]], []) + (CXGate(), [q[2], q[1]], []), + (CCXGate(), [q[0], q[1], q[2]], []), + (CXGate(), [q[2], q[1]], []) ] for inst in rule: definition.append(inst) @@ -131,10 +146,10 @@ def _define(self): def inverse(self): """Invert this gate.""" - return FredkinGate() # self-inverse + return CSwapGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the Fredkin (CSWAP) gate.""" + """Return a numpy.array for the Fredkin (CSWAP) gate.""" return numpy.array([[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], @@ -145,6 +160,18 @@ def to_matrix(self): [0, 0, 0, 0, 0, 0, 0, 1]], dtype=complex) +class FredkinGate(CSwapGate, metaclass=CSwapMeta): + """The deprecated CSwapGate class.""" + + def __init__(self): + import warnings + warnings.warn('The class FredkinGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CSwapGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__() + + @deprecate_arguments({'ctl': 'control_qubit', 'tgt1': 'target_qubit1', 'tgt2': 'target_qubit2'}) @@ -171,13 +198,12 @@ def cswap(self, control_qubit, target_qubit1, target_qubit2, .. jupyter-execute:: - from qiskit.extensions.standard.swap import FredkinGate - FredkinGate().to_matrix() + from qiskit.extensions.standard.swap import CSwapGate + CSwapGate().to_matrix() """ - return self.append(FredkinGate(), - [control_qubit, target_qubit1, target_qubit2], - []) + return self.append(CSwapGate(), [control_qubit, target_qubit1, target_qubit2], []) +# support both cswap and fredkin as methods of QuantumCircuit QuantumCircuit.cswap = cswap QuantumCircuit.fredkin = cswap diff --git a/qiskit/extensions/standard/t.py b/qiskit/extensions/standard/t.py index dd05b7ca5d92..ff49cb7ddcae 100644 --- a/qiskit/extensions/standard/t.py +++ b/qiskit/extensions/standard/t.py @@ -28,7 +28,7 @@ class TGate(Gate): def __init__(self, label=None): """Create new T gate.""" - super().__init__("t", 1, [], label=label) + super().__init__('t', 1, [], label=label) def _define(self): """ @@ -36,9 +36,9 @@ def _define(self): """ from qiskit.extensions.standard.u1 import U1Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ - (U1Gate(pi/4), [q[0]], []) + (U1Gate(pi / 4), [q[0]], []) ] for inst in rule: definition.append(inst) @@ -49,27 +49,27 @@ def inverse(self): return TdgGate() def to_matrix(self): - """Return a Numpy.array for the S gate.""" + """Return a numpy.array for the T gate.""" return numpy.array([[1, 0], - [0, (1+1j) / numpy.sqrt(2)]], dtype=complex) + [0, (1 + 1j) / numpy.sqrt(2)]], dtype=complex) class TdgGate(Gate): - """T Gate: -pi/4 rotation around Z axis.""" + """Tdg Gate: -pi/4 rotation around Z axis.""" def __init__(self, label=None): - """Create new Tdg gate.""" - super().__init__("tdg", 1, [], label=label) + """Create a new Tdg gate.""" + super().__init__('tdg', 1, [], label=label) def _define(self): """ - gate t a { u1(pi/4) a; } + gate tdg a { u1(pi/4) a; } """ from qiskit.extensions.standard.u1 import U1Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ - (U1Gate(-pi/4), [q[0]], []) + (U1Gate(-pi / 4), [q[0]], []) ] for inst in rule: definition.append(inst) @@ -80,9 +80,9 @@ def inverse(self): return TGate() def to_matrix(self): - """Return a Numpy.array for the S gate.""" + """Return a numpy.array for the inverse T gate.""" return numpy.array([[1, 0], - [0, (1-1j) / numpy.sqrt(2)]], dtype=complex) + [0, (1 - 1j) / numpy.sqrt(2)]], dtype=complex) @deprecate_arguments({'q': 'qubit'}) diff --git a/qiskit/extensions/standard/u1.py b/qiskit/extensions/standard/u1.py index 014d24d90718..c1eb41574139 100644 --- a/qiskit/extensions/standard/u1.py +++ b/qiskit/extensions/standard/u1.py @@ -29,12 +29,12 @@ class U1Gate(Gate): def __init__(self, theta, label=None): """Create new diagonal single-qubit gate.""" - super().__init__("u1", 1, [theta], label=label) + super().__init__('u1', 1, [theta], label=label) def _define(self): from qiskit.extensions.standard.u3 import U3Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (U3Gate(0, 0, self.params[0]), [q[0]], []) ] @@ -42,26 +42,30 @@ def _define(self): definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return Cu1Gate(*self.params) - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CU1Gate(*self.params) + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate.""" return U1Gate(-self.params[0]) def to_matrix(self): - """Return a Numpy.array for the U1 gate.""" + """Return a numpy.array for the U1 gate.""" lam = self.params[0] lam = float(lam) return numpy.array([[1, 0], [0, numpy.exp(1j * lam)]], dtype=complex) @@ -99,12 +103,22 @@ def u1(self, theta, qubit, *, q=None): # pylint: disable=invalid-name,unused-ar QuantumCircuit.u1 = u1 -class Cu1Gate(ControlledGate): - """controlled-u1 gate.""" +class CU1Meta(type): + """A metaclass to ensure that Cu1Gate and CU1Gate are of the same type. + + Can be removed when Cu1Gate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CU1Gate, Cu1Gate} # pylint: disable=unidiomatic-typecheck + + +class CU1Gate(ControlledGate, metaclass=CU1Meta): + """The controlled-u1 gate.""" def __init__(self, theta): """Create new cu1 gate.""" - super().__init__("cu1", 2, [theta], num_ctrl_qubits=1) + super().__init__('cu1', 2, [theta], num_ctrl_qubits=1) self.base_gate = U1Gate(theta) def _define(self): @@ -115,14 +129,14 @@ def _define(self): u1(lambda/2) b; } """ - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ (U1Gate(self.params[0] / 2), [q[0]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U1Gate(-self.params[0] / 2), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U1Gate(self.params[0] / 2), [q[1]], []) ] for inst in rule: @@ -131,7 +145,19 @@ def _define(self): def inverse(self): """Invert this gate.""" - return Cu1Gate(-self.params[0]) + return CU1Gate(-self.params[0]) + + +class Cu1Gate(CU1Gate, metaclass=CU1Meta): + """The deprecated CU1Gate class.""" + + def __init__(self, theta): + import warnings + warnings.warn('The class Cu1Gate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CU1Gate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(theta) @deprecate_arguments({'ctl': 'control_qubit', @@ -155,7 +181,7 @@ def cu1(self, theta, control_qubit, target_qubit, circuit.cu1(theta,0,1) circuit.draw() """ - return self.append(Cu1Gate(theta), [control_qubit, target_qubit], []) + return self.append(CU1Gate(theta), [control_qubit, target_qubit], []) QuantumCircuit.cu1 = cu1 diff --git a/qiskit/extensions/standard/u2.py b/qiskit/extensions/standard/u2.py index 50fcf4b35c13..f73c0bae41c8 100644 --- a/qiskit/extensions/standard/u2.py +++ b/qiskit/extensions/standard/u2.py @@ -28,12 +28,12 @@ class U2Gate(Gate): def __init__(self, phi, lam, label=None): """Create new one-pulse single-qubit gate.""" - super().__init__("u2", 1, [phi, lam], label=label) + super().__init__('u2', 1, [phi, lam], label=label) def _define(self): from qiskit.extensions.standard.u3 import U3Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [(U3Gate(pi / 2, self.params[0], self.params[1]), [q[0]], [])] for inst in rule: definition.append(inst) @@ -51,12 +51,16 @@ def to_matrix(self): isqrt2 = 1 / numpy.sqrt(2) phi, lam = self.params phi, lam = float(phi), float(lam) - return numpy.array([[isqrt2, -numpy.exp(1j * lam) * isqrt2], - [ - numpy.exp(1j * phi) * isqrt2, - numpy.exp(1j * (phi + lam)) * isqrt2 - ]], - dtype=complex) + return numpy.array([ + [ + isqrt2, + -numpy.exp(1j * lam) * isqrt2 + ], + [ + numpy.exp(1j * phi) * isqrt2, + numpy.exp(1j * (phi + lam)) * isqrt2 + ] + ], dtype=complex) @deprecate_arguments({'q': 'qubit'}) diff --git a/qiskit/extensions/standard/u3.py b/qiskit/extensions/standard/u3.py index 3a4449a69b28..ccba7e83f817 100644 --- a/qiskit/extensions/standard/u3.py +++ b/qiskit/extensions/standard/u3.py @@ -30,7 +30,7 @@ class U3Gate(Gate): def __init__(self, theta, phi, lam, label=None): """Create new two-pulse single qubit gate.""" - super().__init__("u3", 1, [theta, phi, lam], label=label) + super().__init__('u3', 1, [theta, phi, lam], label=label) def inverse(self): """Invert this gate. @@ -39,34 +39,38 @@ def inverse(self): """ return U3Gate(-self.params[0], -self.params[2], -self.params[1]) - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return Cu3Gate(*self.params) - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CU3Gate(*self.params) + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def to_matrix(self): """Return a Numpy.array for the U3 gate.""" theta, phi, lam = self.params theta, phi, lam = float(theta), float(phi), float(lam) - return numpy.array( - [[ + return numpy.array([ + [ numpy.cos(theta / 2), -numpy.exp(1j * lam) * numpy.sin(theta / 2) ], - [ - numpy.exp(1j * phi) * numpy.sin(theta / 2), - numpy.exp(1j * (phi + lam)) * numpy.cos(theta / 2) - ]], - dtype=complex) + [ + numpy.exp(1j * phi) * numpy.sin(theta / 2), + numpy.exp(1j * (phi + lam)) * numpy.cos(theta / 2) + ] + ], dtype=complex) @deprecate_arguments({'q': 'qubit'}) @@ -103,12 +107,22 @@ def u3(self, theta, phi, lam, qubit, *, q=None): # pylint: disable=invalid-name QuantumCircuit.u3 = u3 -class Cu3Gate(ControlledGate): - """controlled-u3 gate.""" +class CU3Meta(type): + """A metaclass to ensure that Cu3Gate and CU3Gate are of the same type. + + Can be removed when Cu3Gate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CU3Gate, Cu3Gate} # pylint: disable=unidiomatic-typecheck + + +class CU3Gate(ControlledGate, metaclass=CU3Meta): + """The controlled-u3 gate.""" def __init__(self, theta, phi, lam): """Create new cu3 gate.""" - super().__init__("cu3", 2, [theta, phi, lam], num_ctrl_qubits=1) + super().__init__('cu3', 2, [theta, phi, lam], num_ctrl_qubits=1) self.base_gate = U3Gate(theta, phi, lam) def _define(self): @@ -123,15 +137,15 @@ def _define(self): } """ from qiskit.extensions.standard.u1 import U1Gate - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ (U1Gate((self.params[2] + self.params[1]) / 2), [q[0]], []), (U1Gate((self.params[2] - self.params[1]) / 2), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U3Gate(-self.params[0] / 2, 0, -(self.params[1] + self.params[2]) / 2), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (U3Gate(self.params[0] / 2, self.params[1], 0), [q[1]], []) ] for inst in rule: @@ -140,7 +154,19 @@ def _define(self): def inverse(self): """Invert this gate.""" - return Cu3Gate(-self.params[0], -self.params[2], -self.params[1]) + return CU3Gate(-self.params[0], -self.params[2], -self.params[1]) + + +class Cu3Gate(CU3Gate, metaclass=CU3Meta): + """The deprecated CU3Gate class.""" + + def __init__(self, theta, phi, lam): + import warnings + warnings.warn('The class Cu3Gate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CU3Gate instead.', + DeprecationWarning, stacklevel=2) + super().__init__(theta, phi, lam) @deprecate_arguments({'ctl': 'control_qubit', @@ -167,9 +193,7 @@ def cu3(self, theta, phi, lam, control_qubit, target_qubit, circuit.cu3(theta,phi,lam,0,1) circuit.draw() """ - return self.append(Cu3Gate(theta, phi, lam), - [control_qubit, target_qubit], - []) + return self.append(CU3Gate(theta, phi, lam), [control_qubit, target_qubit], []) QuantumCircuit.cu3 = cu3 diff --git a/qiskit/extensions/standard/x.py b/qiskit/extensions/standard/x.py index 93457b083fb3..0f67bcc61ee7 100644 --- a/qiskit/extensions/standard/x.py +++ b/qiskit/extensions/standard/x.py @@ -20,6 +20,9 @@ from qiskit.circuit import Gate from qiskit.circuit import QuantumCircuit from qiskit.circuit import QuantumRegister +from qiskit.extensions.standard.h import HGate +from qiskit.extensions.standard.t import TGate +from qiskit.extensions.standard.t import TdgGate from qiskit.qasm import pi from qiskit.util import deprecate_arguments @@ -29,7 +32,7 @@ class XGate(Gate): def __init__(self, label=None): """Create new X gate.""" - super().__init__("x", 1, [], label=label) + super().__init__('x', 1, [], label=label) def _define(self): """ @@ -39,7 +42,7 @@ def _define(self): """ from qiskit.extensions.standard.u3 import U3Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (U3Gate(pi, 0, pi), [q[0]], []) ] @@ -47,28 +50,32 @@ def _define(self): definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return CnotGate() - elif num_ctrl_qubits == 2: - return ToffoliGate() - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CXGate() + elif num_ctrl_qubits == 2: + return CCXGate() + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate.""" return XGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the X gate.""" + """Return a numpy.array for the X gate.""" return numpy.array([[0, 1], [1, 0]], dtype=complex) @@ -106,40 +113,66 @@ def x(self, qubit, *, q=None): # pylint: disable=unused-argument QuantumCircuit.x = x -class CnotGate(ControlledGate): - """controlled-NOT gate.""" +class CXMeta(type): + """A metaclass to ensure that CnotGate and CXGate are of the same type. + + Can be removed when CnotGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CnotGate, CXGate} # pylint: disable=unidiomatic-typecheck + + +class CXGate(ControlledGate, metaclass=CXMeta): + """The controlled-X gate.""" def __init__(self): - """Create new CNOT gate.""" - super().__init__("cx", 2, [], num_ctrl_qubits=1) + """Create new cx gate.""" + super().__init__('cx', 2, [], num_ctrl_qubits=1) self.base_gate = XGate() - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return ToffoliGate() - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CCXGate() + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate.""" - return CnotGate() # self-inverse + return CXGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the Cx gate.""" + """Return a numpy.array for the CX gate.""" return numpy.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex) +class CnotGate(CXGate, metaclass=CXMeta): + """The deprecated CXGate class.""" + + def __init__(self): + import warnings + warnings.warn('The class CnotGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CXGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__() + + @deprecate_arguments({'ctl': 'control_qubit', 'tgt': 'target_qubit'}) def cx(self, control_qubit, target_qubit, # pylint: disable=invalid-name @@ -166,22 +199,33 @@ def cx(self, control_qubit, target_qubit, # pylint: disable=invalid-name .. jupyter-execute:: - from qiskit.extensions.standard.cx import CnotGate - CnotGate().to_matrix() + from qiskit.extensions.standard.x import CXGate + CXGate().to_matrix() """ - return self.append(CnotGate(), [control_qubit, target_qubit], []) + return self.append(CXGate(), [control_qubit, target_qubit], []) +# support both cx and cnot in QuantumCircuits QuantumCircuit.cx = cx QuantumCircuit.cnot = cx -class ToffoliGate(ControlledGate): - """Toffoli gate.""" +class CCXMeta(type): + """A metaclass to ensure that CCXGate and ToffoliGate are of the same type. + + Can be removed when ToffoliGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CCXGate, ToffoliGate} # pylint: disable=unidiomatic-typecheck + + +class CCXGate(ControlledGate, metaclass=CCXMeta): + """The double-controlled-not gate, also called Toffoli gate.""" def __init__(self): - """Create new Toffoli gate.""" - super().__init__("ccx", 3, [], num_ctrl_qubits=2) + """Create new CCX gate.""" + super().__init__('ccx', 3, [], num_ctrl_qubits=2) self.base_gate = XGate() def _define(self): @@ -193,27 +237,24 @@ def _define(self): t b; t c; h c; cx a,b; t a; tdg b; cx a,b;} """ - from qiskit.extensions.standard.h import HGate - from qiskit.extensions.standard.t import TGate - from qiskit.extensions.standard.t import TdgGate definition = [] - q = QuantumRegister(3, "q") + q = QuantumRegister(3, 'q') rule = [ (HGate(), [q[2]], []), - (CnotGate(), [q[1], q[2]], []), + (CXGate(), [q[1], q[2]], []), (TdgGate(), [q[2]], []), - (CnotGate(), [q[0], q[2]], []), + (CXGate(), [q[0], q[2]], []), (TGate(), [q[2]], []), - (CnotGate(), [q[1], q[2]], []), + (CXGate(), [q[1], q[2]], []), (TdgGate(), [q[2]], []), - (CnotGate(), [q[0], q[2]], []), + (CXGate(), [q[0], q[2]], []), (TGate(), [q[1]], []), (TGate(), [q[2]], []), (HGate(), [q[2]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (TGate(), [q[0]], []), (TdgGate(), [q[1]], []), - (CnotGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []) ] for inst in rule: definition.append(inst) @@ -221,10 +262,10 @@ def _define(self): def inverse(self): """Invert this gate.""" - return ToffoliGate() # self-inverse + return CCXGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the Toffoli gate.""" + """Return a numpy.array for the CCX gate.""" return numpy.array([[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], @@ -235,6 +276,18 @@ def to_matrix(self): [0, 0, 0, 1, 0, 0, 0, 0]], dtype=complex) +class ToffoliGate(CCXGate, metaclass=CCXMeta): + """The deprecated CCXGate class.""" + + def __init__(self): + import warnings + warnings.warn('The class ToffoliGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CCXGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__() + + @deprecate_arguments({'ctl1': 'control_qubit1', 'ctl2': 'control_qubit2', 'tgt': 'target_qubit'}) @@ -260,13 +313,14 @@ def ccx(self, control_qubit1, control_qubit2, target_qubit, .. jupyter-execute:: - from qiskit.extensions.standard.x import ToffoliGate - ToffoliGate().to_matrix() + from qiskit.extensions.standard.x import CCXGate + CCXGate().to_matrix() """ - return self.append(ToffoliGate(), + return self.append(CCXGate(), [control_qubit1, control_qubit2, target_qubit], []) +# support both ccx and toffoli as methods of QuantumCircuit QuantumCircuit.ccx = ccx QuantumCircuit.toffoli = ccx diff --git a/qiskit/extensions/standard/y.py b/qiskit/extensions/standard/y.py index 25f85bd34586..20cda181068b 100644 --- a/qiskit/extensions/standard/y.py +++ b/qiskit/extensions/standard/y.py @@ -29,39 +29,43 @@ class YGate(Gate): def __init__(self, label=None): """Create new Y gate.""" - super().__init__("y", 1, [], label=label) + super().__init__('y', 1, [], label=label) def _define(self): from qiskit.extensions.standard.u3 import U3Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ - (U3Gate(pi, pi/2, pi/2), [q[0]], []) + (U3Gate(pi, pi / 2, pi / 2), [q[0]], []) ] for inst in rule: definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return CyGate() - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CYGate() + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate.""" return YGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the Y gate.""" + """Return a numpy.array for the Y gate.""" return numpy.array([[0, -1j], [1j, 0]], dtype=complex) @@ -99,12 +103,22 @@ def y(self, qubit, *, q=None): # pylint: disable=unused-argument QuantumCircuit.y = y -class CyGate(ControlledGate): - """controlled-Y gate.""" +class CYMeta(type): + """A metaclass to ensure that CyGate and CYGate are of the same type. + + Can be removed when CyGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CYGate, CyGate} # pylint: disable=unidiomatic-typecheck + + +class CYGate(ControlledGate, metaclass=CYMeta): + """The controlled-Y gate.""" def __init__(self): - """Create new CY gate.""" - super().__init__("cy", 2, [], num_ctrl_qubits=1) + """Create a new CY gate.""" + super().__init__('cy', 2, [], num_ctrl_qubits=1) self.base_gate = YGate() def _define(self): @@ -113,12 +127,12 @@ def _define(self): """ from qiskit.extensions.standard.s import SGate from qiskit.extensions.standard.s import SdgGate - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ (SdgGate(), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (SGate(), [q[1]], []) ] for inst in rule: @@ -127,7 +141,26 @@ def _define(self): def inverse(self): """Invert this gate.""" - return CyGate() # self-inverse + return CYGate() # self-inverse + + def to_matrix(self): + """Return a numpy.array for the CY gate.""" + return numpy.array([[1, 0, 0, 0], + [0, 0, 0, -1j], + [0, 0, 1, 0], + [0, 1j, 0, 0]], dtype=complex) + + +class CyGate(CYGate, metaclass=CYMeta): + """A deprecated CYGate class.""" + + def __init__(self): + import warnings + warnings.warn('The class CyGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CYGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__() @deprecate_arguments({'ctl': 'control_qubit', @@ -152,7 +185,7 @@ def cy(self, control_qubit, target_qubit, # pylint: disable=invalid-name circuit.cy(0,1) circuit.draw() """ - return self.append(CyGate(), [control_qubit, target_qubit], []) + return self.append(CYGate(), [control_qubit, target_qubit], []) QuantumCircuit.cy = cy diff --git a/qiskit/extensions/standard/z.py b/qiskit/extensions/standard/z.py index 2c970b8781f5..91a5f76e4cf7 100644 --- a/qiskit/extensions/standard/z.py +++ b/qiskit/extensions/standard/z.py @@ -29,12 +29,12 @@ class ZGate(Gate): def __init__(self, label=None): """Create new Z gate.""" - super().__init__("z", 1, [], label=label) + super().__init__('z', 1, [], label=label) def _define(self): from qiskit.extensions.standard.u1 import U1Gate definition = [] - q = QuantumRegister(1, "q") + q = QuantumRegister(1, 'q') rule = [ (U1Gate(pi), [q[0]], []) ] @@ -42,26 +42,30 @@ def _define(self): definition.append(inst) self.definition = definition - def control(self, num_ctrl_qubits=1, label=None): + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Controlled version of this gate. Args: num_ctrl_qubits (int): number of control qubits. label (str or None): An optional label for the gate [Default: None] + ctrl_state (int or str or None): control state expressed as integer, + string (e.g. '110'), or None. If None, use all 1s. Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: - return CzGate() - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label) + if ctrl_state is None: + if num_ctrl_qubits == 1: + return CZGate() + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, + ctrl_state=ctrl_state) def inverse(self): """Invert this gate.""" return ZGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the X gate.""" + """Return a numpy.array for the Z gate.""" return numpy.array([[1, 0], [0, -1]], dtype=complex) @@ -99,12 +103,22 @@ def z(self, qubit, *, q=None): # pylint: disable=unused-argument QuantumCircuit.z = z -class CzGate(ControlledGate): - """controlled-Z gate.""" +class CZMeta(type): + """A metaclass to ensure that CzGate and CZGate are of the same type. + + Can be removed when CzGate gets removed. + """ + @classmethod + def __instancecheck__(mcs, inst): + return type(inst) in {CZGate, CzGate} # pylint: disable=unidiomatic-typecheck + + +class CZGate(ControlledGate, metaclass=CZMeta): + """The controlled-Z gate.""" def __init__(self, label=None): """Create new CZ gate.""" - super().__init__("cz", 2, [], label=label, num_ctrl_qubits=1) + super().__init__('cz', 2, [], label=label, num_ctrl_qubits=1) self.base_gate = ZGate() def _define(self): @@ -112,12 +126,12 @@ def _define(self): gate cz a,b { h b; cx a,b; h b; } """ from qiskit.extensions.standard.h import HGate - from qiskit.extensions.standard.x import CnotGate + from qiskit.extensions.standard.x import CXGate definition = [] - q = QuantumRegister(2, "q") + q = QuantumRegister(2, 'q') rule = [ (HGate(), [q[1]], []), - (CnotGate(), [q[0], q[1]], []), + (CXGate(), [q[0], q[1]], []), (HGate(), [q[1]], []) ] for inst in rule: @@ -126,16 +140,28 @@ def _define(self): def inverse(self): """Invert this gate.""" - return CzGate() # self-inverse + return CZGate() # self-inverse def to_matrix(self): - """Return a Numpy.array for the Cz gate.""" + """Return a numpy.array for the CZ gate.""" return numpy.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]], dtype=complex) +class CzGate(CZGate, metaclass=CZMeta): + """The deprecated CZGate class.""" + + def __init__(self): + import warnings + warnings.warn('The class CzGate is deprecated as of 0.14.0, and ' + 'will be removed no earlier than 3 months after that release date. ' + 'You should use the class CZGate instead.', + DeprecationWarning, stacklevel=2) + super().__init__() + + @deprecate_arguments({'ctl': 'control_qubit', 'tgt': 'target_qubit'}) def cz(self, control_qubit, target_qubit, # pylint: disable=invalid-name @@ -163,10 +189,10 @@ def cz(self, control_qubit, target_qubit, # pylint: disable=invalid-name .. jupyter-execute:: - from qiskit.extensions.standard.cz import CzGate - CzGate().to_matrix() + from qiskit.extensions.standard.z import CZGate + CZGate().to_matrix() """ - return self.append(CzGate(), [control_qubit, target_qubit], []) + return self.append(CZGate(), [control_qubit, target_qubit], []) QuantumCircuit.cz = cz diff --git a/qiskit/extensions/unitary.py b/qiskit/extensions/unitary.py index a5247c57a6ed..c2b0c6f88586 100644 --- a/qiskit/extensions/unitary.py +++ b/qiskit/extensions/unitary.py @@ -22,6 +22,7 @@ from qiskit.circuit import Gate from qiskit.circuit import QuantumCircuit from qiskit.circuit import QuantumRegister +from qiskit.exceptions import QiskitError from qiskit.extensions.standard import U3Gate from qiskit.quantum_info.operators.predicates import matrix_equal from qiskit.quantum_info.operators.predicates import is_unitary_matrix @@ -111,44 +112,24 @@ def _define(self): raise NotImplementedError("Not able to generate a subcircuit for " "a {}-qubit unitary".format(self.num_qubits)) - def control(self, num_ctrl_qubits=1, label=None): - """Return controlled version of gate + def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + r"""Return controlled version of gate Args: num_ctrl_qubits (int): number of controls to add to gate (default=1) label (str): optional gate label + ctrl_state (int or str or None): The control state in decimal or as a + bit string (e.g. '1011'). If None, use 2**num_ctrl_qubits-1. Returns: UnitaryGate: controlled version of gate. Raises: - QiskitError: unrecognized mode + QiskitError: invalid ctrl_state """ - cmat = self._compute_control_matrix(self.to_matrix(), num_ctrl_qubits) + cmat = _compute_control_matrix(self.to_matrix(), num_ctrl_qubits) return UnitaryGate(cmat, label=label) - def _compute_control_matrix(self, base_mat, num_ctrl_qubits): - """ - Compute the controlled version of the input matrix with qiskit ordering. - - Args: - base_mat (ndarray): unitary to be controlled - num_ctrl_qubits (int): number of controls for new unitary - - Returns: - ndarray: controlled version of base matrix. - """ - num_target = int(numpy.log2(base_mat.shape[0])) - ctrl_dim = 2**num_ctrl_qubits - ctrl_grnd = numpy.repeat([[1], [0]], [1, ctrl_dim-1]) - full_mat_dim = ctrl_dim * base_mat.shape[0] - full_mat = numpy.zeros((full_mat_dim, full_mat_dim), dtype=base_mat.dtype) - ctrl_proj = numpy.diag(numpy.roll(ctrl_grnd, ctrl_dim - 1)) - full_mat = (numpy.kron(numpy.eye(2**num_target), - numpy.eye(ctrl_dim) - ctrl_proj) - + numpy.kron(base_mat, ctrl_proj)) - return full_mat - def qasm(self): """ The qasm for a custom unitary gate This is achieved by adding a custom gate that corresponds to the definition @@ -199,6 +180,52 @@ def qasm(self): return self._qasm_definition + self._qasmif(self._qasm_name) +def _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=None): + r""" + Compute the controlled version of the input matrix with qiskit ordering. + This function computes the controlled unitary with :math:`n` control qubits + and :math:`m` target qubits, + + .. math:: + + V_n^j(U_{2^m}) = (U_{2^m} \otimes |j\rangle\!\langle j|) + + (I_{2^m} \otimes (I_{2^n} - |j\rangle\!\langle j|)). + + where :math:`|j\rangle \in \mathcal{H}^{2^n}` is the control state. + + Args: + base_mat (ndarray): unitary to be controlled + num_ctrl_qubits (int): number of controls for new unitary + ctrl_state (int or str or None): The control state in decimal or as + a bitstring (e.g. '111'). If None, use 2**num_ctrl_qubits-1. + + Returns: + ndarray: controlled version of base matrix. + + Raises: + QiskitError: unrecognized mode or invalid ctrl_state + """ + num_target = int(numpy.log2(base_mat.shape[0])) + ctrl_dim = 2**num_ctrl_qubits + ctrl_grnd = numpy.repeat([[1], [0]], [1, ctrl_dim-1]) + if ctrl_state is None: + ctrl_state = ctrl_dim - 1 + elif isinstance(ctrl_state, str): + ctrl_state = int(ctrl_state, 2) + if isinstance(ctrl_state, int): + if not 0 <= ctrl_state < ctrl_dim: + raise QiskitError('Invalid control state value specified.') + else: + raise QiskitError('Invalid control state type specified.') + full_mat_dim = ctrl_dim * base_mat.shape[0] + full_mat = numpy.zeros((full_mat_dim, full_mat_dim), dtype=base_mat.dtype) + ctrl_proj = numpy.diag(numpy.roll(ctrl_grnd, ctrl_state)) + full_mat = (numpy.kron(numpy.eye(2**num_target), + numpy.eye(ctrl_dim) - ctrl_proj) + + numpy.kron(base_mat, ctrl_proj)) + return full_mat + + def unitary(self, obj, qubits, label=None): """Apply u2 to q.""" if isinstance(qubits, QuantumRegister): diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index 02d4b9169fb4..a8f6e8676c28 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -13,56 +13,85 @@ # that they have been altered from the originals. """ -=============================== -OpenPulse (:mod:`qiskit.pulse`) -=============================== - .. currentmodule:: qiskit.pulse -Channels -======== +=========================== +Pulse (:mod:`qiskit.pulse`) +=========================== -.. autosummary:: - :toctree: ../stubs/ +Qiskit-Pulse is a pulse-level quantum programming kit. This lower level of programming offers the +user more control than programming with :py:class:`~qiskit.circuit.QuantumCircuit` s. - DriveChannel - MeasureChannel - AcquireChannel - ControlChannel - RegisterSlot - MemorySlot +Extracting the greatest performance from quantum hardware requires real-time pulse-level +instructions. Pulse answers that need: it enables the quantum physicist *user* to specify the +exact time dynamics of an experiment. It is especially powerful for error mitigation techniques. + +The input is given as arbitrary, time-ordered signals (see: :ref:`pulse-commands`) scheduled in +parallel over multiple virtual hardware or simulator resources (see: :ref:`pulse-channels`). The +system also allows the user to recover the time dynamics of the measured output. + +This is sufficient to allow the quantum physicist to explore and correct for noise in a quantum +system. + +.. _pulse-commands: -Commands -======== +Commands (:mod:`~qiskit.pulse.commands`) +======================================== .. autosummary:: :toctree: ../stubs/ - Instruction - Acquire - AcquireInstruction - FrameChange - PersistentValue SamplePulse - Snapshot - Kernel - Discriminator Delay - ParametricPulse - ParametricInstruction + FrameChange Gaussian GaussianSquare Drag ConstantPulse + Acquire + Snapshot + +.. _pulse-channels: + +Channels (:mod:`~qiskit.pulse.channels`) +======================================== + +Pulse is meant to be agnostic to the underlying hardware implementation, while still allowing +low-level control. Therefore, our signal channels are *virtual* hardware channels. The backend +which executes our programs is responsible for mapping these virtual channels to the proper +physical channel within the quantum control hardware. + +Channels are characterized by their type and their index. See each channel type below to learn more. + +.. autosummary:: + :toctree: ../stubs/ + + DriveChannel + MeasureChannel + AcquireChannel + ControlChannel + RegisterSlot + MemorySlot Schedules ========= +Schedules are Pulse programs. They describe instruction sequences for the control hardware. +An :class:`~qiskit.pulse.Instruction` is a :py:class:`~qiskit.pulse.commands.Command` which has +been assigned to its :class:`~qiskit.pulse.channels.Channel` (s). + .. autosummary:: :toctree: ../stubs/ Schedule - ScheduleComponent + Instruction + +.. autosummary:: + :hidden: + :toctree: ../stubs/ + + qiskit.pulse.commands + qiskit.pulse.channels Configuration ============= @@ -71,8 +100,18 @@ :toctree: ../stubs/ InstructionScheduleMap - LoConfig - LoRange + +Rescheduling Utilities +====================== + +These utilities return modified :class:`~qiskit.pulse.Schedule` s. + +.. autosummary:: + :toctree: ../stubs/ + + ~reschedule.align_measures + ~reschedule.add_implicit_acquires + ~reschedule.pad Pulse Library ============= @@ -89,6 +128,7 @@ :toctree: ../stubs/ PulseError + """ from .channels import (DriveChannel, MeasureChannel, AcquireChannel, diff --git a/qiskit/pulse/channels.py b/qiskit/pulse/channels.py index 18381ab3bc79..63d5355991ef 100644 --- a/qiskit/pulse/channels.py +++ b/qiskit/pulse/channels.py @@ -15,7 +15,7 @@ """ This module defines Pulse Channels. Channels include: - - transmit channels, which should subclass``PulseChannel`` + - transmit channels, which should subclass ``PulseChannel`` - receive channels, such as ``AcquireChannel`` - non-signal "channels" such as ``SnapshotChannel``, ``MemorySlot`` and ``RegisterChannel``. @@ -31,7 +31,7 @@ class Channel(metaclass=ABCMeta): """Base class of channels. Channels provide a Qiskit-side label for typical quantum control hardware signal channels. The final label -> physical channel mapping is the responsibility - of the hardware backend. For instance,``DriveChannel(0)`` holds instructions which the backend + of the hardware backend. For instance, ``DriveChannel(0)`` holds instructions which the backend should map to the signal line driving gate operations on the qubit labeled (indexed) 0. """ diff --git a/qiskit/pulse/commands/__init__.py b/qiskit/pulse/commands/__init__.py index 6ce0900b59da..edc750b23875 100644 --- a/qiskit/pulse/commands/__init__.py +++ b/qiskit/pulse/commands/__init__.py @@ -12,7 +12,31 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Command classes for pulse.""" +""" +Supported command types in Pulse. + +.. autosummary:: + :toctree: ../stubs/ + + Acquire + FrameChange + SamplePulse + Snapshot + Delay + Gaussian + GaussianSquare + Drag + ConstantPulse + +Abstract Classes +---------------- +.. autosummary:: + :toctree: ../stubs/ + + ParametricPulse + Command + + """ from .instruction import Instruction from .acquire import Acquire, AcquireInstruction from .frame_change import FrameChange, FrameChangeInstruction diff --git a/qiskit/pulse/commands/instruction.py b/qiskit/pulse/commands/instruction.py index 114ccf85ef20..1f4e05da1bdd 100644 --- a/qiskit/pulse/commands/instruction.py +++ b/qiskit/pulse/commands/instruction.py @@ -159,32 +159,27 @@ def shift(self: ScheduleComponent, time: int, name: Optional[str] = None) -> 'Sc name = self.name return Schedule((time, self), name=name) - def insert(self, start_time: int, schedule: ScheduleComponent, buffer: bool = False, + def insert(self, start_time: int, schedule: ScheduleComponent, name: Optional[str] = None) -> 'Schedule': - """Return a new schedule with `schedule` inserted within `self` at `start_time`. + """Return a new :class:`~qiskit.pulse.Schedule` with ``schedule`` inserted within + ``self`` at ``start_time``. Args: start_time: Time to insert the schedule schedule schedule: Schedule to insert - buffer: Whether to obey buffer when inserting name: Name of the new schedule. Defaults to name of self """ - if buffer: - warnings.warn("Buffers are no longer supported. Please use an explicit Delay.") return self.union((start_time, schedule), name=name) - def append(self, schedule: ScheduleComponent, buffer: bool = False, + def append(self, schedule: ScheduleComponent, name: Optional[str] = None) -> 'Schedule': - """Return a new schedule with `schedule` inserted at the maximum time over - all channels shared between `self` and `schedule`. + """Return a new :class:`~qiskit.pulse.Schedule` with ``schedule`` inserted at the + maximum time over all channels shared between ``self`` and ``schedule``. Args: schedule: schedule to be appended - buffer: Whether to obey buffer when appending name: Name of the new schedule. Defaults to name of self """ - if buffer: - warnings.warn("Buffers are no longer supported. Please use an explicit Delay.") common_channels = set(self.channels) & set(schedule.channels) time = self.ch_stop_time(*common_channels) return self.insert(time, schedule, name=name) diff --git a/qiskit/pulse/interfaces.py b/qiskit/pulse/interfaces.py index 31fef1ef5dd9..0210ca14b0da 100644 --- a/qiskit/pulse/interfaces.py +++ b/qiskit/pulse/interfaces.py @@ -15,8 +15,6 @@ """ ScheduleComponent, a common interface for components of schedule (Instruction and Schedule). """ -import warnings - from abc import ABCMeta, abstractmethod from typing import Tuple, List, Union, Optional @@ -54,12 +52,6 @@ def start_time(self) -> int: """Starting time of this schedule component.""" pass - @property - def buffer(self) -> int: - """Buffer for schedule. To be used when appending""" - warnings.warn("Buffers are no longer supported. Please use an explicit Delay.") - return 0 - @property @abstractmethod def stop_time(self) -> int: @@ -128,27 +120,24 @@ def shift(self: 'ScheduleComponent', time: int, @abstractmethod def insert(self, start_time: int, schedule: 'ScheduleComponent', - buffer: bool = False, name: Optional[str] = None) -> 'ScheduleComponent': """Return a new schedule with `schedule` inserted at `start_time` of `self`. Args: start_time: time to be inserted schedule: schedule to be inserted - buffer: Obey buffer when appending name: Name of the new schedule. Defaults to name of parent """ pass @abstractmethod - def append(self, schedule: 'ScheduleComponent', buffer: bool = False, + def append(self, schedule: 'ScheduleComponent', name: Optional[str] = None) -> 'ScheduleComponent': """Return a new schedule with `schedule` inserted at the maximum time over all channels shared between `self` and `schedule`. Args: schedule: schedule to be appended - buffer: Obey buffer when appending name: Name of the new schedule. Defaults to name of parent """ pass diff --git a/qiskit/pulse/schedule.py b/qiskit/pulse/schedule.py index b0118dddccc8..104415eb3e6c 100644 --- a/qiskit/pulse/schedule.py +++ b/qiskit/pulse/schedule.py @@ -12,7 +12,10 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Schedule.""" +"""The Schedule is one of the most fundamental objects to this pulse-level programming module. +A ``Schedule`` is a representation of a *program* in Pulse. Each schedule tracks the time of each +instruction occuring in parallel over multiple signal *channels*. +""" import abc from typing import List, Tuple, Iterable, Union, Dict, Callable, Set, Optional, Type @@ -28,19 +31,21 @@ class Schedule(ScheduleComponent): - """Schedule of `ScheduleComponent`s. The composite node of a schedule tree.""" - # pylint: disable=missing-type-doc + """A quantum program *schedule* with exact time constraints for its instructions, operating + over all input signal *channels* and supporting special syntaxes for building. + """ + def __init__(self, *schedules: List[Union[ScheduleComponent, Tuple[int, ScheduleComponent]]], name: Optional[str] = None): - """Create empty schedule. + """Create an empty schedule. Args: *schedules: Child Schedules of this parent Schedule. May either be passed as - the list of schedules, or a list of (start_time, schedule) pairs - name: Name of this schedule + the list of schedules, or a list of ``(start_time, schedule)`` pairs. + name: Name of this schedule. Raises: - PulseError: If timeslots intercept. + PulseError: If the input schedules have instructions which overlap. """ self._name = name @@ -105,7 +110,7 @@ def _children(self) -> Tuple[Tuple[int, ScheduleComponent], ...]: @property def instructions(self) -> Tuple[Tuple[int, 'Instruction'], ...]: - """Get time-ordered instructions from Schedule tree.""" + """Get the time-ordered instructions from self.""" def key(time_inst_pair): inst = time_inst_pair[1] @@ -115,18 +120,18 @@ def key(time_inst_pair): return tuple(sorted(self._instructions(), key=key)) def ch_duration(self, *channels: List[Channel]) -> int: - """Return duration of schedule over supplied channels. + """Return the time of the end of the last instruction over the supplied channels. Args: - *channels: Supplied channels + *channels: Channels within ``self`` to include. """ return self.timeslots.ch_duration(*channels) def ch_start_time(self, *channels: List[Channel]) -> int: - """Return minimum start time over supplied channels. + """Return the time of the start of the first instruction over the supplied channels. Args: - *channels: Supplied channels + *channels: Channels within ``self`` to include. """ return self.timeslots.ch_start_time(*channels) @@ -134,7 +139,7 @@ def ch_stop_time(self, *channels: List[Channel]) -> int: """Return maximum start time over supplied channels. Args: - *channels: Supplied channels + *channels: Channels within ``self`` to include. """ return self.timeslots.ch_stop_time(*channels) @@ -142,22 +147,22 @@ def _instructions(self, time: int = 0) -> Iterable[Tuple[int, 'Instruction']]: """Iterable for flattening Schedule tree. Args: - time: Shifted time due to parent + time: Shifted time due to parent. Yields: - Tuple[int, Instruction]: Tuple containing time `Instruction` starts - at and the flattened `Instruction`. + Tuple containing the time each :class:`~qiskit.pulse.Instruction` + starts at and the flattened :class:`~qiskit.pulse.Instruction` s. """ for insert_time, child_sched in self._children: yield from child_sched._instructions(time + insert_time) def union(self, *schedules: Union[ScheduleComponent, Tuple[int, ScheduleComponent]], name: Optional[str] = None) -> 'Schedule': - """Return a new schedule which is the union of both `self` and `schedules`. + """Return a new schedule which is the union of both ``self`` and ``schedules``. Args: - *schedules: Schedules to be take the union with this `Schedule`. - name: Name of the new schedule. Defaults to name of self + *schedules: Schedules to be take the union with this ``Schedule``. + name: Name of the new schedule. Defaults to the name of self. """ if name is None: name = self.name @@ -170,10 +175,10 @@ def union(self, *schedules: Union[ScheduleComponent, Tuple[int, ScheduleComponen return new_sched def _union(self, other: Tuple[int, ScheduleComponent]) -> 'Schedule': - """Mutably union `self` and `other` Schedule with shift time. + """Mutably union ``self`` and ``other`` with shift time. Args: - other: Schedule with shift time to be take the union with this `Schedule`. + other: Schedule with shift time to be take the union with this ``Schedule``. """ shift_time, sched = other if isinstance(sched, Schedule): @@ -188,50 +193,47 @@ def _union(self, other: Tuple[int, ScheduleComponent]) -> 'Schedule': self._timeslots = self.timeslots.merge(sched_timeslots) def shift(self, time: int, name: Optional[str] = None) -> 'Schedule': - """Return a new schedule shifted forward by `time`. + """Return a new schedule shifted forward by ``time``. Args: - time: Time to shift by - name: Name of the new schedule. Defaults to name of self + time: Time to shift by. + name: Name of the new schedule. Defaults to the name of self. """ if name is None: name = self.name return Schedule((time, self), name=name) - def insert(self, start_time: int, schedule: ScheduleComponent, buffer: bool = False, + def insert(self, start_time: int, schedule: ScheduleComponent, name: Optional[str] = None) -> 'Schedule': - """Return a new schedule with `schedule` inserted within `self` at `start_time`. + """Return a new schedule with ``schedule`` inserted into ``self`` at ``start_time``. Args: - start_time: Time to insert the schedule - schedule: Schedule to insert - buffer: Whether to obey buffer when inserting - name: Name of the new schedule. Defaults to name of self + start_time: Time to insert the schedule. + schedule: Schedule to insert. + name: Name of the new schedule. Defaults to the name of self. """ - if buffer: - warnings.warn("Buffers are no longer supported. Please use an explicit Delay.") return self.union((start_time, schedule), name=name) - def append(self, schedule: ScheduleComponent, buffer: bool = False, + def append(self, schedule: ScheduleComponent, name: Optional[str] = None) -> 'Schedule': - r"""Return a new schedule with `schedule` inserted at the maximum time over - all channels shared between `self` and `schedule`. + r"""Return a new schedule with ``schedule`` inserted at the maximum time over + all channels shared between ``self`` and ``schedule``. + + .. math:: - $t = \textrm{max}({x.stop\_time |x \in self.channels \cap schedule.channels})$ + t = \textrm{max}(\texttt{x.stop_time} |\texttt{x} \in + \texttt{self.channels} \cap \texttt{schedule.channels}) Args: - schedule: schedule to be appended - buffer: Whether to obey buffer when appending - name: Name of the new schedule. Defaults to name of self + schedule: Schedule to be appended. + name: Name of the new ``Schedule``. Defaults to name of ``self``. """ - if buffer: - warnings.warn("Buffers are no longer supported. Please use an explicit Delay.") common_channels = set(self.channels) & set(schedule.channels) time = self.ch_stop_time(*common_channels) return self.insert(time, schedule, name=name) def flatten(self) -> 'Schedule': - """Return a new schedule which is the flattened schedule contained all `instructions`.""" + """Return a new schedule which is the flattened schedule contained all ``instructions``.""" return Schedule(*self.instructions, name=self.name) def filter(self, *filter_funcs: List[Callable], @@ -239,22 +241,22 @@ def filter(self, *filter_funcs: List[Callable], instruction_types: Optional[Iterable[Type['Instruction']]] = None, time_ranges: Optional[Iterable[Tuple[int, int]]] = None, intervals: Optional[Iterable[Interval]] = None) -> 'Schedule': - """ - Return a new Schedule with only the instructions from this Schedule which pass though the - provided filters; i.e. an instruction will be retained iff every function in filter_funcs - returns True, the instruction occurs on a channel type contained in channels, - the instruction type is contained in instruction_types, and the period over which the - instruction operates is fully contained in one specified in time_ranges or intervals. + """Return a new ``Schedule`` with only the instructions from this ``Schedule`` which pass + though the provided filters; i.e. an instruction will be retained iff every function in + ``filter_funcs`` returns ``True``, the instruction occurs on a channel type contained in + ``channels``, the instruction type is contained in ``instruction_types``, and the period + over which the instruction operates is *fully* contained in one specified in + ``time_ranges`` or ``intervals``. - If no arguments are provided, this schedule is returned. + If no arguments are provided, ``self`` is returned. Args: filter_funcs: A list of Callables which take a (int, ScheduleComponent) tuple and - return a bool - channels: For example, [DriveChannel(0), AcquireChannel(0)] - instruction_types: For example, [PulseInstruction, AcquireInstruction] - time_ranges: For example, [(0, 5), (6, 10)] - intervals: For example, [Interval(0, 5), Interval(6, 10)] + return a bool. + channels: For example, ``[DriveChannel(0), AcquireChannel(0)]``. + instruction_types: For example, ``[PulseInstruction, AcquireInstruction]``. + time_ranges: For example, ``[(0, 5), (6, 10)]``. + intervals: For example, ``[Interval(0, 5), Interval(6, 10)]``. """ composed_filter = self._construct_filter(*filter_funcs, channels=channels, @@ -269,18 +271,18 @@ def exclude(self, *filter_funcs: List[Callable], instruction_types: Optional[Iterable[Type['Instruction']]] = None, time_ranges: Optional[Iterable[Tuple[int, int]]] = None, intervals: Optional[Iterable[Interval]] = None) -> 'Schedule': - """ - Return a Schedule with only the instructions from this Schedule *failing* at least one of - the provided filters. This method is the complement of `self.filter`, so that: + """Return a Schedule with only the instructions from this Schedule *failing* at least one + of the provided filters. This method is the complement of ``self.filter``, so that:: + self.filter(args) | self.exclude(args) == self Args: filter_funcs: A list of Callables which take a (int, ScheduleComponent) tuple and - return a bool - channels: For example, [DriveChannel(0), AcquireChannel(0)] - instruction_types: For example, [PulseInstruction, AcquireInstruction] - time_ranges: For example, [(0, 5), (6, 10)] - intervals: For example, [Interval(0, 5), Interval(6, 10)] + return a bool. + channels: For example, ``[DriveChannel(0), AcquireChannel(0)]``. + instruction_types: For example, ``[PulseInstruction, AcquireInstruction]``. + time_ranges: For example, ``[(0, 5), (6, 10)]``. + intervals: For example, ``[Interval(0, 5), Interval(6, 10)]``. """ composed_filter = self._construct_filter(*filter_funcs, channels=channels, @@ -291,13 +293,12 @@ def exclude(self, *filter_funcs: List[Callable], new_sched_name="{name}-excluded".format(name=self.name)) def _apply_filter(self, filter_func: Callable, new_sched_name: str) -> 'Schedule': - """ - Return a Schedule containing only the instructions from this Schedule for which - filter_func returns True. + """Return a Schedule containing only the instructions from this Schedule for which + ``filter_func`` returns ``True``. Args: - filter_func: function of the form (int, ScheduleComponent) -> bool - new_sched_name: name of the returned Schedule + filter_func: Function of the form (int, ScheduleComponent) -> bool. + new_sched_name: Name of the returned ``Schedule``. """ subschedules = self.flatten()._children valid_subschedules = [sched for sched in subschedules if filter_func(sched)] @@ -308,21 +309,20 @@ def _construct_filter(self, *filter_funcs: List[Callable], instruction_types: Optional[Iterable[Type['Instruction']]] = None, time_ranges: Optional[Iterable[Tuple[int, int]]] = None, intervals: Optional[Iterable[Interval]] = None) -> Callable: - """ - Returns a boolean-valued function with input type (int, ScheduleComponent) that returns True - iff the input satisfies all of the criteria specified by the arguments; i.e. iff every - function in filter_funcs returns True, the instruction occurs on a channel type contained - in channels, the instruction type is contained in instruction_types, and the period over - which the instruction operates is fully contained in one specified in time_ranges or - intervals. + """Returns a boolean-valued function with input type ``(int, ScheduleComponent)`` that + returns ``True`` iff the input satisfies all of the criteria specified by the arguments; + i.e. iff every function in ``filter_funcs`` returns ``True``, the instruction occurs on a + channel type contained in ``channels``, the instruction type is contained in + ``instruction_types``, and the period over which the instruction operates is fully + contained in one specified in ``time_ranges`` or ``intervals``. Args: filter_funcs: A list of Callables which take a (int, ScheduleComponent) tuple and - return a bool - channels: For example, [DriveChannel(0), AcquireChannel(0)] - instruction_types: For example, [PulseInstruction, AcquireInstruction] - time_ranges: For example, [(0, 5), (6, 10)] - intervals: For example, [Interval(0, 5), Interval(6, 10)] + return a bool. + channels: For example, ``[DriveChannel(0), AcquireChannel(0)]``. + instruction_types: For example, ``[PulseInstruction, AcquireInstruction]``. + time_ranges: For example, ``[(0, 5), (6, 10)]``. + intervals: For example, ``[Interval(0, 5), Interval(6, 10)]``. """ def only_channels(channels: Set[Channel]) -> Callable: def channel_filter(time_inst: Tuple[int, 'Instruction']) -> bool: @@ -376,29 +376,29 @@ def draw(self, dt: float = 1, style: Optional['SchedStyle'] = None, filename: Name required to save pulse image interp_method: A function for interpolation scale: Relative visual scaling of waveform amplitudes, see Additional Information. - channel_scales: Channel independent scaling as a dictionary of `Channel` object. - channels_to_plot: Deprecated, see `channels` + channel_scales: Channel independent scaling as a dictionary of ``Channel`` object. + channels_to_plot: Deprecated, see ``channels`` plot_all: Plot empty channels plot_range: A tuple of time range to plot interactive: When set true show the circuit in a new window - (this depends on the matplotlib backend being used supporting this) + (this depends on the matplotlib backend being used supporting this) table: Draw event table for supported commands label: Label individual instructions framechange: Add framechange indicators - scaling: Deprecated, see `scale` + scaling: Deprecated, see ``scale`` channels: A list of channel names to plot show_framechange_channels: Plot channels with only framechanges Additional Information: If you want to manually rescale the waveform amplitude of channels one by one, - you can set `channel_scales` argument instead of `scale`. - The `channel_scales` should be given as a python dictionary:: + you can set ``channel_scales`` argument instead of ``scale``. + The ``channel_scales`` should be given as a python dictionary:: channel_scales = {pulse.DriveChannels(0): 10.0, pulse.MeasureChannels(0): 5.0} - When the channel to plot is not included in the `channel_scales` dictionary, - scaling factor of that channel is overwritten by the value of `scale` argument. + When the channel to plot is not included in the ``channel_scales`` dictionary, + scaling factor of that channel is overwritten by the value of ``scale` argument. In default, waveform amplitude is normalized by the maximum amplitude of the channel. The scaling factor is displayed under the channel name alias. @@ -430,9 +430,12 @@ def __eq__(self, other: ScheduleComponent) -> bool: """Test if two ScheduleComponents are equal. Equality is checked by verifying there is an equal instruction at every time - in `other` for every instruction in this Schedule. + in ``other`` for every instruction in this ``Schedule``. + + .. warning:: + + This does not check for logical equivalency. Ie., - Warning: This does not check for logical equivalency. Ie., ```python >>> (Delay(10)(DriveChannel(0)) + Delay(10)(DriveChannel(0)) == Delay(20)(DriveChannel(0))) @@ -461,15 +464,15 @@ def __eq__(self, other: ScheduleComponent) -> bool: return True def __add__(self, other: ScheduleComponent) -> 'Schedule': - """Return a new schedule with `other` inserted within `self` at `start_time`.""" + """Return a new schedule with ``other`` inserted within ``self`` at ``start_time``.""" return self.append(other) def __or__(self, other: ScheduleComponent) -> 'Schedule': - """Return a new schedule which is the union of `self` and `other`.""" + """Return a new schedule which is the union of ``self`` and ``other``.""" return self.union(other) def __lshift__(self, time: int) -> 'Schedule': - """Return a new schedule which is shifted forward by `time`.""" + """Return a new schedule which is shifted forward by ``time``.""" return self.shift(time) def __repr__(self): @@ -486,10 +489,10 @@ class ParameterizedSchedule: This should not be returned to users as it is currently only a helper class. This class is takes an input command definition that accepts - a set of parameters. Calling `bind` on the class will return a `Schedule`. + a set of parameters. Calling ``bind`` on the class will return a ``Schedule``. # TODO: In the near future this will be replaced with proper incorporation of parameters - into the `Schedule` class. + into the ``Schedule`` class. """ def __init__(self, *schedules, parameters: Optional[Dict[str, Union[float, complex]]] = None, diff --git a/qiskit/quantum_info/operators/operator.py b/qiskit/quantum_info/operators/operator.py index bb492f33863e..d2054101609d 100644 --- a/qiskit/quantum_info/operators/operator.py +++ b/qiskit/quantum_info/operators/operator.py @@ -23,7 +23,7 @@ from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.circuit.instruction import Instruction -from qiskit.extensions.standard import IdGate, XGate, YGate, ZGate, HGate, SGate, TGate +from qiskit.extensions.standard import IGate, XGate, YGate, ZGate, HGate, SGate, TGate from qiskit.exceptions import QiskitError from qiskit.quantum_info.operators.predicates import is_unitary_matrix, matrix_equal from qiskit.quantum_info.operators.base_operator import BaseOperator @@ -140,7 +140,7 @@ def from_label(cls, label): """ # Check label is valid label_mats = { - 'I': IdGate().to_matrix(), + 'I': IGate().to_matrix(), 'X': XGate().to_matrix(), 'Y': YGate().to_matrix(), 'Z': ZGate().to_matrix(), diff --git a/qiskit/quantum_info/operators/pauli.py b/qiskit/quantum_info/operators/pauli.py index c66d66f84039..d71ddc785e67 100644 --- a/qiskit/quantum_info/operators/pauli.py +++ b/qiskit/quantum_info/operators/pauli.py @@ -312,8 +312,8 @@ def to_operator(self): def to_instruction(self): """Convert to Pauli circuit instruction.""" from qiskit.circuit import QuantumCircuit, QuantumRegister - from qiskit.extensions.standard import IdGate, XGate, YGate, ZGate - gates = {'I': IdGate(), 'X': XGate(), 'Y': YGate(), 'Z': ZGate()} + from qiskit.extensions.standard import IGate, XGate, YGate, ZGate + gates = {'I': IGate(), 'X': XGate(), 'Y': YGate(), 'Z': ZGate()} label = self.to_label() n_qubits = self.numberofqubits qreg = QuantumRegister(n_qubits) diff --git a/qiskit/quantum_info/synthesis/two_qubit_decompose.py b/qiskit/quantum_info/synthesis/two_qubit_decompose.py index a921d9addad3..227c3cbf31a1 100644 --- a/qiskit/quantum_info/synthesis/two_qubit_decompose.py +++ b/qiskit/quantum_info/synthesis/two_qubit_decompose.py @@ -34,7 +34,7 @@ from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.extensions.standard.u3 import U3Gate -from qiskit.extensions.standard.x import CnotGate +from qiskit.extensions.standard.x import CXGate from qiskit.exceptions import QiskitError from qiskit.quantum_info.operators.predicates import is_unitary_matrix from qiskit.quantum_info.synthesis.weyl import weyl_coordinates @@ -477,4 +477,4 @@ def num_basis_gates(self, unitary): return np.argmax([trace_to_fid(traces[i]) * self.basis_fidelity**i for i in range(4)]) -two_qubit_cnot_decompose = TwoQubitBasisDecomposer(CnotGate()) +two_qubit_cnot_decompose = TwoQubitBasisDecomposer(CXGate()) diff --git a/qiskit/scheduler/methods/basic.py b/qiskit/scheduler/methods/basic.py index fa5f1317984f..50cc99e9b089 100644 --- a/qiskit/scheduler/methods/basic.py +++ b/qiskit/scheduler/methods/basic.py @@ -55,8 +55,6 @@ def as_soon_as_possible(circuit: QuantumCircuit, A schedule corresponding to the input ``circuit`` with pulses occurring as early as possible. """ - sched = Schedule(name=circuit.name) - qubit_time_available = defaultdict(int) def update_times(inst_qubits: List[int], time: int = 0) -> None: @@ -64,15 +62,20 @@ def update_times(inst_qubits: List[int], time: int = 0) -> None: for q in inst_qubits: qubit_time_available[q] = time + start_times = [] circ_pulse_defs = translate_gates_to_pulse_defs(circuit, schedule_config) for circ_pulse_def in circ_pulse_defs: - time = max(qubit_time_available[q] for q in circ_pulse_def.qubits) - if isinstance(circ_pulse_def.schedule, Barrier): - update_times(circ_pulse_def.qubits, time) - else: - sched = sched.insert(time, circ_pulse_def.schedule) - update_times(circ_pulse_def.qubits, time + circ_pulse_def.schedule.duration) - return sched + start_time = max(qubit_time_available[q] for q in circ_pulse_def.qubits) + stop_time = start_time + if not isinstance(circ_pulse_def.schedule, Barrier): + stop_time += circ_pulse_def.schedule.duration + + start_times.append(start_time) + update_times(circ_pulse_def.qubits, stop_time) + + timed_schedules = [(time, cpd.schedule) for time, cpd in zip(start_times, circ_pulse_defs) + if not isinstance(cpd.schedule, Barrier)] + return Schedule(*timed_schedules, name=circuit.name) def as_late_as_possible(circuit: QuantumCircuit, @@ -97,36 +100,29 @@ def as_late_as_possible(circuit: QuantumCircuit, A schedule corresponding to the input ``circuit`` with pulses occurring as late as possible. """ - sched = Schedule(name=circuit.name) - # Align channel end times. - circuit.barrier() - # We schedule in reverse order to get ALAP behaviour. We need to know how far out from t=0 any - # qubit will become occupied. We add positive shifts to these times as we go along. - # The time is initialized to 0 because all qubits are involved in the final barrier. - qubit_available_until = defaultdict(lambda: 0) - - def update_times(inst_qubits: List[int], shift: int = 0, inst_start_time: int = 0) -> None: + qubit_time_available = defaultdict(int) + + def update_times(inst_qubits: List[int], time: int = 0) -> None: """Update the time tracker for all inst_qubits to the given time.""" for q in inst_qubits: - qubit_available_until[q] = inst_start_time - for q in qubit_available_until.keys(): - if q not in inst_qubits: - # Uninvolved qubits might be free for the duration of the new instruction - qubit_available_until[q] += shift + qubit_time_available[q] = time + rev_stop_times = [] circ_pulse_defs = translate_gates_to_pulse_defs(circuit, schedule_config) for circ_pulse_def in reversed(circ_pulse_defs): - inst_sched = circ_pulse_def.schedule - # The new instruction should end when one of its qubits becomes occupied - inst_start_time = (min([qubit_available_until[q] for q in circ_pulse_def.qubits]) - - getattr(inst_sched, 'duration', 0)) # Barrier has no duration - # We have to translate qubit times forward when the inst_start_time is negative - shift_amount = max(0, -inst_start_time) - inst_start_time = max(inst_start_time, 0) + start_time = max(qubit_time_available[q] for q in circ_pulse_def.qubits) + stop_time = start_time if not isinstance(circ_pulse_def.schedule, Barrier): - sched = inst_sched.shift(inst_start_time).insert(shift_amount, sched, name=sched.name) - update_times(circ_pulse_def.qubits, shift_amount, inst_start_time) - return sched + stop_time += circ_pulse_def.schedule.duration + + rev_stop_times.append(stop_time) + update_times(circ_pulse_def.qubits, stop_time) + + last_stop = max(t for t in qubit_time_available.values()) + start_times = [last_stop - t for t in reversed(rev_stop_times)] + timed_schedules = [(time, cpd.schedule) for time, cpd in zip(start_times, circ_pulse_defs) + if not isinstance(cpd.schedule, Barrier)] + return Schedule(*timed_schedules, name=circuit.name) def translate_gates_to_pulse_defs(circuit: QuantumCircuit, diff --git a/qiskit/transpiler/passes/basis/ms_basis_decomposer.py b/qiskit/transpiler/passes/basis/ms_basis_decomposer.py index 7f0f9be9d8e1..21902baa53c6 100644 --- a/qiskit/transpiler/passes/basis/ms_basis_decomposer.py +++ b/qiskit/transpiler/passes/basis/ms_basis_decomposer.py @@ -18,7 +18,7 @@ from qiskit.exceptions import QiskitError from qiskit.converters import circuit_to_dag -from qiskit.extensions.standard import U3Gate, CnotGate +from qiskit.extensions.standard import U3Gate, CXGate from qiskit.transpiler.passes import Unroller from qiskit.quantum_info.synthesis.one_qubit_decompose import OneQubitEulerDecomposer @@ -28,7 +28,7 @@ class MSBasisDecomposer(TransformationPass): """Convert a circuit in ``U3, CX`` to ``Rx, Ry, Rxx`` without unrolling or simplification.""" - supported_input_gates = (U3Gate, CnotGate) + supported_input_gates = (U3Gate, CXGate) def __init__(self, basis_gates): """MSBasisDecomposer initializer. @@ -79,7 +79,7 @@ def run(self, dag): if isinstance(node.op, U3Gate): replacement_circuit = one_q_decomposer(node.op) - elif isinstance(node.op, CnotGate): + elif isinstance(node.op, CXGate): # N.B. We can't circuit_to_dag once outside the loop because # substitute_node_with_dag will modify the input DAG if the # node to be replaced is conditional. diff --git a/qiskit/transpiler/passes/optimization/consolidate_blocks.py b/qiskit/transpiler/passes/optimization/consolidate_blocks.py index 9b752acfd378..a256be84326f 100644 --- a/qiskit/transpiler/passes/optimization/consolidate_blocks.py +++ b/qiskit/transpiler/passes/optimization/consolidate_blocks.py @@ -20,7 +20,7 @@ from qiskit.dagcircuit import DAGCircuit from qiskit.quantum_info.operators import Operator from qiskit.quantum_info.synthesis import TwoQubitBasisDecomposer -from qiskit.extensions import UnitaryGate, CnotGate +from qiskit.extensions import UnitaryGate, CXGate from qiskit.transpiler.basepasses import TransformationPass from qiskit.transpiler.exceptions import TranspilerError @@ -37,7 +37,7 @@ class ConsolidateBlocks(TransformationPass): given such that blocks are in topological order. The blocks are collected by a previous pass, such as `Collect2qBlocks`. """ - def __init__(self, kak_basis_gate=CnotGate(), force_consolidate=False): + def __init__(self, kak_basis_gate=CXGate(), force_consolidate=False): """ConsolidateBlocks initializer. Args: diff --git a/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py b/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py index 52ee82152391..f22803a65dc4 100644 --- a/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py +++ b/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py @@ -40,7 +40,7 @@ HAS_Z3 = False from qiskit.transpiler.basepasses import TransformationPass from qiskit.dagcircuit import DAGCircuit -from qiskit.extensions.standard import U1Gate, U2Gate, U3Gate, CnotGate +from qiskit.extensions.standard import U1Gate, U2Gate, U3Gate, CXGate from qiskit.circuit import Measure from qiskit.extensions.standard.barrier import Barrier from qiskit.transpiler.exceptions import TranspilerError @@ -327,7 +327,7 @@ def basic_bounds(self): dur = self.bp_u2_dur[q_0] elif isinstance(gate.op, U3Gate): dur = self.bp_u3_dur[q_0] - elif isinstance(gate.op, CnotGate): + elif isinstance(gate.op, CXGate): dur = self.bp_cx_dur[self.cx_tuple(gate)] self.opt.add(self.gate_duration[gate] == dur) @@ -383,7 +383,7 @@ def fidelity_constraints(self): fid = math.log(1.0 - self.bp_u2_err[q_0]) elif isinstance(gate.op, U3Gate): fid = math.log(1.0 - self.bp_u3_err[q_0]) - elif isinstance(gate.op, CnotGate): + elif isinstance(gate.op, CXGate): fid = math.log(1.0 - self.bp_cx_err[self.cx_tuple(gate)]) self.opt.add(self.gate_fidelity[gate] == round(fid, NUM_PREC)) else: diff --git a/qiskit/transpiler/passes/optimization/remove_diagonal_gates_before_measure.py b/qiskit/transpiler/passes/optimization/remove_diagonal_gates_before_measure.py index dd7b7f673e76..fa3674dc53c8 100644 --- a/qiskit/transpiler/passes/optimization/remove_diagonal_gates_before_measure.py +++ b/qiskit/transpiler/passes/optimization/remove_diagonal_gates_before_measure.py @@ -16,7 +16,7 @@ from qiskit.circuit import Measure from qiskit.extensions.standard import RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate,\ - CzGate, CrzGate, Cu1Gate, RZZGate + CZGate, CRZGate, CU1Gate, RZZGate from qiskit.transpiler.basepasses import TransformationPass @@ -37,7 +37,7 @@ def run(self, dag): DAGCircuit: the optimized DAG. """ diagonal_1q_gates = (RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate) - diagonal_2q_gates = (CzGate, CrzGate, Cu1Gate, RZZGate) + diagonal_2q_gates = (CZGate, CRZGate, CU1Gate, RZZGate) nodes_to_remove = set() for measure in dag.op_nodes(Measure): diff --git a/qiskit/transpiler/passes/utils/check_cx_direction.py b/qiskit/transpiler/passes/utils/check_cx_direction.py index edcfbf31649b..958d82c90634 100644 --- a/qiskit/transpiler/passes/utils/check_cx_direction.py +++ b/qiskit/transpiler/passes/utils/check_cx_direction.py @@ -15,7 +15,7 @@ """Check if the CNOTs follow the right direction with respect to the coupling map.""" from qiskit.transpiler.basepasses import AnalysisPass -from qiskit.extensions.standard.x import CnotGate +from qiskit.extensions.standard.x import CXGate class CheckCXDirection(AnalysisPass): @@ -53,7 +53,7 @@ def run(self, dag): physical_q0 = gate.qargs[0].index physical_q1 = gate.qargs[1].index - if isinstance(gate.op, CnotGate) and ( + if isinstance(gate.op, CXGate) and ( physical_q0, physical_q1) not in edges: self.property_set['is_direction_mapped'] = False return diff --git a/qiskit/transpiler/passes/utils/cx_direction.py b/qiskit/transpiler/passes/utils/cx_direction.py index 9fb988a138f3..c45de6e7f352 100644 --- a/qiskit/transpiler/passes/utils/cx_direction.py +++ b/qiskit/transpiler/passes/utils/cx_direction.py @@ -21,7 +21,7 @@ from qiskit.circuit import QuantumRegister from qiskit.dagcircuit import DAGCircuit -from qiskit.extensions.standard import U2Gate, CnotGate +from qiskit.extensions.standard import U2Gate, CXGate class CXDirection(TransformationPass): @@ -91,7 +91,7 @@ def run(self, dag): sub_dag.apply_operation_back(U2Gate(0, pi), [sub_qr[1]], []) # Flips the cx - sub_dag.apply_operation_back(CnotGate(), [sub_qr[1], sub_qr[0]], []) + sub_dag.apply_operation_back(CXGate(), [sub_qr[1], sub_qr[0]], []) # Add H gates after sub_dag.apply_operation_back(U2Gate(0, pi), [sub_qr[0]], []) diff --git a/qiskit/validation/jsonschema/schema_validation.py b/qiskit/validation/jsonschema/schema_validation.py index 35024d9403fc..c0d5cb5bf6ec 100644 --- a/qiskit/validation/jsonschema/schema_validation.py +++ b/qiskit/validation/jsonschema/schema_validation.py @@ -131,21 +131,38 @@ def validate_json_against_schema(json_dict, schema, SchemaValidationError: Raised if validation fails. """ - try: - if isinstance(schema, str): - schema_name = schema - schema = _SCHEMAS[schema_name] - validator = _get_validator(schema_name) - validator.validate(json_dict) - else: + if isinstance(schema, str): + schema_name = schema + schema = _SCHEMAS[schema_name] + validator = _get_validator(schema_name) + errors = list(validator.iter_errors(json_dict)) + if errors: + best_match_error = jsonschema.exceptions.best_match(errors) + failure_path = list(best_match_error.absolute_path) + if len(failure_path) > 1: + failure_path = failure_path[:-1] + error_path = "" + for component in failure_path: + if isinstance(component, int): + error_path += "[%s]" % component + else: + error_path += "['%s']" % component + if failure_path: + err_message = "Validation failed. Possibly at %s" % error_path + err_message += " because of %s" % best_match_error.message + else: + err_message = "Validation failed. " + err_message += "Possibly because %s" % best_match_error.message + raise SchemaValidationError(err_message) + else: + try: jsonschema.validate(json_dict, schema) - except jsonschema.ValidationError as err: - if err_msg is None: - err_msg = "JSON failed validation. Set Qiskit log level to DEBUG " \ - "for further information." - newerr = SchemaValidationError(err_msg) - newerr.__cause__ = _SummaryValidationError(err) - logger.debug('%s', _format_causes(err)) + except jsonschema.ValidationError as err: + if err_msg is None: + err_msg = ("JSON failed validation. Set Qiskit log level to " + "DEBUG for further information.") + newerr = SchemaValidationError(err_msg) + newerr.__cause__ = _SummaryValidationError(err) raise newerr diff --git a/qiskit/visualization/matplotlib.py b/qiskit/visualization/matplotlib.py index ab482a7cd774..a53475029369 100644 --- a/qiskit/visualization/matplotlib.py +++ b/qiskit/visualization/matplotlib.py @@ -910,7 +910,7 @@ def _draw_ops(self, verbose=False): else: self._line(qreg_b, qreg_t, zorder=PORDER_LINE + 1) # control gate - elif op.name in ['cy', 'ch', 'cu3', 'cu1', 'crz']: + elif op.name in ['cy', 'ch', 'cu3', 'crz']: disp = op.name.replace('c', '') color = None @@ -937,6 +937,16 @@ def _draw_ops(self, verbose=False): # add qubit-qubit wiring self._line(qreg_b, qreg_t) + + # cu1 gate + elif op.name == 'cu1': + self._ctrl_qubit(q_xy[0]) + self._ctrl_qubit(q_xy[1]) + self._sidetext(qreg_b, text='U1 ({})'.format(param)) + + # add qubit-qubit wiring + self._line(qreg_b, qreg_t) + # swap gate elif op.name == 'swap': self._swap(q_xy[0]) diff --git a/qiskit/visualization/qcstyle.py b/qiskit/visualization/qcstyle.py index 94371cb6d5b2..02b80c6811bf 100644 --- a/qiskit/visualization/qcstyle.py +++ b/qiskit/visualization/qcstyle.py @@ -47,7 +47,7 @@ def __init__(self): self.sfs = 8 self.colored_add_width = 0.2 self.disptex = { - 'id': 'Id', + 'id': 'I', 'u0': 'U_0', 'u1': 'U_1', 'u2': 'U_2', @@ -151,7 +151,7 @@ def __init__(self): self.colored_add_width = 0.2 self.sfs = 8 self.disptex = { - 'id': 'Id', + 'id': 'I', 'u0': 'U_0', 'u1': 'U_1', 'u2': 'U_2', diff --git a/qiskit/visualization/text.py b/qiskit/visualization/text.py index 731b0a12c284..d6c46378b189 100644 --- a/qiskit/visualization/text.py +++ b/qiskit/visualization/text.py @@ -707,7 +707,12 @@ def label_for_box(instruction, controlled=False): else: label = instruction.name params = TextDrawing.params_for_label(instruction) + + # generate correct label for the box + if label == 'id': + label = 'i' label = label.capitalize() + if params: label += "(%s)" % ','.join(params) return label diff --git a/releasenotes/notes/add-open-controls-bb21111b866ada43.yaml b/releasenotes/notes/add-open-controls-bb21111b866ada43.yaml new file mode 100644 index 000000000000..8efb4bde8f38 --- /dev/null +++ b/releasenotes/notes/add-open-controls-bb21111b866ada43.yaml @@ -0,0 +1,19 @@ +--- +features: + - | + Add ability to specify control conditioned on a qubit being in the + ground state. The state of the control qubits is represented by a + decimal integer. For example: + + from qiskit import QuantumCircuit + from qiskit.extensions.standard import XGate + + qc = QuantumCircuit(4) + cgate = XGate().control(3, ctrl_state=6) + qc.append(cgate, [0, 1, 2, 3]) + + Creates a four qubit gate where the fourth qubit gets flipped if + the first qubit is in the ground state and the second and third + qubits are in the excited state. If ctrl_state is None, the + default, control is conditioned on all control qubits being + excited. diff --git a/releasenotes/notes/boolean-logic-circuit-library-0f913cc04063210b.yaml b/releasenotes/notes/boolean-logic-circuit-library-0f913cc04063210b.yaml new file mode 100644 index 000000000000..8246ef980151 --- /dev/null +++ b/releasenotes/notes/boolean-logic-circuit-library-0f913cc04063210b.yaml @@ -0,0 +1,11 @@ +--- +prelude: > + A library of quantum circuits is introduced in ``qiskit.circuit.library``. + This library contains useful circuits which can be used for experiments or + be used as subcircuits in building up other circuits. The contents of this + library will grow over time and better implementations will be introduced. +features: + - | + A few simple circuits for basic boolean logic operations such as ``shift``, + ``permutation``, and ``inner_product`` are added to + ``qiskit.circuit.library``. diff --git a/releasenotes/notes/channel-module-deprecations-in-pulse-ffc384b325b077f1.yaml b/releasenotes/notes/channel-module-deprecations-in-pulse-ffc384b325b077f1.yaml index eae8962c6f0e..a63c9abdfd54 100644 --- a/releasenotes/notes/channel-module-deprecations-in-pulse-ffc384b325b077f1.yaml +++ b/releasenotes/notes/channel-module-deprecations-in-pulse-ffc384b325b077f1.yaml @@ -1,9 +1,10 @@ --- upgrade: - | - Channel ``buffer`` option was deprecated in Terra 0.11.0 and has now been - removed. To add a delay on a channel, specify it explicitly in your - Schedule with a Delay:: + The pulse ``buffer`` option found in :class:`qiskit.pulse.Channel` and + :class:`qiskit.pulse.Schedule` was deprecated in Terra 0.11.0 and has now + been removed. To add a delay on a channel or in a schedule, specify it + explicitly in your Schedule with a Delay:: sched = Schedule() sched += Delay(5)(DriveChannel(0)) diff --git a/releasenotes/notes/consistent-gate-naming-efa669c7a8d3a654.yaml b/releasenotes/notes/consistent-gate-naming-efa669c7a8d3a654.yaml new file mode 100644 index 000000000000..d87636c94538 --- /dev/null +++ b/releasenotes/notes/consistent-gate-naming-efa669c7a8d3a654.yaml @@ -0,0 +1,31 @@ +--- +deprecations: + - | + The gates are named more consistently. + * the Pauli gates all have one uppercase letter only (I, X, Y, Z) + * the parameterized Pauli gates (i.e. rotations) prepend the uppercase letter R (RX, RY, RZ) + * a controlled version prepends the uppercase letter C (CX, CRX, CCX) + * gates are named according to their action, not their alternative names (CCX, not Toffoli) + + This is a list of the changed names in the format `new_name <- old_name`: + + class | name | qc._ + -------------------------+------------------+---------------------- + CCXGate <- ToffoliGate | ccx | ccx, toffoli + CRXGate <- CrxGate | crx | crx + CRYGate <- CryGate | cry | cry + CRZGate <- CrzGate | crz | crz + CSwapGate <- FredkinGate | cswap | cswap, fredkin + CU1Gate <- Cu1Gate | cu1 | cu1 + CU3Gate <- Cu3Gate | cu3 | cu3 + CXGate <- CnotGate | cx | cx, cnot + CYGate <- CyGate | cy | cy + CZGate <- CzGate | cz | cz + DiagonalGate <- DiagGate | diagonal <- diag | diagonal <- diag_gate + IGate <- IdGate | id | i,id <- iden + Isometry | isometry <- iso | isometry,iso <- iso + UCGate <- UCG | multiplexer | uc <- ucg + UCPauliRotGate <- UCRot | | + UCRXGate <- UCX | ucrx <- ucrotX | ucrx <- ucx + UCRYGate <- UCY | ucry <- ucrotY | ucry <- ucy + UCRZGate <- UCZ | ucrz <- ucrotZ | ucrz <- ucz diff --git a/releasenotes/notes/fix3684-69e230f98546deb6.yaml b/releasenotes/notes/fix3684-69e230f98546deb6.yaml new file mode 100644 index 000000000000..1b89c54fe16a --- /dev/null +++ b/releasenotes/notes/fix3684-69e230f98546deb6.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + The MPL drawer has been fixed and renders cU1 gate correctly. + Fixes `issue #3684 ` \ No newline at end of file diff --git a/test/python/circuit/test_circuit_data.py b/test/python/circuit/test_circuit_data.py index 421d89433bdb..d3ee18bc4f35 100644 --- a/test/python/circuit/test_circuit_data.py +++ b/test/python/circuit/test_circuit_data.py @@ -15,7 +15,7 @@ """Test operations on circuit.data.""" from qiskit.circuit import QuantumCircuit, QuantumRegister -from qiskit.extensions.standard import HGate, XGate, CnotGate +from qiskit.extensions.standard import HGate, XGate, CXGate from qiskit.test import QiskitTestCase from qiskit.circuit.exceptions import CircuitError @@ -39,7 +39,7 @@ def test_getitem_by_insertion_order(self): data = qc.data self.assertEqual(data[0], (HGate(), [qr[0]], [])) - self.assertEqual(data[1], (CnotGate(), [qr[0], qr[1]], [])) + self.assertEqual(data[1], (CXGate(), [qr[0], qr[1]], [])) self.assertEqual(data[2], (HGate(), [qr[1]], [])) def test_count_gates(self): @@ -88,7 +88,7 @@ def test_index_gates(self): qc.h(0) self.assertEqual(qc.data.index((HGate(), [qr[0]], [])), 0) - self.assertEqual(qc.data.index((CnotGate(), [qr[0], qr[1]], [])), 1) + self.assertEqual(qc.data.index((CXGate(), [qr[0], qr[1]], [])), 1) self.assertEqual(qc.data.index((HGate(), [qr[1]], [])), 2) def test_iter(self): @@ -102,7 +102,7 @@ def test_iter(self): iter_ = iter(qc.data) self.assertEqual(next(iter_), (HGate(), [qr[0]], [])) - self.assertEqual(next(iter_), (CnotGate(), [qr[0], qr[1]], [])) + self.assertEqual(next(iter_), (CXGate(), [qr[0], qr[1]], [])) self.assertEqual(next(iter_), (HGate(), [qr[1]], [])) self.assertRaises(StopIteration, next, iter_) @@ -129,9 +129,9 @@ def test_slice(self): (HGate(), [qr[0]], []), ]) self.assertEqual(cx_slice, [ - (CnotGate(), [qr[0], qr[1]], []), - (CnotGate(), [qr[1], qr[0]], []), - (CnotGate(), [qr[0], qr[1]], []), + (CXGate(), [qr[0], qr[1]], []), + (CXGate(), [qr[1], qr[0]], []), + (CXGate(), [qr[0], qr[1]], []), ]) def test_copy(self): @@ -323,7 +323,7 @@ def test_append_is_validated(self): qc = QuantumCircuit(qr) qc.data.append((HGate(), [qr[0]], [])) - qc.data.append((CnotGate(), [0, 1], [])) + qc.data.append((CXGate(), [0, 1], [])) qc.data.append((HGate(), [qr[1]], [])) expected_qc = QuantumCircuit(qr) @@ -343,7 +343,7 @@ def test_insert_is_validated(self): qc = QuantumCircuit(qr) qc.data.insert(0, (HGate(), [qr[0]], [])) - qc.data.insert(1, (CnotGate(), [0, 1], [])) + qc.data.insert(1, (CXGate(), [0, 1], [])) qc.data.insert(2, (HGate(), [qr[1]], [])) expected_qc = QuantumCircuit(qr) @@ -363,7 +363,7 @@ def test_extend_is_validated(self): qc = QuantumCircuit(qr) qc.data.extend([(HGate(), [qr[0]], []), - (CnotGate(), [0, 1], []), + (CXGate(), [0, 1], []), (HGate(), [qr[1]], [])]) expected_qc = QuantumCircuit(qr) @@ -383,7 +383,7 @@ def test_setting_data_is_validated(self): qc = QuantumCircuit(qr) qc.data = [(HGate(), [qr[0]], []), - (CnotGate(), [0, 1], []), + (CXGate(), [0, 1], []), (HGate(), [qr[1]], [])] expected_qc = QuantumCircuit(qr) diff --git a/test/python/circuit/test_circuit_load_from_qasm.py b/test/python/circuit/test_circuit_load_from_qasm.py index 973b2f2dd53c..e1e08fce3e2c 100644 --- a/test/python/circuit/test_circuit_load_from_qasm.py +++ b/test/python/circuit/test_circuit_load_from_qasm.py @@ -26,11 +26,11 @@ class LoadFromQasmTest(QiskitTestCase): def setUp(self): self.qasm_file_name = 'entangled_registers.qasm' - self.qasm_file_path = self._get_resource_path( - 'qasm/' + self.qasm_file_name, Path.EXAMPLES) + self.qasm_file_path = self._get_resource_path('qasm/' + self.qasm_file_name, Path.EXAMPLES) def test_qasm_file(self): - """Test qasm_file and get_circuit. + """ + Test qasm_file and get_circuit. If all is correct we should get the qasm file loaded in _qasm_file_path """ @@ -48,8 +48,54 @@ def test_qasm_file(self): q_circuit_2.measure(qr_b, cr_d) self.assertEqual(q_circuit, q_circuit_2) + def test_loading_all_qelib1_gates(self): + """Test setting up a circuit with all gates defined in qiskit/qasm/libs/qelib1.inc.""" + all_gates_qasm = self._get_resource_path('all_gates.qasm', Path.QASMS) + qasm_circuit = QuantumCircuit.from_qasm_file(all_gates_qasm) + + # the hardware primitives + ref_circuit = QuantumCircuit(3, 3) + ref_circuit.u3(0.2, 0.1, 0.6, 0) + ref_circuit.u2(0.1, 0.6, 0) + ref_circuit.u1(0.6, 0) + ref_circuit.id(0) + ref_circuit.cx(0, 1) + # the standard single qubit gates + ref_circuit.x(0) + ref_circuit.y(0) + ref_circuit.z(0) + ref_circuit.h(0) + ref_circuit.s(0) + ref_circuit.t(0) + ref_circuit.sdg(0) + ref_circuit.tdg(0) + # the standard rotations + ref_circuit.rx(0.1, 0) + ref_circuit.ry(0.1, 0) + ref_circuit.rz(0.1, 0) + # the barrier + ref_circuit.barrier() + # the standard user-defined gates + ref_circuit.swap(0, 1) + ref_circuit.cswap(0, 1, 2) + ref_circuit.cy(0, 1) + ref_circuit.cz(0, 1) + ref_circuit.ch(0, 1) + ref_circuit.cu1(0.6, 0, 1) + ref_circuit.cu3(0.2, 0.1, 0.6, 0, 1) + ref_circuit.ccx(0, 1, 2) + ref_circuit.crx(0.6, 0, 1) + ref_circuit.cry(0.6, 0, 1) + ref_circuit.crz(0.6, 0, 1) + ref_circuit.rxx(0.2, 0, 1) + ref_circuit.rzz(0.2, 0, 1) + ref_circuit.measure([0, 1, 2], [0, 1, 2]) + + self.assertEqual(qasm_circuit, ref_circuit) + def test_fail_qasm_file(self): - """Test fail_qasm_file. + """ + Test fail_qasm_file. If all is correct we should get a QiskitError """ @@ -57,7 +103,8 @@ def test_fail_qasm_file(self): QuantumCircuit.from_qasm_file, "") def test_fail_qasm_string(self): - """Test fail_qasm_string. + """ + Test fail_qasm_string. If all is correct we should get a QiskitError """ @@ -65,7 +112,8 @@ def test_fail_qasm_string(self): QuantumCircuit.from_qasm_str, "") def test_qasm_text(self): - """Test qasm_text and get_circuit. + """ + Test qasm_text and get_circuit. If all is correct we should get the qasm file loaded from the string """ @@ -102,7 +150,8 @@ def test_qasm_text(self): self.assertEqual(q_circuit, ref) def test_qasm_text_conditional(self): - """Test qasm_text and get_circuit when conditionals are present. + """ + Test qasm_text and get_circuit when conditionals are present. """ qasm_string = '\n'.join(["OPENQASM 2.0;", "include \"qelib1.inc\";", @@ -125,8 +174,11 @@ def test_qasm_text_conditional(self): self.assertEqual(q_circuit, ref) def test_opaque_gate(self): - """Test parse an opaque gate - See https://github.com/Qiskit/qiskit-terra/issues/1566""" + """ + Test parse an opaque gate + + See https://github.com/Qiskit/qiskit-terra/issues/1566. + """ qasm_string = '\n'.join(["OPENQASM 2.0;", "include \"qelib1.inc\";", @@ -137,13 +189,13 @@ def test_opaque_gate(self): qr = QuantumRegister(3, 'q') expected = QuantumCircuit(qr) - expected.append(Gate(name='my_gate', num_qubits=2, params=[1, 2, 3]), [qr[1], qr[2]]) + expected.append(Gate(name='my_gate', num_qubits=2, + params=[1, 2, 3]), [qr[1], qr[2]]) self.assertEqual(circuit, expected) def test_qasm_example_file(self): - """Loads qasm/example.qasm. - """ + """Loads qasm/example.qasm.""" qasm_filename = self._get_resource_path('example.qasm', Path.QASMS) expected_circuit = QuantumCircuit.from_qasm_str('\n'.join(["OPENQASM 2.0;", "include \"qelib1.inc\";", @@ -172,7 +224,7 @@ def test_qasm_example_file(self): self.assertEqual(len(q_circuit.qregs), 2) def test_qasm_qas_string_order(self): - """ Test that gates are returned in qasm in ascending order""" + """Test that gates are returned in qasm in ascending order.""" expected_qasm = '\n'.join(["OPENQASM 2.0;", "include \"qelib1.inc\";", "qreg q[3];", diff --git a/test/python/circuit/test_controlled_gate.py b/test/python/circuit/test_controlled_gate.py index d0ad34219a07..68ee5d520d62 100644 --- a/test/python/circuit/test_controlled_gate.py +++ b/test/python/circuit/test_controlled_gate.py @@ -19,25 +19,27 @@ from inspect import signature import numpy as np from numpy import pi -import scipy from ddt import ddt, data from qiskit import QuantumRegister, QuantumCircuit, execute, BasicAer, QiskitError from qiskit.test import QiskitTestCase from qiskit.circuit import ControlledGate +from qiskit.circuit.exceptions import CircuitError from qiskit.quantum_info.operators.predicates import matrix_equal, is_unitary_matrix +from qiskit.quantum_info.random import random_unitary +from qiskit.quantum_info.states import Statevector import qiskit.circuit.add_control as ac from qiskit.transpiler.passes import Unroller from qiskit.converters.circuit_to_dag import circuit_to_dag from qiskit.converters.dag_to_circuit import dag_to_circuit from qiskit.quantum_info import Operator -from qiskit.extensions.standard import (CnotGate, XGate, YGate, ZGate, U1Gate, - CyGate, CzGate, Cu1Gate, SwapGate, - ToffoliGate, HGate, RZGate, RXGate, - RYGate, CryGate, CrxGate, FredkinGate, - U3Gate, CHGate, CrzGate, Cu3Gate, +from qiskit.extensions.standard import (CXGate, XGate, YGate, ZGate, U1Gate, + CYGate, CZGate, CU1Gate, SwapGate, + CCXGate, HGate, RZGate, RXGate, + RYGate, CRYGate, CRXGate, CSwapGate, + U3Gate, CHGate, CRZGate, CU3Gate, MSGate, Barrier, RCCXGate, RCCCXGate) -from qiskit.extensions.unitary import UnitaryGate +from qiskit.extensions.unitary import _compute_control_matrix import qiskit.extensions.standard as allGates @@ -47,15 +49,15 @@ class TestControlledGate(QiskitTestCase): def test_controlled_x(self): """Test creation of controlled x gate""" - self.assertEqual(XGate().control(), CnotGate()) + self.assertEqual(XGate().control(), CXGate()) def test_controlled_y(self): """Test creation of controlled y gate""" - self.assertEqual(YGate().control(), CyGate()) + self.assertEqual(YGate().control(), CYGate()) def test_controlled_z(self): """Test creation of controlled z gate""" - self.assertEqual(ZGate().control(), CzGate()) + self.assertEqual(ZGate().control(), CZGate()) def test_controlled_h(self): """Test creation of controlled h gate""" @@ -64,41 +66,41 @@ def test_controlled_h(self): def test_controlled_u1(self): """Test creation of controlled u1 gate""" theta = 0.5 - self.assertEqual(U1Gate(theta).control(), Cu1Gate(theta)) + self.assertEqual(U1Gate(theta).control(), CU1Gate(theta)) def test_controlled_rz(self): """Test creation of controlled rz gate""" theta = 0.5 - self.assertEqual(RZGate(theta).control(), CrzGate(theta)) + self.assertEqual(RZGate(theta).control(), CRZGate(theta)) def test_controlled_ry(self): """Test creation of controlled ry gate""" theta = 0.5 - self.assertEqual(RYGate(theta).control(), CryGate(theta)) + self.assertEqual(RYGate(theta).control(), CRYGate(theta)) def test_controlled_rx(self): """Test creation of controlled rx gate""" theta = 0.5 - self.assertEqual(RXGate(theta).control(), CrxGate(theta)) + self.assertEqual(RXGate(theta).control(), CRXGate(theta)) def test_controlled_u3(self): """Test creation of controlled u3 gate""" theta, phi, lamb = 0.1, 0.2, 0.3 self.assertEqual(U3Gate(theta, phi, lamb).control(), - Cu3Gate(theta, phi, lamb)) + CU3Gate(theta, phi, lamb)) def test_controlled_cx(self): """Test creation of controlled cx gate""" - self.assertEqual(CnotGate().control(), ToffoliGate()) + self.assertEqual(CXGate().control(), CCXGate()) def test_controlled_swap(self): """Test creation of controlled swap gate""" - self.assertEqual(SwapGate().control(), FredkinGate()) + self.assertEqual(SwapGate().control(), CSwapGate()) def test_circuit_append(self): """Test appending controlled gate to quantum circuit""" circ = QuantumCircuit(5) - inst = CnotGate() + inst = CXGate() circ.append(inst.control(), qargs=[0, 2, 1]) circ.append(inst.control(2), qargs=[0, 3, 1, 2]) circ.append(inst.control().control(), qargs=[0, 3, 1, 2]) # should be same as above @@ -156,8 +158,8 @@ def test_single_controlled_composite_gate(self): num_target = cgate.width() gate = cgate.to_gate() cont_gate = gate.control(num_ctrl_qubits=num_ctrl) - control = QuantumRegister(num_ctrl) - target = QuantumRegister(num_target) + control = QuantumRegister(num_ctrl, 'control') + target = QuantumRegister(num_target, 'target') qc = QuantumCircuit(control, target) qc.append(cont_gate, control[:]+target[:]) simulator = BasicAer.get_backend('unitary_simulator') @@ -194,7 +196,7 @@ def test_multi_control_u3(self): width = 3 qr = QuantumRegister(width) qc_cu3 = QuantumCircuit(qr) - cu3gate = u3.Cu3Gate(alpha, beta, gamma) + cu3gate = u3.CU3Gate(alpha, beta, gamma) c_cu3 = cu3gate.control(1) qc_cu3.append(c_cu3, qr, []) @@ -255,7 +257,7 @@ def test_multi_control_u1(self): width = 3 qr = QuantumRegister(width) qc_cu1 = QuantumCircuit(qr) - cu1gate = u1.Cu1Gate(theta) + cu1gate = u1.CU1Gate(theta) c_cu1 = cu1gate.control(1) qc_cu1.append(c_cu1, qr, []) @@ -400,13 +402,52 @@ def test_controlled_unitary(self, num_ctrl_qubits): def test_controlled_random_unitary(self, num_ctrl_qubits): """test controlled unitary""" num_target = 2 - base_gate = UnitaryGate(scipy.stats.unitary_group.rvs(num_target)) + base_gate = random_unitary(2**num_target).to_instruction() base_mat = base_gate.to_matrix() cgate = base_gate.control(num_ctrl_qubits) test_op = Operator(cgate) cop_mat = _compute_control_matrix(base_mat, num_ctrl_qubits) self.assertTrue(matrix_equal(cop_mat, test_op.data, ignore_phase=True)) + @data(1, 2, 3) + def test_open_controlled_unitary_matrix(self, num_ctrl_qubits): + """test open controlled unitary matrix""" + # verify truth table + num_target_qubits = 2 + num_qubits = num_ctrl_qubits + num_target_qubits + target_op = Operator(XGate()) + for i in range(num_target_qubits - 1): + target_op = target_op.tensor(XGate()) + print('') + for i in range(2**num_qubits): + input_bitstring = bin(i)[2:].zfill(num_qubits) + input_target = input_bitstring[0:num_target_qubits] + input_ctrl = input_bitstring[-num_ctrl_qubits:] + phi = Statevector.from_label(input_bitstring) + cop = Operator(_compute_control_matrix(target_op.data, + num_ctrl_qubits, + ctrl_state=input_ctrl)) + for j in range(2**num_qubits): + output_bitstring = bin(j)[2:].zfill(num_qubits) + output_target = output_bitstring[0:num_target_qubits] + output_ctrl = output_bitstring[-num_ctrl_qubits:] + psi = Statevector.from_label(output_bitstring) + cxout = np.dot(phi.data, psi.evolve(cop).data) + if input_ctrl == output_ctrl: + # flip the target bits + cond_output = ''.join([str(int(not int(a))) for a in input_target]) + else: + cond_output = input_target + if cxout == 1: + self.assertTrue( + (output_ctrl == input_ctrl) and + (output_target == cond_output)) + else: + self.assertTrue( + ((output_ctrl == input_ctrl) and + (output_target != cond_output)) or + output_ctrl != input_ctrl) + def test_base_gate_setting(self): """ Test all gates in standard extensions which are of type ControlledGate @@ -431,7 +472,7 @@ def test_all_inverses(self): # only verify basic gates right now, as already controlled ones # will generate differing definitions with self.subTest(i=cls): - if issubclass(cls, ControlledGate) or cls == allGates.IdGate: + if issubclass(cls, ControlledGate) or issubclass(cls, allGates.IGate): continue try: sig = signature(cls) @@ -516,28 +557,103 @@ def test_relative_phase_toffoli_gates(self, num_ctrl_qubits): # compare simulated matrix with the matrix representation provided by the class self.assertTrue(matrix_equal(simulated_mat, repr_mat)) - -def _compute_control_matrix(base_mat, num_ctrl_qubits): - """ - Compute the controlled version of the input matrix with qiskit ordering. - - Args: - base_mat (ndarray): unitary to be controlled - num_ctrl_qubits (int): number of controls for new unitary - - Returns: - ndarray: controlled version of base matrix. - """ - num_target = int(np.log2(base_mat.shape[0])) - ctrl_dim = 2**num_ctrl_qubits - ctrl_grnd = np.repeat([[1], [0]], [1, ctrl_dim-1]) - full_mat_dim = ctrl_dim * base_mat.shape[0] - full_mat = np.zeros((full_mat_dim, full_mat_dim), dtype=base_mat.dtype) - ctrl_proj = np.diag(np.roll(ctrl_grnd, ctrl_dim - 1)) - full_mat = (np.kron(np.eye(2**num_target), - np.eye(ctrl_dim) - ctrl_proj) - + np.kron(base_mat, ctrl_proj)) - return full_mat + def test_open_controlled_gate(self): + """ + Test controlled gates with control on '0' + """ + base_gate = XGate() + base_mat = base_gate.to_matrix() + num_ctrl_qubits = 3 + + ctrl_state = 5 + cgate = base_gate.control(num_ctrl_qubits, ctrl_state=ctrl_state) + target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state) + self.assertEqual(Operator(cgate), Operator(target_mat)) + + ctrl_state = None + cgate = base_gate.control(num_ctrl_qubits, ctrl_state=ctrl_state) + target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state) + self.assertEqual(Operator(cgate), Operator(target_mat)) + + ctrl_state = 0 + cgate = base_gate.control(num_ctrl_qubits, ctrl_state=ctrl_state) + target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state) + self.assertEqual(Operator(cgate), Operator(target_mat)) + + ctrl_state = 7 + cgate = base_gate.control(num_ctrl_qubits, ctrl_state=ctrl_state) + target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state) + self.assertEqual(Operator(cgate), Operator(target_mat)) + + ctrl_state = '110' + cgate = base_gate.control(num_ctrl_qubits, ctrl_state=ctrl_state) + target_mat = _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=ctrl_state) + self.assertEqual(Operator(cgate), Operator(target_mat)) + + def test_open_controlled_gate_raises(self): + """ + Test controlled gates with open controls raises if ctrl_state isn't allowed. + """ + base_gate = XGate() + num_ctrl_qubits = 3 + with self.assertRaises(CircuitError): + base_gate.control(num_ctrl_qubits, ctrl_state=-1) + with self.assertRaises(CircuitError): + base_gate.control(num_ctrl_qubits, ctrl_state=2**num_ctrl_qubits) + with self.assertRaises(CircuitError): + base_gate.control(num_ctrl_qubits, ctrl_state='201') + + def test_deprecated_gates(self): + """Test types of the deprecated gate classes.""" + + import qiskit.extensions.standard.i as i + import qiskit.extensions.standard.rx as rx + import qiskit.extensions.standard.ry as ry + import qiskit.extensions.standard.rz as rz + import qiskit.extensions.standard.swap as swap + import qiskit.extensions.standard.u1 as u1 + import qiskit.extensions.standard.u3 as u3 + import qiskit.extensions.standard.x as x + import qiskit.extensions.standard.y as y + import qiskit.extensions.standard.z as z + + import qiskit.extensions.quantum_initializer.diagonal as diagonal + import qiskit.extensions.quantum_initializer.uc as uc + import qiskit.extensions.quantum_initializer.uc_pauli_rot as uc_pauli_rot + import qiskit.extensions.quantum_initializer.ucrx as ucrx + import qiskit.extensions.quantum_initializer.ucry as ucry + import qiskit.extensions.quantum_initializer.ucrz as ucrz + + theta, phi, lam = 0.1, 0.2, 0.3 + diag = [1, 1] + unitary = np.array([[1, 0], [0, 1]]) + renamend_gates = [ + (diagonal.DiagonalGate, diagonal.DiagGate, [diag]), + (i.IGate, i.IdGate, []), + (rx.CRXGate, rx.CrxGate, [theta]), + (ry.CRYGate, ry.CryGate, [theta]), + (rz.CRZGate, rz.CrzGate, [theta]), + (swap.CSwapGate, swap.FredkinGate, []), + (u1.CU1Gate, u1.Cu1Gate, [theta]), + (u3.CU3Gate, u3.Cu3Gate, [theta, phi, lam]), + (uc.UCGate, uc.UCG, [[unitary]]), + (uc_pauli_rot.UCPauliRotGate, uc_pauli_rot.UCRot, [[theta], 'X']), + (ucrx.UCRXGate, ucrx.UCX, [[theta]]), + (ucry.UCRYGate, ucry.UCY, [[theta]]), + (ucrz.UCRZGate, ucrz.UCZ, [[theta]]), + (x.CXGate, x.CnotGate, []), + (x.CCXGate, x.ToffoliGate, []), + (y.CYGate, y.CyGate, []), + (z.CZGate, z.CzGate, []), + ] + for new, old, params in renamend_gates: + with self.subTest(msg='comparing {} and {}'.format(new, old)): + # assert old gate class derives from new + self.assertTrue(issubclass(old, new)) + + # assert both are representatives of one another + self.assertTrue(isinstance(new(*params), old)) + self.assertTrue(isinstance(old(*params), new)) if __name__ == '__main__': diff --git a/test/python/circuit/test_diag.py b/test/python/circuit/test_diagonal_gate.py similarity index 95% rename from test/python/circuit/test_diag.py rename to test/python/circuit/test_diagonal_gate.py index 4b0bfbefcb3e..6be0aebb9f7b 100644 --- a/test/python/circuit/test_diag.py +++ b/test/python/circuit/test_diagonal_gate.py @@ -25,10 +25,11 @@ from qiskit.quantum_info.operators.predicates import matrix_equal -class TestDiagGate(QiskitTestCase): +class TestDiagonalGate(QiskitTestCase): """ Diagonal gate tests. """ + def test_diag_gate(self): """Test diagonal gates.""" for phases in [[0, 0], [0, 0.8], [0, 0, 1, 1], [0, 1, 0.5, 1], @@ -40,7 +41,7 @@ def test_diag_gate(self): num_qubits = int(np.log2(len(diag))) q = QuantumRegister(num_qubits) qc = QuantumCircuit(q) - qc.diag_gate(diag, q[0:num_qubits]) + qc.diagonal(diag, q[0:num_qubits]) # Decompose the gate qc = transpile(qc, basis_gates=['u1', 'u3', 'u2', 'cx', 'id']) # Simulate the decomposed gate diff --git a/test/python/circuit/test_extensions_standard.py b/test/python/circuit/test_extensions_standard.py index ecf2020d3c69..bb60bc667078 100644 --- a/test/python/circuit/test_extensions_standard.py +++ b/test/python/circuit/test_extensions_standard.py @@ -29,10 +29,10 @@ from qiskit.quantum_info.operators.predicates import matrix_equal, is_unitary_matrix from qiskit.extensions.standard import ( - HGate, CHGate, IdGate, RGate, RXGate, CrxGate, RYGate, CryGate, RZGate, - CrzGate, SGate, SdgGate, FredkinGate, TGate, TdgGate, U1Gate, Cu1Gate, - U2Gate, U3Gate, Cu3Gate, XGate, CnotGate, ToffoliGate, YGate, CyGate, - ZGate, CzGate + HGate, CHGate, IGate, RGate, RXGate, CRXGate, RYGate, CRYGate, RZGate, + CRZGate, SGate, SdgGate, CSwapGate, TGate, TdgGate, U1Gate, CU1Gate, + U2Gate, U3Gate, CU3Gate, XGate, CXGate, CCXGate, YGate, CYGate, + ZGate, CZGate ) @@ -383,33 +383,33 @@ def test_h_reg_inv(self): self.assertEqual(instruction_set.qargs[1], [self.qr[1]]) def test_iden(self): - self.circuit.iden(self.qr[1]) + self.circuit.i(self.qr[1]) op, _, _ = self.circuit[0] self.assertEqual(op.name, 'id') self.assertEqual(op.params, []) def test_iden_wires(self): - self.circuit.iden(1) + self.circuit.i(1) op, _, _ = self.circuit[0] self.assertEqual(op.name, 'id') self.assertEqual(op.params, []) def test_iden_invalid(self): qc = self.circuit - self.assertRaises(CircuitError, qc.iden, self.cr[0]) - self.assertRaises(CircuitError, qc.iden, self.cr) - self.assertRaises(CircuitError, qc.iden, (self.qr, 3)) - self.assertRaises(CircuitError, qc.iden, (self.qr, 'a')) - self.assertRaises(CircuitError, qc.iden, .0) + self.assertRaises(CircuitError, qc.i, self.cr[0]) + self.assertRaises(CircuitError, qc.i, self.cr) + self.assertRaises(CircuitError, qc.i, (self.qr, 3)) + self.assertRaises(CircuitError, qc.i, (self.qr, 'a')) + self.assertRaises(CircuitError, qc.i, .0) def test_iden_reg(self): - instruction_set = self.circuit.iden(self.qr) + instruction_set = self.circuit.i(self.qr) self.assertEqual(len(instruction_set.instructions), 3) self.assertEqual(instruction_set.instructions[0].name, 'id') self.assertEqual(instruction_set.qargs[1], [self.qr[1]]) def test_iden_reg_inv(self): - instruction_set = self.circuit.iden(self.qr).inverse() + instruction_set = self.circuit.i(self.qr).inverse() self.assertEqual(len(instruction_set.instructions), 3) self.assertEqual(instruction_set.instructions[0].name, 'id') self.assertEqual(instruction_set.qargs[1], [self.qr[1]]) @@ -1390,18 +1390,18 @@ class TestQubitKeywordArgRenaming(QiskitTestCase): @data( ('h', HGate, 0, [('q', 'qubit')]), ('ch', CHGate, 0, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), - ('iden', IdGate, 0, [('q', 'qubit')]), + ('id', IGate, 0, [('q', 'qubit')]), ('r', RGate, 2, [('q', 'qubit')]), ('rx', RXGate, 1, [('q', 'qubit')]), - ('crx', CrxGate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('crx', CRXGate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ('ry', RYGate, 1, [('q', 'qubit')]), - ('cry', CryGate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('cry', CRYGate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ('rz', RZGate, 1, [('q', 'qubit')]), - ('crz', CrzGate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('crz', CRZGate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ('s', SGate, 0, [('q', 'qubit')]), ('sdg', SdgGate, 0, [('q', 'qubit')]), ('cswap', - FredkinGate, + CSwapGate, 0, [('ctl', 'control_qubit'), ('tgt1', 'target_qubit1'), @@ -1409,22 +1409,22 @@ class TestQubitKeywordArgRenaming(QiskitTestCase): ('t', TGate, 0, [('q', 'qubit')]), ('tdg', TdgGate, 0, [('q', 'qubit')]), ('u1', U1Gate, 1, [('q', 'qubit')]), - ('cu1', Cu1Gate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('cu1', CU1Gate, 1, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ('u2', U2Gate, 2, [('q', 'qubit')]), ('u3', U3Gate, 3, [('q', 'qubit')]), - ('cu3', Cu3Gate, 3, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('cu3', CU3Gate, 3, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ('x', XGate, 0, [('q', 'qubit')]), - ('cx', CnotGate, 0, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('cx', CXGate, 0, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ('ccx', - ToffoliGate, + CCXGate, 0, [('ctl1', 'control_qubit1'), ('ctl2', 'control_qubit2'), ('tgt', 'target_qubit')]), ('y', YGate, 0, [('q', 'qubit')]), - ('cy', CyGate, 0, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('cy', CYGate, 0, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ('z', ZGate, 0, [('q', 'qubit')]), - ('cz', CzGate, 0, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), + ('cz', CZGate, 0, [('ctl', 'control_qubit'), ('tgt', 'target_qubit')]), ) # pylint: enable=bad-whitespace def test_kwarg_deprecation(self, instr_name, inst_class, n_params, kwarg_map): diff --git a/test/python/circuit/test_gate_power.py b/test/python/circuit/test_gate_power.py index 442f28c18b4c..3a1f1aff175f 100644 --- a/test/python/circuit/test_gate_power.py +++ b/test/python/circuit/test_gate_power.py @@ -22,7 +22,7 @@ from numpy.linalg import matrix_power from qiskit.test import QiskitTestCase -from qiskit.extensions import SGate, UnitaryGate, CnotGate +from qiskit.extensions import SGate, UnitaryGate, CXGate from qiskit.circuit import Gate from qiskit.quantum_info.operators import Operator @@ -76,7 +76,7 @@ class TestPowerIntCX(QiskitTestCase): def test_cx_int(self, n): """Test CX.power() method. """ - result = CnotGate().power(n) + result = CXGate().power(n) self.assertEqual(result.label, 'cx^' + str(n)) self.assertIsInstance(result, UnitaryGate) diff --git a/test/python/circuit/test_instruction_repeat.py b/test/python/circuit/test_instruction_repeat.py index 57595110c6a9..97fa9413c8be 100644 --- a/test/python/circuit/test_instruction_repeat.py +++ b/test/python/circuit/test_instruction_repeat.py @@ -21,7 +21,7 @@ from qiskit.transpiler import PassManager from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister from qiskit.test import QiskitTestCase -from qiskit.extensions import SGate, U3Gate, UnitaryGate, CnotGate +from qiskit.extensions import SGate, U3Gate, UnitaryGate, CXGate from qiskit.circuit import Instruction, Measure, Gate from qiskit.transpiler.passes import Unroller from qiskit.circuit.exceptions import CircuitError @@ -68,11 +68,11 @@ def test_standard_2Q_two(self): """ qr = QuantumRegister(2, 'qr') expected_circ = QuantumCircuit(qr) - expected_circ.append(CnotGate(), [qr[0], qr[1]]) - expected_circ.append(CnotGate(), [qr[0], qr[1]]) + expected_circ.append(CXGate(), [qr[0], qr[1]]) + expected_circ.append(CXGate(), [qr[0], qr[1]]) expected = expected_circ.to_instruction() - result = CnotGate().repeat(2) + result = CXGate().repeat(2) self.assertEqual(result.name, 'cx*2') self.assertEqual(result.definition, expected.definition) @@ -83,10 +83,10 @@ def test_standard_2Q_one(self): """ qr = QuantumRegister(2, 'qr') expected_circ = QuantumCircuit(qr) - expected_circ.append(CnotGate(), [qr[0], qr[1]]) + expected_circ.append(CXGate(), [qr[0], qr[1]]) expected = expected_circ.to_instruction() - result = CnotGate().repeat(1) + result = CXGate().repeat(1) self.assertEqual(result.name, 'cx*1') self.assertEqual(result.definition, expected.definition) @@ -205,7 +205,7 @@ def test_standard_2Q_minus_one(self): """Test standard 2Q gate.repeat(-1) method. Raises, since n<1. """ with self.assertRaises(CircuitError) as context: - _ = CnotGate().repeat(-1) + _ = CXGate().repeat(-1) self.assertIn('strictly positive integer', str(context.exception)) def test_measure_minus_one(self): @@ -219,7 +219,7 @@ def test_standard_2Q_zero(self): """Test standard 2Q gate.repeat(0) method. Raises, since n<1. """ with self.assertRaises(CircuitError) as context: - _ = CnotGate().repeat(0) + _ = CXGate().repeat(0) self.assertIn('strictly positive integer', str(context.exception)) diff --git a/test/python/circuit/test_instructions.py b/test/python/circuit/test_instructions.py index 4be158ef2de2..93195cc40e39 100644 --- a/test/python/circuit/test_instructions.py +++ b/test/python/circuit/test_instructions.py @@ -24,7 +24,7 @@ from qiskit.circuit import QuantumCircuit from qiskit.circuit import QuantumRegister, ClassicalRegister from qiskit.extensions.standard.h import HGate -from qiskit.extensions.standard.x import CnotGate +from qiskit.extensions.standard.x import CXGate from qiskit.extensions.standard.s import SGate from qiskit.extensions.standard.t import TGate from qiskit.test import QiskitTestCase @@ -53,7 +53,7 @@ def test_instructions_equal(self): self.assertFalse(uop1 == uop3) self.assertTrue(HGate() == HGate()) - self.assertFalse(HGate() == CnotGate()) + self.assertFalse(HGate() == CXGate()) self.assertFalse(hop1 == HGate()) eop1 = Instruction('kraus', 1, 0, [np.array([[1, 0], [0, 1]])]) @@ -117,7 +117,7 @@ def circuit_instruction_circuit_roundtrip(self): circ1 = QuantumCircuit(q, c, name='circuit1') circ1.h(q[0]) circ1.crz(0.1, q[0], q[1]) - circ1.iden(q[1]) + circ1.i(q[1]) circ1.u3(0.1, 0.2, -0.2, q[0]) circ1.barrier() circ1.measure(q, c) @@ -165,13 +165,13 @@ def test_mirror_gate(self): circ = QuantumCircuit(q, c, name='circ') circ.h(q[0]) circ.crz(0.1, q[0], q[1]) - circ.iden(q[1]) + circ.i(q[1]) circ.u3(0.1, 0.2, -0.2, q[0]) gate = circ.to_instruction() circ = QuantumCircuit(q, c, name='circ') circ.u3(0.1, 0.2, -0.2, q[0]) - circ.iden(q[1]) + circ.i(q[1]) circ.crz(0.1, q[0], q[1]) circ.h(q[0]) gate_mirror = circ.to_instruction() @@ -229,12 +229,12 @@ def test_inverse_composite_gate(self): circ = QuantumCircuit(q, name='circ') circ.h(q[0]) circ.crz(0.1, q[0], q[1]) - circ.iden(q[1]) + circ.i(q[1]) circ.u3(0.1, 0.2, -0.2, q[0]) gate = circ.to_instruction() circ = QuantumCircuit(q, name='circ') circ.u3(-0.1, 0.2, -0.2, q[0]) - circ.iden(q[1]) + circ.i(q[1]) circ.crz(-0.1, q[0], q[1]) circ.h(q[0]) gate_inverse = circ.to_instruction() @@ -252,12 +252,12 @@ def test_inverse_recursive(self): qr1 = QuantumRegister(4) circ1 = QuantumCircuit(qr1, name='circuit1') circ1.cu1(-0.1, qr1[0], qr1[2]) - circ1.iden(qr1[1]) + circ1.i(qr1[1]) circ1.append(little_gate, [qr1[2], qr1[3]]) circ_inv = QuantumCircuit(qr1, name='circ1_dg') circ_inv.append(little_gate.inverse(), [qr1[2], qr1[3]]) - circ_inv.iden(qr1[1]) + circ_inv.i(qr1[1]) circ_inv.cu1(0.1, qr1[0], qr1[2]) self.assertEqual(circ1.inverse(), circ_inv) diff --git a/test/python/circuit/test_isometry.py b/test/python/circuit/test_isometry.py index 50476ba7ec8e..ace679e093fd 100644 --- a/test/python/circuit/test_isometry.py +++ b/test/python/circuit/test_isometry.py @@ -48,7 +48,7 @@ def test_isometry(self): n = num_q_input + num_q_ancilla_for_output q = QuantumRegister(n) qc = QuantumCircuit(q) - qc.iso(iso, q[:num_q_input], q[num_q_input:]) + qc.isometry(iso, q[:num_q_input], q[num_q_input:]) # Verify the circuit can be decomposed self.assertIsInstance(qc.decompose(), QuantumCircuit) diff --git a/test/python/circuit/test_library.py b/test/python/circuit/test_library.py new file mode 100644 index 000000000000..225c2801dc83 --- /dev/null +++ b/test/python/circuit/test_library.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Test library of quantum circuits.""" + +from qiskit.test import QiskitTestCase +from qiskit.circuit import QuantumCircuit +from qiskit.circuit.exceptions import CircuitError +from qiskit.circuit.library import Permutation, XOR, InnerProduct + + +class TestBooleanLogicLibrary(QiskitTestCase): + """Test library of boolean logic quantum circuits.""" + + def test_permutation(self): + """Test permutation circuit.""" + circuit = Permutation(n_qubits=4, pattern=[1, 0, 3, 2]) + expected = QuantumCircuit(4) + expected.swap(0, 1) + expected.swap(2, 3) + self.assertEqual(circuit, expected) + + def test_permutation_bad(self): + """Test that [0,..,n-1] permutation is required (no -1 for last element)""" + self.assertRaises(CircuitError, Permutation, 4, [1, 0, -1, 2]) + + def test_xor(self): + """Test xor circuit.""" + circuit = XOR(n_qubits=3, amount=4) + expected = QuantumCircuit(3) + expected.x(2) + self.assertEqual(circuit, expected) + + def test_inner_product(self): + """Test inner product circuit.""" + circuit = InnerProduct(n_qubits=3) + expected = QuantumCircuit(*circuit.qregs) + expected.cz(0, 3) + expected.cz(1, 4) + expected.cz(2, 5) + self.assertEqual(circuit, expected) diff --git a/test/python/circuit/test_ucg.py b/test/python/circuit/test_uc.py similarity index 90% rename from test/python/circuit/test_ucg.py rename to test/python/circuit/test_uc.py index 240c8009c167..46748513e276 100644 --- a/test/python/circuit/test_ucg.py +++ b/test/python/circuit/test_uc.py @@ -26,7 +26,7 @@ import numpy as np from scipy.linalg import block_diag -from qiskit.extensions.quantum_initializer.ucg import UCG +from qiskit.extensions.quantum_initializer.uc import UCGate from qiskit import QuantumCircuit, QuantumRegister, BasicAer, execute from qiskit.test import QiskitTestCase @@ -45,8 +45,8 @@ up_to_diagonal_list = [True, False] -class TestUCG(QiskitTestCase): - """Qiskit UCG tests.""" +class TestUCGate(QiskitTestCase): + """Qiskit UCGate tests.""" def test_ucg(self): """Test uniformly controlled gates.""" @@ -55,7 +55,7 @@ def test_ucg(self): num_con = int(np.log2(len(squs))) q = QuantumRegister(num_con + 1) qc = QuantumCircuit(q) - qc.ucg(squs, q[1:], q[0], up_to_diagonal=up_to_diagonal) + qc.uc(squs, q[1:], q[0], up_to_diagonal=up_to_diagonal) # Decompose the gate qc = transpile(qc, basis_gates=['u1', 'u3', 'u2', 'cx', 'id']) # Simulate the decomposed gate @@ -63,7 +63,7 @@ def test_ucg(self): result = execute(qc, simulator).result() unitary = result.get_unitary(qc) if up_to_diagonal: - ucg = UCG(squs, up_to_diagonal=up_to_diagonal) + ucg = UCGate(squs, up_to_diagonal=up_to_diagonal) unitary = np.dot(np.diagflat(ucg._get_diagonal()), unitary) unitary_desired = _get_ucg_matrix(squs) self.assertTrue(matrix_equal(unitary_desired, unitary, ignore_phase=True)) diff --git a/test/python/circuit/test_ucx_y_z.py b/test/python/circuit/test_ucx_y_z.py index 801ad0fbf017..a5c3e3c8a111 100644 --- a/test/python/circuit/test_ucx_y_z.py +++ b/test/python/circuit/test_ucx_y_z.py @@ -36,8 +36,8 @@ rot_axis_list = ["X", "Y", "Z"] -class TestUCXYZ(QiskitTestCase): - """Qiskit tests for UCX, UCY and UCZ rotations gates.""" +class TestUCRXYZ(QiskitTestCase): + """Qiskit tests for UCRXGate, UCRYGate and UCRZGate rotations gates.""" def test_ucy(self): """ Test the decomposition of uniformly controlled rotations.""" @@ -47,11 +47,11 @@ def test_ucy(self): q = QuantumRegister(num_contr + 1) qc = QuantumCircuit(q) if rot_axis == "X": - qc.ucx(angles, q[1:num_contr + 1], q[0]) + qc.ucrx(angles, q[1:num_contr + 1], q[0]) elif rot_axis == "Y": - qc.ucy(angles, q[1:num_contr + 1], q[0]) + qc.ucry(angles, q[1:num_contr + 1], q[0]) else: - qc.ucz(angles, q[1:num_contr + 1], q[0]) + qc.ucrz(angles, q[1:num_contr + 1], q[0]) # Decompose the gate qc = transpile(qc, basis_gates=['u1', 'u3', 'u2', 'cx', 'id']) # Simulate the decomposed gate diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index 11db9b58a534..d050ec94cca0 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -25,7 +25,7 @@ from qiskit.compiler import transpile from qiskit.converters import circuit_to_dag from qiskit.dagcircuit.exceptions import DAGCircuitError -from qiskit.extensions.standard import CnotGate +from qiskit.extensions.standard import CXGate from qiskit.test import QiskitTestCase, Path from qiskit.test.mock import FakeMelbourne, FakeRueschlikon from qiskit.transpiler import Layout, CouplingMap @@ -118,7 +118,7 @@ def test_transpile_non_adjacent_layout(self): initial_layout=initial_layout) for gate, qargs, _ in new_circuit.data: - if isinstance(gate, CnotGate): + if isinstance(gate, CXGate): self.assertIn([x.index for x in qargs], coupling_map) def test_transpile_qft_grid(self): @@ -138,7 +138,7 @@ def test_transpile_qft_grid(self): coupling_map=coupling_map) for gate, qargs, _ in new_circuit.data: - if isinstance(gate, CnotGate): + if isinstance(gate, CXGate): self.assertIn([x.index for x in qargs], coupling_map) def test_already_mapped_1(self): diff --git a/test/python/pickles/TestsBasicSwap_a_cx_to_map.pickle b/test/python/pickles/TestsBasicSwap_a_cx_to_map.pickle index 2dc1af8cf877..21adf001e29c 100644 Binary files a/test/python/pickles/TestsBasicSwap_a_cx_to_map.pickle and b/test/python/pickles/TestsBasicSwap_a_cx_to_map.pickle differ diff --git a/test/python/pickles/TestsBasicSwap_handle_measurement.pickle b/test/python/pickles/TestsBasicSwap_handle_measurement.pickle index 27eb154a547d..ea6f4c1939be 100644 Binary files a/test/python/pickles/TestsBasicSwap_handle_measurement.pickle and b/test/python/pickles/TestsBasicSwap_handle_measurement.pickle differ diff --git a/test/python/pickles/TestsBasicSwap_initial_layout.pickle b/test/python/pickles/TestsBasicSwap_initial_layout.pickle index dc47de80b9fa..ed250fc70d8d 100644 Binary files a/test/python/pickles/TestsBasicSwap_initial_layout.pickle and b/test/python/pickles/TestsBasicSwap_initial_layout.pickle differ diff --git a/test/python/pickles/TestsLookaheadSwap_a_cx_to_map.pickle b/test/python/pickles/TestsLookaheadSwap_a_cx_to_map.pickle index 1528506f8ca0..036c38571c3f 100644 Binary files a/test/python/pickles/TestsLookaheadSwap_a_cx_to_map.pickle and b/test/python/pickles/TestsLookaheadSwap_a_cx_to_map.pickle differ diff --git a/test/python/pickles/TestsLookaheadSwap_handle_measurement.pickle b/test/python/pickles/TestsLookaheadSwap_handle_measurement.pickle index 8b3dc7f56807..d9b2bcfd85e6 100644 Binary files a/test/python/pickles/TestsLookaheadSwap_handle_measurement.pickle and b/test/python/pickles/TestsLookaheadSwap_handle_measurement.pickle differ diff --git a/test/python/pickles/TestsLookaheadSwap_initial_layout.pickle b/test/python/pickles/TestsLookaheadSwap_initial_layout.pickle index 90be0799bc10..5db1e176b27e 100644 Binary files a/test/python/pickles/TestsLookaheadSwap_initial_layout.pickle and b/test/python/pickles/TestsLookaheadSwap_initial_layout.pickle differ diff --git a/test/python/pickles/TestsStochasticSwap_a_cx_to_map.pickle b/test/python/pickles/TestsStochasticSwap_a_cx_to_map.pickle index 2cfd35193b9a..b34b078fcae7 100644 Binary files a/test/python/pickles/TestsStochasticSwap_a_cx_to_map.pickle and b/test/python/pickles/TestsStochasticSwap_a_cx_to_map.pickle differ diff --git a/test/python/pickles/TestsStochasticSwap_handle_measurement.pickle b/test/python/pickles/TestsStochasticSwap_handle_measurement.pickle index 5491cda0cc0c..b14c896be569 100644 Binary files a/test/python/pickles/TestsStochasticSwap_handle_measurement.pickle and b/test/python/pickles/TestsStochasticSwap_handle_measurement.pickle differ diff --git a/test/python/pickles/TestsStochasticSwap_initial_layout.pickle b/test/python/pickles/TestsStochasticSwap_initial_layout.pickle index 15fc03083b37..41dd643ab0a6 100644 Binary files a/test/python/pickles/TestsStochasticSwap_initial_layout.pickle and b/test/python/pickles/TestsStochasticSwap_initial_layout.pickle differ diff --git a/test/python/qasm/all_gates.qasm b/test/python/qasm/all_gates.qasm new file mode 100644 index 000000000000..2763e3704ac0 --- /dev/null +++ b/test/python/qasm/all_gates.qasm @@ -0,0 +1,55 @@ +OPENQASM 2.0; +include "qelib1.inc"; +qreg q[3]; +creg c[3]; + +// note that the order and where the gates are applied to is important! + +// the hardware primitives +u3(0.2,0.1,0.6) q[0]; +u2(0.1,0.6) q[0]; +u1(0.6) q[0]; +id q[0]; +cx q[0], q[1]; + +// the standard single qubit gates +x q[0]; +y q[0]; +z q[0]; +h q[0]; +s q[0]; +t q[0]; +sdg q[0]; +tdg q[0]; + +// the standard rotations +rx(0.1) q[0]; +ry(0.1) q[0]; +rz(0.1) q[0]; + +// the barrier +barrier q; + +// the standard user-defined gates +swap q[0], q[1]; +cswap q[0], q[1], q[2]; + +cy q[0], q[1]; +cz q[0], q[1]; +ch q[0], q[1]; +cu1(0.6) q[0], q[1]; +cu3(0.2,0.1,0.6) q[0], q[1]; +ccx q[0], q[1], q[2]; + +crx(0.6) q[0], q[1]; +cry(0.6) q[0], q[1]; +crz(0.6) q[0], q[1]; + +rxx(0.2) q[0], q[1]; +rzz(0.2) q[0], q[1]; + +// measure +measure q->c; + + + diff --git a/test/python/quantum_info/operators/test_operator.py b/test/python/quantum_info/operators/test_operator.py index db40a34af5c7..ba29b9a1b32a 100644 --- a/test/python/quantum_info/operators/test_operator.py +++ b/test/python/quantum_info/operators/test_operator.py @@ -24,7 +24,7 @@ from qiskit import QiskitError from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit -from qiskit.extensions.standard import HGate, CHGate, CnotGate +from qiskit.extensions.standard import HGate, CHGate, CXGate from qiskit.test import QiskitTestCase from qiskit.quantum_info.operators.operator import Operator from qiskit.quantum_info.operators.predicates import matrix_equal @@ -173,7 +173,7 @@ def test_circuit_init(self): def test_instruction_init(self): """Test initialization from a circuit.""" - gate = CnotGate() + gate = CXGate() op = Operator(gate).data target = gate.to_matrix() global_phase_equivalent = matrix_equal(op, target, ignore_phase=True) diff --git a/test/python/quantum_info/test_synthesis.py b/test/python/quantum_info/test_synthesis.py index 8e729630e9a6..c857a8cbeef6 100644 --- a/test/python/quantum_info/test_synthesis.py +++ b/test/python/quantum_info/test_synthesis.py @@ -21,8 +21,8 @@ from qiskit import execute from qiskit.circuit import QuantumCircuit, QuantumRegister from qiskit.extensions import UnitaryGate -from qiskit.extensions.standard import (HGate, IdGate, SdgGate, SGate, U3Gate, - XGate, YGate, ZGate, CnotGate) +from qiskit.extensions.standard import (HGate, IGate, SdgGate, SGate, U3Gate, + XGate, YGate, ZGate, CXGate) from qiskit.providers.basicaer import UnitarySimulatorPy from qiskit.quantum_info.operators import Operator, Pauli from qiskit.quantum_info.random import random_unitary @@ -38,9 +38,9 @@ def make_oneq_cliffords(): """Make as list of 1q Cliffords""" - ixyz_list = [g().to_matrix() for g in (IdGate, XGate, YGate, ZGate)] - ih_list = [g().to_matrix() for g in (IdGate, HGate)] - irs_list = [IdGate().to_matrix(), + ixyz_list = [g().to_matrix() for g in (IGate, XGate, YGate, ZGate)] + ih_list = [g().to_matrix() for g in (IGate, HGate)] + irs_list = [IGate().to_matrix(), SdgGate().to_matrix() @ HGate().to_matrix(), HGate().to_matrix() @ SGate().to_matrix()] oneq_cliffords = [Operator(ixyz @ ih @ irs) for ixyz in ixyz_list @@ -401,7 +401,7 @@ def check_exact_decomposition(self, target_unitary, decomposer, tolerance=1.e-7) def test_cnot_rxx_decompose(self): """Verify CNOT decomposition into RXX gate is correct""" - cnot = Operator(CnotGate()) + cnot = Operator(CXGate()) decomps = [cnot_rxx_decompose(), cnot_rxx_decompose(plus_ry=True, plus_rxx=True), cnot_rxx_decompose(plus_ry=True, plus_rxx=False), diff --git a/test/python/test_dagcircuit.py b/test/python/test_dagcircuit.py index 76fb01ac7f0c..e86ec690a5dc 100644 --- a/test/python/test_dagcircuit.py +++ b/test/python/test_dagcircuit.py @@ -27,10 +27,10 @@ from qiskit.circuit import Measure from qiskit.circuit import Reset from qiskit.circuit import Gate, Instruction -from qiskit.extensions.standard.iden import IdGate +from qiskit.extensions.standard.i import IGate from qiskit.extensions.standard.h import HGate -from qiskit.extensions.standard.x import CnotGate -from qiskit.extensions.standard.z import CzGate +from qiskit.extensions.standard.x import CXGate +from qiskit.extensions.standard.z import CZGate from qiskit.extensions.standard.x import XGate from qiskit.extensions.standard.u1 import U1Gate from qiskit.extensions.standard.barrier import Barrier @@ -196,7 +196,7 @@ def setUp(self): def test_apply_operation_back(self): """The apply_operation_back() method.""" self.dag.apply_operation_back(HGate(), [self.qubit0], [], condition=None) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], [], condition=None) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [], condition=None) self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], [], condition=None) self.dag.apply_operation_back(XGate(), [self.qubit1], [], condition=self.condition) self.dag.apply_operation_back(Measure(), [self.qubit0, self.clbit0], [], condition=None) @@ -335,7 +335,7 @@ def test_apply_operation_front(self): def test_get_op_nodes_all(self): """The method dag.op_nodes() returns all op nodes""" self.dag.apply_operation_back(HGate(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Reset(), [self.qubit0], []) op_nodes = self.dag.op_nodes() @@ -350,7 +350,7 @@ def test_get_op_nodes_particular(self): self.dag.apply_operation_back(HGate(), [self.qubit1], []) self.dag.apply_operation_back(Reset(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) op_nodes = self.dag.op_nodes(op=HGate) self.assertEqual(len(op_nodes), 2) @@ -364,7 +364,7 @@ def test_get_op_nodes_particular(self): def test_quantum_successors(self): """The method dag.quantum_successors() returns successors connected by quantum edges""" self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Reset(), [self.qubit0], []) successor_measure = self.dag.quantum_successors( @@ -373,7 +373,7 @@ def test_quantum_successors(self): with self.assertRaises(StopIteration): next(successor_measure) - self.assertIsInstance(cnot_node.op, CnotGate) + self.assertIsInstance(cnot_node.op, CXGate) successor_cnot = self.dag.quantum_successors(cnot_node) self.assertEqual(next(successor_cnot).type, 'out') @@ -384,7 +384,7 @@ def test_quantum_successors(self): def test_quantum_predecessors(self): """The method dag.quantum_predecessors() returns predecessors connected by quantum edges""" self.dag.apply_operation_back(Reset(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], []) predecessor_measure = self.dag.quantum_predecessors( @@ -393,7 +393,7 @@ def test_quantum_predecessors(self): with self.assertRaises(StopIteration): next(predecessor_measure) - self.assertIsInstance(cnot_node.op, CnotGate) + self.assertIsInstance(cnot_node.op, CXGate) predecessor_cnot = self.dag.quantum_predecessors(cnot_node) self.assertIsInstance(next(predecessor_cnot).op, Reset) @@ -404,7 +404,7 @@ def test_quantum_predecessors(self): def test_get_gates_nodes(self): """The method dag.gate_nodes() returns all gate nodes""" self.dag.apply_operation_back(HGate(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Reset(), [self.qubit0], []) op_nodes = self.dag.gate_nodes() @@ -419,7 +419,7 @@ def test_get_gates_nodes(self): def test_two_q_gates(self): """The method dag.twoQ_gates() returns all 2Q gate nodes""" self.dag.apply_operation_back(HGate(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Barrier(2), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Reset(), [self.qubit0], []) @@ -432,10 +432,10 @@ def test_two_q_gates(self): def test_get_named_nodes(self): """The get_named_nodes(AName) method returns all the nodes with name AName""" - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(HGate(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit2, self.qubit1], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit2], []) + self.dag.apply_operation_back(CXGate(), [self.qubit2, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit2], []) self.dag.apply_operation_back(HGate(), [self.qubit2], []) # The ordering is not assured, so we only compare the output (unordered) sets. @@ -453,10 +453,10 @@ def test_get_named_nodes(self): def test_topological_nodes(self): """The topological_nodes() method""" - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(HGate(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit2, self.qubit1], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit2], []) + self.dag.apply_operation_back(CXGate(), [self.qubit2, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit2], []) self.dag.apply_operation_back(HGate(), [self.qubit2], []) named_nodes = self.dag.topological_nodes() @@ -480,10 +480,10 @@ def test_topological_nodes(self): def test_topological_op_nodes(self): """The topological_op_nodes() method""" - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(HGate(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit2, self.qubit1], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit2], []) + self.dag.apply_operation_back(CXGate(), [self.qubit2, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit2], []) self.dag.apply_operation_back(HGate(), [self.qubit2], []) named_nodes = self.dag.topological_op_nodes() @@ -497,7 +497,7 @@ def test_topological_op_nodes(self): def test_dag_nodes_on_wire(self): """Test that listing the gates on a qubit/classical bit gets the correct gates""" - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(HGate(), [self.qubit0], []) qbit = self.dag.qubits()[0] @@ -524,9 +524,9 @@ def test_dag_nodes_on_wire_multiple_successors(self): Both the 2nd CX gate and the H gate follow the first CX gate in the DAG, so they both must be returned but in the correct order. """ - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(HGate(), [self.qubit1], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) nodes = self.dag.nodes_on_wire(self.dag.qubits()[1], only_ops=True) node_names = [nd.name for nd in nodes] @@ -545,10 +545,10 @@ def test_remove_op_node(self): def test_remove_op_node_longer(self): """Test remove_op_node method in a "longer" dag""" - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1]) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1]) self.dag.apply_operation_back(HGate(), [self.qubit0]) - self.dag.apply_operation_back(CnotGate(), [self.qubit2, self.qubit1]) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit2]) + self.dag.apply_operation_back(CXGate(), [self.qubit2, self.qubit1]) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit2]) self.dag.apply_operation_back(HGate(), [self.qubit2]) op_nodes = list(self.dag.topological_op_nodes()) @@ -573,8 +573,8 @@ def test_dag_collect_runs(self): self.dag.apply_operation_back(U1Gate(3.14), [self.qubit0]) self.dag.apply_operation_back(U1Gate(3.14), [self.qubit0]) self.dag.apply_operation_back(U1Gate(3.14), [self.qubit0]) - self.dag.apply_operation_back(CnotGate(), [self.qubit2, self.qubit1]) - self.dag.apply_operation_back(CnotGate(), [self.qubit1, self.qubit2]) + self.dag.apply_operation_back(CXGate(), [self.qubit2, self.qubit1]) + self.dag.apply_operation_back(CXGate(), [self.qubit1, self.qubit2]) self.dag.apply_operation_back(HGate(), [self.qubit2]) collected_runs = self.dag.collect_runs(['u1', 'cx', 'h']) self.assertEqual(len(collected_runs), 3) @@ -643,7 +643,7 @@ def test_layers_basic(self): dag.add_qreg(qreg) dag.add_creg(creg) dag.apply_operation_back(HGate(), [qubit0], []) - dag.apply_operation_back(CnotGate(), [qubit0, qubit1], [], condition=None) + dag.apply_operation_back(CXGate(), [qubit0, qubit1], [], condition=None) dag.apply_operation_back(Measure(), [qubit1, clbit1], [], condition=None) dag.apply_operation_back(XGate(), [qubit1], [], condition=condition) dag.apply_operation_back(Measure(), [qubit0, clbit0], [], condition=None) @@ -680,7 +680,7 @@ def test_layers_maintains_order(self): qc.x(0) dag = circuit_to_dag(qc) dag1 = list(dag.layers())[0]['graph'] - dag1.apply_operation_back(IdGate(), [qr[0]], []) + dag1.apply_operation_back(IGate(), [qr[0]], []) comp = [(nd.type, nd.name, nd._node_id) for nd in dag1.topological_nodes()] self.assertEqual(comp, truth) @@ -833,19 +833,19 @@ def setUp(self): self.condition = (creg, 3) self.dag.apply_operation_back(HGate(), [self.qubit0], []) - self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(XGate(), [self.qubit1], []) def test_substitute_circuit_one_middle(self): """The method substitute_node_with_dag() replaces a in-the-middle node with a DAG.""" - cx_node = self.dag.op_nodes(op=CnotGate).pop() + cx_node = self.dag.op_nodes(op=CXGate).pop() flipped_cx_circuit = DAGCircuit() v = QuantumRegister(2, "v") flipped_cx_circuit.add_qreg(v) flipped_cx_circuit.apply_operation_back(HGate(), [v[0]], []) flipped_cx_circuit.apply_operation_back(HGate(), [v[1]], []) - flipped_cx_circuit.apply_operation_back(CnotGate(), [v[1], v[0]], []) + flipped_cx_circuit.apply_operation_back(CXGate(), [v[1], v[0]], []) flipped_cx_circuit.apply_operation_back(HGate(), [v[0]], []) flipped_cx_circuit.apply_operation_back(HGate(), [v[1]], []) @@ -897,7 +897,7 @@ def test_substituting_node_with_wrong_width_node_raises(self): dag = DAGCircuit() qr = QuantumRegister(2) dag.add_qreg(qr) - node_to_be_replaced = dag.apply_operation_back(CnotGate(), [qr[0], qr[1]]) + node_to_be_replaced = dag.apply_operation_back(CXGate(), [qr[0], qr[1]]) with self.assertRaises(DAGCircuitError) as _: dag.substitute_node(node_to_be_replaced, Measure()) @@ -923,12 +923,12 @@ def test_substituting_node_preserves_args_condition(self, inplace): dag.add_qreg(qr) dag.add_creg(cr) dag.apply_operation_back(HGate(), [qr[1]]) - node_to_be_replaced = dag.apply_operation_back(CnotGate(), [qr[1], qr[0]], + node_to_be_replaced = dag.apply_operation_back(CXGate(), [qr[1], qr[0]], condition=(cr, 1)) dag.apply_operation_back(HGate(), [qr[1]]) - replacement_node = dag.substitute_node(node_to_be_replaced, CzGate(), + replacement_node = dag.substitute_node(node_to_be_replaced, CZGate(), inplace=inplace) raise_if_dagcircuit_invalid(dag) diff --git a/test/python/test_qasm_parser.py b/test/python/test_qasm_parser.py index 8be70a1b5f8b..761a386555e0 100644 --- a/test/python/test_qasm_parser.py +++ b/test/python/test_qasm_parser.py @@ -48,10 +48,20 @@ def test_parser(self): res = parse(self.qasm_file_path) self.log.info(res) # TODO: For now only some basic checks. - self.assertEqual(len(res), 2358) - self.assertEqual(res[:12], "OPENQASM 2.0") - self.assertEqual(res[14:41], "gate u3(theta,phi,lambda) q") - self.assertEqual(res[2342:2357], "measure r -> d;") + starts_expected = "OPENQASM 2.0;\ngate " + ends_expected = '\n'.join(['}', + 'qreg q[3];', + 'qreg r[3];', + 'h q;', + 'cx q,r;', + 'creg c[3];', + 'creg d[3];', + 'barrier q;', + 'measure q -> c;', + 'measure r -> d;', '']) + + self.assertEqual(res[:len(starts_expected)], starts_expected) + self.assertEqual(res[-len(ends_expected):], ends_expected) def test_parser_fail(self): """should fail a for a not valid circuit.""" @@ -61,6 +71,7 @@ def test_parser_fail(self): def test_all_valid_nodes(self): """Test that the tree contains only Node subclasses.""" + def inspect(node): """Inspect node children.""" for child in node.children: diff --git a/test/python/transpiler/test_collect_2q_blocks.py b/test/python/transpiler/test_collect_2q_blocks.py index 5dc3c619f6b9..fa4d07aea38a 100644 --- a/test/python/transpiler/test_collect_2q_blocks.py +++ b/test/python/transpiler/test_collect_2q_blocks.py @@ -67,21 +67,21 @@ def test_block_interrupted_by_gate(self): blocks : [['cx', 'id', 'id', 'id'], ['id', 'cx']] - ┌───┐┌────┐┌─┐ ┌────┐┌───┐ - q_0: |0>┤ X ├┤ Id ├┤M├──────┤ Id ├┤ X ├ - └─┬─┘├────┤└╥┘┌────┐└────┘└─┬─┘ - q_1: |0>──■──┤ Id ├─╫─┤ Id ├────────■── - └────┘ ║ └────┘ - c_0: 0 ════════════╩══════════════════ + ┌───┐┌───┐┌─┐ ┌───┐┌───┐ + q_0: |0>┤ X ├┤ I ├┤M├─────┤ I ├┤ X ├ + └─┬─┘├───┤└╥┘┌───┐└───┘└─┬─┘ + q_1: |0>──■──┤ I ├─╫─┤ I ├───────■── + └───┘ ║ └───┘ + c_0: 0 ═══════════╩════════════════ """ qc = QuantumCircuit(2, 1) qc.cx(1, 0) - qc.iden(0) - qc.iden(1) + qc.i(0) + qc.i(1) qc.measure(0, 0) - qc.iden(0) - qc.iden(1) + qc.i(0) + qc.i(1) qc.cx(1, 0) dag = circuit_to_dag(qc) @@ -89,7 +89,7 @@ def test_block_interrupted_by_gate(self): pass_.run(dag) # list from Collect2QBlocks of nodes that it should have put into blocks - good_names = ["cx", "u1", "u2", "u3", "id"] + good_names = ['cx', 'u1', 'u2', 'u3', 'id'] dag_nodes = [node for node in dag.topological_op_nodes() if node.name in good_names] # we have to convert them to sets as the ordering can be different diff --git a/test/python/transpiler/test_consolidate_blocks.py b/test/python/transpiler/test_consolidate_blocks.py index ea9df3c197d1..2f4639c71645 100644 --- a/test/python/transpiler/test_consolidate_blocks.py +++ b/test/python/transpiler/test_consolidate_blocks.py @@ -190,10 +190,10 @@ def test_node_added_before_block(self): c_0: 0 ══╩══════════════ """ qc = QuantumCircuit(2, 1) - qc.iden(0) + qc.i(0) qc.measure(1, 0) qc.cx(1, 0) - qc.iden(1) + qc.i(1) # can't just add all the nodes to one block as in other tests # as we are trying to test the block gets added in the correct place @@ -224,9 +224,9 @@ def test_node_added_after_block(self): """ qc = QuantumCircuit(3) qc.cx(1, 2) - qc.iden(1) + qc.i(1) qc.cx(0, 1) - qc.iden(2) + qc.i(2) pass_manager = PassManager() pass_manager.append(Collect2qBlocks()) @@ -255,13 +255,13 @@ def test_node_middle_of_blocks(self): qc = QuantumCircuit(4) qc.cx(0, 1) qc.cx(3, 2) - qc.iden(1) - qc.iden(2) + qc.i(1) + qc.i(2) qc.swap(1, 2) - qc.iden(1) - qc.iden(2) + qc.i(1) + qc.i(2) qc.cx(0, 1) qc.cx(3, 2) diff --git a/test/python/transpiler/test_decompose.py b/test/python/transpiler/test_decompose.py index c09952c514ad..7cd4ee0f997c 100644 --- a/test/python/transpiler/test_decompose.py +++ b/test/python/transpiler/test_decompose.py @@ -20,7 +20,7 @@ from qiskit.transpiler.passes import Decompose from qiskit.converters import circuit_to_dag from qiskit.extensions.standard import HGate -from qiskit.extensions.standard import ToffoliGate +from qiskit.extensions.standard import CCXGate from qiskit.test import QiskitTestCase @@ -63,7 +63,7 @@ def test_decompose_toffoli(self): circuit = QuantumCircuit(qr1, qr2) circuit.ccx(qr1[0], qr1[1], qr2[0]) dag = circuit_to_dag(circuit) - pass_ = Decompose(ToffoliGate) + pass_ = Decompose(CCXGate) after_dag = pass_.run(dag) op_nodes = after_dag.op_nodes() self.assertEqual(len(op_nodes), 15) diff --git a/test/python/transpiler/test_optimize_1q_gates.py b/test/python/transpiler/test_optimize_1q_gates.py index d94689aea7e8..1922f522d779 100644 --- a/test/python/transpiler/test_optimize_1q_gates.py +++ b/test/python/transpiler/test_optimize_1q_gates.py @@ -39,8 +39,8 @@ def test_dont_optimize_id(self): """ qr = QuantumRegister(1, 'qr') circuit = QuantumCircuit(qr) - circuit.iden(qr) - circuit.iden(qr) + circuit.i(qr) + circuit.i(qr) dag = circuit_to_dag(circuit) pass_ = Optimize1qGates() diff --git a/test/python/transpiler/test_passmanager_run.py b/test/python/transpiler/test_passmanager_run.py index 8d2c9911ef18..b9e1b522d705 100644 --- a/test/python/transpiler/test_passmanager_run.py +++ b/test/python/transpiler/test_passmanager_run.py @@ -15,7 +15,7 @@ """Tests PassManager.run()""" from qiskit import QuantumRegister, QuantumCircuit -from qiskit.extensions.standard import CnotGate +from qiskit.extensions.standard import CXGate from qiskit.transpiler.preset_passmanagers import level_1_pass_manager from qiskit.test import QiskitTestCase from qiskit.test.mock import FakeMelbourne @@ -64,7 +64,7 @@ def test_default_pass_manager_single(self): new_circuit = pass_manager.run(circuit) for gate, qargs, _ in new_circuit.data: - if isinstance(gate, CnotGate): + if isinstance(gate, CXGate): self.assertIn([x.index for x in qargs], coupling_map) def test_default_pass_manager_two(self): @@ -111,5 +111,5 @@ def test_default_pass_manager_two(self): for new_circuit in new_circuits: for gate, qargs, _ in new_circuit.data: - if isinstance(gate, CnotGate): + if isinstance(gate, CXGate): self.assertIn([x.index for x in qargs], coupling_map) diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py index ef77cb7f8198..7d9c8b8d13bb 100644 --- a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -219,7 +219,7 @@ class TesRemoveDiagonalControlGatesBeforeMeasure(QiskitTestCase): """ Test remove diagonal control gates before measure. """ def test_optimize_1cz_2measure(self): - """ Remove a single CzGate + """ Remove a single CZGate qr0:--Z--m--- qr0:--m--- | | | qr1:--.--|-m- ==> qr1:--|-m- @@ -244,7 +244,7 @@ def test_optimize_1cz_2measure(self): self.assertEqual(circuit_to_dag(expected), after) def test_optimize_1crz_2measure(self): - """ Remove a single CrzGate + """ Remove a single CRZGate qr0:-RZ--m--- qr0:--m--- | | | qr1:--.--|-m- ==> qr1:--|-m- @@ -269,7 +269,7 @@ def test_optimize_1crz_2measure(self): self.assertEqual(circuit_to_dag(expected), after) def test_optimize_1cu1_2measure(self): - """ Remove a single Cu1Gate + """ Remove a single CU1Gate qr0:-CU1-m--- qr0:--m--- | | | qr1:--.--|-m- ==> qr1:--|-m- @@ -323,7 +323,7 @@ class TestRemoveDiagonalGatesBeforeMeasureOveroptimizations(QiskitTestCase): """ Test situations where remove_diagonal_gates_before_measure should not optimize """ def test_optimize_1cz_1measure(self): - """ Do not remove a CzGate because measure happens on only one of the wires + """ Do not remove a CZGate because measure happens on only one of the wires Compare with test_optimize_1cz_2measure. qr0:--Z--m--- diff --git a/test/python/transpiler/test_unroller.py b/test/python/transpiler/test_unroller.py index f4ec79eef7bf..5c1c4d883ce2 100644 --- a/test/python/transpiler/test_unroller.py +++ b/test/python/transpiler/test_unroller.py @@ -125,7 +125,7 @@ def test_unroll_all_instructions(self): circuit.cy(qr[1], qr[2]) circuit.cz(qr[2], qr[0]) circuit.h(qr[1]) - circuit.iden(qr[0]) + circuit.i(qr[0]) circuit.rx(0.1, qr[0]) circuit.ry(0.2, qr[1]) circuit.rz(0.3, qr[2]) @@ -220,7 +220,7 @@ def test_unroll_all_instructions(self): ref_circuit.u3(0, 0, pi/2, qr[2]) ref_circuit.cx(qr[2], qr[0]) ref_circuit.u3(pi/2, 0, pi, qr[0]) - ref_circuit.iden(qr[0]) + ref_circuit.i(qr[0]) ref_circuit.u3(0.1, -pi/2, pi/2, qr[0]) ref_circuit.cx(qr[1], qr[0]) ref_circuit.u3(0, 0, 0.6, qr[0]) diff --git a/test/python/visualization/references/circuit_text_ref.txt b/test/python/visualization/references/circuit_text_ref.txt index e2a89e2967bf..4edf26f864f6 100644 --- a/test/python/visualization/references/circuit_text_ref.txt +++ b/test/python/visualization/references/circuit_text_ref.txt @@ -1,13 +1,13 @@ - ┌───┐┌───┐┌───┐ ░ ┌───┐┌───┐┌─────┐┌───┐┌─────┐┌────┐ ┌────────┐┌────────┐┌────────┐┌────────┐┌───────────┐┌──────────────┐ ┌─┐ -q_0: |0>┤ X ├┤ Y ├┤ Z ├─░─┤ H ├┤ S ├┤ Sdg ├┤ T ├┤ Tdg ├┤ Id ├─|0>─┤ Rx(pi) ├┤ Ry(pi) ├┤ Rz(pi) ├┤ U1(pi) ├┤ U2(pi,pi) ├┤ U3(pi,pi,pi) ├─X───■────■───■───■───■──────────■────────────■─────────■─────────■───────■───■─┤M├────── - └─░─┘└───┘└───┘ ░ └───┘└───┘└─────┘└───┘└─────┘└────┘ └────────┘└────────┘└────────┘└────────┘└───────────┘└──────────────┘ │ ┌─┴─┐┌─┴─┐ │ ┌─┴─┐ │pi ┌──────┴───────┐┌───┴────┐┌───┴────┐┌───┴────┐ │ │ └╥┘┌─┐ -q_1: |0>──░─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────X─┤ X ├┤ Y ├─■─┤ H ├─■───┤ U3(pi,pi,pi) ├┤ Rz(pi) ├┤ Ry(pi) ├┤ Rx(pi) ├──■───X──╫─┤M├─── - ░ └───┘└───┘ └───┘ └──────────────┘└────────┘└────────┘└────────┘┌─┴─┐ │ ║ └╥┘┌─┐ -q_2: |0>──░────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ X ├─X──╫──╫─┤M├ - ░ └───┘ ║ ║ └╥┘ - c_0: 0 ════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩══╬══╬═ - ║ ║ - c_1: 0 ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩══╬═ - ║ - c_2: 0 ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩═ - \ No newline at end of file + ┌───┐┌───┐┌───┐ ░ ┌───┐┌───┐┌─────┐┌───┐┌─────┐┌───┐ ┌────────┐┌────────┐┌────────┐┌────────┐┌───────────┐┌──────────────┐ ┌─┐ +q_0: |0>┤ X ├┤ Y ├┤ Z ├─░─┤ H ├┤ S ├┤ Sdg ├┤ T ├┤ Tdg ├┤ I ├─|0>─┤ Rx(pi) ├┤ Ry(pi) ├┤ Rz(pi) ├┤ U1(pi) ├┤ U2(pi,pi) ├┤ U3(pi,pi,pi) ├─X───■────■───■───■───■──────────■────────────■─────────■─────────■───────■───■─┤M├────── + └─░─┘└───┘└───┘ ░ └───┘└───┘└─────┘└───┘└─────┘└───┘ └────────┘└────────┘└────────┘└────────┘└───────────┘└──────────────┘ │ ┌─┴─┐┌─┴─┐ │ ┌─┴─┐ │pi ┌──────┴───────┐┌───┴────┐┌───┴────┐┌───┴────┐ │ │ └╥┘┌─┐ +q_1: |0>──░────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────X─┤ X ├┤ Y ├─■─┤ H ├─■───┤ U3(pi,pi,pi) ├┤ Rz(pi) ├┤ Ry(pi) ├┤ Rx(pi) ├──■───X──╫─┤M├─── + ░ └───┘└───┘ └───┘ └──────────────┘└────────┘└────────┘└────────┘┌─┴─┐ │ ║ └╥┘┌─┐ +q_2: |0>──░───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ X ├─X──╫──╫─┤M├ + ░ └───┘ ║ ║ └╥┘ + c_0: 0 ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩══╬══╬═ + ║ ║ + c_1: 0 ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩══╬═ + ║ + c_2: 0 ═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩═ + \ No newline at end of file diff --git a/test/python/visualization/references/circuit_text_ref_original.txt b/test/python/visualization/references/circuit_text_ref_original.txt new file mode 100644 index 000000000000..c4c693a71966 --- /dev/null +++ b/test/python/visualization/references/circuit_text_ref_original.txt @@ -0,0 +1,12 @@ + ┌───┐┌───┐┌───┐ ░ ┌───┐┌───┐┌─────┐┌───┐┌─────┐┌────┐ ┌────────┐┌────────┐┌────────┐┌────────┐┌───────────┐┌──────────────┐ ┌─┐ +q_0: |0>┤ X ├┤ Y ├┤ Z ├─░─┤ H ├┤ S ├┤ Sdg ├┤ T ├┤ Tdg ├┤ Id ├─|0>─┤ Rx(pi) ├┤ Ry(pi) ├┤ Rz(pi) ├┤ U1(pi) ├┤ U2(pi,pi) ├┤ U3(pi,pi,pi) ├─X───■────■───■───■───■──────────■────────────■─────────■─────────■───────■───■─┤M├────── + └─░─┘└───┘└───┘ ░ └───┘└───┘└─────┘└───┘└─────┘└────┘ └────────┘└────────┘└────────┘└────────┘└───────────┘└──────────────┘ │ ┌─┴─┐┌─┴─┐ │ ┌─┴─┐ │pi ┌──────┴───────┐┌───┴────┐┌───┴────┐┌───┴────┐ │ │ └╥┘┌─┐ +q_1: |0>──░─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────X─┤ X ├┤ Y ├─■─┤ H ├─■───┤ U3(pi,pi,pi) ├┤ Rz(pi) ├┤ Ry(pi) ├┤ Rx(pi) ├──■───X──╫─┤M├─── + ░ └───┘└───┘ └───┘ └──────────────┘└────────┘└────────┘└────────┘┌─┴─┐ │ ║ └╥┘┌─┐ +q_2: |0>──░────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ X ├─X──╫──╫─┤M├ + ░ └───┘ ║ ║ └╥┘ + c_0: 0 ════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩══╬══╬═ + ║ ║ + c_1: 0 ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩══╬═ + ║ + c_2: 0 ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩═ diff --git a/test/python/visualization/test_circuit_text_drawer.py b/test/python/visualization/test_circuit_text_drawer.py index 676379f65101..eda1cb68e424 100644 --- a/test/python/visualization/test_circuit_text_drawer.py +++ b/test/python/visualization/test_circuit_text_drawer.py @@ -29,7 +29,7 @@ from qiskit.transpiler import Layout from qiskit.visualization import text as elements from qiskit.visualization.circuit_visualization import _text_circuit_drawer -from qiskit.extensions import HGate, U2Gate, XGate, UnitaryGate, CzGate +from qiskit.extensions import HGate, U2Gate, XGate, UnitaryGate, CZGate class TestTextDrawerElement(QiskitTestCase): @@ -965,8 +965,8 @@ def test_label_turns_to_box_2286(self): " └───────────┘"]) qr = QuantumRegister(2, 'q') circ = QuantumCircuit(qr) - circ.append(CzGate(), [qr[0], qr[1]]) - circ.append(CzGate(label='cz label'), [qr[0], qr[1]]) + circ.append(CZGate(), [qr[0], qr[1]]) + circ.append(CZGate(label='cz label'), [qr[0], qr[1]]) self.assertEqual(str(_text_circuit_drawer(circ)), expected) diff --git a/test/python/visualization/test_circuit_visualization_output.py b/test/python/visualization/test_circuit_visualization_output.py index a255c7b0bbbe..7c9bce3e7423 100644 --- a/test/python/visualization/test_circuit_visualization_output.py +++ b/test/python/visualization/test_circuit_visualization_output.py @@ -52,7 +52,7 @@ def sample_circuit(self): circuit.sdg(qr[0]) circuit.t(qr[0]) circuit.tdg(qr[0]) - circuit.iden(qr[0]) + circuit.i(qr[0]) circuit.reset(qr[0]) circuit.rx(pi, qr[0]) circuit.ry(pi, qr[0]) @@ -100,7 +100,7 @@ def test_matplotlib_drawer(self): def test_text_drawer(self): filename = self._get_resource_path('current_textplot.txt') qc = self.sample_circuit() - output = circuit_drawer(qc, filename=filename, output="text", line_length=-1) + output = circuit_drawer(qc, filename=filename, output="text", fold=-1) self.assertFilesAreEqual(filename, self.text_reference) os.remove(filename) try: diff --git a/test/randomized/test_transpiler_equivalence.py b/test/randomized/test_transpiler_equivalence.py index 7b0d6cd1050f..c3f5dee31243 100644 --- a/test/randomized/test_transpiler_equivalence.py +++ b/test/randomized/test_transpiler_equivalence.py @@ -36,16 +36,16 @@ # pylint: disable=wildcard-import,unused-wildcard-import from qiskit.extensions.standard import * -oneQ_gates = [HGate, IdGate, SGate, SdgGate, TGate, TdgGate, XGate, YGate, ZGate, Reset] -twoQ_gates = [CnotGate, CyGate, CzGate, SwapGate, CHGate] -threeQ_gates = [ToffoliGate, FredkinGate] +oneQ_gates = [HGate, IGate, SGate, SdgGate, TGate, TdgGate, XGate, YGate, ZGate, Reset] +twoQ_gates = [CXGate, CYGate, CZGate, SwapGate, CHGate] +threeQ_gates = [CCXGate, CSwapGate] oneQ_oneP_gates = [U1Gate, RXGate, RYGate, RZGate] oneQ_twoP_gates = [U2Gate] oneQ_threeP_gates = [U3Gate] -twoQ_oneP_gates = [CrzGate, RZZGate, Cu1Gate] -twoQ_threeP_gates = [Cu3Gate] +twoQ_oneP_gates = [CRZGate, RZZGate, CU1Gate] +twoQ_threeP_gates = [CU3Gate] oneQ_oneC_gates = [Measure] variadic_gates = [Barrier]