Skip to content

Commit 4342f75

Browse files
Jupyter widget for circuit library (Qiskit#3861)
* add qasm lexer * allow for formatted and saving * lint * style * add to docs as submodule * add html style * add circuit library widget * add line magic * updates * remove circuit title as it formats incorrectly * fix colors * change header font size * make widget formatting a bit better * add ignores * remove wrong commit * remove pygmnets from docs * update * add pygments directly to qasm Co-authored-by: Ali Javadi-Abhari <[email protected]>
1 parent 3e77e86 commit 4342f75

File tree

12 files changed

+457
-7
lines changed

12 files changed

+457
-7
lines changed

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -141,5 +141,4 @@ src/qasm-simulator-cpp/test/qubit_vector_tests
141141
# Cython pass
142142
qiskit/transpiler/passes/**/cython/**/*.cpp
143143

144-
docs/api/*
145144
docs/stubs/*

docs/apidocs/circuit_library.rst

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.. _qiskit-circuit-library:
2+
3+
===============
4+
Circuit Library
5+
===============
6+
7+
.. automodule:: qiskit.circuit.library
8+
:no-members:
9+
:no-inherited-members:
10+
:no-special-members:

docs/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Qiskit Terra documentation
77
:hidden:
88

99
API References <apidocs/terra>
10+
Circuit Library <apidocs/circuit_library>
1011
Release Notes <release_notes>
1112

1213
.. Hiding - Indices and tables

qiskit/circuit/library/__init__.py

+18-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,23 @@
1212
# copyright notice, and modified files need to carry a notice indicating
1313
# that they have been altered from the originals.
1414

15-
"""Module for builtin library of circuits."""
15+
"""
16+
===============================================
17+
Circuit Library (:mod:`qiskit.circuit.library`)
18+
===============================================
19+
20+
.. currentmodule:: qiskit.circuit.library
21+
22+
Boolean Logic Circuits
23+
======================
24+
25+
.. autosummary::
26+
:toctree: ../stubs/
27+
28+
InnerProduct
29+
Permutation
30+
XOR
31+
"""
32+
1633

1734
from .boolean_logic import Permutation, XOR, InnerProduct

qiskit/circuit/library/boolean_logic.py

+28
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ def __init__(self,
4242
4343
Raises:
4444
CircuitError: if permutation pattern is malformed.
45+
46+
Reference Circuit:
47+
.. jupyter-execute::
48+
:hide-code:
49+
50+
from qiskit.circuit.library import Permutation
51+
import qiskit.tools.jupyter
52+
circuit = Permutation(5, seed=42)
53+
%circuit_library_info circuit
54+
4555
"""
4656
super().__init__(n_qubits, name="permutation")
4757

@@ -85,6 +95,15 @@ def __init__(self,
8595
8696
Raises:
8797
CircuitError: if the xor bitstring exceeds available qubits.
98+
99+
Reference Circuit:
100+
.. jupyter-execute::
101+
:hide-code:
102+
103+
from qiskit.circuit.library import XOR
104+
import qiskit.tools.jupyter
105+
circuit = XOR(5, seed=42)
106+
%circuit_library_info circuit
88107
"""
89108
super().__init__(n_qubits, name="xor")
90109

@@ -115,6 +134,15 @@ def __init__(self, n_qubits: int) -> QuantumCircuit:
115134
116135
Returns:
117136
A circuit computing inner product of two registers.
137+
138+
Reference Circuit:
139+
.. jupyter-execute::
140+
:hide-code:
141+
142+
from qiskit.circuit.library import InnerProduct
143+
import qiskit.tools.jupyter
144+
circuit = InnerProduct(5)
145+
%circuit_library_info circuit
118146
"""
119147
qr_a = QuantumRegister(n_qubits)
120148
qr_b = QuantumRegister(n_qubits)

qiskit/circuit/quantumcircuit.py

+27-3
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@
2020
import warnings
2121
import multiprocessing as mp
2222
from collections import OrderedDict
23+
import pygments
24+
from pygments.formatters import Terminal256Formatter # pylint: disable=no-name-in-module
2325
import numpy as np
2426
from qiskit.util import is_main_process
2527
from qiskit.circuit.instruction import Instruction
2628
from qiskit.qasm.qasm import Qasm
29+
from qiskit.qasm.pygments import OpenQASMLexer, QasmTerminalStyle
2730
from qiskit.circuit.exceptions import CircuitError
2831
from .parameterexpression import ParameterExpression
2932
from .quantumregister import QuantumRegister, Qubit
@@ -624,8 +627,16 @@ def _check_compatible_regs(self, rhs):
624627
if element1 != element2:
625628
raise CircuitError("circuits are not compatible")
626629

627-
def qasm(self):
628-
"""Return OpenQASM string."""
630+
def qasm(self, formatted=False, filename=None):
631+
"""Return OpenQASM string.
632+
633+
Parameters:
634+
formatted (bool): Return formatted Qasm string.
635+
filename (str): Save Qasm to file with name 'filename'.
636+
637+
Returns:
638+
str: If formatted=False.
639+
"""
629640
string_temp = self.header + "\n"
630641
string_temp += self.extension_lib + "\n"
631642
for register in self.qregs:
@@ -650,7 +661,20 @@ def qasm(self):
650661
# this resets them, so if another call to qasm() is made the gate def is added again
651662
for gate in unitary_gates:
652663
gate._qasm_def_written = False
653-
return string_temp
664+
665+
if filename:
666+
with open(filename, 'w+') as file:
667+
file.write(string_temp)
668+
file.close()
669+
670+
if formatted:
671+
code = pygments.highlight(string_temp,
672+
OpenQASMLexer(),
673+
Terminal256Formatter(style=QasmTerminalStyle))
674+
print(code)
675+
return None
676+
else:
677+
return string_temp
654678

655679
def draw(self, output=None, scale=0.7, filename=None, style=None,
656680
interactive=False, line_length=None, plot_barriers=True,

qiskit/qasm/__init__.py

+16
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,30 @@
1919
2020
.. currentmodule:: qiskit.qasm
2121
22+
QASM Routines
23+
=============
24+
2225
.. autosummary::
2326
:toctree: ../stubs/
2427
2528
Qasm
2629
QasmError
30+
31+
32+
Pygments
33+
========
34+
35+
.. autosummary::
36+
:toctree: ../stubs/
37+
38+
OpenQASMLexer
39+
QasmHTMLStyle
40+
QasmTerminalStyle
41+
2742
"""
2843

2944
from numpy import pi
3045

3146
from .qasm import Qasm
3247
from .exceptions import QasmError
48+
from .pygments import OpenQASMLexer, QasmHTMLStyle, QasmTerminalStyle

qiskit/qasm/pygments/__init__.py

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# This code is part of Qiskit.
4+
#
5+
# (C) Copyright IBM 2020.
6+
#
7+
# This code is licensed under the Apache License, Version 2.0. You may
8+
# obtain a copy of this license in the LICENSE.txt file in the root directory
9+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
10+
#
11+
# Any modifications or derivative works of this code must retain this
12+
# copyright notice, and modified files need to carry a notice indicating
13+
# that they have been altered from the originals.
14+
15+
"""
16+
=================================================
17+
Qasm Pygments tools (:mod:`qiskit.qasm.pygments`)
18+
=================================================
19+
20+
.. currentmodule:: qiskit.qasm.pygments
21+
22+
.. autosummary::
23+
:toctree: ../stubs/
24+
25+
OpenQASMLexer
26+
QasmTerminalStyle
27+
QasmHTMLStyle
28+
"""
29+
from .lexer import OpenQASMLexer, QasmTerminalStyle, QasmHTMLStyle

qiskit/qasm/pygments/lexer.py

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# This code is part of Qiskit.
4+
#
5+
# (C) Copyright IBM 2020.
6+
#
7+
# This code is licensed under the Apache License, Version 2.0. You may
8+
# obtain a copy of this license in the LICENSE.txt file in the root directory
9+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
10+
#
11+
# Any modifications or derivative works of this code must retain this
12+
# copyright notice, and modified files need to carry a notice indicating
13+
# that they have been altered from the originals.
14+
"""Pygments tools for Qasm.
15+
"""
16+
from pygments.lexer import RegexLexer
17+
from pygments.token import (Comment, String, Keyword,
18+
Name, Number, Text)
19+
from pygments.style import Style
20+
21+
22+
class QasmTerminalStyle(Style):
23+
"""A style for OpenQasm in a Terminal env (e.g. Jupyter print)
24+
"""
25+
styles = {
26+
String: 'ansibrightred',
27+
Number: 'ansibrightcyan',
28+
Keyword.Reserved: 'ansibrightgreen',
29+
Keyword.Declaration: 'ansibrightgreen',
30+
Keyword.Type: 'ansibrightmagenta',
31+
Name.Builtin: 'ansibrightblue',
32+
Name.Function: 'ansibrightyellow'}
33+
34+
35+
class QasmHTMLStyle(Style):
36+
"""A style for OpenQasm in a HTML env (e.g. Jupyter widget)
37+
"""
38+
styles = {
39+
String: 'ansired',
40+
Number: 'ansicyan',
41+
Keyword.Reserved: 'ansigreen',
42+
Keyword.Declaration: 'ansigreen',
43+
Keyword.Type: 'ansimagenta',
44+
Name.Builtin: 'ansiblue',
45+
Name.Function: 'ansiyellow'}
46+
47+
48+
class OpenQASMLexer(RegexLexer):
49+
"""A pygments lexer for OpenQasm
50+
"""
51+
name = 'OpenQASM'
52+
aliases = ['qasm']
53+
filenames = ['*.qasm']
54+
55+
gates = ['id', 'cx', 'x', 'y', 'z', 's', 'sdg', 'h',
56+
't', 'tdg', 'ccx', 'rx', 'ry', 'rz',
57+
'cz', 'cy', 'ch', 'swap', 'cswap', 'crx',
58+
'cry', 'crz', 'cu1', 'cu3', 'rxx', 'rzz',
59+
'rccx', 'rcccx', 'u1', 'u2', 'u3']
60+
61+
tokens = {
62+
'root': [
63+
(r'\n', Text),
64+
(r'[^\S\n]+', Text),
65+
(r'//\n', Comment),
66+
(r'//.*?$', Comment.Single),
67+
68+
# Keywords
69+
(r'(OPENQASM|include)\b', Keyword.Reserved, 'keywords'),
70+
(r'(qreg|creg)\b', Keyword.Declaration),
71+
72+
# Treat 'if' special
73+
(r'(if)\b', Keyword.Reserved, 'if_keywords'),
74+
75+
# Constants
76+
(r'(pi)\b', Name.Constant),
77+
78+
# Special
79+
(r'(barrier|measure|reset)\b', Name.Builtin, 'params'),
80+
81+
# Gates (Types)
82+
('(' + '|'.join(gates) + r')\b', Keyword.Type, 'params'),
83+
(r'[unitary\d+]', Keyword.Type),
84+
85+
# Functions
86+
(r'(gate)\b', Name.Function, 'gate'),
87+
88+
# Generic text
89+
(r"[a-zA-Z_][a-zA-Z0-9_]*", Text, 'index')],
90+
91+
'keywords': [(r'\s*("([^"]|"")*")', String, '#push'),
92+
(r"\d+", Number, '#push'),
93+
(r'.*\(', Text, 'params')],
94+
95+
'if_keywords': [(r'[a-zA-Z0-9_]*', String, '#pop'),
96+
(r"\d+", Number, '#push'),
97+
(r'.*\(', Text, 'params')],
98+
99+
'params': [(r"[a-zA-Z_][a-zA-Z0-9_]*", Text, '#push'),
100+
(r'\d+', Number, '#push'),
101+
(r'(\d+\.\d*|\d*\.\d+)([eEf][+-]?[0-9]+)?', Number, '#push'),
102+
(r'\)', Text)],
103+
104+
'gate': [(r'[unitary\d+]', Keyword.Type, '#push'),
105+
(r'p\d+', Text, '#push')],
106+
107+
'index': [(r"\d+", Number, '#pop')]
108+
}

qiskit/tools/jupyter/jupyter_magics.py

+19-2
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,23 @@
1616

1717
import time
1818
import threading
19+
from IPython import get_ipython
1920
from IPython.display import display # pylint: disable=import-error
2021
from IPython.core import magic_arguments # pylint: disable=import-error
21-
from IPython.core.magic import (cell_magic, line_magic,
22-
Magics, magics_class) # pylint: disable=import-error
22+
from IPython.core.magic import (cell_magic, line_magic, # pylint: disable=import-error
23+
Magics, magics_class,
24+
register_line_magic)
2325

2426
try:
2527
import ipywidgets as widgets # pylint: disable=import-error
2628
except ImportError:
2729
raise ImportError('These functions need ipywidgets. '
2830
'Run "pip install ipywidgets" before.')
2931
import qiskit
32+
from qiskit.visualization.matplotlib import HAS_MATPLOTLIB
3033
from qiskit.tools.events.progressbar import TextProgressBar
3134
from .progressbar import HTMLProgressBar
35+
from .library import circuit_library_widget
3236

3337

3438
def _html_checker(job_var, interval, status, header,
@@ -186,3 +190,16 @@ def qiskit_progress_bar(self, line='', cell=None): # pylint: disable=unused-arg
186190
raise qiskit.QiskitError('Invalid progress bar type.')
187191

188192
return pbar
193+
194+
195+
if HAS_MATPLOTLIB and get_ipython():
196+
@register_line_magic
197+
def circuit_library_info(circuit: qiskit.QuantumCircuit) -> None:
198+
"""Displays library information for a quantum circuit.
199+
200+
Args:
201+
circuit: Input quantum circuit.
202+
"""
203+
shell = get_ipython()
204+
circ = shell.ev(circuit)
205+
circuit_library_widget(circ)

0 commit comments

Comments
 (0)