qilisdk 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qilisdk/__init__.py +47 -0
- qilisdk/__init__.pyi +30 -0
- qilisdk/_optionals.py +105 -0
- qilisdk/analog/__init__.py +17 -0
- qilisdk/analog/algorithms.py +111 -0
- qilisdk/analog/analog_backend.py +43 -0
- qilisdk/analog/analog_result.py +114 -0
- qilisdk/analog/exceptions.py +19 -0
- qilisdk/analog/hamiltonian.py +706 -0
- qilisdk/analog/quantum_objects.py +486 -0
- qilisdk/analog/schedule.py +311 -0
- qilisdk/common/__init__.py +20 -0
- qilisdk/common/algorithm.py +17 -0
- qilisdk/common/backend.py +16 -0
- qilisdk/common/model.py +16 -0
- qilisdk/common/optimizer.py +136 -0
- qilisdk/common/optimizer_result.py +110 -0
- qilisdk/common/result.py +17 -0
- qilisdk/digital/__init__.py +66 -0
- qilisdk/digital/ansatz.py +143 -0
- qilisdk/digital/circuit.py +106 -0
- qilisdk/digital/digital_algorithm.py +20 -0
- qilisdk/digital/digital_backend.py +90 -0
- qilisdk/digital/digital_result.py +145 -0
- qilisdk/digital/exceptions.py +31 -0
- qilisdk/digital/gates.py +989 -0
- qilisdk/digital/vqe.py +165 -0
- qilisdk/extras/__init__.py +13 -0
- qilisdk/extras/cuda/__init__.py +18 -0
- qilisdk/extras/cuda/cuda_analog_result.py +19 -0
- qilisdk/extras/cuda/cuda_backend.py +398 -0
- qilisdk/extras/cuda/cuda_digital_result.py +19 -0
- qilisdk/extras/qaas/__init__.py +13 -0
- qilisdk/extras/qaas/keyring.py +54 -0
- qilisdk/extras/qaas/models.py +57 -0
- qilisdk/extras/qaas/qaas_backend.py +154 -0
- qilisdk/extras/qaas/qaas_digital_result.py +20 -0
- qilisdk/extras/qaas/qaas_settings.py +23 -0
- qilisdk/py.typed +0 -0
- qilisdk/utils/__init__.py +27 -0
- qilisdk/utils/openqasm2.py +215 -0
- qilisdk/utils/serialization.py +128 -0
- qilisdk/yaml.py +71 -0
- qilisdk-0.1.0.dist-info/METADATA +237 -0
- qilisdk-0.1.0.dist-info/RECORD +47 -0
- qilisdk-0.1.0.dist-info/WHEEL +4 -0
- qilisdk-0.1.0.dist-info/licenses/LICENCE +201 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Copyright 2025 Qilimanjaro Quantum Tech
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from .circuit import Circuit
|
|
16
|
+
from .digital_backend import DigitalBackend, DigitalSimulationMethod
|
|
17
|
+
from .digital_result import DigitalResult
|
|
18
|
+
from .gates import (
|
|
19
|
+
CNOT,
|
|
20
|
+
CZ,
|
|
21
|
+
RX,
|
|
22
|
+
RY,
|
|
23
|
+
RZ,
|
|
24
|
+
U1,
|
|
25
|
+
U2,
|
|
26
|
+
U3,
|
|
27
|
+
Adjoint,
|
|
28
|
+
BasicGate,
|
|
29
|
+
Controlled,
|
|
30
|
+
Exponential,
|
|
31
|
+
Gate,
|
|
32
|
+
H,
|
|
33
|
+
M,
|
|
34
|
+
S,
|
|
35
|
+
T,
|
|
36
|
+
X,
|
|
37
|
+
Y,
|
|
38
|
+
Z,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
"CNOT",
|
|
43
|
+
"CZ",
|
|
44
|
+
"RX",
|
|
45
|
+
"RY",
|
|
46
|
+
"RZ",
|
|
47
|
+
"U1",
|
|
48
|
+
"U2",
|
|
49
|
+
"U3",
|
|
50
|
+
"Adjoint",
|
|
51
|
+
"BasicGate",
|
|
52
|
+
"Circuit",
|
|
53
|
+
"Controlled",
|
|
54
|
+
"DigitalBackend",
|
|
55
|
+
"DigitalResult",
|
|
56
|
+
"DigitalSimulationMethod",
|
|
57
|
+
"Exponential",
|
|
58
|
+
"Gate",
|
|
59
|
+
"H",
|
|
60
|
+
"M",
|
|
61
|
+
"S",
|
|
62
|
+
"T",
|
|
63
|
+
"X",
|
|
64
|
+
"Y",
|
|
65
|
+
"Z",
|
|
66
|
+
]
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Copyright 2025 Qilimanjaro Quantum Tech
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
from abc import ABC, abstractmethod
|
|
15
|
+
from typing import ClassVar, Literal, Union
|
|
16
|
+
|
|
17
|
+
from qilisdk.digital.circuit import Circuit
|
|
18
|
+
from qilisdk.digital.gates import CNOT, CZ, U1, U2, U3, M
|
|
19
|
+
from qilisdk.yaml import yaml
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Ansatz(ABC):
|
|
23
|
+
_ONE_QUBIT_GATES: ClassVar[dict[str, type[Union[U1, U2, U3]]]] = {
|
|
24
|
+
"U1": U1,
|
|
25
|
+
"U2": U2,
|
|
26
|
+
"U3": U3,
|
|
27
|
+
}
|
|
28
|
+
_TWO_QUBITS_GATES: ClassVar[dict[str, type[Union[CNOT, CZ]]]] = {
|
|
29
|
+
"CZ": CZ,
|
|
30
|
+
"CNOT": CNOT,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
def __init__(self, nqubits: int, layers: int = 1) -> None:
|
|
34
|
+
self.nqubits = nqubits
|
|
35
|
+
self.layers = layers
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def nparameters(self) -> int:
|
|
39
|
+
"""
|
|
40
|
+
Retrieve the total number of parameters required by all parameterized gates in the ansatz's circuit.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
int: The total count of parameters from all parameterized gates.
|
|
44
|
+
"""
|
|
45
|
+
raise NotImplementedError
|
|
46
|
+
|
|
47
|
+
def get_circuit(self, parameters: list[float]) -> Circuit:
|
|
48
|
+
"""Get the underlying circuit with the given list of parameters.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
params (list[float]): the list of parameters for the unitary gates.
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
ValueError: if the number of parameters provided are less than the parameters expected by the ansatz.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Circuit: The underlying circuit with the updated parameters.
|
|
58
|
+
"""
|
|
59
|
+
if len(parameters) != self.nparameters:
|
|
60
|
+
raise ValueError(f"Expecting {self.nparameters} but received {len(parameters)}")
|
|
61
|
+
|
|
62
|
+
return self._construct_circuit(parameters=list(parameters))
|
|
63
|
+
|
|
64
|
+
@abstractmethod
|
|
65
|
+
def _construct_circuit(self, parameters: list[float]) -> Circuit: ...
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@yaml.register_class
|
|
69
|
+
class HardwareEfficientAnsatz(Ansatz):
|
|
70
|
+
def __init__(
|
|
71
|
+
self,
|
|
72
|
+
n_qubits: int,
|
|
73
|
+
layers: int = 1,
|
|
74
|
+
connectivity: Literal["Circular", "Linear", "Full"] | list[tuple[int, int]] = "Linear",
|
|
75
|
+
structure: Literal["grouped", "interposed"] = "grouped",
|
|
76
|
+
one_qubit_gate: Literal["U1", "U2", "U3"] = "U1",
|
|
77
|
+
two_qubit_gate: Literal["CZ", "CNOT"] = "CZ",
|
|
78
|
+
) -> None:
|
|
79
|
+
super().__init__(n_qubits, layers)
|
|
80
|
+
|
|
81
|
+
# Define chip topology
|
|
82
|
+
if isinstance(connectivity, list):
|
|
83
|
+
self.connectivity = connectivity
|
|
84
|
+
elif connectivity == "Full":
|
|
85
|
+
self.connectivity = [(i, j) for i in range(self.nqubits) for j in range(i + 1, self.nqubits)]
|
|
86
|
+
elif connectivity == "Circular":
|
|
87
|
+
self.connectivity = [(i, i + 1) for i in range(self.nqubits - 1)] + [(self.nqubits - 1, 0)]
|
|
88
|
+
elif connectivity == "Linear":
|
|
89
|
+
self.connectivity = [(i, i + 1) for i in range(self.nqubits - 1)]
|
|
90
|
+
else:
|
|
91
|
+
raise ValueError(f"Unrecognized connectivity type ({connectivity}).")
|
|
92
|
+
|
|
93
|
+
self.gate_types: dict[str, str] = {"one_qubit_gate": one_qubit_gate, "two_qubit_gates": two_qubit_gate}
|
|
94
|
+
self.one_qubit_gate: type[Union[U1, U2, U3]] = self._ONE_QUBIT_GATES[one_qubit_gate]
|
|
95
|
+
self.two_qubit_gate: type[Union[CNOT, CZ]] = self._TWO_QUBITS_GATES[two_qubit_gate]
|
|
96
|
+
|
|
97
|
+
if structure not in {"grouped", "interposed"}:
|
|
98
|
+
raise ValueError(f"provided structure {structure} is not supported.")
|
|
99
|
+
self.structure = structure
|
|
100
|
+
|
|
101
|
+
self.construct_layer_handlers = {
|
|
102
|
+
"interposed": self._construct_layer_interposed,
|
|
103
|
+
"grouped": self._construct_layer_grouped,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def nparameters(self) -> int:
|
|
108
|
+
"""
|
|
109
|
+
Retrieve the total number of parameters required by all parameterized gates in the ansatz's circuit.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
int: The total count of parameters from all parameterized gates.
|
|
113
|
+
"""
|
|
114
|
+
return self.nqubits * (self.layers + 1) * len(self.one_qubit_gate.PARAMETER_NAMES)
|
|
115
|
+
|
|
116
|
+
def _construct_circuit(self, parameters: list[float]) -> Circuit:
|
|
117
|
+
self._circuit = Circuit(self.nqubits)
|
|
118
|
+
# Add initial layer of unitaries
|
|
119
|
+
for i in range(self.nqubits):
|
|
120
|
+
self._circuit.add(self.one_qubit_gate(i, **dict(zip(self.one_qubit_gate.PARAMETER_NAMES, parameters))))
|
|
121
|
+
|
|
122
|
+
construct_layer_handler = self.construct_layer_handlers[self.structure]
|
|
123
|
+
for _ in range(self.layers):
|
|
124
|
+
construct_layer_handler(parameters)
|
|
125
|
+
self._circuit.add(M(*list(range(self.nqubits))))
|
|
126
|
+
|
|
127
|
+
return self._circuit
|
|
128
|
+
|
|
129
|
+
def _construct_layer_interposed(self, parameters: list[float]) -> None:
|
|
130
|
+
for i in range(self.nqubits):
|
|
131
|
+
self._circuit.add(
|
|
132
|
+
self.one_qubit_gate(i, **{name: parameters.pop() for name in self.one_qubit_gate.PARAMETER_NAMES})
|
|
133
|
+
)
|
|
134
|
+
for p, j in self.connectivity:
|
|
135
|
+
self._circuit.add(self.two_qubit_gate(p, j))
|
|
136
|
+
|
|
137
|
+
def _construct_layer_grouped(self, parameters: list[float]) -> None:
|
|
138
|
+
for i in range(self.nqubits):
|
|
139
|
+
self._circuit.add(
|
|
140
|
+
self.one_qubit_gate(i, **{name: parameters.pop() for name in self.one_qubit_gate.PARAMETER_NAMES})
|
|
141
|
+
)
|
|
142
|
+
for i, j in self.connectivity:
|
|
143
|
+
self._circuit.add(self.two_qubit_gate(i, j))
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Copyright 2025 Qilimanjaro Quantum Tech
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
from qilisdk.yaml import yaml
|
|
17
|
+
|
|
18
|
+
from .exceptions import ParametersNotEqualError, QubitOutOfRangeError
|
|
19
|
+
from .gates import Gate
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@yaml.register_class
|
|
23
|
+
class Circuit:
|
|
24
|
+
def __init__(self, nqubits: int) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Initialize a Circuit instance with a specified number of qubits.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
nqubits (int): The number of qubits in the circuit.
|
|
30
|
+
"""
|
|
31
|
+
self._nqubits: int = nqubits
|
|
32
|
+
self._gates: list[Gate] = []
|
|
33
|
+
self._init_state: np.ndarray = np.zeros(nqubits)
|
|
34
|
+
self._parameterized_gates: list[Gate] = []
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def nqubits(self) -> int:
|
|
38
|
+
"""
|
|
39
|
+
Retrieve the number of qubits in the circuit.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
int: The total number of qubits.
|
|
43
|
+
"""
|
|
44
|
+
return self._nqubits
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def nparameters(self) -> int:
|
|
48
|
+
"""
|
|
49
|
+
Retrieve the total number of parameters required by all parameterized gates in the circuit.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
int: The total count of parameters from all parameterized gates.
|
|
53
|
+
"""
|
|
54
|
+
return len([value for gate in self._gates if gate.is_parameterized for value in gate.parameter_values])
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def gates(self) -> list[Gate]:
|
|
58
|
+
"""
|
|
59
|
+
Retrieve the list of gates in the circuit.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
list[Gate]: A list of gates that have been added to the circuit.
|
|
63
|
+
"""
|
|
64
|
+
return self._gates
|
|
65
|
+
|
|
66
|
+
def get_parameter_values(self) -> list[float]:
|
|
67
|
+
"""
|
|
68
|
+
Retrieve the parameter values from all parameterized gates in the circuit.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
list[float]: A list of parameter values from each parameterized gate.
|
|
72
|
+
"""
|
|
73
|
+
return [value for gate in self._gates if gate.is_parameterized for value in gate.parameter_values]
|
|
74
|
+
|
|
75
|
+
def set_parameter_values(self, values: list[float]) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Set new parameter values for all parameterized gates in the circuit.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
values (list[float]): A list containing new parameter values to assign to the parameterized gates.
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
ParametersNotEqualError: If the number of provided values does not match the expected number of parameters.
|
|
84
|
+
"""
|
|
85
|
+
if len(values) != self.nparameters:
|
|
86
|
+
raise ParametersNotEqualError
|
|
87
|
+
k = 0
|
|
88
|
+
for i, gate in enumerate(self._parameterized_gates):
|
|
89
|
+
gate.set_parameter_values(values[i + k : i + k + gate.nparameters])
|
|
90
|
+
k += gate.nparameters - 1
|
|
91
|
+
|
|
92
|
+
def add(self, gate: Gate) -> None:
|
|
93
|
+
"""
|
|
94
|
+
Add a quantum gate to the circuit.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
gate (Gate): The quantum gate to add to the circuit.
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
QubitOutOfRangeError: If any qubit index used by the gate is not within the circuit's qubit range.
|
|
101
|
+
"""
|
|
102
|
+
if any(qubit >= self.nqubits for qubit in gate.qubits):
|
|
103
|
+
raise QubitOutOfRangeError
|
|
104
|
+
if gate.is_parameterized:
|
|
105
|
+
self._parameterized_gates.append(gate)
|
|
106
|
+
self._gates.append(gate)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Copyright 2025 Qilimanjaro Quantum Tech
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
from qilisdk.common.algorithm import Algorithm
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DigitalAlgorithm(Algorithm):
|
|
18
|
+
"""
|
|
19
|
+
Abstract base class for digital quantum algorithms.
|
|
20
|
+
"""
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Copyright 2025 Qilimanjaro Quantum Tech
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
from abc import ABC, abstractmethod
|
|
15
|
+
from enum import Enum
|
|
16
|
+
|
|
17
|
+
from qilisdk.digital.circuit import Circuit
|
|
18
|
+
from qilisdk.digital.digital_result import DigitalResult
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DigitalSimulationMethod(str, Enum):
|
|
22
|
+
"""
|
|
23
|
+
Enumeration of available simulation methods for the CUDA backend.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
STATE_VECTOR = "state_vector"
|
|
27
|
+
TENSOR_NETWORK = "tensor_network"
|
|
28
|
+
MATRIX_PRODUCT_STATE = "matrix_product_state"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class DigitalBackend(ABC):
|
|
32
|
+
"""
|
|
33
|
+
Abstract base class for digital quantum circuit backends.
|
|
34
|
+
|
|
35
|
+
This abstract class defines the interface for a digital backend capable of executing a
|
|
36
|
+
quantum circuit. Subclasses must implement the execute method to run the circuit with a
|
|
37
|
+
specified number of measurement shots and return a DigitalResult encapsulating the measurement
|
|
38
|
+
outcomes.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self, digital_simulation_method: DigitalSimulationMethod = DigitalSimulationMethod.STATE_VECTOR
|
|
43
|
+
) -> None:
|
|
44
|
+
"""
|
|
45
|
+
Initialize the DigitalBackend.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
simulation_method (DigitalSimulationMethod, optional): The simulation method to use.
|
|
49
|
+
Options include STATE_VECTOR, TENSOR_NETWORK, or MATRIX_PRODUCT_STATE.
|
|
50
|
+
Defaults to STATE_VECTOR.
|
|
51
|
+
"""
|
|
52
|
+
self._digital_simulation_method = digital_simulation_method
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def digital_simulation_method(self) -> DigitalSimulationMethod:
|
|
56
|
+
"""
|
|
57
|
+
Get the simulation method currently configured for the backend.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
SimulationMethod: The simulation method to be used for circuit execution.
|
|
61
|
+
"""
|
|
62
|
+
return self._digital_simulation_method
|
|
63
|
+
|
|
64
|
+
@digital_simulation_method.setter
|
|
65
|
+
def digital_simulation_method(self, value: DigitalSimulationMethod) -> None:
|
|
66
|
+
"""
|
|
67
|
+
Set the simulation method for the backend.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
value (SimulationMethod): The simulation method to set. Options include
|
|
71
|
+
STATE_VECTOR, TENSOR_NETWORK, or MATRIX_PRODUCT_STATE.
|
|
72
|
+
"""
|
|
73
|
+
self._digital_simulation_method = value
|
|
74
|
+
|
|
75
|
+
@abstractmethod
|
|
76
|
+
def execute(self, circuit: Circuit, nshots: int = 1000) -> DigitalResult:
|
|
77
|
+
"""
|
|
78
|
+
Execute the provided quantum circuit and return the measurement results.
|
|
79
|
+
|
|
80
|
+
This method should run the given circuit for the specified number of measurement shots and
|
|
81
|
+
produce a DigitalResult instance containing the raw measurement samples and any computed
|
|
82
|
+
probabilities.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
circuit (Circuit): The quantum circuit to be executed.
|
|
86
|
+
nshots (int, optional): The number of measurement shots to perform. Defaults to 1000.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
DigitalResult: The result of executing the circuit, including measurement samples and probabilities.
|
|
90
|
+
"""
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Copyright 2025 Qilimanjaro Quantum Tech
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
import heapq
|
|
15
|
+
import operator
|
|
16
|
+
from pprint import pformat
|
|
17
|
+
|
|
18
|
+
from qilisdk.common import Result
|
|
19
|
+
from qilisdk.yaml import yaml
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@yaml.register_class
|
|
23
|
+
class DigitalResult(Result):
|
|
24
|
+
"""
|
|
25
|
+
Class representing the result of a quantum circuit measurement.
|
|
26
|
+
|
|
27
|
+
DigitalResult encapsulates the outcome of a digital measurement performed on a quantum circuit.
|
|
28
|
+
It includes the total number of measurement shots, the measurement samples and measurement probabilities.
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
nshots (int): The number of measurement shots performed in the experiment.
|
|
32
|
+
nqubits (int): The number of qubits measured.
|
|
33
|
+
samples (dict[str, int]): A dictionary where keys are bitstrings representing measurement outcomes and values are the number of times each outcome was observed.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, nshots: int, samples: dict[str, int]) -> None:
|
|
37
|
+
"""
|
|
38
|
+
Initializes a DigitalResult instance.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
nshots (int): The total number of measurement shots performed.
|
|
42
|
+
samples (dict[str, int]): A dictionary mapping bitstring outcomes to their occurrence counts.
|
|
43
|
+
All keys (bitstrings) must have the same length, which determines the number of qubits.
|
|
44
|
+
|
|
45
|
+
Raises:
|
|
46
|
+
ValueError: If the samples dictionary is empty or if not all bitstring keys have the same length.
|
|
47
|
+
"""
|
|
48
|
+
self._nshots = nshots
|
|
49
|
+
self._samples = samples
|
|
50
|
+
|
|
51
|
+
# Ensure samples is not empty and is correct.
|
|
52
|
+
if not samples:
|
|
53
|
+
raise ValueError("The samples dictionary is empty.")
|
|
54
|
+
bitstrings = list(samples.keys())
|
|
55
|
+
nqubits = len(bitstrings[0])
|
|
56
|
+
if not all(len(bitstring) == nqubits for bitstring in bitstrings):
|
|
57
|
+
raise ValueError("Not all bitstring keys have the same length.")
|
|
58
|
+
|
|
59
|
+
# Assign nqubits to attribute
|
|
60
|
+
self._nqubits = nqubits
|
|
61
|
+
|
|
62
|
+
# Calculate probabilities
|
|
63
|
+
self._probabilities = {
|
|
64
|
+
bitstring: counts / self._nshots if self._nshots > 0 else 0.0 for bitstring, counts in self._samples.items()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def nshots(self) -> int:
|
|
69
|
+
"""
|
|
70
|
+
Gets the number of measurement shots.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
int: The total number of measurement shots performed.
|
|
74
|
+
"""
|
|
75
|
+
return self._nshots
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def nqubits(self) -> int:
|
|
79
|
+
"""
|
|
80
|
+
Gets the number of qubits involved in the measurement.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
int: The number of qubits measured.
|
|
84
|
+
"""
|
|
85
|
+
return self._nqubits
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def samples(self) -> dict[str, int]:
|
|
89
|
+
"""
|
|
90
|
+
Gets the raw measurement samples.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
dict[str, int]: A dictionary where keys are bitstrings representing measurement outcomes
|
|
94
|
+
and values are the number of times each outcome was observed.
|
|
95
|
+
"""
|
|
96
|
+
return dict(self._samples)
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def probabilities(self) -> dict[str, float]:
|
|
100
|
+
"""
|
|
101
|
+
Gets the probabilities for each measurement outcome.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
dict[str, float]: A dictionary mapping each bitstring outcome to its corresponding probability.
|
|
105
|
+
"""
|
|
106
|
+
return dict(self._probabilities)
|
|
107
|
+
|
|
108
|
+
def get_probability(self, bitstring: str) -> float:
|
|
109
|
+
"""
|
|
110
|
+
Computes the probability of a specific measurement outcome.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
bitstring (str): The bitstring representing the measurement outcome of interest.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
float: The probability of the specified bitstring occurring.
|
|
117
|
+
"""
|
|
118
|
+
return self._probabilities.get(bitstring, 0.0)
|
|
119
|
+
|
|
120
|
+
def get_probabilities(self, n: int | None = None) -> list[tuple[str, float]]:
|
|
121
|
+
"""
|
|
122
|
+
Returns the n most probable bitstrings along with their probabilities.
|
|
123
|
+
|
|
124
|
+
Parameters:
|
|
125
|
+
n (int): The number of most probable bitstrings to return.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
list[tuple[str, float]]: A list of tuples (bitstring, probability) sorted in descending order by probability.
|
|
129
|
+
"""
|
|
130
|
+
if n is None:
|
|
131
|
+
n = len(self._probabilities)
|
|
132
|
+
return heapq.nlargest(n, self._probabilities.items(), key=operator.itemgetter(1))
|
|
133
|
+
|
|
134
|
+
def __repr__(self) -> str:
|
|
135
|
+
"""
|
|
136
|
+
Returns a string representation of the DigitalResult instance for debugging purposes.
|
|
137
|
+
|
|
138
|
+
The representation includes the class name, the number of measurement shots, and a formatted
|
|
139
|
+
display of the measurement samples.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
str: A string representation of the DigitalResult instance for debugging.
|
|
143
|
+
"""
|
|
144
|
+
class_name = self.__class__.__name__
|
|
145
|
+
return f"{class_name}(\n nshots={self.nshots},\n samples={pformat(self.samples)}\n)"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Copyright 2025 Qilimanjaro Quantum Tech
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class QubitOutOfRangeError(Exception): ...
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class GateHasNoMatrixError(Exception): ...
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class GateNotParameterizedError(Exception): ...
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ParametersNotEqualError(Exception): ...
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class InvalidParameterNameError(Exception): ...
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class UnsupportedGateError(Exception): ...
|