qaoalib 0.3.0__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 (37) hide show
  1. {qaoalib-0.3.0/qaoalib.egg-info → qaoalib-0.3.1}/PKG-INFO +4 -18
  2. qaoalib-0.3.1/pyproject.toml +26 -0
  3. qaoalib-0.3.1/qaoalib/__init__.py +4 -0
  4. qaoalib-0.3.1/qaoalib/legacy/qaoa/__init__.py +3 -0
  5. qaoalib-0.3.1/qaoalib/legacy/qaoa/landscape/__init__.py +3 -0
  6. qaoalib-0.3.1/qaoalib/legacy/qaoa/landscape/base.py +90 -0
  7. qaoalib-0.3.1/qaoalib/legacy/qaoa/landscape/direct_numpy.py +79 -0
  8. qaoalib-0.3.1/qaoalib/legacy/qaoa/landscape/hadamard_test.py +39 -0
  9. qaoalib-0.3.1/qaoalib/legacy/qaoa/landscape/hybrid_fast.py +44 -0
  10. qaoalib-0.3.1/qaoalib/legacy/qaoa/layerwise.py +184 -0
  11. qaoalib-0.3.1/qaoalib/legacy/qaoa/qis.py +59 -0
  12. qaoalib-0.3.1/qaoalib/legacy/qaoa/qmc.py +51 -0
  13. qaoalib-0.3.1/qaoalib/legacy/qaoa/strategy.py +70 -0
  14. qaoalib-0.3.1/qaoalib/legacy/qaoa/utils.py +135 -0
  15. {qaoalib-0.3.0 → qaoalib-0.3.1/qaoalib.egg-info}/PKG-INFO +4 -18
  16. qaoalib-0.3.1/qaoalib.egg-info/SOURCES.txt +31 -0
  17. qaoalib-0.3.0/qaoalib/__init__.py +0 -2
  18. qaoalib-0.3.0/qaoalib/version.py +0 -1
  19. qaoalib-0.3.0/qaoalib.egg-info/SOURCES.txt +0 -21
  20. qaoalib-0.3.0/setup.py +0 -29
  21. {qaoalib-0.3.0 → qaoalib-0.3.1}/LICENSE +0 -0
  22. {qaoalib-0.3.0 → qaoalib-0.3.1}/MANIFEST.in +0 -0
  23. {qaoalib-0.3.0 → qaoalib-0.3.1}/README.md +0 -0
  24. {qaoalib-0.3.0 → qaoalib-0.3.1}/qaoalib/ansatz.py +0 -0
  25. {qaoalib-0.3.0 → qaoalib-0.3.1}/qaoalib/json.py +0 -0
  26. {qaoalib-0.3.0 → qaoalib-0.3.1}/qaoalib/math.py +0 -0
  27. {qaoalib-0.3.0 → qaoalib-0.3.1}/qaoalib/models/__init__.py +0 -0
  28. {qaoalib-0.3.0 → qaoalib-0.3.1}/qaoalib/models/result.py +0 -0
  29. {qaoalib-0.3.0 → qaoalib-0.3.1}/qaoalib/solver/__init__.py +0 -0
  30. {qaoalib-0.3.0 → qaoalib-0.3.1}/qaoalib/solver/lcc_vqe.py +0 -0
  31. {qaoalib-0.3.0 → qaoalib-0.3.1}/qaoalib/solver/vqe.py +0 -0
  32. {qaoalib-0.3.0 → qaoalib-0.3.1}/qaoalib/utils.py +0 -0
  33. {qaoalib-0.3.0 → qaoalib-0.3.1}/qaoalib.egg-info/dependency_links.txt +0 -0
  34. {qaoalib-0.3.0 → qaoalib-0.3.1}/qaoalib.egg-info/requires.txt +2 -2
  35. {qaoalib-0.3.0 → qaoalib-0.3.1}/qaoalib.egg-info/top_level.txt +0 -0
  36. {qaoalib-0.3.0 → qaoalib-0.3.1}/requirements.txt +0 -0
  37. {qaoalib-0.3.0 → qaoalib-0.3.1}/setup.cfg +0 -0
@@ -1,37 +1,23 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qaoalib
3
- Version: 0.3.0
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
16
13
  Requires-Dist: qiskit_algorithms
17
14
  Requires-Dist: qiskit_aer
18
- Requires-Dist: numpy
19
- Requires-Dist: scipy
20
15
  Requires-Dist: pytest
21
16
  Requires-Dist: pytest-cov
22
17
  Requires-Dist: matplotlib
23
18
  Requires-Dist: networkx
24
19
  Requires-Dist: pydantic
25
- Dynamic: author
26
- Dynamic: author-email
27
- Dynamic: classifier
28
- Dynamic: description
29
- Dynamic: description-content-type
30
- Dynamic: home-page
31
20
  Dynamic: license-file
32
- Dynamic: requires-dist
33
- Dynamic: requires-python
34
- Dynamic: summary
35
21
 
36
22
  # qaoalib
37
23
  Implementations of VQA simulations for combinatorial optimization (mainly with graph and Max-cut).
