[1]:
import quanguru as qg
updateBase#
This the base class for the _sweep
and Update
classes.
_sweep
objects are used/created internally with/by the Sweep
class, and, as their names suggest, these are used to sweep parameters.
Update
is created by the user when a certain parameter need to be updated in a certain step of a protocol.
In both of these cases, the parameter that needs to be changed is just the value of an attribute in some other object/s. This task requires 3 information: which object/s?, which attribute ? (its name), and what is the value of the attribute? updateBase
implements the first two and expects the value to be provided. So, first let’s see how updateBase is used, and explain its details later.
In below examples, I will use Qubit
objects, but updateBase
works with any named
and their attributes. Actually, NOTE THAT updateBase
works only with named
objects or the _auxiliaryClass
. This is a deliberate design choice, and it specifically looks for the type of the given object/s and rejects other types of object. Without this type restriction, the functionality of updateBase
would work for any object and its attribute, but it could create undefined behaviors for
the sweeps. Still, there are ways to go around of this restriction, in case it might be needed for advanced users. I start by showing this hack but don’t really recommend this hack unless you are absolutely sure what you are doing.
[2]:
# so, first let's create a dummy class
class testClass:#pylint:disable=too-few-public-methods
def __init__(self) -> None:
super().__init__()
ob1 = testClass()
# create and set an attribute to it
setattr(ob1, "newAttribute", 5)
# now, we can change the value of this newAttribute with an updateBase as follows
s0 = qg.baseClasses.updateBase(key='newAttribute')
# explicitly add this object in the name mangled `_qBase__subSys` dictionary with some arbitrary key
s0._qBase__subSys["testClass"] = ob1
# print the current newAttribute
print(ob1.newAttribute)
# run the update as follows with the new value for the newAttribute
s0._runUpdate(1)
# print the updated/current newAttribute
print(ob1.newAttribute)
5
1
Having covered some arbitrary object, I will use named
objects from this point on, and, when I simply say object, I will mean a named
object in all my explanations.
So, first let’s cover the easiest case: we have a single object, and we have an explicit reference to it.
[3]:
# say we have the below Qubit objects with the references qub1, qub2
qub1 = qg.Qubit(frequency=1)
qub2 = qg.Qubit(frequency=2)
# and, we want to change their frequencies through a sweep or update
# we can create an updateBase object as follows
# by providing the reference qub1 and the name of the attribute 'frequency'
s1 = qg.baseClasses.updateBase(system=qub1, key='frequency')
# you can also create an update first, and set the system and key after that
s2 = qg.baseClasses.updateBase()
s2.system = qub2
s2.key = 'frequency'
# print the current qubit frequency
print(qub1.frequency, qub2.frequency)
# run the update as follows with the new value for the frequency
s1._runUpdate(3)
print(qub1.frequency, qub2.frequency)
s2._runUpdate(5)
# print the updated/current frequency
print(qub1.frequency, qub2.frequency)
1 2
3 2
3 5
Now, let’s say we want to update the same attribute for more than one objects to the same value.
Even further, let’s say we created these objects in a way that we don’t have an explicit reference to them.
In this case, we first of all don’t want to create several updateBases for each object to update same attribute to the same value. What we want is that, instead of a single object reference, we just give a list of system
objects to the updateBase.
Even further, we could just use their names or aliases, if we don’t have an explicit reference to them. After all the names and aliases of named
object are unique.
[4]:
# above situation might happen when we create composite system with + operator as follows
threeQubits = qg.Qubit(frequency=1) + qg.Qubit(frequency=1, alias='qub3') + qg.Qubit(frequency=1, alias=['qub4', "4"])
# now we don't have explicit references to the individual qubits
# still, we could get references to them using getByNameOrAlias
# but updateBase do that for us
# so let's pass a list containing the name and aliases of our qubits
# we know that the left most one is Qubit3 (because we created only 2 other qubits before this cell),
# and for the other two, we gave them aliases, so we can use any alias we want
# this shows the importance of an alias and why it needs to be unique,
# with aliases, we don't have to track the number of qubits created above,
# also, it might not be clear to everyone if the left-most or right-most is Qubit3
s3 = qg.baseClasses.updateBase(system=['Qubit3', 'qub3', "4"], key='frequency')
# also you can add more systems into this update as
s3.system = 'Qubit2' # this does not remove/replace the existing list, but adds the Qubit2 into it
# let's print the current frequencies
print( [q.frequency for q in threeQubits.subSys.values()] , qub2.frequency)
# run the update as follows with the new value for the frequency
s3._runUpdate(3)
# print the updated/current frequency
print( [q.frequency for q in threeQubits.subSys.values()] , qub2.frequency)
[1, 1, 1] 5
[3, 3, 3] 3
As noted above, the updateBase
can also work with the _auxiliaryClass
so that we may sweep/update our auxiliary information stored in the auxObj
class attribute of qBase
, which is an _auxiliaryClass
instance.
[5]:
# let's create a Qubit (which is a qBase instance), and store something in auxObj
qa = qg.Qubit()
qa.auxObj.someAuxInfo = 3
s4 = qg.baseClasses.updateBase(system=qa.auxObj, key='someAuxInfo')
# let's print the current someAuxInfo
print( qa.auxObj.someAuxInfo )
# run the update as follows with the new value for the someAuxInfo
s4._runUpdate(2)
# print the updated/current someAuxInfo
print( qa.auxObj.someAuxInfo )
3
2
Further, it also can change the value inside the aux
dictionary of qBase
as follows
[6]:
# let's use the above qubit to store some value in aux dictionary
qa.auxDict['some key'] = 10
# let's create a updateBase, but this time we won't give system, but set _aux to True
s5 = qg.baseClasses.updateBase(_aux=True, key='some key')
# let's print the current aux['some key']
print( qa.auxDict['some key'] )
# run the update as follows with the new value for the aux['some key']
s5._runUpdate(1)
# print the updated/current aux['some key']
print( qa.auxDict['some key'] )
10
1
Now, let’s discuss some of the implementation details.
system
is just another name forsubSys
. This is implemented to create terminology. Otherwise, above examples would work exactly the same if we had usedsubSys
. So, we can achieve the same things, like using object name/alias etc., that can achieve withsubSys
usingsystem
.the only difference is that the
system
getter returns a list (values in thesubSys
dictionary),subSys
getter returns a dictionaryname of the attribute that we want to update is stored in
key
, as it is obvious from above_runUpdate
is the default update behavior, and if we want to have some other custom function, we can store it in the__function
attribute, which is used inside the child classes._aux
boolean is to indicate that we are updating theaux
dictionary.
[ ]: