Skip to content

Commit

Permalink
Merge branch 'main' into extend-params
Browse files Browse the repository at this point in the history
  • Loading branch information
raynelfss authored Jul 1, 2024
2 parents d5db780 + 5db984a commit b6aeba3
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 37 deletions.
13 changes: 5 additions & 8 deletions crates/circuit/src/circuit_instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -728,10 +728,7 @@ impl CircuitInstruction {

/// Take a reference to a `CircuitInstruction` and convert the operation
/// inside that to a python side object.
pub(crate) fn operation_type_to_py(
py: Python,
circuit_inst: &CircuitInstruction,
) -> PyResult<PyObject> {
pub fn operation_type_to_py(py: Python, circuit_inst: &CircuitInstruction) -> PyResult<PyObject> {
let (label, duration, unit, condition) = match &circuit_inst.extra_attrs {
None => (None, None, None, None),
Some(extra_attrs) => (
Expand All @@ -757,7 +754,7 @@ pub(crate) fn operation_type_to_py(
/// a Python side full-fat Qiskit operation as a PyObject. This is typically
/// used by accessor functions that need to return an operation to Qiskit, such
/// as accesing `CircuitInstruction.operation`.
pub(crate) fn operation_type_and_data_to_py(
pub fn operation_type_and_data_to_py(
py: Python,
operation: &OperationType,
params: &[Param],
Expand Down Expand Up @@ -796,8 +793,8 @@ pub(crate) fn operation_type_and_data_to_py(

/// A container struct that contains the output from the Python object to
/// conversion to construct a CircuitInstruction object
#[derive(Debug)]
pub(crate) struct OperationTypeConstruct {
#[derive(Debug, Clone)]
pub struct OperationTypeConstruct {
pub operation: OperationType,
pub params: SmallVec<[Param; 3]>,
pub label: Option<String>,
Expand All @@ -809,7 +806,7 @@ pub(crate) struct OperationTypeConstruct {
/// Convert an inbound Python object for a Qiskit operation and build a rust
/// representation of that operation. This will map it to appropriate variant
/// of operation type based on class
pub(crate) fn convert_py_to_operation_type(
pub fn convert_py_to_operation_type(
py: Python,
py_op: PyObject,
) -> PyResult<OperationTypeConstruct> {
Expand Down
10 changes: 5 additions & 5 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -3257,11 +3257,11 @@ def draw(
alternative value set. For example, ``circuit_reverse_bits = True``.
plot_barriers: Enable/disable drawing barriers in the output
circuit. Defaults to ``True``.
justify: Options are ``left``, ``right`` or ``none``. If
anything else is supplied, it defaults to left justified. It refers
to where gates should be placed in the output circuit if there is
an option. ``none`` results in each gate being placed in its own
column.
justify: Options are ``"left"``, ``"right"`` or ``"none"`` (str).
If anything else is supplied, left justified will be used instead.
It refers to where gates should be placed in the output circuit if
there is an option. ``none`` results in each gate being placed in
its own column. Defaults to ``left``.
vertical_compression: ``high``, ``medium`` or ``low``. It
merges the lines generated by the `text` output so the drawing
will take less vertical room. Default is ``medium``. Only used by
Expand Down
7 changes: 7 additions & 0 deletions qiskit/dagcircuit/dagcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1342,6 +1342,13 @@ def replace_block_with_op(
block_cargs.sort(key=wire_pos_map.get)
new_node = DAGOpNode(op, block_qargs, block_cargs, dag=self)

# check the op to insert matches the number of qubits we put it on
if op.num_qubits != len(block_qargs):
raise DAGCircuitError(
f"Number of qubits in the replacement operation ({op.num_qubits}) is not equal to "
f"the number of qubits in the block ({len(block_qargs)})!"
)

try:
new_node._node_id = self._multi_graph.contract_nodes(
block_ids, new_node, check_cycle=cycle_check
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:
sub_dag = self._decompose_to_2q(dag, node.op)

block_op = Commuting2qBlock(set(sub_dag.op_nodes()))
wire_order = {wire: idx for idx, wire in enumerate(dag.qubits)}
wire_order = {
wire: idx
for idx, wire in enumerate(sub_dag.qubits)
if wire not in sub_dag.idle_wires()
}
dag.replace_block_with_op([node], block_op, wire_order)

return dag
Expand Down
42 changes: 33 additions & 9 deletions qiskit/visualization/circuit/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,25 @@

import re
from collections import OrderedDict
from warnings import warn

import numpy as np

from qiskit.circuit import (
ClassicalRegister,
Clbit,
ControlFlowOp,
ControlledGate,
Delay,
Gate,
Instruction,
Measure,
QuantumCircuit,
Qubit,
)
from qiskit.circuit.annotated_operation import AnnotatedOperation, InverseModifier, PowerModifier
from qiskit.circuit.controlflow import condition_resources
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.circuit import ClassicalRegister, QuantumCircuit, Qubit, ControlFlowOp
from qiskit.circuit.annotated_operation import AnnotatedOperation, InverseModifier, PowerModifier
from qiskit.circuit.tools import pi_check
from qiskit.converters import circuit_to_dag
from qiskit.utils import optionals as _optionals
Expand Down Expand Up @@ -370,6 +374,29 @@ def generate_latex_label(label):
return final_str.replace(" ", "\\,") # Put in proper spaces


def _get_valid_justify_arg(justify):
"""Returns a valid `justify` argument, warning if necessary."""
if isinstance(justify, str):
justify = justify.lower()

if justify is None:
justify = "left"

if justify not in ("left", "right", "none"):
# This code should be changed to an error raise, once the deprecation is complete.
warn(
f"Setting QuantumCircuit.draw()’s or circuit_drawer()'s justify argument: {justify}, to a "
"value other than 'left', 'right', 'none' or None (='left'). Default 'left' will be used. "
"Support for invalid justify arguments is deprecated as of qiskit 1.2.0. Starting no "
"earlier than 3 months after the release date, invalid arguments will error.",
DeprecationWarning,
2,
)
justify = "left"

return justify


def _get_layered_instructions(
circuit, reverse_bits=False, justify=None, idle_wires=True, wire_order=None, wire_map=None
):
Expand All @@ -384,21 +411,18 @@ def _get_layered_instructions(
reverse_bits (bool): If true the order of the bits in the registers is
reversed.
justify (str) : `left`, `right` or `none`. Defaults to `left`. Says how
the circuit should be justified.
the circuit should be justified. If an invalid value is provided,
default `left` will be used.
idle_wires (bool): Include idle wires. Default is True.
wire_order (list): A list of ints that modifies the order of the bits
wire_order (list): A list of ints that modifies the order of the bits.
Returns:
Tuple(list,list,list): To be consumed by the visualizer directly.
Raises:
VisualizationError: if both reverse_bits and wire_order are entered.
"""
if justify:
justify = justify.lower()

# default to left
justify = justify if justify in ("right", "none") else "left"
justify = _get_valid_justify_arg(justify)

if wire_map is not None:
qubits = [bit for bit in wire_map if isinstance(bit, Qubit)]
Expand Down
23 changes: 12 additions & 11 deletions qiskit/visualization/circuit/circuit_visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,22 @@

import logging
import os
import shutil
import subprocess
import tempfile
import shutil
import typing
from warnings import warn

from qiskit import user_config
from qiskit.utils import optionals as _optionals
from qiskit.circuit import ControlFlowOp, Measure
from qiskit.utils import optionals as _optionals

from ..exceptions import VisualizationError
from ..utils import _trim as trim_image
from . import _utils
from . import latex as _latex
from . import text as _text
from . import matplotlib as _matplotlib
from . import _utils
from ..utils import _trim as trim_image
from ..exceptions import VisualizationError
from . import text as _text

if typing.TYPE_CHECKING:
from typing import Any
Expand Down Expand Up @@ -131,11 +132,11 @@ def circuit_drawer(
alternative value set. For example, ``circuit_reverse_bits = True``.
plot_barriers: Enable/disable drawing barriers in the output
circuit. Defaults to ``True``.
justify: Options are ``left``, ``right`` or ``none``. If
anything else is supplied, it defaults to left justified. It refers
to where gates should be placed in the output circuit if there is
an option. ``none`` results in each gate being placed in its own
column.
justify: Options are ``"left"``, ``"right"`` or ``"none"`` (str).
If anything else is supplied, left justified will be used instead.
It refers to where gates should be placed in the output circuit if
there is an option. ``none`` results in each gate being placed in
its own column. Defaults to ``left``.
vertical_compression: ``high``, ``medium`` or ``low``. It
merges the lines generated by the `text` output so the drawing
will take less vertical room. Default is ``medium``. Only used by
Expand Down
13 changes: 13 additions & 0 deletions releasenotes/notes/circuit-draw-warn-justify-03434d30cccda452.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
deprecations_visualization:
- |
The ``justify`` argument of :func:`circuit_drawer` or :meth:`QuantumCircuit.draw`, will
no longer support invalid values (previously changing them to the default), and in a future
release they will error. Valid justify values are ``"left"``, ``"right"`` or ``"none"``.
fixes:
- |
Fixed an issue where :func:`circuit_drawer` or the :meth:`QuantumCircuit.draw` method would
not raise a warning when an invalid value was passed to the ``justify`` argument, before
changing it to the default. Now, it will raise a warning if an invalid value is passed.
Valid justify values are ``"left"``, ``"right"`` or ``"none"``. Refer to
`#12089 <https://github.com/Qiskit/qiskit/issues/12089>` for more details.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
fixes:
- |
Previously, :meth:`.DAGCircuit.replace_block_with_op` allowed to place an
``n``-qubit operation onto a block of ``m`` qubits, leaving the DAG in an
invalid state. This behavior has been fixed, and the attempt will raise
a :class:`.DAGCircuitError`.
16 changes: 16 additions & 0 deletions test/python/dagcircuit/test_dagcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -2903,6 +2903,22 @@ def test_single_node_block(self):
self.assertEqual(expected_dag.count_ops(), dag.count_ops())
self.assertIsInstance(new_node.op, XGate)

def test_invalid_replacement_size(self):
"""Test inserting an operation on a wrong number of qubits raises."""

# two X gates, normal circuit
qc = QuantumCircuit(2)
qc.x(range(2))

# mutilate the DAG
dag = circuit_to_dag(qc)
to_replace = list(dag.op_nodes())
new_node = XGate()
idx_map = {node.qargs[0]: i for i, node in enumerate(to_replace)}

with self.assertRaises(DAGCircuitError):
dag.replace_block_with_op(to_replace, new_node, idx_map)

def test_replace_control_flow_block(self):
"""Test that we can replace a block of control-flow nodes with a single one."""
body = QuantumCircuit(1)
Expand Down
24 changes: 21 additions & 3 deletions test/python/visualization/test_circuit_drawer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@

import os
import pathlib
import re
import shutil
import tempfile
import unittest
import warnings
from unittest.mock import patch

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister, visualization
from qiskit.utils import optionals
from qiskit import visualization
from qiskit.visualization.circuit import text, styles
from qiskit.visualization.circuit import styles, text
from qiskit.visualization.exceptions import VisualizationError
from test import QiskitTestCase # pylint: disable=wrong-import-order

Expand Down Expand Up @@ -241,6 +241,24 @@ def test_reverse_bits(self):
result = visualization.circuit_drawer(circuit, output="text", reverse_bits=True)
self.assertEqual(result.__str__(), expected)

def test_warning_for_bad_justify_argument(self):
"""Test that the correct DeprecationWarning is raised when the justify parameter is badly input,
for both of the public interfaces."""
circuit = QuantumCircuit()
bad_arg = "bad"
error_message = re.escape(
f"Setting QuantumCircuit.draw()’s or circuit_drawer()'s justify argument: {bad_arg}, to a "
"value other than 'left', 'right', 'none' or None (='left'). Default 'left' will be used. "
"Support for invalid justify arguments is deprecated as of qiskit 1.2.0. Starting no "
"earlier than 3 months after the release date, invalid arguments will error.",
)

with self.assertWarnsRegex(DeprecationWarning, error_message):
visualization.circuit_drawer(circuit, justify=bad_arg)

with self.assertWarnsRegex(DeprecationWarning, error_message):
circuit.draw(justify=bad_arg)

@unittest.skipUnless(optionals.HAS_PYLATEX, "needs pylatexenc for LaTeX conversion")
def test_no_explict_cregbundle(self):
"""Test no explicit cregbundle should not raise warnings about being disabled
Expand Down

0 comments on commit b6aeba3

Please sign in to comment.