qiskit_trebugger.debugger

Implements the main Debugger class. Raises: DebuggerError: if multiple quantum circuits supplied for debugging

  1"""
  2Implements the main Debugger class.
  3Raises:
  4    DebuggerError: if multiple quantum circuits supplied for debugging
  5"""
  6import curses
  7from typing import Optional, Union
  8import logging
  9import warnings
 10from IPython.display import display
 11
 12
 13from qiskit import QuantumCircuit, transpile, Aer, __qiskit_version__
 14from qiskit.providers.backend import Backend, BackendV1, BackendV2
 15from qiskit.transpiler.basepasses import AnalysisPass, TransformationPass
 16
 17
 18from qiskit_trebugger.model import TranspilerLoggingHandler
 19from qiskit_trebugger.model import TranspilerDataCollector
 20from qiskit_trebugger.model import TranspilationSequence
 21from qiskit_trebugger.views.widget.timeline_view import TimelineView
 22from qiskit_trebugger.views.cli.cli_view import CLIView
 23
 24from .debugger_error import DebuggerError
 25
 26
 27class Debugger:
 28    """Main debugger class for thr qiskit timeline debugger.
 29
 30    Raises:
 31        DebuggerError: if multiple quantum circuits are supplied
 32    """
 33
 34    @classmethod
 35    def debug(
 36        cls,
 37        circuit: QuantumCircuit,
 38        backend: Optional[Union[Backend, BackendV1, BackendV2]] = None,
 39        optimization_level: Optional[int] = 0,
 40        view_type: Optional[str] = "cli",
 41        show: Optional[bool] = True,
 42        **kwargs,
 43    ):
 44        """Calls the transpile method of qiskit with the given parameters
 45           and populates the view of the widget with circuit diagram and
 46           statistics.
 47
 48        Args:
 49            circuit (QuantumCircuit): quantum circuit to debug
 50            backend (Optional[Union[Backend, BackendV1, BackendV2]], optional):
 51                                        Quantum Backend for execution. Defaults to None.
 52            optimization_level (Optional[int], optional):
 53                                        Optimization level of transpiler. Defaults to 0.
 54
 55        Raises:
 56            DebuggerError: if multiple quantum circuits are supplied
 57        """
 58        if view_type not in ["cli", "jupyter"]:
 59            raise DebuggerError("Invalid view type supplied!")
 60
 61        if not isinstance(circuit, QuantumCircuit):
 62            raise DebuggerError(
 63                "Debugger currently supports single QuantumCircuit only!"
 64            )
 65        if backend is None:
 66            backend = Aer.get_backend("qasm_simulator")
 67
 68        if view_type == "cli":
 69            if not cls._is_regular_interpreter():
 70                raise DebuggerError(
 71                    "Can not invoke CLI view in IPython or Juptyer Environment!"
 72                )
 73            cls.view = CLIView()
 74        else:
 75            cls.view = TimelineView()
 76
 77        def on_step_callback(step):
 78            cls.view.add_step(step)
 79
 80        # Prepare the model:
 81        transpilation_sequence = TranspilationSequence(on_step_callback)
 82
 83        if isinstance(backend, BackendV2):
 84            backend_name = backend.name
 85        else:
 86            backend_name = backend.name()
 87
 88        warnings.simplefilter("ignore")
 89
 90        transpilation_sequence.general_info = {
 91            "backend": backend_name,
 92            "optimization_level": optimization_level,
 93            "qiskit version": __qiskit_version__["qiskit"],
 94            "terra version": __qiskit_version__["qiskit-terra"],
 95        }
 96
 97        transpilation_sequence.original_circuit = circuit
 98
 99        warnings.simplefilter("default")
