qaoalib 0.2.1__tar.gz → 0.3.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.
Files changed (38) hide show
  1. qaoalib-0.3.1/MANIFEST.in +1 -0
  2. {qaoalib-0.2.1 → qaoalib-0.3.1}/PKG-INFO +24 -16
  3. {qaoalib-0.2.1 → qaoalib-0.3.1}/README.md +17 -6
  4. qaoalib-0.3.1/pyproject.toml +26 -0
  5. qaoalib-0.3.1/qaoalib/__init__.py +4 -0
  6. qaoalib-0.3.1/qaoalib/ansatz.py +222 -0
  7. qaoalib-0.3.1/qaoalib/legacy/qaoa/__init__.py +3 -0
  8. qaoalib-0.3.1/qaoalib/legacy/qaoa/landscape/__init__.py +3 -0
  9. qaoalib-0.3.1/qaoalib/legacy/qaoa/landscape/base.py +90 -0
  10. qaoalib-0.3.1/qaoalib/legacy/qaoa/landscape/direct_numpy.py +79 -0
  11. qaoalib-0.3.1/qaoalib/legacy/qaoa/landscape/hadamard_test.py +39 -0
  12. qaoalib-0.3.1/qaoalib/legacy/qaoa/landscape/hybrid_fast.py +44 -0
  13. qaoalib-0.3.1/qaoalib/legacy/qaoa/layerwise.py +184 -0
  14. qaoalib-0.3.1/qaoalib/legacy/qaoa/qis.py +59 -0
  15. qaoalib-0.3.1/qaoalib/legacy/qaoa/qmc.py +51 -0
  16. qaoalib-0.3.1/qaoalib/legacy/qaoa/strategy.py +70 -0
  17. qaoalib-0.3.1/qaoalib/legacy/qaoa/utils.py +135 -0
  18. qaoalib-0.3.1/qaoalib/solver/lcc_vqe.py +118 -0
  19. {qaoalib-0.2.1 → qaoalib-0.3.1}/qaoalib/solver/vqe.py +13 -31
  20. {qaoalib-0.2.1 → qaoalib-0.3.1}/qaoalib.egg-info/PKG-INFO +24 -16
  21. qaoalib-0.3.1/qaoalib.egg-info/SOURCES.txt +31 -0
  22. {qaoalib-0.2.1 → qaoalib-0.3.1}/qaoalib.egg-info/requires.txt +3 -2
  23. qaoalib-0.3.1/requirements.txt +11 -0
  24. qaoalib-0.2.1/qaoalib/__init__.py +0 -2
  25. qaoalib-0.2.1/qaoalib/version.py +0 -1
  26. qaoalib-0.2.1/qaoalib.egg-info/SOURCES.txt +0 -18
  27. qaoalib-0.2.1/setup.py +0 -36
  28. qaoalib-0.2.1/test/test.py +0 -5
  29. {qaoalib-0.2.1 → qaoalib-0.3.1}/LICENSE +0 -0
  30. {qaoalib-0.2.1 → qaoalib-0.3.1}/qaoalib/json.py +0 -0
  31. {qaoalib-0.2.1 → qaoalib-0.3.1}/qaoalib/math.py +0 -0
  32. {qaoalib-0.2.1 → qaoalib-0.3.1}/qaoalib/models/__init__.py +0 -0
  33. {qaoalib-0.2.1 → qaoalib-0.3.1}/qaoalib/models/result.py +0 -0
  34. {qaoalib-0.2.1 → qaoalib-0.3.1}/qaoalib/solver/__init__.py +0 -0
  35. {qaoalib-0.2.1 → qaoalib-0.3.1}/qaoalib/utils.py +0 -0
  36. {qaoalib-0.2.1 → qaoalib-0.3.1}/qaoalib.egg-info/dependency_links.txt +0 -0
  37. {qaoalib-0.2.1 → qaoalib-0.3.1}/qaoalib.egg-info/top_level.txt +0 -0
  38. {qaoalib-0.2.1 → qaoalib-0.3.1}/setup.cfg +0 -0
