Skip to content

quast_decisiontree.core.backend

quast_decisiontree.core.backend

contains the class representing quantum backends

Backend

Bases: UserDict

class representing a quantum computing backend.

Can be used like a dictionary from the outside, but also be extended with further attributes

Source code in src/quast_decisiontree/core/backend.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
class Backend(UserDict):
    """class representing a quantum computing backend.

    Can be used like a dictionary from the outside, but also
    be extended with further attributes
    """

    _required_keys = {"name": str, "provider_node": str}
    DEFAULT_SHOTS = 1024

    def __init__(self, submit_func: Callable = None, default_shots: int = None, **kwargs):
        super().__init__(**kwargs)
        for key, typ in self._required_keys.items():
            if key not in self:
                raise ValueError(
                    f"Backend must have entry of type {typ} for key {key}, but {key} is missing."
                )
            elif not isinstance(self[key], typ):
                raise ValueError(
                    f"Backend must have entry of type {typ} for key {key}, but received"
                    f" {self[key]} of type {type(self[key])}."
                )
        self._submit = submit_func
        self._default_shots = default_shots if default_shots is not None else self.DEFAULT_SHOTS

    def run(self, circuit: Any, shots: int = None) -> dict[str, int]:
        """Submit a single circuit."""
        if self._submit is None:
            raise RuntimeError(f"Backend '{self.name!r}' has no submit function attached.")
        if shots is None:
            shots = self._default_shots
        return self._submit(circuit, shots)

    def run_batch(self, circuits: list[Any], shots: int = None) -> list[dict[str, int]]:
        """Submit multiple circuits."""
        return [self.run(c, shots) for c in circuits]

    def __call__(self, circuit: Any, shots: int = None) -> dict[str, int]:
        return self.run(circuit, shots)

    @property
    def has_submit(self) -> bool:
        """Whether this backend has a submit function attached."""
        return self._submit is not None

    @property
    def default_shots(self):
        return self._default_shots

    @default_shots.setter
    def default_shots(self, value: int):
        if not isinstance(value, int) or value < 1:
            raise ValueError(f"default_shots must be a positive integer, got {value}")
        self._default_shots = value

    @property
    def name(self):
        return self.data.get("name", None)

    @name.setter
    def name(self, name):
        self.data["name"] = name

    @property
    def description(self):
        try:
            return self.data["description"]
        except KeyError:
            return self.data["name"]

    @property
    def provider_node(self):
        return self.data.get("provider_node", None)

    @provider_node.setter
    def provider_node(self, node: str):
        self.data["provider_node"] = node

DEFAULT_SHOTS class-attribute instance-attribute

DEFAULT_SHOTS = 1024

has_submit property

has_submit

Whether this backend has a submit function attached.

default_shots property writable

default_shots

name property writable

name

description property

description

provider_node property writable

provider_node

__init__

__init__(submit_func=None, default_shots=None, **kwargs)
Source code in src/quast_decisiontree/core/backend.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def __init__(self, submit_func: Callable = None, default_shots: int = None, **kwargs):
    super().__init__(**kwargs)
    for key, typ in self._required_keys.items():
        if key not in self:
            raise ValueError(
                f"Backend must have entry of type {typ} for key {key}, but {key} is missing."
            )
        elif not isinstance(self[key], typ):
            raise ValueError(
                f"Backend must have entry of type {typ} for key {key}, but received"
                f" {self[key]} of type {type(self[key])}."
            )
    self._submit = submit_func
    self._default_shots = default_shots if default_shots is not None else self.DEFAULT_SHOTS

run

run(circuit, shots=None)

Submit a single circuit.

Source code in src/quast_decisiontree/core/backend.py
41
42
43
44
45
46
47
def run(self, circuit: Any, shots: int = None) -> dict[str, int]:
    """Submit a single circuit."""
    if self._submit is None:
        raise RuntimeError(f"Backend '{self.name!r}' has no submit function attached.")
    if shots is None:
        shots = self._default_shots
    return self._submit(circuit, shots)

run_batch

run_batch(circuits, shots=None)

Submit multiple circuits.

Source code in src/quast_decisiontree/core/backend.py
49
50
51
def run_batch(self, circuits: list[Any], shots: int = None) -> list[dict[str, int]]:
    """Submit multiple circuits."""
    return [self.run(c, shots) for c in circuits]

__call__

__call__(circuit, shots=None)
Source code in src/quast_decisiontree/core/backend.py
53
54
def __call__(self, circuit: Any, shots: int = None) -> dict[str, int]:
    return self.run(circuit, shots)

BackendProvider

Bases: UserList

provides lookup for backends