100
101        Debugger.register_logging_handler(transpilation_sequence)
102        transpiler_callback = Debugger._get_data_collector(transpilation_sequence)
103
104        # Pass the model to the view:
105        cls.view.transpilation_sequence = transpilation_sequence
106
107        if view_type == "jupyter":
108            cls.view.update_params(**kwargs)
109            if show:
110                display(cls.view)
111
112        transpile(
113            circuit,
114            backend,
115            optimization_level=optimization_level,
116            callback=transpiler_callback,
117            **kwargs,
118        )
119
120        if view_type == "jupyter":
121            cls.view.update_summary()
122            cls.view.add_class("done")
123        elif view_type == "cli":
124            curses.wrapper(cls.view.display)
125
126    @classmethod
127    def register_logging_handler(cls, transpilation_sequence):
128        """Registers logging handlers of different transpiler passes.
129
130        Args:
131            transpilation_sequence (TranspilationSequence):
132                                data structure to store the transpiler
133                                passes as a sequence of transpilation
134                                steps
135        """
136
137        # TODO: Do not depend on loggerDict
138        all_loggers = logging.Logger.manager.loggerDict
139        passes_loggers = {
140            key: value
141            for (key, value) in all_loggers.items()
142            if key.startswith("qiskit.transpiler.passes.")
143        }
144
145        loggers_map = {}
146        for _pass in AnalysisPass.__subclasses__():
147            if _pass.__module__ in passes_loggers.keys():
148                loggers_map[_pass.__module__] = _pass.__name__
149
150        for _pass in TransformationPass.__subclasses__():
151            if _pass.__module__ in passes_loggers.keys():
152                loggers_map[_pass.__module__] = _pass.__name__
153
154        handler = TranspilerLoggingHandler(
155            transpilation_sequence=transpilation_sequence, loggers_map=loggers_map
156        )
157        logger = logging.getLogger("qiskit.transpiler.passes")
158        logger.setLevel(logging.DEBUG)
159        logger.addHandler(handler)
160
161    @classmethod
162    def _get_data_collector(cls, transpilation_sequence):
163        """Returns the data collector callback function for transpiler.
164
165        Args:
166            transpilation_sequence (list): List of transpilation steps
167
168        Returns:
169            function: Callback function for transpiler
170        """
171        return TranspilerDataCollector(transpilation_sequence).transpiler_callback
172
173    @classmethod
174    def _is_regular_interpreter(cls):
175        """Checks if the interpreter is regular python interpreter or IPython
176
177        Returns:
178            bool: True if regular python interpreter, False otherwise
179        """
180        try:
181            # The function get_ipython() is available on the global
182            # namespace by default when IPython is started.
183            _ = get_ipython().__class__.__name__
184
185            # if this works, I am not in regular python
186            # interpreter
187            return False
188        except NameError:
189            return True
class Debugger:
 28class Debugger:
 29    """Main debugger class for thr qiskit timeline debugger.
 30
 31    Raises:
 32        DebuggerError: if multiple quantum circuits are supplied
 33    """
 34
 35    @classmethod
 36    def debug(
 37        cls,
 38        circuit: QuantumCircuit,
 39        backend: Optional[Union[Backend, BackendV1, BackendV2]] = None,
 40        optimization_level: Optional[int] = 0,
 41        view_type: Optional[str] = "cli",
 42        show: Optional[bool] = True,
 43        **kwargs,
 44    ):
 45        """Calls the transpile method of qiskit with the given parameters
 46           and populates the view of the widget with circuit diagram and
 47           statistics.
 48
 49        Args:
 50            circuit (QuantumCircuit): quantum circuit to debug
 51            backend (Optional[Union[Backend, BackendV1, BackendV2]], optional):
 52                                        Quantum Backend for execution. Defaults to None.
 53            optimization_level (Optional[int], optional):
 54                                        Optimization level of transpiler. Defaults to 0.
 55
 56        Raises:
 57            DebuggerError: if multiple quantum circuits are supplied
 58        """
 59        if view_type not in ["cli", "jupyter"]:
 60            raise DebuggerError("Invalid view type supplied!")
 61
 62        if not isinstance(circuit, QuantumCircuit):
 63            raise DebuggerError(
 64                "Debugger currently supports single QuantumCircuit only!"
 65            )
 66        if backend is None:
 67            backend = Aer.get_backend("qasm_simulator")
 68
 69        if view_type == "cli":
 70            if not cls._is_regular_interpreter():
 71                raise DebuggerError(
 72                    "Can not invoke CLI view in IPython or Juptyer Environment!"
 73                )
 74            cls.view = CLIView()
 75        else:
 76            cls.view = TimelineView()
 77
 78        def on_step_callback(step):
 79            cls.view.add_step(step)
 80
 81        # Prepare the model:
 82        transpilation_sequence = TranspilationSequence(on_step_callback)
 83
 84        if isinstance(backend, BackendV2):
 85            backend_name = backend.name
 86        else:
 87            backend_name = backend.name()
 88
 89        warnings.simplefilter("ignore")
 90
 91        transpilation_sequence.general_info = {
 92            "backend": backend_name,
 93            "optimization_level": optimization_level,
 94            "qiskit version": __qiskit_version__["qiskit"],
 95            "terra version": __qiskit_version__["qiskit-terra"],
 96        }
 97
 98        transpilation_sequence.original_circuit = circuit
 99
