qaoalib 0.1.11__tar.gz → 0.2.1__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.
- {qaoalib-0.1.11 → qaoalib-0.2.1}/PKG-INFO +8 -3
- {qaoalib-0.1.11 → qaoalib-0.2.1}/qaoalib/models/result.py +8 -0
- qaoalib-0.2.1/qaoalib/solver/__init__.py +1 -0
- qaoalib-0.2.1/qaoalib/solver/vqe.py +137 -0
- qaoalib-0.2.1/qaoalib/version.py +1 -0
- {qaoalib-0.1.11 → qaoalib-0.2.1}/qaoalib.egg-info/PKG-INFO +8 -3
- qaoalib-0.2.1/qaoalib.egg-info/SOURCES.txt +18 -0
- qaoalib-0.2.1/qaoalib.egg-info/requires.txt +10 -0
- {qaoalib-0.1.11 → qaoalib-0.2.1}/setup.py +8 -3
- qaoalib-0.2.1/test/test.py +5 -0
- qaoalib-0.1.11/qaoalib/qaoa/__init__.py +0 -3
- qaoalib-0.1.11/qaoalib/qaoa/landscape/__init__.py +0 -3
- qaoalib-0.1.11/qaoalib/qaoa/landscape/base.py +0 -90
- qaoalib-0.1.11/qaoalib/qaoa/landscape/direct_numpy.py +0 -79
- qaoalib-0.1.11/qaoalib/qaoa/landscape/hadamard_test.py +0 -39
- qaoalib-0.1.11/qaoalib/qaoa/landscape/hybrid_fast.py +0 -44
- qaoalib-0.1.11/qaoalib/qaoa/layerwise.py +0 -184
- qaoalib-0.1.11/qaoalib/qaoa/qis.py +0 -59
- qaoalib-0.1.11/qaoalib/qaoa/qmc.py +0 -51
- qaoalib-0.1.11/qaoalib/qaoa/strategy.py +0 -70
- qaoalib-0.1.11/qaoalib/qaoa/utils.py +0 -135
- qaoalib-0.1.11/qaoalib/version.py +0 -1
- qaoalib-0.1.11/qaoalib.egg-info/SOURCES.txt +0 -27
- qaoalib-0.1.11/qaoalib.egg-info/requires.txt +0 -5
- qaoalib-0.1.11/test/test.py +0 -3
- {qaoalib-0.1.11 → qaoalib-0.2.1}/LICENSE +0 -0
- {qaoalib-0.1.11 → qaoalib-0.2.1}/README.md +0 -0
- {qaoalib-0.1.11 → qaoalib-0.2.1}/qaoalib/__init__.py +0 -0
- {qaoalib-0.1.11 → qaoalib-0.2.1}/qaoalib/json.py +0 -0
- {qaoalib-0.1.11 → qaoalib-0.2.1}/qaoalib/math.py +0 -0
- {qaoalib-0.1.11 → qaoalib-0.2.1}/qaoalib/models/__init__.py +0 -0
- {qaoalib-0.1.11 → qaoalib-0.2.1}/qaoalib/utils.py +0 -0
- {qaoalib-0.1.11 → qaoalib-0.2.1}/qaoalib.egg-info/dependency_links.txt +0 -0
- {qaoalib-0.1.11 → qaoalib-0.2.1}/qaoalib.egg-info/top_level.txt +0 -0
- {qaoalib-0.1.11 → qaoalib-0.2.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: qaoalib
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A package for QAOA Maxcut calculations
|
|
5
5
|
Home-page: https://github.com/xenoicwyce/qaoalib
|
|
6
6
|
Author: Xinwei Lee
|
|
@@ -11,10 +11,15 @@ Classifier: Operating System :: OS Independent
|
|
|
11
11
|
Requires-Python: >=3.9
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
-
Requires-Dist:
|
|
14
|
+
Requires-Dist: qiskit
|
|
15
|
+
Requires-Dist: qiskit_optimization
|
|
16
|
+
Requires-Dist: qiskit_aer
|
|
17
|
+
Requires-Dist: numpy==1.26.0
|
|
18
|
+
Requires-Dist: scipy
|
|
19
|
+
Requires-Dist: pytest
|
|
20
|
+
Requires-Dist: pytest-cov
|
|
15
21
|
Requires-Dist: matplotlib
|
|
16
22
|
Requires-Dist: networkx
|
|
17
|
-
Requires-Dist: qiskit>=0.25.0
|
|
18
23
|
Requires-Dist: pydantic
|
|
19
24
|
|
|
20
25
|
# qaoalib
|
|
@@ -76,3 +76,11 @@ class MultipleTrialResult(BaseResult):
|
|
|
76
76
|
|
|
77
77
|
class ItlwResult(MultipleTrialResult):
|
|
78
78
|
pass
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class VQEResult(BaseResult):
|
|
82
|
+
expectations: list[float] = Field(default_factory=list)
|
|
83
|
+
nfevs: list[int] = Field(default_factory=list)
|
|
84
|
+
initial_params: list[list[float]] = Field(default_factory=list)
|
|
85
|
+
opt_params: list[list[float]] = Field(default_factory=list)
|
|
86
|
+
alphas: list[float] = Field(default_factory=list)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .vqe import VQE
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from qiskit import QuantumCircuit
|
|
4
|
+
from qiskit.circuit import ParameterVector
|
|
5
|
+
from qiskit.primitives import (
|
|
6
|
+
BackendSamplerV2 as BackendSampler,
|
|
7
|
+
BackendEstimatorV2 as BackendEstimator,
|
|
8
|
+
)
|
|
9
|
+
from qiskit.quantum_info import SparsePauliOp, Pauli
|
|
10
|
+
|
|
11
|
+
from qiskit_optimization import QuadraticProgram
|
|
12
|
+
from qiskit_optimization.converters import QuadraticProgramToQubo
|
|
13
|
+
from qiskit_optimization.algorithms import GurobiOptimizer
|
|
14
|
+
from qiskit_algorithms.optimizers import COBYLA, OptimizerResult
|
|
15
|
+
from qiskit_aer import AerSimulator
|
|
16
|
+
|
|
17
|
+
TWO_PI = 2 * np.pi
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class VQE:
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
quadratic_program: QuadraticProgram,
|
|
24
|
+
ansatz: QuantumCircuit = None,
|
|
25
|
+
reps: int = 1,
|
|
26
|
+
shots: int = None,
|
|
27
|
+
sampler=None,
|
|
28
|
+
estimator=None,
|
|
29
|
+
optimizer=None,
|
|
30
|
+
) -> None:
|
|
31
|
+
self.qp = quadratic_program
|
|
32
|
+
self.converter = QuadraticProgramToQubo()
|
|
33
|
+
self.qubo = self.converter.convert(self.qp)
|
|
34
|
+
|
|
35
|
+
hamiltonian, offset = self.qubo.to_ising()
|
|
36
|
+
self.hamiltonian: SparsePauliOp = hamiltonian
|
|
37
|
+
self.offset: float = offset
|
|
38
|
+
|
|
39
|
+
self.num_qubits = hamiltonian.num_qubits
|
|
40
|
+
self.ansatz = self.generate_full_ansatz(hamiltonian.num_qubits, reps) if ansatz is None else ansatz
|
|
41
|
+
|
|
42
|
+
backend_sv = AerSimulator(method='statevector')
|
|
43
|
+
backend_mps = AerSimulator(method='matrix_product_state')
|
|
44
|
+
self.sampler = BackendSampler(backend=backend_mps) if sampler is None else sampler
|
|
45
|
+
self.estimator = BackendEstimator(backend=backend_sv) if estimator is None else estimator
|
|
46
|
+
self.optimizer = COBYLA() if optimizer is None else optimizer
|
|
47
|
+
self.shots = shots
|
|
48
|
+
|
|
49
|
+
self.optimal_params = None
|
|
50
|
+
self.optimal_solution = None
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def generate_full_ansatz(num_qubits: int, reps: int) -> QuantumCircuit:
|
|
54
|
+
qc = QuantumCircuit(num_qubits)
|
|
55
|
+
theta = ParameterVector('θ', (reps + 1) * num_qubits)
|
|
56
|
+
|
|
57
|
+
for k in range(num_qubits):
|
|
58
|
+
qc.ry(theta[k], k)
|
|
59
|
+
|
|
60
|
+
for r in range(1, reps + 1):
|
|
61
|
+
for k in range(num_qubits):
|
|
62
|
+
qc.cz(k, (k + 1) % num_qubits)
|
|
63
|
+
|
|
64
|
+
for k in range(num_qubits):
|
|
65
|
+
qc.ry(theta[r * num_qubits + k], k)
|
|
66
|
+
|
|
67
|
+
return qc
|
|
68
|
+
|
|
69
|
+
def generate_random_params(self, scale=TWO_PI):
|
|
70
|
+
return np.random.rand(self.ansatz.num_parameters) * scale
|
|
71
|
+
|
|
72
|
+
def generate_pubs(
|
|
73
|
+
self,
|
|
74
|
+
params: list[float] | np.ndarray,
|
|
75
|
+
) -> list[tuple[QuantumCircuit, SparsePauliOp, np.ndarray]]:
|
|
76
|
+
return [(self.ansatz, self.hamiltonian, params)]
|
|
77
|
+
|
|
78
|
+
def compute_energy(self, params: list[float] | np.ndarray) -> float:
|
|
79
|
+
"""
|
|
80
|
+
Computes the sum of expectation of the ZZ terms.
|
|
81
|
+
"""
|
|
82
|
+
pubs = self.generate_pubs(params)
|
|
83
|
+
results = self.estimator.run(pubs).result()
|
|
84
|
+
evs = [result.data.evs for result in results]
|
|
85
|
+
return sum(evs)
|
|
86
|
+
|
|
87
|
+
def _sample_optimal_circuit(self) -> dict[str, int]:
|
|
88
|
+
"""
|
|
89
|
+
Run the full circuit with sampler to get the solution.
|
|
90
|
+
"""
|
|
91
|
+
if self.optimal_params is None:
|
|
92
|
+
raise ValueError('Problem not yet solved. Run LCCVQE.solve() to solve the problem.')
|
|
93
|
+
|
|
94
|
+
ansatz = self.ansatz.copy()
|
|
95
|
+
ansatz.measure_all()
|
|
96
|
+
result = self.sampler.run([(ansatz, self.optimal_params)], shots=self.shots).result()[0]
|
|
97
|
+
return result.data.meas.get_counts()
|
|
98
|
+
|
|
99
|
+
def _sample_most_likely(self) -> list[int]:
|
|
100
|
+
counts = self._sample_optimal_circuit()
|
|
101
|
+
highest_count = max(counts.values())
|
|
102
|
+
|
|
103
|
+
for bit_string, count in counts.items():
|
|
104
|
+
if count == highest_count:
|
|
105
|
+
return list(map(int, bit_string[::-1])) # flip the bit-string due to qiskit ordering
|
|
106
|
+
|
|
107
|
+
def get_qp_solution(self) -> list[float]:
|
|
108
|
+
qubo_solution = self._sample_most_likely()
|
|
109
|
+
return self.converter.interpret(qubo_solution)
|
|
110
|
+
|
|
111
|
+
def solve(
|
|
112
|
+
self,
|
|
113
|
+
initial_point: list[float] | np.ndarray = None,
|
|
114
|
+
run_sampler: bool = False,
|
|
115
|
+
) -> OptimizerResult:
|
|
116
|
+
"""
|
|
117
|
+
Calls the Scipy minimize function and returns the OptimizerResult object.
|
|
118
|
+
"""
|
|
119
|
+
if initial_point is None:
|
|
120
|
+
initial_point = self.generate_random_params()
|
|
121
|
+
else:
|
|
122
|
+
assert np.asarray(initial_point).shape[0] == self.ansatz.num_parameters, 'Parameter length does not match.'
|
|
123
|
+
|
|
124
|
+
def obj_func(params):
|
|
125
|
+
return self.compute_energy(params)
|
|
126
|
+
|
|
127
|
+
result = self.optimizer.minimize(obj_func, initial_point)
|
|
128
|
+
self.optimal_params = result.x
|
|
129
|
+
|
|
130
|
+
if run_sampler:
|
|
131
|
+
self.optimal_solution = self.get_qp_solution()
|
|
132
|
+
|
|
133
|
+
return result
|
|
134
|
+
|
|
135
|
+
def solve_gurobi(self) -> float:
|
|
136
|
+
result = GurobiOptimizer().solve(self.qp)
|
|
137
|
+
return result.fval
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.2.1'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: qaoalib
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A package for QAOA Maxcut calculations
|
|
5
5
|
Home-page: https://github.com/xenoicwyce/qaoalib
|
|
6
6
|
Author: Xinwei Lee
|
|
@@ -11,10 +11,15 @@ Classifier: Operating System :: OS Independent
|
|
|
11
11
|
Requires-Python: >=3.9
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
-
Requires-Dist:
|
|
14
|
+
Requires-Dist: qiskit
|
|
15
|
+
Requires-Dist: qiskit_optimization
|
|
16
|
+
Requires-Dist: qiskit_aer
|
|
17
|
+
Requires-Dist: numpy==1.26.0
|
|
18
|
+
Requires-Dist: scipy
|
|
19
|
+
Requires-Dist: pytest
|
|
20
|
+
Requires-Dist: pytest-cov
|
|
15
21
|
Requires-Dist: matplotlib
|
|
16
22
|
Requires-Dist: networkx
|
|
17
|
-
Requires-Dist: qiskit>=0.25.0
|
|
18
23
|
Requires-Dist: pydantic
|
|
19
24
|
|
|
20
25
|
# qaoalib
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
setup.py
|
|
4
|
+
qaoalib/__init__.py
|
|
5
|
+
qaoalib/json.py
|
|
6
|
+
qaoalib/math.py
|
|
7
|
+
qaoalib/utils.py
|
|
8
|
+
qaoalib/version.py
|
|
9
|
+
qaoalib.egg-info/PKG-INFO
|
|
10
|
+
qaoalib.egg-info/SOURCES.txt
|
|
11
|
+
qaoalib.egg-info/dependency_links.txt
|
|
12
|
+
qaoalib.egg-info/requires.txt
|
|
13
|
+
qaoalib.egg-info/top_level.txt
|
|
14
|
+
qaoalib/models/__init__.py
|
|
15
|
+
qaoalib/models/result.py
|
|
16
|
+
qaoalib/solver/__init__.py
|
|
17
|
+
qaoalib/solver/vqe.py
|
|
18
|
+
test/test.py
|
|
@@ -22,10 +22,15 @@ setup(
|
|
|
22
22
|
packages=find_packages(exclude=["test*"]),
|
|
23
23
|
python_requires=">=3.9",
|
|
24
24
|
install_requires=[
|
|
25
|
-
"
|
|
25
|
+
"qiskit",
|
|
26
|
+
"qiskit_optimization",
|
|
27
|
+
"qiskit_aer",
|
|
28
|
+
"numpy==1.26.0",
|
|
29
|
+
"scipy",
|
|
30
|
+
"pytest",
|
|
31
|
+
"pytest-cov",
|
|
26
32
|
"matplotlib",
|
|
27
33
|
"networkx",
|
|
28
|
-
"qiskit>=0.25.0",
|
|
29
34
|
"pydantic",
|
|
30
|
-
]
|
|
35
|
+
],
|
|
31
36
|
)
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
import matplotlib.pyplot as plt
|
|
3
|
-
|
|
4
|
-
DEFAULT_RTOL = 1e-5
|
|
5
|
-
DEFAULT_ATOL = 1e-8
|
|
6
|
-
|
|
7
|
-
class QmcLandscapeBase:
|
|
8
|
-
"""
|
|
9
|
-
This is a base class.
|
|
10
|
-
It should be only used for inhertiance.
|
|
11
|
-
"""
|
|
12
|
-
def __init__(self, G, prev_params=None):
|
|
13
|
-
if prev_params is not None:
|
|
14
|
-
if len(prev_params) % 2:
|
|
15
|
-
raise ValueError(f'Constructor failed. prev_params must have even length, got {len(prev_params)}.')
|
|
16
|
-
self.graph = G
|
|
17
|
-
self.num_qubits = len(G.nodes)
|
|
18
|
-
self.edge_list = list(G.edges)
|
|
19
|
-
self.prev_params = prev_params
|
|
20
|
-
self.grange = None
|
|
21
|
-
self.brange = None
|
|
22
|
-
self.exp_arr = None
|
|
23
|
-
self.depth = 1 if prev_params is None else len(prev_params)//2+1
|
|
24
|
-
self.npts = None
|
|
25
|
-
|
|
26
|
-
def _meshgrid(self):
|
|
27
|
-
gmin, gmax = min(self.grange), max(self.grange)
|
|
28
|
-
bmin, bmax = min(self.brange), max(self.brange)
|
|
29
|
-
gspace = np.linspace(gmin, gmax, self.npts)
|
|
30
|
-
bspace = np.linspace(bmin, bmax, self.npts)
|
|
31
|
-
return np.meshgrid(gspace, bspace)
|
|
32
|
-
|
|
33
|
-
def get_max(self, rtol=DEFAULT_RTOL, atol=DEFAULT_ATOL):
|
|
34
|
-
if self.exp_arr is None:
|
|
35
|
-
raise ValueError('Grid not found. Run create_grid() method first.')
|
|
36
|
-
|
|
37
|
-
gmesh, bmesh = self._meshgrid()
|
|
38
|
-
exp_max = np.max(self.exp_arr)
|
|
39
|
-
whr = np.where(np.isclose(self.exp_arr, exp_max, rtol=rtol, atol=atol))
|
|
40
|
-
indices = zip(whr[0], whr[1])
|
|
41
|
-
angle_list = [(gmesh[idx], bmesh[idx]) for idx in indices]
|
|
42
|
-
return (exp_max, angle_list)
|
|
43
|
-
|
|
44
|
-
def get_min(self, rtol=DEFAULT_RTOL, atol=DEFAULT_ATOL):
|
|
45
|
-
if self.exp_arr is None:
|
|
46
|
-
raise ValueError('Grid not found. Run create_grid() method first.')
|
|
47
|
-
|
|
48
|
-
gmesh, bmesh = self._meshgrid()
|
|
49
|
-
exp_min = np.min(self.exp_arr)
|
|
50
|
-
whr = np.where(np.isclose(self.exp_arr, exp_min, rtol=rtol, atol=atol))
|
|
51
|
-
indices = zip(whr[0], whr[1])
|
|
52
|
-
angle_list = [(gmesh[idx], bmesh[idx]) for idx in indices]
|
|
53
|
-
return (exp_min, angle_list)
|
|
54
|
-
|
|
55
|
-
def show_landscape(self, **plot_options):
|
|
56
|
-
defaults = {
|
|
57
|
-
'figsize': (16, 9),
|
|
58
|
-
}
|
|
59
|
-
defaults.update(plot_options)
|
|
60
|
-
figsize = defaults['figsize']
|
|
61
|
-
|
|
62
|
-
fig, ax = plt.subplots(subplot_kw={'projection': '3d'}, figsize=figsize)
|
|
63
|
-
|
|
64
|
-
if self.exp_arr is None:
|
|
65
|
-
raise ValueError('Grid not found. Run create_grid() method first.')
|
|
66
|
-
|
|
67
|
-
gmesh, bmesh = self._meshgrid()
|
|
68
|
-
surf = ax.plot_surface(gmesh, bmesh, self.exp_arr, cmap='coolwarm')
|
|
69
|
-
ax.set_xlabel('gamma')
|
|
70
|
-
ax.set_ylabel('beta')
|
|
71
|
-
ax.set_zlabel('expectation')
|
|
72
|
-
fig.colorbar(surf, shrink=.5)
|
|
73
|
-
|
|
74
|
-
plt.show()
|
|
75
|
-
|
|
76
|
-
def show_heatmap(self, legacy=False):
|
|
77
|
-
if self.exp_arr is None:
|
|
78
|
-
raise ValueError('Grid not found. Run create_grid() method first.')
|
|
79
|
-
|
|
80
|
-
if legacy:
|
|
81
|
-
plt.xlabel('norm(gamma_p)')
|
|
82
|
-
plt.ylabel('norm(beta_p)')
|
|
83
|
-
plt.imshow(self.exp_arr, cmap='coolwarm', origin='lower', extent=[0, 1, 0, 1])
|
|
84
|
-
else:
|
|
85
|
-
fig, ax = plt.subplots()
|
|
86
|
-
gmesh, bmesh = self._meshgrid()
|
|
87
|
-
ax.pcolormesh(gmesh, bmesh, self.exp_arr, cmap='coolwarm')
|
|
88
|
-
ax.set_xlabel('gamma')
|
|
89
|
-
ax.set_ylabel('beta')
|
|
90
|
-
plt.show()
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
|
|
3
|
-
from .base import QmcLandscapeBase
|
|
4
|
-
from ..utils import I, Z, plus
|
|
5
|
-
from ..utils import rx, make_params_vec
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class DirectNumpy(QmcLandscapeBase):
|
|
9
|
-
def __init__(self, G, prev_params=None):
|
|
10
|
-
super().__init__(G, prev_params)
|
|
11
|
-
self.plusxn = self._mplus()
|
|
12
|
-
self.hamiltonian = self._hmt()
|
|
13
|
-
|
|
14
|
-
def _mplus(self):
|
|
15
|
-
ans = plus
|
|
16
|
-
for q in range(1, self.num_qubits):
|
|
17
|
-
ans = np.kron(ans, plus)
|
|
18
|
-
return ans
|
|
19
|
-
|
|
20
|
-
def _hmt(self):
|
|
21
|
-
N = 2 ** self.num_qubits
|
|
22
|
-
ans = np.zeros((N, N))
|
|
23
|
-
for u, v in self.edge_list:
|
|
24
|
-
ans += np.eye(N) - self.tensor(Z, [u, v])
|
|
25
|
-
return ans/2
|
|
26
|
-
|
|
27
|
-
def tensor(self, u3, qubits):
|
|
28
|
-
if 0 in qubits:
|
|
29
|
-
ans = u3
|
|
30
|
-
else:
|
|
31
|
-
ans = I
|
|
32
|
-
|
|
33
|
-
for idx in range(1, self.num_qubits):
|
|
34
|
-
if idx in qubits:
|
|
35
|
-
ans = np.kron(ans, u3)
|
|
36
|
-
else:
|
|
37
|
-
ans = np.kron(ans, I)
|
|
38
|
-
return ans
|
|
39
|
-
|
|
40
|
-
def ucost(self, q1, q2, gamma):
|
|
41
|
-
return np.diag(np.exp(1j*gamma/2*np.diag(np.eye(2**self.num_qubits)-self.tensor(Z, [q1, q2]))))
|
|
42
|
-
|
|
43
|
-
def umixer_all(self, beta):
|
|
44
|
-
return self.tensor(rx(2*beta), list(range(self.num_qubits)))
|
|
45
|
-
|
|
46
|
-
def ansatz(self, gamma_vec, beta_vec):
|
|
47
|
-
ans = self.plusxn
|
|
48
|
-
for gamma, beta in zip(gamma_vec, beta_vec):
|
|
49
|
-
for u, v in self.edge_list:
|
|
50
|
-
ans = self.ucost(u, v, gamma) @ ans
|
|
51
|
-
ans = self.umixer_all(beta) @ ans
|
|
52
|
-
return ans
|
|
53
|
-
|
|
54
|
-
def expectation(self, params):
|
|
55
|
-
if self.plusxn is None or self.hamiltonian is None:
|
|
56
|
-
self.plusxn = self._mplus()
|
|
57
|
-
self.hamiltonian = self._hmt()
|
|
58
|
-
depth = len(params)//2
|
|
59
|
-
gamma_vec = params[:depth]
|
|
60
|
-
beta_vec = params[depth:]
|
|
61
|
-
ansatz = self.ansatz(gamma_vec, beta_vec)
|
|
62
|
-
ans = ansatz.conj().T @ self.hamiltonian @ ansatz
|
|
63
|
-
ans = ans[0][0]
|
|
64
|
-
if np.isclose(ans.real, np.abs(ans)):
|
|
65
|
-
return ans.real
|
|
66
|
-
else:
|
|
67
|
-
return ans
|
|
68
|
-
|
|
69
|
-
def create_grid(self, npts=100, gmin=0, gmax=2*np.pi, bmin=0, bmax=np.pi):
|
|
70
|
-
self.grange = (gmin, gmax)
|
|
71
|
-
self.brange = (bmin, bmax)
|
|
72
|
-
self.npts = npts
|
|
73
|
-
gmesh, bmesh = self._meshgrid()
|
|
74
|
-
gg = gmesh.reshape((-1,))
|
|
75
|
-
bb = bmesh.reshape((-1,))
|
|
76
|
-
|
|
77
|
-
exp_arr = np.array(list(map(self.expectation, make_params_vec(gg, bb, self.prev_params))))\
|
|
78
|
-
.reshape((npts, npts))
|
|
79
|
-
self.exp_arr = exp_arr
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
|
|
3
|
-
from .base import QmcLandscapeBase
|
|
4
|
-
from ..utils import interp, ht_expectation
|
|
5
|
-
from ..qis import hadamard_test_circuits, run_many_circuits
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class HadamardTest(QmcLandscapeBase):
|
|
9
|
-
def __init__(self, G, prev_params=None):
|
|
10
|
-
super().__init__(G, prev_params)
|
|
11
|
-
|
|
12
|
-
def get_circuits(self, params):
|
|
13
|
-
return hadamard_test_circuits(self.graph, params)
|
|
14
|
-
|
|
15
|
-
def expectation_grid(self, gspace, bspace, npts, prev_params=None):
|
|
16
|
-
qc_list = []
|
|
17
|
-
for beta in bspace:
|
|
18
|
-
for gamma in gspace:
|
|
19
|
-
if prev_params is None:
|
|
20
|
-
qc_list += self.get_circuits([gamma, beta])
|
|
21
|
-
else:
|
|
22
|
-
qc_list += self.get_circuits(interp(prev_params, [gamma, beta]))
|
|
23
|
-
|
|
24
|
-
sv_list = run_many_circuits(qc_list)
|
|
25
|
-
exp_arr = np.array(list(map(ht_expectation, sv_list)))\
|
|
26
|
-
.reshape((npts*npts, len(self.edge_list)))
|
|
27
|
-
exp_arr = np.sum(exp_arr, axis=1).reshape((npts, npts))
|
|
28
|
-
return exp_arr
|
|
29
|
-
|
|
30
|
-
def create_grid(self, npts=100, gmin=0, gmax=2*np.pi, bmin=0, bmax=np.pi):
|
|
31
|
-
self.grange = (gmin, gmax)
|
|
32
|
-
self.brange = (bmin, bmax)
|
|
33
|
-
self.npts = npts
|
|
34
|
-
gmesh, bmesh = self._meshgrid()
|
|
35
|
-
|
|
36
|
-
gspace = np.linspace(gmin, gmax, npts)
|
|
37
|
-
bspace = np.linspace(bmin, bmax, npts)
|
|
38
|
-
exp_arr = self.expectation_grid(gspace, bspace, npts, self.prev_params)
|
|
39
|
-
self.exp_arr = exp_arr
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
|
|
3
|
-
from .base import QmcLandscapeBase
|
|
4
|
-
from ..utils import I, Z, interp
|
|
5
|
-
from ..qis import qaoa_circuit, run_many_circuits
|
|
6
|
-
from ...math import fast_kron
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class HybridFast(QmcLandscapeBase):
|
|
10
|
-
def __init__(self, G, prev_params=None):
|
|
11
|
-
super().__init__(G, prev_params)
|
|
12
|
-
|
|
13
|
-
def get_circuit(self, params):
|
|
14
|
-
return qaoa_circuit(self.graph, params)
|
|
15
|
-
|
|
16
|
-
def _fast_kron_exp(self, sv):
|
|
17
|
-
sum_ = 0
|
|
18
|
-
for edge in self.edge_list:
|
|
19
|
-
kron_list = [Z if i in edge else I for i in range(self.num_qubits)]
|
|
20
|
-
kron_list.reverse()
|
|
21
|
-
sum_ += (sv.conj().T @ fast_kron(kron_list, sv)).item().real
|
|
22
|
-
# return a single expectation value
|
|
23
|
-
return (len(self.edge_list) - sum_)/2
|
|
24
|
-
|
|
25
|
-
def expectation_grid(self, gspace, bspace, npts, prev_params=None):
|
|
26
|
-
if prev_params is None:
|
|
27
|
-
qc_list = [self.get_circuit([gamma, beta]) for beta in bspace for gamma in gspace]
|
|
28
|
-
else:
|
|
29
|
-
qc_list = [self.get_circuit(interp(prev_params, [gamma, beta]))
|
|
30
|
-
for beta in bspace for gamma in gspace]
|
|
31
|
-
ansatz_list = run_many_circuits(qc_list)
|
|
32
|
-
exp_arr = np.array(list(map(self._fast_kron_exp, ansatz_list))).reshape((npts, npts))
|
|
33
|
-
return exp_arr
|
|
34
|
-
|
|
35
|
-
def create_grid(self, npts=100, gmin=0, gmax=2*np.pi, bmin=0, bmax=np.pi):
|
|
36
|
-
self.grange = (gmin, gmax)
|
|
37
|
-
self.brange = (bmin, bmax)
|
|
38
|
-
self.npts = npts
|
|
39
|
-
gmesh, bmesh = self._meshgrid()
|
|
40
|
-
|
|
41
|
-
gspace = np.linspace(gmin, gmax, npts)
|
|
42
|
-
bspace = np.linspace(bmin, bmax, npts)
|
|
43
|
-
exp_arr = self.expectation_grid(gspace, bspace, npts, self.prev_params)
|
|
44
|
-
self.exp_arr = exp_arr
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import pickle
|
|
2
|
-
import numpy as np
|
|
3
|
-
import matplotlib.pyplot as plt
|
|
4
|
-
|
|
5
|
-
from matplotlib import cm
|
|
6
|
-
|
|
7
|
-
from .utils import I, Z
|
|
8
|
-
from .utils import rx
|
|
9
|
-
|
|
10
|
-
class Layerwise:
|
|
11
|
-
def __init__(self, G):
|
|
12
|
-
self.graph = G
|
|
13
|
-
self.num_qubits = len(G.nodes)
|
|
14
|
-
self.edge_list = list(G.edges)
|
|
15
|
-
self.hamiltonian = self.get_maxcut_hmt()
|
|
16
|
-
self.maxcut = np.max(self.hamiltonian)
|
|
17
|
-
self.best_ansatz = self.plusxn()
|
|
18
|
-
self.best_params = None
|
|
19
|
-
self.exp_arr = None
|
|
20
|
-
self.npts = None
|
|
21
|
-
self.gmesh = None
|
|
22
|
-
self.bmesh = None
|
|
23
|
-
self.max_exps = None
|
|
24
|
-
self.depth = 0
|
|
25
|
-
|
|
26
|
-
def tensor_prod(self, u3, qubits):
|
|
27
|
-
if 0 in qubits:
|
|
28
|
-
ans = u3
|
|
29
|
-
else:
|
|
30
|
-
ans = I
|
|
31
|
-
|
|
32
|
-
for idx in range(1, self.num_qubits):
|
|
33
|
-
if idx in qubits:
|
|
34
|
-
ans = np.kron(ans, u3)
|
|
35
|
-
else:
|
|
36
|
-
ans = np.kron(ans, I)
|
|
37
|
-
return ans
|
|
38
|
-
|
|
39
|
-
def plusxn(self):
|
|
40
|
-
N = 2 ** self.num_qubits
|
|
41
|
-
return np.ones((N, 1))/np.sqrt(N)
|
|
42
|
-
|
|
43
|
-
def get_maxcut_hmt(self):
|
|
44
|
-
N = 2 ** self.num_qubits
|
|
45
|
-
ans = np.zeros((N, N))
|
|
46
|
-
for u, v in self.edge_list:
|
|
47
|
-
ans += np.eye(N) - self.tensor_prod(Z, [u, v])
|
|
48
|
-
return ans/2
|
|
49
|
-
|
|
50
|
-
def ehz(self, gamma):
|
|
51
|
-
eigs = np.diag(self.hamiltonian)
|
|
52
|
-
return np.diag(np.exp(1j*gamma/2*eigs))
|
|
53
|
-
|
|
54
|
-
def ehx(self, beta):
|
|
55
|
-
return self.tensor_prod(rx(2*beta), list(range(self.num_qubits)))
|
|
56
|
-
|
|
57
|
-
def ansatz(self, gamma, beta):
|
|
58
|
-
ans = self.best_ansatz[:, -1][:, np.newaxis]
|
|
59
|
-
return self.ehx(beta) @ self.ehz(gamma) @ ans
|
|
60
|
-
|
|
61
|
-
def expectation(self, gamma, beta):
|
|
62
|
-
right = self.ansatz(gamma, beta)
|
|
63
|
-
left = right.conj().T
|
|
64
|
-
return (left @ self.hamiltonian @ right).real
|
|
65
|
-
|
|
66
|
-
def create_grid(self, npts, gmin=0, gmax=2*np.pi, bmin=0, bmax=np.pi):
|
|
67
|
-
grange = np.linspace(gmin, gmax, npts)
|
|
68
|
-
brange = np.linspace(bmin, bmax, npts)
|
|
69
|
-
gmesh, bmesh = np.meshgrid(grange, brange)
|
|
70
|
-
gg = gmesh.reshape((-1,))
|
|
71
|
-
bb = bmesh.reshape((-1,))
|
|
72
|
-
|
|
73
|
-
exp_arr = np.array(list(map(self.expectation, gg, bb)))\
|
|
74
|
-
.reshape((npts, npts))
|
|
75
|
-
|
|
76
|
-
self.npts = npts
|
|
77
|
-
self.gmesh = gmesh
|
|
78
|
-
self.bmesh = bmesh
|
|
79
|
-
if self.exp_arr is None:
|
|
80
|
-
self.exp_arr = exp_arr[:, :, np.newaxis]
|
|
81
|
-
else:
|
|
82
|
-
self.exp_arr = np.dstack((self.exp_arr, exp_arr))
|
|
83
|
-
|
|
84
|
-
# def get_max(self, p):
|
|
85
|
-
# if self.exp_arr is None:
|
|
86
|
-
# raise ValueError('Grid not found. Run create_grid() method first.')
|
|
87
|
-
|
|
88
|
-
# exp_arr = self.exp_arr[:, :, p-1]
|
|
89
|
-
# max_exp = np.max(exp_arr)
|
|
90
|
-
# whr = np.where(np.isclose(exp_arr, max_exp))
|
|
91
|
-
# indices = zip(whr[0], whr[1])
|
|
92
|
-
# angle_list = [(self.gmesh[idx], self.bmesh[idx]) for idx in indices]
|
|
93
|
-
# return (max_exp, angle_list)
|
|
94
|
-
|
|
95
|
-
def find_args(self, p, value):
|
|
96
|
-
""" Find the nearest args given a value. """
|
|
97
|
-
|
|
98
|
-
if self.exp_arr is None:
|
|
99
|
-
raise ValueError('Grid not found. Run create_grid() method first.')
|
|
100
|
-
|
|
101
|
-
dist_arr = np.abs(self.exp_arr[:, :, p-1] - value)
|
|
102
|
-
nearest = np.min(dist_arr)
|
|
103
|
-
whr = np.where(np.isclose(dist_arr, nearest))
|
|
104
|
-
indices = zip(whr[0], whr[1])
|
|
105
|
-
angle_list = [(self.gmesh[idx], self.bmesh[idx]) for idx in indices]
|
|
106
|
-
return angle_list
|
|
107
|
-
|
|
108
|
-
def run(self, p_end, npts=50, cutoff=1.0):
|
|
109
|
-
for i in range(1, p_end + 1):
|
|
110
|
-
print(f'Creating grid for p={i}')
|
|
111
|
-
self.create_grid(npts)
|
|
112
|
-
max_exp = np.max(self.exp_arr[:, :, i-1])
|
|
113
|
-
best_params = self.find_args(i, cutoff * max_exp)[0] # take only one pair of angles
|
|
114
|
-
|
|
115
|
-
if self.max_exps is None:
|
|
116
|
-
self.max_exps = max_exp
|
|
117
|
-
else:
|
|
118
|
-
self.max_exps = np.hstack((self.max_exps, max_exp))
|
|
119
|
-
|
|
120
|
-
if self.best_params is None:
|
|
121
|
-
self.best_params = np.array(best_params)
|
|
122
|
-
else:
|
|
123
|
-
self.best_params = np.column_stack((self.best_params, best_params))
|
|
124
|
-
|
|
125
|
-
best_ansatz = self.ansatz(best_params[0], best_params[1])
|
|
126
|
-
self.best_ansatz = np.hstack((self.best_ansatz, best_ansatz))
|
|
127
|
-
|
|
128
|
-
self.depth += 1
|
|
129
|
-
|
|
130
|
-
def show_landscape(self, p, **plot_options):
|
|
131
|
-
defaults = {
|
|
132
|
-
'figsize': (16, 9),
|
|
133
|
-
}
|
|
134
|
-
defaults.update(plot_options)
|
|
135
|
-
figsize = defaults['figsize']
|
|
136
|
-
|
|
137
|
-
fig, ax = plt.subplots(subplot_kw={'projection': '3d'}, figsize=figsize)
|
|
138
|
-
|
|
139
|
-
if self.gmesh is None or self.bmesh is None or self.exp_arr is None:
|
|
140
|
-
raise ValueError('Grid not found. Run create_grid() method first.')
|
|
141
|
-
|
|
142
|
-
exp_arr = self.exp_arr[:, :, p-1]
|
|
143
|
-
surf = ax.plot_surface(self.gmesh, self.bmesh, exp_arr, cmap=cm.coolwarm)
|
|
144
|
-
ax.set_xlabel('gamma')
|
|
145
|
-
ax.set_ylabel('beta')
|
|
146
|
-
ax.set_zlabel('expectation')
|
|
147
|
-
fig.colorbar(surf, shrink=.5)
|
|
148
|
-
|
|
149
|
-
plt.show()
|
|
150
|
-
|
|
151
|
-
def show_heatmap(self, p):
|
|
152
|
-
if self.exp_arr is None:
|
|
153
|
-
raise ValueError('Grid not found. Run create_grid() method first.')
|
|
154
|
-
|
|
155
|
-
exp_arr = self.exp_arr[:, :, p-1]
|
|
156
|
-
plt.xlabel('gamma_p/2pi')
|
|
157
|
-
plt.ylabel('beta_p/pi')
|
|
158
|
-
plt.imshow(exp_arr, cmap=cm.coolwarm, origin='lower', extent=[0, 1, 0, 1])
|
|
159
|
-
|
|
160
|
-
def plot_alpha(self, alpha=True):
|
|
161
|
-
p_range = range(1, self.depth + 1)
|
|
162
|
-
|
|
163
|
-
fig, ax = plt.subplots()
|
|
164
|
-
ax.set_xlabel('Circuit depth, $p$')
|
|
165
|
-
ax.grid(True)
|
|
166
|
-
ax.set_xticks(p_range)
|
|
167
|
-
if alpha:
|
|
168
|
-
ax.set_ylabel('Approx. ratio')
|
|
169
|
-
ax.plot(p_range, self.max_exps/self.maxcut, marker='.')
|
|
170
|
-
else:
|
|
171
|
-
ax.set_ylabel('Expectation')
|
|
172
|
-
ax.plot(p_range, self.max_exps, marker='.')
|
|
173
|
-
|
|
174
|
-
plt.show()
|
|
175
|
-
|
|
176
|
-
def status(self):
|
|
177
|
-
if not self.depth:
|
|
178
|
-
print('Paramters fixing not yet executed.')
|
|
179
|
-
else:
|
|
180
|
-
print(f'Parameters fixing executed up to p={self.depth}.')
|
|
181
|
-
|
|
182
|
-
def dump(self, path):
|
|
183
|
-
with open(path, 'wb') as f:
|
|
184
|
-
pickle.dump(self, f)
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
from qiskit import QuantumRegister, QuantumCircuit
|
|
2
|
-
from qiskit import Aer, execute
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
sv_backend = Aer.get_backend('statevector_simulator')
|
|
6
|
-
aer_backend = Aer.get_backend('aer_simulator')
|
|
7
|
-
|
|
8
|
-
def qaoa_circuit(G, params):
|
|
9
|
-
"""
|
|
10
|
-
QAOA circuit for Max-cut \sum(I-ZZ). No longer follow qiskit QAOA circuit.
|
|
11
|
-
If want to reproduce the circuit used in qiskit \sum(-ZZ), substitute
|
|
12
|
-
gamma as -gamma will do.
|
|
13
|
-
"""
|
|
14
|
-
depth = len(params)//2
|
|
15
|
-
gamma = params[:depth]
|
|
16
|
-
beta = params[depth:]
|
|
17
|
-
|
|
18
|
-
q = QuantumRegister(len(G.nodes))
|
|
19
|
-
qc = QuantumCircuit(q)
|
|
20
|
-
|
|
21
|
-
qc.h(q)
|
|
22
|
-
for p in range(depth):
|
|
23
|
-
for u, v, d in G.edges(data=True):
|
|
24
|
-
weight = d.get('weight', 1)
|
|
25
|
-
qc.cx(u, v)
|
|
26
|
-
qc.rz(-gamma[p]*weight, v)
|
|
27
|
-
qc.cx(u, v)
|
|
28
|
-
qc.rx(2*beta[p], q)
|
|
29
|
-
return qc
|
|
30
|
-
|
|
31
|
-
def hadamard_test_circuits(G, params):
|
|
32
|
-
"""
|
|
33
|
-
Return a list of QuantumCircuit's for hadamard tests.
|
|
34
|
-
"""
|
|
35
|
-
qc_list = []
|
|
36
|
-
ansatz_circ = qaoa_circuit(G, params)
|
|
37
|
-
|
|
38
|
-
for u, v in G.edges:
|
|
39
|
-
q = QuantumRegister(1)
|
|
40
|
-
ansatz = QuantumRegister(ansatz_circ.num_qubits)
|
|
41
|
-
qc = QuantumCircuit(q, ansatz)
|
|
42
|
-
|
|
43
|
-
qc.h(q)
|
|
44
|
-
qc.append(ansatz_circ, ansatz)
|
|
45
|
-
qc.cz(q, ansatz[u])
|
|
46
|
-
qc.cz(q, ansatz[v])
|
|
47
|
-
qc.h(q)
|
|
48
|
-
|
|
49
|
-
# bit reversal is required to calculate probabilities
|
|
50
|
-
qc = qc.reverse_bits()
|
|
51
|
-
qc_list.append(qc)
|
|
52
|
-
|
|
53
|
-
return qc_list
|
|
54
|
-
|
|
55
|
-
def run_many_circuits(qc_list):
|
|
56
|
-
job = execute(qc_list, sv_backend)
|
|
57
|
-
result = job.result()
|
|
58
|
-
# return a list of np.ndarray of statevectors
|
|
59
|
-
return [result.get_statevector(idx) for idx in range(len(qc_list))]
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
from qiskit import Aer, execute
|
|
3
|
-
|
|
4
|
-
from .qis import qaoa_circuit, sv_backend
|
|
5
|
-
from .utils import expectation, I, Z
|
|
6
|
-
from ..math import fast_kron
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Qmc:
|
|
10
|
-
def __init__(self, G, params):
|
|
11
|
-
self.graph = G
|
|
12
|
-
self.params = params
|
|
13
|
-
self.circuit = qaoa_circuit(self.graph, self.params)
|
|
14
|
-
self.result = None
|
|
15
|
-
self.expectation = None
|
|
16
|
-
|
|
17
|
-
def run(self, backend_name='qasm_simulator', **execute_kw):
|
|
18
|
-
qc = self.circuit
|
|
19
|
-
if backend_name == 'qasm_simulator':
|
|
20
|
-
qc.measure_all()
|
|
21
|
-
|
|
22
|
-
backend = Aer.get_backend(backend_name)
|
|
23
|
-
job = execute(self.circuit, backend, **execute_kw)
|
|
24
|
-
result = job.result()
|
|
25
|
-
if backend_name == 'qasm_simulator':
|
|
26
|
-
self.result = result.get_counts()
|
|
27
|
-
elif backend_name == 'statevector_simulator':
|
|
28
|
-
self.result = np.asarray(result.get_statevector())
|
|
29
|
-
|
|
30
|
-
self.expectation = expectation(self.graph, self.result)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class QmcFastKron(Qmc):
|
|
34
|
-
def __init__(self, G, params):
|
|
35
|
-
super().__init__(G, params)
|
|
36
|
-
|
|
37
|
-
def _fast_kron_exp(self, sv):
|
|
38
|
-
sum_ = 0
|
|
39
|
-
for u, v, d in self.graph.edges(data=True):
|
|
40
|
-
edge = (u,v)
|
|
41
|
-
kron_list = [Z if i in edge else I for i in range(len(self.graph.nodes))]
|
|
42
|
-
kron_list.reverse()
|
|
43
|
-
weight = d.get('weight', 1)
|
|
44
|
-
sum_ += weight * (1 - (sv.conj().T @ fast_kron(kron_list, sv)).item().real)
|
|
45
|
-
# return a single expectation value
|
|
46
|
-
return sum_/2
|
|
47
|
-
|
|
48
|
-
def run(self, **execute_kw):
|
|
49
|
-
job = execute(self.circuit, sv_backend, **execute_kw) # currently only support sv type
|
|
50
|
-
self.result = job.result().get_statevector()
|
|
51
|
-
self.expectation = self._fast_kron_exp(self.result)
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
from typing import TYPE_CHECKING, Optional, Union, Sequence
|
|
3
|
-
from typing_extensions import TypeAlias
|
|
4
|
-
# from .params import Params
|
|
5
|
-
|
|
6
|
-
if TYPE_CHECKING:
|
|
7
|
-
import numpy.typing as npt
|
|
8
|
-
|
|
9
|
-
Params: TypeAlias = Union[Sequence[float], "npt.NDArray[np.float_]"]
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def interp():
|
|
13
|
-
...
|
|
14
|
-
|
|
15
|
-
def bilinear(
|
|
16
|
-
prev_params: Params,
|
|
17
|
-
pp_params: Params,
|
|
18
|
-
gamma_bound: Optional[tuple[float, float]] = None,
|
|
19
|
-
beta_bound: Optional[tuple[float, float]] = None,
|
|
20
|
-
) -> "npt.NDArray[np.float_]":
|
|
21
|
-
if gamma_bound is None:
|
|
22
|
-
gamma_bound = (0, np.pi)
|
|
23
|
-
if beta_bound is None:
|
|
24
|
-
beta_bound = (0, np.pi/2)
|
|
25
|
-
|
|
26
|
-
prev_params = np.asarray(prev_params)
|
|
27
|
-
pp_params = np.asarray(pp_params) # prev_prev_params (p-2)
|
|
28
|
-
|
|
29
|
-
# split into gamma and beta
|
|
30
|
-
prev_gb = np.split(prev_params, 2)
|
|
31
|
-
pp_gb = np.split(pp_params, 2)
|
|
32
|
-
|
|
33
|
-
new_gb = []
|
|
34
|
-
for i in range(2):
|
|
35
|
-
delta2 = prev_gb[i][-2] - pp_gb[i][-1]
|
|
36
|
-
pp_gb[i] = np.hstack([pp_gb[i], prev_gb[i][-1] - delta2])
|
|
37
|
-
|
|
38
|
-
diff = prev_gb[i] - pp_gb[i]
|
|
39
|
-
new_x = prev_gb[i] + diff
|
|
40
|
-
|
|
41
|
-
delta3 = new_x[-1] - new_x[-2]
|
|
42
|
-
new_x = np.hstack([new_x, new_x[-1] + delta3])
|
|
43
|
-
|
|
44
|
-
if i == 0:
|
|
45
|
-
new_x = np.clip(new_x, *gamma_bound)
|
|
46
|
-
else:
|
|
47
|
-
new_x = np.clip(new_x, *beta_bound)
|
|
48
|
-
|
|
49
|
-
new_gb.append(new_x)
|
|
50
|
-
|
|
51
|
-
return np.hstack(new_gb)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def params_fixing(
|
|
55
|
-
prev_params: Params,
|
|
56
|
-
gamma_bound: Optional[tuple[float, float]] = None,
|
|
57
|
-
beta_bound: Optional[tuple[float, float]] = None,
|
|
58
|
-
) -> "npt.NDArray[np.float_]":
|
|
59
|
-
if gamma_bound is None:
|
|
60
|
-
gamma_bound = (0, np.pi)
|
|
61
|
-
if beta_bound is None:
|
|
62
|
-
beta_bound = (0, np.pi/2)
|
|
63
|
-
|
|
64
|
-
prev_params = np.asarray(prev_params)
|
|
65
|
-
gamma, beta = np.split(prev_params)
|
|
66
|
-
|
|
67
|
-
gamma = np.hstack([gamma, np.random.uniform(*gamma_bound)])
|
|
68
|
-
beta = np.hstack([beta, np.random.uniform(*beta_bound)])
|
|
69
|
-
|
|
70
|
-
return np.hstack([gamma, beta])
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
# globals
|
|
5
|
-
I = np.eye(2)
|
|
6
|
-
X = np.array([
|
|
7
|
-
[0, 1],
|
|
8
|
-
[1, 0],
|
|
9
|
-
])
|
|
10
|
-
Z = np.array([
|
|
11
|
-
[1, 0],
|
|
12
|
-
[0, -1],
|
|
13
|
-
])
|
|
14
|
-
cnot = np.array([
|
|
15
|
-
[1, 0, 0, 0],
|
|
16
|
-
[0, 1, 0, 0],
|
|
17
|
-
[0, 0, 0, 1],
|
|
18
|
-
[0, 0, 1, 0],
|
|
19
|
-
])
|
|
20
|
-
plus = np.ones((2, 1)) * 1/np.sqrt(2)
|
|
21
|
-
|
|
22
|
-
# functions
|
|
23
|
-
def split_gb(params):
|
|
24
|
-
depth = len(params)//2
|
|
25
|
-
gamma = params[:depth]
|
|
26
|
-
beta = params[depth:]
|
|
27
|
-
return gamma, beta
|
|
28
|
-
|
|
29
|
-
def random_qaoa_params(p, grange=None, brange=None):
|
|
30
|
-
"""Generate random QAOA parameters."""
|
|
31
|
-
if grange is None:
|
|
32
|
-
grange = (0, 2*np.pi)
|
|
33
|
-
if brange is None:
|
|
34
|
-
brange = (0, np.pi)
|
|
35
|
-
|
|
36
|
-
rng = np.random.default_rng()
|
|
37
|
-
# rng.uniform(low=0., high=1., size=None)
|
|
38
|
-
grand = rng.uniform(grange[0], grange[1], p)
|
|
39
|
-
brand = rng.uniform(brange[0], brange[1], p)
|
|
40
|
-
return np.hstack([grand, brand])
|
|
41
|
-
|
|
42
|
-
def interp(old_params, new_params):
|
|
43
|
-
gamma, beta = split_gb(old_params)
|
|
44
|
-
new_gamma, new_beta = split_gb(new_params)
|
|
45
|
-
return np.hstack((gamma, new_gamma, beta, new_beta)).tolist()
|
|
46
|
-
|
|
47
|
-
def interp_rand(params, grange=None, brange=None):
|
|
48
|
-
"""
|
|
49
|
-
Adds a new random angle to the params array, and return
|
|
50
|
-
the params required by (p+1).
|
|
51
|
-
"""
|
|
52
|
-
if grange is None:
|
|
53
|
-
grange = (0, 2*np.pi)
|
|
54
|
-
if brange is None:
|
|
55
|
-
brange = (0, np.pi)
|
|
56
|
-
|
|
57
|
-
depth = len(params)//2
|
|
58
|
-
gamma = params[:depth]
|
|
59
|
-
beta = params[depth:]
|
|
60
|
-
|
|
61
|
-
rng = np.random.default_rng()
|
|
62
|
-
grand = rng.uniform(grange[0], grange[1], 1)
|
|
63
|
-
brand = rng.uniform(brange[0], brange[1], 1)
|
|
64
|
-
return np.hstack([gamma, grand, beta, brand])
|
|
65
|
-
|
|
66
|
-
def rx(theta):
|
|
67
|
-
return np.array([
|
|
68
|
-
[np.cos(theta/2), -1j*np.sin(theta/2)],
|
|
69
|
-
[-1j*np.sin(theta/2), np.cos(theta/2)],
|
|
70
|
-
])
|
|
71
|
-
|
|
72
|
-
def rz(theta):
|
|
73
|
-
return np.array([
|
|
74
|
-
[np.exp(-1j*theta/2), 0],
|
|
75
|
-
[0, np.exp(1j*theta/2)],
|
|
76
|
-
])
|
|
77
|
-
|
|
78
|
-
def make_params_vec(gamma1d, beta1d, prev_params=None):
|
|
79
|
-
if prev_params is None:
|
|
80
|
-
return zip(gamma1d, beta1d)
|
|
81
|
-
|
|
82
|
-
npts = gamma1d.shape[0]
|
|
83
|
-
depth = len(prev_params)//2
|
|
84
|
-
prev_gamma = prev_params[:depth]
|
|
85
|
-
prev_beta = prev_params[depth:]
|
|
86
|
-
|
|
87
|
-
gamma_list = (np.zeros((npts, depth)) + np.array(prev_gamma)).T.tolist()
|
|
88
|
-
beta_list = (np.zeros((npts, depth)) + np.array(prev_beta)).T.tolist()
|
|
89
|
-
|
|
90
|
-
return zip(*gamma_list, gamma1d, *beta_list, beta1d)
|
|
91
|
-
|
|
92
|
-
def ht_expectation(sv):
|
|
93
|
-
zero, one = np.split(sv, 2)
|
|
94
|
-
zero_prob = np.sum(np.abs(zero)**2)
|
|
95
|
-
one_prob = np.sum(np.abs(one)**2)
|
|
96
|
-
return zero_prob - one_prob
|
|
97
|
-
|
|
98
|
-
def _cut_value(G, eigenstate):
|
|
99
|
-
eigenstate = eigenstate[::-1]
|
|
100
|
-
cut = 0
|
|
101
|
-
for u, v, d in G.edges(data=True):
|
|
102
|
-
if eigenstate[u] != eigenstate[v]:
|
|
103
|
-
weight = d.get('weight', 1)
|
|
104
|
-
cut += weight
|
|
105
|
-
return cut
|
|
106
|
-
|
|
107
|
-
def _sv2dict(sv):
|
|
108
|
-
num_qubits = np.log2(sv.shape[0])
|
|
109
|
-
if num_qubits % 1:
|
|
110
|
-
raise ValueError('Input vector is not a valid statevector.')
|
|
111
|
-
num_qubits = int(num_qubits)
|
|
112
|
-
|
|
113
|
-
sv_dict = {
|
|
114
|
-
f'{idx:0{num_qubits}b}': sv[idx]
|
|
115
|
-
for idx in range(2**num_qubits)
|
|
116
|
-
}
|
|
117
|
-
return sv_dict
|
|
118
|
-
|
|
119
|
-
def expectation(G, counts_or_sv):
|
|
120
|
-
sum_ = 0
|
|
121
|
-
if isinstance(counts_or_sv, dict):
|
|
122
|
-
counts = counts_or_sv
|
|
123
|
-
total = sum(counts.values())
|
|
124
|
-
for eigs, count in counts.items():
|
|
125
|
-
sum_ += _cut_value(G, eigs) * count / total
|
|
126
|
-
return sum_
|
|
127
|
-
|
|
128
|
-
elif isinstance(counts_or_sv, np.ndarray):
|
|
129
|
-
sv = _sv2dict(counts_or_sv)
|
|
130
|
-
for eigs, count in sv.items():
|
|
131
|
-
sum_ += _cut_value(G, eigs) * (np.abs(sv[eigs])**2)
|
|
132
|
-
return sum_
|
|
133
|
-
|
|
134
|
-
else:
|
|
135
|
-
raise TypeError(f'Unrecognized type {type(counts_or_sv)}.')
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '0.1.11'
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
LICENSE
|
|
2
|
-
README.md
|
|
3
|
-
setup.py
|
|
4
|
-
qaoalib/__init__.py
|
|
5
|
-
qaoalib/json.py
|
|
6
|
-
qaoalib/math.py
|
|
7
|
-
qaoalib/utils.py
|
|
8
|
-
qaoalib/version.py
|
|
9
|
-
qaoalib.egg-info/PKG-INFO
|
|
10
|
-
qaoalib.egg-info/SOURCES.txt
|
|
11
|
-
qaoalib.egg-info/dependency_links.txt
|
|
12
|
-
qaoalib.egg-info/requires.txt
|
|
13
|
-
qaoalib.egg-info/top_level.txt
|
|
14
|
-
qaoalib/models/__init__.py
|
|
15
|
-
qaoalib/models/result.py
|
|
16
|
-
qaoalib/qaoa/__init__.py
|
|
17
|
-
qaoalib/qaoa/layerwise.py
|
|
18
|
-
qaoalib/qaoa/qis.py
|
|
19
|
-
qaoalib/qaoa/qmc.py
|
|
20
|
-
qaoalib/qaoa/strategy.py
|
|
21
|
-
qaoalib/qaoa/utils.py
|
|
22
|
-
qaoalib/qaoa/landscape/__init__.py
|
|
23
|
-
qaoalib/qaoa/landscape/base.py
|
|
24
|
-
qaoalib/qaoa/landscape/direct_numpy.py
|
|
25
|
-
qaoalib/qaoa/landscape/hadamard_test.py
|
|
26
|
-
qaoalib/qaoa/landscape/hybrid_fast.py
|
|
27
|
-
test/test.py
|
qaoalib-0.1.11/test/test.py
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|