Note

This page was generated from tut/1-Overview/1.1-Quick-start.ipynb.

1.1 Quick start

Open in Colab Binder

💡 Running in Colab or Binder? Skip the desktop GUI install — the cell below grabs the lite (no-Qt) wheel, and qm.gui(design) auto-picks an inline matplotlib viewer with the same API (gui.rebuild(), gui.screenshot(), gui.edit_component(...)) as the desktop MetalGUI.

[ ]:
# In Colab / Binder, uncomment to install Quantum Metal (lite, no Qt).
# Locally you should already have it via `pip install quantum-metal` or
# `pip install 'quantum-metal[gui]'` for the desktop GUI.
# !pip install -q quantum-metal

Import Qiskit Metal

[2]:
import qiskit_metal as metal
import qiskit_metal as qm  # alias used by qm.gui / qm.view
from qiskit_metal import designs, draw
from qiskit_metal import Dict, open_docs

%metal_heading Welcome to Qiskit Metal!

Welcome to Qiskit Metal!

My first Quantum Design (QDesign)

A QDesign is the canvas that holds your chip components. Start with the planar variant:

design = designs.DesignPlanar()
gui = qm.gui(design)

qm.gui(design) is a factory: it returns the desktop MetalGUI when you have PySide6 and a display, and an inline matplotlib viewer (MetalGUIHeadless) otherwise. The same gui.* API works in both.

Run the cells below to instantiate a design and view it. The chip starts empty — let’s add a qubit.

[3]:
design = designs.DesignPlanar()
gui = qm.gui(design)  # Qt locally, inline matplotlib in Colab/Binder
[4]:
gui.screenshot()
../../_images/tut_1-Overview_1.1-Quick-start_7_0.png
[5]:
%metal_heading Hello Quantum World!

Hello Quantum World!

My first QComponent — a transmon qubit

Quantum Metal ships a ready-made transmon in qiskit_metal.qlibrary.qubits.transmon_pocket. Adding one is a single line; the GUI refreshes automatically.

[6]:
# Select a QComponent to create (The QComponent is a python class named `TransmonPocket`)
from qiskit_metal.qlibrary.qubits.transmon_pocket import TransmonPocket

# Create a new qcomponent object with name 'Q1'
q1 = TransmonPocket(design, "Q1")
gui.rebuild()  # rebuild the design and plot
[7]:
# save screenshot
gui.edit_component("Q1")
gui.autoscale()
gui.screenshot()
../../_images/tut_1-Overview_1.1-Quick-start_11_0.png

Inspect the component we just created:

[8]:
q1
[8]:
name:    Q1
class:   TransmonPocket        
options: 
  'pos_x'             : '0.0um',
  'pos_y'             : '0.0um',
  'orientation'       : '0.0',
  'chip'              : 'main',
  'layer'             : '1',
  'connection_pads'   : {
                        },
  'pad_gap'           : '30um',
  'inductor_width'    : '20um',
  'pad_width'         : '455um',
  'pad_height'        : '90um',
  'pocket_width'      : '650um',
  'pocket_height'     : '650um',
  'hfss_wire_bonds'   : False,
  'q3d_wire_bonds'    : False,
  'aedt_q3d_wire_bonds': False,
  'aedt_hfss_wire_bonds': False,
  'hfss_inductance'   : '10nH',
  'hfss_capacitance'  : 0,
  'hfss_resistance'   : 0,
  'hfss_mesh_kw_jj'   : 7e-06,
  'q3d_inductance'    : '10nH',
  'q3d_capacitance'   : 0,
  'q3d_resistance'    : 0,
  'q3d_mesh_kw_jj'    : 7e-06,
  'gds_cell_name'     : 'my_other_junction',
  'aedt_q3d_inductance': 1e-08,
  'aedt_q3d_capacitance': 0,
  'aedt_hfss_inductance': 1e-08,
  'aedt_hfss_capacitance': 0,
module:  qiskit_metal.qlibrary.qubits.transmon_pocket
id:      1

Default options

Every QComponent has a default_options dictionary. Quantum Metal parses these strings into numeric values during rebuild(). You can change them through the GUI or the Python API — both call the same underlying setters.

[9]:
%metal_print How do I edit options?  API or GUI
How do I edit options? API or GUI