@@ -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,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
@@ -0,0 +1,184 @@
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)
@@ -0,0 +1,59 @@
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))]
@@ -0,0 +1,51 @@
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)
@@ -0,0 +1,70 @@
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])
@@ -0,0 +1,135 @@
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,37 +1,23 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qaoalib
3
- Version: 0.3.0
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
16
13
  Requires-Dist: qiskit_algorithms
17
14
  Requires-Dist: qiskit_aer
18
- Requires-Dist: numpy
19
- Requires-Dist: scipy
20
15
  Requires-Dist: pytest
21
16
  Requires-Dist: pytest-cov
22
17
  Requires-Dist: matplotlib
23
18
  Requires-Dist: networkx
24
19
  Requires-Dist: pydantic
25
- Dynamic: author
26
- Dynamic: author-email
27
- Dynamic: classifier
28
- Dynamic: description
29
- Dynamic: description-content-type
30
- Dynamic: home-page
31
20
  Dynamic: license-file
32
- Dynamic: requires-dist
33
- Dynamic: requires-python
34
- Dynamic: summary
35
21
 
36
22
  # qaoalib
37
23
  Implementations of VQA simulations for combinatorial optimization (mainly with graph and Max-cut).
@@ -0,0 +1,31 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ requirements.txt
6
+ qaoalib/__init__.py
7
+ qaoalib/ansatz.py
8
+ qaoalib/json.py
9
+ qaoalib/math.py
10
+ qaoalib/utils.py
11
+ qaoalib.egg-info/PKG-INFO
12
+ qaoalib.egg-info/SOURCES.txt
13
+ qaoalib.egg-info/dependency_links.txt
14
+ qaoalib.egg-info/requires.txt
15
+ qaoalib.egg-info/top_level.txt
16
+ qaoalib/legacy/qaoa/__init__.py
17
+ qaoalib/legacy/qaoa/layerwise.py
18
+ qaoalib/legacy/qaoa/qis.py
19
+ qaoalib/legacy/qaoa/qmc.py
20
+ qaoalib/legacy/qaoa/strategy.py
21
+ qaoalib/legacy/qaoa/utils.py
22
+ qaoalib/legacy/qaoa/landscape/__init__.py
23
+ qaoalib/legacy/qaoa/landscape/base.py
24
+ qaoalib/legacy/qaoa/landscape/direct_numpy.py
25
+ qaoalib/legacy/qaoa/landscape/hadamard_test.py
26
+ qaoalib/legacy/qaoa/landscape/hybrid_fast.py
27
+ qaoalib/models/__init__.py
28
+ qaoalib/models/result.py
29
+ qaoalib/solver/__init__.py
30
+ qaoalib/solver/lcc_vqe.py
31
+ qaoalib/solver/vqe.py
@@ -1,2 +0,0 @@
1
- from .version import __version__
2
- from .utils import *
@@ -1 +0,0 @@
1
- 0.3.0
@@ -1,21 +0,0 @@
1
- LICENSE
2
- MANIFEST.in
3
- README.md
4
- requirements.txt
5
- setup.py
6
- qaoalib/__init__.py
7
- qaoalib/ansatz.py
8
- qaoalib/json.py
9
- qaoalib/math.py
10
- qaoalib/utils.py
11
- qaoalib/version.py
12
- qaoalib.egg-info/PKG-INFO
13
- qaoalib.egg-info/SOURCES.txt
14
- qaoalib.egg-info/dependency_links.txt
15
- qaoalib.egg-info/requires.txt
16
- qaoalib.egg-info/top_level.txt
17
- qaoalib/models/__init__.py
18
- qaoalib/models/result.py
19
- qaoalib/solver/__init__.py
20
- qaoalib/solver/lcc_vqe.py
21
- qaoalib/solver/vqe.py
qaoalib-0.3.0/setup.py DELETED
@@ -1,29 +0,0 @@
1
- from setuptools import setup, find_packages
2
-
3
- with open("README.md", "r", encoding="utf-8") as fh:
4
- long_description = fh.read()
5
-
6
- with open("qaoalib/version.py", "r") as f:
7
- __version__ = f.read().strip()
8
-
9
- with open("requirements.txt", "r") as f:
10
- requirements = [package.strip() for package in f]
11
-
12
- setup(
13
- name="qaoalib",
14
- version=__version__,
15
- author="Xinwei Lee",
16
- author_email="xenoicwyce@gmail.com",
17
- description="A package for QAOA Maxcut calculations",
18
- long_description=long_description,
19
- long_description_content_type="text/markdown",
20
- url="https://github.com/xenoicwyce/qaoalib",
21
- classifiers=[
22
- "Programming Language :: Python :: 3",
23
- "License :: OSI Approved :: MIT License",
24
- "Operating System :: OS Independent",
25
- ],
26
- packages=find_packages(exclude=["test*"]),
27
- python_requires=">=3.9",
28
- install_requires=requirements,
29
- )
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,9 +1,9 @@
1
+ numpy
2
+ scipy
1
3
  qiskit
2
4
  qiskit_optimization
3
5
  qiskit_algorithms
4
6
  qiskit_aer
5
- numpy
6
- scipy
7
7
  pytest
8
8
  pytest-cov
9
9
  matplotlib
File without changes
File without changes