100        warnings.simplefilter("default")
101
102        Debugger.register_logging_handler(transpilation_sequence)
103        transpiler_callback = Debugger._get_data_collector(transpilation_sequence)
104
105        # Pass the model to the view:
106        cls.view.transpilation_sequence = transpilation_sequence
107
108        if view_type == "jupyter":
109            cls.view.update_params(**kwargs)
110            if show:
111                display(cls.view)
112
113        transpile(
114            circuit,
115            backend,
116            optimization_level=optimization_level,
117            callback=transpiler_callback,
118            **kwargs,
119        )
120
121        if view_type == "jupyter":
122            cls.view.update_summary()
123            cls.view.add_class("done")
124        elif view_type == "cli":
125            curses.wrapper(cls.view.display)
126
127    @classmethod
128    def register_logging_handler(cls, transpilation_sequence):
129        """Registers logging handlers of different transpiler passes.
130
131        Args:
132            transpilation_sequence (TranspilationSequence):
133                                data structure to store the transpiler
134                                passes as a sequence of transpilation
135                                steps
136        """
137
138        # TODO: Do not depend on loggerDict
139        all_loggers = logging.Logger.manager.loggerDict
140        passes_loggers = {
141            key: value
142            for (key, value) in all_loggers.items()
143            if key.startswith("qiskit.transpiler.passes.")
144        }
145
146        loggers_map = {}
147        for _pass in AnalysisPass.__subclasses__():
148            if _pass.__module__ in passes_loggers.keys():
149                loggers_map[_pass.__module__] = _pass.__name__
150
151        for _pass in TransformationPass.__subclasses__():
152            if _pass.__module__ in passes_loggers.keys():
153                loggers_map[_pass.__module__] = _pass.__name__
154
155        handler = TranspilerLoggingHandler(
156            transpilation_sequence=transpilation_sequence, loggers_map=loggers_map
157        )
158        logger = logging.getLogger("qiskit.transpiler.passes")
159        logger.setLevel(logging.DEBUG)
160        logger.addHandler(handler)
161
162    @classmethod
163    def _get_data_collector(cls, transpilation_sequence):
164        """Returns the data collector callback function for transpiler.
165
166        Args:
167            transpilation_sequence (list): List of transpilation steps
168
169        Returns:
170            function: Callback function for transpiler
171        """
172        return TranspilerDataCollector(transpilation_sequence).transpiler_callback
173
174    @classmethod
175    def _is_regular_interpreter(cls):
176        """Checks if the interpreter is regular python interpreter or IPython
177
178        Returns:
179            bool: True if regular python interpreter, False otherwise
180        """
181        try:
182            # The function get_ipython() is available on the global
183            # namespace by default when IPython is started.
184            _ = get_ipython().__class__.__name__
185
186            # if this works, I am not in regular python
187            # interpreter
188            return False
189        except NameError:
190            return True

Main debugger class for thr qiskit timeline debugger.

Raises: DebuggerError: if multiple quantum circuits are supplied

