pyqrackising 9.2.3__py3-none-macosx_15_0_arm64.whl → 9.5.12__py3-none-macosx_15_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyqrackising/__init__.py CHANGED
@@ -1,9 +1,11 @@
1
- from .generate_tfim_samples import generate_tfim_samples
1
+ from .generate_tfim_samples import generate_tfim_samples, get_tfim_hamming_distribution
2
2
  from .tfim_magnetization import tfim_magnetization
3
3
  from .tfim_square_magnetization import tfim_square_magnetization
4
+ from .otoc import generate_otoc_samples, get_otoc_hamming_distribution
4
5
  from .maxcut_tfim import maxcut_tfim
5
6
  from .maxcut_tfim_sparse import maxcut_tfim_sparse
6
7
  from .maxcut_tfim_streaming import maxcut_tfim_streaming
8
+ from .maxcut_tfim_util import probability_by_hamming_weight
7
9
  from .spin_glass_solver import spin_glass_solver
8
10
  from .spin_glass_solver_sparse import spin_glass_solver_sparse
9
11
  from .spin_glass_solver_streaming import spin_glass_solver_streaming
@@ -1,10 +1,14 @@
1
- from .maxcut_tfim_util import probability_by_hamming_weight, sample_mag
1
+ from .maxcut_tfim_util import probability_by_hamming_weight, sample_mag, opencl_context
2
2
  import itertools
3
3
  import math
4
+ import sys
4
5
  import numpy as np
5
6
  from numba import njit
6
7
 
7
8
 
9
+ epsilon = opencl_context.epsilon
10
+
11
+
8
12
  @njit
9
13
  def factor_width(width, is_transpose=False):
10
14
  col_len = math.floor(math.sqrt(width))
@@ -85,13 +89,12 @@ def expected_closeness_weight(n_rows, n_cols, hamming_weight):
85
89
  def sample_hamming_weight(thresholds, shots):
86
90
  hamming_samples = np.zeros(shots, dtype=np.int32)
87
91
  for s in range(shots):
88
- hamming_samples[s] = sample_mag(thresholds)
92
+ hamming_samples[s] = sample_mag(thresholds) + 1
89
93
 
90
94
  return hamming_samples
91
95
 
92
96
  @njit
93
97
  def fix_cdf(hamming_prob):
94
- hamming_prob /= hamming_prob.sum()
95
98
  tot_prob = 0.0
96
99
  n_bias = len(hamming_prob)
97
100
  cum_prob = np.empty(n_bias, dtype=np.float64)
@@ -103,13 +106,26 @@ def fix_cdf(hamming_prob):
103
106
  return cum_prob
104
107
 
105
108
 
109
+ @njit
110
+ def get_tfim_hamming_distribution(J=-1.0, h=2.0, z=4, theta=0.174532925199432957, t=5, n_qubits=56):
111
+ if h <= epsilon:
112
+ bias = np.empty(n_qubits + 1, dtype=np.float64)
113
+ if J > 0:
114
+ bias[-1] = 1.0
115
+ else:
116
+ bias[0] = 1.0
117
+ return bias
118
+ bias = probability_by_hamming_weight(J, h, z, theta, t, n_qubits + 1)
119
+ return bias / bias.sum()
120
+
121
+
106
122
  def generate_tfim_samples(
107
123
  J=-1.0, h=2.0, z=4, theta=0.174532925199432957, t=5, n_qubits=56, shots=100
108
124
  ):
109
125
  n_rows, n_cols = factor_width(n_qubits)
110
126
 
111
127
  # First dimension: Hamming weight
112
- thresholds = fix_cdf(probability_by_hamming_weight(J, h, z, theta, t, n_qubits + 1))
128
+ thresholds = fix_cdf(get_tfim_hamming_distribution(J, h, z, theta, t, n_qubits + 1))
113
129
  hamming_samples = sample_hamming_weight(thresholds, shots)
114
130
 
115
131
  samples = []
@@ -3,7 +3,7 @@ import numpy as np
3
3
  from numba import njit, prange
4
4
  import os
5
5
 
6
- from .maxcut_tfim_util import compute_cut, compute_energy, convert_bool_to_uint, get_cut, get_cut_base, make_G_m_buf, make_theta_buf, maxcut_hamming_cdf, opencl_context, sample_mag, setup_opencl, bit_pick
6
+ from .maxcut_tfim_util import compute_cut, compute_energy, convert_bool_to_uint, get_cut, get_cut_base, init_thresholds, make_G_m_buf, make_theta_buf, maxcut_hamming_cdf, opencl_context, sample_mag, setup_opencl, bit_pick
7
7
 
8
8
  IS_OPENCL_AVAILABLE = True
9
9
  try:
@@ -177,21 +177,6 @@ def init_J_and_z(G_m, repulsion_base):
177
177
  return J_eff, degrees
178
178
 
179
179
 
180
- @njit
181
- def cpu_footer(shots, thread_count, quality, n_qubits, G_m, nodes, is_spin_glass, anneal_t, anneal_h, repulsion_base):
182
- J_eff, degrees = init_J_and_z(G_m, repulsion_base)
183
- hamming_prob = maxcut_hamming_cdf(n_qubits, J_eff, degrees, quality, anneal_t, anneal_h)
184
-
185
- degrees = None
186
- J_eff = None
187
-
188
- best_solution, best_value = sample_measurement(G_m, shots, thread_count, hamming_prob, repulsion_base, is_spin_glass)
189
-
190
- bit_string, l, r = get_cut(best_solution, nodes, n_qubits)
191
-
192
- return bit_string, best_value, (l, r)
193
-
194
-
195
180
  def run_cut_opencl(best_energy, samples, G_m_buf, is_segmented, local_size, global_size, args_buf, local_energy_buf, local_index_buf, max_energy_host, max_index_host, max_energy_buf, max_index_buf):
