Currently the DeviceSpecification must be initialized with a list of Channels.
We should also provide the ability to initialize but a number of elements.
ie.,
schedule = Schedule(n_qubits=5, memory_slots=5, n_control_channels, n_registers=5)
I guess my one issue here is that this is called DeviceSpecification, but it does not specify my device. In reality it sets the collection of channels and classical memory allocation. The actual device specifications go into assemble.
I agree, we brainstormed a name for this collection but this is the best we came up with. Do you have a suggestion? We could still deprecate the above name realtively transparently.
Actually it does not do the memory allocation part, so why not just ChannelCollection or the like?
Initially DeviceSpecification had frequency information as well, and this is why we started call this a specification. I agree the name is bit misleading for now, but I think it is still worth to keep the concept of this class, i.e. to handle a qubit with multiple U-channels, mapping of ideal qubit to physical channel should be considered and DeviceSpecification is best place to have such data structure.
example:
spec.qubit[0].control[0] # control0 of qubit0 - 'u0'
spec.qubit[1].control[0] # control0 of qubit1 - 'u1'
spec.qubit[1].control[1] # control1 of qubit1 - 'u2'
For our device, qubit-channel mapping is really simple and the concept of ChannelCollection works.
DeviceSpecification is different from a collection of channels in the sense that it owns a Qubits (and Buses in the future?) and topology among qubits and channels as shown in @nkanazawa1989 's example. I think such a topology information should be managed within a class DeviceSpecification (or DeviceTopology?). If we have many qubits (and buses) in the future, it will be impossible to handle "which u-channels are connected to a qubit (or a bus)" manually. Also index-based mapping between qubits and channels in qobj will be improved in the future. We need to prepare for it. That's why I think the following interface is not so good in the long run.
spec = DeviceSpecification(n_qubits=5, memory_slots=5, n_registers=5)
How about introducing a new function creating DeviceSpecification which have exactly one d/u/m-channel for each qubit (and have no buses)? For example,
spec = SimpleDeviceSpecification(n_qubits=5, memory_slots=5, n_registers=5)
So let me explain the reason for the discussion. For the pulse simulator, I need to be able to take an user defined Hamiltonian, construct the schedule for it, and build it into a qobj that contains the Hamiltonian as well. The name DeviceSpecification to me indicates that this actually specifies my device, and this is where the Hamiltonian should be encoded. Do we include the Hamiltonian into this specification, i.e. does this actually become a device specification instead of just a collection of channels and what qubits they operate on?
I do not understand the the example of a qubit with multiple control channels. The spec document says one U channel per qubit. Why are you worried about situations that are outside of our specified domain of validity? From the point of view of the spec document, there is one drive, control, and measure channels per qubit.
I'm comfortable with having all necessary information to create schedule in DeviceSpecification . In this way users don't need to know the data structure of backend - remembering where the information is stored is cumbersome, i.e. I prefer spec.cmd_def() rather than backend.defaults().build_cmd_def() to have command definition. I think DeviceSpecification is a wrapper of backend and we can use the same script even if the data structure of backend is changed. If DeviceSpecification is just a collection of channels, I have no opposition to change its name to ChannelCollection.
Why are you worried about situations that are outside of our specified domain of validity?
This is due to this discussion. According to our spec
The goal of the OpenPulse specification is to allow a user to have pulse-level control(i.e. control of the continuous time dynamics) of a general quantum device.
we should take care of such configuration at some point.
Which device are you thinking of when you have multiple controls?
So my issue is that at some point the DeviceSpecification becomes no different than a backend in terms of the information stored. If I can get everything from a backend instance, then I see no point for an additional object.
We can of course do ChannelCollection(n_qubits, n_control_channels=None) and default to one control per qubit if not explicitly defined. You can of course doChannelCollection().from_device()`
The only thing I have issue with is that the memory slots and registers are also in this object. Perhaps the correct object would be called PulseSpecification here.
Actually, going back to the openpulse spec, I see that it does not specify the number of control channels must be equal to the number of qubits, so that would be an additional input needed. But it also does not attach the control channels to qubits. Indeed, in many cases this does not make sense. See for example Sec. 6.3 in the spec doc. So the way the device specification works now spec.qubits[0].control[1] does not capture everything that we need it to.
Actually, going back to the openpulse spec, I see that it does not specify the number of control channels must be equal to the number of qubits, so that would be an additional input needed. But it also does not attach the control channels to qubits. Indeed, in many cases this does not make sense. See for example Sec. 6.3 in the spec doc. So the way the device specification works now
spec.qubits[0].control[1]does not capture everything that we need it to.
Yes I agree, this was a last minute change before release as the behaviour I found was that qubit qi was mapped to a single control channel ui which was explicitly wrong.
Initially
DeviceSpecificationhad frequency information as well, and this is why we started call this a specification. I agree the name is bit misleading for now, but I think it is still worth to keep the concept of this class, i.e. to handle a qubit with multiple U-channels, mapping of ideal qubit to physical channel should be considered andDeviceSpecificationis best place to have such data structure.example:
spec.qubit[0].control[0] # control0 of qubit0 - 'u0' spec.qubit[1].control[0] # control0 of qubit1 - 'u1' spec.qubit[1].control[1] # control1 of qubit1 - 'u2'For our device, qubit-channel mapping is really simple and the concept of
ChannelCollectionworks.
For reference current syntax is
spec.q[0].control[0]
Which device are you thinking of when you have multiple controls?
I know you corrected this later on, but just for reference multiple control channels associated with a qubit is standard. For example for every coupling map entry one, could argue there is an associated control channel.
I think I agree with @itoko there may be a need for a DeviceTopology class (and this is in a sense is what the DeviceSpecification should be), with various sub-classes to handle certain types of specific topologies such as for our devices. However, with the current spec information, it is very difficult to extract this information. Perhaps, the only place it is available is in the Hamiltonian which is a tricky data structure to parse. Perhaps there should be an additional data structure returned, or the Hamiltonian h_str should be supplanted with a JSON object.
I also agree with @nonhermitian that a better way to construct these topologies must be made.
But what if my controls are unrelated to the qubits themselves. The sec 6.3 in the spec is one example of this; the controls come from a tunable inductor.
Yes, I agree, the way u-channels are is wrong (all are associated with each qubit) this was a minor change I could make without restructuring the whole class the day of release. This is why I argue for additional metadata above.
So what about doing
spec = PulseSpecification(n_qubits, n_channels=0, memory_slots=None,...)
or
spec = PulseSpecification().from_device()
with
spec.qubits[i].drive += ...
spec.controls[j] += ...
I am not sure what other information you need at this stage. This same configuration could be applied to multiple devices so long as their channel and memory spec were the same.
I think the initializer you propose is good. I would prefer from_device to be a constructor.
I agree that we need some form of device Topology. Most of this information should come from the Hamiltonian but there is also a RF hardware component to consider. For instance, consider the single-qubit Hamiltonian with a drive on the qubit: \omega a^\dagger a + \Omega(t)(a^\dagger + a). Physically, the drive \Omega(t) may be implemented by several physical channels if the user is applying drives at very different RF frequencies (i.e. the setup would have multiple LOs with AWGs combined at room temperature or in the fridge. Examples: reset, multi-qubit geometric opertions, etc...). This would be an example where knowing the Hamiltonian is not enough. In a simulation of the Hamiltonian dynamics you probably would not care about this distinction.
Here is a different example of multiple drives per qubit: if you have frequency tunable qubits, where flux pulses are used to change the frequency of the qubits, you would have two physically different channels: one for XY control and one for Z control. However, in this example we can specify everything from the Hamiltonian.
It also does not make sense to always bind the controls to the qubits, think of tunable couplers. In this case it is pretty clear from the Hamiltonian that the control affects the TC.
In the first example could we not represent these multiple physical channels as separate drive channels? Personally, I find the limitation of a single drive/measure channel per-qubit limiting especially in the context of unlimited u-channels existing anyways. I have a feeling this will be ripe for abuse. For example the case above, where users will add additional drive channels and just call them u-channels. What seems to make more sense to me is making the number of channels assigned to each qubit defined in some sort of Hamiltonian type structure which you are thinking about above.
I guess nothing prevents us from having the same term in the Hamiltonian several times. We would of course need to tell the user the constraints associated to each term. For the example above we would thus have \omega a^\dagger a + \Omega_1(t)(a^\dagger + a) + \Omega_2(t)(a^\dagger + a) where \Omega_1 and \Omega_2 would have different LOs. This would work.
Yes, I agree. However, this doesn't currently within the specification. We should raise an issue on the internal specification with this.
@taalexander I guess adding a driving channel as u-channel is difficult because currently u-channels have no controllable LOs. I agree with the limitation of a single drive/measure channel per-qubit, but I believe the specification doesn't limit the number of channels per qubit. This is just due to the design of DeviceSpecification.
In FPGA, the mapping is provided by UCF, such as
NET "d(0)" LOC="q0" # qubit0
NET "d(1)" LOC="q0" # qubit0
NET "d(2)" LOC="tc0" # tunable coupler0
We can add physical constraints such as lo_range.
Can we embed the same config into pulse, in addition to the Hamiltonian?
I'm sorry, I am having trouble understanding the comments above. Currently, the specification does limit the number of drive channels to one per qubit. For the case @eggerdj was discussing with multiple physical drive channels for a single qubit this could be handled with software signal processing, however, there are cases where we may not wish to do that. It is also possible to conceive of cases where we might have both say current and voltage controls to an individual qubit and in my opinion, these would correspond to two different drive channels rather than a drive channel and a control channel.
The above is about how to define connection between qubit and channel. I think extracting this information from Hamiltonian is still difficult because there is ambiguity due to frame. UCF is just an example, but here doesn't seem to be right place to discuss that issue. Sorry.
Is there any case we need to control current and voltage separately? I agree with allowing for multiple drive channels per qubit anyways.
If there are N qubits then there can only be N independent phases (ie these phases can be arbitrary at the start of any exp), which is why there are N drives. Control channels are therefore any channel that doesn't have an independent phase. Z control of a qubit has no phase therefore is a control channel. You can have two channels for XY control of a qubit, but 1 of those channels has to set the phase.