qua-platform / quam

The Quantum Abstract Machine (QuAM) is a comprehensive framework designed to abstract and manage quantum programming environments, providing robust tools for configuring and running quantum operations effectively. It is built over the QUA programming language, offering an organized structure for handling complex quantum components and operations.
http://qua-platform.github.io/quam/
BSD 3-Clause "New" or "Revised" License
4 stars 2 forks source link

Infinite Recursion Error when trying to deepcopy QuamDict #63

Open melmatt7 opened 1 month ago

melmatt7 commented 1 month ago

When one tries to deepcopy the generated configuration object of type QuamBase, they run into a recursion error as seen below:

File c:\Users\Lab\Documents\GitHub\qm-2qb-feedback\qua-qLDPC\Lib\site-packages\quam\core\quam_classes.py:744, in QuamDict.__getitem__(self, i)
    [743](file:///C:/Users/Lab/Documents/GitHub/qm-2qb-feedback/qua-qLDPC/Lib/site-packages/quam/core/quam_classes.py:743) def __getitem__(self, i):
--> [744](file:///C:/Users/Lab/Documents/GitHub/qm-2qb-feedback/qua-qLDPC/Lib/site-packages/quam/core/quam_classes.py:744)     elem = super().__getitem__(i)
    [745](file:///C:/Users/Lab/Documents/GitHub/qm-2qb-feedback/qua-qLDPC/Lib/site-packages/quam/core/quam_classes.py:745)     if string_reference.is_reference(elem):
    [746](file:///C:/Users/Lab/Documents/GitHub/qm-2qb-feedback/qua-qLDPC/Lib/site-packages/quam/core/quam_classes.py:746)         try:

RecursionError: maximum recursion depth exceeded

This error specifically is raised within the Bakery class provided by QM. The source of this error is due the way __getattr__ and __setattr__ are implemented, leading to infinite recursion when deepcopy attempts to copy the attributes of the object.

A very quick fix to this issue is the following modifications to the __getattr__ and __setattr__ functions within the QuamDict class:

    def __getattr__(self, key):
        if key in ["data", "_value_annotation", "_initialized"]:
            return self.__dict__[key]
        try:
            return self[key]
        except KeyError as e:
            try:
                repr = f"{self.__class__.__name__}: {self.get_reference()}"
            except Exception:
                repr = self.__class__.__name__
            raise AttributeError(f'{repr} has no attribute "{key}"') from e

    def __setattr__(self, key, value):
        if key in ["data", "parent", "config_settings", "_initialized", "_value_annotation"]:
            self.__dict__[key] = value
        else:
            self[key] = value
nulinspiratie commented 1 month ago

Thanks for raising this, I'll go ahead and investigate it. Do you have a code example that could reproduce this error?

melmatt7 commented 1 month ago

Hi, sure, something like:

local_config = copy.deepcopy(config)

for a config object as attached old_config.txt

nulinspiratie commented 1 month ago

Hi @melmatt7 I seem to be unable to reproduce the error. The config file you sent is of the qua config, not QuAM. Could you share the JSON file which results from QuAM.save()? Or alternatively the code you use to generate the QuAM structure?