Source code in src/quast_decisiontree/core/backend.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
class BackendProvider(UserList):
    """provides lookup for backends"""

    def __getitem__(self, ind_or_name):
        if isinstance(ind_or_name, str):
            return self.get_backend(ind_or_name)
        else:
            return self.data[ind_or_name]

    @property
    def default(self):
        try:
            if self._default is not None:
                return self[self._default]
        except AttributeError:
            return self[0]

    @default.setter
    def default(self, val: str | int):
        try:
            _ = self[val]
        except (ValueError, IndexError, TypeError) as exc:
            raise ValueError(
                f"Couldn't resolve specification of default backend: {val} of type {type(val)}.\n"
                "Backends can be retrieved by their name or index.s"
            ) from exc
        else:
            self._default = val

    @property
    def names(self):
        yield from (backend.name for backend in self)

    @property
    def has_unique_names(self):
        return len(set(self.names)) == len(list(self.names))

    @property
    def provider_nodes(self):
        try:
            return self._provider_nodes
        except AttributeError:
            self._provider_nodes = []
            for backend in self:
                if backend.provider_node not in self._provider_nodes:
                    self._provider_nodes.append(backend.provider_node)
            return self._provider_nodes

    def get_backend(self, name: str, filter: dict | None = None, return_first: bool = False):
        """searches for a backend of that name, throwing an error if no results are found, or
        multiple results are found. Optionally filters the backends first. If return_first is
        True, returns the first backend found with that name.
        """
        # retrieving all matching backends
        if filter is not None:
            candidates = self.filter(filter)
            out = [backend for backend in candidates if backend.name == name]
        else:
            out = [backend for backend in self if backend.name == name]

        # raising errors if appropriate
        if len(out) == 0:
            raise ValueError(
                f"No backend with name {name} found with filter {filter}, "
                "consider relaxing filters."
            )
        elif return_first or len(out) == 1:
            return out[0]
        else:
            raise ValueError(
                f"{len(out)} backends found for name {name}. Consider restricting the selection"
                " by filtering (more) or setting return_first to True."
            )

    def filter(self, filter: dict | None):
        """filters the backends. Filter clauses are given either as
        key:value or key:[value1, value2...]

        The former selects all backends with the correct key-value pairs, the latter is interpreted
        as an or-clause, returning all backends containing one of (key, value1), (key, value2) etc.
        When the filter dictionary contains multiple entries, they are combined in an and-fashion.
        """
        if filter is None:
            return self
        normalized = {}
        for key, values in filter.items():
            if isinstance(values, str) or not isinstance(values, Iterable):
                normalized[key] = [values]
            else:
                normalized[key] = list(values)
        out = BackendProvider()
        for backend in self:
            if all(key in backend and backend[key] in vals for key, vals in normalized.items()):
                out.append(backend)
        return out

default property writable

default

names property

names

has_unique_names property

has_unique_names

provider_nodes property

provider_nodes

__getitem__

__getitem__(ind_or_name)
Source code in src/quast_decisiontree/core/backend.py
 98
 99
100
101
102
def __getitem__(self, ind_or_name):
    if isinstance(ind_or_name, str):
        return self.get_backend(ind_or_name)
    else:
        return self.data[ind_or_name]

get_backend

get_backend(name, filter=None, return_first=False)

searches for a backend of that name, throwing an error if no results are found, or multiple results are found. Optionally filters the backends first. If return_first is True, returns the first backend found with that name.

Source code in src/quast_decisiontree/core/backend.py
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def get_backend(self, name: str, filter: dict | None = None, return_first: bool = False):
    """searches for a backend of that name, throwing an error if no results are found, or
    multiple results are found. Optionally filters the backends first. If return_first is
    True, returns the first backend found with that name.
    """
    # retrieving all matching backends
    if filter is not None:
        candidates = self.filter(filter)
        out = [backend for backend in candidates if backend.name == name]
    else:
        out = [backend for backend in self if backend.name == name]

    # raising errors if appropriate
    if len(out) == 0:
        raise ValueError(
            f"No backend with name {name} found with filter {filter}, "
            "consider relaxing filters."
        )
    elif return_first or len(out) == 1:
        return out[0]
    else:
        raise ValueError(
            f"{len(out)} backends found for name {name}. Consider restricting the selection"
            " by filtering (more) or setting return_first to True."
        )

filter

filter(filter)

filters the backends. Filter clauses are given either as key:value or key:[value1, value2...]

The former selects all backends with the correct key-value pairs, the latter is interpreted as an or-clause, returning all backends containing one of (key, value1), (key, value2) etc. When the filter dictionary contains multiple entries, they are combined in an and-fashion.

Source code in src/quast_decisiontree/core/backend.py
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
def filter(self, filter: dict | None):
    """filters the backends. Filter clauses are given either as
    key:value or key:[value1, value2...]

    The former selects all backends with the correct key-value pairs, the latter is interpreted
    as an or-clause, returning all backends containing one of (key, value1), (key, value2) etc.
    When the filter dictionary contains multiple entries, they are combined in an and-fashion.
    """
    if filter is None:
        return self
    normalized = {}
    for key, values in filter.items():
        if isinstance(values, str) or not isinstance(values, Iterable):
            normalized[key] = [values]
        else:
            normalized[key] = list(values)
    out = BackendProvider()
    for backend in self:
        if all(key in backend and backend[key] in vals for key, vals in normalized.items()):
            out.append(backend)
    return out