Spaces:
Sleeping
Sleeping
harishaseebat92
commited on
Commit
·
13251a2
1
Parent(s):
50d95a5
IonQ QPU branch is now implemented
Browse files- em/simulation.py +189 -14
em/simulation.py
CHANGED
|
@@ -792,21 +792,196 @@ async def _run_simulation_async():
|
|
| 792 |
await _flush_async()
|
| 793 |
return
|
| 794 |
|
| 795 |
-
# IonQ QPU
|
| 796 |
-
|
| 797 |
-
|
| 798 |
-
state.status_message = "IonQ QPU backend unavailable."
|
| 799 |
-
state.status_type = "warning"
|
| 800 |
-
state.show_progress = False
|
| 801 |
-
state.is_running = False
|
| 802 |
-
state.run_button_text = "RUN!"
|
| 803 |
-
state.stop_button_disabled = True
|
| 804 |
-
log_to_console("IonQ QPU backend not connected. Use IBM QPU or Statevector Estimator instead.")
|
| 805 |
-
await _flush_async()
|
| 806 |
try:
|
| 807 |
-
|
| 808 |
-
|
| 809 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 810 |
return
|
| 811 |
|
| 812 |
# Simulator path - run blocking simulation in executor
|
|
|
|
| 792 |
await _flush_async()
|
| 793 |
return
|
| 794 |
|
| 795 |
+
# IonQ QPU branch
|
| 796 |
+
ionq_qpu_selected = state.backend_type == "QPU" and state.selected_qpu == "IonQ QPU"
|
| 797 |
+
if ionq_qpu_selected:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 798 |
try:
|
| 799 |
+
log_to_console("Running IonQ QPU simulation...")
|
| 800 |
+
state.status_message = "Running IonQ QPU simulation..."
|
| 801 |
+
state.simulation_progress = 5
|
| 802 |
+
await _flush_async()
|
| 803 |
+
|
| 804 |
+
# Import IonQ QPU backend (same module as IBM, different platform param)
|
| 805 |
+
try:
|
| 806 |
+
from quantum.utils.EBU_Quantum.no_body.base_functions import get_field_values as ionq_get_field_values, create_time_frames as ionq_create_time_frames
|
| 807 |
+
except ModuleNotFoundError:
|
| 808 |
+
from utils.EBU_Quantum.no_body.base_functions import get_field_values as ionq_get_field_values, create_time_frames as ionq_create_time_frames
|
| 809 |
+
|
| 810 |
+
# Inputs for IonQ QPU (single field, single position only!)
|
| 811 |
+
snapshot_dt = float(state.dt_user)
|
| 812 |
+
ix_imp, iy_imp = nearest_node_index(float(state.impulse_x), float(state.impulse_y), nx)
|
| 813 |
+
impulse_pos = (ix_imp, iy_imp)
|
| 814 |
+
|
| 815 |
+
# Get field and single position from UI
|
| 816 |
+
field_type = (state.qpu_field_components or "Ez").strip()
|
| 817 |
+
if field_type == "All":
|
| 818 |
+
field_type = "Ez"
|
| 819 |
+
log_to_console("Warning: IonQ QPU only supports single field. Defaulting to Ez.")
|
| 820 |
+
|
| 821 |
+
# Parse single monitor position
|
| 822 |
+
pts_str = str(state.qpu_monitor_gridpoints or "").strip()
|
| 823 |
+
raw_pts = [tuple(map(int, m)) for m in re.findall(r"\((\d+)\s*,\s*(\d+)\)", pts_str)]
|
| 824 |
+
if not raw_pts:
|
| 825 |
+
monitor_x, monitor_y = impulse_pos
|
| 826 |
+
log_to_console(f"No monitor position specified. Using impulse position ({monitor_x}, {monitor_y}).")
|
| 827 |
+
else:
|
| 828 |
+
monitor_x, monitor_y = raw_pts[0]
|
| 829 |
+
if len(raw_pts) > 1:
|
| 830 |
+
log_to_console(f"Warning: IonQ QPU only supports single position. Using first: ({monitor_x}, {monitor_y})")
|
| 831 |
+
|
| 832 |
+
state.status_message = "Step 1: Generating circuit..."
|
| 833 |
+
state.simulation_progress = 0
|
| 834 |
+
await _flush_async()
|
| 835 |
+
|
| 836 |
+
def _ionq_progress_callback(pct, message=None):
|
| 837 |
+
"""Progress callback for IonQ QPU."""
|
| 838 |
+
state.simulation_progress = int(pct)
|
| 839 |
+
if message:
|
| 840 |
+
state.status_message = message
|
| 841 |
+
elif pct < 10:
|
| 842 |
+
state.status_message = f"Step 1: Generating circuit ({int(pct)}%)"
|
| 843 |
+
elif pct < 60:
|
| 844 |
+
state.status_message = f"Step 2: Optimising circuit ({int(pct)}%)"
|
| 845 |
+
elif pct < 90:
|
| 846 |
+
state.status_message = f"Step 3: Job execution ({int(pct)}%)"
|
| 847 |
+
else:
|
| 848 |
+
state.status_message = f"Step 4: Creating plots ({int(pct)}%)"
|
| 849 |
+
_flush_state_threadsafe()
|
| 850 |
+
|
| 851 |
+
# Call the IonQ QPU get_field_values function in executor
|
| 852 |
+
def _run_ionq_qpu():
|
| 853 |
+
return ionq_get_field_values(
|
| 854 |
+
field=field_type,
|
| 855 |
+
x=monitor_x,
|
| 856 |
+
y=monitor_y,
|
| 857 |
+
T=float(T),
|
| 858 |
+
snapshot_time=snapshot_dt,
|
| 859 |
+
nx=nx,
|
| 860 |
+
impulse_pos=impulse_pos,
|
| 861 |
+
shots=10000,
|
| 862 |
+
pm_optimization_level=1, # IonQ recommended
|
| 863 |
+
simulation="False",
|
| 864 |
+
optimization="True",
|
| 865 |
+
platform="IONQ", # <-- Key difference from IBM
|
| 866 |
+
progress_callback=_ionq_progress_callback,
|
| 867 |
+
print_callback=log_to_console,
|
| 868 |
+
)
|
| 869 |
+
|
| 870 |
+
field_values = await loop.run_in_executor(executor, _run_ionq_qpu)
|
| 871 |
+
|
| 872 |
+
# Build time frames to match the output
|
| 873 |
+
times = ionq_create_time_frames(float(T), snapshot_dt)
|
| 874 |
+
|
| 875 |
+
# Build Plotly figure for the single time series
|
| 876 |
+
import plotly.graph_objects as go
|
| 877 |
+
fig = go.Figure()
|
| 878 |
+
|
| 879 |
+
# Determine grid dimensions for label
|
| 880 |
+
if field_type == 'Ez':
|
| 881 |
+
gw, gh = nx, nx
|
| 882 |
+
elif field_type == 'Hx':
|
| 883 |
+
gw, gh = nx, nx - 1
|
| 884 |
+
else:
|
| 885 |
+
gw, gh = nx - 1, nx
|
| 886 |
+
|
| 887 |
+
from .utils import normalized_position_label
|
| 888 |
+
label = normalized_position_label(monitor_x, monitor_y, gw, gh)
|
| 889 |
+
|
| 890 |
+
# Color based on field type
|
| 891 |
+
if field_type == 'Ez':
|
| 892 |
+
color = "#d32f2f"
|
| 893 |
+
elif field_type == 'Hx':
|
| 894 |
+
color = "#388e3c"
|
| 895 |
+
else:
|
| 896 |
+
color = "#1976d2"
|
| 897 |
+
|
| 898 |
+
fig.add_trace(
|
| 899 |
+
go.Scatter(
|
| 900 |
+
x=list(times),
|
| 901 |
+
y=[float(v) for v in field_values],
|
| 902 |
+
mode='lines+markers',
|
| 903 |
+
name=f"{field_type} @ {label}",
|
| 904 |
+
line=dict(color=color, width=2.5),
|
| 905 |
+
marker=dict(size=7, symbol="circle", color=color),
|
| 906 |
+
hovertemplate=f"{field_type} | t=%{{x:.3f}}s<br>Value=%{{y:.6g}}<extra>{label}</extra>",
|
| 907 |
+
)
|
| 908 |
+
)
|
| 909 |
+
|
| 910 |
+
max_abs = max((abs(float(v)) for v in field_values), default=1.0)
|
| 911 |
+
pad = 0.12 * max_abs if max_abs > 0 else 0.1
|
| 912 |
+
|
| 913 |
+
fig.update_layout(
|
| 914 |
+
title=f"IonQ QPU Time Series - {field_type} @ {label}",
|
| 915 |
+
height=660, width=900,
|
| 916 |
+
margin=dict(l=50, r=30, t=50, b=50),
|
| 917 |
+
hovermode="x unified",
|
| 918 |
+
legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1, title_text=""),
|
| 919 |
+
paper_bgcolor="#FFFFFF",
|
| 920 |
+
plot_bgcolor="#FFFFFF",
|
| 921 |
+
)
|
| 922 |
+
fig.update_xaxes(title_text="Time (s)", title_font=dict(size=22), tickfont=dict(size=16), showgrid=True, gridcolor="rgba(0,0,0,.06)")
|
| 923 |
+
fig.update_yaxes(title_text="Field Value", title_font=dict(size=22), tickfont=dict(size=16), showgrid=True, gridcolor="rgba(0,0,0,.06)")
|
| 924 |
+
fig.update_yaxes(range=[-max_abs - pad, max_abs + pad])
|
| 925 |
+
|
| 926 |
+
# Cache the figure for export
|
| 927 |
+
qpu_ts_cache["fig"] = fig
|
| 928 |
+
qpu_ts_cache["times"] = list(times)
|
| 929 |
+
qpu_ts_cache["series_map"] = {(field_type, monitor_x, monitor_y): list(field_values)}
|
| 930 |
+
qpu_ts_cache["field"] = field_type
|
| 931 |
+
qpu_ts_cache["unique_fields"] = [field_type]
|
| 932 |
+
|
| 933 |
+
try:
|
| 934 |
+
ctrl.qpu_ts_update(fig)
|
| 935 |
+
except Exception:
|
| 936 |
+
pass
|
| 937 |
+
|
| 938 |
+
state.simulation_has_run = True
|
| 939 |
+
state.run_button_text = "Successful!"
|
| 940 |
+
state.simulation_progress = 100
|
| 941 |
+
state.status_message = "IonQ QPU simulation completed successfully!"
|
| 942 |
+
log_to_console("IonQ QPU run completed")
|
| 943 |
+
state.status_type = "success"
|
| 944 |
+
state.show_progress = False
|
| 945 |
+
_auto_hide_status_window(3.0)
|
| 946 |
+
await _flush_async()
|
| 947 |
+
|
| 948 |
+
ready = bool(field_values) and len(field_values) > 0
|
| 949 |
+
state.qpu_ts_ready = ready
|
| 950 |
+
state.qpu_plot_style = (
|
| 951 |
+
"width: 900px; height: 660px; margin: 0 auto;"
|
| 952 |
+
if ready else "display: none; width: 900px; height: 660px; margin: 0 auto;"
|
| 953 |
+
)
|
| 954 |
+
state.qpu_ts_other_ready = False
|
| 955 |
+
state.qpu_other_plot_style = "display: none; width: 900px; height: 660px; margin: 0 auto;"
|
| 956 |
+
|
| 957 |
+
# Set filter options for single result
|
| 958 |
+
state.qpu_plot_field_options = ["All", field_type]
|
| 959 |
+
state.qpu_plot_filter = "All"
|
| 960 |
+
state.qpu_plot_position_options = ["All positions", label]
|
| 961 |
+
state.qpu_plot_position_filter = "All positions"
|
| 962 |
+
|
| 963 |
+
if not ready:
|
| 964 |
+
state.error_message = "No IonQ QPU time series generated. Check Δt, T, nx, and monitor position."
|
| 965 |
+
state.status_message = "Warning: No IonQ QPU time series generated."
|
| 966 |
+
state.status_type = "warning"
|
| 967 |
+
log_to_console("IonQ QPU complete.")
|
| 968 |
+
|
| 969 |
+
except Exception as e:
|
| 970 |
+
import traceback
|
| 971 |
+
state.error_message = f"IonQ QPU run failed: {e}"
|
| 972 |
+
state.status_message = f"IonQ QPU Error: {e}"
|
| 973 |
+
state.status_type = "error"
|
| 974 |
+
state.show_progress = False
|
| 975 |
+
state.run_button_text = "RUN!"
|
| 976 |
+
state.qpu_ts_ready = False
|
| 977 |
+
log_to_console(f"IonQ QPU error: {e}")
|
| 978 |
+
log_to_console(traceback.format_exc())
|
| 979 |
+
finally:
|
| 980 |
+
state.is_running = False
|
| 981 |
+
state.stop_button_disabled = True
|
| 982 |
+
_stop_progress_heartbeat()
|
| 983 |
+
executor.shutdown(wait=False)
|
| 984 |
+
await _flush_async()
|
| 985 |
return
|
| 986 |
|
| 987 |
# Simulator path - run blocking simulation in executor
|