qadence 1.1.1__py3-none-any.whl → 1.2.1__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.
Files changed (64) hide show
  1. qadence/__init__.py +1 -0
  2. qadence/analog/__init__.py +4 -2
  3. qadence/analog/addressing.py +167 -0
  4. qadence/analog/constants.py +59 -0
  5. qadence/analog/device.py +82 -0
  6. qadence/analog/hamiltonian_terms.py +101 -0
  7. qadence/analog/parse_analog.py +120 -0
  8. qadence/backend.py +42 -12
  9. qadence/backends/__init__.py +1 -2
  10. qadence/backends/api.py +27 -9
  11. qadence/backends/braket/backend.py +3 -2
  12. qadence/backends/horqrux/__init__.py +5 -0
  13. qadence/backends/horqrux/backend.py +216 -0
  14. qadence/backends/horqrux/config.py +26 -0
  15. qadence/backends/horqrux/convert_ops.py +273 -0
  16. qadence/backends/jax_utils.py +45 -0
  17. qadence/backends/pulser/__init__.py +0 -1
  18. qadence/backends/pulser/backend.py +31 -15
  19. qadence/backends/pulser/config.py +19 -10
  20. qadence/backends/pulser/devices.py +57 -63
  21. qadence/backends/pulser/pulses.py +70 -12
  22. qadence/backends/pyqtorch/backend.py +4 -4
  23. qadence/backends/pyqtorch/config.py +18 -12
  24. qadence/backends/pyqtorch/convert_ops.py +15 -7
  25. qadence/backends/utils.py +5 -9
  26. qadence/blocks/abstract.py +5 -1
  27. qadence/blocks/analog.py +18 -9
  28. qadence/blocks/block_to_tensor.py +11 -0
  29. qadence/blocks/embedding.py +46 -24
  30. qadence/blocks/primitive.py +81 -9
  31. qadence/blocks/utils.py +20 -1
  32. qadence/circuit.py +3 -9
  33. qadence/constructors/__init__.py +4 -0
  34. qadence/constructors/feature_maps.py +84 -60
  35. qadence/constructors/hamiltonians.py +27 -98
  36. qadence/constructors/rydberg_feature_maps.py +113 -0
  37. qadence/divergences.py +12 -0
  38. qadence/engines/__init__.py +0 -0
  39. qadence/engines/differentiable_backend.py +152 -0
  40. qadence/engines/jax/__init__.py +8 -0
  41. qadence/engines/jax/differentiable_backend.py +73 -0
  42. qadence/engines/jax/differentiable_expectation.py +94 -0
  43. qadence/engines/torch/__init__.py +4 -0
  44. qadence/engines/torch/differentiable_backend.py +85 -0
  45. qadence/extensions.py +21 -9
  46. qadence/finitediff.py +47 -0
  47. qadence/mitigations/readout.py +92 -25
  48. qadence/ml_tools/models.py +10 -3
  49. qadence/models/qnn.py +88 -23
  50. qadence/models/quantum_model.py +13 -2
  51. qadence/operations.py +55 -70
  52. qadence/parameters.py +24 -13
  53. qadence/register.py +91 -43
  54. qadence/transpile/__init__.py +1 -0
  55. qadence/transpile/apply_fn.py +40 -0
  56. qadence/types.py +32 -2
  57. qadence/utils.py +35 -0
  58. {qadence-1.1.1.dist-info → qadence-1.2.1.dist-info}/METADATA +22 -3
  59. {qadence-1.1.1.dist-info → qadence-1.2.1.dist-info}/RECORD +62 -44
  60. {qadence-1.1.1.dist-info → qadence-1.2.1.dist-info}/WHEEL +1 -1
  61. qadence/analog/interaction.py +0 -198
  62. qadence/analog/utils.py +0 -132
  63. /qadence/{backends/pytorch_wrapper.py → engines/torch/differentiable_expectation.py} +0 -0
  64. {qadence-1.1.1.dist-info → qadence-1.2.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,198 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from copy import deepcopy
4
- from functools import singledispatch
5
- from itertools import product
6
- from typing import Any, Callable, Union, overload
7
-
8
- import torch
9
-
10
- from qadence.analog.utils import ising_interaction, rot_generator, xy_interaction
11
- from qadence.blocks.abstract import AbstractBlock
12
- from qadence.blocks.analog import (
13
- AnalogBlock,
14
- AnalogChain,
15
- AnalogKron,
16
- ConstantAnalogRotation,
17
- Interaction,
18
- WaitBlock,
19
- )
20
- from qadence.blocks.composite import CompositeBlock
21
- from qadence.blocks.primitive import PrimitiveBlock, ScaleBlock
22
- from qadence.blocks.utils import _construct, add, chain, kron
23
- from qadence.circuit import QuantumCircuit
24
- from qadence.operations import HamEvo, I, wait
25
- from qadence.qubit_support import QubitSupport
26
- from qadence.register import Register
27
- from qadence.transpile.transpile import blockfn_to_circfn
28
-
29
- INTERACTIONS = {Interaction.NN: ising_interaction, Interaction.XY: xy_interaction}
30
-
31
-
32
- @overload
33
- def add_interaction(circuit: QuantumCircuit, **kwargs: Any) -> QuantumCircuit:
34
- ...
35
-
36
-
37
- @overload
38
- def add_interaction(block: AbstractBlock, **kwargs: Any) -> AbstractBlock:
39
- ...
40
-
41
-
42
- @overload
43
- def add_interaction(register: Register, block: AbstractBlock, **kwargs: Any) -> AbstractBlock:
44
- ...
45
-
46
-
47
- @singledispatch
48
- def add_interaction(
49
- x: Register | QuantumCircuit | AbstractBlock,
50
- *args: Any,
51
- interaction: Interaction | Callable = Interaction.NN,
52
- ) -> QuantumCircuit | AbstractBlock:
53
- """Turns blocks or circuits into (a chain of) `HamEvo` blocks.
54
-
55
- This includes a chosen interaction term.
56
-
57
- This is a `@singledipatch`ed function which can be called in three ways:
58
-
59
- * With a `QuantumCircuit` which contains all necessary information: `add_interaction(circuit)`
60
- * With a `Register` and an `AbstractBlock`: `add_interaction(reg, block)`
61
- * With an `AbstractBlock` only: `add_interaction(block)`
62
-
63
- See the section about [analog blocks](/digital_analog_qc/analog-basics.md) for
64
- detailed information about how which types of blocks are translated.
65
-
66
- Arguments:
67
- x: Circuit or block to be emulated. See the examples on which argument
68
- combinations are accepted.
69
- interaction: Type of interaction that is added. Can also be a function that accepts a
70
- register and a list of edges that define which qubits interact (see the examples).
71
-
72
- Examples:
73
- ```python exec="on" source="material-block" result="json"
74
- from qadence import QuantumCircuit, AnalogRX, add_interaction
75
-
76
- c = QuantumCircuit(2, AnalogRX(2.0))
77
- e = add_interaction(c)
78
- print(str(e.block.generator)) # markdown-exec: hide
79
- ```
80
- You can also use `add_interaction` directly on a block, but you have to provide either
81
- the `Register` or define a non-global qubit support.
82
- ```python exec="on" source="material-block" result="json"
83
- from qadence import AnalogRX, Register, add_interaction
84
-
85
- b = AnalogRX(2.0)
86
- r = Register(1)
87
- e = add_interaction(r, b)
88
- print(e.generator) # markdown-exec: hide
89
-
90
- # or provide only the block with local qubit support
91
- # in this case the register is created via `Register(b.n_qubits)`
92
- e = add_interaction(AnalogRX(2.0, qubit_support=(0,)))
93
- print(e.generator)
94
- ```
95
- You can specify a custom `interaction` function which has to accept a `Register` and a list
96
- of `edges: list[tuple[int, int]]`:
97
- ```python exec="on" source="material-block" result="json"
98
- from qadence import AnalogRX, Register, add_interaction
99
- from qadence.analog.utils import ising_interaction
100
-
101
- def int_fn(r: Register, pairs: list[tuple[int, int]]) -> AbstractBlock:
102
- # do either something completely custom
103
- # ...
104
- # or e.g. change the default kwargs to `ising_interaction`
105
- return ising_interaction(r, pairs, rydberg_level=70)
106
-
107
- b = AnalogRX(2.0)
108
- r = Register(1)
109
- e = add_interaction(r, b, interaction=int_fn)
110
- ```
111
- """
112
- raise ValueError(f"`add_interaction` is not implemented for {type(x)}")
113
-
114
-
115
- @add_interaction.register # type: ignore[attr-defined]
116
- def _(circuit: QuantumCircuit, **kwargs: Any) -> QuantumCircuit:
117
- reg = circuit.register
118
- return blockfn_to_circfn(lambda b: add_interaction(reg, b, **kwargs))(circuit)
119
-
120
-
121
- @add_interaction.register # type: ignore[attr-defined]
122
- def _(block: AbstractBlock, **kwargs: Any) -> AbstractBlock:
123
- return add_interaction(Register(block.n_qubits), block, **kwargs)
124
-
125
-
126
- @add_interaction.register # type: ignore[attr-defined]
127
- def _(
128
- register: Register,
129
- block: AbstractBlock,
130
- interaction: Union[Interaction, Callable] = Interaction.NN,
131
- ) -> AbstractBlock:
132
- try:
133
- fn = interaction if callable(interaction) else INTERACTIONS[Interaction(interaction)]
134
- except KeyError:
135
- raise KeyError(
136
- "Function `add_interaction` only supports NN and XY, or a custom callable function."
137
- )
138
- return _add_interaction(block, register, fn) # type: ignore[arg-type]
139
-
140
-
141
- @singledispatch
142
- def _add_interaction(b: AbstractBlock, r: Register, interaction: Callable) -> AbstractBlock:
143
- raise NotImplementedError(f"Cannot emulate {type(b)}")
144
-
145
-
146
- @_add_interaction.register
147
- def _(b: CompositeBlock, r: Register, i: Callable) -> AbstractBlock:
148
- return _construct(type(b), tuple(map(lambda b: _add_interaction(b, r, i), b.blocks)))
149
-
150
-
151
- @_add_interaction.register
152
- def _(block: ScaleBlock, register: Register, interaction: Callable) -> AbstractBlock:
153
- if isinstance(block.block, AnalogBlock):
154
- raise NotImplementedError("Scaling emulated analog blocks is not implemented.")
155
- return block
156
-
157
-
158
- @_add_interaction.register
159
- def _(block: PrimitiveBlock, register: Register, interaction: Callable) -> AbstractBlock:
160
- return block
161
-
162
-
163
- @_add_interaction.register
164
- def _(block: WaitBlock, register: Register, interaction: Callable) -> AbstractBlock:
165
- duration = block.parameters.duration
166
-
167
- support = tuple(range(register.n_qubits))
168
- assert support == block.qubit_support if not block.qubit_support.is_global else True
169
- pairs = list(filter(lambda x: x[0] < x[1], product(support, support)))
170
-
171
- return HamEvo(interaction(register, pairs), duration / 1000) if len(pairs) else I(0)
172
-
173
-
174
- @_add_interaction.register
175
- def _(block: ConstantAnalogRotation, register: Register, interaction: Callable) -> AbstractBlock:
176
- # convert "global" to indexed qubit suppport so that we can re-use `kron` dispatched function
177
- b = deepcopy(block)
178
- b.qubit_support = QubitSupport(*range(register.n_qubits))
179
- return _add_interaction(kron(b), register, interaction)
180
-
181
-
182
- @_add_interaction.register
183
- def _(block: AnalogKron, register: Register, interaction: Callable) -> AbstractBlock:
184
- from qadence import block_to_tensor
185
-
186
- w_block = wait(duration=block.duration, qubit_support=block.qubit_support)
187
- i_terms = add_interaction(register, w_block, interaction=interaction)
188
-
189
- generator = add(rot_generator(b) for b in block.blocks if isinstance(b, ConstantAnalogRotation))
190
- generator = generator if i_terms == I(0) else generator + i_terms.generator # type: ignore[attr-defined] # noqa: E501
191
-
192
- norm = torch.norm(block_to_tensor(generator)).item()
193
- return HamEvo(generator / norm, norm * block.duration / 1000)
194
-
195
-
196
- @_add_interaction.register
197
- def _(block: AnalogChain, register: Register, interaction: Callable) -> AbstractBlock:
198
- return chain(add_interaction(register, b, interaction=interaction) for b in block.blocks)
qadence/analog/utils.py DELETED
@@ -1,132 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from math import dist as euclidean_distance
4
-
5
- from sympy import cos, sin
6
-
7
- from qadence.blocks.abstract import AbstractBlock
8
- from qadence.blocks.analog import (
9
- ConstantAnalogRotation,
10
- )
11
- from qadence.blocks.utils import add, kron
12
- from qadence.operations import N, X, Y
13
- from qadence.register import Register
14
-
15
- # Ising coupling coefficient depending on the Rydberg level
16
- # Include a normalization to the Planck constant hbar
17
- # In units of [rad . µm^6 / µs]
18
-
19
- C6_DICT = {
20
- 50: 96120.72,
21
- 51: 122241.6,
22
- 52: 154693.02,
23
- 53: 194740.36,
24
- 54: 243973.91,
25
- 55: 304495.01,
26
- 56: 378305.98,
27
- 57: 468027.05,
28
- 58: 576714.85,
29
- 59: 707911.38,
30
- 60: 865723.02,
31
- 61: 1054903.11,
32
- 62: 1281042.11,
33
- 63: 1550531.15,
34
- 64: 1870621.31,
35
- 65: 2249728.57,
36
- 66: 2697498.69,
37
- 67: 3224987.51,
38
- 68: 3844734.37,
39
- 69: 4571053.32,
40
- 70: 5420158.53,
41
- 71: 6410399.4,
42
- 72: 7562637.31,
43
- 73: 8900342.14,
44
- 74: 10449989.62,
45
- 75: 12241414.53,
46
- 76: 14308028.03,
47
- 77: 16687329.94,
48
- 78: 19421333.62,
49
- 79: 22557029.94,
50
- 80: 26146720.74,
51
- 81: 30248886.65,
52
- 82: 34928448.69,
53
- 83: 40257623.67,
54
- 84: 46316557.88,
55
- 85: 53194043.52,
56
- 86: 60988354.64,
57
- 87: 69808179.15,
58
- 88: 79773468.88,
59
- 89: 91016513.07,
60
- 90: 103677784.57,
61
- 91: 117933293.96,
62
- 92: 133943541.9,
63
- 93: 151907135.94,
64
- 94: 172036137.34,
65
- 95: 194562889.89,
66
- 96: 219741590.56,
67
- 97: 247850178.91,
68
- 98: 279192193.77,
69
- 99: 314098829.39,
70
- 100: 352931119.11,
71
- }
72
-
73
-
74
- def _qubitposition(register: Register, i: int) -> tuple[int, int]:
75
- (x, y) = list(register.coords.values())[i]
76
- return (x, y)
77
-
78
-
79
- def ising_interaction(
80
- register: Register, pairs: list[tuple[int, int]], rydberg_level: int = 60
81
- ) -> AbstractBlock:
82
- """
83
- Computes the Rydberg Ising interaction Hamiltonian for a register of qubits.
84
-
85
- H_int = ∑_(j<i) (C_6 / R**6) * kron(N_i, N_j)
86
-
87
- Args:
88
- register: the register of qubits.
89
- pairs: a list of all pairs of interacting qubits.
90
- rydberg_level: determines the value of C_6
91
- """
92
- c6 = C6_DICT[rydberg_level]
93
-
94
- def term(i: int, j: int) -> AbstractBlock:
95
- qi, qj = _qubitposition(register, i), _qubitposition(register, j)
96
- rij = euclidean_distance(qi, qj)
97
- return (c6 / rij**6) * kron(N(i), N(j))
98
-
99
- return add(term(i, j) for (i, j) in pairs)
100
-
101
-
102
- def xy_interaction(
103
- register: Register, pairs: list[tuple[int, int]], c3: float = 3700.0
104
- ) -> AbstractBlock:
105
- """
106
- Computes the Rydberg XY interaction Hamiltonian for a register of qubits.
107
-
108
- H_int = ∑_(j<i) (C_3 / R**3) * (kron(X_i, X_j) + kron(Y_i, Y_j))
109
-
110
- Args:
111
- register: the register of qubits.
112
- pairs: a list of all pairs of interacting qubits.
113
- c3: the coefficient value of C_3 in units of [rad . µm^3 / µs]
114
- """
115
-
116
- def term(i: int, j: int) -> AbstractBlock:
117
- qi, qj = _qubitposition(register, i), _qubitposition(register, j)
118
- rij = euclidean_distance(qi, qj)
119
- return (c3 / rij**3) * (kron(X(i), X(j)) + kron(Y(i), Y(j)))
120
-
121
- return add(term(i, j) for (i, j) in pairs)
122
-
123
-
124
- def rot_generator(block: ConstantAnalogRotation) -> AbstractBlock:
125
- omega = block.parameters.omega
126
- delta = block.parameters.delta
127
- phase = block.parameters.phase
128
- support = block.qubit_support
129
-
130
- x_terms = (omega / 2) * add(cos(phase) * X(i) - sin(phase) * Y(i) for i in support)
131
- z_terms = delta * add(N(i) for i in support)
132
- return x_terms - z_terms # type: ignore[no-any-return]