Spaces:
Paused
Paused
Commit
·
eb4058d
1
Parent(s):
49e1dbd
Corrected Local Error
Browse files- app.py +13 -17
- em.ipynb +254 -0
- em_trame.py +10 -3
- fluid.py +1043 -0
- fluid3d_pyvista.py +638 -0
- pages/__pycache__/__init__.cpython-310.pyc +0 -0
- pages/__pycache__/em_page.cpython-310.pyc +0 -0
- pages/__pycache__/qlbm_page.cpython-310.pyc +0 -0
- pages/em_page.py +4 -1
- pages/qlbm_page.py +46 -187
- qlbm.py +649 -593
- utils/__pycache__/base_functions.cpython-310.pyc +0 -0
- utils/__pycache__/delta_impulse_generator.cpython-310.pyc +0 -0
- utils/base_ionq.py +458 -0
app.py
CHANGED
|
@@ -4,10 +4,10 @@ from trame_vuetify.widgets import vuetify3
|
|
| 4 |
from trame.widgets import html as trame_html
|
| 5 |
import os
|
| 6 |
|
| 7 |
-
# Import
|
| 8 |
-
|
| 9 |
-
em_page = None
|
| 10 |
-
|
| 11 |
|
| 12 |
# Force embedded mode for nested pages (avoid iframes/secondary servers)
|
| 13 |
os.environ.setdefault("TRAME_EMBEDDED", "1")
|
|
@@ -73,23 +73,19 @@ with SinglePageLayout(server) as layout:
|
|
| 73 |
with vuetify3.VContainer(v_if="current_page === 'EM'", fluid=True, classes="pa-0 fill-height"):
|
| 74 |
try:
|
| 75 |
if not globals().get("em_page"):
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
globals()["em_page"] = mod
|
| 79 |
-
if hasattr(em_page, "build"):
|
| 80 |
-
em_page.build(server)
|
| 81 |
-
else:
|
| 82 |
-
trame_html.Div("EM module missing build(server).", style="padding:8px;color:#666;")
|
| 83 |
except Exception as e:
|
| 84 |
-
trame_html.Div(
|
| 85 |
-
f"EM page failed to load in embedded mode: {e}. "
|
| 86 |
-
"Refactor EM to avoid starting a secondary server/iframe.",
|
| 87 |
-
style="padding:8px;color:#b00020;"
|
| 88 |
-
)
|
| 89 |
|
| 90 |
# Mount QLBM page
|
| 91 |
with vuetify3.VContainer(v_if="current_page === 'QLBM'", fluid=True, classes="pa-0 fill-height"):
|
| 92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
|
| 94 |
if __name__ == "__main__":
|
| 95 |
# If PORT is provided (e.g., Hugging Face), bind on 0.0.0.0; otherwise use localhost
|
|
|
|
| 4 |
from trame.widgets import html as trame_html
|
| 5 |
import os
|
| 6 |
|
| 7 |
+
# Import embedded page wrappers (lazy load inside tabs)
|
| 8 |
+
from importlib import import_module
|
| 9 |
+
em_page = None
|
| 10 |
+
qlbm_page = None
|
| 11 |
|
| 12 |
# Force embedded mode for nested pages (avoid iframes/secondary servers)
|
| 13 |
os.environ.setdefault("TRAME_EMBEDDED", "1")
|
|
|
|
| 73 |
with vuetify3.VContainer(v_if="current_page === 'EM'", fluid=True, classes="pa-0 fill-height"):
|
| 74 |
try:
|
| 75 |
if not globals().get("em_page"):
|
| 76 |
+
globals()["em_page"] = import_module("pages.em_page")
|
| 77 |
+
em_page.build(server)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
except Exception as e:
|
| 79 |
+
trame_html.Div(f"EM embed failed: {e}", style="padding:8px;color:#b00020;")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
|
| 81 |
# Mount QLBM page
|
| 82 |
with vuetify3.VContainer(v_if="current_page === 'QLBM'", fluid=True, classes="pa-0 fill-height"):
|
| 83 |
+
try:
|
| 84 |
+
if not globals().get("qlbm_page"):
|
| 85 |
+
globals()["qlbm_page"] = import_module("pages.qlbm_page")
|
| 86 |
+
qlbm_page.build(server)
|
| 87 |
+
except Exception as e:
|
| 88 |
+
trame_html.Div(f"QLBM embed failed: {e}", style="padding:8px;color:#b00020;")
|
| 89 |
|
| 90 |
if __name__ == "__main__":
|
| 91 |
# If PORT is provided (e.g., Hugging Face), bind on 0.0.0.0; otherwise use localhost
|
em.ipynb
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": 17,
|
| 6 |
+
"id": "fe68f8a9",
|
| 7 |
+
"metadata": {},
|
| 8 |
+
"outputs": [
|
| 9 |
+
{
|
| 10 |
+
"name": "stdout",
|
| 11 |
+
"output_type": "stream",
|
| 12 |
+
"text": [
|
| 13 |
+
"Available backends: [<qiskit_ionq.ionq_backend.IonQSimulatorBackend object at 0x7f91854ff520>, <qiskit_ionq.ionq_backend.IonQQPUBackend object at 0x7f91851f8d90>]\n"
|
| 14 |
+
]
|
| 15 |
+
},
|
| 16 |
+
{
|
| 17 |
+
"name": "stderr",
|
| 18 |
+
"output_type": "stream",
|
| 19 |
+
"text": [
|
| 20 |
+
"/home/cudaq/.local/lib/python3.10/site-packages/qiskit_ionq/ionq_backend.py:127: IonQTranspileLevelWarning: Transpiler default optimization_level=2. IonQ (QIS) recommends 0-1 to avoid aggressive re-synthesis; use transpile(..., optimization_level=1).\n",
|
| 21 |
+
" warn_bad_transpile_level()\n"
|
| 22 |
+
]
|
| 23 |
+
}
|
| 24 |
+
],
|
| 25 |
+
"source": [
|
| 26 |
+
"from qiskit import QuantumCircuit\n",
|
| 27 |
+
"from qiskit_ionq import IonQProvider\n",
|
| 28 |
+
"import os\n",
|
| 29 |
+
"my_api_key = os.getenv(\"IONQ_API_KEY\")\n",
|
| 30 |
+
"# # provider = IonQProvider()\n",
|
| 31 |
+
"\n",
|
| 32 |
+
"api_token = \"SgUkiDq1r2bVEadyiUfvtuxQ03Qci6UW\"\n",
|
| 33 |
+
"provider = IonQProvider(api_token)\n",
|
| 34 |
+
"\n",
|
| 35 |
+
"\n",
|
| 36 |
+
"backends = provider.backends()\n",
|
| 37 |
+
"print(\"Available backends:\", backends)"
|
| 38 |
+
]
|
| 39 |
+
},
|
| 40 |
+
{
|
| 41 |
+
"cell_type": "code",
|
| 42 |
+
"execution_count": 15,
|
| 43 |
+
"id": "6b2b90ef",
|
| 44 |
+
"metadata": {},
|
| 45 |
+
"outputs": [
|
| 46 |
+
{
|
| 47 |
+
"name": "stderr",
|
| 48 |
+
"output_type": "stream",
|
| 49 |
+
"text": [
|
| 50 |
+
"/home/cudaq/.local/lib/python3.10/site-packages/qiskit_ionq/ionq_backend.py:127: IonQTranspileLevelWarning: Transpiler default optimization_level=2. IonQ (QIS) recommends 0-1 to avoid aggressive re-synthesis; use transpile(..., optimization_level=1).\n",
|
| 51 |
+
" warn_bad_transpile_level()\n"
|
| 52 |
+
]
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
"name": "stdout",
|
| 56 |
+
"output_type": "stream",
|
| 57 |
+
"text": [
|
| 58 |
+
"Expectation values: [1.55664062]\n"
|
| 59 |
+
]
|
| 60 |
+
}
|
| 61 |
+
],
|
| 62 |
+
"source": [
|
| 63 |
+
"from qiskit import QuantumCircuit\n",
|
| 64 |
+
"from qiskit_ionq import IonQProvider\n",
|
| 65 |
+
"\n",
|
| 66 |
+
"backend = provider.get_backend(\"simulator\")\n",
|
| 67 |
+
"\n",
|
| 68 |
+
"from qiskit.circuit.library import real_amplitudes\n",
|
| 69 |
+
"from qiskit.quantum_info import SparsePauliOp\n",
|
| 70 |
+
"from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
|
| 71 |
+
"from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator\n",
|
| 72 |
+
"from qiskit.primitives import StatevectorEstimator\n",
|
| 73 |
+
"\n",
|
| 74 |
+
"# service = QiskitRuntimeService()\n",
|
| 75 |
+
"# backend = service.least_busy(operational=True, simulator=False)\n",
|
| 76 |
+
" \n",
|
| 77 |
+
"psi = real_amplitudes(num_qubits=2, reps=2)\n",
|
| 78 |
+
"hamiltonian = SparsePauliOp.from_list([(\"II\", 1), (\"IZ\", 2), (\"XI\", 3)])\n",
|
| 79 |
+
"theta = [0, 1, 1, 2, 3, 5]\n",
|
| 80 |
+
" \n",
|
| 81 |
+
"pm = generate_preset_pass_manager(backend=backend, optimization_level=1)\n",
|
| 82 |
+
"isa_psi = pm.run(psi)\n",
|
| 83 |
+
"isa_observables = hamiltonian.apply_layout(isa_psi.layout)\n",
|
| 84 |
+
" \n",
|
| 85 |
+
"estimator = Estimator(mode=backend)\n",
|
| 86 |
+
" \n",
|
| 87 |
+
"# calculate [ <psi(theta)|hamiltonian|psi(theta)> ]\n",
|
| 88 |
+
"job = estimator.run([(isa_psi, isa_observables, [theta])])\n",
|
| 89 |
+
"pub_result = job.result()[0]\n",
|
| 90 |
+
"print(f\"Expectation values: {pub_result.data.evs}\")"
|
| 91 |
+
]
|
| 92 |
+
},
|
| 93 |
+
{
|
| 94 |
+
"cell_type": "code",
|
| 95 |
+
"execution_count": 16,
|
| 96 |
+
"id": "4147b18a",
|
| 97 |
+
"metadata": {},
|
| 98 |
+
"outputs": [
|
| 99 |
+
{
|
| 100 |
+
"name": "stdout",
|
| 101 |
+
"output_type": "stream",
|
| 102 |
+
"text": [
|
| 103 |
+
"Expectation values: [1.55555728]\n"
|
| 104 |
+
]
|
| 105 |
+
}
|
| 106 |
+
],
|
| 107 |
+
"source": [
|
| 108 |
+
"qiskit_estimator = StatevectorEstimator()\n",
|
| 109 |
+
"job2 = qiskit_estimator.run([(psi, hamiltonian, [theta])])\n",
|
| 110 |
+
"pub_result = job2.result()[0]\n",
|
| 111 |
+
"print(f\"Expectation values: {pub_result.data.evs}\") "
|
| 112 |
+
]
|
| 113 |
+
},
|
| 114 |
+
{
|
| 115 |
+
"cell_type": "code",
|
| 116 |
+
"execution_count": null,
|
| 117 |
+
"id": "109a7251",
|
| 118 |
+
"metadata": {},
|
| 119 |
+
"outputs": [],
|
| 120 |
+
"source": [
|
| 121 |
+
"def run_sim_ionq(nx, na, R, initial_state, T, snapshot_dt=None, stop_check=None, progress_callback=None):\n",
|
| 122 |
+
" \"\"\"\n",
|
| 123 |
+
" Runs the quantum simulation for electromagnetic scattering with fixed dt=0.1.\n",
|
| 124 |
+
" Captures frames only at user-defined snapshot times: [0, Δt, 2Δt, ..., ≤ T_eff],\n",
|
| 125 |
+
" always including t=0 and the final solver-aligned T (T_eff = floor(T/dt)*dt).\n",
|
| 126 |
+
"\n",
|
| 127 |
+
" Returns:\n",
|
| 128 |
+
" frames (np.ndarray), snapshot_times (np.ndarray)\n",
|
| 129 |
+
" \"\"\"\n",
|
| 130 |
+
" dt = 0.1\n",
|
| 131 |
+
" # Validate total time and compute solver-aligned end time\n",
|
| 132 |
+
" try:\n",
|
| 133 |
+
" T_val = float(T)\n",
|
| 134 |
+
" except Exception:\n",
|
| 135 |
+
" return np.array([]), np.array([])\n",
|
| 136 |
+
" if T_val <= 0:\n",
|
| 137 |
+
" return np.array([]), np.array([])\n",
|
| 138 |
+
"\n",
|
| 139 |
+
" steps = int(np.floor(T_val / dt))\n",
|
| 140 |
+
" if steps <= 0:\n",
|
| 141 |
+
" return np.array([]), np.array([])\n",
|
| 142 |
+
" T_eff = steps * dt\n",
|
| 143 |
+
"\n",
|
| 144 |
+
" # Determine snapshot Δt on solver grid\n",
|
| 145 |
+
" tol = 1e-12\n",
|
| 146 |
+
" if snapshot_dt is None:\n",
|
| 147 |
+
" snapshot_dt_val = dt\n",
|
| 148 |
+
" else:\n",
|
| 149 |
+
" try:\n",
|
| 150 |
+
" snapshot_dt_val = float(snapshot_dt)\n",
|
| 151 |
+
" except Exception:\n",
|
| 152 |
+
" snapshot_dt_val = dt\n",
|
| 153 |
+
" if snapshot_dt_val < dt - tol:\n",
|
| 154 |
+
" snapshot_dt_val = dt\n",
|
| 155 |
+
" k = max(1, int(round(snapshot_dt_val / dt)))\n",
|
| 156 |
+
" snapshot_dt_eff = k * dt\n",
|
| 157 |
+
"\n",
|
| 158 |
+
" # Build requested snapshot times on solver grid\n",
|
| 159 |
+
" target_times = [0.0]\n",
|
| 160 |
+
" t = 0.0\n",
|
| 161 |
+
" while t + snapshot_dt_eff <= T_eff + tol:\n",
|
| 162 |
+
" t = round(t + snapshot_dt_eff, 12)\n",
|
| 163 |
+
" if t <= T_eff + tol:\n",
|
| 164 |
+
" target_times.append(min(t, T_eff))\n",
|
| 165 |
+
" if abs(target_times[-1] - T_eff) > tol:\n",
|
| 166 |
+
" target_times.append(T_eff)\n",
|
| 167 |
+
"\n",
|
| 168 |
+
" # Setup circuit\n",
|
| 169 |
+
" nq = int(np.ceil(np.log2(nx)))\n",
|
| 170 |
+
" dp = 2 * R * np.pi / 2 ** na\n",
|
| 171 |
+
" p = np.arange(-R * np.pi, R * np.pi, step=dp)\n",
|
| 172 |
+
" fp = np.exp(-np.abs(p))\n",
|
| 173 |
+
" system, ancilla = QuantumRegister(2 * nq + 2), QuantumRegister(na)\n",
|
| 174 |
+
" qc = QuantumCircuit(system, ancilla)\n",
|
| 175 |
+
" qc.append(StatePreparation(initial_state), system)\n",
|
| 176 |
+
" qc.append(StatePreparation(fp / np.linalg.norm(fp)), ancilla)\n",
|
| 177 |
+
" expA1 = V1(nx, dt).to_gate()\n",
|
| 178 |
+
" expA2 = V2(nx, dt)\n",
|
| 179 |
+
"\n",
|
| 180 |
+
" frames = []\n",
|
| 181 |
+
" \n",
|
| 182 |
+
" # Run the circuit on IonQ's platform:\n",
|
| 183 |
+
" job = simulator_backend.run(qc, shots=10000)\n",
|
| 184 |
+
"\n",
|
| 185 |
+
" # Print the counts\n",
|
| 186 |
+
" # print(job.get_counts())\n",
|
| 187 |
+
" # # Capture initial frame at t=0\n",
|
| 188 |
+
" # sv0 = np.real(Statevector(qc)).reshape(2 ** na, 2 ** (2 * nq + 2))\n",
|
| 189 |
+
" # frames.append(sv0[2 ** (na - 1)])\n",
|
| 190 |
+
" # next_idx = 1 # next target_times index to capture\n",
|
| 191 |
+
"\n",
|
| 192 |
+
" # for i in range(steps):\n",
|
| 193 |
+
" # if stop_check and stop_check():\n",
|
| 194 |
+
" # print(f\"Simulation interrupted at step {i}/{steps}\")\n",
|
| 195 |
+
" # break\n",
|
| 196 |
+
" # # One solver step\n",
|
| 197 |
+
" # qc.append(QFTGate(na), ancilla)\n",
|
| 198 |
+
" # qc.x(ancilla[-1])\n",
|
| 199 |
+
" # for j in range(na - 1):\n",
|
| 200 |
+
" # qc.append(expA1.control().repeat(2 ** j), [ancilla[j]] + system[:])\n",
|
| 201 |
+
" # qc.append(expA1.inverse().control(ctrl_state=\"0\").repeat(2 ** (na - 1)), [ancilla[na - 1]] + system[:])\n",
|
| 202 |
+
" # qc.append(expA2, system[:])\n",
|
| 203 |
+
" # qc.x(ancilla[-1])\n",
|
| 204 |
+
" # qc.append(QFTGate(na).inverse(), ancilla)\n",
|
| 205 |
+
"\n",
|
| 206 |
+
" # current_time = (i + 1) * dt\n",
|
| 207 |
+
" # if next_idx < len(target_times) and abs(current_time - target_times[next_idx]) <= tol:\n",
|
| 208 |
+
" # u = np.real(Statevector(qc)).reshape(2 ** na, 2 ** (2 * nq + 2))\n",
|
| 209 |
+
" # frames.append(u[2 ** (na - 1)])\n",
|
| 210 |
+
" # next_idx += 1\n",
|
| 211 |
+
"\n",
|
| 212 |
+
" # if progress_callback:\n",
|
| 213 |
+
" # try:\n",
|
| 214 |
+
" # progress = ((i + 1) / steps) * 100\n",
|
| 215 |
+
" # progress_callback(progress)\n",
|
| 216 |
+
" # except Exception:\n",
|
| 217 |
+
" # pass\n",
|
| 218 |
+
"\n",
|
| 219 |
+
" # if progress_callback:\n",
|
| 220 |
+
" # try:\n",
|
| 221 |
+
" # progress_callback(100.0)\n",
|
| 222 |
+
" # except Exception:\n",
|
| 223 |
+
" # pass\n",
|
| 224 |
+
"\n",
|
| 225 |
+
" # # Ensure snapshot_times align with number of captured frames (covers early stop)\n",
|
| 226 |
+
" # frames_arr = np.asarray(frames)\n",
|
| 227 |
+
" # times_arr = np.asarray(target_times[: len(frames_arr)])\n",
|
| 228 |
+
" # return frames_arr, times_arr\n",
|
| 229 |
+
"\n"
|
| 230 |
+
]
|
| 231 |
+
}
|
| 232 |
+
],
|
| 233 |
+
"metadata": {
|
| 234 |
+
"kernelspec": {
|
| 235 |
+
"display_name": "Python 3",
|
| 236 |
+
"language": "python",
|
| 237 |
+
"name": "python3"
|
| 238 |
+
},
|
| 239 |
+
"language_info": {
|
| 240 |
+
"codemirror_mode": {
|
| 241 |
+
"name": "ipython",
|
| 242 |
+
"version": 3
|
| 243 |
+
},
|
| 244 |
+
"file_extension": ".py",
|
| 245 |
+
"mimetype": "text/x-python",
|
| 246 |
+
"name": "python",
|
| 247 |
+
"nbconvert_exporter": "python",
|
| 248 |
+
"pygments_lexer": "ipython3",
|
| 249 |
+
"version": "3.10.12"
|
| 250 |
+
}
|
| 251 |
+
},
|
| 252 |
+
"nbformat": 4,
|
| 253 |
+
"nbformat_minor": 5
|
| 254 |
+
}
|
em_trame.py
CHANGED
|
@@ -2600,9 +2600,16 @@ def build(host_server):
|
|
| 2600 |
update_initial_state_preview()
|
| 2601 |
|
| 2602 |
if __name__ == "__main__":
|
| 2603 |
-
#
|
| 2604 |
-
# so it can be embedded via IFrame in the multipage app
|
| 2605 |
port = int(os.environ.get("EM_APP_PORT", "8701"))
|
| 2606 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2607 |
|
| 2608 |
|
|
|
|
| 2600 |
update_initial_state_preview()
|
| 2601 |
|
| 2602 |
if __name__ == "__main__":
|
| 2603 |
+
# Standalone launch: prefer IPv4 loopback to avoid OSError 99 on ::1
|
|
|
|
| 2604 |
port = int(os.environ.get("EM_APP_PORT", "8701"))
|
| 2605 |
+
host = os.environ.get("EM_HOST", "127.0.0.1")
|
| 2606 |
+
try:
|
| 2607 |
+
server.start(host=host, port=port, open_browser=False)
|
| 2608 |
+
except OSError as e:
|
| 2609 |
+
# Fallback: try any local interface
|
| 2610 |
+
try:
|
| 2611 |
+
server.start(host="0.0.0.0", port=port, open_browser=False)
|
| 2612 |
+
except Exception:
|
| 2613 |
+
print(f"EM server failed to bind on {host}:{port} -> {e}")
|
| 2614 |
|
| 2615 |
|
fluid.py
ADDED
|
@@ -0,0 +1,1043 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import math
|
| 3 |
+
import tempfile
|
| 4 |
+
import gradio as gr
|
| 5 |
+
import cudaq
|
| 6 |
+
import numpy as np
|
| 7 |
+
import cupy as cp
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
import plotly.graph_objects as go
|
| 10 |
+
import plotly.io as pio
|
| 11 |
+
from sympy import sympify, symbols, lambdify
|
| 12 |
+
from gradio_litmodel3d import LitModel3D
|
| 13 |
+
import zipfile
|
| 14 |
+
|
| 15 |
+
# Set Plotly engine for image export
|
| 16 |
+
try:
|
| 17 |
+
pio.kaleido.scope.mathjax = None
|
| 18 |
+
except AttributeError:
|
| 19 |
+
pass
|
| 20 |
+
|
| 21 |
+
# Existing functions (bin_to_gray, gray_to_bin, etc.) remain unchanged
|
| 22 |
+
def bin_to_gray(bin_s):
|
| 23 |
+
XOR=lambda x,y: (x or y) and not (x and y)
|
| 24 |
+
gray_s=bin_s[0]
|
| 25 |
+
for i in range(len(bin_s)-1):
|
| 26 |
+
c_bool=XOR(bool(int(bin_s[i])),bool(int(bin_s[i+1])))
|
| 27 |
+
gray_s+=str(int(c_bool))
|
| 28 |
+
return gray_s
|
| 29 |
+
|
| 30 |
+
def gray_to_bin(gray_s):
|
| 31 |
+
XOR=lambda x,y: (x or y) and not (x and y)
|
| 32 |
+
bin_s=gray_s[0]
|
| 33 |
+
for i in range(len(gray_s)-1):
|
| 34 |
+
c_bool=XOR(bool(int(bin_s[i])),bool(int(gray_s[i+1])))
|
| 35 |
+
bin_s+=str(int(c_bool))
|
| 36 |
+
return bin_s
|
| 37 |
+
|
| 38 |
+
def bin_to_int(bin_s):
|
| 39 |
+
return int(bin_s,2)
|
| 40 |
+
|
| 41 |
+
def int_to_bin(i,pad):
|
| 42 |
+
return bin(i)[2:].zfill(pad)
|
| 43 |
+
|
| 44 |
+
def fwht_approx(f,N,num_points_per_dim,threshold=1e-10):
|
| 45 |
+
linear_block_size=int(N//num_points_per_dim)
|
| 46 |
+
num_angles_per_block=int(np.log2(linear_block_size))
|
| 47 |
+
|
| 48 |
+
thetas={}
|
| 49 |
+
|
| 50 |
+
for k in range(num_points_per_dim):
|
| 51 |
+
for j in range(num_points_per_dim):
|
| 52 |
+
for i in range(num_points_per_dim):
|
| 53 |
+
|
| 54 |
+
avg_f=2*np.arccos(f(i*linear_block_size+(linear_block_size-1)/2,j*linear_block_size+(linear_block_size-1)/2,k*linear_block_size+(linear_block_size-1)/2))
|
| 55 |
+
thetas[k*(N**2)*linear_block_size+j*N*linear_block_size+i*linear_block_size]=avg_f
|
| 56 |
+
|
| 57 |
+
slope_x=(2*np.arccos(f(i*linear_block_size,j*linear_block_size+(linear_block_size-1)/2,k*linear_block_size+(linear_block_size-1)/2))-2*np.arccos(f(((i+1)%N)*linear_block_size,j*linear_block_size+(linear_block_size-1)/2,k*linear_block_size+(linear_block_size-1)/2)))/linear_block_size
|
| 58 |
+
slope_y=(2*np.arccos(f(i*linear_block_size+(linear_block_size-1)/2,j*linear_block_size,k*linear_block_size+(linear_block_size-1)/2))-2*np.arccos(f(i*linear_block_size+(linear_block_size-1)/2,((j+1)%N)*linear_block_size,k*linear_block_size+(linear_block_size-1)/2)))/linear_block_size
|
| 59 |
+
slope_z=(2*np.arccos(f(i*linear_block_size+(linear_block_size-1)/2,j*linear_block_size+(linear_block_size-1)/2,k*linear_block_size))-2*np.arccos(f(i*linear_block_size+(linear_block_size-1)/2,j*linear_block_size+(linear_block_size-1)/2,((k+1)%N)*linear_block_size)))/linear_block_size
|
| 60 |
+
|
| 61 |
+
for m in range(num_angles_per_block):
|
| 62 |
+
thetas[k*(N**2)*linear_block_size+j*N*linear_block_size+i*linear_block_size + 2**m]=slope_x*(2**(m-1))
|
| 63 |
+
thetas[k*(N**2)*linear_block_size+j*N*linear_block_size+i*linear_block_size + N*(2**m)]=slope_y*(2**(m-1))
|
| 64 |
+
thetas[k*(N**2)*linear_block_size+j*N*linear_block_size+i*linear_block_size + (N**2)*(2**m)]=slope_z*(2**(m-1))
|
| 65 |
+
|
| 66 |
+
h = linear_block_size
|
| 67 |
+
while h < N**3:
|
| 68 |
+
for i in range(0, N**3, h * 2):
|
| 69 |
+
if (i//N)%linear_block_size!=0:
|
| 70 |
+
continue
|
| 71 |
+
if (i//(N**2))%linear_block_size!=0:
|
| 72 |
+
continue
|
| 73 |
+
j=i
|
| 74 |
+
while j<i+h:
|
| 75 |
+
index=j
|
| 76 |
+
x = thetas[index]
|
| 77 |
+
y = thetas[index + h]
|
| 78 |
+
thetas[index] = (x + y)/2
|
| 79 |
+
thetas[index + h] = (x - y)/2
|
| 80 |
+
|
| 81 |
+
for ax in range(3):
|
| 82 |
+
for m in range(num_angles_per_block):
|
| 83 |
+
index = j + (N**ax) * (2**m)
|
| 84 |
+
x = thetas[index]
|
| 85 |
+
y = thetas[index + h]
|
| 86 |
+
thetas[index] = (x + y)/2
|
| 87 |
+
thetas[index + h] = (x - y)/2
|
| 88 |
+
|
| 89 |
+
j+=linear_block_size
|
| 90 |
+
if (j//N)%linear_block_size==1:
|
| 91 |
+
j+=(linear_block_size-1)*N
|
| 92 |
+
if (j//(N**2))%linear_block_size==1:
|
| 93 |
+
j+=(linear_block_size-1)*(N**2)
|
| 94 |
+
|
| 95 |
+
h *= 2
|
| 96 |
+
if h==N:
|
| 97 |
+
h=N*linear_block_size
|
| 98 |
+
if h==N**2:
|
| 99 |
+
h=(N**2)*linear_block_size
|
| 100 |
+
|
| 101 |
+
return [theta for theta in thetas.values() if abs(theta)>threshold],[key for key in thetas.keys() if abs(thetas[key])>threshold]
|
| 102 |
+
|
| 103 |
+
def get_circuit_inputs(f,num_reg_qubits,num_points_per_dim):
|
| 104 |
+
theta_vec,indices=fwht_approx(f,2**num_reg_qubits,num_points_per_dim)
|
| 105 |
+
circ_pos=[]
|
| 106 |
+
for ind in indices:
|
| 107 |
+
circ_pos+=[bin_to_int(gray_to_bin(int_to_bin(ind,num_reg_qubits*3)))]
|
| 108 |
+
|
| 109 |
+
sorted_theta_vec=sorted(zip(theta_vec,circ_pos),key=lambda el:el[1])
|
| 110 |
+
ctrls=[]
|
| 111 |
+
|
| 112 |
+
current_bs="0"*(3*num_reg_qubits)
|
| 113 |
+
for el in sorted_theta_vec:
|
| 114 |
+
new_bs=bin_to_gray(int_to_bin((el[1])%(2**(3*num_reg_qubits)),(3*num_reg_qubits)))
|
| 115 |
+
ctrls += [[i for i, (char1, char2) in enumerate(zip(current_bs, new_bs)) if char1 != char2]]
|
| 116 |
+
current_bs=new_bs
|
| 117 |
+
new_bs="0"*(3*num_reg_qubits)
|
| 118 |
+
ctrls += [[i for i, (char1, char2) in enumerate(zip(current_bs, new_bs)) if char1 != char2]]
|
| 119 |
+
|
| 120 |
+
ctrls_flat_list=[]
|
| 121 |
+
for ctrl_list in ctrls:
|
| 122 |
+
ctrls_flat_list+=[len(ctrl_list)]+ctrl_list
|
| 123 |
+
|
| 124 |
+
return [el[0] for el in sorted_theta_vec]+[0.0],ctrls_flat_list
|
| 125 |
+
|
| 126 |
+
# Simulation functions remain unchanged
|
| 127 |
+
def simulate_qlbm_and_animate(num_reg_qubits: int, T: int, distribution_type: str, velocity_field: str, vx_input: float, vy_input: float, boundary_condition: str):
|
| 128 |
+
num_anc = 3
|
| 129 |
+
num_qubits_total = 2 * num_reg_qubits + num_anc
|
| 130 |
+
current_N = 2**num_reg_qubits
|
| 131 |
+
N_tot_state_vector = 2**num_qubits_total
|
| 132 |
+
num_ranks = 1
|
| 133 |
+
rank = 0
|
| 134 |
+
N_sub_per_rank = int(N_tot_state_vector // num_ranks)
|
| 135 |
+
NUM_ANIMATION_FRAMES = 40
|
| 136 |
+
|
| 137 |
+
if T == 0:
|
| 138 |
+
time_steps = [0]
|
| 139 |
+
else:
|
| 140 |
+
num_points = min(T + 1, NUM_ANIMATION_FRAMES)
|
| 141 |
+
time_steps = np.linspace(start=0, stop=T, num=num_points, dtype=int)
|
| 142 |
+
time_steps = sorted(list(set(time_steps)))
|
| 143 |
+
|
| 144 |
+
if distribution_type == "Sinusoidal":
|
| 145 |
+
selected_initial_state_function_raw = lambda x, y, N_val_func: \
|
| 146 |
+
np.sin(x * 2 * np.pi / N_val_func) * (1 - 0.5 * x / N_val_func) * \
|
| 147 |
+
np.sin(y * 4 * np.pi / N_val_func) * (1 - 0.5 * y / N_val_func) + 1
|
| 148 |
+
elif distribution_type == "Gaussian":
|
| 149 |
+
selected_initial_state_function_raw = lambda x, y, N_val_func: \
|
| 150 |
+
np.exp(-((x - N_val_func / 2)**2 / (2 * (N_val_func / 5)**2) +
|
| 151 |
+
(y - N_val_func / 2)**2 / (2 * (N_val_func / 5)**2))) * 1.8 + 0.2
|
| 152 |
+
elif distribution_type == "Random":
|
| 153 |
+
selected_initial_state_function_raw = lambda x, y, N_val_func: \
|
| 154 |
+
np.random.rand(N_val_func, N_val_func) * 1.5 + 0.2 if isinstance(x, int) else \
|
| 155 |
+
np.random.rand(x.shape[0], x.shape[1]) * 1.5 + 0.2
|
| 156 |
+
else:
|
| 157 |
+
print(f"Warning: Unknown distribution type '{distribution_type}'. Defaulting to Sinusoidal.")
|
| 158 |
+
selected_initial_state_function_raw = lambda x, y, N_val_func: \
|
| 159 |
+
np.sin(x * 2 * np.pi / N_val_func) * (1 - 0.5 * x / N_val_func) * \
|
| 160 |
+
np.sin(y * 4 * np.pi / N_val_func) * (1 - 0.5 * y / N_val_func) + 1
|
| 161 |
+
|
| 162 |
+
initial_state_func_eval = lambda x_coords, y_coords: \
|
| 163 |
+
selected_initial_state_function_raw(x_coords, y_coords, current_N) * \
|
| 164 |
+
(y_coords < current_N).astype(int)
|
| 165 |
+
|
| 166 |
+
if velocity_field == "User":
|
| 167 |
+
pass
|
| 168 |
+
elif velocity_field == "Shear":
|
| 169 |
+
vx_input = vx_input * (current_N / 2)
|
| 170 |
+
elif velocity_field == "TGV":
|
| 171 |
+
vx_input = vx_input * np.sin(np.pi * current_N / 10)
|
| 172 |
+
vy_input = vy_input * np.cos(np.pi * current_N / 10)
|
| 173 |
+
elif velocity_field == "Swirl":
|
| 174 |
+
vx_input = vx_input * np.cos(np.pi * current_N / 5)
|
| 175 |
+
vy_input = vy_input * np.sin(np.pi * current_N / 5)
|
| 176 |
+
|
| 177 |
+
with tempfile.TemporaryDirectory() as tmp_npy_dir:
|
| 178 |
+
intermediate_folder_path = Path(tmp_npy_dir)
|
| 179 |
+
cudaq.set_target('nvidia', option='fp64')
|
| 180 |
+
|
| 181 |
+
@cudaq.kernel
|
| 182 |
+
def alloc_kernel(num_qubits_alloc: int):
|
| 183 |
+
qubits = cudaq.qvector(num_qubits_alloc)
|
| 184 |
+
|
| 185 |
+
from cupy.cuda.memory import MemoryPointer, UnownedMemory
|
| 186 |
+
|
| 187 |
+
def to_cupy_array(state):
|
| 188 |
+
tensor = state.getTensor()
|
| 189 |
+
pDevice = tensor.data()
|
| 190 |
+
sizeByte = tensor.get_num_elements() * tensor.get_element_size()
|
| 191 |
+
mem = UnownedMemory(pDevice, sizeByte, owner=state)
|
| 192 |
+
memptr_obj = MemoryPointer(mem, 0)
|
| 193 |
+
cupy_array_val = cp.ndarray(tensor.get_num_elements(),
|
| 194 |
+
dtype=cp.complex128,
|
| 195 |
+
memptr=memptr_obj)
|
| 196 |
+
return cupy_array_val
|
| 197 |
+
|
| 198 |
+
class QLBMAdvecDiffD2Q5_new:
|
| 199 |
+
def __init__(self, vx=0.2, vy=0.15) -> None:
|
| 200 |
+
self.dim = 2
|
| 201 |
+
self.ndir = 5
|
| 202 |
+
self.nq_dir = math.ceil(np.log2(self.ndir))
|
| 203 |
+
self.dirs = []
|
| 204 |
+
for dir_int in range(self.ndir):
|
| 205 |
+
dir_bin = f"{dir_int:b}".zfill(self.nq_dir)
|
| 206 |
+
self.dirs.append(dir_bin)
|
| 207 |
+
self.e_unitvec = np.array([0, 1, -1, 1, -1])
|
| 208 |
+
self.wts = np.array([2/6, 1/6, 1/6, 1/6, 1/6])
|
| 209 |
+
self.cs = 1 / np.sqrt(3)
|
| 210 |
+
self.vx = vx
|
| 211 |
+
self.vy = vy
|
| 212 |
+
self.u = np.array([0, self.vx, self.vx, self.vy, self.vy])
|
| 213 |
+
self.wtcoeffs = np.multiply(self.wts, 1 + self.e_unitvec * self.u / self.cs**2)
|
| 214 |
+
self.create_circuit()
|
| 215 |
+
|
| 216 |
+
def create_circuit(self):
|
| 217 |
+
v = np.pad(self.wtcoeffs, (0, 2**num_anc - self.ndir))
|
| 218 |
+
v = v**0.5
|
| 219 |
+
v = v / np.linalg.norm(v)
|
| 220 |
+
U_prep = 2 * np.outer(v, v) - np.eye(len(v))
|
| 221 |
+
cudaq.register_operation("prep_op", U_prep)
|
| 222 |
+
|
| 223 |
+
def collisionOp(dirs_list):
|
| 224 |
+
dirs_i_list_val = []
|
| 225 |
+
for dir_str in dirs_list:
|
| 226 |
+
dirs_i = [(int(c)) for c in dir_str]
|
| 227 |
+
dirs_i_list_val += dirs_i[::-1]
|
| 228 |
+
return dirs_i_list_val
|
| 229 |
+
|
| 230 |
+
self.dirs_i_list = collisionOp(self.dirs)
|
| 231 |
+
|
| 232 |
+
@cudaq.kernel
|
| 233 |
+
def rshift(q: cudaq.qview, n: int):
|
| 234 |
+
for i in range(n):
|
| 235 |
+
if i == n - 1:
|
| 236 |
+
x(q[n - 1 - i])
|
| 237 |
+
elif i == n - 2:
|
| 238 |
+
x.ctrl(q[n - 1 - (i + 1)], q[n - 1 - i])
|
| 239 |
+
else:
|
| 240 |
+
x.ctrl(q[0:n - 1 - i], q[n - 1 - i])
|
| 241 |
+
|
| 242 |
+
@cudaq.kernel
|
| 243 |
+
def lshift(q: cudaq.qview, n: int):
|
| 244 |
+
for i in range(n):
|
| 245 |
+
if i == 0:
|
| 246 |
+
x(q[0])
|
| 247 |
+
elif i == 1:
|
| 248 |
+
x.ctrl(q[0], q[1])
|
| 249 |
+
else:
|
| 250 |
+
x.ctrl(q[0:i], q[i])
|
| 251 |
+
|
| 252 |
+
@cudaq.kernel
|
| 253 |
+
def d2q5_tstep(q: cudaq.qview, nqx: int, nqy: int, nq_dir_val: int, dirs_i_val: list[int]):
|
| 254 |
+
qx = q[0:nqx]
|
| 255 |
+
qy = q[nqx:nqx + nqy]
|
| 256 |
+
qdir = q[nqx + nqy:nqx + nqy + nq_dir_val]
|
| 257 |
+
|
| 258 |
+
idx_lqx = 2
|
| 259 |
+
b_list = dirs_i_val[idx_lqx * nq_dir_val:(idx_lqx + 1) * nq_dir_val]
|
| 260 |
+
for j in range(nq_dir_val):
|
| 261 |
+
if b_list[j] == 0: x(qdir[j])
|
| 262 |
+
cudaq.control(lshift, qdir, qx, nqx)
|
| 263 |
+
for j in range(nq_dir_val):
|
| 264 |
+
if b_list[j] == 0: x(qdir[j])
|
| 265 |
+
|
| 266 |
+
idx_rqx = 1
|
| 267 |
+
b_list = dirs_i_val[idx_rqx * nq_dir_val:(idx_rqx + 1) * nq_dir_val]
|
| 268 |
+
for j in range(nq_dir_val):
|
| 269 |
+
if b_list[j] == 0: x(qdir[j])
|
| 270 |
+
cudaq.control(rshift, qdir, qx, nqx)
|
| 271 |
+
for j in range(nq_dir_val):
|
| 272 |
+
if b_list[j] == 0: x(qdir[j])
|
| 273 |
+
|
| 274 |
+
idx_lqy = 4
|
| 275 |
+
b_list = dirs_i_val[idx_lqy * nq_dir_val:(idx_lqy + 1) * nq_dir_val]
|
| 276 |
+
for j in range(nq_dir_val):
|
| 277 |
+
if b_list[j] == 0: x(qdir[j])
|
| 278 |
+
cudaq.control(lshift, qdir, qy, nqy)
|
| 279 |
+
for j in range(nq_dir_val):
|
| 280 |
+
if b_list[j] == 0: x(qdir[j])
|
| 281 |
+
|
| 282 |
+
idx_rqy = 3
|
| 283 |
+
b_list = dirs_i_val[idx_rqy * nq_dir_val:(idx_rqy + 1) * nq_dir_val]
|
| 284 |
+
for j in range(nq_dir_val):
|
| 285 |
+
if b_list[j] == 0: x(qdir[j])
|
| 286 |
+
cudaq.control(rshift, qdir, qy, nqy)
|
| 287 |
+
for j in range(nq_dir_val):
|
| 288 |
+
if b_list[j] == 0: x(qdir[j])
|
| 289 |
+
|
| 290 |
+
@cudaq.kernel
|
| 291 |
+
def d2q5_tstep_wrapper(state_arg: cudaq.State, nqx: int, nqy: int, nq_dir_val: int, dirs_i_val: list[int]):
|
| 292 |
+
q = cudaq.qvector(state_arg)
|
| 293 |
+
qdir = q[nqx + nqy:nqx + nqy + nq_dir_val]
|
| 294 |
+
prep_op(qdir[2], qdir[1], qdir[0])
|
| 295 |
+
d2q5_tstep(q, nqx, nqy, nq_dir_val, dirs_i_val)
|
| 296 |
+
prep_op(qdir[2], qdir[1], qdir[0])
|
| 297 |
+
|
| 298 |
+
def run_timestep_func(vec_arg, hadamard=False):
|
| 299 |
+
result = cudaq.get_state(d2q5_tstep_wrapper, vec_arg, num_reg_qubits, num_reg_qubits, self.nq_dir, self.dirs_i_list)
|
| 300 |
+
num_nonzero_ranks = num_ranks / (2**num_anc)
|
| 301 |
+
rank_slice_cupy = to_cupy_array(result)
|
| 302 |
+
if rank >= num_nonzero_ranks and num_nonzero_ranks > 0:
|
| 303 |
+
sub_sv_zeros = np.zeros(N_sub_per_rank, dtype=np.complex128)
|
| 304 |
+
cp.cuda.runtime.memcpy(rank_slice_cupy.data.ptr, sub_sv_zeros.ctypes.data, sub_sv_zeros.nbytes, cp.cuda.runtime.memcpyHostToDevice)
|
| 305 |
+
if rank == 0 and num_nonzero_ranks < 1 and N_sub_per_rank > 0:
|
| 306 |
+
limit_idx = int(N_tot_state_vector / (2**num_anc))
|
| 307 |
+
if limit_idx < rank_slice_cupy.size:
|
| 308 |
+
rank_slice_cupy[limit_idx:] = 0
|
| 309 |
+
return result
|
| 310 |
+
self.run_timestep = run_timestep_func
|
| 311 |
+
|
| 312 |
+
def write_state(self, state_to_write, t_step_str_val):
|
| 313 |
+
rank_slice_cupy = to_cupy_array(state_to_write)
|
| 314 |
+
num_nonzero_ranks = num_ranks / (2**num_anc)
|
| 315 |
+
if rank < num_nonzero_ranks or (rank == 0 and num_nonzero_ranks <= 0):
|
| 316 |
+
save_path = intermediate_folder_path / f"{t_step_str_val}_{rank}.npy"
|
| 317 |
+
with open(save_path, 'wb') as f:
|
| 318 |
+
arr_to_save = None
|
| 319 |
+
data_limit = N_sub_per_rank
|
| 320 |
+
if num_nonzero_ranks < 1 and rank == 0:
|
| 321 |
+
data_limit = int(N_tot_state_vector / (2**num_anc))
|
| 322 |
+
if data_limit > 0:
|
| 323 |
+
relevant_part_cupy = cp.real(rank_slice_cupy[:data_limit])
|
| 324 |
+
else:
|
| 325 |
+
relevant_part_cupy = cp.array([], dtype=cp.float64)
|
| 326 |
+
if relevant_part_cupy.size >= current_N * current_N:
|
| 327 |
+
arr_flat = relevant_part_cupy[:current_N * current_N]
|
| 328 |
+
if downsampling_factor > 1 and current_N > 0:
|
| 329 |
+
arr_reshaped = arr_flat.reshape((current_N, current_N))
|
| 330 |
+
arr_downsampled = arr_reshaped[::downsampling_factor, ::downsampling_factor]
|
| 331 |
+
arr_to_save = arr_downsampled.flatten()
|
| 332 |
+
else:
|
| 333 |
+
arr_to_save = arr_flat
|
| 334 |
+
elif relevant_part_cupy.size > 0:
|
| 335 |
+
if downsampling_factor > 1:
|
| 336 |
+
arr_to_save = relevant_part_cupy[::downsampling_factor]
|
| 337 |
+
else:
|
| 338 |
+
arr_to_save = relevant_part_cupy
|
| 339 |
+
if arr_to_save is not None and arr_to_save.size > 0:
|
| 340 |
+
np.save(f, arr_to_save.get() if isinstance(arr_to_save, cp.ndarray) else arr_to_save)
|
| 341 |
+
|
| 342 |
+
def run_evolution(self, initial_state_arg, total_timesteps, time_steps_to_save, observable=False):
|
| 343 |
+
current_state_val = initial_state_arg
|
| 344 |
+
save_times = set(time_steps_to_save)
|
| 345 |
+
if 0 in save_times:
|
| 346 |
+
self.write_state(current_state_val, '0')
|
| 347 |
+
for t_iter in range(total_timesteps):
|
| 348 |
+
if (t_iter + 1) in save_times:
|
| 349 |
+
next_state_val = self.run_timestep(current_state_val)
|
| 350 |
+
self.write_state(next_state_val, str(t_iter + 1))
|
| 351 |
+
current_state_val = next_state_val
|
| 352 |
+
else:
|
| 353 |
+
current_state_val = self.run_timestep(current_state_val)
|
| 354 |
+
cp.get_default_memory_pool().free_all_blocks()
|
| 355 |
+
if rank == 0:
|
| 356 |
+
print(f"Timestep: {total_timesteps}/{total_timesteps} (Evolution complete)")
|
| 357 |
+
cp.get_default_memory_pool().free_all_blocks()
|
| 358 |
+
self.final_state = current_state_val
|
| 359 |
+
|
| 360 |
+
if boundary_condition == "Periodic":
|
| 361 |
+
pass
|
| 362 |
+
elif boundary_condition == "Dirichlet":
|
| 363 |
+
pass
|
| 364 |
+
elif boundary_condition == "Neumann":
|
| 365 |
+
pass
|
| 366 |
+
|
| 367 |
+
downsampling_factor = 2**5
|
| 368 |
+
if current_N == 0:
|
| 369 |
+
print("Error: current_N is zero. num_reg_qubits likely too small.")
|
| 370 |
+
return None, None # Modified return
|
| 371 |
+
if current_N < downsampling_factor:
|
| 372 |
+
downsampling_factor = current_N if current_N > 0 else 1
|
| 373 |
+
|
| 374 |
+
qlbm_obj = QLBMAdvecDiffD2Q5_new(vx=vx_input, vy=vy_input)
|
| 375 |
+
initial_state_val = cudaq.get_state(alloc_kernel, num_qubits_total)
|
| 376 |
+
|
| 377 |
+
xv_init = np.arange(current_N)
|
| 378 |
+
yv_init = np.arange(current_N)
|
| 379 |
+
initial_grid_2d_X, initial_grid_2d_Y = np.meshgrid(xv_init, yv_init)
|
| 380 |
+
|
| 381 |
+
if distribution_type == "Random":
|
| 382 |
+
initial_grid_2d = selected_initial_state_function_raw(current_N, current_N, current_N)
|
| 383 |
+
else:
|
| 384 |
+
initial_grid_2d = initial_state_func_eval(initial_grid_2d_X, initial_grid_2d_Y)
|
| 385 |
+
|
| 386 |
+
sub_sv_init_flat = initial_grid_2d.flatten().astype(np.complex128)
|
| 387 |
+
norm = np.linalg.norm(sub_sv_init_flat)
|
| 388 |
+
if norm > 0:
|
| 389 |
+
sub_sv_init_flat /= norm
|
| 390 |
+
else:
|
| 391 |
+
print("Error: Initial state norm is zero.")
|
| 392 |
+
return None, None # Modified return
|
| 393 |
+
full_initial_sv_host = np.zeros(N_sub_per_rank, dtype=np.complex128)
|
| 394 |
+
num_computational_states = current_N * current_N
|
| 395 |
+
if len(sub_sv_init_flat) == num_computational_states:
|
| 396 |
+
if num_computational_states <= N_sub_per_rank:
|
| 397 |
+
full_initial_sv_host[:num_computational_states] = sub_sv_init_flat
|
| 398 |
+
else:
|
| 399 |
+
print(f"Error: Grid data {num_computational_states} > N_sub_per_rank {N_sub_per_rank}")
|
| 400 |
+
return None, None # Modified return
|
| 401 |
+
else:
|
| 402 |
+
print(f"Warning: Initial state size {len(sub_sv_init_flat)} != expected {num_computational_states}")
|
| 403 |
+
fill_len = min(len(sub_sv_init_flat), num_computational_states, N_sub_per_rank)
|
| 404 |
+
full_initial_sv_host[:fill_len] = sub_sv_init_flat[:fill_len]
|
| 405 |
+
|
| 406 |
+
rank_slice_init = to_cupy_array(initial_state_val)
|
| 407 |
+
print(f'Rank {rank}: Initializing state with {distribution_type} (vx={vx_input}, vy={vy_input})...')
|
| 408 |
+
cp.cuda.runtime.memcpy(rank_slice_init.data.ptr, full_initial_sv_host.ctypes.data, full_initial_sv_host.nbytes, cp.cuda.runtime.memcpyHostToDevice)
|
| 409 |
+
print(f'Rank {rank}: Initial state copied. Size: {len(sub_sv_init_flat)}. N_sub_per_rank: {N_sub_per_rank}')
|
| 410 |
+
|
| 411 |
+
print("Starting QLBM evolution...")
|
| 412 |
+
qlbm_obj.run_evolution(initial_state_val, T, time_steps)
|
| 413 |
+
print("QLBM evolution complete.")
|
| 414 |
+
|
| 415 |
+
print("Generating interactive plot with Plotly...")
|
| 416 |
+
downsampled_N = current_N // downsampling_factor
|
| 417 |
+
if downsampled_N == 0 and current_N > 0:
|
| 418 |
+
downsampled_N = 1
|
| 419 |
+
elif current_N == 0:
|
| 420 |
+
print("Error: current_N is zero before Plotly stage.")
|
| 421 |
+
return None, None # Modified return
|
| 422 |
+
|
| 423 |
+
data_frames = []
|
| 424 |
+
actual_timesteps = []
|
| 425 |
+
for t in time_steps:
|
| 426 |
+
file_path = intermediate_folder_path / f"{t}_{rank}.npy"
|
| 427 |
+
if file_path.exists():
|
| 428 |
+
sol_loaded = np.load(file_path)
|
| 429 |
+
if sol_loaded.size == downsampled_N * downsampled_N:
|
| 430 |
+
Z_data = np.reshape(sol_loaded, (downsampled_N, downsampled_N))
|
| 431 |
+
data_frames.append(Z_data)
|
| 432 |
+
actual_timesteps.append(t)
|
| 433 |
+
print(f"Time {t}: Min={np.min(Z_data)}, Max={np.max(Z_data)}, Mean={np.mean(Z_data)}")
|
| 434 |
+
else:
|
| 435 |
+
print(f"Warning: File {file_path} size {sol_loaded.size} != expected {downsampled_N*downsampled_N}. Skipping.")
|
| 436 |
+
else:
|
| 437 |
+
print(f"Warning: File {file_path} not found. Skipping.")
|
| 438 |
+
|
| 439 |
+
if not data_frames:
|
| 440 |
+
print("Error: No data frames loaded for plotting.")
|
| 441 |
+
return None, None # Modified return
|
| 442 |
+
|
| 443 |
+
x_coords_plot = np.linspace(0, 1, downsampled_N)
|
| 444 |
+
y_coords_plot = np.linspace(0, 1, downsampled_N)
|
| 445 |
+
|
| 446 |
+
z_min = min([np.min(Z) for Z in data_frames])
|
| 447 |
+
z_max = max([np.max(Z) for Z in data_frames])
|
| 448 |
+
if z_max == z_min:
|
| 449 |
+
z_max += 1e-9
|
| 450 |
+
|
| 451 |
+
fig = go.Figure()
|
| 452 |
+
|
| 453 |
+
# Store individual frames for download
|
| 454 |
+
plotly_json_frames = []
|
| 455 |
+
|
| 456 |
+
for i, Z in enumerate(data_frames):
|
| 457 |
+
frame_trace = go.Surface(
|
| 458 |
+
z=Z, x=x_coords_plot, y=y_coords_plot,
|
| 459 |
+
colorscale='Blues',
|
| 460 |
+
cmin=z_min, cmax=z_max,
|
| 461 |
+
name=f'Time: {actual_timesteps[i]}',
|
| 462 |
+
showscale=False
|
| 463 |
+
)
|
| 464 |
+
fig.add_trace(frame_trace)
|
| 465 |
+
|
| 466 |
+
# Create a figure for the individual frame and convert to JSON
|
| 467 |
+
single_frame_fig = go.Figure(data=[frame_trace], layout=fig.layout)
|
| 468 |
+
single_frame_fig.update_layout(
|
| 469 |
+
title=f"Time: {actual_timesteps[i]}",
|
| 470 |
+
scene=dict(
|
| 471 |
+
xaxis_title='X',
|
| 472 |
+
yaxis_title='Y',
|
| 473 |
+
zaxis_title='Density',
|
| 474 |
+
xaxis=dict(range=[x_coords_plot[0], x_coords_plot[-1]]),
|
| 475 |
+
yaxis=dict(range=[y_coords_plot[0], y_coords_plot[-1]]),
|
| 476 |
+
zaxis=dict(range=[z_min, z_max]),
|
| 477 |
+
)
|
| 478 |
+
)
|
| 479 |
+
plotly_json_frames.append(single_frame_fig.to_json())
|
| 480 |
+
|
| 481 |
+
|
| 482 |
+
for trace in fig.data[1:]:
|
| 483 |
+
trace.visible = False
|
| 484 |
+
|
| 485 |
+
steps = []
|
| 486 |
+
for i in range(len(data_frames)):
|
| 487 |
+
step = dict(
|
| 488 |
+
method="update",
|
| 489 |
+
args=[{"visible": [False] * len(data_frames)}],
|
| 490 |
+
label=f"Time: {actual_timesteps[i]}"
|
| 491 |
+
)
|
| 492 |
+
step["args"][0]["visible"][i] = True
|
| 493 |
+
steps.append(step)
|
| 494 |
+
|
| 495 |
+
sliders = [dict(active=0, currentvalue={"prefix": "Time: "}, pad={"t": 50}, steps=steps)]
|
| 496 |
+
|
| 497 |
+
# fig.update_layout(
|
| 498 |
+
# title='', # Removed graph title
|
| 499 |
+
# scene=dict(
|
| 500 |
+
# xaxis_title='X',
|
| 501 |
+
# yaxis_title='Y',
|
| 502 |
+
# zaxis_title='Density',
|
| 503 |
+
# xaxis=dict(range=[x_coords_plot[0], x_coords_plot[-1]]),
|
| 504 |
+
# yaxis=dict(range=[y_coords_plot[0], y_coords_plot[-1]]),
|
| 505 |
+
# zaxis=dict(range=[z_min, z_max]),
|
| 506 |
+
# ),
|
| 507 |
+
# sliders=sliders,
|
| 508 |
+
# width=800,
|
| 509 |
+
# height=700
|
| 510 |
+
# )
|
| 511 |
+
fig.update_layout(
|
| 512 |
+
title='', # Removed graph title
|
| 513 |
+
scene=dict(
|
| 514 |
+
xaxis_title='X',
|
| 515 |
+
yaxis_title='Y',
|
| 516 |
+
zaxis_title='Density',
|
| 517 |
+
xaxis=dict(visible=False),
|
| 518 |
+
yaxis=dict(visible=False),
|
| 519 |
+
zaxis=dict(visible=False),
|
| 520 |
+
),
|
| 521 |
+
sliders=sliders,
|
| 522 |
+
width=800,
|
| 523 |
+
height=700
|
| 524 |
+
)
|
| 525 |
+
return fig, plotly_json_frames # Modified return
|
| 526 |
+
|
| 527 |
+
def simulate_qlbm_3D_and_animate(num_reg_qubits: int, T: int, distribution_type: str, vx_input, vy_input, vz_input, boundary_condition: str):
|
| 528 |
+
num_anc = 3
|
| 529 |
+
num_qubits_total = 3 * num_reg_qubits + num_anc
|
| 530 |
+
current_N = 2**num_reg_qubits
|
| 531 |
+
N_tot_state_vector = 2**num_qubits_total
|
| 532 |
+
num_ranks = 1
|
| 533 |
+
rank = 0
|
| 534 |
+
N_sub_per_rank = int(N_tot_state_vector // num_ranks)
|
| 535 |
+
|
| 536 |
+
# Simplified time steps for 3D since slider steps are removed
|
| 537 |
+
NUM_ANIMATION_FRAMES_3D = 10 # Default number of frames if no specific slider steps
|
| 538 |
+
|
| 539 |
+
if T == 0:
|
| 540 |
+
time_steps = [0]
|
| 541 |
+
else:
|
| 542 |
+
num_points = min(T + 1, NUM_ANIMATION_FRAMES_3D)
|
| 543 |
+
time_steps = np.linspace(start=0, stop=T, num=num_points, dtype=int)
|
| 544 |
+
time_steps = sorted(list(set(time_steps)))
|
| 545 |
+
|
| 546 |
+
|
| 547 |
+
if distribution_type == "Sinusoidal":
|
| 548 |
+
selected_initial_state_function_raw = lambda x, y, z, N_val_func: \
|
| 549 |
+
np.sin(x * 2 * np.pi / N_val_func) * \
|
| 550 |
+
np.sin(y * 2 * np.pi / N_val_func) * \
|
| 551 |
+
np.sin(z * 2 * np.pi / N_val_func) + 1
|
| 552 |
+
elif distribution_type == "Gaussian":
|
| 553 |
+
selected_initial_state_function_raw = lambda x, y, z, N_val_func: \
|
| 554 |
+
np.exp(-((x - N_val_func / 2)**2 / (2 * (N_val_func / 5)**2) + (y - N_val_func / 2)**2 / (2 * (N_val_func / 5)**2) +
|
| 555 |
+
(z - N_val_func / 2)**2 / (2 * (N_val_func / 5)**2))) * 1.8 + 0.2
|
| 556 |
+
else:
|
| 557 |
+
print(f"Warning: Unknown distribution type '{distribution_type}'. Defaulting to Sinusoidal.")
|
| 558 |
+
selected_initial_state_function_raw = lambda x, y, z, N_val_func: \
|
| 559 |
+
np.sin(x * 2 * np.pi / N_val_func) * \
|
| 560 |
+
np.sin(y * 2 * np.pi / N_val_func) * \
|
| 561 |
+
np.sin(z * 2 * np.pi / N_val_func) + 1
|
| 562 |
+
|
| 563 |
+
initial_state_func_eval = lambda i:\
|
| 564 |
+
selected_initial_state_function_raw(i%current_N,(i//current_N)%current_N,i//(current_N**2),current_N)*(i<(current_N**3)).astype(int)
|
| 565 |
+
|
| 566 |
+
with tempfile.TemporaryDirectory() as tmp_npy_dir:
|
| 567 |
+
intermediate_folder_path = Path(tmp_npy_dir)
|
| 568 |
+
|
| 569 |
+
cudaq.set_target('nvidia', option='fp64')
|
| 570 |
+
|
| 571 |
+
@cudaq.kernel
|
| 572 |
+
def alloc_kernel(num_qubits_alloc: int):
|
| 573 |
+
qubits = cudaq.qvector(num_qubits_alloc)
|
| 574 |
+
|
| 575 |
+
from cupy.cuda.memory import MemoryPointer, UnownedMemory
|
| 576 |
+
|
| 577 |
+
def to_cupy_array(state):
|
| 578 |
+
tensor = state.getTensor()
|
| 579 |
+
pDevice = tensor.data()
|
| 580 |
+
sizeByte = tensor.get_num_elements() * tensor.get_element_size()
|
| 581 |
+
mem = UnownedMemory(pDevice, sizeByte, owner=state)
|
| 582 |
+
memptr_obj = MemoryPointer(mem, 0)
|
| 583 |
+
cupy_array_val = cp.ndarray(tensor.get_num_elements(),
|
| 584 |
+
dtype=cp.complex128,
|
| 585 |
+
memptr=memptr_obj)
|
| 586 |
+
return cupy_array_val
|
| 587 |
+
|
| 588 |
+
class QLBMAdvecDiffD3Q7_new:
|
| 589 |
+
def __init__(self,vx,vy,vz) -> None:
|
| 590 |
+
self.dim = 3
|
| 591 |
+
self.ndir = 7
|
| 592 |
+
self.nq_dir = math.ceil(np.log2(self.ndir))
|
| 593 |
+
self.dirs=[]
|
| 594 |
+
for dir_int in range(self.ndir):
|
| 595 |
+
if dir_int==4:
|
| 596 |
+
dir_bin="111"
|
| 597 |
+
else:
|
| 598 |
+
dir_bin = f"{dir_int:b}".zfill(self.nq_dir)
|
| 599 |
+
self.dirs.append(dir_bin)
|
| 600 |
+
self.cs = 1/np.sqrt(3)
|
| 601 |
+
self.ux = lambda x,y,z: vx(x,y,z)/self.cs**2
|
| 602 |
+
self.uy = lambda x,y,z: vy(x,y,z)/self.cs**2
|
| 603 |
+
self.uz = lambda x,y,z: vz(x,y,z)/self.cs**2
|
| 604 |
+
self.create_circuit()
|
| 605 |
+
|
| 606 |
+
def create_circuit(self):
|
| 607 |
+
print("Creating circuit")
|
| 608 |
+
x_coeffs,x_coeff_var_indices=get_circuit_inputs(lambda x,y,z: ((1+self.ux(x/current_N,y/current_N,z/current_N))/2)**0.5,num_reg_qubits,min(current_N,32))
|
| 609 |
+
y_coeffs,y_coeff_var_indices=get_circuit_inputs(lambda x,y,z: ((1+self.uy(x/current_N,y/current_N,z/current_N))/2)**0.5,num_reg_qubits,min(current_N,32))
|
| 610 |
+
z_coeffs,z_coeff_var_indices=get_circuit_inputs(lambda x,y,z: ((1+self.uz(x/current_N,y/current_N,z/current_N))/2)**0.5,num_reg_qubits,min(current_N,32))
|
| 611 |
+
x_coeffs_,x_coeff_var_indices_=get_circuit_inputs(lambda x,y,z: 0 if (1+self.ux((x-1)/current_N,y/current_N,z/current_N))==0 else \
|
| 612 |
+
((1+self.ux((x-1)/current_N,y/current_N,z/current_N))/(2+self.ux((x-1)/current_N,y/current_N,z/current_N)-self.ux((x+1)/current_N,y/current_N,z/current_N)))**0.5,num_reg_qubits,min(current_N,32))
|
| 613 |
+
y_coeffs_,y_coeff_var_indices_=get_circuit_inputs(lambda x,y,z: 0 if (1+self.uy(x/current_N,(y-1)/current_N,z/current_N))==0 else \
|
| 614 |
+
((1+self.uy(x/current_N,(y-1)/current_N,z/current_N))/(2+self.uy(x/current_N,(y-1)/current_N,z/current_N)-self.uy(x/current_N,(y+1)/current_N,z/current_N)))**0.5,num_reg_qubits,min(current_N,32))
|
| 615 |
+
z_coeffs_,z_coeff_var_indices_=get_circuit_inputs(lambda x,y,z: 0 if (1+self.uz(x/current_N,y/current_N,(z-1)/current_N))==0 else \
|
| 616 |
+
((1+self.uz(x/current_N,y/current_N,(z-1)/current_N))/(2+self.uz(x/current_N,y/current_N,(z-1)/current_N)-self.uz(x/current_N,y/current_N,(z+1)/current_N)))**0.5,num_reg_qubits,min(current_N,32))
|
| 617 |
+
unprep1_coeffs,unprep1_coeff_var_indices=get_circuit_inputs(lambda x,y,z:\
|
| 618 |
+
(1/3**0.5)*(1+(self.ux((x-1)/current_N,y/current_N,z/current_N)-self.ux((x+1)/current_N,y/current_N,z/current_N))/2)**0.5,num_reg_qubits,min(current_N,32))
|
| 619 |
+
unprep2_coeffs, unprep2_coeff_var_indices = get_circuit_inputs(lambda x, y, z: ((1 + (self.uy(x/current_N, (y-1)/current_N, z/current_N) - self.uy(x/current_N, (y+1)/current_N, z/current_N))/2) /(2 - (self.ux((x-1)/current_N, y/current_N, z/current_N) - self.ux((x+1)/current_N, y/current_N, z/current_N))/2))**0.5, num_reg_qubits, min(current_N, 32))
|
| 620 |
+
print("Generated angles")
|
| 621 |
+
v=np.pad([1/4, 1/4, 0, 1/4, 0, 1/4, 0],(0,2**num_anc - self.ndir))
|
| 622 |
+
v=v**0.5
|
| 623 |
+
v[0]+=1
|
| 624 |
+
v=v/np.linalg.norm(v)
|
| 625 |
+
U_prep=2*np.outer(v,v)-np.eye(len(v))
|
| 626 |
+
cudaq.register_operation("prep_op", U_prep)
|
| 627 |
+
def collisionOp(dirs):
|
| 628 |
+
dirs_i_list=[]
|
| 629 |
+
for dir_ in dirs:
|
| 630 |
+
dirs_i=[(int(c)) for c in dir_]
|
| 631 |
+
dirs_i_list+=dirs_i[::-1]
|
| 632 |
+
return dirs_i_list
|
| 633 |
+
self.dirs_i_list=collisionOp(self.dirs)
|
| 634 |
+
print("Generated dirs_i_list")
|
| 635 |
+
@cudaq.kernel
|
| 636 |
+
def rshift(q: cudaq.qview, n: int):
|
| 637 |
+
for i in range(n):
|
| 638 |
+
if i == n-1:
|
| 639 |
+
x(q[n-1-i])
|
| 640 |
+
elif i == n-2:
|
| 641 |
+
x.ctrl(q[n-1-(i+1)], q[n-1-i])
|
| 642 |
+
else:
|
| 643 |
+
x.ctrl(q[0:n-1-i], q[n-1-i])
|
| 644 |
+
@cudaq.kernel
|
| 645 |
+
def lshift(q: cudaq.qview, n: int):
|
| 646 |
+
for i in range(n):
|
| 647 |
+
if i == 0:
|
| 648 |
+
x(q[0])
|
| 649 |
+
elif i == 1:
|
| 650 |
+
x.ctrl(q[0], q[1])
|
| 651 |
+
else:
|
| 652 |
+
x.ctrl(q[0:i], q[i])
|
| 653 |
+
@cudaq.kernel
|
| 654 |
+
def d2q5_tstep(q: cudaq.qview, nqx: int, nqy: int, nqz: int, nq_dir: int, dirs_i: list[int]):
|
| 655 |
+
qx=q[0:nqx]
|
| 656 |
+
qy=q[nqx:nqx+nqy]
|
| 657 |
+
qz=q[nqx+nqy:nqx+nqy+nqz]
|
| 658 |
+
qdir=q[nqx+nqy+nqz:nqx+nqy+nqz+nq_dir]
|
| 659 |
+
i=2
|
| 660 |
+
b_list=dirs_i[i*nq_dir:(i+1)*nq_dir]
|
| 661 |
+
for j in range(nq_dir):
|
| 662 |
+
b=b_list[j]
|
| 663 |
+
if b==0:
|
| 664 |
+
x(qdir[j])
|
| 665 |
+
cudaq.control(lshift,qdir,qx,nqx)
|
| 666 |
+
for j in range(nq_dir):
|
| 667 |
+
b=b_list[j]
|
| 668 |
+
if b==0:
|
| 669 |
+
x(qdir[j])
|
| 670 |
+
i=1
|
| 671 |
+
b_list=dirs_i[i*nq_dir:(i+1)*nq_dir]
|
| 672 |
+
for j in range(nq_dir):
|
| 673 |
+
b=b_list[j]
|
| 674 |
+
if b==0:
|
| 675 |
+
x(qdir[j])
|
| 676 |
+
cudaq.control(rshift,qdir,qx,nqx)
|
| 677 |
+
for j in range(nq_dir):
|
| 678 |
+
b=b_list[j]
|
| 679 |
+
if b==0:
|
| 680 |
+
x(qdir[j])
|
| 681 |
+
i=4
|
| 682 |
+
b_list=dirs_i[i*nq_dir:(i+1)*nq_dir]
|
| 683 |
+
for j in range(nq_dir):
|
| 684 |
+
b=b_list[j]
|
| 685 |
+
if b==0:
|
| 686 |
+
x(qdir[j])
|
| 687 |
+
cudaq.control(lshift,qdir,qy,nqy)
|
| 688 |
+
for j in range(nq_dir):
|
| 689 |
+
b=b_list[j]
|
| 690 |
+
if b==0:
|
| 691 |
+
x(qdir[j])
|
| 692 |
+
i=3
|
| 693 |
+
b_list=dirs_i[i*nq_dir:(i+1)*nq_dir]
|
| 694 |
+
for j in range(nq_dir):
|
| 695 |
+
b=b_list[j]
|
| 696 |
+
if b==0:
|
| 697 |
+
x(qdir[j])
|
| 698 |
+
cudaq.control(rshift,qdir,qy,nqy)
|
| 699 |
+
for j in range(nq_dir):
|
| 700 |
+
b=b_list[j]
|
| 701 |
+
if b==0:
|
| 702 |
+
x(qdir[j])
|
| 703 |
+
i=6
|
| 704 |
+
b_list=dirs_i[i*nq_dir:(i+1)*nq_dir]
|
| 705 |
+
for j in range(nq_dir):
|
| 706 |
+
b=b_list[j]
|
| 707 |
+
if b==0:
|
| 708 |
+
x(qdir[j])
|
| 709 |
+
cudaq.control(lshift,qdir,qz,nqz)
|
| 710 |
+
for j in range(nq_dir):
|
| 711 |
+
b=b_list[j]
|
| 712 |
+
if b==0:
|
| 713 |
+
x(qdir[j])
|
| 714 |
+
i=5
|
| 715 |
+
b_list=dirs_i[i*nq_dir:(i+1)*nq_dir]
|
| 716 |
+
for j in range(nq_dir):
|
| 717 |
+
b=b_list[j]
|
| 718 |
+
if b==0:
|
| 719 |
+
x(qdir[j])
|
| 720 |
+
cudaq.control(rshift,qdir,qz,nqz)
|
| 721 |
+
for j in range(nq_dir):
|
| 722 |
+
b=b_list[j]
|
| 723 |
+
if b==0:
|
| 724 |
+
x(qdir[j])
|
| 725 |
+
@cudaq.kernel
|
| 726 |
+
def d2q5_tstep_wrapper(state: cudaq.State,nqx:int,nqy:int,nqz:int,nq_dir:int,dirs_i:list[int],\
|
| 727 |
+
x_coeff_var_indices:list[int],x_coeffs:list[float],\
|
| 728 |
+
y_coeff_var_indices:list[int],y_coeffs:list[float],\
|
| 729 |
+
z_coeff_var_indices:list[int],z_coeffs:list[float],\
|
| 730 |
+
x_coeff_var_indices_:list[int],x_coeffs_:list[float],\
|
| 731 |
+
y_coeff_var_indices_:list[int],y_coeffs_:list[float],\
|
| 732 |
+
z_coeff_var_indices_:list[int],z_coeffs_:list[float],\
|
| 733 |
+
unprep1_coeff_var_indices:list[int],unprep1_coeffs:list[float],\
|
| 734 |
+
unprep2_coeff_var_indices:list[int],unprep2_coeffs:list[float]):
|
| 735 |
+
q=cudaq.qvector(state)
|
| 736 |
+
qdir=q[nqx+nqy+nqz:nqx+nqy+nqz+nq_dir]
|
| 737 |
+
prep_op(qdir[2],qdir[1],qdir[0])
|
| 738 |
+
x.ctrl(qdir[0],qdir[1])
|
| 739 |
+
ind=0
|
| 740 |
+
coeff_ind=0
|
| 741 |
+
x(qdir[2])
|
| 742 |
+
while ind<len(x_coeff_var_indices):
|
| 743 |
+
tuple_length=x_coeff_var_indices[ind]
|
| 744 |
+
for sub_ind in range(ind+1, ind+1+tuple_length):
|
| 745 |
+
x.ctrl(q[nqx+nqy+nqz-1-x_coeff_var_indices[sub_ind]],qdir[0])
|
| 746 |
+
ry.ctrl(-x_coeffs[coeff_ind],[qdir[2],qdir[1]],qdir[0])
|
| 747 |
+
coeff_ind+=1
|
| 748 |
+
ind+=(1+tuple_length)
|
| 749 |
+
x(qdir[2])
|
| 750 |
+
ind=0
|
| 751 |
+
coeff_ind=0
|
| 752 |
+
while ind<len(z_coeff_var_indices):
|
| 753 |
+
tuple_length=z_coeff_var_indices[ind]
|
| 754 |
+
for sub_ind in range(ind+1,ind+1+tuple_length):
|
| 755 |
+
x.ctrl(q[nqx+nqy+nqz-1-z_coeff_var_indices[sub_ind]],qdir[0])
|
| 756 |
+
ry.ctrl(-z_coeffs[coeff_ind],[qdir[2],qdir[1]],qdir[0])
|
| 757 |
+
coeff_ind+=1
|
| 758 |
+
ind+=(1+tuple_length)
|
| 759 |
+
x.ctrl(qdir[0],qdir[1])
|
| 760 |
+
ind=0
|
| 761 |
+
coeff_ind=0
|
| 762 |
+
while ind<len(y_coeff_var_indices):
|
| 763 |
+
tuple_length=y_coeff_var_indices[ind]
|
| 764 |
+
for sub_ind in range(ind+1,ind+1+tuple_length):
|
| 765 |
+
x.ctrl(q[nqx+nqy+nqz-1-y_coeff_var_indices[sub_ind]],qdir[2])
|
| 766 |
+
ry.ctrl(y_coeffs[coeff_ind],[qdir[0],qdir[1]],qdir[2])
|
| 767 |
+
coeff_ind+=1
|
| 768 |
+
ind+=(1+tuple_length)
|
| 769 |
+
d2q5_tstep(q,nqx,nqy,nqz,nq_dir,dirs_i)
|
| 770 |
+
ind=0
|
| 771 |
+
coeff_ind=0
|
| 772 |
+
while ind<len(y_coeff_var_indices_):
|
| 773 |
+
tuple_length=y_coeff_var_indices_[ind]
|
| 774 |
+
for sub_ind in range(ind+1,ind+1+tuple_length):
|
| 775 |
+
x.ctrl(q[nqx+nqy+nqz-1-y_coeff_var_indices_[sub_ind]],qdir[2])
|
| 776 |
+
ry.ctrl(-y_coeffs_[coeff_ind],[qdir[0],qdir[1]],qdir[2])
|
| 777 |
+
coeff_ind+=1
|
| 778 |
+
ind+=(1+tuple_length)
|
| 779 |
+
x.ctrl(qdir[0],qdir[1])
|
| 780 |
+
ind=0
|
| 781 |
+
coeff_ind=0
|
| 782 |
+
x(qdir[2])
|
| 783 |
+
while ind<len(x_coeff_var_indices_):
|
| 784 |
+
tuple_length=x_coeff_var_indices_[ind]
|
| 785 |
+
for sub_ind in range(ind+1,ind+1+tuple_length):
|
| 786 |
+
x.ctrl(q[nqx+nqy+nqz-1-x_coeff_var_indices_[sub_ind]],qdir[0])
|
| 787 |
+
ry.ctrl(x_coeffs_[coeff_ind],[qdir[1],qdir[2]],qdir[0])
|
| 788 |
+
coeff_ind+=1
|
| 789 |
+
ind+=(1+tuple_length)
|
| 790 |
+
x(qdir[2])
|
| 791 |
+
ind=0
|
| 792 |
+
coeff_ind=0
|
| 793 |
+
while ind<len(z_coeff_var_indices_):
|
| 794 |
+
tuple_length=z_coeff_var_indices_[ind]
|
| 795 |
+
for sub_ind in range(ind+1,ind+1+tuple_length):
|
| 796 |
+
x.ctrl(q[nqx+nqy+nqz-1-z_coeff_var_indices_[sub_ind]],qdir[0])
|
| 797 |
+
ry.ctrl(z_coeffs_[coeff_ind],[qdir[1],qdir[2]],qdir[0])
|
| 798 |
+
coeff_ind+=1
|
| 799 |
+
ind+=(1+tuple_length)
|
| 800 |
+
x.ctrl(qdir[0],qdir[1])
|
| 801 |
+
ind=0
|
| 802 |
+
coeff_ind=0
|
| 803 |
+
x.ctrl(qdir[1],qdir[2])
|
| 804 |
+
while ind<len(unprep2_coeff_var_indices):
|
| 805 |
+
tuple_length=unprep2_coeff_var_indices[ind]
|
| 806 |
+
for sub_ind in range(ind+1,ind+1+tuple_length):
|
| 807 |
+
x.ctrl(q[nqx+nqy+nqz-1-unprep2_coeff_var_indices[sub_ind]],qdir[1])
|
| 808 |
+
ry.ctrl(unprep2_coeffs[coeff_ind],qdir[2],qdir[1])
|
| 809 |
+
coeff_ind+=1
|
| 810 |
+
ind+=(1+tuple_length)
|
| 811 |
+
x.ctrl(qdir[1],qdir[2])
|
| 812 |
+
ind=0
|
| 813 |
+
coeff_ind=0
|
| 814 |
+
while ind<len(unprep1_coeff_var_indices):
|
| 815 |
+
tuple_length=unprep1_coeff_var_indices[ind]
|
| 816 |
+
for sub_ind in range(ind+1,ind+1+tuple_length):
|
| 817 |
+
x.ctrl(q[nqx+nqy+nqz-1-unprep1_coeff_var_indices[sub_ind]],qdir[1])
|
| 818 |
+
ry.ctrl(-unprep1_coeffs[coeff_ind],qdir[0],qdir[1])
|
| 819 |
+
coeff_ind+=1
|
| 820 |
+
ind+=(1+tuple_length)
|
| 821 |
+
ry(-2*np.pi/3,qdir[0])
|
| 822 |
+
print("Kernels defined")
|
| 823 |
+
def run_timestep_func(vec_arg, hadamard=False):
|
| 824 |
+
result=cudaq.get_state(d2q5_tstep_wrapper,vec_arg,num_reg_qubits,num_reg_qubits,num_reg_qubits,self.nq_dir,self.dirs_i_list,\
|
| 825 |
+
x_coeff_var_indices,x_coeffs,\
|
| 826 |
+
y_coeff_var_indices,y_coeffs,\
|
| 827 |
+
z_coeff_var_indices,z_coeffs,\
|
| 828 |
+
x_coeff_var_indices_,x_coeffs_,\
|
| 829 |
+
y_coeff_var_indices_,y_coeffs_,\
|
| 830 |
+
z_coeff_var_indices_,z_coeffs_,\
|
| 831 |
+
unprep1_coeff_var_indices,unprep1_coeffs,\
|
| 832 |
+
unprep2_coeff_var_indices,unprep2_coeffs)
|
| 833 |
+
num_nonzero_ranks = num_ranks / (2**num_anc)
|
| 834 |
+
rank_slice_cupy = to_cupy_array(result)
|
| 835 |
+
if rank >= num_nonzero_ranks and num_nonzero_ranks > 0:
|
| 836 |
+
sub_sv_zeros = np.zeros(N_sub_per_rank, dtype=np.complex128)
|
| 837 |
+
cp.cuda.runtime.memcpy(rank_slice_cupy.data.ptr, sub_sv_zeros.ctypes.data, sub_sv_zeros.nbytes, cp.cuda.runtime.memcpyHostToDevice)
|
| 838 |
+
if rank == 0 and num_nonzero_ranks < 1 and N_sub_per_rank > 0:
|
| 839 |
+
limit_idx = int(N_tot_state_vector / (2**num_anc))
|
| 840 |
+
if limit_idx < rank_slice_cupy.size:
|
| 841 |
+
rank_slice_cupy[limit_idx:] = 0
|
| 842 |
+
return result
|
| 843 |
+
self.run_timestep = run_timestep_func
|
| 844 |
+
print("Circuit created")
|
| 845 |
+
def write_state(self, state_to_write, t_step_str_val):
|
| 846 |
+
rank_slice_cupy = to_cupy_array(state_to_write)
|
| 847 |
+
num_nonzero_ranks = num_ranks / (2**num_anc)
|
| 848 |
+
if rank < num_nonzero_ranks or (rank == 0 and num_nonzero_ranks <= 0):
|
| 849 |
+
save_path = intermediate_folder_path / f"{t_step_str_val}_{rank}.npy"
|
| 850 |
+
with open(save_path, 'wb') as f:
|
| 851 |
+
arr_to_save = None
|
| 852 |
+
data_limit = N_sub_per_rank
|
| 853 |
+
if num_nonzero_ranks < 1 and rank == 0:
|
| 854 |
+
data_limit = int(N_tot_state_vector / (2**num_anc))
|
| 855 |
+
if data_limit > 0:
|
| 856 |
+
relevant_part_cupy = cp.real(rank_slice_cupy[:data_limit])
|
| 857 |
+
else:
|
| 858 |
+
relevant_part_cupy = cp.array([], dtype=cp.float64)
|
| 859 |
+
if relevant_part_cupy.size >= current_N * current_N * current_N:
|
| 860 |
+
arr_flat = relevant_part_cupy[:current_N * current_N * current_N]
|
| 861 |
+
if downsampling_factor > 1 and current_N > 0:
|
| 862 |
+
arr_reshaped = arr_flat.reshape((current_N, current_N, current_N))
|
| 863 |
+
arr_downsampled = arr_reshaped[::downsampling_factor, ::downsampling_factor, ::downsampling_factor]
|
| 864 |
+
arr_to_save = arr_downsampled.flatten()
|
| 865 |
+
else:
|
| 866 |
+
arr_to_save = arr_flat
|
| 867 |
+
elif relevant_part_cupy.size > 0:
|
| 868 |
+
if downsampling_factor > 1:
|
| 869 |
+
arr_to_save = relevant_part_cupy[::downsampling_factor]
|
| 870 |
+
else:
|
| 871 |
+
arr_to_save = relevant_part_cupy
|
| 872 |
+
if arr_to_save is not None and arr_to_save.size > 0:
|
| 873 |
+
np.save(f, arr_to_save.get() if isinstance(arr_to_save, cp.ndarray) else arr_to_save)
|
| 874 |
+
print("Write state defined")
|
| 875 |
+
def run_evolution(self, initial_state_arg, total_timesteps, time_steps_to_save, observable=False):
|
| 876 |
+
current_state_val = initial_state_arg
|
| 877 |
+
save_times = set(time_steps_to_save)
|
| 878 |
+
if 0 in save_times:
|
| 879 |
+
print("Writing first state")
|
| 880 |
+
self.write_state(current_state_val, '0')
|
| 881 |
+
for t_iter in range(total_timesteps):
|
| 882 |
+
print("Running timestep")
|
| 883 |
+
next_state_val = self.run_timestep(current_state_val)
|
| 884 |
+
if (t_iter + 1) in save_times:
|
| 885 |
+
print("Writing next state")
|
| 886 |
+
self.write_state(next_state_val, str(t_iter + 1))
|
| 887 |
+
cp.get_default_memory_pool().free_all_blocks()
|
| 888 |
+
current_state_val = next_state_val
|
| 889 |
+
if rank == 0:
|
| 890 |
+
print(f"Timestep: {total_timesteps}/{total_timesteps} (Evolution complete)")
|
| 891 |
+
cp.get_default_memory_pool().free_all_blocks()
|
| 892 |
+
self.final_state = current_state_val
|
| 893 |
+
|
| 894 |
+
if boundary_condition == "Periodic":
|
| 895 |
+
pass
|
| 896 |
+
elif boundary_condition == "Dirichlet":
|
| 897 |
+
pass
|
| 898 |
+
elif boundary_condition == "Neumann":
|
| 899 |
+
pass
|
| 900 |
+
|
| 901 |
+
downsampling_factor = 1
|
| 902 |
+
if current_N == 0:
|
| 903 |
+
print("Error: current_N is zero. num_reg_qubits likely too small.")
|
| 904 |
+
return None, None # Modified return
|
| 905 |
+
if current_N < downsampling_factor:
|
| 906 |
+
downsampling_factor = current_N if current_N > 0 else 1
|
| 907 |
+
|
| 908 |
+
qlbm_obj = QLBMAdvecDiffD3Q7_new(vx=vx_input, vy=vy_input, vz=vz_input)
|
| 909 |
+
initial_state_val = cudaq.get_state(alloc_kernel, num_qubits_total)
|
| 910 |
+
|
| 911 |
+
sub_sv_init_flat = initial_state_func_eval(np.arange(N_sub_per_rank)).astype(np.complex128)
|
| 912 |
+
|
| 913 |
+
norm = np.linalg.norm(sub_sv_init_flat)
|
| 914 |
+
if norm > 0:
|
| 915 |
+
sub_sv_init_flat /= norm
|
| 916 |
+
else:
|
| 917 |
+
print("Error: Initial state norm is zero.")
|
| 918 |
+
return None, None # Modified return
|
| 919 |
+
full_initial_sv_host = np.zeros(N_sub_per_rank, dtype=np.complex128)
|
| 920 |
+
num_computational_states = current_N ** 3
|
| 921 |
+
if len(sub_sv_init_flat) == num_computational_states:
|
| 922 |
+
if num_computational_states <= N_sub_per_rank:
|
| 923 |
+
full_initial_sv_host[:num_computational_states] = sub_sv_init_flat
|
| 924 |
+
else:
|
| 925 |
+
print(f"Error: Grid data {num_computational_states} > N_sub_per_rank {N_sub_per_rank}")
|
| 926 |
+
return None, None # Modified return
|
| 927 |
+
else:
|
| 928 |
+
print(f"Warning: Initial state size {len(sub_sv_init_flat)} != expected {num_computational_states}")
|
| 929 |
+
fill_len = min(len(sub_sv_init_flat), num_computational_states, N_sub_per_rank)
|
| 930 |
+
full_initial_sv_host[:fill_len] = sub_sv_init_flat[:fill_len]
|
| 931 |
+
|
| 932 |
+
rank_slice_init = to_cupy_array(initial_state_val)
|
| 933 |
+
print(f'Rank {rank}: Initializing state with {distribution_type} (vx={vx_input}, vy={vy_input})...')
|
| 934 |
+
cp.cuda.runtime.memcpy(rank_slice_init.data.ptr, full_initial_sv_host.ctypes.data, full_initial_sv_host.nbytes, cp.cuda.runtime.memcpyHostToDevice)
|
| 935 |
+
print(f'Rank {rank}: Initial state copied. Size: {len(sub_sv_init_flat)}. N_sub_per_rank: {N_sub_per_rank}')
|
| 936 |
+
|
| 937 |
+
print("Starting QLBM evolution...")
|
| 938 |
+
qlbm_obj.run_evolution(initial_state_val, T, time_steps)
|
| 939 |
+
print("QLBM evolution complete.")
|
| 940 |
+
|
| 941 |
+
print("Generating interactive plot with Plotly...")
|
| 942 |
+
downsampled_N = current_N // downsampling_factor
|
| 943 |
+
if downsampled_N == 0 and current_N > 0:
|
| 944 |
+
downsampled_N = 1
|
| 945 |
+
elif current_N == 0:
|
| 946 |
+
print("Error: current_N is zero before Plotly stage.")
|
| 947 |
+
return None, None # Modified return
|
| 948 |
+
|
| 949 |
+
data_frames = []
|
| 950 |
+
actual_timesteps = []
|
| 951 |
+
for t in time_steps:
|
| 952 |
+
file_path = intermediate_folder_path / f"{t}_{rank}.npy"
|
| 953 |
+
if file_path.exists():
|
| 954 |
+
sol_loaded = np.load(file_path)
|
| 955 |
+
if sol_loaded.size == downsampled_N * downsampled_N* downsampled_N:
|
| 956 |
+
data = np.reshape(sol_loaded, (downsampled_N, downsampled_N, downsampled_N))
|
| 957 |
+
data_frames.append(data)
|
| 958 |
+
actual_timesteps.append(t)
|
| 959 |
+
print(f"Time {t}: Min={np.min(data)}, Max={np.max(data)}, Mean={np.mean(data)}")
|
| 960 |
+
else:
|
| 961 |
+
print(f"Warning: File {file_path} size {sol_loaded.size} != expected {downsampled_N*downsampled_N*downsampled_N}. Skipping.")
|
| 962 |
+
else:
|
| 963 |
+
print(f"Warning: File {file_path} not found. Skipping.")
|
| 964 |
+
|
| 965 |
+
if not data_frames:
|
| 966 |
+
print("Error: No data frames loaded for plotting.")
|
| 967 |
+
return None, None # Modified return
|
| 968 |
+
|
| 969 |
+
x_coords_plot = np.linspace(0, 1, downsampled_N)
|
| 970 |
+
y_coords_plot = np.linspace(0, 1, downsampled_N)
|
| 971 |
+
z_coords_plot = np.linspace(0, 1, downsampled_N)
|
| 972 |
+
Z_grid_mesh, Y_grid_mesh, X_grid_mesh = np.meshgrid(x_coords_plot, y_coords_plot, z_coords_plot, indexing='ij')
|
| 973 |
+
|
| 974 |
+
data_min = min([np.min(data) for data in data_frames])
|
| 975 |
+
data_max = max([np.max(data) for data in data_frames])
|
| 976 |
+
if data_max == data_min:
|
| 977 |
+
data_max += 1e-9
|
| 978 |
+
|
| 979 |
+
fig = go.Figure()
|
| 980 |
+
|
| 981 |
+
# Store individual frames for download
|
| 982 |
+
plotly_json_frames = []
|
| 983 |
+
|
| 984 |
+
for i, output_data in enumerate(data_frames):
|
| 985 |
+
frame_trace = go.Isosurface(
|
| 986 |
+
x=X_grid_mesh.flatten(),
|
| 987 |
+
y=Y_grid_mesh.flatten(),
|
| 988 |
+
z=Z_grid_mesh.flatten(),
|
| 989 |
+
value=output_data.flatten(),
|
| 990 |
+
isomin=data_min,
|
| 991 |
+
isomax=data_max,
|
| 992 |
+
opacity=0.4, # needs to be small to see through all surfaces
|
| 993 |
+
surface_count=7, # needs to be a large number for good volume rendering,
|
| 994 |
+
caps=dict(x_show=False, y_show=False, z_show=False)
|
| 995 |
+
)
|
| 996 |
+
fig.add_trace(frame_trace)
|
| 997 |
+
|
| 998 |
+
# Create a figure for the individual frame and convert to JSON
|
| 999 |
+
single_frame_fig = go.Figure(data=[frame_trace], layout=fig.layout)
|
| 1000 |
+
single_frame_fig.update_layout(
|
| 1001 |
+
title=f"Time: {actual_timesteps[i]}",
|
| 1002 |
+
scene=dict(
|
| 1003 |
+
xaxis_title='X',
|
| 1004 |
+
yaxis_title='Y',
|
| 1005 |
+
zaxis_title='Z',
|
| 1006 |
+
xaxis=dict(range=[x_coords_plot[0], x_coords_plot[-1]]),
|
| 1007 |
+
yaxis=dict(range=[y_coords_plot[0], y_coords_plot[-1]]),
|
| 1008 |
+
zaxis=dict(range=[z_coords_plot[0], z_coords_plot[-1]]),
|
| 1009 |
+
)
|
| 1010 |
+
)
|
| 1011 |
+
plotly_json_frames.append(single_frame_fig.to_json())
|
| 1012 |
+
|
| 1013 |
+
for trace in fig.data[1:]:
|
| 1014 |
+
trace.visible = False
|
| 1015 |
+
|
| 1016 |
+
steps = []
|
| 1017 |
+
for i in range(len(data_frames)):
|
| 1018 |
+
step = dict(
|
| 1019 |
+
method="update",
|
| 1020 |
+
args=[{"visible": [False] * len(data_frames)}],
|
| 1021 |
+
label=f"Time: {actual_timesteps[i]}"
|
| 1022 |
+
)
|
| 1023 |
+
step["args"][0]["visible"][i] = True
|
| 1024 |
+
steps.append(step)
|
| 1025 |
+
|
| 1026 |
+
sliders = [dict(active=0, currentvalue={"prefix": "Time: "}, pad={"t": 50}, steps=steps)]
|
| 1027 |
+
|
| 1028 |
+
fig.update_layout(
|
| 1029 |
+
title='', # Removed graph title
|
| 1030 |
+
scene=dict(
|
| 1031 |
+
xaxis_title='X',
|
| 1032 |
+
yaxis_title='Y',
|
| 1033 |
+
zaxis_title='Z',
|
| 1034 |
+
xaxis=dict(range=[x_coords_plot[0], x_coords_plot[-1]]),
|
| 1035 |
+
yaxis=dict(range=[y_coords_plot[0], y_coords_plot[-1]]),
|
| 1036 |
+
zaxis=dict(range=[z_coords_plot[0], z_coords_plot[-1]]),
|
| 1037 |
+
),
|
| 1038 |
+
sliders=sliders,
|
| 1039 |
+
width=800,
|
| 1040 |
+
height=700
|
| 1041 |
+
)
|
| 1042 |
+
|
| 1043 |
+
return fig, plotly_json_frames # Modified return
|
fluid3d_pyvista.py
ADDED
|
@@ -0,0 +1,638 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import math
|
| 3 |
+
import tempfile
|
| 4 |
+
import cudaq
|
| 5 |
+
import numpy as np
|
| 6 |
+
import cupy as cp
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
import plotly.graph_objects as go
|
| 9 |
+
import plotly.io as pio
|
| 10 |
+
|
| 11 |
+
# Set Plotly engine for image export
|
| 12 |
+
try:
|
| 13 |
+
pio.kaleido.scope.mathjax = None
|
| 14 |
+
except AttributeError:
|
| 15 |
+
pass
|
| 16 |
+
|
| 17 |
+
# Existing functions (bin_to_gray, gray_to_bin, etc.) remain unchanged
|
| 18 |
+
def bin_to_gray(bin_s):
|
| 19 |
+
XOR=lambda x,y: (x or y) and not (x and y)
|
| 20 |
+
gray_s=bin_s[0]
|
| 21 |
+
for i in range(len(bin_s)-1):
|
| 22 |
+
c_bool=XOR(bool(int(bin_s[i])),bool(int(bin_s[i+1])))
|
| 23 |
+
gray_s+=str(int(c_bool))
|
| 24 |
+
return gray_s
|
| 25 |
+
|
| 26 |
+
def gray_to_bin(gray_s):
|
| 27 |
+
XOR=lambda x,y: (x or y) and not (x and y)
|
| 28 |
+
bin_s=gray_s[0]
|
| 29 |
+
for i in range(len(gray_s)-1):
|
| 30 |
+
c_bool=XOR(bool(int(bin_s[i])),bool(int(gray_s[i+1])))
|
| 31 |
+
bin_s+=str(int(c_bool))
|
| 32 |
+
return bin_s
|
| 33 |
+
|
| 34 |
+
def bin_to_int(bin_s):
|
| 35 |
+
return int(bin_s,2)
|
| 36 |
+
|
| 37 |
+
def int_to_bin(i,pad):
|
| 38 |
+
return bin(i)[2:].zfill(pad)
|
| 39 |
+
|
| 40 |
+
def fwht_approx(f,N,num_points_per_dim,threshold=1e-10):
|
| 41 |
+
linear_block_size=int(N//num_points_per_dim)
|
| 42 |
+
num_angles_per_block=int(np.log2(linear_block_size))
|
| 43 |
+
|
| 44 |
+
thetas={}
|
| 45 |
+
|
| 46 |
+
for k in range(num_points_per_dim):
|
| 47 |
+
for j in range(num_points_per_dim):
|
| 48 |
+
for i in range(num_points_per_dim):
|
| 49 |
+
|
| 50 |
+
avg_f=2*np.arccos(f(i*linear_block_size+(linear_block_size-1)/2,j*linear_block_size+(linear_block_size-1)/2,k*linear_block_size+(linear_block_size-1)/2))
|
| 51 |
+
thetas[k*(N**2)*linear_block_size+j*N*linear_block_size+i*linear_block_size]=avg_f
|
| 52 |
+
|
| 53 |
+
slope_x=(2*np.arccos(f(i*linear_block_size,j*linear_block_size+(linear_block_size-1)/2,k*linear_block_size+(linear_block_size-1)/2))-2*np.arccos(f(((i+1)%N)*linear_block_size,j*linear_block_size+(linear_block_size-1)/2,k*linear_block_size+(linear_block_size-1)/2)))/linear_block_size
|
| 54 |
+
slope_y=(2*np.arccos(f(i*linear_block_size+(linear_block_size-1)/2,j*linear_block_size,k*linear_block_size+(linear_block_size-1)/2))-2*np.arccos(f(i*linear_block_size+(linear_block_size-1)/2,((j+1)%N)*linear_block_size,k*linear_block_size+(linear_block_size-1)/2)))/linear_block_size
|
| 55 |
+
slope_z=(2*np.arccos(f(i*linear_block_size+(linear_block_size-1)/2,j*linear_block_size+(linear_block_size-1)/2,k*linear_block_size))-2*np.arccos(f(i*linear_block_size+(linear_block_size-1)/2,j*linear_block_size+(linear_block_size-1)/2,((k+1)%N)*linear_block_size)))/linear_block_size
|
| 56 |
+
|
| 57 |
+
for m in range(num_angles_per_block):
|
| 58 |
+
thetas[k*(N**2)*linear_block_size+j*N*linear_block_size+i*linear_block_size + 2**m]=slope_x*(2**(m-1))
|
| 59 |
+
thetas[k*(N**2)*linear_block_size+j*N*linear_block_size+i*linear_block_size + N*(2**m)]=slope_y*(2**(m-1))
|
| 60 |
+
thetas[k*(N**2)*linear_block_size+j*N*linear_block_size+i*linear_block_size + (N**2)*(2**m)]=slope_z*(2**(m-1))
|
| 61 |
+
|
| 62 |
+
h = linear_block_size
|
| 63 |
+
while h < N**3:
|
| 64 |
+
for i in range(0, N**3, h * 2):
|
| 65 |
+
if (i//N)%linear_block_size!=0:
|
| 66 |
+
continue
|
| 67 |
+
if (i//(N**2))%linear_block_size!=0:
|
| 68 |
+
continue
|
| 69 |
+
j=i
|
| 70 |
+
while j<i+h:
|
| 71 |
+
index=j
|
| 72 |
+
x = thetas[index]
|
| 73 |
+
y = thetas[index + h]
|
| 74 |
+
thetas[index] = (x + y)/2
|
| 75 |
+
thetas[index + h] = (x - y)/2
|
| 76 |
+
|
| 77 |
+
for ax in range(3):
|
| 78 |
+
for m in range(num_angles_per_block):
|
| 79 |
+
index = j + (N**ax) * (2**m)
|
| 80 |
+
x = thetas[index]
|
| 81 |
+
y = thetas[index + h]
|
| 82 |
+
thetas[index] = (x + y)/2
|
| 83 |
+
thetas[index + h] = (x - y)/2
|
| 84 |
+
|
| 85 |
+
j+=linear_block_size
|
| 86 |
+
if (j//N)%linear_block_size==1:
|
| 87 |
+
j+=(linear_block_size-1)*N
|
| 88 |
+
if (j//(N**2))%linear_block_size==1:
|
| 89 |
+
j+=(linear_block_size-1)*(N**2)
|
| 90 |
+
|
| 91 |
+
h *= 2
|
| 92 |
+
if h==N:
|
| 93 |
+
h=N*linear_block_size
|
| 94 |
+
if h==N**2:
|
| 95 |
+
h=(N**2)*linear_block_size
|
| 96 |
+
|
| 97 |
+
return [theta for theta in thetas.values() if abs(theta)>threshold],[key for key in thetas.keys() if abs(thetas[key])>threshold]
|
| 98 |
+
|
| 99 |
+
def get_circuit_inputs(f,num_reg_qubits,num_points_per_dim):
|
| 100 |
+
theta_vec,indices=fwht_approx(f,2**num_reg_qubits,num_points_per_dim)
|
| 101 |
+
circ_pos=[]
|
| 102 |
+
for ind in indices:
|
| 103 |
+
circ_pos+=[bin_to_int(gray_to_bin(int_to_bin(ind,num_reg_qubits*3)))]
|
| 104 |
+
|
| 105 |
+
sorted_theta_vec=sorted(zip(theta_vec,circ_pos),key=lambda el:el[1])
|
| 106 |
+
ctrls=[]
|
| 107 |
+
|
| 108 |
+
current_bs="0"*(3*num_reg_qubits)
|
| 109 |
+
for el in sorted_theta_vec:
|
| 110 |
+
new_bs=bin_to_gray(int_to_bin((el[1])%(2**(3*num_reg_qubits)),(3*num_reg_qubits)))
|
| 111 |
+
ctrls += [[i for i, (char1, char2) in enumerate(zip(current_bs, new_bs)) if char1 != char2]]
|
| 112 |
+
current_bs=new_bs
|
| 113 |
+
new_bs="0"*(3*num_reg_qubits)
|
| 114 |
+
ctrls += [[i for i, (char1, char2) in enumerate(zip(current_bs, new_bs)) if char1 != char2]]
|
| 115 |
+
|
| 116 |
+
ctrls_flat_list=[]
|
| 117 |
+
for ctrl_list in ctrls:
|
| 118 |
+
ctrls_flat_list+=[len(ctrl_list)]+ctrl_list
|
| 119 |
+
|
| 120 |
+
return [el[0] for el in sorted_theta_vec]+[0.0],ctrls_flat_list
|
| 121 |
+
|
| 122 |
+
def simulate_qlbm_3D_and_animate(num_reg_qubits: int, T: int, distribution_type: str, vx_input, vy_input, vz_input, boundary_condition: str):
|
| 123 |
+
num_anc = 3
|
| 124 |
+
num_qubits_total = 3 * num_reg_qubits + num_anc
|
| 125 |
+
current_N = 2**num_reg_qubits
|
| 126 |
+
N_tot_state_vector = 2**num_qubits_total
|
| 127 |
+
num_ranks = 1
|
| 128 |
+
rank = 0
|
| 129 |
+
N_sub_per_rank = int(N_tot_state_vector // num_ranks)
|
| 130 |
+
|
| 131 |
+
# Simplified time steps for 3D since slider steps are removed
|
| 132 |
+
NUM_ANIMATION_FRAMES_3D = 10 # Default number of frames if no specific slider steps
|
| 133 |
+
|
| 134 |
+
if T == 0:
|
| 135 |
+
time_steps = [0]
|
| 136 |
+
else:
|
| 137 |
+
num_points = min(T + 1, NUM_ANIMATION_FRAMES_3D)
|
| 138 |
+
time_steps = np.linspace(start=0, stop=T, num=num_points, dtype=int)
|
| 139 |
+
time_steps = sorted(list(set(time_steps)))
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
if distribution_type == "Sinusoidal":
|
| 143 |
+
selected_initial_state_function_raw = lambda x, y, z, N_val_func: \
|
| 144 |
+
np.sin(x * 2 * np.pi / N_val_func) * \
|
| 145 |
+
np.sin(y * 2 * np.pi / N_val_func) * \
|
| 146 |
+
np.sin(z * 2 * np.pi / N_val_func) + 1
|
| 147 |
+
elif distribution_type == "Gaussian":
|
| 148 |
+
selected_initial_state_function_raw = lambda x, y, z, N_val_func: \
|
| 149 |
+
np.exp(-((x - N_val_func / 2)**2 / (2 * (N_val_func / 5)**2) + (y - N_val_func / 2)**2 / (2 * (N_val_func / 5)**2) +
|
| 150 |
+
(z - N_val_func / 2)**2 / (2 * (N_val_func / 5)**2))) * 1.8 + 0.2
|
| 151 |
+
else:
|
| 152 |
+
print(f"Warning: Unknown distribution type '{distribution_type}'. Defaulting to Sinusoidal.")
|
| 153 |
+
selected_initial_state_function_raw = lambda x, y, z, N_val_func: \
|
| 154 |
+
np.sin(x * 2 * np.pi / N_val_func) * \
|
| 155 |
+
np.sin(y * 2 * np.pi / N_val_func) * \
|
| 156 |
+
np.sin(z * 2 * np.pi / N_val_func) + 1
|
| 157 |
+
|
| 158 |
+
initial_state_func_eval = lambda i:\
|
| 159 |
+
selected_initial_state_function_raw(i%current_N,(i//current_N)%current_N,i//(current_N**2),current_N)*(i<(current_N**3)).astype(int)
|
| 160 |
+
|
| 161 |
+
with tempfile.TemporaryDirectory() as tmp_npy_dir:
|
| 162 |
+
intermediate_folder_path = Path(tmp_npy_dir)
|
| 163 |
+
|
| 164 |
+
cudaq.set_target('nvidia', option='fp64')
|
| 165 |
+
|
| 166 |
+
@cudaq.kernel
|
| 167 |
+
def alloc_kernel(num_qubits_alloc: int):
|
| 168 |
+
qubits = cudaq.qvector(num_qubits_alloc)
|
| 169 |
+
|
| 170 |
+
from cupy.cuda.memory import MemoryPointer, UnownedMemory
|
| 171 |
+
|
| 172 |
+
def to_cupy_array(state):
|
| 173 |
+
tensor = state.getTensor()
|
| 174 |
+
pDevice = tensor.data()
|
| 175 |
+
sizeByte = tensor.get_num_elements() * tensor.get_element_size()
|
| 176 |
+
mem = UnownedMemory(pDevice, sizeByte, owner=state)
|
| 177 |
+
memptr_obj = MemoryPointer(mem, 0)
|
| 178 |
+
cupy_array_val = cp.ndarray(tensor.get_num_elements(),
|
| 179 |
+
dtype=cp.complex128,
|
| 180 |
+
memptr=memptr_obj)
|
| 181 |
+
return cupy_array_val
|
| 182 |
+
|
| 183 |
+
class QLBMAdvecDiffD3Q7_new:
|
| 184 |
+
def __init__(self,vx,vy,vz) -> None:
|
| 185 |
+
self.dim = 3
|
| 186 |
+
self.ndir = 7
|
| 187 |
+
self.nq_dir = math.ceil(np.log2(self.ndir))
|
| 188 |
+
self.dirs=[]
|
| 189 |
+
for dir_int in range(self.ndir):
|
| 190 |
+
if dir_int==4:
|
| 191 |
+
dir_bin="111"
|
| 192 |
+
else:
|
| 193 |
+
dir_bin = f"{dir_int:b}".zfill(self.nq_dir)
|
| 194 |
+
self.dirs.append(dir_bin)
|
| 195 |
+
self.cs = 1/np.sqrt(3)
|
| 196 |
+
self.ux = lambda x,y,z: vx(x,y,z)/self.cs**2
|
| 197 |
+
self.uy = lambda x,y,z: vy(x,y,z)/self.cs**2
|
| 198 |
+
self.uz = lambda x,y,z: vz(x,y,z)/self.cs**2
|
| 199 |
+
self.create_circuit()
|
| 200 |
+
|
| 201 |
+
def create_circuit(self):
|
| 202 |
+
print("Creating circuit")
|
| 203 |
+
x_coeffs,x_coeff_var_indices=get_circuit_inputs(lambda x,y,z: ((1+self.ux(x/current_N,y/current_N,z/current_N))/2)**0.5,num_reg_qubits,min(current_N,32))
|
| 204 |
+
y_coeffs,y_coeff_var_indices=get_circuit_inputs(lambda x,y,z: ((1+self.uy(x/current_N,y/current_N,z/current_N))/2)**0.5,num_reg_qubits,min(current_N,32))
|
| 205 |
+
z_coeffs,z_coeff_var_indices=get_circuit_inputs(lambda x,y,z: ((1+self.uz(x/current_N,y/current_N,z/current_N))/2)**0.5,num_reg_qubits,min(current_N,32))
|
| 206 |
+
x_coeffs_,x_coeff_var_indices_=get_circuit_inputs(lambda x,y,z: 0 if (1+self.ux((x-1)/current_N,y/current_N,z/current_N))==0 else \
|
| 207 |
+
((1+self.ux((x-1)/current_N,y/current_N,z/current_N))/(2+self.ux((x-1)/current_N,y/current_N,z/current_N)-self.ux((x+1)/current_N,y/current_N,z/current_N)))**0.5,num_reg_qubits,min(current_N,32))
|
| 208 |
+
y_coeffs_,y_coeff_var_indices_=get_circuit_inputs(lambda x,y,z: 0 if (1+self.uy(x/current_N,(y-1)/current_N,z/current_N))==0 else \
|
| 209 |
+
((1+self.uy(x/current_N,(y-1)/current_N,z/current_N))/(2+self.uy(x/current_N,(y-1)/current_N,z/current_N)-self.uy(x/current_N,(y+1)/current_N,z/current_N)))**0.5,num_reg_qubits,min(current_N,32))
|
| 210 |
+
z_coeffs_,z_coeff_var_indices_=get_circuit_inputs(lambda x,y,z: 0 if (1+self.uz(x/current_N,y/current_N,(z-1)/current_N))==0 else \
|
| 211 |
+
((1+self.uz(x/current_N,y/current_N,(z-1)/current_N))/(2+self.uz(x/current_N,y/current_N,(z-1)/current_N)-self.uz(x/current_N,y/current_N,(z+1)/current_N)))**0.5,num_reg_qubits,min(current_N,32))
|
| 212 |
+
unprep1_coeffs,unprep1_coeff_var_indices=get_circuit_inputs(lambda x,y,z:\
|
| 213 |
+
(1/3**0.5)*(1+(self.ux((x-1)/current_N,y/current_N,z/current_N)-self.ux((x+1)/current_N,y/current_N,z/current_N))/2)**0.5,num_reg_qubits,min(current_N,32))
|
| 214 |
+
unprep2_coeffs, unprep2_coeff_var_indices = get_circuit_inputs(lambda x, y, z: ((1 + (self.uy(x/current_N, (y-1)/current_N, z/current_N) - self.uy(x/current_N, (y+1)/current_N, z/current_N))/2) /(2 - (self.ux((x-1)/current_N, y/current_N, z/current_N) - self.ux((x+1)/current_N, y/current_N, z/current_N))/2))**0.5, num_reg_qubits, min(current_N, 32))
|
| 215 |
+
print("Generated angles")
|
| 216 |
+
v=np.pad([1/4, 1/4, 0, 1/4, 0, 1/4, 0],(0,2**num_anc - self.ndir))
|
| 217 |
+
v=v**0.5
|
| 218 |
+
v[0]+=1
|
| 219 |
+
v=v/np.linalg.norm(v)
|
| 220 |
+
U_prep=2*np.outer(v,v)-np.eye(len(v))
|
| 221 |
+
cudaq.register_operation("prep_op", U_prep)
|
| 222 |
+
def collisionOp(dirs):
|
| 223 |
+
dirs_i_list=[]
|
| 224 |
+
for dir_ in dirs:
|
| 225 |
+
dirs_i=[(int(c)) for c in dir_]
|
| 226 |
+
dirs_i_list+=dirs_i[::-1]
|
| 227 |
+
return dirs_i_list
|
| 228 |
+
self.dirs_i_list=collisionOp(self.dirs)
|
| 229 |
+
print("Generated dirs_i_list")
|
| 230 |
+
@cudaq.kernel
|
| 231 |
+
def rshift(q: cudaq.qview, n: int):
|
| 232 |
+
for i in range(n):
|
| 233 |
+
if i == n-1:
|
| 234 |
+
x(q[n-1-i])
|
| 235 |
+
elif i == n-2:
|
| 236 |
+
x.ctrl(q[n-1-(i+1)], q[n-1-i])
|
| 237 |
+
else:
|
| 238 |
+
x.ctrl(q[0:n-1-i], q[n-1-i])
|
| 239 |
+
@cudaq.kernel
|
| 240 |
+
def lshift(q: cudaq.qview, n: int):
|
| 241 |
+
for i in range(n):
|
| 242 |
+
if i == 0:
|
| 243 |
+
x(q[0])
|
| 244 |
+
elif i == 1:
|
| 245 |
+
x.ctrl(q[0], q[1])
|
| 246 |
+
else:
|
| 247 |
+
x.ctrl(q[0:i], q[i])
|
| 248 |
+
@cudaq.kernel
|
| 249 |
+
def d2q5_tstep(q: cudaq.qview, nqx: int, nqy: int, nqz: int, nq_dir: int, dirs_i: list[int]):
|
| 250 |
+
qx=q[0:nqx]
|
| 251 |
+
qy=q[nqx:nqx+nqy]
|
| 252 |
+
qz=q[nqx+nqy:nqx+nqy+nqz]
|
| 253 |
+
qdir=q[nqx+nqy+nqz:nqx+nqy+nqz+nq_dir]
|
| 254 |
+
i=2
|
| 255 |
+
b_list=dirs_i[i*nq_dir:(i+1)*nq_dir]
|
| 256 |
+
for j in range(nq_dir):
|
| 257 |
+
b=b_list[j]
|
| 258 |
+
if b==0:
|
| 259 |
+
x(qdir[j])
|
| 260 |
+
cudaq.control(lshift,qdir,qx,nqx)
|
| 261 |
+
for j in range(nq_dir):
|
| 262 |
+
b=b_list[j]
|
| 263 |
+
if b==0:
|
| 264 |
+
x(qdir[j])
|
| 265 |
+
i=1
|
| 266 |
+
b_list=dirs_i[i*nq_dir:(i+1)*nq_dir]
|
| 267 |
+
for j in range(nq_dir):
|
| 268 |
+
b=b_list[j]
|
| 269 |
+
if b==0:
|
| 270 |
+
x(qdir[j])
|
| 271 |
+
cudaq.control(rshift,qdir,qx,nqx)
|
| 272 |
+
for j in range(nq_dir):
|
| 273 |
+
b=b_list[j]
|
| 274 |
+
if b==0:
|
| 275 |
+
x(qdir[j])
|
| 276 |
+
i=4
|
| 277 |
+
b_list=dirs_i[i*nq_dir:(i+1)*nq_dir]
|
| 278 |
+
for j in range(nq_dir):
|
| 279 |
+
b=b_list[j]
|
| 280 |
+
if b==0:
|
| 281 |
+
x(qdir[j])
|
| 282 |
+
cudaq.control(lshift,qdir,qy,nqy)
|
| 283 |
+
for j in range(nq_dir):
|
| 284 |
+
b=b_list[j]
|
| 285 |
+
if b==0:
|
| 286 |
+
x(qdir[j])
|
| 287 |
+
i=3
|
| 288 |
+
b_list=dirs_i[i*nq_dir:(i+1)*nq_dir]
|
| 289 |
+
for j in range(nq_dir):
|
| 290 |
+
b=b_list[j]
|
| 291 |
+
if b==0:
|
| 292 |
+
x(qdir[j])
|
| 293 |
+
cudaq.control(rshift,qdir,qy,nqy)
|
| 294 |
+
for j in range(nq_dir):
|
| 295 |
+
b=b_list[j]
|
| 296 |
+
if b==0:
|
| 297 |
+
x(qdir[j])
|
| 298 |
+
i=6
|
| 299 |
+
b_list=dirs_i[i*nq_dir:(i+1)*nq_dir]
|
| 300 |
+
for j in range(nq_dir):
|
| 301 |
+
b=b_list[j]
|
| 302 |
+
if b==0:
|
| 303 |
+
x(qdir[j])
|
| 304 |
+
cudaq.control(lshift,qdir,qz,nqz)
|
| 305 |
+
for j in range(nq_dir):
|
| 306 |
+
b=b_list[j]
|
| 307 |
+
if b==0:
|
| 308 |
+
x(qdir[j])
|
| 309 |
+
i=5
|
| 310 |
+
b_list=dirs_i[i*nq_dir:(i+1)*nq_dir]
|
| 311 |
+
for j in range(nq_dir):
|
| 312 |
+
b=b_list[j]
|
| 313 |
+
if b==0:
|
| 314 |
+
x(qdir[j])
|
| 315 |
+
cudaq.control(rshift,qdir,qz,nqz)
|
| 316 |
+
for j in range(nq_dir):
|
| 317 |
+
b=b_list[j]
|
| 318 |
+
if b==0:
|
| 319 |
+
x(qdir[j])
|
| 320 |
+
@cudaq.kernel
|
| 321 |
+
def d2q5_tstep_wrapper(state: cudaq.State,nqx:int,nqy:int,nqz:int,nq_dir:int,dirs_i:list[int],\
|
| 322 |
+
x_coeff_var_indices:list[int],x_coeffs:list[float],\
|
| 323 |
+
y_coeff_var_indices:list[int],y_coeffs:list[float],\
|
| 324 |
+
z_coeff_var_indices:list[int],z_coeffs:list[float],\
|
| 325 |
+
x_coeff_var_indices_:list[int],x_coeffs_:list[float],\
|
| 326 |
+
y_coeff_var_indices_:list[int],y_coeffs_:list[float],\
|
| 327 |
+
z_coeff_var_indices_:list[int],z_coeffs_:list[float],\
|
| 328 |
+
unprep1_coeff_var_indices:list[int],unprep1_coeffs:list[float],\
|
| 329 |
+
unprep2_coeff_var_indices:list[int],unprep2_coeffs:list[float]):
|
| 330 |
+
q=cudaq.qvector(state)
|
| 331 |
+
qdir=q[nqx+nqy+nqz:nqx+nqy+nqz+nq_dir]
|
| 332 |
+
prep_op(qdir[2],qdir[1],qdir[0])
|
| 333 |
+
x.ctrl(qdir[0],qdir[1])
|
| 334 |
+
ind=0
|
| 335 |
+
coeff_ind=0
|
| 336 |
+
x(qdir[2])
|
| 337 |
+
while ind<len(x_coeff_var_indices):
|
| 338 |
+
tuple_length=x_coeff_var_indices[ind]
|
| 339 |
+
for sub_ind in range(ind+1, ind+1+tuple_length):
|
| 340 |
+
x.ctrl(q[nqx+nqy+nqz-1-x_coeff_var_indices[sub_ind]],qdir[0])
|
| 341 |
+
ry.ctrl(-x_coeffs[coeff_ind],[qdir[2],qdir[1]],qdir[0])
|
| 342 |
+
coeff_ind+=1
|
| 343 |
+
ind+=(1+tuple_length)
|
| 344 |
+
x(qdir[2])
|
| 345 |
+
ind=0
|
| 346 |
+
coeff_ind=0
|
| 347 |
+
while ind<len(z_coeff_var_indices):
|
| 348 |
+
tuple_length=z_coeff_var_indices[ind]
|
| 349 |
+
for sub_ind in range(ind+1,ind+1+tuple_length):
|
| 350 |
+
x.ctrl(q[nqx+nqy+nqz-1-z_coeff_var_indices[sub_ind]],qdir[0])
|
| 351 |
+
ry.ctrl(-z_coeffs[coeff_ind],[qdir[2],qdir[1]],qdir[0])
|
| 352 |
+
coeff_ind+=1
|
| 353 |
+
ind+=(1+tuple_length)
|
| 354 |
+
x.ctrl(qdir[0],qdir[1])
|
| 355 |
+
ind=0
|
| 356 |
+
coeff_ind=0
|
| 357 |
+
while ind<len(y_coeff_var_indices):
|
| 358 |
+
tuple_length=y_coeff_var_indices[ind]
|
| 359 |
+
for sub_ind in range(ind+1,ind+1+tuple_length):
|
| 360 |
+
x.ctrl(q[nqx+nqy+nqz-1-y_coeff_var_indices[sub_ind]],qdir[2])
|
| 361 |
+
ry.ctrl(y_coeffs[coeff_ind],[qdir[0],qdir[1]],qdir[2])
|
| 362 |
+
coeff_ind+=1
|
| 363 |
+
ind+=(1+tuple_length)
|
| 364 |
+
d2q5_tstep(q,nqx,nqy,nqz,nq_dir,dirs_i)
|
| 365 |
+
ind=0
|
| 366 |
+
coeff_ind=0
|
| 367 |
+
while ind<len(y_coeff_var_indices_):
|
| 368 |
+
tuple_length=y_coeff_var_indices_[ind]
|
| 369 |
+
for sub_ind in range(ind+1,ind+1+tuple_length):
|
| 370 |
+
x.ctrl(q[nqx+nqy+nqz-1-y_coeff_var_indices_[sub_ind]],qdir[2])
|
| 371 |
+
ry.ctrl(-y_coeffs_[coeff_ind],[qdir[0],qdir[1]],qdir[2])
|
| 372 |
+
coeff_ind+=1
|
| 373 |
+
ind+=(1+tuple_length)
|
| 374 |
+
x.ctrl(qdir[0],qdir[1])
|
| 375 |
+
ind=0
|
| 376 |
+
coeff_ind=0
|
| 377 |
+
x(qdir[2])
|
| 378 |
+
while ind<len(x_coeff_var_indices_):
|
| 379 |
+
tuple_length=x_coeff_var_indices_[ind]
|
| 380 |
+
for sub_ind in range(ind+1,ind+1+tuple_length):
|
| 381 |
+
x.ctrl(q[nqx+nqy+nqz-1-x_coeff_var_indices_[sub_ind]],qdir[0])
|
| 382 |
+
ry.ctrl(x_coeffs_[coeff_ind],[qdir[1],qdir[2]],qdir[0])
|
| 383 |
+
coeff_ind+=1
|
| 384 |
+
ind+=(1+tuple_length)
|
| 385 |
+
x(qdir[2])
|
| 386 |
+
ind=0
|
| 387 |
+
coeff_ind=0
|
| 388 |
+
while ind<len(z_coeff_var_indices_):
|
| 389 |
+
tuple_length=z_coeff_var_indices_[ind]
|
| 390 |
+
for sub_ind in range(ind+1,ind+1+tuple_length):
|
| 391 |
+
x.ctrl(q[nqx+nqy+nqz-1-z_coeff_var_indices_[sub_ind]],qdir[0])
|
| 392 |
+
ry.ctrl(z_coeffs_[coeff_ind],[qdir[1],qdir[2]],qdir[0])
|
| 393 |
+
coeff_ind+=1
|
| 394 |
+
ind+=(1+tuple_length)
|
| 395 |
+
x.ctrl(qdir[0],qdir[1])
|
| 396 |
+
ind=0
|
| 397 |
+
coeff_ind=0
|
| 398 |
+
x.ctrl(qdir[1],qdir[2])
|
| 399 |
+
while ind<len(unprep2_coeff_var_indices):
|
| 400 |
+
tuple_length=unprep2_coeff_var_indices[ind]
|
| 401 |
+
for sub_ind in range(ind+1,ind+1+tuple_length):
|
| 402 |
+
x.ctrl(q[nqx+nqy+nqz-1-unprep2_coeff_var_indices[sub_ind]],qdir[1])
|
| 403 |
+
ry.ctrl(unprep2_coeffs[coeff_ind],qdir[2],qdir[1])
|
| 404 |
+
coeff_ind+=1
|
| 405 |
+
ind+=(1+tuple_length)
|
| 406 |
+
x.ctrl(qdir[1],qdir[2])
|
| 407 |
+
ind=0
|
| 408 |
+
coeff_ind=0
|
| 409 |
+
while ind<len(unprep1_coeff_var_indices):
|
| 410 |
+
tuple_length=unprep1_coeff_var_indices[ind]
|
| 411 |
+
for sub_ind in range(ind+1,ind+1+tuple_length):
|
| 412 |
+
x.ctrl(q[nqx+nqy+nqz-1-unprep1_coeff_var_indices[sub_ind]],qdir[1])
|
| 413 |
+
ry.ctrl(-unprep1_coeffs[coeff_ind],qdir[0],qdir[1])
|
| 414 |
+
coeff_ind+=1
|
| 415 |
+
ind+=(1+tuple_length)
|
| 416 |
+
ry(-2*np.pi/3,qdir[0])
|
| 417 |
+
print("Kernels defined")
|
| 418 |
+
def run_timestep_func(vec_arg, hadamard=False):
|
| 419 |
+
result=cudaq.get_state(d2q5_tstep_wrapper,vec_arg,num_reg_qubits,num_reg_qubits,num_reg_qubits,self.nq_dir,self.dirs_i_list,\
|
| 420 |
+
x_coeff_var_indices,x_coeffs,\
|
| 421 |
+
y_coeff_var_indices,y_coeffs,\
|
| 422 |
+
z_coeff_var_indices,z_coeffs,\
|
| 423 |
+
x_coeff_var_indices_,x_coeffs_,\
|
| 424 |
+
y_coeff_var_indices_,y_coeffs_,\
|
| 425 |
+
z_coeff_var_indices_,z_coeffs_,\
|
| 426 |
+
unprep1_coeff_var_indices,unprep1_coeffs,\
|
| 427 |
+
unprep2_coeff_var_indices,unprep2_coeffs)
|
| 428 |
+
num_nonzero_ranks = num_ranks / (2**num_anc)
|
| 429 |
+
rank_slice_cupy = to_cupy_array(result)
|
| 430 |
+
if rank >= num_nonzero_ranks and num_nonzero_ranks > 0:
|
| 431 |
+
sub_sv_zeros = np.zeros(N_sub_per_rank, dtype=np.complex128)
|
| 432 |
+
cp.cuda.runtime.memcpy(rank_slice_cupy.data.ptr, sub_sv_zeros.ctypes.data, sub_sv_zeros.nbytes, cp.cuda.runtime.memcpyHostToDevice)
|
| 433 |
+
if rank == 0 and num_nonzero_ranks < 1 and N_sub_per_rank > 0:
|
| 434 |
+
limit_idx = int(N_tot_state_vector / (2**num_anc))
|
| 435 |
+
if limit_idx < rank_slice_cupy.size:
|
| 436 |
+
rank_slice_cupy[limit_idx:] = 0
|
| 437 |
+
return result
|
| 438 |
+
self.run_timestep = run_timestep_func
|
| 439 |
+
print("Circuit created")
|
| 440 |
+
def write_state(self, state_to_write, t_step_str_val):
|
| 441 |
+
rank_slice_cupy = to_cupy_array(state_to_write)
|
| 442 |
+
num_nonzero_ranks = num_ranks / (2**num_anc)
|
| 443 |
+
if rank < num_nonzero_ranks or (rank == 0 and num_nonzero_ranks <= 0):
|
| 444 |
+
save_path = intermediate_folder_path / f"{t_step_str_val}_{rank}.npy"
|
| 445 |
+
with open(save_path, 'wb') as f:
|
| 446 |
+
arr_to_save = None
|
| 447 |
+
data_limit = N_sub_per_rank
|
| 448 |
+
if num_nonzero_ranks < 1 and rank == 0:
|
| 449 |
+
data_limit = int(N_tot_state_vector / (2**num_anc))
|
| 450 |
+
if data_limit > 0:
|
| 451 |
+
relevant_part_cupy = cp.real(rank_slice_cupy[:data_limit])
|
| 452 |
+
else:
|
| 453 |
+
relevant_part_cupy = cp.array([], dtype=cp.float64)
|
| 454 |
+
if relevant_part_cupy.size >= current_N * current_N * current_N:
|
| 455 |
+
arr_flat = relevant_part_cupy[:current_N * current_N * current_N]
|
| 456 |
+
if downsampling_factor > 1 and current_N > 0:
|
| 457 |
+
arr_reshaped = arr_flat.reshape((current_N, current_N, current_N))
|
| 458 |
+
arr_downsampled = arr_reshaped[::downsampling_factor, ::downsampling_factor, ::downsampling_factor]
|
| 459 |
+
arr_to_save = arr_downsampled.flatten()
|
| 460 |
+
else:
|
| 461 |
+
arr_to_save = arr_flat
|
| 462 |
+
elif relevant_part_cupy.size > 0:
|
| 463 |
+
if downsampling_factor > 1:
|
| 464 |
+
arr_to_save = relevant_part_cupy[::downsampling_factor]
|
| 465 |
+
else:
|
| 466 |
+
arr_to_save = relevant_part_cupy
|
| 467 |
+
if arr_to_save is not None and arr_to_save.size > 0:
|
| 468 |
+
np.save(f, arr_to_save.get() if isinstance(arr_to_save, cp.ndarray) else arr_to_save)
|
| 469 |
+
print("Write state defined")
|
| 470 |
+
def run_evolution(self, initial_state_arg, total_timesteps, time_steps_to_save, observable=False):
|
| 471 |
+
current_state_val = initial_state_arg
|
| 472 |
+
save_times = set(time_steps_to_save)
|
| 473 |
+
if 0 in save_times:
|
| 474 |
+
print("Writing first state")
|
| 475 |
+
self.write_state(current_state_val, '0')
|
| 476 |
+
for t_iter in range(total_timesteps):
|
| 477 |
+
print("Running timestep")
|
| 478 |
+
next_state_val = self.run_timestep(current_state_val)
|
| 479 |
+
if (t_iter + 1) in save_times:
|
| 480 |
+
print("Writing next state")
|
| 481 |
+
self.write_state(next_state_val, str(t_iter + 1))
|
| 482 |
+
cp.get_default_memory_pool().free_all_blocks()
|
| 483 |
+
current_state_val = next_state_val
|
| 484 |
+
if rank == 0:
|
| 485 |
+
print(f"Timestep: {total_timesteps}/{total_timesteps} (Evolution complete)")
|
| 486 |
+
cp.get_default_memory_pool().free_all_blocks()
|
| 487 |
+
self.final_state = current_state_val
|
| 488 |
+
|
| 489 |
+
if boundary_condition == "Periodic":
|
| 490 |
+
pass
|
| 491 |
+
elif boundary_condition == "Dirichlet":
|
| 492 |
+
pass
|
| 493 |
+
elif boundary_condition == "Neumann":
|
| 494 |
+
pass
|
| 495 |
+
|
| 496 |
+
downsampling_factor = 1
|
| 497 |
+
if current_N == 0:
|
| 498 |
+
print("Error: current_N is zero. num_reg_qubits likely too small.")
|
| 499 |
+
return None, None # Modified return
|
| 500 |
+
if current_N < downsampling_factor:
|
| 501 |
+
downsampling_factor = current_N if current_N > 0 else 1
|
| 502 |
+
|
| 503 |
+
qlbm_obj = QLBMAdvecDiffD3Q7_new(vx=vx_input, vy=vy_input, vz=vz_input)
|
| 504 |
+
initial_state_val = cudaq.get_state(alloc_kernel, num_qubits_total)
|
| 505 |
+
|
| 506 |
+
sub_sv_init_flat = initial_state_func_eval(np.arange(N_sub_per_rank)).astype(np.complex128)
|
| 507 |
+
|
| 508 |
+
norm = np.linalg.norm(sub_sv_init_flat)
|
| 509 |
+
if norm > 0:
|
| 510 |
+
sub_sv_init_flat /= norm
|
| 511 |
+
else:
|
| 512 |
+
print("Error: Initial state norm is zero.")
|
| 513 |
+
return None, None # Modified return
|
| 514 |
+
full_initial_sv_host = np.zeros(N_sub_per_rank, dtype=np.complex128)
|
| 515 |
+
num_computational_states = current_N ** 3
|
| 516 |
+
if len(sub_sv_init_flat) == num_computational_states:
|
| 517 |
+
if num_computational_states <= N_sub_per_rank:
|
| 518 |
+
full_initial_sv_host[:num_computational_states] = sub_sv_init_flat
|
| 519 |
+
else:
|
| 520 |
+
print(f"Error: Grid data {num_computational_states} > N_sub_per_rank {N_sub_per_rank}")
|
| 521 |
+
return None, None # Modified return
|
| 522 |
+
else:
|
| 523 |
+
print(f"Warning: Initial state size {len(sub_sv_init_flat)} != expected {num_computational_states}")
|
| 524 |
+
fill_len = min(len(sub_sv_init_flat), num_computational_states, N_sub_per_rank)
|
| 525 |
+
full_initial_sv_host[:fill_len] = sub_sv_init_flat[:fill_len]
|
| 526 |
+
|
| 527 |
+
rank_slice_init = to_cupy_array(initial_state_val)
|
| 528 |
+
print(f'Rank {rank}: Initializing state with {distribution_type} (vx={vx_input}, vy={vy_input})...')
|
| 529 |
+
cp.cuda.runtime.memcpy(rank_slice_init.data.ptr, full_initial_sv_host.ctypes.data, full_initial_sv_host.nbytes, cp.cuda.runtime.memcpyHostToDevice)
|
| 530 |
+
print(f'Rank {rank}: Initial state copied. Size: {len(sub_sv_init_flat)}. N_sub_per_rank: {N_sub_per_rank}')
|
| 531 |
+
|
| 532 |
+
print("Starting QLBM evolution...")
|
| 533 |
+
qlbm_obj.run_evolution(initial_state_val, T, time_steps)
|
| 534 |
+
print("QLBM evolution complete.")
|
| 535 |
+
|
| 536 |
+
print("Generating interactive plot with Plotly...")
|
| 537 |
+
downsampled_N = current_N // downsampling_factor
|
| 538 |
+
if downsampled_N == 0 and current_N > 0:
|
| 539 |
+
downsampled_N = 1
|
| 540 |
+
elif current_N == 0:
|
| 541 |
+
print("Error: current_N is zero before Plotly stage.")
|
| 542 |
+
return None, None # Modified return
|
| 543 |
+
|
| 544 |
+
data_frames = []
|
| 545 |
+
actual_timesteps = []
|
| 546 |
+
for t in time_steps:
|
| 547 |
+
file_path = intermediate_folder_path / f"{t}_{rank}.npy"
|
| 548 |
+
if file_path.exists():
|
| 549 |
+
sol_loaded = np.load(file_path)
|
| 550 |
+
if sol_loaded.size == downsampled_N * downsampled_N* downsampled_N:
|
| 551 |
+
data = np.reshape(sol_loaded, (downsampled_N, downsampled_N, downsampled_N))
|
| 552 |
+
data_frames.append(data)
|
| 553 |
+
actual_timesteps.append(t)
|
| 554 |
+
print(f"Time {t}: Min={np.min(data)}, Max={np.max(data)}, Mean={np.mean(data)}")
|
| 555 |
+
else:
|
| 556 |
+
print(f"Warning: File {file_path} size {sol_loaded.size} != expected {downsampled_N*downsampled_N*downsampled_N}. Skipping.")
|
| 557 |
+
else:
|
| 558 |
+
print(f"Warning: File {file_path} not found. Skipping.")
|
| 559 |
+
|
| 560 |
+
if not data_frames:
|
| 561 |
+
print("Error: No data frames loaded for plotting.")
|
| 562 |
+
return None, None # Modified return
|
| 563 |
+
|
| 564 |
+
x_coords_plot = np.linspace(0, 1, downsampled_N)
|
| 565 |
+
y_coords_plot = np.linspace(0, 1, downsampled_N)
|
| 566 |
+
z_coords_plot = np.linspace(0, 1, downsampled_N)
|
| 567 |
+
Z_grid_mesh, Y_grid_mesh, X_grid_mesh = np.meshgrid(x_coords_plot, y_coords_plot, z_coords_plot, indexing='ij')
|
| 568 |
+
|
| 569 |
+
data_min = min([np.min(data) for data in data_frames])
|
| 570 |
+
data_max = max([np.max(data) for data in data_frames])
|
| 571 |
+
if data_max == data_min:
|
| 572 |
+
data_max += 1e-9
|
| 573 |
+
|
| 574 |
+
fig = go.Figure()
|
| 575 |
+
|
| 576 |
+
# Store individual frames for download
|
| 577 |
+
plotly_json_frames = []
|
| 578 |
+
|
| 579 |
+
for i, output_data in enumerate(data_frames):
|
| 580 |
+
frame_trace = go.Isosurface(
|
| 581 |
+
x=X_grid_mesh.flatten(),
|
| 582 |
+
y=Y_grid_mesh.flatten(),
|
| 583 |
+
z=Z_grid_mesh.flatten(),
|
| 584 |
+
value=output_data.flatten(),
|
| 585 |
+
isomin=data_min,
|
| 586 |
+
isomax=data_max,
|
| 587 |
+
opacity=0.4, # needs to be small to see through all surfaces
|
| 588 |
+
surface_count=7, # needs to be a large number for good volume rendering,
|
| 589 |
+
caps=dict(x_show=False, y_show=False, z_show=False)
|
| 590 |
+
)
|
| 591 |
+
fig.add_trace(frame_trace)
|
| 592 |
+
|
| 593 |
+
# Create a figure for the individual frame and convert to JSON
|
| 594 |
+
single_frame_fig = go.Figure(data=[frame_trace], layout=fig.layout)
|
| 595 |
+
single_frame_fig.update_layout(
|
| 596 |
+
title=f"Time: {actual_timesteps[i]}",
|
| 597 |
+
scene=dict(
|
| 598 |
+
xaxis_title='X',
|
| 599 |
+
yaxis_title='Y',
|
| 600 |
+
zaxis_title='Z',
|
| 601 |
+
xaxis=dict(range=[x_coords_plot[0], x_coords_plot[-1]]),
|
| 602 |
+
yaxis=dict(range=[y_coords_plot[0], y_coords_plot[-1]]),
|
| 603 |
+
zaxis=dict(range=[z_coords_plot[0], z_coords_plot[-1]]),
|
| 604 |
+
)
|
| 605 |
+
)
|
| 606 |
+
plotly_json_frames.append(single_frame_fig.to_json())
|
| 607 |
+
|
| 608 |
+
for trace in fig.data[1:]:
|
| 609 |
+
trace.visible = False
|
| 610 |
+
|
| 611 |
+
steps = []
|
| 612 |
+
for i in range(len(data_frames)):
|
| 613 |
+
step = dict(
|
| 614 |
+
method="update",
|
| 615 |
+
args=[{"visible": [False] * len(data_frames)}],
|
| 616 |
+
label=f"Time: {actual_timesteps[i]}"
|
| 617 |
+
)
|
| 618 |
+
step["args"][0]["visible"][i] = True
|
| 619 |
+
steps.append(step)
|
| 620 |
+
|
| 621 |
+
sliders = [dict(active=0, currentvalue={"prefix": "Time: "}, pad={"t": 50}, steps=steps)]
|
| 622 |
+
|
| 623 |
+
fig.update_layout(
|
| 624 |
+
title='', # Removed graph title
|
| 625 |
+
scene=dict(
|
| 626 |
+
xaxis_title='X',
|
| 627 |
+
yaxis_title='Y',
|
| 628 |
+
zaxis_title='Z',
|
| 629 |
+
xaxis=dict(range=[x_coords_plot[0], x_coords_plot[-1]]),
|
| 630 |
+
yaxis=dict(range=[y_coords_plot[0], y_coords_plot[-1]]),
|
| 631 |
+
zaxis=dict(range=[z_coords_plot[0], z_coords_plot[-1]]),
|
| 632 |
+
),
|
| 633 |
+
sliders=sliders,
|
| 634 |
+
width=800,
|
| 635 |
+
height=700
|
| 636 |
+
)
|
| 637 |
+
|
| 638 |
+
return fig, plotly_json_frames # Modified return
|
pages/__pycache__/__init__.cpython-310.pyc
ADDED
|
Binary file (222 Bytes). View file
|
|
|
pages/__pycache__/em_page.cpython-310.pyc
ADDED
|
Binary file (1.93 kB). View file
|
|
|
pages/__pycache__/qlbm_page.cpython-310.pyc
ADDED
|
Binary file (2.18 kB). View file
|
|
|
pages/em_page.py
CHANGED
|
@@ -7,6 +7,7 @@ import atexit
|
|
| 7 |
|
| 8 |
# Keep a single child process for the EM app
|
| 9 |
_em_proc = None
|
|
|
|
| 10 |
|
| 11 |
|
| 12 |
def _kill_em_process():
|
|
@@ -38,6 +39,7 @@ def _ensure_em_process_started():
|
|
| 38 |
env = os.environ.copy()
|
| 39 |
# Port used by iframe
|
| 40 |
env.setdefault("EM_APP_PORT", env.get("PORT_EM", "8701"))
|
|
|
|
| 41 |
# Start em_trame.py in a separate process
|
| 42 |
python_exe = sys.executable or "python"
|
| 43 |
_em_proc = subprocess.Popen([python_exe, em_path], cwd=base_dir, env=env)
|
|
@@ -51,9 +53,10 @@ def build(server):
|
|
| 51 |
"""Render the EM app via iframe and ensure its process is running."""
|
| 52 |
_ensure_em_process_started()
|
| 53 |
port = os.environ.get("EM_APP_PORT", os.environ.get("PORT_EM", "8701"))
|
|
|
|
| 54 |
with vuetify3.VContainer(fluid=True, classes="pa-0 fill-height"):
|
| 55 |
trame_html.Iframe(
|
| 56 |
-
src=("em_iframe_src", f"http://
|
| 57 |
style="border:0; width:100%; height: calc(100vh - 64px);",
|
| 58 |
)
|
| 59 |
trame_html.Div(
|
|
|
|
| 7 |
|
| 8 |
# Keep a single child process for the EM app
|
| 9 |
_em_proc = None
|
| 10 |
+
_EM_HOST = os.environ.get("EM_HOST", "127.0.0.1")
|
| 11 |
|
| 12 |
|
| 13 |
def _kill_em_process():
|
|
|
|
| 39 |
env = os.environ.copy()
|
| 40 |
# Port used by iframe
|
| 41 |
env.setdefault("EM_APP_PORT", env.get("PORT_EM", "8701"))
|
| 42 |
+
env.setdefault("EM_HOST", _EM_HOST)
|
| 43 |
# Start em_trame.py in a separate process
|
| 44 |
python_exe = sys.executable or "python"
|
| 45 |
_em_proc = subprocess.Popen([python_exe, em_path], cwd=base_dir, env=env)
|
|
|
|
| 53 |
"""Render the EM app via iframe and ensure its process is running."""
|
| 54 |
_ensure_em_process_started()
|
| 55 |
port = os.environ.get("EM_APP_PORT", os.environ.get("PORT_EM", "8701"))
|
| 56 |
+
host = os.environ.get("EM_HOST", _EM_HOST)
|
| 57 |
with vuetify3.VContainer(fluid=True, classes="pa-0 fill-height"):
|
| 58 |
trame_html.Iframe(
|
| 59 |
+
src=("em_iframe_src", f"http://{host}:{port}/"),
|
| 60 |
style="border:0; width:100%; height: calc(100vh - 64px);",
|
| 61 |
)
|
| 62 |
trame_html.Div(
|
pages/qlbm_page.py
CHANGED
|
@@ -1,203 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from trame_vuetify.widgets import vuetify3
|
| 2 |
from trame.widgets import html as trame_html
|
| 3 |
-
from trame_plotly.widgets import plotly as plotly_widgets
|
| 4 |
-
import plotly.graph_objects as go
|
| 5 |
-
import os
|
| 6 |
|
| 7 |
-
|
|
|
|
| 8 |
|
| 9 |
|
| 10 |
-
def
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
"qlbm_initialized": True,
|
| 14 |
-
"logo_src": getattr(state, "logo_src", None),
|
| 15 |
-
"is_running": False,
|
| 16 |
-
"simulation_has_run": False,
|
| 17 |
-
"error_message": "",
|
| 18 |
-
"geometry_selection": getattr(state, "geometry_selection", None),
|
| 19 |
-
"lbm_dim": getattr(state, "lbm_dim", "2D"),
|
| 20 |
-
"domain_L": getattr(state, "domain_L", 1.0),
|
| 21 |
-
"domain_W": getattr(state, "domain_W", 1.0),
|
| 22 |
-
"domain_H": getattr(state, "domain_H", 1.0),
|
| 23 |
-
"dist_type": getattr(state, "dist_type", None),
|
| 24 |
-
"advecting_field": getattr(state, "advecting_field", None),
|
| 25 |
-
"inlet_velocity": getattr(state, "inlet_velocity", 1.0),
|
| 26 |
-
"inlet_temperature": getattr(state, "inlet_temperature", 300.0),
|
| 27 |
-
"nx_slider_index": getattr(state, "nx_slider_index", None),
|
| 28 |
-
"nx": getattr(state, "nx", None),
|
| 29 |
-
"output_type": getattr(state, "output_type", "Surface Plot"),
|
| 30 |
-
})
|
| 31 |
-
except Exception:
|
| 32 |
-
pass
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
def _build_placeholder_figure(state):
|
| 36 |
-
try:
|
| 37 |
-
if state.geometry_selection == "Rectangular domain with a heated box (2D/3D)" and str(state.lbm_dim) == "3D":
|
| 38 |
-
fig = go.Figure(data=[go.Scatter3d(x=[0, 1], y=[0, 1], z=[0, 1], mode="markers")])
|
| 39 |
-
fig.update_layout(height=560, margin=dict(l=10, r=10, t=30, b=10))
|
| 40 |
-
return fig
|
| 41 |
-
fig = go.Figure(data=go.Heatmap(z=[[0, 1], [1, 0]], colorscale="RdBu"))
|
| 42 |
-
fig.update_layout(height=560, margin=dict(l=10, r=10, t=30, b=10))
|
| 43 |
-
return fig
|
| 44 |
-
except Exception:
|
| 45 |
-
return go.Figure()
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
def build(server):
|
| 49 |
-
state, ctrl = server.state, server.controller
|
| 50 |
-
_ensure_state_defaults(state)
|
| 51 |
-
|
| 52 |
-
def run_simulation():
|
| 53 |
-
state.is_running = True
|
| 54 |
-
state.error_message = ""
|
| 55 |
-
try:
|
| 56 |
-
fig = _build_placeholder_figure(state)
|
| 57 |
-
try:
|
| 58 |
-
ctrl.qlbm_plot_update(fig)
|
| 59 |
-
except Exception:
|
| 60 |
-
pass
|
| 61 |
-
state.simulation_has_run = True
|
| 62 |
-
except Exception as e:
|
| 63 |
-
state.error_message = f"Run failed: {e}"
|
| 64 |
-
finally:
|
| 65 |
-
state.is_running = False
|
| 66 |
-
|
| 67 |
-
def reset_all():
|
| 68 |
try:
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
"domain_L": 1.0,
|
| 73 |
-
"domain_W": 1.0,
|
| 74 |
-
"domain_H": 1.0,
|
| 75 |
-
"dist_type": None,
|
| 76 |
-
"advecting_field": None,
|
| 77 |
-
"inlet_velocity": 1.0,
|
| 78 |
-
"inlet_temperature": 300.0,
|
| 79 |
-
"nx_slider_index": None,
|
| 80 |
-
"nx": None,
|
| 81 |
-
"output_type": "Surface Plot",
|
| 82 |
-
"is_running": False,
|
| 83 |
-
"simulation_has_run": False,
|
| 84 |
-
"error_message": "",
|
| 85 |
-
})
|
| 86 |
try:
|
| 87 |
-
|
| 88 |
except Exception:
|
| 89 |
pass
|
| 90 |
-
|
| 91 |
-
pass
|
| 92 |
|
| 93 |
-
@state.change("nx_slider_index")
|
| 94 |
-
def _on_nx_index_change(nx_slider_index, **_):
|
| 95 |
-
try:
|
| 96 |
-
state.nx = GRID_SIZES[int(nx_slider_index)] if nx_slider_index is not None else None
|
| 97 |
-
except Exception:
|
| 98 |
-
state.nx = None
|
| 99 |
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
{"title": "2. Laminar flow & heat transfer for a heated body in water."},
|
| 113 |
-
],
|
| 114 |
-
))
|
| 115 |
-
vuetify3.VCardSubtitle("Governing Equations", classes="text-subtitle-1 font-weight-bold mt-2")
|
| 116 |
-
vuetify3.VListItemTitle("Laminar Navier-Stokes including energy", classes="text-body-2")
|
| 117 |
-
vuetify3.VCardSubtitle("Inputs", classes="text-subtitle-1 font-weight-bold mt-2")
|
| 118 |
-
vuetify3.VListItemTitle("Geometry, Boundary conditions - temperature and flow", classes="text-body-2")
|
| 119 |
-
vuetify3.VCardSubtitle("Outputs", classes="text-subtitle-1 font-weight-bold mt-2")
|
| 120 |
-
vuetify3.VListItemTitle("Surface plots on sections OR sampling through a line in 3D domain", classes="text-body-2")
|
| 121 |
-
|
| 122 |
-
with vuetify3.VCard(classes="mb-4"):
|
| 123 |
-
vuetify3.VCardTitle("Geometry", classes="text-primary")
|
| 124 |
-
with vuetify3.VCardText():
|
| 125 |
-
vuetify3.VSelect(
|
| 126 |
-
label="Select",
|
| 127 |
-
v_model=("geometry_selection", None),
|
| 128 |
-
items=(
|
| 129 |
-
"geometry_options",
|
| 130 |
-
[
|
| 131 |
-
"None",
|
| 132 |
-
"Free space",
|
| 133 |
-
"Rectangular domain with a heated box (2D/3D)",
|
| 134 |
-
],
|
| 135 |
-
),
|
| 136 |
-
placeholder="Select",
|
| 137 |
-
density="compact",
|
| 138 |
-
color="primary",
|
| 139 |
-
)
|
| 140 |
-
with vuetify3.VContainer(v_if="geometry_selection === 'Rectangular domain with a heated box (2D/3D)'", classes="pa-0 mt-2"):
|
| 141 |
-
vuetify3.VRadioGroup(v_model=("lbm_dim", "2D"), row=True, density="compact", color="primary", children=[
|
| 142 |
-
vuetify3.VRadio(label="2D", value="2D"),
|
| 143 |
-
vuetify3.VRadio(label="3D", value="3D"),
|
| 144 |
-
])
|
| 145 |
-
with vuetify3.VRow(dense=True):
|
| 146 |
-
vuetify3.VCol(children=[vuetify3.VTextField(label="Length (L)", v_model=("domain_L", 1.0), type="number", step="0.1", density="compact", color="primary")])
|
| 147 |
-
vuetify3.VCol(children=[vuetify3.VTextField(label="Width (W)", v_model=("domain_W", 1.0), type="number", step="0.1", density="compact", color="primary")])
|
| 148 |
-
vuetify3.VTextField(v_if="lbm_dim === '3D'", label="Height (H)", v_model=("domain_H", 1.0), type="number", step="0.1", density="compact", color="primary")
|
| 149 |
|
| 150 |
-
with vuetify3.VCard(v_if="geometry_selection === 'Rectangular domain with a heated box (2D/3D)'", classes="mb-4"):
|
| 151 |
-
vuetify3.VCardTitle("Initial & Boundary Conditions", classes="text-primary")
|
| 152 |
-
with vuetify3.VCardText():
|
| 153 |
-
vuetify3.VSelect(label="Initial Condition", v_model=("dist_type", None), items=("dist_type_opts", ["None", "Delta", "Gaussian"]), density="compact", color="primary")
|
| 154 |
-
vuetify3.VSelect(label="Advecting field", v_model=("advecting_field", None), items=("advect_fields", ["Uniform", "Swirl", "Shear", "TGV"]), density="compact", color="primary", classes="mt-2")
|
| 155 |
-
with vuetify3.VRow(dense=True, classes="mt-2"):
|
| 156 |
-
vuetify3.VCol(children=[vuetify3.VTextField(label="Inlet flow velocity", v_model=("inlet_velocity", 1.0), type="number", step="0.1", density="compact", color="primary")])
|
| 157 |
-
vuetify3.VCol(children=[vuetify3.VTextField(label="Inlet temperature (K)", v_model=("inlet_temperature", 300.0), type="number", step="1", density="compact", color="primary")])
|
| 158 |
|
| 159 |
-
|
| 160 |
-
vuetify3.VCardTitle("Meshing", classes="text-primary")
|
| 161 |
-
with vuetify3.VCardText():
|
| 162 |
-
with vuetify3.VSlider(
|
| 163 |
-
v_model=("nx_slider_index", None),
|
| 164 |
-
label="No. of points per direction:",
|
| 165 |
-
min=0,
|
| 166 |
-
max=len(GRID_SIZES) - 1,
|
| 167 |
-
step=1,
|
| 168 |
-
show_ticks="always",
|
| 169 |
-
thumb_label="always",
|
| 170 |
-
density="compact",
|
| 171 |
-
color="primary",
|
| 172 |
-
):
|
| 173 |
-
vuetify3.Template(v_slot_thumb_label="{ modelValue }", children=["{{ modelValue === null ? 'Select' : [16, 32, 64, 128, 256, 512][modelValue] }}"])
|
| 174 |
|
| 175 |
-
with vuetify3.VCard(classes="mb-4"):
|
| 176 |
-
vuetify3.VCardTitle("Backends", classes="text-primary")
|
| 177 |
-
with vuetify3.VCardText():
|
| 178 |
-
vuetify3.VAlert(type="info", color="primary", variant="tonal", density="compact", children=["Simulator only (placeholder)"])
|
| 179 |
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
vuetify3.VRadio(label="Surface", value="Surface Plot")
|
| 194 |
-
vuetify3.VRadio(label="Line Sampling", value="Line Sampling")
|
| 195 |
|
| 196 |
-
with vuetify3.VCard(classes="flex-grow-1", style="min-height: 0;"):
|
| 197 |
-
with vuetify3.VContainer(v_if="is_running", fluid=True, classes="fill-height d-flex flex-column align-center justify-center"):
|
| 198 |
-
vuetify3.VProgressCircular(indeterminate=True, size=64, color="primary")
|
| 199 |
-
vuetify3.VCardSubtitle("Running simulation...", classes="mt-4")
|
| 200 |
-
with vuetify3.VContainer(v_if="!is_running", fluid=True, classes="fill-height pa-2"):
|
| 201 |
-
fig = plotly_widgets.Figure(figure=go.Figure(), responsive=True, style="width: 100%; min-height: 560px;")
|
| 202 |
-
ctrl.qlbm_plot_update = fig.update
|
| 203 |
-
vuetify3.VContainer(v_if="!simulation_has_run", classes="d-flex align-center justify-center", style="height: 360px; color: rgba(0,0,0,.6);", children=["Configure inputs and run to display results."])
|
|
|
|
| 1 |
+
"""Embedded QLBM fluids page wrapper.
|
| 2 |
+
Starts the standalone qlbm.py server in a background subprocess so the main
|
| 3 |
+
multi-page app only needs `python app.py`.
|
| 4 |
+
|
| 5 |
+
Environment variables:
|
| 6 |
+
QLBM_APP_PORT / PORT_QLBM -> port (default 8702)
|
| 7 |
+
QLBM_HOST -> host interface (default 127.0.0.1)
|
| 8 |
+
"""
|
| 9 |
+
from __future__ import annotations
|
| 10 |
+
import os, sys, subprocess, atexit
|
| 11 |
from trame_vuetify.widgets import vuetify3
|
| 12 |
from trame.widgets import html as trame_html
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
+
_qlbm_proc = None
|
| 15 |
+
_QLBM_HOST = os.environ.get("QLBM_HOST", "127.0.0.1")
|
| 16 |
|
| 17 |
|
| 18 |
+
def _kill_qlbm_process():
|
| 19 |
+
global _qlbm_proc
|
| 20 |
+
if _qlbm_proc and _qlbm_proc.poll() is None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
try:
|
| 22 |
+
_qlbm_proc.terminate()
|
| 23 |
+
_qlbm_proc.wait(timeout=2)
|
| 24 |
+
except Exception:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
try:
|
| 26 |
+
_qlbm_proc.kill()
|
| 27 |
except Exception:
|
| 28 |
pass
|
| 29 |
+
_qlbm_proc = None
|
|
|
|
| 30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
|
| 32 |
+
def _ensure_qlbm_process_started():
|
| 33 |
+
global _qlbm_proc
|
| 34 |
+
if _qlbm_proc and _qlbm_proc.poll() is None:
|
| 35 |
+
return
|
| 36 |
+
_kill_qlbm_process()
|
| 37 |
+
base_dir = os.path.dirname(os.path.dirname(__file__))
|
| 38 |
+
qlbm_path = os.path.join(base_dir, "qlbm.py")
|
| 39 |
+
env = os.environ.copy()
|
| 40 |
+
env.setdefault("QLBM_APP_PORT", env.get("PORT_QLBM", "8702"))
|
| 41 |
+
env.setdefault("QLBM_HOST", _QLBM_HOST)
|
| 42 |
+
py = sys.executable or "python"
|
| 43 |
+
_qlbm_proc = subprocess.Popen([py, qlbm_path], cwd=base_dir, env=env)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
+
atexit.register(_kill_qlbm_process)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
+
def build(server): # signature matches app.py expectation
|
| 50 |
+
_ensure_qlbm_process_started()
|
| 51 |
+
port = os.environ.get("QLBM_APP_PORT", os.environ.get("PORT_QLBM", "8702"))
|
| 52 |
+
host = os.environ.get("QLBM_HOST", _QLBM_HOST)
|
| 53 |
+
with vuetify3.VContainer(fluid=True, classes="pa-0 fill-height"):
|
| 54 |
+
trame_html.Iframe(
|
| 55 |
+
src=("qlbm_iframe_src", f"http://{host}:{port}/"),
|
| 56 |
+
style="border:0;width:100%;height:calc(100vh - 64px);",
|
| 57 |
+
)
|
| 58 |
+
trame_html.Div(
|
| 59 |
+
"If the QLBM view is blank, wait a few seconds for the subprocess to start.",
|
| 60 |
+
style="color:rgba(0,0,0,.6);padding:6px;",
|
| 61 |
+
)
|
|
|
|
|
|
|
| 62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
qlbm.py
CHANGED
|
@@ -1,613 +1,669 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import os
|
| 2 |
-
import numpy as np
|
| 3 |
import math
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
from trame.app import get_server
|
| 5 |
from trame_vuetify.ui.vuetify3 import SinglePageLayout
|
| 6 |
from trame_vuetify.widgets import vuetify3
|
| 7 |
from trame.widgets import html as trame_html
|
| 8 |
from trame_plotly.widgets import plotly as plotly_widgets
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
#
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
state, ctrl = server.state, server.controller
|
| 14 |
|
| 15 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
state.update({
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
"selected_qpu": "IBM QPU",
|
| 48 |
-
# Outputs
|
| 49 |
-
"output_type": "Surface Plot", # Surface Plot | Line Sampling
|
| 50 |
-
"qubit_grid_info": "",
|
| 51 |
-
"qubit_warning": "",
|
| 52 |
})
|
| 53 |
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
#
|
| 57 |
-
def load_logo_data_uri():
|
| 58 |
-
try:
|
| 59 |
-
base_dir = os.path.dirname(__file__)
|
| 60 |
-
candidates = [
|
| 61 |
-
os.path.join(base_dir, "ansys-part-of-synopsys-logo.svg"),
|
| 62 |
-
os.path.join(base_dir, "synopsys-logo-color-rgb.svg"),
|
| 63 |
-
os.path.join(base_dir, "synopsys-logo-color-rgb.png"),
|
| 64 |
-
os.path.join(base_dir, "synopsys-logo-color-rgb.jpg"),
|
| 65 |
-
]
|
| 66 |
-
for p in candidates:
|
| 67 |
-
if os.path.exists(p):
|
| 68 |
-
ext = os.path.splitext(p)[1].lower()
|
| 69 |
-
mime = "image/svg+xml" if ext == ".svg" else ("image/png" if ext == ".png" else "image/jpeg")
|
| 70 |
-
with open(p, "rb") as f:
|
| 71 |
-
import base64
|
| 72 |
-
b64 = base64.b64encode(f.read()).decode("ascii")
|
| 73 |
-
return f"data:{mime};base64,{b64}"
|
| 74 |
-
except Exception:
|
| 75 |
-
pass
|
| 76 |
-
return None
|
| 77 |
-
|
| 78 |
-
state.logo_src = load_logo_data_uri()
|
| 79 |
-
|
| 80 |
-
# --- 3D Qubit Info Function ---
|
| 81 |
def update_qubit_3D_info(grid_size: int):
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
def run_simulation():
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
#
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
with SinglePageLayout(server) as layout:
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
|
|
|
|
|
|
|
|
|
| 420 |
|
| 421 |
def build(host_server):
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
"mu_x": 0.5, "mu_y": 0.5, "sigma_x": 0.25, "sigma_y": 0.15, "mu_pair": "(0.5, 0.5)",
|
| 442 |
-
"excitation_error_message": "",
|
| 443 |
-
"advecting_field": None,
|
| 444 |
-
"inlet_velocity": 1.0,
|
| 445 |
-
"inlet_temperature": 300.0,
|
| 446 |
-
"nx_slider_index": None,
|
| 447 |
-
"nx": None,
|
| 448 |
-
"backend_type": "Simulator",
|
| 449 |
-
"selected_simulator": "IBM Qiskit simulator",
|
| 450 |
-
"selected_qpu": "IBM QPU",
|
| 451 |
-
"output_type": "Surface Plot",
|
| 452 |
-
"qubit_grid_info": "",
|
| 453 |
-
"qubit_warning": "",
|
| 454 |
-
})
|
| 455 |
-
|
| 456 |
-
# UI (previously inside SinglePageLayout.content)
|
| 457 |
-
with vuetify3.VContainer(fluid=True, classes="pa-0 fill-height"):
|
| 458 |
-
with vuetify3.VRow(no_gutters=True, classes="fill-height"):
|
| 459 |
-
# Left column
|
| 460 |
-
with vuetify3.VCol(cols=5, classes="pa-4 d-flex flex-column"):
|
| 461 |
-
# Overview
|
| 462 |
-
with vuetify3.VCard(classes="mb-4"):
|
| 463 |
-
vuetify3.VCardTitle("Overview", classes="text-h5 font-weight-bold text-primary")
|
| 464 |
-
with vuetify3.VCardText():
|
| 465 |
-
vuetify3.VDivider(classes="my-2")
|
| 466 |
-
vuetify3.VCardSubtitle("Problems!", classes="text-subtitle-1 font-weight-bold mt-2")
|
| 467 |
-
vuetify3.VSelect(
|
| 468 |
-
key="overview_problems",
|
| 469 |
-
label="Select a problem",
|
| 470 |
-
v_model=("problems_selection", None),
|
| 471 |
-
items=("qlbm_problems", [
|
| 472 |
-
"1. Scalar advection-diffusion in a box",
|
| 473 |
-
"2. Laminar flow & heat transfer for a heated body in water.",
|
| 474 |
-
]),
|
| 475 |
-
placeholder="Select",
|
| 476 |
-
density="compact",
|
| 477 |
-
color="primary",
|
| 478 |
-
)
|
| 479 |
-
vuetify3.VCardSubtitle("Governing Equations", classes="text-subtitle-1 font-weight-bold mt-2")
|
| 480 |
-
vuetify3.VListItemTitle("Laminar Navier-Stokes including energy", classes="text-body-2")
|
| 481 |
-
vuetify3.VCardSubtitle("Inputs", classes="text-subtitle-1 font-weight-bold mt-2")
|
| 482 |
-
vuetify3.VListItemTitle("Geometry, Boundary conditions - temperature and flow", classes="text-body-2")
|
| 483 |
-
vuetify3.VCardSubtitle("Outputs", classes="text-subtitle-1 font-weight-bold mt-2")
|
| 484 |
-
vuetify3.VListItemTitle("Surface plots on sections OR sampling through a line in 3D domain", classes="text-body-2")
|
| 485 |
-
# Geometry
|
| 486 |
-
with vuetify3.VCard(classes="mb-4"):
|
| 487 |
-
vuetify3.VCardTitle("Geometry", classes="text-primary")
|
| 488 |
-
with vuetify3.VCardText():
|
| 489 |
-
vuetify3.VSelect(
|
| 490 |
-
key="geom_select",
|
| 491 |
-
label="Select",
|
| 492 |
-
v_model=("geometry_selection", None),
|
| 493 |
-
items=("geometry_options", [
|
| 494 |
-
"Free space",
|
| 495 |
-
"Rectangular domain with a heated box (3D)",
|
| 496 |
-
]),
|
| 497 |
-
placeholder="Select",
|
| 498 |
-
density="compact",
|
| 499 |
-
color="primary",
|
| 500 |
-
)
|
| 501 |
-
with vuetify3.VContainer(v_if="geometry_selection === 'Rectangular domain with a heated box (3D)'", classes="pa-0 mt-2"):
|
| 502 |
-
with vuetify3.VRow(dense=True):
|
| 503 |
-
vuetify3.VCol(children=[vuetify3.VTextField(key="geom_len", label="Length (L)", v_model=("domain_L", 1.0), type="number", step="0.1", density="compact", color="primary")])
|
| 504 |
-
vuetify3.VCol(children=[vuetify3.VTextField(key="geom_breadth", label="Breadth (B)", v_model=("domain_W", 1.0), type="number", step="0.1", density="compact", color="primary")])
|
| 505 |
-
vuetify3.VTextField(key="geom_height", label="Height (H)", v_model=("domain_H", 1.0), type="number", step="0.1", density="compact", color="primary")
|
| 506 |
-
# Initial & BC
|
| 507 |
-
with vuetify3.VCard(v_if="geometry_selection === 'Rectangular domain with a heated box (3D)'", classes="mb-4"):
|
| 508 |
-
vuetify3.VCardTitle("Initial & Boundary Conditions", classes="text-primary")
|
| 509 |
-
with vuetify3.VCardText():
|
| 510 |
-
vuetify3.VTextField(key="gauss_mu", v_model=("mu_pair", "(0.5, 0.5)"), label="Gaussian μ (x, y) in [0,1]", density="compact", color="primary")
|
| 511 |
-
with vuetify3.VRow(dense=True, classes="mt-1"):
|
| 512 |
-
vuetify3.VCol(children=[vuetify3.VTextField(key="sigma_x", label="Sigma X (0–1)", v_model=("sigma_x", 0.25), type="number", step="0.01", density="compact", color="primary")])
|
| 513 |
-
vuetify3.VCol(children=[vuetify3.VTextField(key="sigma_y", label="Sigma Y (0–1)", v_model=("sigma_y", 0.15), type="number", step="0.01", density="compact", color="primary")])
|
| 514 |
-
vuetify3.VAlert(v_if="excitation_error_message", type="error", variant="tonal", density="compact", children=["{{ excitation_error_message }}"], classes="mt-2")
|
| 515 |
-
vuetify3.VSelect(key="adv_field", label="Advecting field", v_model=("advecting_field", None), items=("advect_fields", ["Uniform", "Swirl", "Shear", "TGV"]), density="compact", color="primary", classes="mt-2")
|
| 516 |
-
with vuetify3.VRow(dense=True, classes="mt-2"):
|
| 517 |
-
vuetify3.VCol(children=[vuetify3.VTextField(key="inlet_vel", label="Inlet flow velocity", v_model=("inlet_velocity", 1.0), type="number", step="0.1", density="compact", color="primary")])
|
| 518 |
-
vuetify3.VCol(children=[vuetify3.VTextField(key="inlet_temp", label="Inlet temperature (K)", v_model=("inlet_temperature", 300.0), type="number", step="1", density="compact", color="primary")])
|
| 519 |
-
# Meshing
|
| 520 |
-
with vuetify3.VCard(classes="mb-4"):
|
| 521 |
-
vuetify3.VCardTitle("Meshing", classes="text-primary")
|
| 522 |
-
with vuetify3.VCardText():
|
| 523 |
-
with vuetify3.VMenu(open_on_hover=True, close_on_content_click=False, location="end"):
|
| 524 |
-
with vuetify3.Template(v_slot_activator="{ props }"):
|
| 525 |
-
with vuetify3.VSlider(
|
| 526 |
-
key="mesh_slider",
|
| 527 |
-
v_bind="props",
|
| 528 |
-
v_model=("nx_slider_index", None),
|
| 529 |
-
label="No. of points per direction:",
|
| 530 |
-
min=0,
|
| 531 |
-
max=len(GRID_SIZES) - 1,
|
| 532 |
-
step=1,
|
| 533 |
-
show_ticks="always",
|
| 534 |
-
thumb_label="always",
|
| 535 |
-
density="compact",
|
| 536 |
-
color="primary",
|
| 537 |
-
):
|
| 538 |
-
vuetify3.Template(v_slot_thumb_label="{ modelValue }", children=["{{ modelValue === null ? 'Select' : [16, 32, 64, 128, 256, 512, 1024, 2048][modelValue] }}"])
|
| 539 |
-
with vuetify3.VSheet(classes="pa-2", elevation=6, rounded=True, style="width: 700px;"):
|
| 540 |
-
with vuetify3.VContainer(fluid=True, classes="pa-0"):
|
| 541 |
-
qubit_fig_widget = plotly_widgets.Figure(
|
| 542 |
-
figure=go.Figure(),
|
| 543 |
-
responsive=True,
|
| 544 |
-
style="width: 616px; height: 320px; min-height: 320px;",
|
| 545 |
-
)
|
| 546 |
-
vuetify3.VCardText(children=["{{ qubit_grid_info }}", "{{ qubit_warning }}"], classes="text-caption mt-2")
|
| 547 |
-
# Backends
|
| 548 |
-
with vuetify3.VCard(classes="mb-4"):
|
| 549 |
-
vuetify3.VCardTitle("Backends", classes="text-primary")
|
| 550 |
-
with vuetify3.VCardText():
|
| 551 |
-
with vuetify3.VRow(dense=True, classes="mb-2"):
|
| 552 |
-
with vuetify3.VCol():
|
| 553 |
-
vuetify3.VAlert(
|
| 554 |
-
type="info",
|
| 555 |
-
color="primary",
|
| 556 |
-
variant="tonal",
|
| 557 |
-
density="compact",
|
| 558 |
-
children=[
|
| 559 |
-
"Selected: ",
|
| 560 |
-
"{{ backend_type || '—' }}",
|
| 561 |
-
" - ",
|
| 562 |
-
"{{ backend_type === 'Simulator' ? selected_simulator : (backend_type === 'QPU' ? selected_qpu : '—') }}",
|
| 563 |
-
],
|
| 564 |
-
)
|
| 565 |
-
with vuetify3.VMenu(open_on_hover=True, close_on_content_click=True, location="end"):
|
| 566 |
-
with vuetify3.Template(v_slot_activator="{ props }"):
|
| 567 |
-
vuetify3.VBtn(v_bind="props", text="Choose Backend", color="primary", variant="tonal", block=True)
|
| 568 |
-
with vuetify3.VList(density="compact"):
|
| 569 |
-
with vuetify3.VMenu(open_on_hover=True, close_on_content_click=True, location="end", offset=8):
|
| 570 |
-
with vuetify3.Template(v_slot_activator="{ props }"):
|
| 571 |
-
vuetify3.VListItem(v_bind="props", title="Simulator", prepend_icon="mdi-robot-outline", append_icon="mdi-chevron-right")
|
| 572 |
-
with vuetify3.VList(density="compact"):
|
| 573 |
-
vuetify3.VListItem(title="IBM Qiskit simulator", click="backend_type = 'Simulator'; selected_simulator = 'IBM Qiskit simulator'")
|
| 574 |
-
vuetify3.VListItem(title="IonQ simulator", click="backend_type = 'Simulator'; selected_simulator = 'IonQ simulator'")
|
| 575 |
-
with vuetify3.VMenu(open_on_hover=True, close_on_content_click=True, location="end", offset=8):
|
| 576 |
-
with vuetify3.Template(v_slot_activator="{ props }"):
|
| 577 |
-
vuetify3.VListItem(v_bind="props", title="QPU", prepend_icon="mdi-chip", append_icon="mdi-chevron-right")
|
| 578 |
-
with vuetify3.VList(density="compact"):
|
| 579 |
-
vuetify3.VListItem(title="IBM QPU", click="backend_type = 'QPU'; selected_qpu = 'IBM QPU'")
|
| 580 |
-
vuetify3.VListItem(title="IonQ QPU", click="backend_type = 'QPU'; selected_qpu = 'IonQ QPU'")
|
| 581 |
-
with vuetify3.VRow(dense=True, classes="gap-2 mt-4"):
|
| 582 |
-
with vuetify3.VCol():
|
| 583 |
-
vuetify3.VBtn(
|
| 584 |
-
text="Run Simulation",
|
| 585 |
-
color="primary",
|
| 586 |
-
block=True,
|
| 587 |
-
disabled=("is_running || !geometry_selection || (geometry_selection === 'Rectangular domain with a heated box (3D)' && nx === null)", False),
|
| 588 |
-
click=run_simulation
|
| 589 |
-
)
|
| 590 |
-
with vuetify3.VCol():
|
| 591 |
-
vuetify3.VBtn(
|
| 592 |
-
text="Reset",
|
| 593 |
-
color="secondary",
|
| 594 |
-
variant="tonal",
|
| 595 |
-
block=True,
|
| 596 |
-
click=reset_all
|
| 597 |
-
)
|
| 598 |
-
# Right column
|
| 599 |
-
with vuetify3.VCol(cols=7, classes="pa-4 d-flex flex-column"):
|
| 600 |
-
with vuetify3.VContainer(fluid=True, classes="fill-height d-flex align-center justify-center"):
|
| 601 |
-
vuetify3.VCardText("Select a geometry and configure inputs to display results.", classes="text-center text-medium-emphasis")
|
| 602 |
-
|
| 603 |
-
# Register qubit figure update hook
|
| 604 |
-
ctrl.qubit_plot_update = qubit_fig_widget.update
|
| 605 |
-
|
| 606 |
-
# Re-register state change callbacks on host server
|
| 607 |
-
host_server.state.change("nx")(_update_qubit_plot)
|
| 608 |
-
host_server.state.change("peak_pair")(sync_peak_pair)
|
| 609 |
-
host_server.state.change("mu_pair")(sync_mu_pair)
|
| 610 |
-
host_server.state.change("dist_type", "mu_x", "mu_y", "sigma_x", "sigma_y", "impulse_x", "impulse_y", "nx")(on_preview_params_change)
|
| 611 |
|
| 612 |
# --- Entry point ---
|
| 613 |
if __name__ == "__main__":
|
|
|
|
| 1 |
+
"""trame3d.py
|
| 2 |
+
An equivalent Trame application replicating the functionality of `gradio3d.py`.
|
| 3 |
+
|
| 4 |
+
Features ported from Gradio version:
|
| 5 |
+
- Example initial distributions (Sinusoidal / Gaussian) with buttons to load presets
|
| 6 |
+
- Simulation parameter controls (grid size slider, time steps slider)
|
| 7 |
+
- Dynamic qubit requirement plot vs. grid size (log2 relation)
|
| 8 |
+
- Velocity field presets (Uniform / Swirl / Shear / TGV) updating velocity expression text boxes
|
| 9 |
+
- Custom velocity expression inputs parsed via SymPy (x, y, z)
|
| 10 |
+
- Initial distribution type radio (Gaussian / Sinusoidal)
|
| 11 |
+
- Boundary condition radio (Periodic / Dirichlet / Neumann)
|
| 12 |
+
- Run simulation button producing a mock 3D Plotly scatter visualization
|
| 13 |
+
- Download JSON frames button (zip of Plotly figure JSON) enabled post-run
|
| 14 |
+
|
| 15 |
+
This is a standalone Trame app and can also be embedded via the `build(host_server)` helper.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
import os
|
|
|
|
| 19 |
import math
|
| 20 |
+
import tempfile
|
| 21 |
+
import zipfile
|
| 22 |
+
import numpy as np
|
| 23 |
+
import plotly.graph_objects as go
|
| 24 |
+
import importlib
|
| 25 |
+
import sys
|
| 26 |
+
import types
|
| 27 |
+
import time
|
| 28 |
+
import multiprocessing as mp
|
| 29 |
+
import plotly.io as pio
|
| 30 |
+
symbols = sympify = lambdify = None # Deferred SymPy import handled at runtime
|
| 31 |
+
|
| 32 |
from trame.app import get_server
|
| 33 |
from trame_vuetify.ui.vuetify3 import SinglePageLayout
|
| 34 |
from trame_vuetify.widgets import vuetify3
|
| 35 |
from trame.widgets import html as trame_html
|
| 36 |
from trame_plotly.widgets import plotly as plotly_widgets
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
# Local simulation module (lazy with optional shim for gradio_litmodel3d)
|
| 40 |
+
qfluid = None
|
| 41 |
+
|
| 42 |
+
def _ensure_qfluid_import():
|
| 43 |
+
"""Attempt to import fluid.py as qfluid.
|
| 44 |
+
If gradio_litmodel3d is missing, temporarily stub it to avoid import failure.
|
| 45 |
+
Return imported module or None.
|
| 46 |
+
"""
|
| 47 |
+
global qfluid
|
| 48 |
+
if qfluid is not None:
|
| 49 |
+
return qfluid
|
| 50 |
+
# Try plain import first
|
| 51 |
+
try:
|
| 52 |
+
import fluid as _qf
|
| 53 |
+
qfluid = _qf
|
| 54 |
+
return qfluid
|
| 55 |
+
except Exception:
|
| 56 |
+
pass
|
| 57 |
+
# Retry with a lightweight stub for gradio_litmodel3d
|
| 58 |
+
if 'gradio_litmodel3d' not in sys.modules:
|
| 59 |
+
sys.modules['gradio_litmodel3d'] = types.ModuleType('gradio_litmodel3d')
|
| 60 |
+
try:
|
| 61 |
+
import importlib as _il
|
| 62 |
+
qfluid = _il.import_module('fluid3d_pyvista')
|
| 63 |
+
return qfluid
|
| 64 |
+
except Exception:
|
| 65 |
+
return None
|
| 66 |
+
|
| 67 |
+
server = get_server(name="trame3d")
|
| 68 |
state, ctrl = server.state, server.controller
|
| 69 |
|
| 70 |
+
# Register static assets (PNG previews)
|
| 71 |
+
_assets_dir = os.path.join(os.path.dirname(__file__), "Placeholder_Images")
|
| 72 |
+
if os.path.isdir(_assets_dir):
|
| 73 |
+
try:
|
| 74 |
+
server.assets.add(path=_assets_dir, prefix="assets")
|
| 75 |
+
except Exception:
|
| 76 |
+
pass
|
| 77 |
+
|
| 78 |
+
# Throttle settings for live plot updates
|
| 79 |
+
_last_qubit_update = 0.0
|
| 80 |
+
_qubit_update_min_interval = 0.05 # seconds
|
| 81 |
+
|
| 82 |
+
# --------------------------------------------------------------------------------------
|
| 83 |
+
# State Defaults
|
| 84 |
+
# --------------------------------------------------------------------------------------
|
| 85 |
state.update({
|
| 86 |
+
# Core inputs
|
| 87 |
+
"grid_size": 32,
|
| 88 |
+
"grid_index": 2, # 0..5 -> [8,16,32,64,128,256]
|
| 89 |
+
"time_steps": 100,
|
| 90 |
+
"distribution_type": "Sinusoidal", # Sinusoidal | Gaussian
|
| 91 |
+
"vx_expr": "0.2",
|
| 92 |
+
"vy_expr": "-0.15",
|
| 93 |
+
"vz_expr": "0.3",
|
| 94 |
+
"boundary_condition": "Periodic", # Periodic | Dirichlet | Neumann
|
| 95 |
+
# Velocity preset selection
|
| 96 |
+
"advecting_field": "Uniform", # Uniform | Swirl | Shear | TGV
|
| 97 |
+
# Simulation / outputs
|
| 98 |
+
"is_running": False,
|
| 99 |
+
"run_error": "",
|
| 100 |
+
"has_frames": False,
|
| 101 |
+
"download_ready": False,
|
| 102 |
+
"download_path": None,
|
| 103 |
+
# Qubit plot info
|
| 104 |
+
"qubit_grid_info": "Grid Size: 32 × 32 × 32",
|
| 105 |
+
"qubit_warning": "",
|
| 106 |
+
# Overview / Backends (ported from qlbm)
|
| 107 |
+
"problems_selection": None,
|
| 108 |
+
"backend_type": "Simulator",
|
| 109 |
+
"selected_simulator": "IBM Qiskit simulator",
|
| 110 |
+
"selected_qpu": "IBM QPU",
|
| 111 |
+
# Geometry (for left-side Geometry cell)
|
| 112 |
+
"geometry_selection": None,
|
| 113 |
+
"domain_L": 1.0,
|
| 114 |
+
"domain_W": 1.0,
|
| 115 |
+
"domain_H": 1.0,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
})
|
| 117 |
|
| 118 |
+
# --------------------------------------------------------------------------------------
|
| 119 |
+
# Helpers
|
| 120 |
+
# --------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
def update_qubit_3D_info(grid_size: int):
|
| 122 |
+
"""Generate qubit requirement plot and info strings.
|
| 123 |
+
For a cubic grid: qubits/direction = log2(grid_size).
|
| 124 |
+
"""
|
| 125 |
+
try:
|
| 126 |
+
num_reg_qubits = int(math.log2(grid_size)) if grid_size > 0 else 3
|
| 127 |
+
x = np.array([16, 32, 64, 128, 256])
|
| 128 |
+
y = np.log2(x).astype(int)
|
| 129 |
+
fig = go.Figure()
|
| 130 |
+
fig.add_trace(go.Scatter(x=x, y=y, mode='lines', name='Qubits/Direction', line=dict(color='#7A3DB5', width=3)))
|
| 131 |
+
fig.add_trace(go.Scatter(x=[grid_size], y=[num_reg_qubits], mode='markers',
|
| 132 |
+
marker=dict(size=12, color='red'), name='Current Selection'))
|
| 133 |
+
fig.update_layout(
|
| 134 |
+
xaxis_title="Grid Size (Points/Direction)",
|
| 135 |
+
yaxis_title="Qubits/Direction",
|
| 136 |
+
width=616,
|
| 137 |
+
height=320,
|
| 138 |
+
margin=dict(l=40, r=20, t=20, b=40)
|
| 139 |
+
)
|
| 140 |
+
grid_display = f"Grid Size: {grid_size} × {grid_size} × {grid_size}"
|
| 141 |
+
warning = "⚠️ Warning: Grid sizes > 64 may exceed simulator/memory limits!" if grid_size > 64 else ""
|
| 142 |
+
return fig, grid_display, warning
|
| 143 |
+
except Exception:
|
| 144 |
+
return go.Figure(), "Grid Size: N/A", ""
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
def _mock_simulation(num_reg_qubits, time_steps, distribution_type, vx, vy, vz, boundary_condition):
|
| 148 |
+
"""Produce a mock 3D scatter figure and JSON frames (placeholder)."""
|
| 149 |
+
# Random 3D scatter sized by 50 points.
|
| 150 |
+
x, y, z = np.random.rand(3, 50)
|
| 151 |
+
fig = go.Figure(data=[go.Scatter3d(
|
| 152 |
+
x=x, y=y, z=z,
|
| 153 |
+
mode='markers',
|
| 154 |
+
marker=dict(size=5, color=z, colorscale='Viridis', opacity=0.85),
|
| 155 |
+
text=[f"({xi:.2f},{yi:.2f},{zi:.2f})" for xi, yi, zi in zip(x, y, z)],
|
| 156 |
+
hovertemplate="<b>Point</b><br>X=%{x:.2f}<br>Y=%{y:.2f}<br>Z=%{z:.2f}<extra></extra>",
|
| 157 |
+
)])
|
| 158 |
+
fig.update_layout(
|
| 159 |
+
title=f"Mock 3D Simulation Result ({distribution_type})",
|
| 160 |
+
scene=dict(xaxis_title='X', yaxis_title='Y', zaxis_title='Z'),
|
| 161 |
+
margin=dict(l=0, r=0, t=40, b=0),
|
| 162 |
+
showlegend=False,
|
| 163 |
+
)
|
| 164 |
+
return fig, [fig.to_json()]
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
def create_zip_from_frames(frames_json_list):
|
| 168 |
+
"""Create a temporary zip archive containing frame JSON strings."""
|
| 169 |
+
if not frames_json_list:
|
| 170 |
+
return None
|
| 171 |
+
tmp = tempfile.NamedTemporaryFile(suffix="_frames.zip", delete=False)
|
| 172 |
+
tmp.close()
|
| 173 |
+
with zipfile.ZipFile(tmp.name, 'w', zipfile.ZIP_DEFLATED) as zf:
|
| 174 |
+
for i, frame_json in enumerate(frames_json_list):
|
| 175 |
+
zf.writestr(f"frame_{i}.json", frame_json)
|
| 176 |
+
return tmp.name
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def _ensure_sympy_import():
|
| 180 |
+
global symbols, sympify, lambdify
|
| 181 |
+
if symbols is None or sympify is None or lambdify is None:
|
| 182 |
+
try:
|
| 183 |
+
sym = importlib.import_module("sympy")
|
| 184 |
+
symbols = sym.symbols
|
| 185 |
+
sympify = sym.sympify
|
| 186 |
+
lambdify = sym.lambdify
|
| 187 |
+
except Exception:
|
| 188 |
+
symbols = sympify = lambdify = None
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
def _build_velocity_functions(vx_expr: str, vy_expr: str, vz_expr: str):
|
| 192 |
+
"""Return callables vx(x,y,z), vy(x,y,z), vz(x,y,z) from user expressions.
|
| 193 |
+
Fallback to constants if SymPy is unavailable or parsing fails."""
|
| 194 |
+
_ensure_sympy_import()
|
| 195 |
+
if symbols and sympify and lambdify:
|
| 196 |
+
x_sym, y_sym, z_sym = symbols('x y z')
|
| 197 |
+
try:
|
| 198 |
+
vx_sym = sympify(vx_expr)
|
| 199 |
+
vy_sym = sympify(vy_expr)
|
| 200 |
+
vz_sym = sympify(vz_expr)
|
| 201 |
+
vx_fn = lambdify((x_sym, y_sym, z_sym), vx_sym, modules="numpy")
|
| 202 |
+
vy_fn = lambdify((x_sym, y_sym, z_sym), vy_sym, modules="numpy")
|
| 203 |
+
vz_fn = lambdify((x_sym, y_sym, z_sym), vz_sym, modules="numpy")
|
| 204 |
+
return vx_fn, vy_fn, vz_fn
|
| 205 |
+
except Exception:
|
| 206 |
+
pass
|
| 207 |
+
# Fallback to constant-valued functions
|
| 208 |
+
try:
|
| 209 |
+
cx = float(vx_expr)
|
| 210 |
+
except Exception:
|
| 211 |
+
cx = 0.0
|
| 212 |
+
try:
|
| 213 |
+
cy = float(vy_expr)
|
| 214 |
+
except Exception:
|
| 215 |
+
cy = 0.0
|
| 216 |
+
try:
|
| 217 |
+
cz = float(vz_expr)
|
| 218 |
+
except Exception:
|
| 219 |
+
cz = 0.0
|
| 220 |
+
return (
|
| 221 |
+
lambda x, y, z, _cx=cx: _cx,
|
| 222 |
+
lambda x, y, z, _cy=cy: _cy,
|
| 223 |
+
lambda x, y, z, _cz=cz: _cz,
|
| 224 |
+
)
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
# --------------------------------------------------------------------------------------
|
| 231 |
+
# Simulation Controller
|
| 232 |
+
# --------------------------------------------------------------------------------------
|
| 233 |
def run_simulation():
|
| 234 |
+
if state.is_running:
|
| 235 |
+
return
|
| 236 |
+
state.is_running = True
|
| 237 |
+
state.run_error = ""
|
| 238 |
+
state.download_ready = False
|
| 239 |
+
state.download_path = None
|
| 240 |
+
try:
|
| 241 |
+
# Determine qubits from grid size
|
| 242 |
+
num_reg_qubits = int(math.log2(state.grid_size)) if state.grid_size and state.grid_size > 0 else 3
|
| 243 |
+
fig = None
|
| 244 |
+
frames = []
|
| 245 |
+
try:
|
| 246 |
+
# Lazy import like trial.py (single-process behavior)
|
| 247 |
+
fluid = importlib.import_module('fluid')
|
| 248 |
+
# Build constant velocity functions to avoid SymPy in critical path
|
| 249 |
+
def _const(s, d=0.0):
|
| 250 |
+
try:
|
| 251 |
+
return float(s)
|
| 252 |
+
except Exception:
|
| 253 |
+
return d
|
| 254 |
+
cx, cy, cz = _const(state.vx_expr), _const(state.vy_expr), _const(state.vz_expr)
|
| 255 |
+
vx = lambda x, y, z, _cx=cx: _cx
|
| 256 |
+
vy = lambda x, y, z, _cy=cy: _cy
|
| 257 |
+
vz = lambda x, y, z, _cz=cz: _cz
|
| 258 |
+
fig, frames = fluid.simulate_qlbm_3D_and_animate(
|
| 259 |
+
num_reg_qubits=num_reg_qubits,
|
| 260 |
+
T=state.time_steps,
|
| 261 |
+
distribution_type=state.distribution_type,
|
| 262 |
+
vx_input=vx,
|
| 263 |
+
vy_input=vy,
|
| 264 |
+
vz_input=vz,
|
| 265 |
+
boundary_condition=state.boundary_condition,
|
| 266 |
+
)
|
| 267 |
+
except Exception as e:
|
| 268 |
+
state.run_error = f"Simulation failed: {e}. Falling back to mock visualization."
|
| 269 |
+
if fig is None:
|
| 270 |
+
fig, frames = _mock_simulation(
|
| 271 |
+
num_reg_qubits=num_reg_qubits,
|
| 272 |
+
time_steps=state.time_steps,
|
| 273 |
+
distribution_type=state.distribution_type,
|
| 274 |
+
vx=state.vx_expr,
|
| 275 |
+
vy=state.vy_expr,
|
| 276 |
+
vz=state.vz_expr,
|
| 277 |
+
boundary_condition=state.boundary_condition,
|
| 278 |
+
)
|
| 279 |
+
if fig is None:
|
| 280 |
+
state.run_error = "Simulation returned no figure. Check GPU/driver and parameters."
|
| 281 |
+
return
|
| 282 |
+
if hasattr(ctrl, "sim_plot_update"):
|
| 283 |
+
ctrl.sim_plot_update(fig)
|
| 284 |
+
# Prepare frames for download (if provided)
|
| 285 |
+
state.has_frames = bool(frames)
|
| 286 |
+
state._frames_cache = frames or [] # private cache
|
| 287 |
+
# Enable download button
|
| 288 |
+
state.download_ready = bool(frames)
|
| 289 |
+
except Exception as e:
|
| 290 |
+
state.run_error = f"Simulation failed: {e}"
|
| 291 |
+
finally:
|
| 292 |
+
state.is_running = False
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
def download_frames():
|
| 296 |
+
if not state.has_frames:
|
| 297 |
+
return
|
| 298 |
+
path = create_zip_from_frames(getattr(state, "_frames_cache", []))
|
| 299 |
+
if path:
|
| 300 |
+
state.download_path = path
|
| 301 |
+
# Trigger file download
|
| 302 |
+
return server.file(path, "plotly_frames.zip")
|
| 303 |
+
|
| 304 |
+
|
| 305 |
+
def download_sinusoidal_stl():
|
| 306 |
+
path = os.path.abspath(os.path.join(os.path.dirname(__file__), "Placeholder_Images", "sinusoidal_surface.stl"))
|
| 307 |
+
if os.path.exists(path):
|
| 308 |
+
return server.file(path, filename="sinusoidal_surface.stl")
|
| 309 |
+
|
| 310 |
+
|
| 311 |
+
def download_gaussian_stl():
|
| 312 |
+
path = os.path.abspath(os.path.join(os.path.dirname(__file__), "Placeholder_Images", "gaussian_surface.stl"))
|
| 313 |
+
if os.path.exists(path):
|
| 314 |
+
return server.file(path, filename="gaussian_surface.stl")
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
def set_example(dist_type):
|
| 318 |
+
"""Load an example initial distribution configuration."""
|
| 319 |
+
state.grid_size = 32
|
| 320 |
+
state.time_steps = 100
|
| 321 |
+
state.distribution_type = dist_type
|
| 322 |
+
state.vx_expr = "0.2"
|
| 323 |
+
state.vy_expr = "-0.15"
|
| 324 |
+
state.vz_expr = "0.3"
|
| 325 |
+
state.boundary_condition = "Periodic"
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
def set_velocity_preset(preset_name):
|
| 329 |
+
"""Map velocity preset buttons to expression triplets."""
|
| 330 |
+
mapping = {
|
| 331 |
+
"Uniform": ("0.2", "-0.15", "0.3"),
|
| 332 |
+
"Swirl": ("0.3*sin(-2*pi*z)", "0.2", "0.3*sin(2*pi*x)"),
|
| 333 |
+
"Shear": ("abs(z-0.5)*1.2-0.3", "0", "0"),
|
| 334 |
+
"TGV": ("0.15*cos(2*pi*x)*sin(2*pi*y)*sin(2*pi*z)", "-0.3*sin(2*pi*x)*cos(2*pi*y)*sin(2*pi*z)", "0.15*sin(2*pi*x)*sin(2*pi*y)*cos(2*pi*z)"),
|
| 335 |
+
}
|
| 336 |
+
vx, vy, vz = mapping.get(preset_name, mapping["Uniform"])
|
| 337 |
+
state.advecting_field = preset_name
|
| 338 |
+
state.vx_expr = vx
|
| 339 |
+
state.vy_expr = vy
|
| 340 |
+
state.vz_expr = vz
|
| 341 |
+
|
| 342 |
+
@state.change("advecting_field")
|
| 343 |
+
def _on_advect_dropdown_change(advecting_field, **_):
|
| 344 |
+
if advecting_field:
|
| 345 |
+
set_velocity_preset(advecting_field)
|
| 346 |
+
|
| 347 |
+
|
| 348 |
+
# --------------------------------------------------------------------------------------
|
| 349 |
+
# Reactive updates
|
| 350 |
+
# --------------------------------------------------------------------------------------
|
| 351 |
+
@state.change("grid_size")
|
| 352 |
+
def _on_grid_size_change(grid_size, **_):
|
| 353 |
+
try:
|
| 354 |
+
# Restrict to powers of two in [8, 16, 32, 64, 128, 256]
|
| 355 |
+
allowed = [8, 16, 32, 64, 128, 256]
|
| 356 |
+
if grid_size not in allowed:
|
| 357 |
+
# snap to nearest allowed value
|
| 358 |
+
nearest = min(allowed, key=lambda v: abs(v - (grid_size or 0)))
|
| 359 |
+
state.grid_size = nearest
|
| 360 |
+
return
|
| 361 |
+
# Throttle frequent updates while dragging
|
| 362 |
+
global _last_qubit_update
|
| 363 |
+
now = time.time()
|
| 364 |
+
if (now - _last_qubit_update) < _qubit_update_min_interval:
|
| 365 |
+
return
|
| 366 |
+
_last_qubit_update = now
|
| 367 |
+
fig, info, warn = update_qubit_3D_info(grid_size)
|
| 368 |
+
state.qubit_grid_info = info
|
| 369 |
+
state.qubit_warning = warn
|
| 370 |
+
if hasattr(ctrl, "qubit_plot_update"):
|
| 371 |
+
ctrl.qubit_plot_update(fig)
|
| 372 |
+
except Exception:
|
| 373 |
+
pass
|
| 374 |
+
|
| 375 |
+
|
| 376 |
+
@state.change("boundary_condition")
|
| 377 |
+
def _force_periodic(boundary_condition, **_):
|
| 378 |
+
"""Ensure boundary condition remains 'Periodic' regardless of user selection."""
|
| 379 |
+
try:
|
| 380 |
+
if boundary_condition != "Periodic":
|
| 381 |
+
state.boundary_condition = "Periodic"
|
| 382 |
+
except Exception:
|
| 383 |
+
state.boundary_condition = "Periodic"
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
@state.change("problems_selection")
|
| 387 |
+
def _on_problem_selection_change(problems_selection, **_):
|
| 388 |
+
"""Auto-select geometry based on the chosen problem and show read-only."""
|
| 389 |
+
try:
|
| 390 |
+
if not problems_selection:
|
| 391 |
+
state.geometry_selection = None
|
| 392 |
+
return
|
| 393 |
+
if isinstance(problems_selection, str) and problems_selection.startswith("1."):
|
| 394 |
+
# Scalar advection-diffusion in a box
|
| 395 |
+
state.geometry_selection = "Cube"
|
| 396 |
+
elif isinstance(problems_selection, str) and problems_selection.startswith("2."):
|
| 397 |
+
# Laminar flow & heat transfer for a heated body in water.
|
| 398 |
+
state.geometry_selection = "Rectangular domain with a heated box (3D)"
|
| 399 |
+
else:
|
| 400 |
+
state.geometry_selection = None
|
| 401 |
+
except Exception:
|
| 402 |
+
state.geometry_selection = None
|
| 403 |
+
|
| 404 |
+
|
| 405 |
+
@state.change("grid_index")
|
| 406 |
+
def _on_grid_index_change(grid_index, **_):
|
| 407 |
+
"""Map discrete slider index to allowed grid sizes [8,16,32,64,128,256]."""
|
| 408 |
+
try:
|
| 409 |
+
allowed = [8, 16, 32, 64, 128, 256]
|
| 410 |
+
if grid_index is None:
|
| 411 |
+
return
|
| 412 |
+
if isinstance(grid_index, (int, float)):
|
| 413 |
+
idx = int(grid_index)
|
| 414 |
+
idx = max(0, min(idx, len(allowed) - 1))
|
| 415 |
+
val = allowed[idx]
|
| 416 |
+
if state.grid_size != val:
|
| 417 |
+
state.grid_size = val
|
| 418 |
+
except Exception:
|
| 419 |
+
pass
|
| 420 |
+
|
| 421 |
+
|
| 422 |
+
# --------------------------------------------------------------------------------------
|
| 423 |
+
# UI Construction
|
| 424 |
+
# --------------------------------------------------------------------------------------
|
| 425 |
with SinglePageLayout(server) as layout:
|
| 426 |
+
# Remove top toolbar/title per request
|
| 427 |
+
layout.toolbar.style = "display:none" # Hide toolbar entirely
|
| 428 |
+
|
| 429 |
+
trame_html.Style("""
|
| 430 |
+
:root{ --v-theme-primary:95,37,159; }
|
| 431 |
+
.example-img{ max-width:100%; border-radius:4px; }
|
| 432 |
+
.warn-text{ color:#b71c1c; font-size:0.85rem; }
|
| 433 |
+
""")
|
| 434 |
+
|
| 435 |
+
# No toolbar content
|
| 436 |
+
|
| 437 |
+
with layout.content:
|
| 438 |
+
with vuetify3.VContainer(fluid=True, classes="pa-4"):
|
| 439 |
+
# Row 1: Overview + Geometry + Examples (left) and Visualization (right)
|
| 440 |
+
with vuetify3.VRow(classes="mb-4"):
|
| 441 |
+
# Left column (Overview, Geometry, Examples)
|
| 442 |
+
with vuetify3.VCol(cols=5):
|
| 443 |
+
# Overview cell (from qlbm)
|
| 444 |
+
with vuetify3.VCard(classes="mb-4"):
|
| 445 |
+
vuetify3.VCardTitle("Overview", classes="text-h5 font-weight-bold text-primary")
|
| 446 |
+
with vuetify3.VCardText():
|
| 447 |
+
vuetify3.VDivider(classes="my-2")
|
| 448 |
+
vuetify3.VCardSubtitle("Problems", classes="text-subtitle-1 font-weight-bold mt-2")
|
| 449 |
+
vuetify3.VSelect(
|
| 450 |
+
key="overview_problems",
|
| 451 |
+
label="Select a problem",
|
| 452 |
+
v_model=("problems_selection", None),
|
| 453 |
+
items=(
|
| 454 |
+
"qlbm_problems",
|
| 455 |
+
[
|
| 456 |
+
"1. Scalar advection-diffusion in a box",
|
| 457 |
+
"2. Laminar flow & heat transfer for a heated body in water.",
|
| 458 |
+
],
|
| 459 |
+
),
|
| 460 |
+
placeholder="Select",
|
| 461 |
+
density="compact",
|
| 462 |
+
color="primary",
|
| 463 |
+
)
|
| 464 |
+
vuetify3.VCardSubtitle("Governing Equations", classes="text-subtitle-1 font-weight-bold mt-2")
|
| 465 |
+
vuetify3.VListItemTitle("Laminar Navier-Stokes including energy", classes="text-body-2")
|
| 466 |
+
vuetify3.VCardSubtitle("Inputs", classes="text-subtitle-1 font-weight-bold mt-2")
|
| 467 |
+
vuetify3.VListItemTitle("Geometry, Boundary conditions - temperature and flow", classes="text-body-2")
|
| 468 |
+
vuetify3.VCardSubtitle("Outputs", classes="text-subtitle-1 font-weight-bold mt-2")
|
| 469 |
+
vuetify3.VListItemTitle("Surface plots on sections OR sampling through a line in 3D domain", classes="text-body-2")
|
| 470 |
+
|
| 471 |
+
# Geometry cell
|
| 472 |
+
with vuetify3.VCard(classes="mb-4"):
|
| 473 |
+
vuetify3.VCardTitle("Geometry", classes="text-primary")
|
| 474 |
+
with vuetify3.VCardText():
|
| 475 |
+
# Read-only geometry display auto-set by problem selection
|
| 476 |
+
vuetify3.VAlert(
|
| 477 |
+
v_if="geometry_selection",
|
| 478 |
+
type="info",
|
| 479 |
+
variant="tonal",
|
| 480 |
+
density="compact",
|
| 481 |
+
color="primary",
|
| 482 |
+
children=["Selected Geometry: ", "{{ geometry_selection }}"],
|
| 483 |
+
)
|
| 484 |
+
vuetify3.VAlert(
|
| 485 |
+
v_if="!geometry_selection",
|
| 486 |
+
type="info",
|
| 487 |
+
variant="tonal",
|
| 488 |
+
density="compact",
|
| 489 |
+
color="primary",
|
| 490 |
+
children=["No geometry selected. Choose a problem to auto-set."],
|
| 491 |
+
)
|
| 492 |
+
with vuetify3.VContainer(v_if="geometry_selection === 'Rectangular domain with a heated box (3D)'", classes="pa-0 mt-4"):
|
| 493 |
+
vuetify3.VCardSubtitle("Domain dimensions", classes="text-subtitle-1 font-weight-bold mb-2")
|
| 494 |
+
with vuetify3.VRow(dense=True):
|
| 495 |
+
vuetify3.VCol(children=[vuetify3.VTextField(label="Length (L)", v_model=("domain_L", 1.0), type="number", step="0.1", density="compact", color="primary")])
|
| 496 |
+
vuetify3.VCol(children=[vuetify3.VTextField(label="Width (W)", v_model=("domain_W", 1.0), type="number", step="0.1", density="compact", color="primary")])
|
| 497 |
+
vuetify3.VCol(children=[vuetify3.VTextField(label="Height (H)", v_model=("domain_H", 1.0), type="number", step="0.1", density="compact", color="primary")])
|
| 498 |
+
|
| 499 |
+
# Initial Distribution Examples card removed per latest UI request
|
| 500 |
+
|
| 501 |
+
# (Simulation Parameters: moved here to avoid vertical gap)
|
| 502 |
+
|
| 503 |
+
# Combined Initial & Boundary Conditions
|
| 504 |
+
with vuetify3.VCard(classes="mb-4"):
|
| 505 |
+
vuetify3.VCardTitle("Initial & Boundary Conditions", classes="text-primary")
|
| 506 |
+
with vuetify3.VCardText():
|
| 507 |
+
vuetify3.VSelect(label="Initial Distribution Type", v_model=("distribution_type", "Sinusoidal"), items=("dist_types", ["Gaussian", "Sinusoidal"]), density="compact", color="primary")
|
| 508 |
+
# Boundary condition restricted to Periodic only
|
| 509 |
+
vuetify3.VSelect(label="Boundary Condition", v_model=("boundary_condition", "Periodic"), items=("bc_types", ["Periodic"]), density="compact", color="primary", classes="mt-2")
|
| 510 |
+
|
| 511 |
+
# Advecting Fields card
|
| 512 |
+
with vuetify3.VCard(classes="mb-4"):
|
| 513 |
+
vuetify3.VCardTitle("Advecting Fields", classes="text-primary")
|
| 514 |
+
with vuetify3.VCardText():
|
| 515 |
+
vuetify3.VSelect(
|
| 516 |
+
label="Select Advecting field",
|
| 517 |
+
v_model=("advecting_field", "Uniform"),
|
| 518 |
+
items=("adv_presets", ["Uniform", "Swirl", "Shear", "TGV"]),
|
| 519 |
+
density="compact",
|
| 520 |
+
color="primary"
|
| 521 |
+
)
|
| 522 |
+
trame_html.Div("Velocity Components", classes="text-body-2 mt-4")
|
| 523 |
+
vuetify3.VTextField(label="Velocity vx", v_model=("vx_expr", "0.2"), density="compact", color="primary")
|
| 524 |
+
vuetify3.VTextField(label="Velocity vy", v_model=("vy_expr", "-0.15"), density="compact", color="primary")
|
| 525 |
+
vuetify3.VTextField(label="Velocity vz", v_model=("vz_expr", "0.3"), density="compact", color="primary")
|
| 526 |
+
|
| 527 |
+
# Meshing
|
| 528 |
+
with vuetify3.VCard(classes="mb-4"):
|
| 529 |
+
vuetify3.VCardTitle("Meshing", classes="text-primary")
|
| 530 |
+
with vuetify3.VCardText():
|
| 531 |
+
with vuetify3.VMenu(open_on_hover=True, close_on_content_click=False, location="end"):
|
| 532 |
+
with vuetify3.Template(v_slot_activator="{ props }"):
|
| 533 |
+
with vuetify3.VSlider(
|
| 534 |
+
v_bind="props",
|
| 535 |
+
label="Number of Points / Direction",
|
| 536 |
+
v_model=("grid_index", 2),
|
| 537 |
+
min=0, max=5, step=1,
|
| 538 |
+
thumb_label="always",
|
| 539 |
+
show_ticks="always",
|
| 540 |
+
color="primary",
|
| 541 |
+
density="compact"
|
| 542 |
+
):
|
| 543 |
+
# Map index -> power-of-two label
|
| 544 |
+
vuetify3.Template(v_slot_thumb_label="{ modelValue }", children=["{{ ['8','16','32','64','128','256'][modelValue] }}"])
|
| 545 |
+
with vuetify3.VSheet(classes="pa-2", elevation=6, rounded=True, style="width: 700px;"):
|
| 546 |
+
with vuetify3.VContainer(fluid=True, classes="pa-0"):
|
| 547 |
+
qubit_fig = plotly_widgets.Figure(figure=go.Figure(), style="width: 616px; height: 320px; min-height: 320px;", responsive=True)
|
| 548 |
+
trame_html.Div("{{ qubit_grid_info }}", classes="mt-2 text-caption")
|
| 549 |
+
trame_html.Div("{{ qubit_warning }}", classes="warn-text")
|
| 550 |
+
vuetify3.VAlert(v_if="grid_size > 32", type="warning", variant="tonal", density="compact", children=["Warning: High grid size may impact performance."], classes="mt-2")
|
| 551 |
+
|
| 552 |
+
# Time
|
| 553 |
+
with vuetify3.VCard(classes="mb-4"):
|
| 554 |
+
vuetify3.VCardTitle("Time", classes="text-primary")
|
| 555 |
+
with vuetify3.VCardText():
|
| 556 |
+
vuetify3.VSlider(label="Time Steps", v_model=("time_steps", 100), min=0, max=2000, step=10, thumb_label="always", show_ticks="always", color="primary", density="compact")
|
| 557 |
+
vuetify3.VAlert(v_if="time_steps > 100", type="warning", variant="tonal", density="compact", children=["Warning: High time steps may increase runtime."], classes="mt-2")
|
| 558 |
+
|
| 559 |
+
# Backends cell (from qlbm) with Run button beneath
|
| 560 |
+
with vuetify3.VCard(classes="mb-4"):
|
| 561 |
+
vuetify3.VCardTitle("Backends", classes="text-primary")
|
| 562 |
+
with vuetify3.VCardText():
|
| 563 |
+
with vuetify3.VRow(dense=True, classes="mb-2"):
|
| 564 |
+
with vuetify3.VCol():
|
| 565 |
+
vuetify3.VAlert(
|
| 566 |
+
type="info",
|
| 567 |
+
color="primary",
|
| 568 |
+
variant="tonal",
|
| 569 |
+
density="compact",
|
| 570 |
+
children=[
|
| 571 |
+
"Selected: ",
|
| 572 |
+
"{{ backend_type || '—' }}",
|
| 573 |
+
" - ",
|
| 574 |
+
"{{ backend_type === 'Simulator' ? selected_simulator : (backend_type === 'QPU' ? selected_qpu : '—') }}",
|
| 575 |
+
],
|
| 576 |
+
)
|
| 577 |
+
with vuetify3.VMenu(open_on_hover=True, close_on_content_click=True, location="end"):
|
| 578 |
+
with vuetify3.Template(v_slot_activator="{ props }"):
|
| 579 |
+
vuetify3.VBtn(v_bind="props", text="Choose Backend", color="primary", variant="tonal", block=True)
|
| 580 |
+
with vuetify3.VList(density="compact"):
|
| 581 |
+
with vuetify3.VMenu(open_on_hover=True, close_on_content_click=True, location="end", offset=8):
|
| 582 |
+
with vuetify3.Template(v_slot_activator="{ props }"):
|
| 583 |
+
vuetify3.VListItem(v_bind="props", title="Simulator", prepend_icon="mdi-robot-outline", append_icon="mdi-chevron-right")
|
| 584 |
+
with vuetify3.VList(density="compact"):
|
| 585 |
+
vuetify3.VListItem(title="IBM Qiskit simulator", click="backend_type = 'Simulator'; selected_simulator = 'IBM Qiskit simulator'")
|
| 586 |
+
vuetify3.VListItem(title="IonQ simulator", click="backend_type = 'Simulator'; selected_simulator = 'IonQ simulator'")
|
| 587 |
+
with vuetify3.VMenu(open_on_hover=True, close_on_content_click=True, location="end", offset=8):
|
| 588 |
+
with vuetify3.Template(v_slot_activator="{ props }"):
|
| 589 |
+
vuetify3.VListItem(v_bind="props", title="QPU", prepend_icon="mdi-chip", append_icon="mdi-chevron-right")
|
| 590 |
+
with vuetify3.VList(density="compact"):
|
| 591 |
+
vuetify3.VListItem(title="IBM QPU", click="backend_type = 'QPU'; selected_qpu = 'IBM QPU'")
|
| 592 |
+
vuetify3.VListItem(title="IonQ QPU", click="backend_type = 'QPU'; selected_qpu = 'IonQ QPU'")
|
| 593 |
+
vuetify3.VDivider(classes="my-3")
|
| 594 |
+
vuetify3.VBtn(text="Run Simulation", color="primary", block=True, disabled=("is_running", False), click=run_simulation)
|
| 595 |
+
|
| 596 |
+
# Right column (Visualization)
|
| 597 |
+
with vuetify3.VCol(cols=7):
|
| 598 |
+
with vuetify3.VCard(classes="fill-height"):
|
| 599 |
+
vuetify3.VCardTitle("QLBM 3D Visualization", classes="text-primary")
|
| 600 |
+
with vuetify3.VCardText(classes="pb-2", style="min-height:760px;display:flex;flex-direction:column;"):
|
| 601 |
+
vuetify3.VProgressCircular(v_if="is_running", indeterminate=True, color="primary", size=64, classes="my-4 mx-auto d-flex")
|
| 602 |
+
sim_fig = plotly_widgets.Figure(figure=go.Figure(), style="width:100%;height:700px;", responsive=True)
|
| 603 |
+
vuetify3.VAlert(v_if="run_error", type="error", variant="tonal", density="compact", children=["{{ run_error }}"], classes="mt-2")
|
| 604 |
+
# Removed Download Plot Data button per request
|
| 605 |
+
|
| 606 |
+
# Row 2 removed to keep left panel contiguous with Geometry (no vertical gap)
|
| 607 |
+
|
| 608 |
+
# Controller hooks for plot updates
|
| 609 |
+
ctrl.qubit_plot_update = qubit_fig.update
|
| 610 |
+
ctrl.sim_plot_update = sim_fig.update
|
| 611 |
+
|
| 612 |
+
# --------------------------------------------------------------------------------------
|
| 613 |
+
# Embedding helper
|
| 614 |
+
# --------------------------------------------------------------------------------------
|
| 615 |
+
_started = False
|
| 616 |
+
|
| 617 |
+
def build(host_server):
|
| 618 |
+
"""Embed this app into another Trame server via an iframe, auto-starting its own server."""
|
| 619 |
+
global _started
|
| 620 |
+
if not _started:
|
| 621 |
+
import threading
|
| 622 |
+
def _run():
|
| 623 |
+
try:
|
| 624 |
+
port = int(os.environ.get("TRAME3D_PORT", "8811"))
|
| 625 |
+
server.start(host="0.0.0.0", port=port, open_browser=False)
|
| 626 |
+
except Exception as e:
|
| 627 |
+
print(f"trame3d server failed to start: {e}")
|
| 628 |
+
threading.Thread(target=_run, daemon=True).start()
|
| 629 |
+
_started = True
|
| 630 |
+
h_state = host_server.state
|
| 631 |
+
default_port = os.environ.get("TRAME3D_PORT", "8811")
|
| 632 |
+
if not getattr(h_state, "trame3d_iframe_src", None):
|
| 633 |
+
h_state.trame3d_iframe_src = f"http://localhost:{default_port}/"
|
| 634 |
+
with vuetify3.VContainer(fluid=True, classes="pa-0 fill-height"):
|
| 635 |
+
trame_html.Iframe(src=("trame3d_iframe_src", f"http://localhost:{default_port}/"), style="border:0; width:100%; height: calc(100vh - 64px);")
|
| 636 |
+
trame_html.Div("If the 3D QLBM view is blank, wait a few seconds or verify TRAME3D_PORT.", style="color: rgba(0,0,0,.6); padding: 6px;")
|
| 637 |
+
|
| 638 |
+
# Controller hooks for plot updates
|
| 639 |
+
ctrl.qubit_plot_update = qubit_fig.update
|
| 640 |
+
ctrl.sim_plot_update = sim_fig.update
|
| 641 |
+
|
| 642 |
+
# --------------------------------------------------------------------------------------
|
| 643 |
+
# Embedding helper
|
| 644 |
+
# --------------------------------------------------------------------------------------
|
| 645 |
+
_started = False
|
| 646 |
|
| 647 |
def build(host_server):
|
| 648 |
+
"""Embed this app into another Trame server via an iframe, auto-starting its own server."""
|
| 649 |
+
global _started
|
| 650 |
+
if not _started:
|
| 651 |
+
import threading
|
| 652 |
+
def _run():
|
| 653 |
+
try:
|
| 654 |
+
port = int(os.environ.get("TRAME3D_PORT", "8811"))
|
| 655 |
+
server.start(host="0.0.0.0", port=port, open_browser=False)
|
| 656 |
+
except Exception as e:
|
| 657 |
+
print(f"trame3d server failed to start: {e}")
|
| 658 |
+
threading.Thread(target=_run, daemon=True).start()
|
| 659 |
+
_started = True
|
| 660 |
+
h_state = host_server.state
|
| 661 |
+
default_port = os.environ.get("TRAME3D_PORT", "8811")
|
| 662 |
+
if not getattr(h_state, "trame3d_iframe_src", None):
|
| 663 |
+
h_state.trame3d_iframe_src = f"http://localhost:{default_port}/"
|
| 664 |
+
with vuetify3.VContainer(fluid=True, classes="pa-0 fill-height"):
|
| 665 |
+
trame_html.Iframe(src=("trame3d_iframe_src", f"http://localhost:{default_port}/"), style="border:0; width:100%; height: calc(100vh - 64px);")
|
| 666 |
+
trame_html.Div("If the 3D QLBM view is blank, wait a few seconds or verify TRAME3D_PORT.", style="color: rgba(0,0,0,.6); padding: 6px;")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 667 |
|
| 668 |
# --- Entry point ---
|
| 669 |
if __name__ == "__main__":
|
utils/__pycache__/base_functions.cpython-310.pyc
ADDED
|
Binary file (9.58 kB). View file
|
|
|
utils/__pycache__/delta_impulse_generator.cpython-310.pyc
ADDED
|
Binary file (13.8 kB). View file
|
|
|
utils/base_ionq.py
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import scipy.sparse as sp
|
| 3 |
+
import math
|
| 4 |
+
import random
|
| 5 |
+
import matplotlib.pyplot as plt
|
| 6 |
+
from scipy.special import jn
|
| 7 |
+
from scipy.sparse import identity, csr_matrix, kron, diags, eye
|
| 8 |
+
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
|
| 9 |
+
from qiskit.circuit.library import MCXGate, MCPhaseGate, RXGate, CRXGate, QFTGate, StatePreparation, PauliEvolutionGate, RZGate
|
| 10 |
+
from qiskit.quantum_info import SparsePauliOp, Statevector, Operator, Pauli
|
| 11 |
+
from scipy.linalg import expm
|
| 12 |
+
# from tools import *
|
| 13 |
+
from qiskit.qasm3 import dumps # QASM 3 exporter
|
| 14 |
+
from qiskit.qasm3 import loads
|
| 15 |
+
from qiskit.circuit.library import QFT
|
| 16 |
+
from qiskit.primitives import StatevectorEstimator
|
| 17 |
+
from qiskit import transpile
|
| 18 |
+
from qiskit_addon_aqc_tensor.simulation import tensornetwork_from_circuit, apply_circuit_to_state, compute_overlap
|
| 19 |
+
from qiskit_aer import AerSimulator
|
| 20 |
+
from qiskit_ionq import IonQProvider
|
| 21 |
+
import os
|
| 22 |
+
my_api_key = os.getenv("IONQ_API_KEY")
|
| 23 |
+
from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
# provider = IonQProvider()
|
| 29 |
+
|
| 30 |
+
api_token = "SgUkiDq1r2bVEadyiUfvtuxQ03Qci6UW"
|
| 31 |
+
provider = IonQProvider(api_token)
|
| 32 |
+
ionq_backend = provider.get_backend("simulator")
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
simulator_settings = AerSimulator(
|
| 38 |
+
method="matrix_product_state",
|
| 39 |
+
matrix_product_state_max_bond_dimension=100,
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
def Wj(j, theta, lam, name='Wj', xgate=False):
|
| 43 |
+
if not xgate:
|
| 44 |
+
name = f' $W_{j}$ '
|
| 45 |
+
qc=QuantumCircuit(j, name=name)
|
| 46 |
+
|
| 47 |
+
if j > 1:
|
| 48 |
+
qc.cx(j-1, range(j-1))
|
| 49 |
+
if lam != 0:
|
| 50 |
+
qc.p(lam, j-1)
|
| 51 |
+
qc.h(j-1)
|
| 52 |
+
if xgate:
|
| 53 |
+
qc.x(range(j-1))
|
| 54 |
+
|
| 55 |
+
# the multicontrolled rz gate
|
| 56 |
+
# it will be decomposed in qiskit
|
| 57 |
+
if j > 1:
|
| 58 |
+
qc.mcrz(theta, range(j-1), j-1)
|
| 59 |
+
else:
|
| 60 |
+
qc.rz(theta, j-1)
|
| 61 |
+
|
| 62 |
+
if xgate:
|
| 63 |
+
qc.x(range(j-1))
|
| 64 |
+
qc.h(j-1)
|
| 65 |
+
if lam != 0:
|
| 66 |
+
qc.p(-lam, j-1)
|
| 67 |
+
if j > 1:
|
| 68 |
+
qc.cx(j-1, range(j-1))
|
| 69 |
+
|
| 70 |
+
return qc
|
| 71 |
+
|
| 72 |
+
def Wj_block(j, n, ctrl_state, theta, lam, name='Wj_block', xgate=False):
|
| 73 |
+
if not xgate:
|
| 74 |
+
name = f' $W_{j}_block$ '
|
| 75 |
+
qc=QuantumCircuit(n + j, name=name)
|
| 76 |
+
|
| 77 |
+
if j > 1:
|
| 78 |
+
qc.cx(n + j-1, range(n, n+j-1))
|
| 79 |
+
if lam != 0:
|
| 80 |
+
qc.p(lam, n + j -1)
|
| 81 |
+
qc.h(n + j -1)
|
| 82 |
+
|
| 83 |
+
if xgate and j>1:
|
| 84 |
+
if isinstance(xgate, (list, tuple)): # selective application
|
| 85 |
+
for idx, flag in enumerate(xgate):
|
| 86 |
+
if flag: # only apply where flag == 1
|
| 87 |
+
qc.x(n + idx)
|
| 88 |
+
elif xgate is True: # apply to all
|
| 89 |
+
qc.x(range(n, n+j-1))
|
| 90 |
+
|
| 91 |
+
# the multicontrolled rz gate
|
| 92 |
+
# it will be decomposed in qiskit
|
| 93 |
+
if j > 1:
|
| 94 |
+
mcrz = RZGate(theta).control(len(ctrl_state) + j-1, ctrl_state = "1"*(j-1)+ctrl_state)
|
| 95 |
+
qc.append(mcrz, range(0, n + j))
|
| 96 |
+
else:
|
| 97 |
+
mcrz = RZGate(theta).control(len(ctrl_state), ctrl_state = ctrl_state)
|
| 98 |
+
qc.append(mcrz, range(0, n+j))
|
| 99 |
+
|
| 100 |
+
if xgate and j>1:
|
| 101 |
+
if isinstance(xgate, (list, tuple)): # selective application
|
| 102 |
+
for idx, flag in enumerate(xgate):
|
| 103 |
+
if flag: # only apply where flag == 1
|
| 104 |
+
qc.x(n + idx)
|
| 105 |
+
elif xgate is True: # apply to all
|
| 106 |
+
qc.x(range(n, n+j-1))
|
| 107 |
+
|
| 108 |
+
qc.h(n+ j-1)
|
| 109 |
+
if lam != 0:
|
| 110 |
+
qc.p(-lam, n + j-1)
|
| 111 |
+
if j > 1:
|
| 112 |
+
qc.cx(n + j-1, range(n, n +j-1))
|
| 113 |
+
|
| 114 |
+
return qc.to_gate(label=name)
|
| 115 |
+
|
| 116 |
+
def V1(nx, dt, name = "V1"):
|
| 117 |
+
n = int(np.ceil(np.log2(nx)))
|
| 118 |
+
|
| 119 |
+
derivatives = QuantumRegister(2*n)
|
| 120 |
+
blocks = QuantumRegister(2)
|
| 121 |
+
|
| 122 |
+
qc = QuantumCircuit(derivatives, blocks)
|
| 123 |
+
|
| 124 |
+
W1 = Wj_block(2, n, "0"*n, -dt , 0, xgate=True)
|
| 125 |
+
qc.append(W1, list(derivatives[0:n])+list(blocks[:]))
|
| 126 |
+
|
| 127 |
+
# qc.barrier()
|
| 128 |
+
|
| 129 |
+
W2 = Wj_block(3, n-1, "1"*(n-1), dt , 0, xgate=[0,1])
|
| 130 |
+
qc.append(W2, list(derivatives[1:n])+[derivatives[0]]+list(blocks[:]))
|
| 131 |
+
|
| 132 |
+
# qc.barrier()
|
| 133 |
+
|
| 134 |
+
W3 = Wj_block(1, n+1, "0"*(n+1), dt , 0, xgate=False)
|
| 135 |
+
qc.append(W3, list(derivatives[n:2*n])+list(blocks[:]))
|
| 136 |
+
|
| 137 |
+
# qc.barrier()
|
| 138 |
+
|
| 139 |
+
W4 = Wj_block(2, n, "0"+"1"*(n-1), -dt , 0, xgate=False)
|
| 140 |
+
qc.append(W4, list(derivatives[n+1:2*n]) + [blocks[0]] + [derivatives[n]] + [blocks[1]])
|
| 141 |
+
|
| 142 |
+
return qc
|
| 143 |
+
|
| 144 |
+
def V2(nx, dt, name = "V2"):
|
| 145 |
+
n = int(np.ceil(np.log2(nx)))
|
| 146 |
+
|
| 147 |
+
derivatives = QuantumRegister(2*n)
|
| 148 |
+
blocks = QuantumRegister(2)
|
| 149 |
+
|
| 150 |
+
qc = QuantumCircuit(derivatives, blocks)
|
| 151 |
+
|
| 152 |
+
W1 = Wj_block(2, 0, "", -2*dt , -np.pi/2, xgate=True)
|
| 153 |
+
qc.append(W1, list(blocks[:]))
|
| 154 |
+
|
| 155 |
+
# qc.barrier()
|
| 156 |
+
|
| 157 |
+
for j in range(1, n+1):
|
| 158 |
+
W2 = Wj_block(2+j, 0, "", 2*dt , -np.pi/2, xgate=[1]*(j-1)+[0,1])
|
| 159 |
+
qc.append(W2, list(derivatives[0:j])+list(blocks[:]))
|
| 160 |
+
|
| 161 |
+
# qc.barrier()
|
| 162 |
+
|
| 163 |
+
W3 = Wj_block(2, n, "0"*n, -dt , -np.pi/2, xgate=True)
|
| 164 |
+
qc.append(W3, list(derivatives[0:n])+list(blocks[:]))
|
| 165 |
+
|
| 166 |
+
# qc.barrier()
|
| 167 |
+
|
| 168 |
+
W4 = Wj_block(2, n, "1"*n, 2*dt , -np.pi/2, xgate=True)
|
| 169 |
+
qc.append(W4, list(derivatives[0:n])+list(blocks[:]))
|
| 170 |
+
|
| 171 |
+
# qc.barrier()
|
| 172 |
+
|
| 173 |
+
W5 = Wj_block(3, n-1, "1"*(n-1), dt , -np.pi/2, xgate=[0,1])
|
| 174 |
+
qc.append(W5, list(derivatives[1:n])+[derivatives[0]]+list(blocks[:]))
|
| 175 |
+
|
| 176 |
+
# qc.barrier()
|
| 177 |
+
|
| 178 |
+
W6 = Wj_block(1, 1, "0", 2*dt , -np.pi/2, xgate=False)
|
| 179 |
+
qc.append(W6, list(blocks[:]))
|
| 180 |
+
|
| 181 |
+
# qc.barrier()
|
| 182 |
+
|
| 183 |
+
for j in range(1, n+1):
|
| 184 |
+
W7 = Wj_block(1+j, 1, "0", -2*dt , -np.pi/2, xgate=[1]*(j-1))
|
| 185 |
+
qc.append(W7, [blocks[0]]+list(derivatives[n:n+j])+[blocks[1]])
|
| 186 |
+
|
| 187 |
+
# qc.barrier()
|
| 188 |
+
|
| 189 |
+
W8 = Wj_block(1, n+1, "0"*(n+1), dt , -np.pi/2, xgate=False)
|
| 190 |
+
qc.append(W8, list(derivatives[n:2*n])+list(blocks[:]))
|
| 191 |
+
|
| 192 |
+
# qc.barrier()
|
| 193 |
+
|
| 194 |
+
W9 = Wj_block(1, n+1, "0"+"1"*(n), -2*dt , -np.pi/2, xgate=False)
|
| 195 |
+
qc.append(W9, list(derivatives[n:2*n])+list(blocks[:]))
|
| 196 |
+
|
| 197 |
+
# qc.barrier()
|
| 198 |
+
|
| 199 |
+
W10 = Wj_block(2, n, "0"+"1"*(n-1), -dt , -np.pi/2, xgate=False)
|
| 200 |
+
qc.append(W10, list(derivatives[n+1:2*n]) + [blocks[0]] + [derivatives[n]] + [blocks[1]])
|
| 201 |
+
|
| 202 |
+
# qc.barrier()
|
| 203 |
+
|
| 204 |
+
return qc
|
| 205 |
+
|
| 206 |
+
def schro(nx, na, R, dt, initial_state, steps):
|
| 207 |
+
|
| 208 |
+
nq = int(np.ceil(np.log2(nx)))
|
| 209 |
+
|
| 210 |
+
# warped phase transformation
|
| 211 |
+
dp = 2 * R * np.pi / 2**na
|
| 212 |
+
p = np.arange(- R * np.pi, R * np.pi, step=dp)
|
| 213 |
+
fp = np.exp(-np.abs(p))
|
| 214 |
+
norm1 = np.linalg.norm(fp[2**(na-1):]) # norm of p>=0
|
| 215 |
+
|
| 216 |
+
# construct quantum circuit
|
| 217 |
+
system = QuantumRegister(2*nq+2, name='system')
|
| 218 |
+
ancilla = QuantumRegister(na, name='ancilla')
|
| 219 |
+
qc = QuantumCircuit(system, ancilla)
|
| 220 |
+
|
| 221 |
+
# initialization
|
| 222 |
+
prep = StatePreparation(initial_state)
|
| 223 |
+
anc_prep = StatePreparation(fp / np.linalg.norm(fp))
|
| 224 |
+
|
| 225 |
+
qc.append(prep, system)
|
| 226 |
+
# qc.append(anc_prep, ancilla)
|
| 227 |
+
qc.initialize(fp / np.linalg.norm(fp), ancilla)
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
# QFT
|
| 231 |
+
qc.append(QFTGate(na), ancilla)
|
| 232 |
+
qc.x(ancilla[-1])
|
| 233 |
+
|
| 234 |
+
A1 = V1(nx, dt, name = "V1").to_gate()
|
| 235 |
+
A2 = V2(nx, dt, name = "V2")
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
# Hamiltonian simulation for Nt steps
|
| 239 |
+
for i in range(steps):
|
| 240 |
+
# circuit for one step
|
| 241 |
+
for j in range(na):
|
| 242 |
+
# repeat controlled H1 for 2**j times
|
| 243 |
+
qc.append(A1.control().repeat(2**j), [ancilla[j]] + system[:])
|
| 244 |
+
|
| 245 |
+
# qc.append(A1.inverse().control(ctrl_state = "0").repeat(2**(na-1)), [ancilla[na-1]] + system[:])
|
| 246 |
+
qc.append(A1.inverse().repeat(2**(na-1)), system[:])
|
| 247 |
+
qc.append(A2, system[:])
|
| 248 |
+
|
| 249 |
+
# rearrange eta
|
| 250 |
+
qc.x(ancilla[-1])
|
| 251 |
+
qc.append(QFTGate(na).inverse(), ancilla)
|
| 252 |
+
|
| 253 |
+
return qc
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
def circ_for_magnitude(field, x, y, nx, na, R, dt, initial_state, steps):
|
| 258 |
+
|
| 259 |
+
qc = schro(nx, na, R, dt, initial_state, steps)
|
| 260 |
+
naimark = QuantumRegister(1, name='Naimark')
|
| 261 |
+
qc.add_register(naimark)
|
| 262 |
+
|
| 263 |
+
if field == 'Ez':
|
| 264 |
+
index = nx * y + x
|
| 265 |
+
elif field == 'Hx':
|
| 266 |
+
index = 2*nx*nx + nx * y + x
|
| 267 |
+
else:
|
| 268 |
+
index = 3*nx*nx + nx * y + x
|
| 269 |
+
|
| 270 |
+
index_bin = format(index, f'0{qc.num_qubits-2}b')
|
| 271 |
+
ctrl_state = '1' + index_bin
|
| 272 |
+
ctrl_qubits = qc.qubits[:-1]
|
| 273 |
+
qc.mcx(ctrl_qubits, naimark[0], ctrl_state=ctrl_state)
|
| 274 |
+
|
| 275 |
+
return qc
|
| 276 |
+
|
| 277 |
+
def circuits_for_sign(field, x, y, nx, na, dt, R, initial_state, steps, xref, yref, field_ref = 'Ez'):
|
| 278 |
+
qc = schro(nx, na, R, dt, initial_state, steps)
|
| 279 |
+
|
| 280 |
+
naimark = QuantumRegister(1, name='Naimark')
|
| 281 |
+
qc.add_register(naimark)
|
| 282 |
+
|
| 283 |
+
if field == 'Ez':
|
| 284 |
+
index = nx * y + x
|
| 285 |
+
elif field == 'Hx':
|
| 286 |
+
index = 2*nx*nx + nx * y + x
|
| 287 |
+
else:
|
| 288 |
+
index = 3*nx*nx + nx * y + x
|
| 289 |
+
|
| 290 |
+
if field_ref == 'Ez':
|
| 291 |
+
index_ref = nx * yref + xref
|
| 292 |
+
elif field_ref == 'Hx':
|
| 293 |
+
index_ref = 2*nx*nx + nx * yref + xref
|
| 294 |
+
else:
|
| 295 |
+
index_ref = 3*nx*nx + nx * yref + xref
|
| 296 |
+
|
| 297 |
+
index_bin = [(index >> i) & 1 for i in range(qc.num_qubits-2)]
|
| 298 |
+
index_ref_bin = [(index_ref >> i) & 1 for i in range(qc.num_qubits-2)]
|
| 299 |
+
index_bin.append(1)
|
| 300 |
+
index_ref_bin.append(1)
|
| 301 |
+
|
| 302 |
+
#Convert reference bitstring to 00000
|
| 303 |
+
for i, bit in enumerate(index_ref_bin):
|
| 304 |
+
if bit == 1:
|
| 305 |
+
qc.x(i)
|
| 306 |
+
|
| 307 |
+
d_bits = [b ^ r for b, r in zip(index_ref_bin, index_bin)]
|
| 308 |
+
control = d_bits.index(1)
|
| 309 |
+
|
| 310 |
+
#Convert the other bitstring to 0001000
|
| 311 |
+
for target, bit in enumerate(d_bits):
|
| 312 |
+
if bit == 1 and target != control:
|
| 313 |
+
qc.cx(control, target)
|
| 314 |
+
qc.h(control)
|
| 315 |
+
|
| 316 |
+
ctrl_state_sum = '0'*(qc.num_qubits-1)
|
| 317 |
+
ctrl_state_diff = '0'*(qc.num_qubits-1-control-1)+'1'+'0'*(control)
|
| 318 |
+
|
| 319 |
+
qcdiff = qc.copy()
|
| 320 |
+
|
| 321 |
+
ctrl_qubits = qc.qubits[:-1]
|
| 322 |
+
|
| 323 |
+
qc.mcx(ctrl_qubits, naimark[0], ctrl_state=ctrl_state_sum)
|
| 324 |
+
qcdiff.mcx(ctrl_qubits, naimark[0], ctrl_state=ctrl_state_diff)
|
| 325 |
+
|
| 326 |
+
return qc, qcdiff
|
| 327 |
+
|
| 328 |
+
def get_absolute_field_value(qc, nq, na, offset, norm):
|
| 329 |
+
|
| 330 |
+
pauli_label = 'Z'+'I'*(2*nq+2+na)
|
| 331 |
+
observable = SparsePauliOp(Pauli(pauli_label))
|
| 332 |
+
########################################################################################
|
| 333 |
+
estimator = StatevectorEstimator()
|
| 334 |
+
|
| 335 |
+
# === Run Estimator (no parameters needed) ===
|
| 336 |
+
pub = (qc, observable)
|
| 337 |
+
job = estimator.run([pub])
|
| 338 |
+
result = job.result()[0]
|
| 339 |
+
z_exp = result.data.evs.item()
|
| 340 |
+
#########################################################################################
|
| 341 |
+
# === Compute projector expectation ===
|
| 342 |
+
pi_expect = (1 - z_exp) / 2
|
| 343 |
+
|
| 344 |
+
Absolute_value = norm*np.sqrt(pi_expect)-offset
|
| 345 |
+
|
| 346 |
+
return Absolute_value
|
| 347 |
+
|
| 348 |
+
def get_relative_sign(qc, qcdiff, nq, na):
|
| 349 |
+
|
| 350 |
+
pauli_label = 'Z'+'I'*(2*nq+2+na)
|
| 351 |
+
observable = SparsePauliOp(Pauli(pauli_label))
|
| 352 |
+
########################################################################################
|
| 353 |
+
estimator = StatevectorEstimator()
|
| 354 |
+
|
| 355 |
+
# === Run Estimator ===
|
| 356 |
+
pub = (qc, observable)
|
| 357 |
+
job = estimator.run([pub])
|
| 358 |
+
result = job.result()[0]
|
| 359 |
+
z_exp = result.data.evs.item()
|
| 360 |
+
|
| 361 |
+
pub_diff = (qcdiff, observable)
|
| 362 |
+
job_diff = estimator.run([pub_diff])
|
| 363 |
+
result_diff = job_diff.result()[0]
|
| 364 |
+
z_exp_diff = result_diff.data.evs.item()
|
| 365 |
+
#########################################################################################
|
| 366 |
+
# === Compute projector expectation ===
|
| 367 |
+
pi_expect_sum = (1 - z_exp) / 2
|
| 368 |
+
pi_expect_diff = (1 - z_exp_diff) / 2
|
| 369 |
+
|
| 370 |
+
relative_sign = 'same' if pi_expect_sum >= pi_expect_diff else 'different'
|
| 371 |
+
|
| 372 |
+
return relative_sign
|
| 373 |
+
|
| 374 |
+
def Eref_value(nx, nq, R, dt, na, steps, xref, yref, field_ref = 'Ez'):
|
| 375 |
+
if steps < 31:
|
| 376 |
+
offset = 1
|
| 377 |
+
else :
|
| 378 |
+
offset = 0.15
|
| 379 |
+
deltastate = np.zeros(4*nx*nx)
|
| 380 |
+
# deltastate[nx*nx//2+nx//2:nx*nx//2+nx//2+1] = 1
|
| 381 |
+
deltastate[nx*yref+xref] = 1
|
| 382 |
+
deltastate[0:nx*nx] = deltastate[0:nx*nx] + offset
|
| 383 |
+
norm1 = np.linalg.norm(deltastate)
|
| 384 |
+
initial_state = deltastate/norm1
|
| 385 |
+
|
| 386 |
+
dp = 2 * R * np.pi / 2**na
|
| 387 |
+
p = np.arange(- R * np.pi, R * np.pi, step=dp)
|
| 388 |
+
fp = np.exp(-np.abs(p))
|
| 389 |
+
norm2 = np.linalg.norm(fp)
|
| 390 |
+
norm = norm1 * norm2
|
| 391 |
+
|
| 392 |
+
qc = circ_for_magnitude(field_ref, xref, yref, nx, na, R, dt, initial_state, steps)
|
| 393 |
+
|
| 394 |
+
Ezref = get_absolute_field_value(qc, nq, na, offset, norm)
|
| 395 |
+
|
| 396 |
+
return Ezref
|
| 397 |
+
|
| 398 |
+
|
| 399 |
+
def transpile_circ(circ, basis_gates=None):
|
| 400 |
+
"""
|
| 401 |
+
Transpile the circuit to the specified basis gates.
|
| 402 |
+
"""
|
| 403 |
+
if basis_gates is None:
|
| 404 |
+
basis_gates = ['z', 'y', 'x', 'sdg', 's', 'h', 'rz', 'ry', 'rx', 'ecr', 'cz', 'cx']
|
| 405 |
+
|
| 406 |
+
transpiled_circ = transpile(circ, basis_gates=basis_gates)
|
| 407 |
+
return transpiled_circ
|
| 408 |
+
|
| 409 |
+
def compute_fidelity(circ1, circ2):
|
| 410 |
+
|
| 411 |
+
circ_1 = tensornetwork_from_circuit(transpile_circ(circ1), simulator_settings)
|
| 412 |
+
circ_2 = tensornetwork_from_circuit(transpile_circ(circ2), simulator_settings)
|
| 413 |
+
fidelity = abs(compute_overlap(circ_1, circ_2))**2
|
| 414 |
+
|
| 415 |
+
return fidelity
|
| 416 |
+
|
| 417 |
+
# def create_impulse_state(grid_dims, impulse_pos):
|
| 418 |
+
# """
|
| 419 |
+
# Creates an initial state vector with a single delta impulse at a specified grid position.
|
| 420 |
+
|
| 421 |
+
# The 2D grid is flattened into a 1D vector in row-major order, and this
|
| 422 |
+
# vector is then padded to match the full simulation state space size (4x).
|
| 423 |
+
|
| 424 |
+
# Args:
|
| 425 |
+
# grid_dims (tuple): A tuple (width, height) defining the simulation grid dimensions.
|
| 426 |
+
# For your original code, this would be (nx, nx).
|
| 427 |
+
# impulse_pos (tuple): A tuple (x, y) for the position of the impulse.
|
| 428 |
+
# Coordinates are 0-indexed.
|
| 429 |
+
|
| 430 |
+
# Returns:
|
| 431 |
+
# numpy.ndarray: The full, padded initial state vector with a single 1.
|
| 432 |
+
|
| 433 |
+
# Raises:
|
| 434 |
+
# ValueError: If the impulse position is outside the grid dimensions.
|
| 435 |
+
# """
|
| 436 |
+
# grid_width, grid_height = grid_dims
|
| 437 |
+
# impulse_x, impulse_y = impulse_pos
|
| 438 |
+
|
| 439 |
+
# # --- Input Validation ---
|
| 440 |
+
# # Ensure the requested impulse position is actually on the grid.
|
| 441 |
+
# if not (0 <= impulse_x < grid_width and 0 <= impulse_y < grid_height):
|
| 442 |
+
# raise ValueError(f"Impulse position ({impulse_x}, {impulse_y}) is outside the "
|
| 443 |
+
# f"grid dimensions ({grid_width}x{grid_height}).")
|
| 444 |
+
|
| 445 |
+
# # --- 1. Calculate the 1D Array Index ---
|
| 446 |
+
# # Convert the (x, y) coordinate to a single index in a flattened 1D array.
|
| 447 |
+
# # The formula for row-major order is: index = y_coord * width + x_coord
|
| 448 |
+
# flat_index = impulse_y * grid_width + impulse_x
|
| 449 |
+
|
| 450 |
+
# # --- 2. Create the Full, Padded State Vector ---
|
| 451 |
+
# grid_size = grid_width * grid_height
|
| 452 |
+
# total_size = 4 * grid_size # The simulation space is 4x the grid size.
|
| 453 |
+
# initial_state = np.zeros(total_size)
|
| 454 |
+
|
| 455 |
+
# # --- 3. Set the Delta Impulse ---
|
| 456 |
+
# initial_state[flat_index] = 1
|
| 457 |
+
|
| 458 |
+
# return initial_state
|