|
1 | | -from typing import NamedTuple, Set, Callable, Dict, Tuple, Union, Iterable, Any, Mapping |
| 1 | +from typing import NamedTuple, Set, Callable, Dict, Tuple, Union, Iterable, Any, Mapping, Self |
2 | 2 | from collections import defaultdict |
| 3 | +from dataclasses import dataclass |
3 | 4 | import warnings |
4 | 5 | import numbers |
5 | 6 |
|
6 | 7 | from qupulse.hardware.awgs.base import AWG |
7 | 8 | from qupulse.hardware.dacs import DAC |
8 | 9 | from qupulse.program.loop import Loop |
| 10 | +from qupulse.program.multi import MultiProgram |
9 | 11 |
|
10 | 12 | from qupulse.utils.types import ChannelID |
11 | 13 |
|
@@ -313,8 +315,171 @@ def registered_programs(self) -> Dict[str, RegisteredProgram]: |
313 | 315 | return self._registered_programs |
314 | 316 |
|
315 | 317 |
|
| 318 | +@dataclass |
| 319 | +class RegisteredMultiProgram: |
| 320 | + program: MultiProgram |
| 321 | + measurement_windows: Dict[str, Tuple[np.ndarray, np.ndarray]] |
| 322 | + run_callback: Callable |
| 323 | + hw_setups_to_utilize: Set[HardwareSetup] |
| 324 | + dacs_to_arm: Set[DAC] |
316 | 325 |
|
317 | 326 |
|
| 327 | +class MetaHardwareSetup: |
| 328 | + |
| 329 | + def __init__(self): |
| 330 | + # self._channel_map: Dict[str, Set[ChannelID]] = dict() |
| 331 | + self._setup_map: Dict[str, HardwareSetup] = dict() |
| 332 | + |
| 333 | + |
| 334 | + self._registered_programs: Dict[str, RegisteredMultiProgram] = dict() |
| 335 | + |
| 336 | + self._alibi_measurement_setup = HardwareSetup() |
| 337 | + |
| 338 | + def register_program(self, |
| 339 | + name: str, |
| 340 | + program: MultiProgram, |
| 341 | + run_callback: Callable = lambda: None, |
| 342 | + update: bool = False, |
| 343 | + flatten_structure: bool = False, |
| 344 | + measurements: Mapping[str, Tuple[np.ndarray, np.ndarray]] = None |
| 345 | + ) -> None: |
| 346 | + |
| 347 | + hw_setups_to_utilize = set() |
| 348 | + |
| 349 | + if flatten_structure: |
| 350 | + program_map = program._flattened_program_map |
| 351 | + else: |
| 352 | + program_map = program.program_map |
| 353 | + |
| 354 | + ############ |
| 355 | + #GET MEASUREMENT WINDOWS, collect from all subprograms (can have different ones...) |
| 356 | + # measurements: Mapping[str, Tuple[np.ndarray, np.ndarray]] |
| 357 | + if measurements is None: |
| 358 | + measurements = program.get_measurement_windows(drop=True) |
| 359 | + |
| 360 | + self._alibi_measurement_setup.register_program(name, None, |
| 361 | + run_callback=lambda: None, |
| 362 | + channels=set(),measurements=measurements |
| 363 | + ) |
| 364 | + |
| 365 | + #END MEAS. |
| 366 | + ############ |
| 367 | + |
| 368 | + |
| 369 | + |
| 370 | + |
| 371 | + for s_ident,prog in program_map.items(): |
| 372 | + self.setup_map[s_ident].register_program(s_ident+'_'+name, prog, |
| 373 | + update=update,run_callback=lambda:None, |
| 374 | + measurements={}) |
| 375 | + hw_setups_to_utilize.add(self.setup_map[s_ident]) |
| 376 | + |
| 377 | + self._registered_programs[name] = RegisteredMultiProgram(program, |
| 378 | + self._alibi_measurement_setup.registered_programs[name].measurement_windows, #meas todo TODOTODO exract from alibi setup |
| 379 | + run_callback, |
| 380 | + hw_setups_to_utilize, |
| 381 | + dacs_to_arm=self._alibi_measurement_setup.registered_programs[name].dacs_to_arm, |
| 382 | + # dacs_to_arm=set(affected_dacs.keys()) |
| 383 | + ) |
| 384 | + |
| 385 | + return |
| 386 | + |
| 387 | + @property |
| 388 | + def _measurement_map(self) -> Dict[str, Set[MeasurementMask]]: |
| 389 | + return self._alibi_measurement_setup._measurement_map |
| 390 | + |
| 391 | + |
| 392 | + def set_measurement(self, measurement_name: str, |
| 393 | + measurement_mask: Union[MeasurementMask, Iterable[MeasurementMask]], |
| 394 | + allow_multiple_registration: bool=False): |
| 395 | + return self._alibi_measurement_setup.set_measurement(measurement_name, measurement_mask, allow_multiple_registration) |
| 396 | + |
| 397 | + |
| 398 | + def remove_program(self, name: str): |
| 399 | + if name in self.registered_programs: |
| 400 | + for setup in self.setup_map.values(): |
| 401 | + if name in setup.registered_programs: |
| 402 | + setup.remove_program(name) |
| 403 | + self._alibi_measurement_setup.remove_program(name) |
| 404 | + |
| 405 | + |
| 406 | + def clear_programs(self): |
| 407 | + for setup in self._setup_map.values(): |
| 408 | + setup.clear_programs() |
| 409 | + |
| 410 | + self._alibi_measurement_setup.clear_programs() |
| 411 | + self._registered_programs = {} |
| 412 | + |
| 413 | + @property |
| 414 | + def known_hw_setups(self) -> Set[Union[HardwareSetup,"MetaHardwareSetup"]]: |
| 415 | + return set(self._setup_map.values()) |
| 416 | + |
| 417 | + @property |
| 418 | + def setup_map(self) -> Dict[str,Union[HardwareSetup,"MetaHardwareSetup"]]: |
| 419 | + return self._setup_map |
| 420 | + |
| 421 | + @property |
| 422 | + def known_awgs(self) -> Set[AWG]: |
| 423 | + return set().union(*[s.known_awgs for s in self._setup_map.values()]) |
| 424 | + |
| 425 | + @property |
| 426 | + def known_dacs(self) -> Set[DAC]: |
| 427 | + return self._alibi_measurement_setup.known_dacs |
| 428 | + |
| 429 | + def arm_program(self, name: str) -> None: |
| 430 | + """Assert program is in memory. Hardware will wait for trigger event""" |
| 431 | + if name not in self._registered_programs: |
| 432 | + raise KeyError('{} is not a registered program'.format(name)) |
| 433 | + |
| 434 | + hw_setups_to_utilize = self._registered_programs[name].hw_setups_to_utilize |
| 435 | + for key,setup in self._setup_map.items(): |
| 436 | + if setup in hw_setups_to_utilize: |
| 437 | + setup.arm_program(key+'_'+name) |
| 438 | + else: |
| 439 | + # The other AWGs should ignore the trigger |
| 440 | + # awg.arm(None) |
| 441 | + pass |
| 442 | + self._alibi_measurement_setup.arm_program(name) |
| 443 | + # for dac in dacs_to_arm: |
| 444 | + # dac.arm_program(name) |
| 445 | + @property |
| 446 | + def _channel_map(self) -> Dict[str, Set[ChannelID]]: |
| 447 | + return {k: s.registered_channels.keys() for k,s in self._setup_map.items()} |
| 448 | + |
| 449 | + def run_program(self, name) -> None: |
| 450 | + """Calls arm program and starts it using the run callback""" |
| 451 | + self.arm_program(name) |
| 452 | + self._registered_programs[name].run_callback() |
| 453 | + |
| 454 | + def add_setup(self, identifier: str, |
| 455 | + setup: Union[HardwareSetup,"MetaHardwareSetup"], |
| 456 | + allow_multiple_registration: bool=False) -> None: |
| 457 | + |
| 458 | + assert identifier not in self._setup_map |
| 459 | + assert setup not in self._setup_map.values() |
| 460 | + |
| 461 | + assert not any(s in self.known_awgs for s in setup.known_awgs) |
| 462 | + |
| 463 | + self._setup_map[identifier] = setup |
| 464 | + |
| 465 | + def rm_setup(self, identifier: str) -> None: |
| 466 | + self._setup_map.pop(identifier) |
| 467 | + |
| 468 | + @property |
| 469 | + def all_registered_channels(self) -> Dict[ChannelID, Set[_SingleChannel]]: |
| 470 | + return {k:v for setup in self._setup_map.values() for k,v in setup.registered_channels().items()} |
| 471 | + |
| 472 | + # def update_parameters(self, name: str, parameters: Mapping[str, numbers.Real]): |
| 473 | + # *_, awgs, dacs = self._registered_programs[name] |
| 474 | + |
| 475 | + # for awg in self.known_awgs: |
| 476 | + # if awg in awgs: |
| 477 | + # awg.set_volatile_parameters(name, parameters) |
| 478 | + |
| 479 | + @property |
| 480 | + def registered_programs(self) -> Dict[str, RegisteredMultiProgram]: |
| 481 | + return self._registered_programs |
| 482 | + |
318 | 483 |
|
319 | 484 |
|
320 | 485 |
|
|
0 commit comments