Both paths converge: the GUI just calls the same API you’d write in a notebook.

[11]:
# Change options
q1.options.pos_x = "0.5 mm"
q1.options.pos_y = "0.25 mm"
q1.options.pad_height = "225 um"
q1.options.pad_width = "250 um"
q1.options.pad_gap = "50 um"

# Update the geometry, since we changed the options
gui.rebuild()
[12]:
gui.autoscale()
gui.screenshot()
../../_images/tut_1-Overview_1.1-Quick-start_18_0.png

Compare before and after

Design-as-code means every parameter change is immediate and visual. qm.view() renders into any matplotlib axes — drop two of them side-by-side to see what changed.

[ ]:
import qiskit_metal as qm
import matplotlib.pyplot as plt

# Record the original value so we can restore it
_orig_pad_width = q1.options.pad_width

# Make a change
q1.options.pad_width = "550 um"
design.rebuild()

# Side-by-side comparison rendered to a PNG so it displays inline in
# Jupyter regardless of which matplotlib backend the GUI activated
# (the desktop MetalGUI sets `QtAgg`, which doesn't render Figures
# directly into notebook cells; ``display(fig)`` falls back to a
# text repr otherwise).
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
qm.view(
    design,
    components=["Q1"],
    title=f"Original  pad_width={_orig_pad_width}",
    ax=axes[0],
)
qm.view(design, components=["Q1"], title="Modified  pad_width=550 um", ax=axes[1])
plt.tight_layout()

import io
from IPython.display import Image, display

buf = io.BytesIO()
fig.savefig(buf, format="png", bbox_inches="tight", dpi=120)
plt.close(fig)
display(Image(buf.getvalue()))
[ ]:
# Restore the original value before continuing
q1.options.pad_width = _orig_pad_width
design.rebuild()
print(f"Restored pad_width to {q1.options.pad_width}")
Restored pad_width to 250 um

Where are the QComponents stored?

In design.components. Access by name (design.components['Q1']) or attribute (design.components.Q1).

[14]:
q1 = design.components["Q1"]
[16]:
%metal_print "Where are the default options?"
"Where are the default options?"

A QComponent is created with defaults. To inspect them without instantiating one, call QComponentClass.get_template_options(design).

[17]:
TransmonPocket.get_template_options(design)
[17]:
{'pos_x': '0.0um',
 'pos_y': '0.0um',
 'orientation': '0.0',
 'chip': 'main',
 'layer': '1',
 'connection_pads': {},
 '_default_connection_pads': {'pad_gap': '15um',
  'pad_width': '125um',
  'pad_height': '30um',
  'pad_cpw_shift': '5um',
  'pad_cpw_extent': '25um',
  'cpw_width': 'cpw_width',
  'cpw_gap': 'cpw_gap',
  'cpw_extend': '100um',
  'pocket_extent': '5um',
  'pocket_rise': '65um',
  'loc_W': '+1',
  'loc_H': '+1'},
 'pad_gap': '30um',
 'inductor_width': '20um',
 'pad_width': '455um',
 'pad_height': '90um',
 'pocket_width': '650um',
 'pocket_height': '650um',
 'hfss_wire_bonds': False,
 'q3d_wire_bonds': False,
 'aedt_q3d_wire_bonds': False,
 'aedt_hfss_wire_bonds': False,
 'hfss_inductance': '10nH',
 'hfss_capacitance': 0,
 'hfss_resistance': 0,
 'hfss_mesh_kw_jj': 7e-06,
 'q3d_inductance': '10nH',
 'q3d_capacitance': 0,
 'q3d_resistance': 0,
 'q3d_mesh_kw_jj': 7e-06,
 'gds_cell_name': 'my_other_junction',
 'aedt_q3d_inductance': 1e-08,
 'aedt_q3d_capacitance': 0,
 'aedt_hfss_inductance': 1e-08,
 'aedt_hfss_capacitance': 0}
[18]:
%metal_print How do I change the default options
How do I change the default options

Two ways to set component options:

  • Instance-levelq1.options.<field> = ... changes only q1.

  • Class-levelQComponent.default_options.<field> = ... changes the default for every future component instance.

