%load_ext autoreload
%autoreload 2

import numpy as np
import qiskit_metal as metal
from qiskit_metal import designs, draw
from qiskit_metal import MetalGUI, Dict, open_docs

%metal_heading Welcome to Qiskit Metal Study!

# To start qiskit-metal, you have to create instance of below two.
# After runing this cell, metal gui will appear.
design = designs.DesignPlanar()
gui = MetalGUI(design)

# If you delete all components in the current design, you can use delete_all_components method.
design.delete_all_components()

# To apply changes in design instance to your gui, you can use rebuild method in gui.
gui.rebuild()

# You can import templated qubit class from qiskit_metal.qlibrary.qubits.
# For transmon pocket, 
from qiskit_metal.qlibrary.qubits.transmon_pocket import TransmonPocket


# For most simplest case, you can create TransmonPocket instance with default options as below.
# Methods for creating QComponent with options will be explained in the section 2.4
# Please ignore warnings here. These are caused by Shapely's future update, and would not make any troubles now.
q1 = TransmonPocket(design, 'Q1')
gui.rebuild()

# If you call TransmonPocket instance, you can see the parameters of it. 
q1

# Above options are same with TransmonPocket's default option
TransmonPocket.default_options

# If you want to change parameters of Transmon qubit, 
# you can simply change the parameters in the q1.options as below
q1.options.pos_x = '2.0 mm' # Note that if you don't input the unit here, default length unit is mm not um.
q1.options.pos_y = '2.0 mm'
q1.options.pad_height = '250 um'
q1.options.pad_width  = '300 um'

# By rebuilding gui, you can check the parameters are changed.
gui.rebuild()

# In many cases, copying QComponents is very useful. 
# To copy single QComponent, you can use copy_qcomponent method for design instance.
q2 = design.copy_qcomponent(q1, 'Q2')
# If you want to change parameters for copyed object, you can do it with two different way. 
# First method is same with above explained changing parameters.
q2.options.pos_x = '-2.0mm'
gui.rebuild()
# By runing below, you can automatly scale the gui.
gui.autoscale()

# Second method is giving options to the copy_qcomponent as below.
q3 = design.copy_qcomponent(q1, 'Q3', dict(pos_x='-4.0mm'))

# If you want to copy multiple qcomponent, you can use below method. 
newcopies = design.copy_multiple_qcomponents([q1, q2], ['Q3', 'Q4'], [dict(pos_y='-2.0mm'), dict(pos_y='-2.0mm')])
gui.rebuild()
gui.autoscale()

design.delete_all_components()
gui.rebuild()

# To create Transmon Qubit with option, the only thing you have to do is 
# creating dictionary of options and give it as a input when you create TransmonPocket instance.
# You can give options directly, without create dictionary as variable, like below, 

# Adding connection pads also can be done by add "connection_pads" in your option dictionary as below.
q1 = TransmonPocket(design, 'Q1', 
                    options = dict(
                        pos_x='-1.5mm', 
                        pos_y='+0.0mm', 
                        pad_width = '425 um',
                        pocket_height = '650um',
                        connection_pads = dict(
                            a = dict(loc_W=+1,loc_H=+1), # (low_W, low_H) = (1, 1) => First Quadrant
                            b = dict(loc_W=-1,loc_H=+1, pad_height='30um'), # (low_W, low_H) = (-1, 1) => Second Quadrant
                            c = dict(loc_W=+1,loc_H=-1, pad_width='200um'), # (low_W, low_H) = (1, -1) => Fourth Quadrant
                            d = dict(loc_W=-1,loc_H=-1, pad_height='50um') # (low_W, low_H) = (-1, -1) => Third Quadrant
                        ) # Note that connection_pads have two geometrical options pad_height and pad_weight.
                          # You can modify those values from default, by add them in option as above.
                    )
                )
gui.rebuild()


design.delete_all_components()
gui.rebuild()

# I prefer below method, which is very convenient for reusing the parameters.