@classmethod
def debug( cls, circuit: qiskit.circuit.quantumcircuit.QuantumCircuit, backend: Union[qiskit.providers.backend.Backend, qiskit.providers.backend.BackendV1, qiskit.providers.backend.BackendV2, NoneType] = None, optimization_level: Optional[int] = 0, view_type: Optional[str] = 'cli', show: Optional[bool] = True, **kwargs):
 35    @classmethod
 36    def debug(
 37        cls,
 38        circuit: QuantumCircuit,
 39        backend: Optional[Union[Backend, BackendV1, BackendV2]] = None,
 40        optimization_level: Optional[int] = 0,
 41        view_type: Optional[str] = "cli",
 42        show: Optional[bool] = True,
 43        **kwargs,
 44    ):
 45        """Calls the transpile method of qiskit with the given parameters
 46           and populates the view of the widget with circuit diagram and
 47           statistics.
 48
 49        Args:
 50            circuit (QuantumCircuit): quantum circuit to debug
 51            backend (Optional[Union[Backend, BackendV1, BackendV2]], optional):
 52                                        Quantum Backend for execution. Defaults to None.
 53            optimization_level (Optional[int], optional):
 54                                        Optimization level of transpiler. Defaults to 0.
 55
 56        Raises:
 57            DebuggerError: if multiple quantum circuits are supplied
 58        """
 59        if view_type not in ["cli", "jupyter"]:
 60            raise DebuggerError("Invalid view type supplied!")
 61
 62        if not isinstance(circuit, QuantumCircuit):
 63            raise DebuggerError(
 64                "Debugger currently supports single QuantumCircuit only!"
 65            )
 66        if backend is None:
 67            backend = Aer.get_backend("qasm_simulator")
 68
 69        if view_type == "cli":
 70            if not cls._is_regular_interpreter():
 71                raise DebuggerError(
 72                    "Can not invoke CLI view in IPython or Juptyer Environment!"
 73                )
 74            cls.view = CLIView()
 75        else:
 76            cls.view = TimelineView()
 77
 78        def on_step_callback(step):
 79            cls.view.add_step(step)
 80
 81        # Prepare the model:
 82        transpilation_sequence = TranspilationSequence(on_step_callback)
 83
 84        if isinstance(backend, BackendV2):
 85            backend_name = backend.name
 86        else:
 87            backend_name = backend.name()
 88
 89        warnings.simplefilter("ignore")
 90
 91        transpilation_sequence.general_info = {
 92            "backend": backend_name,
 93            "optimization_level": optimization_level,
 94            "qiskit version": __qiskit_version__["qiskit"],
 95            "terra version": __qiskit_version__["qiskit-terra"],
 96        }
 97
 98        transpilation_sequence.original_circuit = circuit
 99
100        warnings.simplefilter("default")
101
102        Debugger.register_logging_handler(transpilation_sequence)
103        transpiler_callback = Debugger._get_data_collector(transpilation_sequence)
104
105        # Pass the model to the view:
106        cls.view.transpilation_sequence = transpilation_sequence
107
108        if view_type == "jupyter":
109            cls.view.update_params(**kwargs)
110            if show:
111                display(cls.view)
112
113        transpile(
114            circuit,
115            backend,
116            optimization_level=optimization_level,
117            callback=transpiler_callback,
118            **kwargs,
119        )
120
121        if view_type == "jupyter":
122            cls.view.update_summary()
123            cls.view.add_class("done")
124        elif view_type == "cli":
125            curses.wrapper(cls.view.display)

Calls the transpile method of qiskit with the given parameters and populates the view of the widget with circuit diagram and statistics.

Args: circuit (QuantumCircuit): quantum circuit to debug backend (Optional[Union[Backend, BackendV1, BackendV2]], optional): Quantum Backend for execution. Defaults to None. optimization_level (Optional[int], optional): Optimization level of transpiler. Defaults to 0.

Raises: DebuggerError: if multiple quantum circuits are supplied

@classmethod
def register_logging_handler(cls, transpilation_sequence):
127    @classmethod
128    def register_logging_handler(cls, transpilation_sequence):
129        """Registers logging handlers of different transpiler passes.
130
131        Args:
132            transpilation_sequence (TranspilationSequence):
133                                data structure to store the transpiler
134                                passes as a sequence of transpilation
135                                steps
136        """
137
138        # TODO: Do not depend on loggerDict
139        all_loggers = logging.Logger.manager.loggerDict
140        passes_loggers = {
141            key: value
142            for (key, value) in all_loggers.items()
143            if key.startswith("qiskit.transpiler.passes.")
144        }
145
146        loggers_map = {}
147        for _pass in AnalysisPass.__subclasses__():
148            if _pass.__module__ in passes_loggers.keys():
149                loggers_map[_pass.__module__] = _pass.__name__
150
151        for _pass in TransformationPass.__subclasses__():
152            if _pass.__module__ in passes_loggers.keys():
153                loggers_map[_pass.__module__] = _pass.__name__
154
155        handler = TranspilerLoggingHandler(
156            transpilation_sequence=transpilation_sequence, loggers_map=loggers_map
157        )
158        logger = logging.getLogger("qiskit.transpiler.passes")
159        logger.setLevel(logging.DEBUG)
160        logger.addHandler(handler)

Registers logging handlers of different transpiler passes.

Args: transpilation_sequence (TranspilationSequence): data structure to store the transpiler passes as a sequence of transpilation steps