The cell below demonstrates both and restores the class defaults at the end so the rest of the notebook isn’t polluted.

[19]:
# Two different "change the options" patterns:

# 1. Change THIS instance only — what the tutorial has been doing so far.
#    Affects q1, not any future TransmonPocket you create.
q1.options.pos_x = "0.5 mm"
q1.options.pos_y = "250 um"

# 2. Change the CLASS-LEVEL DEFAULTS — affects every TransmonPocket
#    you instantiate after this point. The base ``QComponent.default_options``
#    dict carries ``pos_x``/``pos_y``; component subclasses inherit them.
#    (To change a TransmonPocket-specific default like ``pad_width``,
#    edit ``TransmonPocket.default_options.pad_width`` instead.)
from qiskit_metal.qlibrary.core import QComponent

QComponent.default_options.pos_x = "0.5 mm"
QComponent.default_options.pos_y = "250 um"

# Rebuild for changes to propagate
gui.rebuild()

# Reset class defaults so the rest of the notebook doesn't see them.
QComponent.default_options.pos_x = "0.0um"
QComponent.default_options.pos_y = "0.0um"
[20]:
%metal_print How do I work with units? <br><br> (parse options and values)
How do I work with units?

(parse options and values)

Parsing strings into floats

Quantum Metal accepts options as strings with units ("250 um", "0.5 mm"). Use design.parse_value or any component’s .parse_value to convert.

[21]:
print("Design default units for length: ", design.get_units())
print(
    "\nExample 250 micron parsed to design units:",
    design.parse_value("250 um"),
    design.get_units(),
)

dictionary = {"key_in_cm": "1.2 cm", "key_in_microns": "50 um"}
print("\nExample parse dict:", design.parse_value(dictionary))

a_list = ["1m", "1mm", "1um", "1 nm"]
print("\nExample parse list:", design.parse_value(a_list))
Design default units for length:  mm

Example 250 micron parsed to design units: 0.25 mm

Example parse dict: {'key_in_cm': 12.0, 'key_in_microns': 0.05}

Example parse list: [1000.0, 1, 0.001, 1.0000000000000002e-06]

Arithmetic in option strings

[22]:
design.parse_value("2 * 2um")
[22]:
0.004
[23]:
design.parse_value("2um + 5um")
[23]:
0.007
[24]:
design.qgeometry.tables["junction"]
[24]:
component name geometry layer subtract helper chip width hfss_inductance hfss_capacitance ... hfss_mesh_kw_jj q3d_inductance q3d_capacitance q3d_resistance q3d_mesh_kw_jj gds_cell_name aedt_q3d_inductance aedt_q3d_capacitance aedt_hfss_inductance aedt_hfss_capacitance
0 1 rect_jj LINESTRING (0.5 0.225, 0.5 0.275) 1 False False main 0.02 10nH 0 ... 0.000007 10nH 0 0 0.000007 my_other_junction 1.000000e-08 0 1.000000e-08 0

1 rows × 21 columns

[71]:
%metal_heading The geometry of QComponent: QGeometry

The geometry of QComponent: QGeometry

Bounding box

q1.qgeometry_bounds() returns (xmin, ymin, xmax, ymax) for the component’s geometry.

[72]:
for name, qcomponent in design.components.items():
    print(f"{name:10s} : {qcomponent.qgeometry_bounds()}")
Q1         : [ 2.125 -0.325  2.975  0.325]
Q2         : [-0.325 -1.325  0.325 -0.475]
Q3         : [-2.975 -0.325 -2.125  0.325]
Q4         : [-0.325  0.475  0.325  1.325]
cpw1       : [ 0.2025     -0.63618364  2.125      -0.06881636]
cpw2       : [-2.125      -0.64243364 -0.2025     -0.06256636]
cpw3       : [-2.125       0.06881636 -0.2025      0.63618364]
cpw4       : [0.2025     0.06256636 2.125      0.64243364]
spiral     : [0.28  1.83  0.988 2.538]
spiral_cut : [0.28  1.83  0.988 2.538]
cpw_s1     : [0.2025 1.325  0.28   1.83  ]
ngon       : [-1.08339511  1.79033236 -0.60851854  2.24965738]
ngon_negative : [-1.17675315  1.7064653  -0.51192596  2.34952034]
CircleRaster : [-1.8  1.7 -1.2  2.3]
RectangleHollow : [-2.55  1.85 -2.05  2.15]

