lightsolver-lib 0.7.0__tar.gz → 0.8.0__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.
- {lightsolver_lib-0.7.0 → lightsolver_lib-0.8.0}/PKG-INFO +1 -1
- {lightsolver_lib-0.7.0 → lightsolver_lib-0.8.0}/lightsolver_lib/__init__.py +3 -3
- {lightsolver_lib-0.7.0 → lightsolver_lib-0.8.0}/lightsolver_lib/lightsolver_lib.py +245 -21
- {lightsolver_lib-0.7.0 → lightsolver_lib-0.8.0}/lightsolver_lib.egg-info/PKG-INFO +1 -1
- {lightsolver_lib-0.7.0 → lightsolver_lib-0.8.0}/pyproject.toml +1 -1
- {lightsolver_lib-0.7.0 → lightsolver_lib-0.8.0}/LICENSE +0 -0
- {lightsolver_lib-0.7.0 → lightsolver_lib-0.8.0}/README.md +0 -0
- {lightsolver_lib-0.7.0 → lightsolver_lib-0.8.0}/lightsolver_lib.egg-info/SOURCES.txt +0 -0
- {lightsolver_lib-0.7.0 → lightsolver_lib-0.8.0}/lightsolver_lib.egg-info/dependency_links.txt +0 -0
- {lightsolver_lib-0.7.0 → lightsolver_lib-0.8.0}/lightsolver_lib.egg-info/requires.txt +0 -0
- {lightsolver_lib-0.7.0 → lightsolver_lib-0.8.0}/lightsolver_lib.egg-info/top_level.txt +0 -0
- {lightsolver_lib-0.7.0 → lightsolver_lib-0.8.0}/setup.cfg +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import numpy
|
|
2
|
+
import plotly.graph_objects as go
|
|
3
|
+
import plotly.express as px
|
|
2
4
|
|
|
3
5
|
def calc_ising_energies(problem_mat_ising: numpy.ndarray, states_ising: numpy.ndarray) -> numpy.ndarray:
|
|
4
6
|
"""
|
|
@@ -124,8 +126,8 @@ def best_energy_search_xy(state: numpy.ndarray, probmat_ising: numpy.ndarray) ->
|
|
|
124
126
|
- `best_energy` - Energy of the best state
|
|
125
127
|
"""
|
|
126
128
|
is_single_state = len(state.shape) == 1
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
if is_single_state:
|
|
130
|
+
state = state.reshape(-1, 1)
|
|
129
131
|
|
|
130
132
|
n_lasers = state.shape[0]
|
|
131
133
|
n_states = 1 if is_single_state else state.shape[1]
|
|
@@ -257,21 +259,18 @@ class XYModelParams():
|
|
|
257
259
|
## Parameters for converting an Ising model to the XY model.
|
|
258
260
|
|
|
259
261
|
### Members:
|
|
260
|
-
- `
|
|
261
|
-
- `alphaI` - Vortex lasers self coupling strength - default: 0.7
|
|
262
|
+
- `minSelfCoup` - Vortex lasers minimal allowed self coupling strength - default: 0.5
|
|
262
263
|
- `coupAmp` - Coupling amplitude, default: 0.3
|
|
263
264
|
- `exFieldCoup` - Coupling of external field, default : 0.3
|
|
264
265
|
"""
|
|
265
|
-
def __init__(self,
|
|
266
|
-
self.
|
|
267
|
-
self.alphaR = alphaR
|
|
266
|
+
def __init__(self, minSelfCoup = 0.7, coupAmp = 0.3, exFieldCoup = 0.3):
|
|
267
|
+
self.minSelfCoup = minSelfCoup
|
|
268
268
|
self.coupAmp = coupAmp
|
|
269
269
|
self.exFieldCoup = exFieldCoup
|
|
270
270
|
|
|
271
271
|
def __eq__(self, other: object) -> bool:
|
|
272
272
|
return isinstance(other, XYModelParams) and \
|
|
273
|
-
self.
|
|
274
|
-
self.alphaR == other.alphaR and \
|
|
273
|
+
self.minSelfCoup == other.minSelfCoup and \
|
|
275
274
|
self.coupAmp == other.coupAmp and \
|
|
276
275
|
self.exFieldCoup == other.exFieldCoup
|
|
277
276
|
|
|
@@ -289,18 +288,243 @@ def coupling_matrix_xy(problem_matrix:numpy.ndarray, modelParamsXY:XYModelParams
|
|
|
289
288
|
- `coupling_matrix` - The generated coupling matrix for the XY model
|
|
290
289
|
"""
|
|
291
290
|
N = problem_matrix.shape[0]
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
coupling_matrix = numpy.zeros((N + 1, N + 1), dtype=numpy.float32)
|
|
296
|
-
coupling_matrix[1:, 1:] = - (problem_matrix / max_sum_rows) * modelParamsXY.coupAmp
|
|
297
|
-
numpy.fill_diagonal(coupling_matrix[1:, 1:], modelParamsXY.alphaI)
|
|
298
|
-
coupling_matrix[1:, 0] = - modelParamsXY.exFieldCoup * (problem_matrix.diagonal() / max_sum_rows)
|
|
299
|
-
coupling_matrix[0, 0] = modelParamsXY.alphaR
|
|
291
|
+
problem_matrix[numpy.diag_indices(N)] = 0.5 * numpy.diag(problem_matrix)
|
|
292
|
+
max_sum_rows = numpy.max(numpy.sum(numpy.abs(problem_matrix),axis=1))
|
|
293
|
+
row_sum = 1
|
|
300
294
|
|
|
295
|
+
if external_field:
|
|
296
|
+
external_field_half = numpy.diag(problem_matrix)
|
|
297
|
+
sum_ex_field_half = numpy.sum(numpy.abs(external_field_half))
|
|
298
|
+
M = max(sum_ex_field_half, max_sum_rows) / (1 - modelParamsXY.minSelfCoup)
|
|
299
|
+
|
|
300
|
+
coupling_matrix = numpy.zeros((N + 1, N + 1),dtype=numpy.float32)
|
|
301
|
+
coupling_matrix[1:,1:] = -(problem_matrix / M) * modelParamsXY.coupAmp
|
|
302
|
+
coupling_matrix[1:, 0] = -modelParamsXY.exFieldCoup * (external_field_half / M)
|
|
303
|
+
coupling_matrix[0, 1:] = -modelParamsXY.exFieldCoup * (external_field_half / M)
|
|
304
|
+
coupling_matrix[numpy.diag_indices(N + 1)] = row_sum - numpy.sum(numpy.abs(coupling_matrix), axis=1)
|
|
301
305
|
else:
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
numpy.
|
|
306
|
+
M = max_sum_rows / (1 - modelParamsXY.minSelfCoup)
|
|
307
|
+
|
|
308
|
+
coupling_matrix = numpy.zeros((N, N), dtype=numpy.float32)
|
|
309
|
+
coupling_matrix = -(problem_matrix / M) * modelParamsXY.coupAmp
|
|
310
|
+
coupling_matrix[numpy.diag_indices(N)] = row_sum - numpy.sum(numpy.abs(coupling_matrix), axis=1)
|
|
311
|
+
|
|
312
|
+
assert (numpy.diag(coupling_matrix) >= modelParamsXY.minSelfCoup).all()
|
|
313
|
+
assert (numpy.sum(numpy.abs(coupling_matrix), axis=1) <= 1).all()
|
|
314
|
+
|
|
315
|
+
return coupling_matrix
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def embed_coupmat(coupling_matrix, indices=None, total_laser_num=15):
|
|
319
|
+
'''
|
|
320
|
+
## Embed a coupling matrix into a larger matrix of size total_laser_num x total_laser_num.
|
|
321
|
+
|
|
322
|
+
### Parameters:
|
|
323
|
+
- coupling_matrix: (N x N) complex matrix to embed
|
|
324
|
+
- indices: list of indices at which to embed the matrix. If None, embed from index 0.
|
|
325
|
+
- total_laser_num: total size of the output matrix (default 15)
|
|
326
|
+
|
|
327
|
+
### Returns:
|
|
328
|
+
- embedded: (total_laser_num x total_laser_num) matrix with the coupling matrix embedded
|
|
329
|
+
'''
|
|
330
|
+
N = coupling_matrix.shape[0]
|
|
331
|
+
|
|
332
|
+
embedded = 0.001 * numpy.eye(total_laser_num).astype(numpy.complex64) # small coupling for the rest of the lasers
|
|
333
|
+
# (value will remain for those not included in the problem)
|
|
334
|
+
|
|
335
|
+
if indices is None:
|
|
336
|
+
embedded[:N, :N] = coupling_matrix
|
|
337
|
+
else:
|
|
338
|
+
if len(indices) == 1:
|
|
339
|
+
start = indices[0]
|
|
340
|
+
embedded[start:start+N, start:start+N] = coupling_matrix
|
|
341
|
+
else:
|
|
342
|
+
indices = numpy.array(indices)
|
|
343
|
+
embedded[numpy.ix_(indices, indices)] = coupling_matrix
|
|
344
|
+
|
|
345
|
+
return embedded
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def analyze_sol_XY(problem_matrix, phases_diffs):
|
|
349
|
+
'''
|
|
350
|
+
## Function to analyze the solution from LPU and return binary state and its energy.
|
|
351
|
+
## It recives the problem matrix and the phase difference solution and returns binary solution according to XY mapping.
|
|
352
|
+
### Parameters:
|
|
353
|
+
- `problem_matrix` - The problem matrix (Ising model)
|
|
354
|
+
- `phases_diffs` - The phase differences solution from the LPU
|
|
355
|
+
### Returns:
|
|
356
|
+
- `min_energy` - The minimum energy found
|
|
357
|
+
- `best_state` - The binary state corresponding to the minimum energy
|
|
358
|
+
'''
|
|
359
|
+
#
|
|
360
|
+
phases = (numpy.cumsum(phases_diffs, axis=1)) % (2 * numpy.pi)
|
|
361
|
+
N = phases.shape[1]
|
|
362
|
+
|
|
363
|
+
# Initialize:
|
|
364
|
+
energy = numpy.zeros(phases.shape[0])
|
|
365
|
+
x = numpy.zeros(shape=phases.shape)
|
|
366
|
+
correction = numpy.linspace(0, 0.1, N) # optional error sizes
|
|
367
|
+
correction = numpy.append(correction, -1*correction) # error may go both ways
|
|
368
|
+
|
|
369
|
+
def _to_scalar(value):
|
|
370
|
+
# best_energy_search_xy may return a scalar-like ndarray; normalize it.
|
|
371
|
+
return float(numpy.asarray(value).reshape(-1)[0])
|
|
372
|
+
|
|
373
|
+
for i in range(phases.shape[0]) :
|
|
305
374
|
|
|
306
|
-
|
|
375
|
+
min_bin_state, eng_new = best_energy_search_xy(numpy.exp(1J * phases[i, :]), numpy.real(problem_matrix))
|
|
376
|
+
min_bin_energy = _to_scalar(eng_new) if numpy.asarray(eng_new).size == 1 else eng_new
|
|
377
|
+
|
|
378
|
+
energies = [numpy.Inf]
|
|
379
|
+
|
|
380
|
+
for corr in correction:
|
|
381
|
+
correction_array = corr * numpy.arange(phases[i, :].shape[0])
|
|
382
|
+
new_state = phases[i, :] + correction_array
|
|
383
|
+
sol_new =(new_state[:])
|
|
384
|
+
laser_sol_new, eng_new = best_energy_search_xy(numpy.exp(1J * sol_new), numpy.real(problem_matrix))
|
|
385
|
+
eng_new_value = _to_scalar(eng_new) if numpy.asarray(eng_new).size == 1 else eng_new
|
|
386
|
+
if eng_new_value < min_bin_energy:
|
|
387
|
+
min_bin_state = laser_sol_new
|
|
388
|
+
min_bin_energy = eng_new_value
|
|
389
|
+
energies.append(eng_new_value)
|
|
390
|
+
|
|
391
|
+
corrected = numpy.argmin(energies)
|
|
392
|
+
x[i, :] = numpy.asarray(min_bin_state).reshape(-1)[:N]
|
|
393
|
+
energy[i] = numpy.min(energies)
|
|
394
|
+
|
|
395
|
+
min_ind = numpy.argmin(energy)
|
|
396
|
+
|
|
397
|
+
return numpy.min(energy), x[min_ind,:]
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def generate_animation(outWave, save=False):
|
|
401
|
+
'''
|
|
402
|
+
## Generate an animated polar plot using Plotly to visualize the evolution of laser states.
|
|
403
|
+
### Parameters:
|
|
404
|
+
- `outWave` - A 2D numpy array where each row represents the state of lasers at a specific time/frame.
|
|
405
|
+
- `save` - Boolean flag to save the animation as an HTML file (default is False).
|
|
406
|
+
### Returns:
|
|
407
|
+
- None (displays the animation and optionally saves it).
|
|
408
|
+
'''
|
|
409
|
+
# Number of frames in the animation
|
|
410
|
+
num_frames = outWave.shape[0]
|
|
411
|
+
color_scale = px.colors.qualitative.Set1[:outWave.shape[1]]
|
|
412
|
+
|
|
413
|
+
fig = go.Figure()
|
|
414
|
+
|
|
415
|
+
# Create text for the polar plot
|
|
416
|
+
N = outWave.shape[1]
|
|
417
|
+
text = [str(int(a)) for a in numpy.linspace(1, N, num=N, endpoint=True)]
|
|
418
|
+
|
|
419
|
+
# Create data for the initial frame
|
|
420
|
+
theta = numpy.angle(outWave[0, :]) * 180 / numpy.pi
|
|
421
|
+
radius = numpy.abs(outWave[0, :])
|
|
422
|
+
initial_frame_data = [go.Scatterpolar(r=radius, theta=theta, mode='markers+text', marker=dict(size=10, color=color_scale), text=text, textposition='top center', showlegend=False),
|
|
423
|
+
]
|
|
424
|
+
|
|
425
|
+
# Create the layout
|
|
426
|
+
layout = go.Layout(
|
|
427
|
+
showlegend=False,
|
|
428
|
+
polar=dict(radialaxis=dict(visible=True)),
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
# Create the figure with the initial frame
|
|
432
|
+
fig.add_trace(initial_frame_data[0]) # Add the initial trace
|
|
433
|
+
fig.update_layout(layout)
|
|
434
|
+
fig.update_xaxes(title_text="iteration")
|
|
435
|
+
fig.update_layout(showlegend=True)
|
|
436
|
+
|
|
437
|
+
# Define animation frames
|
|
438
|
+
animation_frames = []
|
|
439
|
+
|
|
440
|
+
for i in range(1, num_frames):
|
|
441
|
+
theta = numpy.angle(outWave[i, :]) * 180 / numpy.pi
|
|
442
|
+
r_values = numpy.abs(outWave[i, :])
|
|
443
|
+
frame_data = [go.Scatterpolar(r=r_values, theta=theta, mode='markers+text', marker=dict(size=10, color=color_scale), name='polar plot', text=text, textposition='top center', showlegend=False),
|
|
444
|
+
]
|
|
445
|
+
animation_frames.append(go.Frame(data=frame_data, name=f"frame_{i}"))
|
|
446
|
+
|
|
447
|
+
# Add frames to the figure
|
|
448
|
+
fig.frames = animation_frames
|
|
449
|
+
|
|
450
|
+
# Define animation options
|
|
451
|
+
animation_opts = dict(
|
|
452
|
+
frame=dict(duration=500, redraw=True),
|
|
453
|
+
fromcurrent=True
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
# Add play button
|
|
457
|
+
fig.update_layout(
|
|
458
|
+
updatemenus=[
|
|
459
|
+
{
|
|
460
|
+
'buttons': [
|
|
461
|
+
{
|
|
462
|
+
'args': [None, {'frame': {'duration': 500, 'redraw': True}, 'fromcurrent': True}],
|
|
463
|
+
'label': 'Play',
|
|
464
|
+
'method': 'animate'
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
'args': [[None], {'frame': {'duration': 0, 'redraw': True}, 'mode': 'immediate', 'transition': {'duration': 0}}],
|
|
468
|
+
'label': 'Pause',
|
|
469
|
+
'method': 'animate'
|
|
470
|
+
}
|
|
471
|
+
],
|
|
472
|
+
'direction': 'left',
|
|
473
|
+
'pad': {'r': 10, 't': 87},
|
|
474
|
+
'showactive': False,
|
|
475
|
+
'type': 'buttons',
|
|
476
|
+
'x': 0.1,
|
|
477
|
+
'xanchor': 'right',
|
|
478
|
+
'y': 0.2,
|
|
479
|
+
'yanchor': 'top'
|
|
480
|
+
}
|
|
481
|
+
],
|
|
482
|
+
template= "seaborn" # "plotly_dark"
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
# Define slider steps
|
|
486
|
+
steps = []
|
|
487
|
+
for i in range(num_frames):
|
|
488
|
+
step = dict(
|
|
489
|
+
method="animate",
|
|
490
|
+
args=[
|
|
491
|
+
[f"frame_{i}"],
|
|
492
|
+
dict(
|
|
493
|
+
mode="immediate",
|
|
494
|
+
frame=dict(duration=300, redraw=True),
|
|
495
|
+
transition=dict(duration=0)
|
|
496
|
+
),
|
|
497
|
+
],
|
|
498
|
+
label=str(i)
|
|
499
|
+
)
|
|
500
|
+
steps.append(step)
|
|
501
|
+
|
|
502
|
+
# Add slider to layout
|
|
503
|
+
fig.update_layout(
|
|
504
|
+
sliders=[dict(
|
|
505
|
+
active=0,
|
|
506
|
+
currentvalue={"prefix": "Frame: "},
|
|
507
|
+
pad={"t": 50},
|
|
508
|
+
steps=steps
|
|
509
|
+
)]
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
fig.update_layout(
|
|
513
|
+
polar=dict(
|
|
514
|
+
radialaxis=dict(visible=True)
|
|
515
|
+
),
|
|
516
|
+
xaxis=dict(domain=[0.65, 1]),
|
|
517
|
+
yaxis=dict(domain=[0.65, 1]),
|
|
518
|
+
yaxis2=dict(domain=[0, 0.35], anchor='x', overlaying='y', side='right')
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
fig.update_layout(
|
|
522
|
+
autosize=False,
|
|
523
|
+
width=1200,
|
|
524
|
+
height=800,
|
|
525
|
+
)
|
|
526
|
+
# Display & save the figure
|
|
527
|
+
fig.show()
|
|
528
|
+
# Save the figure as an HTML file
|
|
529
|
+
if save:
|
|
530
|
+
fig.write_html('polar_animation.html')
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lightsolver_lib-0.7.0 → lightsolver_lib-0.8.0}/lightsolver_lib.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|