@@ -0,0 +1 @@
1
+ include requirements.txt
@@ -1,35 +1,28 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: qaoalib
3
- Version: 0.2.1
3
+ Version: 0.3.1
4
4
  Summary: A package for QAOA Maxcut calculations
5
- Home-page: https://github.com/xenoicwyce/qaoalib
6
- Author: Xinwei Lee
7
- Author-email: xenoicwyce@gmail.com
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
5
+ Project-URL: Homepage, https://github.com/xenoicwyce/qaoalib
11
6
  Requires-Python: >=3.9
12
7
  Description-Content-Type: text/markdown
13
8
  License-File: LICENSE
9
+ Requires-Dist: numpy
10
+ Requires-Dist: scipy
14
11
  Requires-Dist: qiskit
15
12
  Requires-Dist: qiskit_optimization
13
+ Requires-Dist: qiskit_algorithms
16
14
  Requires-Dist: qiskit_aer
17
- Requires-Dist: numpy==1.26.0
18
- Requires-Dist: scipy
19
15
  Requires-Dist: pytest
20
16
  Requires-Dist: pytest-cov
21
17
  Requires-Dist: matplotlib
22
18
  Requires-Dist: networkx
23
19
  Requires-Dist: pydantic
20
+ Dynamic: license-file
24
21
 
25
22
  # qaoalib
26
- A package for QAOA Max-cut Calculations.
23
+ Implementations of VQA simulations for combinatorial optimization (mainly with graph and Max-cut).
27
24
 
28
- Packages required:
29
- - numpy
30
- - networkx
31
- - matplotlib
32
- - qiskit
25
+ v0.2 works towards adapting the circuit simulations with new Qiskit primitives: `Sampler` and `Estimator` (starting from Qiskit 1.0).
33
26
 
34
27
  # How to install
35
28
  You can install from the PyPI:
@@ -38,6 +31,21 @@ pip install --upgrade qaoalib
38
31
  ```
39
32
 
40
33
  # Usage
34
+ Solving Max-cut with VQE:
35
+ ```
36
+ import networkx as nx
37
+ from qaoalib.solver import VQE
38
+ from qiskit_optimization.applications import Maxcut
39
+
40
+ G = nx.random_regular_graph(3, 6) # 3-regular graph wtih 6 nodes
41
+ qp = Maxcut(G).to_quadratic_program()
42
+ ansatz = # some preferred ansatz
43
+ solver = VQE(qp, ansatz)
44
+ result = solver.solve()
45
+ print(result)
46
+ ```
47
+
48
+ # Usage (legacy)
41
49
  Calculate Max-cut expectation with `Qmc` or `QmcFastKron` (faster version):
42
50
  ```
43
51
  import networkx as nx
@@ -1,11 +1,7 @@
1
1
  # qaoalib
2
- A package for QAOA Max-cut Calculations.
2
+ Implementations of VQA simulations for combinatorial optimization (mainly with graph and Max-cut).
3
3
 
4
- Packages required:
5
- - numpy
6
- - networkx
7
- - matplotlib
8
- - qiskit
4
+ v0.2 works towards adapting the circuit simulations with new Qiskit primitives: `Sampler` and `Estimator` (starting from Qiskit 1.0).
9
5
 
10
6
  # How to install
11
7
  You can install from the PyPI:
@@ -14,6 +10,21 @@ pip install --upgrade qaoalib
14
10
  ```
15
11
 
16
12
  # Usage
13
+ Solving Max-cut with VQE:
14
+ ```
15
+ import networkx as nx
16
+ from qaoalib.solver import VQE
17
+ from qiskit_optimization.applications import Maxcut
18
+
19
+ G = nx.random_regular_graph(3, 6) # 3-regular graph wtih 6 nodes
20
+ qp = Maxcut(G).to_quadratic_program()
21
+ ansatz = # some preferred ansatz
22
+ solver = VQE(qp, ansatz)
23
+ result = solver.solve()
24
+ print(result)
25
+ ```
26
+
27
+ # Usage (legacy)
17
28
  Calculate Max-cut expectation with `Qmc` or `QmcFastKron` (faster version):
18
29
  ```