196
181
  queue = opencl_context.queue
197
182
  calculate_cut_kernel = opencl_context.calculate_cut_segmented_kernel if is_segmented else opencl_context.calculate_cut_kernel
@@ -253,6 +238,7 @@ def run_cut_opencl(best_energy, samples, G_m_buf, is_segmented, local_size, glob
253
238
 
254
239
  return samples[max_index_host[best_x]], energy
255
240
 
241
+
256
242
  def maxcut_tfim(
257
243
  G,
258
244
  quality=None,
@@ -308,31 +294,21 @@ def maxcut_tfim(
308
294
  if repulsion_base is None:
309
295
  repulsion_base = 5.0
310
296
 
311
- is_opencl = is_maxcut_gpu and IS_OPENCL_AVAILABLE
312
-
313
- if not is_opencl:
314
- thread_count = os.cpu_count() ** 2
315
-
316
- bit_string, best_value, partition = cpu_footer(shots, thread_count, quality, n_qubits, G_m, nodes, is_spin_glass, anneal_t, anneal_h, repulsion_base)
317
-
318
- if best_value < 0.0:
319
- # Best cut is trivial partition, all/empty
320
- return '0' * n_qubits, 0.0, (nodes, [])
321
-
322
- return bit_string, best_value, partition
323
-
324
- segment_size = (G_m.shape[0] * G_m.shape[1] + 3) >> 2
325
- theta_segment_size = (((n_qubits + 31) >> 5) * (((shots + wgs - 1) // wgs) * wgs) + 3) >> 2
326
- is_segmented = ((G_m.nbytes << 1) > opencl_context.max_alloc) or ((theta_segment_size << 3) > opencl_context.max_alloc)
327
- G_m_buf = make_G_m_buf(G_m, is_segmented, segment_size)
328
-
329
297
  J_eff, degrees = init_J_and_z(G_m, repulsion_base)
330
- hamming_prob = maxcut_hamming_cdf(n_qubits, J_eff, degrees, quality, anneal_t, anneal_h)
298
+ cum_prob = maxcut_hamming_cdf(init_thresholds(n_qubits), n_qubits, J_eff, degrees, quality, anneal_t, anneal_h)
331
299
 
332
300
  degrees = None
333
301
  J_eff = None
334
302
 
335
- best_solution, best_value = sample_for_opencl(G_m, G_m_buf, shots, hamming_prob, repulsion_base, is_spin_glass, is_segmented, segment_size, theta_segment_size)
303
+ if is_maxcut_gpu and IS_OPENCL_AVAILABLE:
304
+ segment_size = (G_m.shape[0] * G_m.shape[1] + 3) >> 2
305
+ theta_segment_size = (((n_qubits + 31) >> 5) * (((shots + wgs - 1) // wgs) * wgs) + 3) >> 2
306
+ is_segmented = ((G_m.nbytes << 1) > opencl_context.max_alloc) or ((theta_segment_size << 3) > opencl_context.max_alloc)
307
+ G_m_buf = make_G_m_buf(G_m, is_segmented, segment_size)
308
+ best_solution, best_value = sample_for_opencl(G_m, G_m_buf, shots, cum_prob, repulsion_base, is_spin_glass, is_segmented, segment_size, theta_segment_size)
309
+ else:
310
+ thread_count = os.cpu_count() ** 2
311
+ best_solution, best_value = sample_measurement(G_m, shots, thread_count, cum_prob, repulsion_base, is_spin_glass)
336
312
 
337
313
  bit_string, l, r = get_cut(best_solution, nodes, n_qubits)
338
314
 
@@ -3,7 +3,7 @@ import numpy as np
3
3
  from numba import njit, prange
4
4
  import os
5
5
 
6
- from .maxcut_tfim_util import binary_search, compute_cut_sparse, compute_energy_sparse, convert_bool_to_uint, get_cut, get_cut_base, make_G_m_csr_buf, make_theta_buf, maxcut_hamming_cdf, opencl_context, sample_mag, setup_opencl, bit_pick, to_scipy_sparse_upper_triangular
6
+ from .maxcut_tfim_util import binary_search, compute_cut_sparse, compute_energy_sparse, convert_bool_to_uint, get_cut, get_cut_base, init_thresholds, make_G_m_csr_buf, make_theta_buf, maxcut_hamming_cdf, opencl_context, sample_mag, setup_opencl, bit_pick, to_scipy_sparse_upper_triangular
7
7
 
8
8
  IS_OPENCL_AVAILABLE = True
9
9
  try:
@@ -187,17 +187,6 @@ def init_J_and_z(G_m, repulsion_base):
187
187
  return J_eff, degrees
188
188
 
189
189
 
190
- @njit
191
- def cpu_footer(J_eff, degrees, shots, thread_count, quality, n_qubits, G_data, G_rows, G_cols, nodes, is_spin_glass, anneal_t, anneal_h, repulsion_base):
192
- hamming_prob = maxcut_hamming_cdf(n_qubits, J_eff, degrees, quality, anneal_t, anneal_h)
193
-
194
- best_solution, best_value = sample_measurement(G_data, G_rows, G_cols, shots, thread_count, hamming_prob, repulsion_base, is_spin_glass)
195
-
196
- bit_string, l, r = get_cut(best_solution, nodes, n_qubits)
197
-
198
- return bit_string, best_value, (l, r)
199
-
200
-
201
190
  def run_cut_opencl(best_energy, samples, G_data_buf, G_rows_buf, G_cols_buf, is_segmented, local_size, global_size, args_buf, local_energy_buf, local_index_buf, max_energy_host, max_index_host, max_energy_buf, max_index_buf):
202
191
  queue = opencl_context.queue
203
192
  calculate_cut_kernel = opencl_context.calculate_cut_sparse_segmented_kernel if is_segmented else opencl_context.calculate_cut_sparse_kernel
@@ -319,35 +308,23 @@ def maxcut_tfim_sparse(
319
308
  if repulsion_base is None:
320
309
  repulsion_base = 5.0
321
310
 
322
- J_eff, degrees = init_J_and_z(G_m, repulsion_base)
323
-
324
311
  n_qubits = G_m.shape[0]
325
312
 
326
- is_opencl = is_maxcut_gpu and IS_OPENCL_AVAILABLE
327
-
328
- if not is_opencl:
329
- thread_count = os.cpu_count() ** 2
330
-
331
- bit_string, best_value, partition = cpu_footer(J_eff, degrees, shots, thread_count, quality, n_qubits, G_m.data, G_m.indptr, G_m.indices, nodes, is_spin_glass, anneal_t, anneal_h, repulsion_base)
332
-
333
- if best_value < 0.0:
334
- # Best cut is trivial partition, all/empty
335
- return '0' * n_qubits, 0.0, (nodes, [])
336
-
337
- return bit_string, best_value, partition
338
-
339
- segment_size = (G_m.data.shape[0] + 3) >> 2
340
- theta_segment_size = (((n_qubits + 31) >> 5) * (((shots + wgs - 1) // wgs) * wgs) + 3) >> 2
341
- is_segmented = (G_m.data.nbytes << 1) > opencl_context.max_alloc or ((theta_segment_size << 3) > opencl_context.max_alloc)
342
-
343
- G_data_buf, G_rows_buf, G_cols_buf = make_G_m_csr_buf(G_m, is_segmented, segment_size)
344
-
345
- hamming_prob = maxcut_hamming_cdf(n_qubits, J_eff, degrees, quality, anneal_t, anneal_h)
313
+ J_eff, degrees = init_J_and_z(G_m, repulsion_base)
314
+ cum_prob = maxcut_hamming_cdf(init_thresholds(n_qubits), n_qubits, J_eff, degrees, quality, anneal_t, anneal_h)
346
315
 
347
316
  degrees = None
348
317
  J_eff = None
349
318
 
350
- best_solution, best_value = sample_for_opencl(G_m.data, G_m.indptr, G_m.indices, G_data_buf, G_rows_buf, G_cols_buf, shots, hamming_prob, repulsion_base, is_spin_glass, is_segmented, segment_size, theta_segment_size)
319
+ if is_maxcut_gpu and IS_OPENCL_AVAILABLE:
320
+ segment_size = (G_m.data.shape[0] + 3) >> 2
321
+ theta_segment_size = (((n_qubits + 31) >> 5) * (((shots + wgs - 1) // wgs) * wgs) + 3) >> 2
322
+ is_segmented = (G_m.data.nbytes << 1) > opencl_context.max_alloc or ((theta_segment_size << 3) > opencl_context.max_alloc)
323
+ G_data_buf, G_rows_buf, G_cols_buf = make_G_m_csr_buf(G_m, is_segmented, segment_size)
324
+ best_solution, best_value = sample_for_opencl(G_m.data, G_m.indptr, G_m.indices, G_data_buf, G_rows_buf, G_cols_buf, shots, cum_prob, repulsion_base, is_spin_glass, is_segmented, segment_size, theta_segment_size)
325
+ else:
326
+ thread_count = os.cpu_count() ** 2
327
+ best_solution, best_value = sample_measurement(G_m.data, G_m.indptr, G_m.indices, shots, thread_count, cum_prob, repulsion_base, is_spin_glass)
351
328
 
352
329
  bit_string, l, r = get_cut(best_solution, nodes, n_qubits)
353
330
 
@@ -3,7 +3,7 @@ import numpy as np
3
3
  from numba import njit, prange
4
4
  import os
5
5
 
6
- from .maxcut_tfim_util import compute_cut_streaming, compute_energy_streaming, get_cut, get_cut_base, maxcut_hamming_cdf, opencl_context, sample_mag, bit_pick
6
+ from .maxcut_tfim_util import compute_cut_streaming, compute_energy_streaming, get_cut, get_cut_base, init_thresholds, maxcut_hamming_cdf, opencl_context, sample_mag, bit_pick
7
7
 
8
8
 
9
9
  epsilon = opencl_context.epsilon
@@ -145,21 +145,6 @@ def find_G_min(G_func, nodes, n_nodes):
145
145
  return G_min
146
146
 
147
147
 
148
- @njit
149
- def cpu_footer(shots, thread_count, quality, n_qubits, G_min, G_func, nodes, is_spin_glass, anneal_t, anneal_h, repulsion_base):
150
- J_eff, degrees = init_J_and_z(G_func, nodes, G_min, repulsion_base)
151
- hamming_prob = maxcut_hamming_cdf(n_qubits, J_eff, degrees, quality, anneal_t, anneal_h)
152
-
153
- degrees = None
154
- J_eff = None
155
-
156
- best_solution, best_value = sample_measurement(G_func, nodes, shots, thread_count, hamming_prob, n_qubits, repulsion_base, is_spin_glass)
157
-
158
- bit_string, l, r = get_cut(best_solution, nodes, n_qubits)
159
-
160
- return bit_string, best_value, (l, r)
161
-
162
-
163
148
  def maxcut_tfim_streaming(
164
149
  G_func,
165
150
  nodes,
@@ -207,10 +192,18 @@ def maxcut_tfim_streaming(
207
192
 
208
193
  thread_count = os.cpu_count() ** 2
209
194
 
210
- bit_string, best_value, partition = cpu_footer(shots, thread_count, quality, n_qubits, G_min, G_func, nodes, is_spin_glass, anneal_t, anneal_h, repulsion_base)
195
+ J_eff, degrees = init_J_and_z(G_func, nodes, G_min, repulsion_base)
196
+ cum_prob = maxcut_hamming_cdf(init_thresholds(n_qubits), n_qubits, J_eff, degrees, quality, anneal_t, anneal_h)
197
+
198
+ degrees = None
199
+ J_eff = None
200
+
201
+ best_solution, best_value = sample_measurement(G_func, nodes, shots, thread_count, cum_prob, n_qubits, repulsion_base, is_spin_glass)
202
+
203
+ bit_string, l, r = get_cut(best_solution, nodes, n_qubits)
211
204
 
212
205
  if best_value < 0.0:
213
206
  # Best cut is trivial partition, all/empty
214
207
  return '0' * n_qubits, 0.0, (nodes, [])
215
208
 
216
- return bit_string, best_value, partition
209
+ return bit_string, best_value, (l, r)
@@ -389,29 +389,26 @@ def init_theta(h_mult, n_qubits, J_eff, degrees):
389
389
  return theta
390
390
 
391
391
 
392
- @njit
393
392
  def init_thresholds(n_qubits):
394
- n_bias = n_qubits - 1
393
+ n_bias = n_qubits + 1
395
394
  thresholds = np.empty(n_bias, dtype=np.float64)
396
395
  tot_prob = 0
397
- p = 1.0
398
- if n_qubits & 1:
399
- q = n_qubits // 2
400
- thresholds[q - 1] = p
401
- tot_prob = p
402
- p /= 2
403
- for q in range(1, n_qubits // 2):
404
- thresholds[q - 1] = p
405
- thresholds[n_bias - q] = p
396
+ p = 1
397
+ for q in range(n_qubits >> 1):
398
+ thresholds[q] = p
399
+ thresholds[n_bias - (q + 1)] = p
406
400
  tot_prob += 2 * p
407
- p /= 2
401
+ p = math.comb(n_qubits, q + 1)
402
+ if n_qubits & 1:
403
+ thresholds[n_qubits >> 1] = p
404
+ tot_prob += p
408
405
  thresholds /= tot_prob
409
406
 
410
407
  return thresholds
411
408
 
412
409
 
413
410
  @njit
414
- def probability_by_hamming_weight(J, h, z, theta, t, n_bias):
411
+ def probability_by_hamming_weight(J, h, z, theta, t, n_bias, normalized=True):
415
412
  zJ = z * J
416
413
  theta_c = ((np.pi if J > 0 else -np.pi) / 2) if abs(zJ) < epsilon else np.arcsin(max(-1.0, min(1.0, h / zJ)))
417
414
 
@@ -435,20 +432,21 @@ def probability_by_hamming_weight(J, h, z, theta, t, n_bias):
435
432
  if J > 0.0:
436
433
  return bias[::-1]
437
434
 
438
- return bias
435
+ if normalized:
436
+ bias /= bias.sum()
439
437
 
438
+ return bias
440
439
 
441
- @njit(parallel=True)
442
- def maxcut_hamming_cdf(n_qubits, J_func, degrees, quality, tot_t, h_mult):
443
- hamming_prob = init_thresholds(n_qubits)
444
440
 
441
+ @njit
442
+ def maxcut_hamming_cdf(hamming_prob, n_qubits, J_func, degrees, quality, tot_t, h_mult):
445
443
  n_steps = 1 << quality
446
444
  delta_t = tot_t / n_steps
447
445
  n_bias = n_qubits + 1
448
446
 
449
447
  theta = init_theta(h_mult, n_qubits, J_func, degrees)
450
448
 
451
- for qc in prange(n_qubits, n_steps * n_qubits):
449
+ for qc in range(n_qubits, n_steps * n_qubits):
452
450
  step = qc // n_qubits
453
451
  q = qc % n_qubits
454
452
  J_eff = J_func[q]
@@ -459,8 +457,8 @@ def maxcut_hamming_cdf(n_qubits, J_func, degrees, quality, tot_t, h_mult):
459
457
  t = step * delta_t
460
458
  tm1 = (step - 1) * delta_t
461
459
  h_t = h_mult * (tot_t - t)
462
- bias = probability_by_hamming_weight(J_eff, h_t, z, theta_eff, t, n_bias)
463
- last_bias = probability_by_hamming_weight(J_eff, h_t, z, theta_eff, tm1, n_bias)
460
+ bias = probability_by_hamming_weight(J_eff, h_t, z, theta_eff, t, n_bias, False)
461
+ last_bias = probability_by_hamming_weight(J_eff, h_t, z, theta_eff, tm1, n_bias, False)
464
462
  for i in range(n_bias):
465
463
  hamming_prob[i] += bias[i] - last_bias[i]
466
464
 
@@ -485,7 +483,7 @@ def sample_mag(cum_prob):
485
483
  while True:
486
484
  m = (left + right) >> 1
487
485
 
488
- if (cum_prob[m] >= p) and ((m == 0) or cum_prob[m - 1] < p):
486
+ if (cum_prob[m] >= p) and ((m == 0) or (cum_prob[m - 1] < p)):
489
487
  break
490
488
 
491
489
  if cum_prob[m] < p:
@@ -493,7 +491,7 @@ def sample_mag(cum_prob):
493
491
  else:
494
492
  right = m - 1
495
493
 
496
- return m + 1
494
+ return m
497
495
 
498
496
 
499
497
  @njit
pyqrackising/otoc.py ADDED
@@ -0,0 +1,221 @@
1
+ from .maxcut_tfim_util import probability_by_hamming_weight, sample_mag, opencl_context
2
+ import math
3
+ from numba import njit
4
+ import numpy as np
5
+ import sys
6
+
7
+
8
+ epsilon = opencl_context.epsilon
9
+
10
+
11
+ def get_otoc_hamming_distribution(J=-1.0, h=2.0, z=4, theta=0.0, t=5, n_qubits=65, pauli_strings = ['X' + 'I' * 64]):
12
+ n_bias = n_qubits + 1
13
+ if h <= epsilon:
14
+ bias = np.empty(n_bias, dtype=np.float64)
15
+ bias[0] = 1.0
16
+ return bias
17
+
18
+ diff_x = np.empty(n_bias, dtype=np.float64)
19
+ tot_prob = 0
20
+ p = 1.0
21
+ for q in range(n_qubits >> 1):
22
+ diff_x[q] = p
23
+ diff_x[n_bias - (q + 1)] = p
24
+ tot_prob += 2 * p
25
+ p = math.comb(n_qubits, q + 1)
26
+ if n_qubits & 1:
27
+ diff_x[n_qubits >> 1] = p
28
+ tot_prob += p
29
+ diff_x *= n_qubits / tot_prob
30
+
31
+ signal_frac = 0.0
32
+ diff_z = np.zeros(n_bias, dtype=np.float64)
33
+ diff_z[0] = n_qubits
34
+ for pauli_string in pauli_strings:
35
+ pauli_string = list(pauli_string)
36
+ if len(pauli_string) != n_qubits:
37
+ raise ValueError("OTOCS pauli_string must be same length as n_qubits! (Use 'I' for qubits that aren't changed.)")
38
+
39
+ term_signal = 0.5 * pauli_string.count('X') + pauli_string.count('Z') + 1.5 * pauli_string.count('Y')
40
+ if term_signal == 0:
41
+ continue
42
+
43
+ signal_frac -= term_signal
44
+
45
+ fwd = probability_by_hamming_weight(J, h, z, theta, t, n_qubits + 1)
46
+ rev = probability_by_hamming_weight(-J, -h, z, theta + np.pi, t, n_qubits + 1)
47
+ diff_theta = rev - fwd
48
+
49
+ phi = theta + np.pi / 2
50
+ fwd = probability_by_hamming_weight(-h, -J, z, phi, t, n_qubits + 1)
51
+ rev = probability_by_hamming_weight(h, J, z, phi - np.pi, t, n_qubits + 1)
52
+ diff_phi = rev - fwd
53
+
54
+ for b in pauli_string:
55
+ match b:
56
+ case 'X':
57
+ diff_z += diff_theta
58
+ case 'Z':
59
+ diff_x += diff_phi
60
+ case 'Y':
61
+ diff_z += diff_theta
62
+ diff_x += diff_phi
63
+ case _:
64
+ pass
65
+
66
+ # Normalize:
67
+ diff_z /= diff_z.sum()
68
+ diff_x /= diff_x.sum()
69
+
70
+ signal_frac = 2 ** signal_frac
71
+ diff_z = signal_frac * diff_z + (1 - signal_frac) * diff_x
72
+
73
+ # Normalize:
74
+ diff_z /= diff_z.sum()
75
+
76
+ return diff_z
77
+
78
+
79
+ @njit
80
+ def fix_cdf(hamming_prob):
81
+ tot_prob = 0.0
82
+ n_bias = len(hamming_prob)
83
+ cum_prob = np.empty(n_bias, dtype=np.float64)
84
+ for i in range(n_bias):
85
+ tot_prob += hamming_prob[i]
86
+ cum_prob[i] = tot_prob
87
+ cum_prob[-1] = 2.0
88
+
89
+ return cum_prob
90
+
91
+
92
+ @njit
93
+ def factor_width(width):
94
+ col_len = int(np.floor(np.sqrt(width)))
95
+ while ((width // col_len) * col_len) != width:
96
+ col_len -= 1
97
+ row_len = width // col_len
98
+
99
+ return row_len, col_len
100
+
101
+
102
+ # Provided by Google search AI
103
+ def find_all_str_occurrences(main_string, sub_string):
104
+ indices = []
105
+ start_index = 0
106
+ while True:
107
+ index = main_string.find(sub_string, start_index)
108
+ if index == -1:
109
+ break
110
+ indices.append(index)
111
+ start_index = index + 1 # Start searching after the found occurrence
112
+
113
+ return indices
114
+
115
+
116
+ def take_sample(n_qubits, sample, m, inv_dist):
117
+ indices = [i for i in range(n_qubits)]
118
+ tot_inv_dist = 0.0
119
+ for i in range(n_qubits):
120
+ tot_inv_dist += inv_dist[i]
121
+ selected = []
122
+ for i in range(m):
123
+ r = tot_inv_dist * np.random.random()
124
+ p = inv_dist[indices[0]]
125
+ idx = 0
126
+ while p < r:
127
+ idx += 1
128
+ if idx >= len(indices):
129
+ idx = len(indices) - 1
130
+ break
131
+ p += inv_dist[indices[idx]]
132
+ i = indices[idx]
133
+ del indices[idx]
134
+ selected.append(i)
135
+ tot_inv_dist -= inv_dist[i]
136
+ for i in selected:
137
+ sample |= 1 << i
138
+
139
+ return sample
140
+
141
+
142
+ def get_willow_inv_dist(butterfly_idx_x, butterfly_idx_z, n_qubits, row_len, col_len, t):
143
+ inv_dist = np.zeros(n_qubits, dtype=np.float64)
144
+ for idx in butterfly_idx_x:
145
+ b_row, b_col = divmod(idx, row_len)
146
+ for q in range(n_qubits):
147
+ q_row, q_col = divmod(q, row_len)
148
+ inv_dist[q] += abs(q_row - b_row) + abs(q_col - b_col)
149
+ for idx in butterfly_idx_z:
150
+ b_row, b_col = divmod(idx, row_len)
151
+ for q in range(n_qubits):
152
+ q_row, q_col = divmod(q, row_len)
153
+ inv_dist[q] -= abs(q_row - b_row) + abs(q_col - b_col)
154
+ inv_dist = 2 ** (inv_dist / t)
155
+
156
+ return inv_dist
157
+
158
+
159
+ def get_inv_dist(butterfly_idx_x, butterfly_idx_z, n_qubits, row_len, col_len, t):
160
+ inv_dist = np.zeros(n_qubits, dtype=np.float64)
161
+ half_row = row_len >> 1
162
+ half_col = col_len >> 1
163
+ for idx in butterfly_idx_x:
164
+ b_row, b_col = divmod(idx, row_len)
165
+ for q in range(n_qubits):
166
+ q_row, q_col = divmod(q, row_len)
167
+ row_d = abs(q_row - b_row)
168
+ if row_d > half_row:
169
+ row_d = row_len - row_d
170
+ col_d = abs(q_col - b_col)
171
+ if col_d > half_col:
172
+ col_d = col_len - col_d
173
+ inv_dist[q] += row_d + col_d
174
+ for idx in butterfly_idx_z:
175
+ b_row, b_col = divmod(idx, row_len)
176
+ for q in range(n_qubits):
177
+ q_row, q_col = divmod(q, row_len)
178
+ row_d = abs(q_row - b_row)
179
+ if row_d > half_row:
180
+ row_d = row_len - row_d
181
+ col_d = abs(q_col - b_col)
182
+ if col_d > half_col:
183
+ col_d = col_len - col_d
184
+ inv_dist[q] -= row_d + col_d
185
+ inv_dist = 2 ** (inv_dist / t)
186
+
187
+ return inv_dist
188
+
189
+
190
+ def generate_otoc_samples(J=-1.0, h=2.0, z=4, theta=0.0, t=5, n_qubits=65, pauli_strings = ['X' + 'I' * 64], shots=100, is_orbifold=True):
191
+ thresholds = fix_cdf(get_otoc_hamming_distribution(J, h, z, theta, t, n_qubits, pauli_strings))
192
+
193
+ row_len, col_len = factor_width(n_qubits)
194
+ inv_dist = np.zeros(n_qubits, dtype=np.float64)
195
+ for pauli_string in pauli_strings:
196
+ if (pauli_string.count('X') + pauli_string.count('Y') + pauli_string.count('Z')) == 0:
197
+ continue
198
+ butterfly_idx_x = find_all_str_occurrences(pauli_string, 'X')
199
+ butterfly_idx_z = find_all_str_occurrences(pauli_string, 'Z')
200
+ if is_orbifold:
201
+ inv_dist += get_inv_dist(butterfly_idx_x, butterfly_idx_z, n_qubits, row_len, col_len, t)
202
+ else:
203
+ inv_dist += get_willow_inv_dist(butterfly_idx_x, butterfly_idx_z, n_qubits, row_len, col_len, t)
204
+ inv_dist /= 2.0
205
+
206
+ qubit_pows = [1 << q for q in range(n_qubits)]
207
+ samples = []
208
+ for _ in range(shots):
209
+ # First dimension: Hamming weight
210
+ m = sample_mag(thresholds)
211
+ if m == 0:
212
+ samples.append(0)
213
+ continue
214
+ if m >= n_qubits:
215
+ samples.append((1 << n_qubits) - 1)
216
+ continue
217
+
218
+ # Second dimension: permutation within Hamming weight
219
+ samples.append(take_sample(n_qubits, 0, m, inv_dist))
220
+
221
+ return samples
@@ -376,6 +376,33 @@ def spin_glass_solver(
376
376
  max_energy = energy
377
377
  best_theta = state
378
378
  improved = True
379
+ continue
380
+
381
+ # Post-reheat phase
382
+ reheat_theta = state
383
+
384
+ # Single bit flips with O(n^2)
385
+ if is_opencl:
386
+ theta_buf = make_best_theta_buf(reheat_theta)
387
+ energy, state = run_bit_flips_opencl(False, n_qubits, single_bit_flips_kernel, max_energy, reheat_theta, theta_buf, G_m_buf, is_segmented, *opencl_args)
388
+ else:
389
+ energy, state = run_single_bit_flips(reheat_theta, is_spin_glass, G_m)
390
+ if energy > max_energy:
391
+ max_energy = energy
392
+ best_theta = state
393
+ improved = True
394
+ continue
395
+
396
+ # Double bit flips with O(n^3)
397
+ if is_opencl:
398
+ # theta_buf has not changed
399
+ energy, state = run_bit_flips_opencl(True, n_qubits, double_bit_flips_kernel, max_energy, reheat_theta, theta_buf, G_m_buf, is_segmented, *opencl_args)
400
+ else:
401
+ energy, state = run_double_bit_flips(reheat_theta, is_spin_glass, G_m, thread_count)
402
+ if energy > max_energy:
403
+ max_energy = energy
404
+ best_theta = state
405
+ improved = True
379
406
 
380
407
  bitstring, l, r = get_cut(best_theta, nodes, n_qubits)
381
408
  if is_spin_glass:
@@ -380,6 +380,33 @@ def spin_glass_solver_sparse(
380
380
  max_energy = energy
381
381
  best_theta = state
382
382
  improved = True
383
+ continue
384
+
385
+ # Post-reheat phase
386
+ reheat_theta = state
387
+
388
+ # Single bit flips with O(n^2)
389
+ if is_opencl:
390
+ theta_buf = make_best_theta_buf(reheat_theta)
391
+ energy, state = run_bit_flips_opencl(False, n_qubits, single_bit_flips_kernel, max_energy, reheat_theta, theta_buf, G_data_buf, G_rows_buf, G_cols_buf, is_segmented, *opencl_args)
392
+ else:
393
+ energy, state = run_single_bit_flips(reheat_theta, is_spin_glass, G_m.data, G_m.indptr, G_m.indices)
394
+ if energy > max_energy:
395
+ max_energy = energy
396
+ best_theta = state
397
+ improved = True
398
+ continue
399
+
400
+ # Double bit flips with O(n^3)
401
+ if is_opencl:
402
+ # theta_buf has not changed
403
+ energy, state = run_bit_flips_opencl(True, n_qubits, double_bit_flips_kernel, max_energy, reheat_theta, theta_buf, G_data_buf, G_rows_buf, G_cols_buf, is_segmented, *opencl_args)
404
+ else:
405
+ energy, state = run_double_bit_flips(reheat_theta, is_spin_glass, G_m.data, G_m.indptr, G_m.indices, thread_count)
406
+ if energy > max_energy:
407
+ max_energy = energy
408
+ best_theta = state
409
+ improved = True
383
410
 
384
411
  bitstring, l, r = get_cut(best_theta, nodes, n_qubits)
385
412
  if is_spin_glass:
@@ -261,6 +261,25 @@ def spin_glass_solver_streaming(
261
261
  max_energy = energy
262
262
  best_theta = state
263
263
  improved = True
264
+ continue
265
+
266
+ # Post-reheat phase
267
+ reheat_theta = state
268
+
269
+ # Single bit flips with O(n^2)
270
+ energy, state = run_single_bit_flips(reheat_theta, is_spin_glass, G_func, nodes)
271
+ if energy > max_energy:
272
+ max_energy = energy
273
+ best_theta = state
274
+ improved = True
275
+ continue
276
+
277
+ # Double bit flips with O(n^3)
278
+ energy, state = run_double_bit_flips(reheat_theta, is_spin_glass, G_func, nodes, thread_count)
279
+ if energy > max_energy:
280
+ max_energy = energy
281
+ best_theta = state
282
+ improved = True
264
283
 
265
284
  bitstring, l, r = get_cut(best_theta, nodes, n_qubits)
266
285
  if is_spin_glass:
pyqrackising/tsp.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from .maxcut_tfim_util import binary_search, opencl_context, to_scipy_sparse_upper_triangular
2
- from .maxcut_tfim import maxcut_tfim
2
+ from .spin_glass_solver import spin_glass_solver
3
3
  from concurrent.futures import ProcessPoolExecutor
4
- import time
5
4
  import itertools
6
5
  import networkx as nx
7
6
  from numba import njit, prange
@@ -639,7 +638,7 @@ def tsp_symmetric_header(G_m, nodes, quality, shots, anneal_t, anneal_h, repulsi
639
638
  for _ in range(multi_start):
640
639
  bits = ([], [])
641
640
  while (len(bits[0]) == 0) or (len(bits[1]) == 0):
642
- _, cut_value, bits = maxcut_tfim(G_m, quality=quality, shots=shots, anneal_t=anneal_t, anneal_h=anneal_h, repulsion_base=repulsion_base)
641
+ _, cut_value, bits, _ = spin_glass_solver(G_m, is_spin_glass=False, quality=quality, shots=shots, anneal_t=anneal_t, anneal_h=anneal_h, repulsion_base=repulsion_base)
643
642
  if cut_value > best_cut:
644
643
  best_cut = cut_value
645
644
  a, b = bits
@@ -851,7 +850,7 @@ def tsp_asymmetric_header(G_m, nodes, quality, shots, anneal_t, anneal_h, repuls
851
850
  for _ in range(multi_start):
852
851
  bits = ([], [])
853
852
  while (len(bits[0]) == 0) or (len(bits[1]) == 0):
854
- _, cut_value, bits = maxcut_tfim((G_m + G_m.T) / 2, quality=quality, shots=shots, anneal_t=anneal_t, anneal_h=anneal_h, repulsion_base=repulsion_base)
853
+ _, cut_value, bits, _ = spin_glass_solver((G_m + G_m.T) / 2, is_spin_glass=False, quality=quality, shots=shots, anneal_t=anneal_t, anneal_h=anneal_h, repulsion_base=repulsion_base)
855
854
  if cut_value > best_cut:
856
855
  best_cut = cut_value
857
856
  a, b = bits
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrackising
3
- Version: 9.2.3
3
+ Version: 9.5.12
4
4
  Summary: Fast MAXCUT, TSP, and sampling heuristics from near-ideal transverse field Ising model (TFIM)
5
5
  Home-page: https://github.com/vm6502q/PyQrackIsing
6
6
  Author: Dan Strano
@@ -0,0 +1,21 @@
1
+ pyqrackising/__init__.py,sha256=Q-fHQRApRseeFnnO6VfD1LpjPHCVkWoz0Gnaqmv6lp0,884
2
+ pyqrackising/convert_tensor_network_to_tsp.py,sha256=IbZdPfHQ2bzYqDXUdHfwdZJh-pC8lri_cAgESaskQWI,3450
3
+ pyqrackising/generate_tfim_samples.py,sha256=IlAz1l8oLExO6wJBO8LCQKlU_4ZPlyGsNE8xUt_iTrg,4762
4
+ pyqrackising/kernels.cl,sha256=Q9LZ1oBOZ2A_VsQ8uRUGQKNVpzqVXYwhG6c27FO6ljI,27004
5
+ pyqrackising/maxcut_tfim.py,sha256=05nRjk5hhEjNFjtXdKVObL0cYYuJ8URfcjrOGK5tGi4,10106
6
+ pyqrackising/maxcut_tfim_sparse.py,sha256=dddVdu54bvA5NCmknkWthnszPsCsC0OKoJswb3qI-hA,11205
7
+ pyqrackising/maxcut_tfim_streaming.py,sha256=EcRXKurqLiQs6pMNz-rhMp2YQzRXD726RnkXsPt4IJ0,6346
8
+ pyqrackising/maxcut_tfim_util.py,sha256=lbyNrUnjPajUUVDbSh1pRwru0eshFsP2hOrVppUf4Co,16865
9
+ pyqrackising/otoc.py,sha256=PKmpjwkGpMmwXGWmeqtvbVj1NfE35i9kkIdjQnDUZKE,7208
10
+ pyqrackising/spin_glass_solver.py,sha256=YtsIfYfpwhEMQPVd_sbjqpz6nQcrv8p2mUBYZ-wRpnM,13792
11
+ pyqrackising/spin_glass_solver_sparse.py,sha256=E4Ft7H-uKXZyq1cS7Z77AgdkL5fMlKMK6DwnXgrgOhk,14740
12
+ pyqrackising/spin_glass_solver_streaming.py,sha256=xpWq63yODMzBPQQtpDuB1Tro6ta8pPbVwc0ZkDWVASk,10018
13
+ pyqrackising/tfim_magnetization.py,sha256=On1MhCNGGHRxJFRmCOpMcdqQJiy25gWkjz0Ka8i5f-Q,499
14
+ pyqrackising/tfim_square_magnetization.py,sha256=9uJCT8ytyufcGFrZiignjCkWJr9UcP44sAAy0BIBw34,531
15
+ pyqrackising/tsp.py,sha256=kqDxU2RCjad-T4tW_C9WO1I-COSwX7fHB6VhIuQsjfQ,62464
16
+ pyqrackising/tsp_maxcut.py,sha256=ngxfSJgePXVwJXfNXYdk4jv1ISznx8zHOqR-Vbf33B0,9772
17
+ pyqrackising-9.5.12.dist-info/licenses/LICENSE.md,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
18
+ pyqrackising-9.5.12.dist-info/METADATA,sha256=BzfdPzf4NK65jk4YO3KtSlSfkSlLoRrWAU827KVmVRM,1144
19
+ pyqrackising-9.5.12.dist-info/WHEEL,sha256=tt4-VHVHv-wywtMZy9Hvv8M6zRlbacCdJyvDZp-_L0E,105
20
+ pyqrackising-9.5.12.dist-info/top_level.txt,sha256=bxlfGuLwzeVEI8Jm5D9HvC_WedgvvkSrpFwbGDjg-Ag,13
21
+ pyqrackising-9.5.12.dist-info/RECORD,,
@@ -1,20 +0,0 @@
1
- pyqrackising/__init__.py,sha256=nrX4qYm9kEPcjbiy3KEMrLSPBtkVGFXVF3iosptSv1w,722
2
- pyqrackising/convert_tensor_network_to_tsp.py,sha256=IbZdPfHQ2bzYqDXUdHfwdZJh-pC8lri_cAgESaskQWI,3450
3
- pyqrackising/generate_tfim_samples.py,sha256=wi1I-pqZelYe3qoKQgW9mdDu41LBHIcUkl4cbYTlXEw,4325
4
- pyqrackising/kernels.cl,sha256=Q9LZ1oBOZ2A_VsQ8uRUGQKNVpzqVXYwhG6c27FO6ljI,27004
5
- pyqrackising/maxcut_tfim.py,sha256=U1nNjyfMS48TtTQk7TRf5_VF3pVPfAcZEa2awc2nR8k,10862
6
- pyqrackising/maxcut_tfim_sparse.py,sha256=eenJNSEwRvgwACfKoH0tj6rpn7uqH6nNTBuxUPh_lDg,11941
7
- pyqrackising/maxcut_tfim_streaming.py,sha256=FkBsRoXSRhv4gUeN9O7Ivx54oxq_SqiCDKnYsyxU4bs,6664
8
- pyqrackising/maxcut_tfim_util.py,sha256=2-tJrBMe7A5hjY-Q1rQKm6PHJfFOgL5WpPGhKndIf_Y,16856
9
- pyqrackising/spin_glass_solver.py,sha256=WzH3Ud0MGHsaWDfBDZ2dPUFArmG1S4A318SiA0c03Fk,12687
10
- pyqrackising/spin_glass_solver_sparse.py,sha256=UmpHO7IbPc3uaTdYRl2ilBmH0fTQuIphiJIVEBIGNdo,13521
11
- pyqrackising/spin_glass_solver_streaming.py,sha256=ccvuxN042R2TKhd1JJrYWDI1001g6OzzOmaIMaNse-w,9400
12
- pyqrackising/tfim_magnetization.py,sha256=On1MhCNGGHRxJFRmCOpMcdqQJiy25gWkjz0Ka8i5f-Q,499
13
- pyqrackising/tfim_square_magnetization.py,sha256=9uJCT8ytyufcGFrZiignjCkWJr9UcP44sAAy0BIBw34,531
14
- pyqrackising/tsp.py,sha256=UbWxMdmf9yEAAoGULWYbQW5c-dgOkGiuE_uyY3Js6KU,62404
15
- pyqrackising/tsp_maxcut.py,sha256=ngxfSJgePXVwJXfNXYdk4jv1ISznx8zHOqR-Vbf33B0,9772
16
- pyqrackising-9.2.3.dist-info/licenses/LICENSE.md,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
17
- pyqrackising-9.2.3.dist-info/METADATA,sha256=SGmIq39ouVgjTbk9R8jb-DDNmwxPHW4GMkc_ajFx7T8,1143
18
- pyqrackising-9.2.3.dist-info/WHEEL,sha256=tt4-VHVHv-wywtMZy9Hvv8M6zRlbacCdJyvDZp-_L0E,105
19
- pyqrackising-9.2.3.dist-info/top_level.txt,sha256=bxlfGuLwzeVEI8Jm5D9HvC_WedgvvkSrpFwbGDjg-Ag,13
20
- pyqrackising-9.2.3.dist-info/RECORD,,