pyqrackising 9.3.0__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.
@@ -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,7 +89,7 @@ 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
 
@@ -104,6 +108,13 @@ def fix_cdf(hamming_prob):
104
108
 
105
109
  @njit
106
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
107
118
  bias = probability_by_hamming_weight(J, h, z, theta, t, n_qubits + 1)
108
119
  return bias / bias.sum()
109
120
 
@@ -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,22 +389,19 @@ 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
@@ -441,17 +438,15 @@ def probability_by_hamming_weight(J, h, z, theta, t, n_bias, normalized=True):
441
438
  return bias
442
439
 
443
440
 
444
- @njit(parallel=True)
445
- def maxcut_hamming_cdf(n_qubits, J_func, degrees, quality, tot_t, h_mult):
446
- hamming_prob = init_thresholds(n_qubits)
447
-
441
+ @njit
442
+ def maxcut_hamming_cdf(hamming_prob, n_qubits, J_func, degrees, quality, tot_t, h_mult):
448
443
  n_steps = 1 << quality
449
444
  delta_t = tot_t / n_steps
450
445
  n_bias = n_qubits + 1
451
446
 
452
447
  theta = init_theta(h_mult, n_qubits, J_func, degrees)
453
448
 
454
- for qc in prange(n_qubits, n_steps * n_qubits):
449
+ for qc in range(n_qubits, n_steps * n_qubits):
455
450
  step = qc // n_qubits
456
451
  q = qc % n_qubits
457
452
  J_eff = J_func[q]
@@ -488,7 +483,7 @@ def sample_mag(cum_prob):
488
483
  while True:
489
484
  m = (left + right) >> 1
490
485
 
491
- 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)):
492
487
  break
493
488
 
494
489
  if cum_prob[m] < p:
@@ -496,7 +491,7 @@ def sample_mag(cum_prob):
496
491
  else:
497
492
  right = m - 1
498
493
 
499
- return m + 1
494
+ return m
500
495
 
501
496
 
502
497
  @njit
pyqrackising/otoc.py CHANGED
@@ -1,59 +1,79 @@
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
+ import math
2
3
  from numba import njit
3
4
  import numpy as np
4
- import random
5
+ import sys
5
6
 
6
7
 
7
- def get_otoc_hamming_distribution(J=-1.0, h=2.0, z=4, theta=0.174532925199432957, t=5, n_qubits=56, cycles=1, pauli_string = 'X' + 'I' * 55):
8
- pauli_string = list(pauli_string)
9
- if len(pauli_string) != n_qubits:
10
- raise ValueError("OTOCS pauli_string must be same length as n_qubits! (Use 'I' for qubits that aren't changed.)")
8
+ epsilon = opencl_context.epsilon
11
9
 
12
- fwd = probability_by_hamming_weight(J, h, z, theta, t, n_qubits + 1, False)
13
- rev = probability_by_hamming_weight(-J, -h, z, -theta, t, n_qubits + 1, False)
14
- diff_theta = rev - fwd
15
-
16
- phi = theta - np.pi / 2
17
- fwd = probability_by_hamming_weight(J, h, z, phi, t, n_qubits + 1, False)
18
- rev = probability_by_hamming_weight(-J, -h, z, -phi, t, n_qubits + 1, False)
19
- diff_phi = rev - fwd
20
-
21
- diff_theta *= cycles
22
- diff_phi *= cycles
23
- diff_lam = 0.5 * (diff_theta + diff_phi)
24
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]):
25
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
26
32
  diff_z = np.zeros(n_bias, dtype=np.float64)
27
- diff_x = np.zeros(n_bias, dtype=np.float64)
28
- diff_y = np.zeros(n_bias, dtype=np.float64)
29
- for b in pauli_string:
30
- match b:
31
- case 'X':
32
- diff_z += diff_theta
33
- diff_x += diff_lam
34
- diff_y += diff_phi
35
- case 'Z':
36
- diff_z += diff_phi
37
- diff_x += diff_theta
38
- diff_y += diff_lam
39
- case 'Y':
40
- diff_z += diff_lam
41
- diff_x += diff_phi
42
- diff_y += diff_theta
43
-
44
- diff_z -= diff_z.min()
45
- diff_x -= diff_x.min()
46
- diff_y -= diff_y.min()
47
-
48
- diff_z[0] += n_bias
49
- diff_x[0] += n_bias
50
- diff_y[0] += n_bias
51
-
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:
52
67
  diff_z /= diff_z.sum()
53
68
  diff_x /= diff_x.sum()
54
- diff_y /= diff_y.sum()
55
69
 
56
- return { 'X': diff_x, 'Y': diff_y, 'Z': diff_z }
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
57
77
 
58
78
 
59
79
  @njit
@@ -70,104 +90,132 @@ def fix_cdf(hamming_prob):
70
90
 
71
91
 
72
92
  @njit
73
- def take_all(b, basis, sample):
74
- for i in range(len(basis)):
75
- if basis[i] == b:
76
- sample |= (1 << i)
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
77
98
 
78
- return sample
99
+ return row_len, col_len
79
100
 
80
101
 