19
30
  import networkx as nx
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["setuptools", "wheel", "numpy", "networkx"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "qaoalib"
7
+ version = "0.3.1"
8
+ description = "A package for QAOA Maxcut calculations"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ dependencies = [
12
+ "numpy",
13
+ "scipy",
14
+ "qiskit",
15
+ "qiskit_optimization",
16
+ "qiskit_algorithms",
17
+ "qiskit_aer",
18
+ "pytest",
19
+ "pytest-cov",
20
+ "matplotlib",
21
+ "networkx",
22
+ "pydantic",
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://github.com/xenoicwyce/qaoalib"
@@ -0,0 +1,4 @@
1
+ from importlib.metadata import version
2
+ from .utils import *
3
+
4
+ __version__ = version("qaoalib")
@@ -0,0 +1,222 @@
1
+ import numpy as np
2
+ import networkx as nx
3
+
4
+ from qiskit import QuantumCircuit
5
+ from qiskit.quantum_info import SparsePauliOp
6
+ from qiskit.circuit import ParameterVector
7
+ from qiskit.circuit.library import PauliEvolutionGate
8
+
9
+
10
+ def generate_sum_x_pauli_str(length):
11
+ ret = []
12
+ for i in range(length):
13
+ paulis = ['I'] * length
14
+ paulis[i] = 'X'
15
+ ret.append(''.join(paulis))
16
+
17
+ return ret
18
+
19
+ def qaoa(problem_ham: SparsePauliOp, reps: int = 1) -> QuantumCircuit:
20
+ r"""
21
+ Input:
22
+ - problem_ham: Problem Hamiltonian to construct the QAOA circuit.
23
+ Standard procedure would be:
24
+ ```
25
+ hamiltonian, offset = qubo.to_ising()
26
+ qc = qaoa_circuit_from_qubo(hamiltonian)
27
+ ```
28
+
29
+ Returns:
30
+ - qc: A QuantumCircuit object representing the QAOA circuit e^{-i\beta H_M} e^{-i\gamma H_C}.
31
+ """
32
+ num_qubits = problem_ham.num_qubits
33
+
34
+ gamma = ParameterVector(name=r'$\gamma$', length=reps)
35
+ beta = ParameterVector(name=r'$\beta$', length=reps)
36
+
37
+ mixer_ham = SparsePauliOp(generate_sum_x_pauli_str(num_qubits))
38
+
39
+ qc = QuantumCircuit(num_qubits)
40
+ qc.h(range(num_qubits))
41
+
42
+ for p in range(reps):
43
+ exp_gamma = PauliEvolutionGate(problem_ham, time=gamma[p])
44
+ exp_beta = PauliEvolutionGate(mixer_ham, time=beta[p])
45
+ qc.append(exp_gamma, qargs=range(num_qubits))
46
+ qc.append(exp_beta, qargs=range(num_qubits))
47
+
48
+ return qc
49
+
50
+ def multi_angle_qaoa(problem_ham: SparsePauliOp, reps: int = 1) -> QuantumCircuit:
51
+ r"""
52
+ Input:
53
+ - problem_ham: Problem Hamiltonian to construct the QAOA circuit.
54
+ Standard procedure would be:
55
+ ```
56
+ hamiltonian, offset = qubo.to_ising()
57
+ qc = qaoa_circuit_from_qubo(hamiltonian)
58
+ ```
59
+
60
+ Returns:
61
+ - qc: A QuantumCircuit object representing the QAOA circuit e^{-i\beta H_M} e^{-i\gamma H_C}.
62
+ """
63
+ num_qubits = problem_ham.num_qubits
64
+
65
+ gamma = ParameterVector(name=r'$\gamma$', length=len(problem_ham) * reps)
66
+ beta = ParameterVector(name=r'$\beta$', length=num_qubits * reps)
67
+
68
+ qc = QuantumCircuit(num_qubits)
69
+ qc.h(range(num_qubits))
70
+
71
+ for p in range(reps):
72
+ for idx, term in enumerate(problem_ham):
73
+ exp_gamma = PauliEvolutionGate(term, time=gamma[p * num_qubits + idx])
74
+ qc.append(exp_gamma, qargs=range(num_qubits))
75
+ for i in range(num_qubits):
76
+ qc.rx(beta[p * num_qubits + i], i)
77
+
78
+ return qc
79
+
80
+ def two_local(num_qubits: int, reps: int = 1, entanglement: str = 'linear') -> QuantumCircuit:
81
+ # only linear or circular entanglement
82
+ if entanglement not in ['linear', 'circular']:
83
+ raise ValueError(f'Entanglment must be linear or circular, got {entanglement} instead.')
84
+
85
+ qc = QuantumCircuit(num_qubits)
86
+ params = ParameterVector('θ', num_qubits * (reps + 1))
87
+
88
+ for i in range(num_qubits):
89
+ qc.ry(params[i], i)
90
+
91
+ for r in range(1, reps + 1):
92
+ for i in range(num_qubits - 1):
93
+ qc.cz(i, i+1)
94
+ if entanglement == 'circular':
95
+ qc.cz(num_qubits - 1, 0)
96
+ for i in range(num_qubits):
97
+ qc.ry(params[r * num_qubits + i], i)
98
+
99
+ return qc
100
+
101
+ def _det_warm_start_angle(ci: float, epsilon: float):
102
+ if ci <= epsilon:
103
+ return 2 * np.arcsin(np.sqrt(epsilon))
104
+ elif ci >= 1 - epsilon:
105
+ return 2 * np.arcsin(np.sqrt(1 - epsilon))
106
+ else:
107
+ return 2 * np.arcsin(np.sqrt(ci))
108
+
109
+ def warm_start_qaoa(
110
+ problem_ham: SparsePauliOp,
111
+ relaxed_qp_solution: list[float],
112
+ depth: int = 1,
113
+ regularization: float = 0.01,
114
+ flip_solution_indices: bool = False,
115
+ ) -> QuantumCircuit:
116
+ r"""
117
+ Input:
118
+ - problem_ham: Problem Hamiltonian to construct the QAOA circuit.
119
+ - relaxed_qp_solution: The solution for the relaxed quadratic program.
120
+
121
+ Returns:
122
+ - qc: A QuantumCircuit object representing the QAOA circuit e^{-i\beta H_M} e^{-i\gamma H_C}.
123
+ """
124
+ num_qubits = problem_ham.num_qubits
125
+ if num_qubits != len(relaxed_qp_solution):
126
+ raise ValueError(f'The number of qubits and the length of the solution must be equal.')
127
+
128
+ # try setting this to True if doesn't work, as Qiskit works with reversed indices.
129
+ if flip_solution_indices:
130
+ relaxed_qp_solution = relaxed_qp_solution[::-1]
131
+
132
+ gamma = ParameterVector(name=r'$\gamma$', length=depth)
133
+ beta = ParameterVector(name=r'$\beta$', length=depth)
134
+
135
+ # calculate the thetas for warm start
136
+ reg_warm_start_angle = lambda ci: _det_warm_start_angle(ci, regularization)
137
+ theta = list(map(reg_warm_start_angle, relaxed_qp_solution))
138
+
139
+ qc = QuantumCircuit(num_qubits)
140
+
141
+ # initial layer
142
+ for i in range(num_qubits):
143
+ qc.ry(theta[i], i)
144
+
145
+ for p in range(depth):
146
+ # phase separation
147
+ exp_gamma = PauliEvolutionGate(problem_ham, time=gamma[p])
148
+ qc.append(exp_gamma, qargs=range(num_qubits))
149
+
150
+ # mixer
151
+ for i in range(num_qubits):
152
+ qc.ry(-theta[i], i)
153
+ qc.rz(beta[p], i)
154
+ qc.ry(theta[i], i)
155
+
156
+ return qc
157
+
158
+ def rzy_gate(theta: float) -> QuantumCircuit:
159
+ """
160
+ Add RZY gate to QuantumCircuit object, with Z acting on q1 and Y acting on q0 (following qiskit convention).
161
+ RZY(theta) = exp(-i * theta * kron(Y, Z)).
162
+ """
163
+ qc = QuantumCircuit(2, name='Rzy')
164
+ qc.rx(np.pi/2, 1)
165
+ qc.cx(0, 1)
166
+ qc.rz(theta, 1)
167
+ qc.cx(0, 1)
168
+ qc.rx(-np.pi/2, 1)
169
+ return qc
170
+
171
+ def drop_weights(G: nx.Graph) -> nx.Graph:
172
+ G_unweighted = G.copy()
173
+ for _, _, data in G_unweighted.edges(data=True):
174
+ data['weights'] = 1.0
175
+
176
+ return G_unweighted
177
+
178
+ def ihva_from_graph(G: nx.Graph, reps: int = 1, weighted_gates: bool = False) -> QuantumCircuit:
179
+ num_nodes = len(G.nodes)
180
+ num_edges = len(G.edges)
181
+ edge_buffer = []
182
+ G_unweighted = drop_weights(G)
183
+ while G_unweighted.edges:
184
+ tree = nx.maximum_spanning_tree(G_unweighted)
185
+ edge_buffer += list(tree.edges)
186
+ G_unweighted.remove_edges_from(tree.edges)
187
+
188
+ qc = QuantumCircuit(num_nodes)
189
+ qc.h(range(num_nodes))
190
+ params = ParameterVector('θ', num_edges * reps)
191
+ for r in range(reps):
192
+ for idx, edge in enumerate(edge_buffer):
193
+ if r % 2 == 0:
194
+ i, j = edge
195
+ else:
196
+ j, i = edge
197
+ gate_weight = G[i][j]['weight'] if weighted_gates else 1.0
198
+ qc.append(rzy_gate(gate_weight * params [r * num_edges + idx] ), qargs=[i, j])
199
+
200
+ return qc
201
+
202
+ def linear_ansatz(num_qubits: int, reps: int = 1) -> QuantumCircuit:
203
+ qc = QuantumCircuit(num_qubits)
204
+ params = ParameterVector('θ', num_qubits * reps)
205
+
206
+ # initial state
207
+ qc.h(range(num_qubits - 1)) # leave the last qubit with state |0>
208
+
209
+ for r in range(reps):
210
+ for i in range(num_qubits):
211
+ qc.ry(params[r * num_qubits + i], i)
212
+
213
+ return qc
214
+
215
+ def pure_rx_ansatz(num_qubits: int) -> QuantumCircuit:
216
+ params = ParameterVector('θ', num_qubits)
217
+ qc = QuantumCircuit(num_qubits)
218
+
219
+ for i in range(num_qubits):
220
+ qc.rx(params[i], i)
221
+
222
+ return qc
@@ -0,0 +1,3 @@
1
+ from .qmc import Qmc, QmcFastKron
2
+ from .layerwise import Layerwise
3
+ from .utils import *
@@ -0,0 +1,3 @@
1
+ from .direct_numpy import DirectNumpy
2
+ from .hybrid_fast import HybridFast
3
+ from .hadamard_test import HadamardTest
@@ -0,0 +1,90 @@
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()
@@ -0,0 +1,79 @@
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
@@ -0,0 +1,39 @@
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
@@ -0,0 +1,44 @@
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