Note
This page was generated from tut//3-Renderers//3.1-Introduction-to-QRenderers.ipynb.
Introduction to QRenderers#
For convenience, let’s begin by enabling automatic reloading of modules when they change.
[1]:
%load_ext autoreload
%autoreload 2
Import Qiskit Metal#
[2]:
import qiskit_metal as metal
from qiskit_metal import designs, draw
from qiskit_metal import MetalGUI, Dict, Headings
from qiskit_metal.qlibrary.qubits.transmon_pocket import TransmonPocket
from qiskit_metal.qlibrary.qubits.transmon_cross import TransmonCross
from qiskit_metal.renderers.renderer_gds.gds_renderer import QGDSRenderer
[3]:
Headings.h1('The default_options in a QComponent are different than the default_options in QRenderers.')
The default_options in a QComponent are different than the default_options in QRenderers.
[4]:
TransmonPocket.default_options
[4]:
{'chip': 'main',
'pos_x': '0um',
'pos_y': '0um',
'pad_gap': '30um',
'inductor_width': '20um',
'pad_width': '455um',
'pad_height': '90um',
'pocket_width': '650um',
'pocket_height': '650um',
'orientation': '0',
'_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'}}
[5]:
QGDSRenderer.default_options
[5]:
{'short_segments_to_not_fillet': 'True',
'check_short_segments_by_scaling_fillet': '2.0',
'gds_unit': '1',
'ground_plane': 'True',
'negative_mask': {'main': []},
'corners': 'circular bend',
'tolerance': '0.00001',
'precision': '0.000000001',
'width_LineString': '10um',
'path_filename': '../resources/Fake_Junctions.GDS',
'junction_pad_overlap': '5um',
'max_points': '199',
'cheese': {'datatype': '100',
'shape': '0',
'cheese_0_x': '25um',
'cheese_0_y': '25um',
'cheese_1_radius': '100um',
'view_in_file': {'main': {1: True}},
'delta_x': '100um',
'delta_y': '100um',
'edge_nocheese': '200um'},
'no_cheese': {'datatype': '99',
'buffer': '25um',
'cap_style': '2',
'join_style': '2',
'view_in_file': {'main': {1: True}}},
'bounding_box_scale_x': '1.2',
'bounding_box_scale_y': '1.2'}
A renderer needs to inherent from QRenderer#
For Example, QGDSRender inherents from QRenderer.
When any QRenderer is registered within QDesign, the QRenderer instance has options, which hold the latest set of values for default_options. The GUI can also update these options.
An example of updating options is further below in this notebook.
A user can customize things two ways#
Directly update the options that originated from default_options, for either QComponent or QRenderer.
Pass options to a QComponent which will be placed in a QGeometry table, then used by QRenderer.
How to get options from QRenderer to be placed within the QGeometry table?#
We set this up so that older QComponents can be agnostic of newer QRenderers.
An example of this below is gds_cell_name='FakeJunction_01'
. This is passed through to QGeometry, when a QComponent is instantiated. The QGDSRenderer has a default, which is not editable during run-time, but can be customized when a QComponent is instantiated.
[6]:
Headings.h1('How does a QRenderer get registered within QDesign?')
How does a QRenderer get registered within QDesign?
By default, QRenderers are registered within QDesign during init QDesign#
renderers_to_load
has the name of the QRenderer (key), class name (value), and path (value).Presently, GDS and Ansys QRenderers are registered during init.
[7]:
design = designs.DesignPlanar()
[8]:
# Use GDS QRenderer for remaining examples. Can do similar things with Ansys QRenderer.
#an_ansys = design._renderers['ansys']
#an_ansys = design._renderers.ansys
#a_gds = design._renderers['gds']
a_gds = design._renderers.gds
[9]:
gui = MetalGUI(design)
design.overwrite_enabled = True
[10]:
Headings.h1('Populate QDesign to demonstrate exporting to GDS format.')
Populate QDesign to demonstrate exporting to GDS format.
[11]:
from qiskit_metal.qlibrary.qubits.transmon_pocket import TransmonPocket
# Allow running the same cell here multiple times to overwrite changes
design.overwrite_enabled = True
## Custom options for all the transmons
options = dict(
# Some options we want to modify from the deafults
# (see below for defaults)
pad_width = '425 um',
pad_gap = '80 um',
pocket_height = '650um',
# Adding 4 connectors (see below for defaults)
connection_pads=dict(
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')
)
)
Note:#
[12]:
## Create 4 TransmonPockets
q1 = TransmonPocket(design, 'Q1', options = dict(
pos_x='+2.55mm', pos_y='+0.0mm', gds_cell_name='FakeJunction_02', **options))
q2 = TransmonPocket(design, 'Q2', options = dict(
pos_x='+0.0mm', pos_y='-0.9mm', orientation = '90', gds_cell_name='FakeJunction_02', **options))
q3 = TransmonPocket(design, 'Q3', options = dict(
pos_x='-2.55mm', pos_y='+0.0mm', gds_cell_name='FakeJunction_01',**options))
q4 = TransmonPocket(design, 'Q4', options = dict(
pos_x='+0.0mm', pos_y='+0.9mm', orientation = '90', gds_cell_name='my_other_junction', **options))
[13]:
## Rebuild the design
gui.rebuild()
gui.autoscale()
#Connect using techniques explained earlier notebooks.
from qiskit_metal.qlibrary.tlines.meandered import RouteMeander
RouteMeander.get_template_options(design)
options = Dict(
meander=Dict(
lead_start='0.1mm',
lead_end='0.1mm',
asymmetry='0 um')
)
def connect(component_name: str, component1: str, pin1: str, component2: str, pin2: str,
length: str, asymmetry='0 um', flip=False, fillet='50um'):
"""Connect two pins with a CPW."""
myoptions = Dict(
fillet=fillet,
pin_inputs=Dict(
start_pin=Dict(
component=component1,
pin=pin1),
end_pin=Dict(
component=component2,
pin=pin2)),
lead=Dict(
start_straight='0.13mm',
end_straight='0.13mm'
),
total_length=length)
myoptions.update(options)
myoptions.meander.asymmetry = asymmetry
myoptions.meander.lead_direction_inverted = 'true' if flip else 'false'
return RouteMeander(design, component_name, myoptions)
asym = 90
cpw1 = connect('cpw1', 'Q1', 'd', 'Q2', 'c', '5.7 mm', f'+{asym}um', fillet='25um')
cpw2 = connect('cpw2', 'Q3', 'c', 'Q2', 'a', '5.4 mm', f'-{asym}um', flip=True, fillet='100um')
cpw3 = connect('cpw3', 'Q3', 'a', 'Q4', 'b', '5.3 mm', f'+{asym}um', fillet='75um')
cpw4 = connect('cpw4', 'Q1', 'b', 'Q4', 'd', '5.5 mm', f'-{asym}um', flip=True)
gui.rebuild()
gui.autoscale()
[14]:
gui.screenshot()
[15]:
Headings.h1('Exporting a GDS file.')
Exporting a GDS file.
[16]:
#QDesign enables GDS renderer during init.
a_gds = design.renderers.gds
# An alternate way to envoke the gds commands without using a_gds:
# design.renderers.gds.export_to_gds()
#Show the options for GDS
a_gds.options
[16]:
{'short_segments_to_not_fillet': 'True',
'check_short_segments_by_scaling_fillet': '2.0',
'gds_unit': 0.001,
'ground_plane': 'True',
'negative_mask': {'main': []},
'corners': 'circular bend',
'tolerance': '0.00001',
'precision': '0.000000001',
'width_LineString': '10um',
'path_filename': '../resources/Fake_Junctions.GDS',
'junction_pad_overlap': '5um',
'max_points': '199',
'cheese': {'datatype': '100',
'shape': '0',
'cheese_0_x': '25um',
'cheese_0_y': '25um',
'cheese_1_radius': '100um',
'view_in_file': {'main': {1: True}},
'delta_x': '100um',
'delta_y': '100um',
'edge_nocheese': '200um'},
'no_cheese': {'datatype': '99',
'buffer': '25um',
'cap_style': '2',
'join_style': '2',
'view_in_file': {'main': {1: True}}},
'bounding_box_scale_x': '1.2',
'bounding_box_scale_y': '1.2'}
To make the junction table work correctly, GDS Renderer needs the correct path to the gds file which has cells#
Each cell is a junction to be placed in a Transmon. A sample gds file is provided in directory qiskit_metal/tutorials/resources
. There are three cells with names “Fake_Junction_01”, “Fake_Junction_01”, and “my_other_junction”. The default name used by GDS Render is “my_other_junction”. If you want to customize and select a junction, through the options, you can pass it when a qcomponent is being added to QDesign.
This allows for an already prepared e-beam pattern for a given junction structure to be automatically imported and placed at the correct location.
[17]:
a_gds.options['path_filename'] = '../resources/Fake_Junctions.GDS'
Do you want GDS Renderer to fix any short-segments in your QDesign when using fillet?’
[18]:
#If you have a fillet_value and there are LineSegments that are shorter than 2*fillet_value,
#When true, the short segments will not be fillet'd.
a_gds.options['short_segments_to_not_fillet'] = 'True'
scale_fillet = 2.0
a_gds.options['check_short_segments_by_scaling_fillet'] = scale_fillet
[19]:
# Export GDS file for all components in design.
#def export_to_gds(self, file_name: str, highlight_qcomponents: list = []) -> int:
# Please change the path where you want to write a GDS file.
#Examples below.
#a_gds.export_to_gds("../../../gds-files/GDS QRenderer Notebook.gds")
a_gds.export_to_gds('GDS QRenderer Notebook.gds')
[19]:
1
[20]:
# Export a GDS file which contains only few components.
# You will probably want to put the exported file in a specific directory.
# Please give the full path for output.
a_gds.export_to_gds("four_qcomponents.gds",
highlight_qcomponents=['cpw1', 'cpw4', 'Q1', 'Q3'])
[20]:
1
How to “execute” exporting an QRenderer from GUI vs notebook?#
Within the GUI, there are icons: GDS, HFSS and Q3D.
Example for GDS: Select the components that you want to export from QGeometry Tables. Select the path/file_name and the same thing should happen as the cells above.
[21]:
Headings.h1('QUESTION: Where is the geometry of a QComponent placed?')
QUESTION: Where is the geometry of a QComponent placed?
Answer: QGeometry tables!#
What is QGeometry?#
All QRenderers use the QGeometry tables to export from QDesign. Each table is a Pandas DataFrame.#
We can get all the QGeometry of a QComponent. There are several kinds, such as path
, poly
and, junction
.
[22]:
#Many ways to view the QGeometry tables.
#If you want to view, uncomment below lines and and run it.
#design.qgeometry.tables
#design.qgeometry.tables['path']
#design.qgeometry.tables['poly']
[23]:
design.qgeometry.tables['junction']
[23]:
component | name | geometry | layer | subtract | helper | chip | width | hfss_inductance | hfss_capacitance | hfss_resistance | hfss_mesh_kw_jj | q3d_inductance | q3d_capacitance | q3d_resistance | q3d_mesh_kw_jj | gds_cell_name | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | rect_jj | LINESTRING (2.55000 -0.04000, 2.55000 0.04000) | 1 | False | False | main | 0.02 | 10nH | 0 | 0 | 0.000007 | 10nH | 0 | 0 | 0.000007 | FakeJunction_02 |
1 | 2 | rect_jj | LINESTRING (0.04000 -0.90000, -0.04000 -0.90000) | 1 | False | False | main | 0.02 | 10nH | 0 | 0 | 0.000007 | 10nH | 0 | 0 | 0.000007 | FakeJunction_02 |
2 | 3 | rect_jj | LINESTRING (-2.55000 -0.04000, -2.55000 0.04000) | 1 | False | False | main | 0.02 | 10nH | 0 | 0 | 0.000007 | 10nH | 0 | 0 | 0.000007 | FakeJunction_01 |
3 | 4 | rect_jj | LINESTRING (0.04000 0.90000, -0.04000 0.90000) | 1 | False | False | main | 0.02 | 10nH | 0 | 0 | 0.000007 | 10nH | 0 | 0 | 0.000007 | my_other_junction |
Let us look at all the polygons used to create qubit q1
#
Poly table hold the polygons identified from QComponents.
[24]:
q1.qgeometry_table('poly')
[24]:
component | name | geometry | layer | subtract | helper | chip | fillet | |
---|---|---|---|---|---|---|---|---|
0 | 1 | pad_top | POLYGON ((2.33750 0.04000, 2.76250 0.04000, 2.... | 1 | False | False | main | NaN |
1 | 1 | pad_bot | POLYGON ((2.33750 -0.13000, 2.76250 -0.13000, ... | 1 | False | False | main | NaN |
2 | 1 | rect_pk | POLYGON ((2.22500 -0.32500, 2.87500 -0.32500, ... | 1 | True | False | main | NaN |
3 | 1 | a_connector_pad | POLYGON ((2.63750 0.14500, 2.76250 0.14500, 2.... | 1 | False | False | main | NaN |
4 | 1 | b_connector_pad | POLYGON ((2.46250 0.14500, 2.33750 0.14500, 2.... | 1 | False | False | main | NaN |
5 | 1 | c_connector_pad | POLYGON ((2.56250 -0.14500, 2.76250 -0.14500, ... | 1 | False | False | main | NaN |
6 | 1 | d_connector_pad | POLYGON ((2.46250 -0.14500, 2.33750 -0.14500, ... | 1 | False | False | main | NaN |
Paths are lines. These can have a width.
[25]:
q1.qgeometry_table('path')
[25]:
component | name | geometry | layer | subtract | helper | chip | width | fillet | hfss_wire_bonds | q3d_wire_bonds | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | a_wire | LINESTRING (2.76250 0.15500, 2.78750 0.15500, ... | 1 | False | False | main | 0.010 | NaN | False | False |
1 | 1 | a_wire_sub | LINESTRING (2.76250 0.15500, 2.78750 0.15500, ... | 1 | True | False | main | 0.022 | NaN | False | False |
2 | 1 | b_wire | LINESTRING (2.33750 0.15500, 2.31250 0.15500, ... | 1 | False | False | main | 0.010 | NaN | False | False |
3 | 1 | b_wire_sub | LINESTRING (2.33750 0.15500, 2.31250 0.15500, ... | 1 | True | False | main | 0.022 | NaN | False | False |
4 | 1 | c_wire | LINESTRING (2.76250 -0.15500, 2.78750 -0.15500... | 1 | False | False | main | 0.010 | NaN | False | False |
5 | 1 | c_wire_sub | LINESTRING (2.76250 -0.15500, 2.78750 -0.15500... | 1 | True | False | main | 0.022 | NaN | False | False |
6 | 1 | d_wire | LINESTRING (2.33750 -0.15500, 2.31250 -0.15500... | 1 | False | False | main | 0.010 | NaN | False | False |
7 | 1 | d_wire_sub | LINESTRING (2.33750 -0.15500, 2.31250 -0.15500... | 1 | True | False | main | 0.022 | NaN | False | False |
The junction table is handled differently by each QRenderer.#
What does GDS do with “junction” table?#
This is better explained in folder 5 All QRenderers/5.2 GDS/GDS QRenderer notebook.
[26]:
q1.qgeometry_table('junction')
[26]:
component | name | geometry | layer | subtract | helper | chip | width | hfss_inductance | hfss_capacitance | hfss_resistance | hfss_mesh_kw_jj | q3d_inductance | q3d_capacitance | q3d_resistance | q3d_mesh_kw_jj | gds_cell_name | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | rect_jj | LINESTRING (2.55000 -0.04000, 2.55000 0.04000) | 1 | False | False | main | 0.02 | 10nH | 0 | 0 | 0.000007 | 10nH | 0 | 0 | 0.000007 | FakeJunction_02 |
Geometric boundary of a QComponent?#
q1.qgeometry_bounds()
.[27]:
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.22 -0.54399198 2.125 -0.07600802]
cpw2 : [-2.125 -0.55810289 -0.22 -0.06189711]
cpw3 : [-2.125 0.07552405 -0.22 0.54447595]
cpw4 : [0.22 0.07576603 2.125 0.54423397]
Qiskit Metal Version#
[28]:
metal.about();
Qiskit Metal 0.0.3
Basic
____________________________________
Python 3.7.8 | packaged by conda-forge | (default, Nov 27 2020, 18:48:03) [MSC v.1916 64 bit (AMD64)]
Platform Windows AMD64
Installation path c:\workspace\qiskit-metal\qiskit_metal
Packages
____________________________________
Numpy 1.19.5
Qutip 4.5.3
Rendering
____________________________________
Matplotlib 3.3.4
GUI
____________________________________
PySide2 version 5.13.2
Qt version 5.9.7
SIP version 4.19.8
IBM Quantum Team
[29]:
# gui.main_window.close()
For more information, review the Introduction to Quantum Computing and Quantum Hardware lectures below
|
Lecture Video | Lecture Notes | Lab |
|
Lecture Video | Lecture Notes | Lab |
|
Lecture Video | Lecture Notes | Lab |
|
Lecture Video | Lecture Notes | Lab |
|
Lecture Video | Lecture Notes | Lab |
|
Lecture Video | Lecture Notes | Lab |