QDAC2 wiring#103
Conversation
…dexing of digital marker.
|
@sebastianorbell-qm regarding the commit where I added the default baseband pulses, I didn't update the function |
|
@TheoLaudatQM Your confusion is valid, the I think default pulses require less flexibility than macros so maybe there is an argument for having them aligned more tightly with the quam construction - I have been intentional about creating separation with macro definitions |
| >>> gate.offset_parameter() # Returns 0.1V | ||
| """ | ||
|
|
||
| opx_output: Any = None |
There was a problem hiding this comment.
Makes sense - but maybe should throw an error if both opx_output and dac_spec are None
There was a problem hiding this comment.
Do you think it is necessary?
In the end you might want to be able to connect something else that a DAC or an OPX channel, but I am not sure.
Also, currently nothing will really happen if a user sets both of them to None.
| module = importlib.import_module(config["driver_module"]) | ||
| dac_class = getattr(module, config["driver_class"]) | ||
| if dac_name in dac_instances: | ||
| dac_instances[dac_name]["driver"].close() |
There was a problem hiding this comment.
I'm not sure I understand why there is a .close(), any chance you can explain
There was a problem hiding this comment.
You cannot open two QDAC2 instance with the same name, so if one instance already exists when the user is trying to connect to the QDAC2, then we will first close the existing connection and re-open a new one afterward. An alternative could be to just ignore the new connection if one instance already exists, but I thought that the user might have a good reason for re-opening it.
| self.qmm = QuantumMachinesManager(**settings) | ||
|
|
||
| ## TODO: need to also call self.create_virtual_dc_set("main_qpu") every time? | ||
| if self.dac_config and ~skip_dacs: |
There was a problem hiding this comment.
if skip_dacs is a bool, then ~skip_dacs will result in
skip_dacs=True then ~skip_dacs -> -2
and skip_dacs=False then ~skip_dacs -> -1
as ~ is a bitwise operator.
We should use not skip_dacs
There was a problem hiding this comment.
Good point, I fixed it.
@KU-QM Regarding the TODO comment here, do you know if we need to call create_virtual_dc_set("main_qpu") every time we connect to the DAC to update the voltages, of if it is only required once at the beginning to set the virtual dc set?
There was a problem hiding this comment.
You should not have to do it every time. That function will create it and store as a Quam attr, and it will know to apply voltages via the offset_parameter.
You do need to ensure that the VoltageGate's offset_parameter is wired correctly to the DAC driver, which is what the connect_to_external_source function was designed to do
There was a problem hiding this comment.
Cool thanks @KU-QM, this is also what I thought, but create_virtual_dc_set("main_qpu") is querying the actual DC voltages inside the add_layer() method, so I was surprised:
def add_layer(
self,
source_gates: List[str],
target_gates: List[str],
matrix: List[List[float]],
layer_id: str = None,
) -> VirtualizationLayer:
"""
Adds a new virtualization layer to the VirtualDCSet.
Args:
source_gates: A list of names for the virtual gates in this layer.
target_gates: A list of names for the physical (or underlying virtual)
gates that this layer maps to.
matrix: The virtualization matrix defining the transformation.
Returns:
The created VirtualizationLayer object.
"""
self._validate_new_layer(layer_id, source_gates, target_gates, matrix)
matrix_array = np.array(matrix, dtype=float)
use_pseudoinverse = matrix_array.shape[0] != matrix_array.shape[1]
virtualization_layer = VirtualizationLayer(
id=layer_id,
source_gates=source_gates,
target_gates=target_gates,
matrix=matrix,
use_pseudoinverse=use_pseudoinverse,
)
self.layers.append(virtualization_layer)
# Populate the new layer
**all_voltages = self.all_current_voltages
self._current_levels.update(all_voltages)**
Are the last two line really needed here?
There was a problem hiding this comment.
You mean that we should be able to create the virtual DC set without connecting to the DAC?
The reason that those last two lines are there, is so that whenever a new layer is created, the cached dictionary of virtual voltages (_current_levels) is immediately kept up to date. We want to avoid someone using get_voltages() after adding a layer and running into an error that says the cached dict _current_levels does not have that key.
However, I do see your point that perhaps we would like to create the DC set without actually interfacing with the DAC. Perhaps the best strategy here is to have a bool in add_layer and add_to_layer with the option to query the DAC, defaulting to True. In the BaseQuamQD function, you could set that to False, so that we can create the set without querying the DAC. Thoughts?
There was a problem hiding this comment.
Oh interesting. I think that it makes sense but if nothing weird happened, then why not using the cached values directly self._current_physical_voltages instead of querying them with self.current_physical_voltages?
There was a problem hiding this comment.
That could be a good option, to use _populate_virtual_gate_voltages(self._current_physical_voltages). I think I wanted to ensure that there was a sync, just in-case you have some other QDAC controller at some point. Perhaps the bool option could just use either self.current_physical_voltages if requery = True and self._current_physical_voltages if else. I think it would be nicer to have the option
There was a problem hiding this comment.
Yep, sounds good. I'll make the modification then. Thanks!
Pull Request
Description
This branch completes end-to-end QDAC2 wiring and configuration for quantum-dot QUAM builds: structured QDAC entries in
machine.wiring, automaticQdacSpecattachment from that wiring (no manualdac_mapping), logical (non-resolvable) QDAC reference strings to avoid QUAM#/pointer warnings, and DAC driver configuration stored inwiring.jsonunderdac_config(aligned with hownetworkis stored), with optional injection viabuild_quam_wiring(..., dac_config=...).High-level changes:
qdac_output/qdac_triggerblocks withunit_index,port, and a stablerefstring (logical path, not a QUAM JSON pointer—no#/prefix) plus legacy fallbacks where applicable.build_base_quam/build_quamapply QDAC specs using_dac_mapping_from_wiring, mirroring Stage 1 gate IDs and barrier indexing;dac_mappingwas removed from the public API.add_dacs: Takes only the machine (and optional kwargs); always derives the gate map from wiring.BaseQuamQD.dac_configis serialized withwiringandnetworkinwiring.json;set_dac_configupdates that field. Separatedac/*.jsoncwrite/read paths are removed (no legacy migration on load).QdacSpecfrom wiring,dac_configround-trip inwiring.json, etc.Type of Change
Related Issues
Related to PR-341 on qualang-tools
Checklist
Code Quality
pre-commithooks and all checks pass (pre-commit run --all-files)Testing
pytest)Documentation
Breaking Changes
Does this PR introduce breaking changes? Yes
dac_mappingremoved frombuild_base_quamandbuild_quam. QDAC output/trigger metadata must come frommachine.wiring(qdac_output/ legacyqdac_channel, etc.). Overrides previously done only viadac_mappingshould be expressed in wiring or by post-build APIs (e.g.BaseQuamQD.wire_voltage_gate_qdac/apply_qdac_channel_mapping) if still needed.add_dacs(machine, ...)no longer accepts a mapping argument; wiring is the single source of truth for which gates getQdacSpec.dac_confignow lives inwiring.json. The olddac/*.jsonclayout is no longer written or read on save/load. Existing repos that relied on that folder must migrate entries intowiring.json→dac_config(or setdac_configbefore save).refstrings underqdac_output/qdac_triggerare no longer#/qdac/...QUAM pointers; they are logical paths (e.g.qdac/analog_outputs/qdac1/1). Anything that assumed a#/prefix must be updated.Migration guide (summary):
wiring.json: adddac_configwith the same structure previously passed toset_dac_config(per-DAC keys likeqdac1,qdac2, …).dac_mappinginbuild_base_quam/build_quam; ensure wirer/builder emitsqdac_output(and triggers if used) on the relevant lines.dac_configintobuild_quam_wiring(...)when creating wiring so the first save already contains network + wiring + DAC config together.dac/*.jsonc; merge those files intodac_configinwiring.jsonif you still have old state directories.Testing Details
StrEnumand other 3.11 APIs); confirm in CI.pytestforquam-builder(e.g.tests/builder/quantum_dots/test_wirer_builder_integration.pyand related builder tests).build_quam_wiring(with and withoutdac_config), thenbuild_base_quam/build_quam, save, reload, and confirm:Could not get reference "#/qdac/..."warnings from wiring traversal.wiring.jsoncontainswiring,network, anddac_configas expected.connect_to_external_sourceworks whendac_configis populated andQdacSpec.dac_namekeys matchdac_configentries.Screenshots/Examples
Example
dac_config(inwiring.json):{ "wiring": { }, "network": { "host": "...", "port": null, "cluster_name": "..." }, "dac_config": { "qdac1": { "driver_module": "qcodes_contrib_drivers.drivers.QDevil.QDAC2", "driver_class": "QDac2", "connection": { "visalib": "@py", "address": "TCPIP::<ip>::5025::SOCKET" }, "channel_method": "channel", "accessor": "dc_constant_V", "is_qdac": true } } }