What is QGeometry?

Each QComponent contributes one or more shapely geometries to the design. They’re grouped by kind — poly, path, junction — accessible via qgeometry_table(kind).

[73]:
q1.qgeometry_table("poly")
[73]:
component name geometry layer subtract helper chip fillet
0 3 pad_top POLYGON ((2.325 0.015, 2.775 0.015, 2.775 0.10... 1 False False main NaN
1 3 pad_bot POLYGON ((2.325 -0.105, 2.775 -0.105, 2.775 -0... 1 False False main NaN
2 3 rect_pk POLYGON ((2.225 -0.325, 2.875 -0.325, 2.875 0.... 1 True False main NaN
3 3 a_connector_pad POLYGON ((2.65 0.12, 2.775 0.12, 2.775 0.15, 2... 1 False False main NaN
4 3 b_connector_pad POLYGON ((2.45 0.12, 2.325 0.12, 2.325 0.15, 2... 1 False False main NaN
5 3 c_connector_pad POLYGON ((2.575 -0.12, 2.775 -0.12, 2.775 -0.1... 1 False False main NaN
6 3 d_connector_pad POLYGON ((2.45 -0.12, 2.325 -0.12, 2.325 -0.17... 1 False False main NaN

path geometries are lines with a width:

[74]:
q1.qgeometry_table("path")
[74]:
component name geometry layer subtract helper chip width fillet hfss_wire_bonds q3d_wire_bonds aedt_q3d_wire_bonds aedt_hfss_wire_bonds
0 3 a_wire LINESTRING (2.775 0.1375, 2.8 0.1375, 2.87 0.2... 1 False False main 0.025 NaN False False False False
1 3 a_wire_sub LINESTRING (2.775 0.1375, 2.8 0.1375, 2.87 0.2... 1 True False main 0.049 NaN False False False False
2 3 b_wire LINESTRING (2.325 0.1375, 2.3 0.1375, 2.23 0.2... 1 False False main 0.025 NaN False False False False
3 3 b_wire_sub LINESTRING (2.325 0.1375, 2.3 0.1375, 2.23 0.2... 1 True False main 0.049 NaN False False False False
4 3 c_wire LINESTRING (2.775 -0.1375, 2.8 -0.1375, 2.87 -... 1 False False main 0.025 NaN False False False False
5 3 c_wire_sub LINESTRING (2.775 -0.1375, 2.8 -0.1375, 2.87 -... 1 True False main 0.049 NaN False False False False
6 3 d_wire LINESTRING (2.325 -0.1375, 2.3 -0.1375, 2.23 -... 1 False False main 0.025 NaN False False False False
7 3 d_wire_sub LINESTRING (2.325 -0.1375, 2.3 -0.1375, 2.23 -... 1 True False main 0.049 NaN False False False False

junction geometries hold Josephson-junction placement, defined by a LineString and a width.

[75]:
q1.qgeometry_table("junction")
[75]:
component name geometry layer subtract helper chip width hfss_inductance hfss_capacitance ... hfss_mesh_kw_jj q3d_inductance q3d_capacitance q3d_resistance q3d_mesh_kw_jj gds_cell_name aedt_q3d_inductance aedt_q3d_capacitance aedt_hfss_inductance aedt_hfss_capacitance
0 3 rect_jj LINESTRING (2.55 -0.015, 2.55 0.015) 1 False False main 0.02 10nH 0 ... 0.000007 10nH 0 0 0.000007 my_other_junction 1.000000e-08 0 1.000000e-08 0

1 rows × 21 columns

Advanced — arrays and expressions

Option strings support pythonic ast.literal_eval — you can embed lists, tuples, and arithmetic, all parsed lazily.

[25]:
#### List
print("* " * 10 + " LIST " + "* " * 10, "\n")
str_in = "[1,2,3,'10um']"
out = design.parse_value(str_in)
print(f"Parsed output:\n {str_in}  ->  {out} \n Out type: {type(out)}\n")

str_in = "['2*2um', '2um + 5um']"
out = design.parse_value(str_in)
print(f"Parsed output:\n {str_in}  ->  {out} \n Out type: {type(out)}\n")

#### Dict
print("* " * 10 + " DICT " + "* " * 10, "\n")

str_in = "{'key1': '100um', 'key2': '1m'}"
out = design.parse_value(str_in)
print(f"Parsed output:\n {str_in}  ->  {out} \n Out type: {type(out)}\n")
* * * * * * * * * *  LIST * * * * * * * * * *

Parsed output:
 [1,2,3,'10um']  ->  [1, 2, 3, 0.01]
 Out type: <class 'list'>

Parsed output:
 ['2*2um', '2um + 5um']  ->  [0.004, 0.007]
 Out type: <class 'list'>

* * * * * * * * * *  DICT * * * * * * * * * *

Parsed output:
 {'key1': '100um', 'key2': '1m'}  ->  {'key1': 0.1, 'key2': 1000.0}
 Out type: <class 'addict.addict.Dict'>

Overwriting components

By default, instantiating a component with an existing name raises. Enable overwrite for repeatable demos:

[26]:
design.overwrite_enabled = True
[27]:
%metal_heading Quantum pins: QPins!

Quantum pins: QPins!

QPins — the dynamic way to connect components

A component designer can expose QPin endpoints. Pins let components link up: two transmons each expose a pin, a RouteMeander connects them into a CPW. Below, we recreate Q1 with named connection pads.

[28]:
from qiskit_metal.qlibrary.qubits.transmon_pocket import TransmonPocket

design.delete_all_components()

options = dict(
    pad_width="425 um",
    pocket_height="650um",
    connection_pads=dict(  # pin connectors
        a=dict(loc_W=+1, loc_H=+1),
        b=dict(loc_W=-1, loc_H=+1, pad_height="30um"),
        c=dict(loc_W=+1, loc_H=-1, pad_width="200um"),
        d=dict(loc_W=-1, loc_H=-1, pad_height="50um"),
    ),
)

q1 = TransmonPocket(
    design, "Q1", options=dict(pos_x="+0.5mm", pos_y="+0.5mm", **options)
)
[29]:
# Take a screenshot with the component highlighted and the pins shown
gui.rebuild()
gui.autoscale()
gui.edit_component("Q1")
gui.zoom_on_components(["Q1"])
gui.highlight_components(["Q1"])
gui.screenshot()
../../_images/tut_1-Overview_1.1-Quick-start_55_0.png

Access a pin by name:

[30]:
q1.pins.a
q1.pins["a"]
[30]:
{'points': [array([0.925, 0.7  ]), array([0.925, 0.69 ])],
 'middle': array([0.925, 0.695]),
 'normal': array([1., 0.]),
 'tangent': array([0., 1.]),
 'width': 0.01,
 'gap': 0.006,
 'chip': 'main',
 'parent_name': 2,
 'net_id': 0,
 'length': 0}

Editing component source from the API

gui.edit_component("Q1") opens the Qt source editor in the desktop GUI. In the headless viewer it’s a no-op — edit the source file directly and call gui.rebuild().

[31]:
gui.edit_component("Q1")

What’s next?

You’ve placed a transmon, modified it, compared variants, connected two qubits with a CPW, and inspected geometry. Plenty for one tutorial.



For more information, review the Introduction to Quantum Computing and Quantum Hardware lectures below

  • Superconducting Qubits I: Quantizing a Harmonic Oscillator, Josephson Junctions Part 1
Lecture Video Lecture Notes Lab
  • Superconducting Qubits I: Quantizing a Harmonic Oscillator, Josephson Junctions Part 2
Lecture Video Lecture Notes Lab
  • Superconducting Qubits I: Quantizing a Harmonic Oscillator, Josephson Junctions Part 3
Lecture Video Lecture Notes Lab
  • Superconducting Qubits II: Circuit Quantum Electrodynamics, Readout and Calibration Methods Part 1
Lecture Video Lecture Notes Lab
  • Superconducting Qubits II: Circuit Quantum Electrodynamics, Readout and Calibration Methods Part 2
Lecture Video Lecture Notes Lab
  • Superconducting Qubits II: Circuit Quantum Electrodynamics, Readout and Calibration Methods Part 3
Lecture Video Lecture Notes Lab