optionsQ = dict(
    pad_width = '425 um',
    pocket_height = '650um',
    connection_pads = dict(  # Qbits defined to have 4 pins
        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='-1.5mm', pos_y='+0.0mm', **optionsQ))
q2 = TransmonPocket(design, 'Q2', options = dict(pos_x='+0.35mm', pos_y='+1.0mm', orientation = '90',**optionsQ))
q3 = TransmonPocket(design, 'Q3', options = dict(pos_x='2.0mm', pos_y='+0.0mm', **optionsQ))

gui.rebuild()
gui.autoscale()
gui.highlight_components(['Q1', 'Q2', 'Q3'])  # This is to show the pins, so we can choose what to connect
gui.rebuild()
gui.autoscale()

# You can import RouteMeander class as below
from qiskit_metal.qlibrary.tlines.meandered import RouteMeander

# Same with TransmonPocket, it also have its default options
RouteMeander.get_template_options(design)

# Connecting two TransmonPocket with RouteMinder is very easy.
# It can be done by setting start_pin and end_pin in option as below
ops=dict(fillet='90um')
options = Dict(
    total_length= '8mm', # This parameter is key parameter for changing resonant frequency
    hfss_wire_bonds = True,
    pin_inputs=Dict(
        start_pin=Dict(
            component= 'Q1', # Start component Name
            pin= 'a'), # Start Pin in component
        end_pin=Dict(
            component= 'Q2',
            pin= 'b')),
    lead=Dict(
        start_straight='0mm', # How long go straight from the starting point
        end_straight='0.5mm'), # How long go straight to the end point
    meander=Dict(
       asymmetry='-1mm'),
    **ops # Remark that this syntax usually used for reusing options.
)

cpw = RouteMeander(design, options=options)
gui.rebuild()
gui.autoscale()

cpw.options.lead.start_straight='100um'
gui.rebuild()
gui.autoscale()

cpw.options['fillet']='30um'
gui.rebuild()
gui.autoscale()

from collections import OrderedDict
jogs = OrderedDict()
jogs[0] = ["L", '800um']
jogs[1] = ["L", '500um']
jogs[2] = ["R", '200um']
jogs[3] = ["R", '500um']
 
options = Dict(
    total_length= '14mm',
    pin_inputs=Dict(
        start_pin=Dict(
            component= 'Q1',
            pin= 'd'),
        end_pin=Dict(
            component= 'Q3',
            pin= 'd')),
    lead=Dict(
        start_straight='0.1mm',
        end_straight='0.1mm',
        start_jogged_extension=jogs,
        end_jogged_extension=jogs),
    meander=Dict(
       asymmetry='-1.2mm'),
    **ops
)

try:
    cpw5.delete()
except NameError: pass

cpw5 = RouteMeander(design,options=options)
gui.rebuild()
gui.autoscale()

# Before showing examples of anchors, I will reset design and add some new transmon pockets. 

design.delete_all_components()
gui.rebuild()

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')
    )
)

q0 = TransmonPocket(design, 'Q0', options = dict(pos_x='-1.0mm', pos_y='-1.0mm', **options))
q1 = TransmonPocket(design, 'Q1', options = dict(pos_x='1.0mm', pos_y='+0.0mm', **options))

gui.rebuild()
gui.autoscale()

# You can import RoutePathfinder as below
from qiskit_metal.qlibrary.tlines.pathfinder import RoutePathfinder
from collections import OrderedDict
# You should add anchors in OrderedDict because the order in anchors are very important.
anchors = OrderedDict()
anchors[0] = np.array([-0.452, -0.555])
anchors[1] = np.array([-0.452, -1.5])
anchors[2] = np.array([0.048, -1.5])

options = {'pin_inputs': 
            {'start_pin': {'component': 'Q0', 'pin': 'b'}, 
             'end_pin': {'component': 'Q1', 'pin': 'b'}},
            'lead': {'start_straight': '91um', 'end_straight': '95um'},
            'step_size': '0.25mm',
            'anchors': anchors,
            **ops
           }

qa = RoutePathfinder(design, 'line', options)

gui.rebuild()
gui.autoscale()