81
- def take_sample(b, basis, sample, m):
102
+ # Provided by Google search AI
103
+ def find_all_str_occurrences(main_string, sub_string):
82
104
  indices = []
83
- for i in range(len(basis)):
84
- if basis[i] == b:
85
- indices.append(i)
86
- indices = random.sample(indices, m)
87
- for i in 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:
88
137
  sample |= 1 << i
89
138
 
90
139
  return sample
91
140
 
92
141
 
93
- def generate_otoc_samples(J=-1.0, h=2.0, z=4, theta=0.174532925199432957, t=5, n_qubits=56, cycles=1, pauli_string = 'X' + 'I' * 55, shots=100, measurement_basis='Z' * 56):
94
- pauli_string = list(pauli_string)
95
- if len(pauli_string) != n_qubits:
96
- raise ValueError("OTOC pauli_string must be same length as n_qubits! (Use 'I' for qubits that aren't changed.)")
97
-
98
- measurement_basis = list(measurement_basis)
99
- if len(measurement_basis) != n_qubits:
100
- raise ValueError("OTOC measurement_basis must be same length as measurement_basis! (Use 'I' for excluded qubits.)")
101
-
102
- basis_x, basis_y, basis_z = [], [], []
103
- for b in pauli_string:
104
- if b == 'Z':
105
- basis_z.append('X')
106
- basis_y.append('Z')
107
- basis_x.append('I')
108
- elif b == 'X':
109
- basis_z.append('Z')
110
- basis_y.append('I')
111
- basis_x.append('X')
112
- elif b == 'Y':
113
- basis_z.append('I')
114
- basis_y.append('I')
115
- basis_x.append('X')
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)
116
202
  else:
117
- basis_z.append('I')
118
- basis_y.append('X')
119
- basis_x.append('Z')
120
-
121
- bases = { 'X': basis_x, 'Y': basis_y, 'Z': basis_z }
122
- thresholds = { key: fix_cdf(value) for key, value in get_otoc_hamming_distribution(J, h, z, theta, t, n_qubits, cycles, pauli_string).items() }
123
-
124
- samples_3_axis = {}
125
- for key, value in thresholds.items():
126
- basis = bases[key]
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
127
205
 
206
+ qubit_pows = [1 << q for q in range(n_qubits)]
128
207
  samples = []
129
208
  for _ in range(shots):
130
- sample_3_axis = { 'X': 0, 'Y': 0, 'Z': 0 }
131
- for key, value in thresholds.items():
132
- basis = bases[key]
133
-
134
- # First dimension: Hamming weight
135
- m = sample_mag(value)
136
- if m >= n_qubits:
137
- sample_3_axis[key] = (1 << n_qubits) - 1
138
- continue
139
-
140
- # Second dimension: permutation within Hamming weight
141
- z_count = basis.count('Z')
142
- if z_count > m:
143
- sample_3_axis[key] = take_sample('Z', basis, sample_3_axis[key], m)
144
- continue
145
- m -= z_count
146
- sample_3_axis[key] = take_all('Z', basis, sample_3_axis[key])
147
- if m == 0:
148
- continue
149
-
150
- i_count = basis.count('I')
151
- if i_count > m:
152
- sample_3_axis[key] = take_sample('I', basis, sample_3_axis[key], m)
153
- continue
154
- m -= i_count
155
- sample_3_axis[key] = take_all('I', basis, sample_3_axis[key])
156
- if m == 0:
157
- continue
158
-
159
- sample_3_axis[key] = take_sample('X', basis, sample_3_axis[key], m)
160
-
161
- sample = 0
162
- j = 0
163
- for i in range(n_qubits):
164
- base = measurement_basis[i]
165
- if base not in ['X', 'Y', 'Z']:
166
- continue
167
- if (sample_3_axis[base] >> i) & 1:
168
- sample |= 1 << j
169
- j += 1
170
-
171
- samples.append(sample)
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))
172
220
 
173
221
  return samples
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrackising
3
- Version: 9.3.0
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,21 +0,0 @@
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=NZh9dMW7RurMkfawFLdWI8WFZmXS5CTosTQDrwCBOSk,4503
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=D5tIbfKVLazR6f_INAoFypNJA0iuPin_bkiIM7-_pvg,16937
9
- pyqrackising/otoc.py,sha256=IzARDeNvuGwL_RHBotQsth5d7iz5oGbsIharCrCLUyg,5620
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.3.0.dist-info/licenses/LICENSE.md,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
18
- pyqrackising-9.3.0.dist-info/METADATA,sha256=O5jeEBKDsW3iCI2rFiIFTbS997jR8rNZ4ARO-1TrVs4,1143
19
- pyqrackising-9.3.0.dist-info/WHEEL,sha256=tt4-VHVHv-wywtMZy9Hvv8M6zRlbacCdJyvDZp-_L0E,105
20
- pyqrackising-9.3.0.dist-info/top_level.txt,sha256=bxlfGuLwzeVEI8Jm5D9HvC_WedgvvkSrpFwbGDjg-Ag,13
21
- pyqrackising-9.3.0.dist-info/RECORD,,