ascent-science-sqa 0.1.0__tar.gz
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.
- ascent_science_sqa-0.1.0/PKG-INFO +68 -0
- ascent_science_sqa-0.1.0/README.md +55 -0
- ascent_science_sqa-0.1.0/pyproject.toml +25 -0
- ascent_science_sqa-0.1.0/setup.cfg +4 -0
- ascent_science_sqa-0.1.0/src/ascent_science_sqa/__init__.py +293 -0
- ascent_science_sqa-0.1.0/src/ascent_science_sqa.egg-info/PKG-INFO +68 -0
- ascent_science_sqa-0.1.0/src/ascent_science_sqa.egg-info/SOURCES.txt +9 -0
- ascent_science_sqa-0.1.0/src/ascent_science_sqa.egg-info/dependency_links.txt +1 -0
- ascent_science_sqa-0.1.0/src/ascent_science_sqa.egg-info/entry_points.txt +2 -0
- ascent_science_sqa-0.1.0/src/ascent_science_sqa.egg-info/requires.txt +1 -0
- ascent_science_sqa-0.1.0/src/ascent_science_sqa.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ascent-science-sqa
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Path-integral simulated quantum annealing for the Ascent Science SDK
|
|
5
|
+
Author: Ascent Science
|
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
9
|
+
Classifier: Typing :: Typed
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: ascent-science<0.10,>=0.9.4
|
|
13
|
+
|
|
14
|
+
# Ascent Science SQA plugin
|
|
15
|
+
|
|
16
|
+
This package implements simulated quantum annealing (SQA) with the standard
|
|
17
|
+
Suzuki--Trotter path-integral mapping and single-spin Metropolis updates. It is
|
|
18
|
+
a classical simulation of a transverse-field Ising model; it does not use a
|
|
19
|
+
quantum processor.
|
|
20
|
+
|
|
21
|
+
For development from this repository, install the core SDK and plugin in editable mode:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
python -m pip install -e ./sdk-python -e ./sdk-plugins/sqa
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
After the `0.1.0` package is published, it can instead be installed with
|
|
28
|
+
`python -m pip install ascent-science-sqa==0.1.0`.
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import ascent
|
|
32
|
+
|
|
33
|
+
solver = ascent.quantum.SimulatedQuantumAnnealing(
|
|
34
|
+
trotter_slices=32,
|
|
35
|
+
sweeps=500,
|
|
36
|
+
beta=5.0,
|
|
37
|
+
gamma_start=5.0,
|
|
38
|
+
gamma_end=0.01,
|
|
39
|
+
seed=42,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Minimize x.T Q x for x in {0, 1}^n.
|
|
43
|
+
result = solver.solve_qubo([
|
|
44
|
+
[-2.0, 1.0],
|
|
45
|
+
[1.0, -2.0],
|
|
46
|
+
])
|
|
47
|
+
print(result.best_solution, result.best_value)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
For an Ising model, `solve_ising(J, fields=h)` minimizes
|
|
51
|
+
`s.T @ J @ s + h.T @ s + offset`, with spins in `{-1, +1}`. For a canonical
|
|
52
|
+
problem object use `solver.solve(QuboProblem(...))` or
|
|
53
|
+
`solver.solve(IsingProblem(...))`.
|
|
54
|
+
|
|
55
|
+
SQA is stochastic and does not prove optimality. Results therefore have status
|
|
56
|
+
`FEASIBLE` (or `TIME_LIMIT` if a supplied `SolveOptions` limit is reached).
|
|
57
|
+
|
|
58
|
+
## Build a release
|
|
59
|
+
|
|
60
|
+
Use the same release flow as the ACO plugin:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
sh scripts/build-release.sh
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The wheel, source archive, and `SHA256SUMS` are written to `dist/`. If the
|
|
67
|
+
default Python does not provide a recent setuptools/build installation, select
|
|
68
|
+
another interpreter with `ASCENT_SQA_BUILD_PYTHON=/path/to/python`.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Ascent Science SQA plugin
|
|
2
|
+
|
|
3
|
+
This package implements simulated quantum annealing (SQA) with the standard
|
|
4
|
+
Suzuki--Trotter path-integral mapping and single-spin Metropolis updates. It is
|
|
5
|
+
a classical simulation of a transverse-field Ising model; it does not use a
|
|
6
|
+
quantum processor.
|
|
7
|
+
|
|
8
|
+
For development from this repository, install the core SDK and plugin in editable mode:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
python -m pip install -e ./sdk-python -e ./sdk-plugins/sqa
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
After the `0.1.0` package is published, it can instead be installed with
|
|
15
|
+
`python -m pip install ascent-science-sqa==0.1.0`.
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
import ascent
|
|
19
|
+
|
|
20
|
+
solver = ascent.quantum.SimulatedQuantumAnnealing(
|
|
21
|
+
trotter_slices=32,
|
|
22
|
+
sweeps=500,
|
|
23
|
+
beta=5.0,
|
|
24
|
+
gamma_start=5.0,
|
|
25
|
+
gamma_end=0.01,
|
|
26
|
+
seed=42,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# Minimize x.T Q x for x in {0, 1}^n.
|
|
30
|
+
result = solver.solve_qubo([
|
|
31
|
+
[-2.0, 1.0],
|
|
32
|
+
[1.0, -2.0],
|
|
33
|
+
])
|
|
34
|
+
print(result.best_solution, result.best_value)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For an Ising model, `solve_ising(J, fields=h)` minimizes
|
|
38
|
+
`s.T @ J @ s + h.T @ s + offset`, with spins in `{-1, +1}`. For a canonical
|
|
39
|
+
problem object use `solver.solve(QuboProblem(...))` or
|
|
40
|
+
`solver.solve(IsingProblem(...))`.
|
|
41
|
+
|
|
42
|
+
SQA is stochastic and does not prove optimality. Results therefore have status
|
|
43
|
+
`FEASIBLE` (or `TIME_LIMIT` if a supplied `SolveOptions` limit is reached).
|
|
44
|
+
|
|
45
|
+
## Build a release
|
|
46
|
+
|
|
47
|
+
Use the same release flow as the ACO plugin:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
sh scripts/build-release.sh
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The wheel, source archive, and `SHA256SUMS` are written to `dist/`. If the
|
|
54
|
+
default Python does not provide a recent setuptools/build installation, select
|
|
55
|
+
another interpreter with `ASCENT_SQA_BUILD_PYTHON=/path/to/python`.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ascent-science-sqa"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Path-integral simulated quantum annealing for the Ascent Science SDK"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "LicenseRef-Proprietary"
|
|
12
|
+
dependencies = ["ascent-science>=0.9.4,<0.10"]
|
|
13
|
+
authors = [{name = "Ascent Science"}]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
17
|
+
"Typing :: Typed",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.entry-points."ascent.algorithms"]
|
|
21
|
+
sqa = "ascent_science_sqa:register"
|
|
22
|
+
|
|
23
|
+
[tool.setuptools.packages.find]
|
|
24
|
+
where = ["src"]
|
|
25
|
+
include = ["ascent_science_sqa*"]
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"""Suzuki--Trotter path-integral simulated quantum annealing.
|
|
2
|
+
|
|
3
|
+
This is a classical Monte Carlo simulation of a transverse-field Ising model.
|
|
4
|
+
It does not submit work to quantum hardware.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import math
|
|
10
|
+
import random
|
|
11
|
+
import time
|
|
12
|
+
|
|
13
|
+
from ascent.core import (
|
|
14
|
+
AlgorithmSpec,
|
|
15
|
+
ExecutionInfo,
|
|
16
|
+
IsingProblem,
|
|
17
|
+
QuboProblem,
|
|
18
|
+
SolveOptions,
|
|
19
|
+
SolveResult,
|
|
20
|
+
SolveStatus,
|
|
21
|
+
SolverIdentity,
|
|
22
|
+
qubo_to_ising,
|
|
23
|
+
symmetrize,
|
|
24
|
+
)
|
|
25
|
+
from ascent.plugin_api import register_algorithm, validated_license
|
|
26
|
+
from ascent.usage import tracked_call
|
|
27
|
+
|
|
28
|
+
__version__ = "0.1.0"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _finite_vector(values, size, name):
|
|
32
|
+
try:
|
|
33
|
+
vector = [float(value) for value in values]
|
|
34
|
+
except (TypeError, ValueError) as error:
|
|
35
|
+
raise ValueError(f"{name} must contain finite numbers") from error
|
|
36
|
+
if len(vector) != size or any(not math.isfinite(value) for value in vector):
|
|
37
|
+
raise ValueError(f"{name} must contain one finite value per variable")
|
|
38
|
+
return vector
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SimulatedQuantumAnnealing:
|
|
42
|
+
"""Minimize Ising or QUBO energy using standard path-integral SQA.
|
|
43
|
+
|
|
44
|
+
``trotter_slices`` controls the Suzuki--Trotter discretization. ``beta`` is
|
|
45
|
+
the inverse physical temperature and ``gamma_start``/``gamma_end`` define
|
|
46
|
+
the transverse-field schedule. A result is heuristic and is therefore
|
|
47
|
+
reported as ``FEASIBLE``, never as a certified optimum.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
*,
|
|
53
|
+
trotter_slices=16,
|
|
54
|
+
sweeps=200,
|
|
55
|
+
beta=5.0,
|
|
56
|
+
gamma_start=5.0,
|
|
57
|
+
gamma_end=0.01,
|
|
58
|
+
schedule="geometric",
|
|
59
|
+
seed=None,
|
|
60
|
+
user_id=None,
|
|
61
|
+
sdk_code=None,
|
|
62
|
+
endpoint=None,
|
|
63
|
+
license_path=None,
|
|
64
|
+
timeout=30.0,
|
|
65
|
+
report_usage=True,
|
|
66
|
+
validation_mode="cached",
|
|
67
|
+
**unused,
|
|
68
|
+
):
|
|
69
|
+
if unused:
|
|
70
|
+
raise TypeError("unsupported SQA parameters: %s" % ", ".join(sorted(unused)))
|
|
71
|
+
self.trotter_slices = int(trotter_slices)
|
|
72
|
+
self.sweeps = int(sweeps)
|
|
73
|
+
self.beta = float(beta)
|
|
74
|
+
self.gamma_start = float(gamma_start)
|
|
75
|
+
self.gamma_end = float(gamma_end)
|
|
76
|
+
self.schedule = str(schedule).lower()
|
|
77
|
+
self.seed = seed
|
|
78
|
+
self._validate_parameters()
|
|
79
|
+
self.endpoint, self.license_user_id, self._usage_reporter = validated_license(
|
|
80
|
+
endpoint, user_id, sdk_code, license_path, timeout, report_usage, validation_mode
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def _validate_parameters(self):
|
|
84
|
+
if self.trotter_slices < 2:
|
|
85
|
+
raise ValueError("trotter_slices must be at least 2")
|
|
86
|
+
if self.sweeps < 1:
|
|
87
|
+
raise ValueError("sweeps must be a positive integer")
|
|
88
|
+
if not math.isfinite(self.beta) or self.beta <= 0:
|
|
89
|
+
raise ValueError("beta must be a finite positive number")
|
|
90
|
+
if not math.isfinite(self.gamma_start) or self.gamma_start <= 0:
|
|
91
|
+
raise ValueError("gamma_start must be a finite positive number")
|
|
92
|
+
if not math.isfinite(self.gamma_end) or self.gamma_end <= 0:
|
|
93
|
+
raise ValueError("gamma_end must be a finite positive number")
|
|
94
|
+
if self.gamma_start < self.gamma_end:
|
|
95
|
+
raise ValueError("gamma_start must be greater than or equal to gamma_end")
|
|
96
|
+
if self.schedule not in {"linear", "geometric"}:
|
|
97
|
+
raise ValueError("schedule must be 'linear' or 'geometric'")
|
|
98
|
+
|
|
99
|
+
def solve(self, problem, *, options=None):
|
|
100
|
+
if options is not None and not isinstance(options, SolveOptions):
|
|
101
|
+
raise TypeError("options must be a SolveOptions instance")
|
|
102
|
+
if isinstance(problem, QuboProblem):
|
|
103
|
+
return self.solve_qubo(
|
|
104
|
+
problem.matrix,
|
|
105
|
+
offset=float(problem.metadata.get("offset", 0.0)),
|
|
106
|
+
options=options,
|
|
107
|
+
)
|
|
108
|
+
if isinstance(problem, IsingProblem):
|
|
109
|
+
return self.solve_ising(
|
|
110
|
+
problem.matrix,
|
|
111
|
+
fields=problem.metadata.get("fields"),
|
|
112
|
+
offset=float(problem.metadata.get("offset", 0.0)),
|
|
113
|
+
options=options,
|
|
114
|
+
)
|
|
115
|
+
raise TypeError("problem must be a QuboProblem or IsingProblem")
|
|
116
|
+
|
|
117
|
+
def solve_qubo(self, qubo, *, offset=0.0, initial_state=None, options=None):
|
|
118
|
+
coefficients = qubo_to_ising(qubo)
|
|
119
|
+
initial_spins = None
|
|
120
|
+
if initial_state is not None:
|
|
121
|
+
try:
|
|
122
|
+
bits = list(initial_state)
|
|
123
|
+
except TypeError as error:
|
|
124
|
+
raise ValueError("initial_state must contain one 0 or 1 value per variable") from error
|
|
125
|
+
if any(bit not in (0, 1) for bit in bits):
|
|
126
|
+
raise ValueError("initial_state must contain one 0 or 1 value per variable")
|
|
127
|
+
initial_spins = [2 * int(bit) - 1 for bit in bits]
|
|
128
|
+
|
|
129
|
+
def execute():
|
|
130
|
+
result = self._solve_ising(
|
|
131
|
+
coefficients.couplings,
|
|
132
|
+
coefficients.fields,
|
|
133
|
+
float(offset) + coefficients.offset,
|
|
134
|
+
initial_state=initial_spins,
|
|
135
|
+
options=options,
|
|
136
|
+
)
|
|
137
|
+
spins = result.best_solution
|
|
138
|
+
bits = [(spin + 1) // 2 for spin in spins]
|
|
139
|
+
metrics = dict(result.metrics)
|
|
140
|
+
metrics["best_spins"] = spins
|
|
141
|
+
return SolveResult(
|
|
142
|
+
status=result.status,
|
|
143
|
+
best_solution=bits,
|
|
144
|
+
best_value=result.best_value,
|
|
145
|
+
solver=result.solver,
|
|
146
|
+
runtime_seconds=result.runtime_seconds,
|
|
147
|
+
history=result.history,
|
|
148
|
+
metrics=metrics,
|
|
149
|
+
execution=result.execution,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
return tracked_call(self._usage_reporter, "sqa", "local", execute)
|
|
153
|
+
|
|
154
|
+
def solve_ising(self, couplings, *, fields=None, offset=0.0, initial_state=None, options=None):
|
|
155
|
+
return tracked_call(
|
|
156
|
+
self._usage_reporter,
|
|
157
|
+
"sqa",
|
|
158
|
+
"local",
|
|
159
|
+
lambda: self._solve_ising(couplings, fields, offset, initial_state=initial_state, options=options),
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
def _gamma(self, step):
|
|
163
|
+
if self.sweeps == 1:
|
|
164
|
+
return self.gamma_end
|
|
165
|
+
fraction = step / (self.sweeps - 1)
|
|
166
|
+
if self.schedule == "linear":
|
|
167
|
+
return self.gamma_start + fraction * (self.gamma_end - self.gamma_start)
|
|
168
|
+
return self.gamma_start * (self.gamma_end / self.gamma_start) ** fraction
|
|
169
|
+
|
|
170
|
+
def _solve_ising(self, couplings, fields, offset, *, initial_state, options):
|
|
171
|
+
if options is not None and not isinstance(options, SolveOptions):
|
|
172
|
+
raise TypeError("options must be a SolveOptions instance")
|
|
173
|
+
started = time.perf_counter()
|
|
174
|
+
matrix = symmetrize(couplings)
|
|
175
|
+
size = len(matrix)
|
|
176
|
+
linear = [0.0] * size if fields is None else _finite_vector(fields, size, "fields")
|
|
177
|
+
offset = float(offset)
|
|
178
|
+
if not math.isfinite(offset):
|
|
179
|
+
raise ValueError("offset must be finite")
|
|
180
|
+
if initial_state is not None:
|
|
181
|
+
initial_state = [int(value) for value in initial_state]
|
|
182
|
+
if len(initial_state) != size or any(value not in {-1, 1} for value in initial_state):
|
|
183
|
+
raise ValueError("initial_state must contain one -1 or +1 value per variable")
|
|
184
|
+
|
|
185
|
+
seed = self.seed if options is None or options.random_seed is None else options.random_seed
|
|
186
|
+
rng = random.Random(seed)
|
|
187
|
+
replicas = [
|
|
188
|
+
list(initial_state) if initial_state is not None else [rng.choice((-1, 1)) for _ in range(size)]
|
|
189
|
+
for _ in range(self.trotter_slices)
|
|
190
|
+
]
|
|
191
|
+
|
|
192
|
+
def classical_energy(spins):
|
|
193
|
+
return offset + sum(linear[i] * spins[i] for i in range(size)) + sum(
|
|
194
|
+
matrix[i][j] * spins[i] * spins[j]
|
|
195
|
+
for i in range(size)
|
|
196
|
+
for j in range(size)
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
energies = [classical_energy(replica) for replica in replicas]
|
|
200
|
+
best_index = min(range(self.trotter_slices), key=energies.__getitem__)
|
|
201
|
+
best_spins = replicas[best_index][:]
|
|
202
|
+
best_value = energies[best_index]
|
|
203
|
+
history = [best_value]
|
|
204
|
+
accepted = attempted = 0
|
|
205
|
+
deadline = None
|
|
206
|
+
if options is not None and options.time_limit_seconds is not None:
|
|
207
|
+
deadline = started + options.time_limit_seconds
|
|
208
|
+
|
|
209
|
+
completed_sweeps = 0
|
|
210
|
+
timed_out = False
|
|
211
|
+
for step in range(self.sweeps):
|
|
212
|
+
gamma = self._gamma(step)
|
|
213
|
+
argument = self.beta * gamma / self.trotter_slices
|
|
214
|
+
temporal_coupling = -0.5 * math.log(max(math.tanh(argument), 1e-300))
|
|
215
|
+
updates = [(slice_index, spin_index) for slice_index in range(self.trotter_slices) for spin_index in range(size)]
|
|
216
|
+
rng.shuffle(updates)
|
|
217
|
+
for slice_index, spin_index in updates:
|
|
218
|
+
if deadline is not None and time.perf_counter() >= deadline:
|
|
219
|
+
timed_out = True
|
|
220
|
+
break
|
|
221
|
+
spins = replicas[slice_index]
|
|
222
|
+
spin = spins[spin_index]
|
|
223
|
+
local_field = linear[spin_index] + 2.0 * sum(
|
|
224
|
+
matrix[spin_index][other] * spins[other]
|
|
225
|
+
for other in range(size)
|
|
226
|
+
if other != spin_index
|
|
227
|
+
)
|
|
228
|
+
delta_energy = -2.0 * spin * local_field
|
|
229
|
+
previous_spin = replicas[(slice_index - 1) % self.trotter_slices][spin_index]
|
|
230
|
+
next_spin = replicas[(slice_index + 1) % self.trotter_slices][spin_index]
|
|
231
|
+
delta_action = (
|
|
232
|
+
self.beta * delta_energy / self.trotter_slices
|
|
233
|
+
+ 2.0 * temporal_coupling * spin * (previous_spin + next_spin)
|
|
234
|
+
)
|
|
235
|
+
attempted += 1
|
|
236
|
+
if delta_action <= 0.0 or rng.random() < math.exp(-min(delta_action, 745.0)):
|
|
237
|
+
spins[spin_index] = -spin
|
|
238
|
+
energies[slice_index] += delta_energy
|
|
239
|
+
accepted += 1
|
|
240
|
+
if energies[slice_index] < best_value:
|
|
241
|
+
best_value = energies[slice_index]
|
|
242
|
+
best_spins = spins[:]
|
|
243
|
+
if timed_out:
|
|
244
|
+
break
|
|
245
|
+
completed_sweeps += 1
|
|
246
|
+
history.append(best_value)
|
|
247
|
+
|
|
248
|
+
return SolveResult(
|
|
249
|
+
status=SolveStatus.TIME_LIMIT if timed_out else SolveStatus.FEASIBLE,
|
|
250
|
+
best_solution=best_spins,
|
|
251
|
+
best_value=float(best_value),
|
|
252
|
+
solver=SolverIdentity(
|
|
253
|
+
"SimulatedQuantumAnnealing",
|
|
254
|
+
f"ascent-science-sqa {__version__}",
|
|
255
|
+
"Suzuki-Trotter Monte Carlo",
|
|
256
|
+
),
|
|
257
|
+
runtime_seconds=time.perf_counter() - started,
|
|
258
|
+
history=tuple(history),
|
|
259
|
+
metrics={
|
|
260
|
+
"trotter_slices": self.trotter_slices,
|
|
261
|
+
"completed_sweeps": completed_sweeps,
|
|
262
|
+
"beta": self.beta,
|
|
263
|
+
"gamma_start": self.gamma_start,
|
|
264
|
+
"gamma_end": self.gamma_end,
|
|
265
|
+
"schedule": self.schedule,
|
|
266
|
+
"accepted_moves": accepted,
|
|
267
|
+
"attempted_moves": attempted,
|
|
268
|
+
"acceptance_rate": accepted / attempted if attempted else 0.0,
|
|
269
|
+
},
|
|
270
|
+
execution=ExecutionInfo(location="local", backend="path-integral-monte-carlo"),
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def register():
|
|
275
|
+
return register_algorithm(
|
|
276
|
+
category="quantum",
|
|
277
|
+
exports={"SimulatedQuantumAnnealing": SimulatedQuantumAnnealing},
|
|
278
|
+
specification=AlgorithmSpec(
|
|
279
|
+
name="sqa",
|
|
280
|
+
family="quantum",
|
|
281
|
+
problem_types=("qubo", "ising"),
|
|
282
|
+
input_kind="qubo_or_ising",
|
|
283
|
+
output_kind="binary_or_spin",
|
|
284
|
+
factory=SimulatedQuantumAnnealing,
|
|
285
|
+
deterministic=False,
|
|
286
|
+
supports_initial_solution=True,
|
|
287
|
+
aliases=("simulated_quantum_annealing",),
|
|
288
|
+
description="Suzuki-Trotter path-integral simulated quantum annealing.",
|
|
289
|
+
),
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
__all__ = ["SimulatedQuantumAnnealing", "register"]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ascent-science-sqa
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Path-integral simulated quantum annealing for the Ascent Science SDK
|
|
5
|
+
Author: Ascent Science
|
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
9
|
+
Classifier: Typing :: Typed
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: ascent-science<0.10,>=0.9.4
|
|
13
|
+
|
|
14
|
+
# Ascent Science SQA plugin
|
|
15
|
+
|
|
16
|
+
This package implements simulated quantum annealing (SQA) with the standard
|
|
17
|
+
Suzuki--Trotter path-integral mapping and single-spin Metropolis updates. It is
|
|
18
|
+
a classical simulation of a transverse-field Ising model; it does not use a
|
|
19
|
+
quantum processor.
|
|
20
|
+
|
|
21
|
+
For development from this repository, install the core SDK and plugin in editable mode:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
python -m pip install -e ./sdk-python -e ./sdk-plugins/sqa
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
After the `0.1.0` package is published, it can instead be installed with
|
|
28
|
+
`python -m pip install ascent-science-sqa==0.1.0`.
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import ascent
|
|
32
|
+
|
|
33
|
+
solver = ascent.quantum.SimulatedQuantumAnnealing(
|
|
34
|
+
trotter_slices=32,
|
|
35
|
+
sweeps=500,
|
|
36
|
+
beta=5.0,
|
|
37
|
+
gamma_start=5.0,
|
|
38
|
+
gamma_end=0.01,
|
|
39
|
+
seed=42,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Minimize x.T Q x for x in {0, 1}^n.
|
|
43
|
+
result = solver.solve_qubo([
|
|
44
|
+
[-2.0, 1.0],
|
|
45
|
+
[1.0, -2.0],
|
|
46
|
+
])
|
|
47
|
+
print(result.best_solution, result.best_value)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
For an Ising model, `solve_ising(J, fields=h)` minimizes
|
|
51
|
+
`s.T @ J @ s + h.T @ s + offset`, with spins in `{-1, +1}`. For a canonical
|
|
52
|
+
problem object use `solver.solve(QuboProblem(...))` or
|
|
53
|
+
`solver.solve(IsingProblem(...))`.
|
|
54
|
+
|
|
55
|
+
SQA is stochastic and does not prove optimality. Results therefore have status
|
|
56
|
+
`FEASIBLE` (or `TIME_LIMIT` if a supplied `SolveOptions` limit is reached).
|
|
57
|
+
|
|
58
|
+
## Build a release
|
|
59
|
+
|
|
60
|
+
Use the same release flow as the ACO plugin:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
sh scripts/build-release.sh
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The wheel, source archive, and `SHA256SUMS` are written to `dist/`. If the
|
|
67
|
+
default Python does not provide a recent setuptools/build installation, select
|
|
68
|
+
another interpreter with `ASCENT_SQA_BUILD_PYTHON=/path/to/python`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/ascent_science_sqa/__init__.py
|
|
4
|
+
src/ascent_science_sqa.egg-info/PKG-INFO
|
|
5
|
+
src/ascent_science_sqa.egg-info/SOURCES.txt
|
|
6
|
+
src/ascent_science_sqa.egg-info/dependency_links.txt
|
|
7
|
+
src/ascent_science_sqa.egg-info/entry_points.txt
|
|
8
|
+
src/ascent_science_sqa.egg-info/requires.txt
|
|
9
|
+
src/ascent_science_sqa.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ascent-science<0.10,>=0.9.4
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ascent_science_sqa
|