Simulated charge stability diagrams for a 2x2 quantum dot system
This example shows how to use qtt.simulation.dotsystem
to define a Hubbard-based model system of a 4 quantum dot array in a 2x2 plaquette configuration. Here we will use this model system to reproduce the Fig 1c plot from https://aip.scitation.org/doi/10.1063/1.5025928
[1]:
%matplotlib inline
import os
import numpy as np
import matplotlib.pyplot as plt
import qtt.simulation.dotsystem as dotsystem
Define some extra helper functions:
[2]:
def gates_from_det(dot_system, det_values=None):
""" Sets the correct gate voltages. Run this function after setting the detuning variables."""
if det_values:
return np.dot(np.linalg.inv(dot_system.la_matrix), det_values)
det_values = [getattr(dot_system, 'det%d' % (i + 1)) for i in range(dot_system.ndots)]
gate_values = np.dot(np.linalg.inv(dot_system.la_matrix), det_values)
for i in range(dot_system.ndots):
setattr(dot_system, 'P%d' % (i + 1), gate_values[i])
return gate_values
def det_from_gates(dot_system, plunger_values=None):
""" Sets the correct detuning variables that matches the gate combination.
Run this function after setting the gate voltages.
"""
if plunger_values:
return np.dot(dot_system.la_matrix, plunger_values)
plunger_values = np.array([getattr(dot_system, 'P%d' % (i + 1)) for i in range(dot_system.ndots)])
det_values = np.dot(dot_system.la_matrix, plunger_values)
for i in range(dot_system.ndots):
setattr(dot_system, 'det%d' % (i + 1), det_values[i])
return det_values
def parse_scan_parameters(dot_system, scan_parameters, scan_steps, scan_range):
""" Used to parse the input to the simulate_honeycomb function."""
half_range = scan_range/2
scan_steps_x, scan_steps_y = scan_steps
scan_min_max = [[-half_range, half_range, -half_range, half_range],
[-half_range, -half_range, half_range, half_range]]
dot_system.makeparamvalues2D(scan_parameters, scan_min_max, scan_steps_x, scan_steps_y)
if scan_parameters[0].startswith('det'):
for parameter in dot_system.scan_parameters:
dot_system.vals2D[pn] += getattr(dot_system, parameter)
parameters = dot_system.vals2D.copy()
return parameters
initial_values = dot_system.getall('det')
det = [np.zeros(dot_system.vals2D[scan_parameters[0]].shape) for i in range (dot_system.ndots)]
params = dot_system.vals2D.copy()
dict_params = {}
for name in scan_parameters:
if '{' in name:
dict_prop = eval(name)
for name2, prop in dict_prop.items():
dict_params[name2] = getattr(dot_system, name2) + params[name] * prop
else:
dict_params[name] = getattr(dot_system, name) + params[name]
for step_x in range(scan_steps_x):
for step_y in range(scan_steps_y):
for pn, pv in dict_params.items():
setattr(dot_system, pn, pv[step_x, step_y])
det_temp = det_from_gates(dot_system)
for k in range(len(det_temp)):
det[k][step_x, step_y] = det_temp[k]
dot_system.setall('det', initial_values)
dot_system.vals2D = {}
for i in range(len(det)):
dot_system.vals2D['det%i' % (i + 1)] = det[i]
return params
def show_charge_occupation_numbers_on_click(dot_system, x_data, y_data, number_of_clicks=1):
""" Shows the charge occupation numbers at the clicked points in the plotted charge stability diagram.
Args:
dot_system (dot_system): The simulated dot system.
x_data (np.array): The parsed result data from the independent gate variable.
y_data (np.array): The parsed result data from the dependent gate variable.
number_of_clicks (int): The number of times the occupation numbers should be printed.
"""
mV_minimum_x = x_data.min()
mV_minimum_y = y_data.min()
mV_range_x = x_data.max() - mV_minimum_x
mV_range_y = y_data.max() - mV_minimum_y
pixels_range_x, pixels_range_y = np.shape(x_data)
if not 'QTT_UNITTEST' in os.environ:
for i in range(number_of_clicks):
mouse_clicks = plt.ginput()
if mouse_clicks:
(mV_coordinate_x, mV_coordinate_y) = mouse_clicks[0]
x_index = int((mV_coordinate_x - mV_minimum_x) / mV_range_x * pixels_range_x)
y_index = int((mV_coordinate_y - mV_minimum_y) / mV_range_y * pixels_range_y)
charge_occupation_numbers = str(dot_system.hcgs[y_index, x_index])
plt.text(mV_coordinate_x, mV_coordinate_y, charge_occupation_numbers, color='white')
Initialize the model system with the experimental parameters
[3]:
def initialize_two_by_two_system():
""" Creates the two by two quantum model. The parameters are set according to the experimental setup."""
two_by_two = dotsystem.TwoXTwo()
# cross-capacitance matrix and lever arms
# P1 P2 P3 P4
cross_capacitance_matrix = np.array([[ 1.00, 0.45, 0.54, 0.87], # Dot 1
[ 0.65, 1.00, 0.47, 0.50], # Dot 2
[ 0.17, 0.47, 1.00, 0.24], # Dot 3
[ 0.44, 0.35, 0.88, 1.00]]) # Dot 4
det_to_plunger = np.array([0.039 * np.ones(4), 0.041 * np.ones(4),
0.054 * np.ones(4), 0.031 * np.ones(4)]) # meV/mV
two_by_two.la_matrix = cross_capacitance_matrix * det_to_plunger
# All the following values in meV
# On-site interaction per dot
two_by_two.osC1 = 2.5
two_by_two.osC2 = 2.3
two_by_two.osC3 = 3
two_by_two.osC4 = 1.8
# Intersite interaction per pairs of dots
two_by_two.isC1 = 0.47 # 1-2
two_by_two.isC2 = 0.35 # 2-3
two_by_two.isC3 = 0.43 # 3-4
two_by_two.isC4 = 0.30 # 4-1
two_by_two.isC5 = 0.28 # 1-3
two_by_two.isC6 = 0.18 # 2-4
# Tunnel coupling per pairs of dots
two_by_two.tun1 = 0.02 # 1-2
two_by_two.tun2 = 0.02 # 2-3
two_by_two.tun3 = 0.02 # 3-4
two_by_two.tun4 = 0.02 # 4-1
# Energy offsets per dot (0 is the boundary for adding 1 electron)
two_by_two.det1 = 1
two_by_two.det2 = 1
two_by_two.det3 = 0
two_by_two.det4 = 0
gate_voltages = gates_from_det(two_by_two) # This adds the gate voltages (tbt.P#, in mV) that result in the above detuning
print('Current gate voltages: P1={:.2f} mV, P2={:.2f} mV, P3={:.2f} mV, P4={:.2f} mV'.format(*gate_voltages))
return two_by_two
Run a 2D gate scan simulation and plot the charge stability diagram
[4]:
two_by_two = initialize_two_by_two_system()
scan_parameters = ['P2', 'P4']
parameter_x, parameter_y = scan_parameters
scan_steps = [61, 61]
scan_range = 150
parsed_results = parse_scan_parameters(two_by_two, scan_parameters, scan_steps, scan_range)
two_by_two.simulatehoneycomb()
x_values = parsed_results[parameter_x]
y_values = parsed_results[parameter_y]
plt.figure()
plt.pcolor(x_values, y_values, two_by_two.honeycomb, shading='auto')
plt.xlabel("{0} (mV)".format(parameter_x))
plt.ylabel("{0} (mV)".format(parameter_y))
_ = plt.title('Charge stability diagram')
Current gate voltages: P1=35.83 mV, P2=11.19 mV, P3=-8.40 mV, P4=-12.29 mV
simulatehoneycomb: 0/61
simulatehoneycomb: 4/61
simulatehoneycomb: 8/61
simulatehoneycomb: 12/61
simulatehoneycomb: 16/61
simulatehoneycomb: 20/61
simulatehoneycomb: 24/61
simulatehoneycomb: 28/61
simulatehoneycomb: 32/61
simulatehoneycomb: 36/61
simulatehoneycomb: 40/61
simulatehoneycomb: 44/61
simulatehoneycomb: 48/61
simulatehoneycomb: 52/61
simulatehoneycomb: 56/61
simulatehoneycomb: 60/61
simulatehoneycomb: 17.52 [s] (multiprocess False)
If you would like to check the charge occupation states at different points in the charge stability diagram, you can do that using the method below. The module matplotlib is set to interactive mode using %pylab tk
. This will show up a new window that allows for clicking functionality.
[5]:
if not 'QTT_UNITTEST' in os.environ:
%pylab tk
two_by_two = initialize_two_by_two_system()
scan_parameters = ['P2', 'P4']
parameter_x, parameter_y = scan_parameters
scan_steps = [61, 61]
scan_range = 150
parsed_results = parse_scan_parameters(two_by_two, scan_parameters, scan_steps, scan_range)
two_by_two.simulatehoneycomb()
x_values = parsed_results[parameter_x]
y_values = parsed_results[parameter_y]
plt.figure()
plt.pcolor(x_values, y_values, two_by_two.honeycomb, shading='auto')
plt.xlabel("{0} (mV)".format(parameter_x))
plt.ylabel("{0} (mV)".format(parameter_y))
_ = plt.title('Charge stability diagram')
show_charge_occupation_numbers_on_click(two_by_two, x_values, y_values, number_of_clicks=4)
Populating the interactive namespace from numpy and matplotlib
Current gate voltages: P1=35.83 mV, P2=11.19 mV, P3=-8.40 mV, P4=-12.29 mV
simulatehoneycomb: 0/61
simulatehoneycomb: 4/61
simulatehoneycomb: 8/61
simulatehoneycomb: 12/61
simulatehoneycomb: 16/61
simulatehoneycomb: 20/61
simulatehoneycomb: 24/61
simulatehoneycomb: 28/61
simulatehoneycomb: 32/61
simulatehoneycomb: 36/61
simulatehoneycomb: 40/61
simulatehoneycomb: 44/61
simulatehoneycomb: 48/61
simulatehoneycomb: 52/61
simulatehoneycomb: 56/61
simulatehoneycomb: 60/61
simulatehoneycomb: 18.50 [s] (multiprocess False)
---------------------------------------------------------------------------
TclError Traceback (most recent call last)
d:\dev\qtt_release\env\lib\site-packages\matplotlib\blocking_input.py in __call__(self, n, timeout)
91 # Start event loop.
---> 92 self.fig.canvas.start_event_loop(timeout=timeout)
93 finally: # Run even on exception like ctrl-c.
d:\dev\qtt_release\env\lib\site-packages\matplotlib\backend_bases.py in start_event_loop(self, timeout)
2287 while self._looping and counter * timestep < timeout:
-> 2288 self.flush_events()
2289 time.sleep(timestep)
d:\dev\qtt_release\env\lib\site-packages\matplotlib\backends\_backend_tk.py in flush_events(self)
404 # docstring inherited
--> 405 self._master.update()
406
~\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py in update(self)
1176 """Enter event loop until all pending events have been processed by Tcl."""
-> 1177 self.tk.call('update')
1178 def update_idletasks(self):
TclError: can't invoke "update" command: application has been destroyed
During handling of the above exception, another exception occurred:
TclError Traceback (most recent call last)
d:\dev\qtt_release\env\lib\site-packages\matplotlib\backend_bases.py in _wait_cursor_for_draw_cm(self)
2784 try:
-> 2785 self.set_cursor(cursors.WAIT)
2786 yield
d:\dev\qtt_release\env\lib\site-packages\matplotlib\backends\_backend_tk.py in set_cursor(self, cursor)
543 window = self.canvas.get_tk_widget().master
--> 544 window.configure(cursor=cursord[cursor])
545 window.update_idletasks()
~\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py in configure(self, cnf, **kw)
1484 """
-> 1485 return self._configure('configure', cnf, kw)
1486 config = configure
~\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py in _configure(self, cmd, cnf, kw)
1475 return self._getconfigure1(_flatten((self._w, cmd, '-'+cnf)))
-> 1476 self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
1477 # These used to be defined in Widget:
TclError: invalid command name "."
During handling of the above exception, another exception occurred:
TclError Traceback (most recent call last)
<ipython-input-5-3b8135c8f477> in <module>
21 _ = plt.title('Charge stability diagram')
22
---> 23 show_charge_occupation_numbers_on_click(two_by_two, x_values, y_values, number_of_clicks=4)
<ipython-input-2-144fae537620> in show_charge_occupation_numbers_on_click(dot_system, x_data, y_data, number_of_clicks)
82 if not 'QTT_UNITTEST' in os.environ:
83 for i in range(number_of_clicks):
---> 84 mouse_clicks = plt.ginput()
85 if mouse_clicks:
86 (mV_coordinate_x, mV_coordinate_y) = mouse_clicks[0]
d:\dev\qtt_release\env\lib\site-packages\matplotlib\pyplot.py in ginput(n, timeout, show_clicks, mouse_add, mouse_pop, mouse_stop)
2320 n=n, timeout=timeout, show_clicks=show_clicks,
2321 mouse_add=mouse_add, mouse_pop=mouse_pop,
-> 2322 mouse_stop=mouse_stop)
2323
2324
d:\dev\qtt_release\env\lib\site-packages\matplotlib\figure.py in ginput(self, n, timeout, show_clicks, mouse_add, mouse_pop, mouse_stop)
2329 mouse_stop=mouse_stop)
2330 return blocking_mouse_input(n=n, timeout=timeout,
-> 2331 show_clicks=show_clicks)
2332
2333 def waitforbuttonpress(self, timeout=-1):
d:\dev\qtt_release\env\lib\site-packages\matplotlib\blocking_input.py in __call__(self, n, timeout, show_clicks)
261 self.clicks = []
262 self.marks = []
--> 263 BlockingInput.__call__(self, n=n, timeout=timeout)
264 return self.clicks
265
d:\dev\qtt_release\env\lib\site-packages\matplotlib\blocking_input.py in __call__(self, n, timeout)
93 finally: # Run even on exception like ctrl-c.
94 # Disconnect the callbacks.
---> 95 self.cleanup()
96 # Return the events in this case.
97 return self.events
d:\dev\qtt_release\env\lib\site-packages\matplotlib\blocking_input.py in cleanup(self, event)
250 mark.remove()
251 self.marks = []
--> 252 self.fig.canvas.draw()
253 # Call base class to remove callbacks.
254 BlockingInput.cleanup(self)
d:\dev\qtt_release\env\lib\site-packages\matplotlib\backends\backend_tkagg.py in draw(self)
7 class FigureCanvasTkAgg(FigureCanvasAgg, FigureCanvasTk):
8 def draw(self):
----> 9 super(FigureCanvasTkAgg, self).draw()
10 _backend_tk.blit(self._tkphoto, self.renderer._renderer, (0, 1, 2, 3))
11 self._master.update_idletasks()
d:\dev\qtt_release\env\lib\site-packages\matplotlib\backends\backend_agg.py in draw(self)
390 with RendererAgg.lock, \
391 (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
--> 392 else nullcontext()):
393 self.figure.draw(self.renderer)
394 # A GUI class may be need to update a window using this draw, so
~\AppData\Local\Programs\Python\Python36\lib\contextlib.py in __enter__(self)
79 def __enter__(self):
80 try:
---> 81 return next(self.gen)
82 except StopIteration:
83 raise RuntimeError("generator didn't yield") from None
d:\dev\qtt_release\env\lib\site-packages\matplotlib\backend_bases.py in _wait_cursor_for_draw_cm(self)
2786 yield
2787 finally:
-> 2788 self.set_cursor(self._lastCursor)
2789 else:
2790 yield
d:\dev\qtt_release\env\lib\site-packages\matplotlib\backends\_backend_tk.py in set_cursor(self, cursor)
542 def set_cursor(self, cursor):
543 window = self.canvas.get_tk_widget().master
--> 544 window.configure(cursor=cursord[cursor])
545 window.update_idletasks()
546
~\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py in configure(self, cnf, **kw)
1483 the allowed keyword arguments call the method keys.
1484 """
-> 1485 return self._configure('configure', cnf, kw)
1486 config = configure
1487 def cget(self, key):
~\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py in _configure(self, cmd, cnf, kw)
1474 if isinstance(cnf, str):
1475 return self._getconfigure1(_flatten((self._w, cmd, '-'+cnf)))
-> 1476 self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
1477 # These used to be defined in Widget:
1478 def configure(self, cnf=None, **kw):
TclError: invalid command name "."
ERROR:tornado.application:Exception in callback functools.partial(<function Kernel.enter_eventloop.<locals>.advance_eventloop at 0x00000223CF62FC80>)
Traceback (most recent call last):
File "d:\dev\qtt_release\env\lib\site-packages\tornado\ioloop.py", line 743, in _run_callback
ret = callback()
File "d:\dev\qtt_release\env\lib\site-packages\ipykernel\kernelbase.py", line 314, in advance_eventloop
eventloop(self)
File "d:\dev\qtt_release\env\lib\site-packages\ipykernel\eventloops.py", line 232, in loop_tk
app.tk.createfilehandler(stream.getsockopt(zmq.FD), READABLE, notifier)
AttributeError: '_tkinter.tkapp' object has no attribute 'createfilehandler'
[ ]: