harishaseebat92 commited on
Commit
d6abeb6
·
1 Parent(s): 732829e

Enforce snap to grid behavior for both Delta and Gaussian excitations in the simulator, matching QPU semantics.

Browse files
em/simulation.py CHANGED
@@ -215,7 +215,7 @@ def redraw_surface_plot():
215
  mesh,
216
  scalars='scalars',
217
  # clim=g.surface_clims[field],
218
- cmap="Blues",
219
  show_scalar_bar=False,
220
  show_edges=True,
221
  edge_color='grey',
@@ -307,13 +307,15 @@ def run_simulation_only():
307
  if state.dist_type == "Delta":
308
  initial_state = create_impulse_state_from_pos(
309
  (nx, nx),
310
- (float(state.impulse_x), float(state.impulse_y))
 
311
  )
312
  else:
313
  initial_state = create_gaussian_state_from_pos(
314
  (nx, nx),
315
  (float(state.mu_x), float(state.mu_y)),
316
- (float(state.sigma_x), float(state.sigma_y))
 
317
  )
318
  except ValueError as e:
319
  state.error_message = f"Initial State Error: {e}"
 
215
  mesh,
216
  scalars='scalars',
217
  # clim=g.surface_clims[field],
218
+ cmap="turbo",
219
  show_scalar_bar=False,
220
  show_edges=True,
221
  edge_color='grey',
 
307
  if state.dist_type == "Delta":
308
  initial_state = create_impulse_state_from_pos(
309
  (nx, nx),
310
+ (float(state.impulse_x), float(state.impulse_y)),
311
+ snap_to_grid=True,
312
  )
313
  else:
314
  initial_state = create_gaussian_state_from_pos(
315
  (nx, nx),
316
  (float(state.mu_x), float(state.mu_y)),
317
+ (float(state.sigma_x), float(state.sigma_y)),
318
+ snap_to_grid=True,
319
  )
320
  except ValueError as e:
321
  state.error_message = f"Initial State Error: {e}"
qlbm/qlbm_sample_app.py CHANGED
@@ -509,7 +509,7 @@ def run_sampling_hw_ibm(
509
  uz,
510
  init_state_prep_circ,
511
  T_list,
512
- shots=2**19,
513
  vel_resolution=32,
514
  output_resolution=40,
515
  logger=None,
@@ -619,7 +619,7 @@ def run_sampling_hw_ionq(
619
  uz,
620
  init_state_prep_circ,
621
  T_list,
622
- shots=2**19,
623
  vel_resolution=32,
624
  output_resolution=40,
625
  logger=None,
 
509
  uz,
510
  init_state_prep_circ,
511
  T_list,
512
+ shots=2**14,
513
  vel_resolution=32,
514
  output_resolution=40,
515
  logger=None,
 
619
  uz,
620
  init_state_prep_circ,
621
  T_list,
622
+ shots=2**14,
623
  vel_resolution=32,
624
  output_resolution=40,
625
  logger=None,
qlbm_embedded.py CHANGED
@@ -1197,7 +1197,7 @@ def run_simulation():
1197
  init_state_prep_circ=init_state_prep_circ,
1198
  T_list=params["T_list"],
1199
  # shots=2**14, # Reduced shots for responsiveness/quota
1200
- shots=2**18,
1201
  vel_resolution=min(params['grid_size'], 32),
1202
  output_resolution=min(2*params['grid_size'], 40),
1203
  logger=log_to_console
@@ -1290,7 +1290,7 @@ def run_simulation():
1290
  uz=params["vz_expr"],
1291
  init_state_prep_circ=init_state_prep_circ,
1292
  T_list=params["T_list"],
1293
- shots=2**18,
1294
  vel_resolution=min(params['grid_size'], 32),
1295
  output_resolution=min(2*params['grid_size'], 40),
1296
  logger=log_to_console
@@ -1398,7 +1398,7 @@ def run_simulation():
1398
  if grid_obj:
1399
  _plotter.clear()
1400
  isosurfaces = grid_obj.contour(isosurfaces=7, scalars="scalars")
1401
- _plotter.add_mesh(isosurfaces, cmap="Blues", opacity=0.3, show_scalar_bar=True)
1402
  _plotter.add_axes()
1403
  _plotter.show_grid()
1404
 
@@ -1837,7 +1837,7 @@ def _build_control_panels(plotter):
1837
  with vuetify3.VCard(classes="mb-2"):
1838
  vuetify3.VCardTitle("Time", classes="text-subtitle-2 font-weight-bold text-primary")
1839
  with vuetify3.VCardText():
1840
- vuetify3.VSlider(label="Total Time", v_model=("qlbm_time_steps", 10), min=0, max=50, step=2,
1841
  thumb_label="always", show_ticks="always", color="primary", density="compact", hide_details=True)
1842
  vuetify3.VAlert(v_if="qlbm_time_steps > 100", type="warning", variant="tonal", density="compact",
1843
  children=["Warning: High time steps may increase runtime."], classes="mt-2")
 
1197
  init_state_prep_circ=init_state_prep_circ,
1198
  T_list=params["T_list"],
1199
  # shots=2**14, # Reduced shots for responsiveness/quota
1200
+ shots=2**14,
1201
  vel_resolution=min(params['grid_size'], 32),
1202
  output_resolution=min(2*params['grid_size'], 40),
1203
  logger=log_to_console
 
1290
  uz=params["vz_expr"],
1291
  init_state_prep_circ=init_state_prep_circ,
1292
  T_list=params["T_list"],
1293
+ shots=2**14,
1294
  vel_resolution=min(params['grid_size'], 32),
1295
  output_resolution=min(2*params['grid_size'], 40),
1296
  logger=log_to_console
 
1398
  if grid_obj:
1399
  _plotter.clear()
1400
  isosurfaces = grid_obj.contour(isosurfaces=7, scalars="scalars")
1401
+ _plotter.add_mesh(isosurfaces, cmap="turbo", opacity=0.3, show_scalar_bar=True)
1402
  _plotter.add_axes()
1403
  _plotter.show_grid()
1404
 
 
1837
  with vuetify3.VCard(classes="mb-2"):
1838
  vuetify3.VCardTitle("Time", classes="text-subtitle-2 font-weight-bold text-primary")
1839
  with vuetify3.VCardText():
1840
+ vuetify3.VSlider(label="Total Time", v_model=("qlbm_time_steps", 10), min=0, max=30, step=1,
1841
  thumb_label="always", show_ticks="always", color="primary", density="compact", hide_details=True)
1842
  vuetify3.VAlert(v_if="qlbm_time_steps > 100", type="warning", variant="tonal", density="compact",
1843
  children=["Warning: High time steps may increase runtime."], classes="mt-2")
utils/delta_impulse_generator.py CHANGED
@@ -107,16 +107,31 @@ def _normalize_to_unit(vec: np.ndarray) -> np.ndarray:
107
 
108
 
109
 
110
- def create_impulse_state_from_pos(grid_dims, pos01):
111
  """
112
  Create a delta-like initial state from continuous position pos01=(x,y) in [0,1].
113
 
 
 
 
 
 
 
 
114
  Why grid_dims?
115
  - Simulation runs on a discrete nx×ny lattice; the continuous position must be
116
  discretized onto that grid to produce the state vector fed into the solver.
117
- - grid_dims provides (nx, ny) so we can map (x,y)∈[0,1]→grid coordinates via
118
- gx = x*(nx-1), gy = y*(ny-1), then distribute amplitude bilinearly to the 4
119
- neighboring nodes. This is required only for the simulation state, not the preview.
 
 
 
 
 
 
 
 
120
 
121
  The preview uses create_impulse_preview_state(), which renders a smooth bump on a
122
  fixed unit-square grid independent of nx for visualization.
@@ -128,6 +143,25 @@ def create_impulse_state_from_pos(grid_dims, pos01):
128
 
129
  gx = px * (grid_width - 1)
130
  gy = py * (grid_height - 1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  i0, j0 = int(np.floor(gx)), int(np.floor(gy))
132
  i1, j1 = min(i0 + 1, grid_width - 1), min(j0 + 1, grid_height - 1)
133
  dx, dy = gx - i0, gy - j0
@@ -137,8 +171,6 @@ def create_impulse_state_from_pos(grid_dims, pos01):
137
  w01 = (1 - dx) * dy
138
  w11 = dx * dy
139
 
140
- grid_size = grid_width * grid_height
141
- total_size = 4 * grid_size
142
  field = np.zeros(grid_size)
143
  field[j0 * grid_width + i0] += w00
144
  field[j0 * grid_width + i1] += w10
@@ -151,11 +183,19 @@ def create_impulse_state_from_pos(grid_dims, pos01):
151
  return initial_state
152
 
153
 
154
- def create_gaussian_state_from_pos(grid_dims, mu01, sigma01):
155
  """
156
  Create a Gaussian initial state with center mu01=(x,y) and spreads sigma01=(sx,sy)
157
  in [0,1] of the domain, then discretize to the solver grid given by grid_dims.
158
 
 
 
 
 
 
 
 
 
159
  Why grid_dims?
160
  - The quantum solver expects a vector aligned to the chosen nx×ny simulation grid.
161
  We convert normalized μ and σ (fractions of the domain) into grid units using
@@ -177,6 +217,11 @@ def create_gaussian_state_from_pos(grid_dims, mu01, sigma01):
177
 
178
  mu_x = mu_x01 * (grid_width - 1)
179
  mu_y = mu_y01 * (grid_height - 1)
 
 
 
 
 
180
  sigma_x = sig_x01 * (grid_width - 1)
181
  sigma_y = sig_y01 * (grid_height - 1)
182
 
 
107
 
108
 
109
 
110
+ def create_impulse_state_from_pos(grid_dims, pos01, snap_to_grid=True):
111
  """
112
  Create a delta-like initial state from continuous position pos01=(x,y) in [0,1].
113
 
114
+ Args:
115
+ grid_dims: Tuple (nx, ny) defining the simulation grid dimensions.
116
+ pos01: Tuple (x, y) with continuous position in [0, 1] range.
117
+ snap_to_grid: If True (default), snaps the impulse to the nearest grid node
118
+ for an exact delta with peak value = 1.0. If False, uses
119
+ bilinear interpolation which distributes amplitude to 4 nodes.
120
+
121
  Why grid_dims?
122
  - Simulation runs on a discrete nx×ny lattice; the continuous position must be
123
  discretized onto that grid to produce the state vector fed into the solver.
124
+ - grid_dims provides (nx, ny) so we can map (x,y)∈[0,1]→grid coordinates.
125
+
126
+ When snap_to_grid=True (default):
127
+ - The impulse is placed exactly on the nearest grid node.
128
+ - Peak amplitude = 1.0 (exact delta function on the discrete grid).
129
+ - This is recommended for visualization and accurate peak value display.
130
+
131
+ When snap_to_grid=False:
132
+ - Uses bilinear interpolation to distribute amplitude to the 4 neighboring nodes.
133
+ - Peak amplitude depends on position (e.g., 0.5 when exactly between 4 nodes).
134
+ - Total energy is preserved (L2 norm = 1).
135
 
136
  The preview uses create_impulse_preview_state(), which renders a smooth bump on a
137
  fixed unit-square grid independent of nx for visualization.
 
143
 
144
  gx = px * (grid_width - 1)
145
  gy = py * (grid_height - 1)
146
+
147
+ grid_size = grid_width * grid_height
148
+ total_size = 4 * grid_size
149
+
150
+ if snap_to_grid:
151
+ # Snap to nearest grid node for exact delta with peak = 1.0
152
+ i0 = int(round(gx))
153
+ j0 = int(round(gy))
154
+ i0 = max(0, min(i0, grid_width - 1))
155
+ j0 = max(0, min(j0, grid_height - 1))
156
+
157
+ field = np.zeros(grid_size)
158
+ field[j0 * grid_width + i0] = 1.0 # Peak value = 1.0
159
+
160
+ initial_state = np.zeros(total_size)
161
+ initial_state[:grid_size] = field
162
+ return initial_state
163
+
164
+ # Bilinear interpolation mode (snap_to_grid=False)
165
  i0, j0 = int(np.floor(gx)), int(np.floor(gy))
166
  i1, j1 = min(i0 + 1, grid_width - 1), min(j0 + 1, grid_height - 1)
167
  dx, dy = gx - i0, gy - j0
 
171
  w01 = (1 - dx) * dy
172
  w11 = dx * dy
173
 
 
 
174
  field = np.zeros(grid_size)
175
  field[j0 * grid_width + i0] += w00
176
  field[j0 * grid_width + i1] += w10
 
183
  return initial_state
184
 
185
 
186
+ def create_gaussian_state_from_pos(grid_dims, mu01, sigma01, snap_to_grid=True):
187
  """
188
  Create a Gaussian initial state with center mu01=(x,y) and spreads sigma01=(sx,sy)
189
  in [0,1] of the domain, then discretize to the solver grid given by grid_dims.
190
 
191
+ Args:
192
+ grid_dims: Tuple (nx, ny) defining the simulation grid dimensions.
193
+ mu01: Tuple (x, y) with Gaussian center in [0, 1].
194
+ sigma01: Tuple (sx, sy) with Gaussian std dev as fraction of domain in [0, 1].
195
+ snap_to_grid: If True (default), snap the Gaussian center to the nearest grid
196
+ node before discretization. This makes the simulator behavior consistent
197
+ with the "nearest-node" semantics often used elsewhere in the app.
198
+
199
  Why grid_dims?
200
  - The quantum solver expects a vector aligned to the chosen nx×ny simulation grid.
201
  We convert normalized μ and σ (fractions of the domain) into grid units using
 
217
 
218
  mu_x = mu_x01 * (grid_width - 1)
219
  mu_y = mu_y01 * (grid_height - 1)
220
+ if snap_to_grid:
221
+ mu_x = float(int(round(mu_x)))
222
+ mu_y = float(int(round(mu_y)))
223
+ mu_x = float(max(0, min(int(mu_x), grid_width - 1)))
224
+ mu_y = float(max(0, min(int(mu_y), grid_height - 1)))
225
  sigma_x = sig_x01 * (grid_width - 1)
226
  sigma_y = sig_y01 * (grid_height - 1)
227