Spaces:
Build error
Build error
Upload 5 files
Browse files- .gitattributes +35 -35
- Dockerfile.txt +27 -0
- README.md +12 -12
- app.py +565 -0
- requirements.txt +5 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,35 @@
|
|
| 1 |
-
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
Dockerfile.txt
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Use an NVIDIA CUDA base image with Python
|
| 2 |
+
FROM nvidia/cuda:12.2.0-devel-ubuntu22.04
|
| 3 |
+
|
| 4 |
+
# Set working directory
|
| 5 |
+
WORKDIR /app
|
| 6 |
+
|
| 7 |
+
# Install system dependencies
|
| 8 |
+
RUN apt-get update && apt-get install -y \
|
| 9 |
+
python3 \
|
| 10 |
+
python3-pip \
|
| 11 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 12 |
+
|
| 13 |
+
# Upgrade pip
|
| 14 |
+
RUN pip3 install --upgrade pip
|
| 15 |
+
|
| 16 |
+
# Copy requirements.txt and install Python dependencies
|
| 17 |
+
COPY requirements.txt .
|
| 18 |
+
RUN pip3 install -r requirements.txt
|
| 19 |
+
|
| 20 |
+
# Copy the application code
|
| 21 |
+
COPY gaussian.py .
|
| 22 |
+
|
| 23 |
+
# Expose the port used by Gradio (default is 7860)
|
| 24 |
+
EXPOSE 7860
|
| 25 |
+
|
| 26 |
+
# Command to run the application
|
| 27 |
+
CMD ["python3", "gaussian.py"]
|
README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: Container Trial
|
| 3 |
-
emoji: 🏃
|
| 4 |
-
colorFrom: gray
|
| 5 |
-
colorTo: green
|
| 6 |
-
sdk: docker
|
| 7 |
-
pinned: false
|
| 8 |
-
license: mit
|
| 9 |
-
short_description: QLBM Container
|
| 10 |
-
---
|
| 11 |
-
|
| 12 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Container Trial
|
| 3 |
+
emoji: 🏃
|
| 4 |
+
colorFrom: gray
|
| 5 |
+
colorTo: green
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
license: mit
|
| 9 |
+
short_description: QLBM Container
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
ADDED
|
@@ -0,0 +1,565 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
import time
|
| 4 |
+
import math
|
| 5 |
+
import threading
|
| 6 |
+
import tempfile
|
| 7 |
+
import gradio as gr
|
| 8 |
+
|
| 9 |
+
import cudaq
|
| 10 |
+
import numpy as np
|
| 11 |
+
import cupy as cp
|
| 12 |
+
# from scipy import interpolate # Not used
|
| 13 |
+
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
|
| 16 |
+
from matplotlib import pyplot as plt
|
| 17 |
+
from matplotlib.animation import FuncAnimation
|
| 18 |
+
|
| 19 |
+
def simulate_qlbm_and_animate(num_reg_qubits: int, T: int, distribution_type: str, ux_input: float, uy_input: float):
|
| 20 |
+
"""
|
| 21 |
+
Simulates a 2D advection-diffusion problem using a Quantum Lattice Boltzmann Method (QLBM)
|
| 22 |
+
and generates a GIF animation of the simulation's evolution.
|
| 23 |
+
|
| 24 |
+
Args:
|
| 25 |
+
num_reg_qubits (int): The number of register qubits (determines grid size N=2^num_reg_qubits).
|
| 26 |
+
T (int): The total number of timesteps to run the simulation.
|
| 27 |
+
distribution_type (str): The type of initial distribution to use.
|
| 28 |
+
ux_input (float): Advection velocity in the x-direction.
|
| 29 |
+
uy_input (float): Advection velocity in the y-direction.
|
| 30 |
+
|
| 31 |
+
Returns:
|
| 32 |
+
str: The file path to the generated GIF animation, or None if an error occurs.
|
| 33 |
+
"""
|
| 34 |
+
|
| 35 |
+
video_length = T
|
| 36 |
+
simulation_fps = 0.1
|
| 37 |
+
frames = int(simulation_fps * video_length)
|
| 38 |
+
if frames == 0:
|
| 39 |
+
frames = 1
|
| 40 |
+
|
| 41 |
+
num_anc = 3
|
| 42 |
+
num_qubits_total = 2 * num_reg_qubits + num_anc
|
| 43 |
+
current_N = 2**num_reg_qubits
|
| 44 |
+
N_tot_state_vector = 2**num_qubits_total
|
| 45 |
+
num_ranks = 1
|
| 46 |
+
rank = 0
|
| 47 |
+
N_sub_per_rank = int(N_tot_state_vector // num_ranks)
|
| 48 |
+
|
| 49 |
+
timesteps_per_frame = 1
|
| 50 |
+
if frames < T and frames > 0:
|
| 51 |
+
timesteps_per_frame = int(T / frames)
|
| 52 |
+
if timesteps_per_frame == 0:
|
| 53 |
+
timesteps_per_frame = 1
|
| 54 |
+
|
| 55 |
+
if distribution_type == "Sine Wave (Original)":
|
| 56 |
+
selected_initial_state_function_raw = lambda x, y, N_val_func: \
|
| 57 |
+
np.sin(x * 2 * np.pi / N_val_func) * (1 - 0.5 * x / N_val_func) * \
|
| 58 |
+
np.sin(y * 4 * np.pi / N_val_func) * (1 - 0.5 * y / N_val_func) + 1
|
| 59 |
+
elif distribution_type == "Gaussian":
|
| 60 |
+
selected_initial_state_function_raw = lambda x, y, N_val_func: \
|
| 61 |
+
np.exp(-((x - N_val_func / 2)**2 / (2 * (N_val_func / 5)**2) +
|
| 62 |
+
(y - N_val_func / 2)**2 / (2 * (N_val_func / 5)**2))) * 1.8 + 0.2
|
| 63 |
+
elif distribution_type == "Random":
|
| 64 |
+
selected_initial_state_function_raw = lambda x, y, N_val_func: \
|
| 65 |
+
np.random.rand(x.shape[0], x.shape[1]) * 1.5 + 0.2
|
| 66 |
+
else:
|
| 67 |
+
print(f"Warning: Unknown distribution type '{distribution_type}'. Defaulting to Sine Wave.")
|
| 68 |
+
selected_initial_state_function_raw = lambda x, y, N_val_func: \
|
| 69 |
+
np.sin(x * 2 * np.pi / N_val_func) * (1 - 0.5 * x / N_val_func) * \
|
| 70 |
+
np.sin(y * 4 * np.pi / N_val_func) * (1 - 0.5 * y / N_val_func) + 1
|
| 71 |
+
|
| 72 |
+
initial_state_func_eval = lambda x_coords, y_coords: \
|
| 73 |
+
selected_initial_state_function_raw(x_coords, y_coords, current_N) * \
|
| 74 |
+
(y_coords < current_N).astype(int)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
with tempfile.TemporaryDirectory() as tmp_npy_dir:
|
| 78 |
+
intermediate_folder_path = Path(tmp_npy_dir)
|
| 79 |
+
|
| 80 |
+
cudaq.set_target('nvidia', option='mgpu,fp64')
|
| 81 |
+
|
| 82 |
+
@cudaq.kernel
|
| 83 |
+
def alloc_kernel(num_qubits_alloc: int):
|
| 84 |
+
qubits = cudaq.qvector(num_qubits_alloc)
|
| 85 |
+
|
| 86 |
+
from cupy.cuda.memory import MemoryPointer, UnownedMemory
|
| 87 |
+
|
| 88 |
+
def to_cupy_array(state):
|
| 89 |
+
tensor = state.getTensor()
|
| 90 |
+
pDevice = tensor.data()
|
| 91 |
+
sizeByte = tensor.get_num_elements() * tensor.get_element_size()
|
| 92 |
+
mem = UnownedMemory(pDevice, sizeByte, owner=state)
|
| 93 |
+
memptr_obj = MemoryPointer(mem, 0)
|
| 94 |
+
cupy_array = cp.ndarray(tensor.get_num_elements(),
|
| 95 |
+
dtype=cp.complex128,
|
| 96 |
+
memptr=memptr_obj)
|
| 97 |
+
return cupy_array
|
| 98 |
+
|
| 99 |
+
class QLBMAdvecDiffD2Q5_new:
|
| 100 |
+
def __init__(self, ux=0.2, uy=0.15) -> None: # ux, uy are now passed here
|
| 101 |
+
self.dim = 2
|
| 102 |
+
self.ndir = 5
|
| 103 |
+
self.nq_dir = math.ceil(np.log2(self.ndir))
|
| 104 |
+
self.dirs = []
|
| 105 |
+
for dir_int in range(self.ndir):
|
| 106 |
+
dir_bin = f"{dir_int:b}".zfill(self.nq_dir)
|
| 107 |
+
self.dirs.append(dir_bin)
|
| 108 |
+
self.e_unitvec = np.array([0, 1, -1, 1, -1])
|
| 109 |
+
self.wts = np.array([2/6, 1/6, 1/6, 1/6, 1/6])
|
| 110 |
+
self.cs = 1 / np.sqrt(3)
|
| 111 |
+
self.ux = ux # Use passed ux
|
| 112 |
+
self.uy = uy # Use passed uy
|
| 113 |
+
self.u = np.array([0, self.ux, self.ux, self.uy, self.uy])
|
| 114 |
+
self.wtcoeffs = np.multiply(self.wts, 1 + self.e_unitvec * self.u / self.cs**2)
|
| 115 |
+
self.create_circuit()
|
| 116 |
+
|
| 117 |
+
def create_circuit(self):
|
| 118 |
+
v = np.pad(self.wtcoeffs, (0, 2**num_anc - self.ndir))
|
| 119 |
+
v = v**0.5
|
| 120 |
+
v[0] += 1
|
| 121 |
+
v = v / np.linalg.norm(v)
|
| 122 |
+
U_prep = 2 * np.outer(v, v) - np.eye(len(v))
|
| 123 |
+
cudaq.register_operation("prep_op", U_prep)
|
| 124 |
+
|
| 125 |
+
def collisionOp(dirs):
|
| 126 |
+
dirs_i_list = []
|
| 127 |
+
for dir_ in dirs:
|
| 128 |
+
dirs_i = [(int(c)) for c in dir_]
|
| 129 |
+
dirs_i_list += dirs_i[::-1]
|
| 130 |
+
return dirs_i_list
|
| 131 |
+
|
| 132 |
+
self.dirs_i_list = collisionOp(self.dirs)
|
| 133 |
+
|
| 134 |
+
@cudaq.kernel
|
| 135 |
+
def rshift(q: cudaq.qview, n: int):
|
| 136 |
+
for i in range(n):
|
| 137 |
+
if i == n - 1:
|
| 138 |
+
x(q[n - 1 - i])
|
| 139 |
+
elif i == n - 2:
|
| 140 |
+
x.ctrl(q[n - 1 - (i + 1)], q[n - 1 - i])
|
| 141 |
+
else:
|
| 142 |
+
x.ctrl(q[0:n - 1 - i], q[n - 1 - i])
|
| 143 |
+
|
| 144 |
+
@cudaq.kernel
|
| 145 |
+
def lshift(q: cudaq.qview, n: int):
|
| 146 |
+
for i in range(n):
|
| 147 |
+
if i == 0:
|
| 148 |
+
x(q[0])
|
| 149 |
+
elif i == 1:
|
| 150 |
+
x.ctrl(q[0], q[1])
|
| 151 |
+
else:
|
| 152 |
+
x.ctrl(q[0:i], q[i])
|
| 153 |
+
|
| 154 |
+
@cudaq.kernel
|
| 155 |
+
def d2q5_tstep(q: cudaq.qview, nqx: int, nqy: int, nq_dir: int, dirs_i: list[int]):
|
| 156 |
+
qx = q[0:nqx]
|
| 157 |
+
qy = q[nqx:nqx + nqy]
|
| 158 |
+
qdir = q[nqx + nqy:nqx + nqy + nq_dir]
|
| 159 |
+
i = 2
|
| 160 |
+
b_list = dirs_i[i * nq_dir:(i + 1) * nq_dir]
|
| 161 |
+
for j in range(nq_dir):
|
| 162 |
+
b = b_list[j]
|
| 163 |
+
if b == 0:
|
| 164 |
+
x(qdir[j])
|
| 165 |
+
cudaq.control(lshift, qdir, qx, nqx)
|
| 166 |
+
for j in range(nq_dir):
|
| 167 |
+
b = b_list[j]
|
| 168 |
+
if b == 0:
|
| 169 |
+
x(qdir[j])
|
| 170 |
+
i = 1
|
| 171 |
+
b_list = dirs_i[i * nq_dir:(i + 1) * nq_dir]
|
| 172 |
+
for j in range(nq_dir):
|
| 173 |
+
b = b_list[j]
|
| 174 |
+
if b == 0:
|
| 175 |
+
x(qdir[j])
|
| 176 |
+
cudaq.control(rshift, qdir, qx, nqx)
|
| 177 |
+
for j in range(nq_dir):
|
| 178 |
+
b = b_list[j]
|
| 179 |
+
if b == 0:
|
| 180 |
+
x(qdir[j])
|
| 181 |
+
i = 4
|
| 182 |
+
b_list = dirs_i[i * nq_dir:(i + 1) * nq_dir]
|
| 183 |
+
for j in range(nq_dir):
|
| 184 |
+
b = b_list[j]
|
| 185 |
+
if b == 0:
|
| 186 |
+
x(qdir[j])
|
| 187 |
+
cudaq.control(lshift, qdir, qy, nqy)
|
| 188 |
+
for j in range(nq_dir):
|
| 189 |
+
b = b_list[j]
|
| 190 |
+
if b == 0:
|
| 191 |
+
x(qdir[j])
|
| 192 |
+
i = 3
|
| 193 |
+
b_list = dirs_i[i * nq_dir:(i + 1) * nq_dir]
|
| 194 |
+
for j in range(nq_dir):
|
| 195 |
+
b = b_list[j]
|
| 196 |
+
if b == 0:
|
| 197 |
+
x(qdir[j])
|
| 198 |
+
cudaq.control(rshift, qdir, qy, nqy)
|
| 199 |
+
for j in range(nq_dir):
|
| 200 |
+
b = b_list[j]
|
| 201 |
+
if b == 0:
|
| 202 |
+
x(qdir[j])
|
| 203 |
+
|
| 204 |
+
@cudaq.kernel
|
| 205 |
+
def d2q5_tstep_wrapper(state_arg: cudaq.State, nqx: int, nqy: int, nq_dir: int, dirs_i: list[int]):
|
| 206 |
+
q = cudaq.qvector(state_arg)
|
| 207 |
+
qdir = q[nqx + nqy:nqx + nqy + nq_dir]
|
| 208 |
+
prep_op(qdir[2], qdir[1], qdir[0])
|
| 209 |
+
d2q5_tstep(q, nqx, nqy, nq_dir, dirs_i)
|
| 210 |
+
prep_op(qdir[2], qdir[1], qdir[0])
|
| 211 |
+
|
| 212 |
+
@cudaq.kernel
|
| 213 |
+
def d2q5_tstep_wrapper_hadamard(vec: list[complex], nqx: int, nqy: int, nq_dir: int, dirs_i: list[int]):
|
| 214 |
+
q = cudaq.qvector(vec)
|
| 215 |
+
qdir = q[nqx + nqy:nqx + nqy + nq_dir]
|
| 216 |
+
qy = q[nqx:nqx + nqy]
|
| 217 |
+
prep_op(qdir[2], qdir[1], qdir[0])
|
| 218 |
+
d2q5_tstep(q, nqx, nqy, nq_dir, dirs_i)
|
| 219 |
+
prep_op(qdir[2], qdir[1], qdir[0])
|
| 220 |
+
for i in range(nqy):
|
| 221 |
+
h(qy[i])
|
| 222 |
+
|
| 223 |
+
def run_timestep_func(vec, hadamard=False):
|
| 224 |
+
if hadamard:
|
| 225 |
+
result = cudaq.get_state(d2q5_tstep_wrapper_hadamard, vec, num_reg_qubits, num_reg_qubits, self.nq_dir, self.dirs_i_list)
|
| 226 |
+
else:
|
| 227 |
+
result = cudaq.get_state(d2q5_tstep_wrapper, vec, num_reg_qubits, num_reg_qubits, self.nq_dir, self.dirs_i_list)
|
| 228 |
+
|
| 229 |
+
num_nonzero_ranks = num_ranks / (2**num_anc)
|
| 230 |
+
rank_slice_cupy = to_cupy_array(result)
|
| 231 |
+
|
| 232 |
+
if rank >= num_nonzero_ranks:
|
| 233 |
+
sub_sv_zeros = np.zeros(N_sub_per_rank, dtype=np.complex128)
|
| 234 |
+
cp.cuda.runtime.memcpy(rank_slice_cupy.data.ptr, sub_sv_zeros.ctypes.data, sub_sv_zeros.nbytes, cp.cuda.runtime.memcpyHostToDevice)
|
| 235 |
+
|
| 236 |
+
if rank == 0 and num_nonzero_ranks < 1:
|
| 237 |
+
sub_sv_get = (rank_slice_cupy).get()
|
| 238 |
+
sub_sv_get[int(N_tot_state_vector / (2**num_anc)):] = 0
|
| 239 |
+
cp.cuda.runtime.memcpy(rank_slice_cupy.data.ptr, sub_sv_get.ctypes.data, sub_sv_get.nbytes, cp.cuda.runtime.memcpyHostToDevice)
|
| 240 |
+
return result
|
| 241 |
+
|
| 242 |
+
self.run_timestep = run_timestep_func
|
| 243 |
+
|
| 244 |
+
def write_state(self, state_to_write, t_step):
|
| 245 |
+
rank_slice_cupy = to_cupy_array(state_to_write)
|
| 246 |
+
num_nonzero_ranks = num_ranks / (2**num_anc)
|
| 247 |
+
|
| 248 |
+
if rank < num_nonzero_ranks:
|
| 249 |
+
save_path = intermediate_folder_path / f"{t_step}_{rank}.npy"
|
| 250 |
+
with open(save_path, 'wb') as f:
|
| 251 |
+
arr_to_save = None
|
| 252 |
+
if num_nonzero_ranks < 1:
|
| 253 |
+
arr_to_save = cp.real(rank_slice_cupy)[:int(N_tot_state_vector / (2**num_anc))]
|
| 254 |
+
else:
|
| 255 |
+
arr_to_save = cp.real(rank_slice_cupy)
|
| 256 |
+
|
| 257 |
+
if len(arr_to_save) > current_N :
|
| 258 |
+
arr_to_save=arr_to_save.reshape((current_N, current_N))
|
| 259 |
+
arr_to_save=arr_to_save[::downsampling_factor,::downsampling_factor]
|
| 260 |
+
arr_to_save=arr_to_save.flatten()
|
| 261 |
+
elif len(arr_to_save) > 0 :
|
| 262 |
+
arr_to_save=arr_to_save[::downsampling_factor]
|
| 263 |
+
|
| 264 |
+
np.save(f, arr_to_save.get() if isinstance(arr_to_save, cp.ndarray) else arr_to_save)
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
def run_evolution(self, initial_state_arg, total_timesteps, observable=False):
|
| 268 |
+
current_state = initial_state_arg
|
| 269 |
+
for t_iter in range(total_timesteps):
|
| 270 |
+
next_state = None
|
| 271 |
+
if t_iter == total_timesteps - 1 and observable:
|
| 272 |
+
next_state = self.run_timestep(current_state, True)
|
| 273 |
+
self.write_state(next_state, str(t_iter + 1) + "_h")
|
| 274 |
+
else:
|
| 275 |
+
next_state = self.run_timestep(current_state)
|
| 276 |
+
if (t_iter + 1) % timesteps_per_frame == 0:
|
| 277 |
+
self.write_state(next_state, t_iter + 1)
|
| 278 |
+
if rank == 0:
|
| 279 |
+
print(f"Timestep: {t_iter + 1}/{total_timesteps}")
|
| 280 |
+
|
| 281 |
+
cp.get_default_memory_pool().free_all_blocks()
|
| 282 |
+
current_state = next_state
|
| 283 |
+
|
| 284 |
+
last_saved_timestep_name = str(total_timesteps) if not observable else str(total_timesteps) + "_h"
|
| 285 |
+
# Check if the very last state (T or T_h) was potentially saved by the loop or observable condition.
|
| 286 |
+
# If not, save it.
|
| 287 |
+
final_state_file_path = intermediate_folder_path.joinpath(f"{last_saved_timestep_name}_{rank}.npy")
|
| 288 |
+
# Also check if the non-observable final T state was saved if T aligns with timesteps_per_frame
|
| 289 |
+
final_T_state_file_path = intermediate_folder_path.joinpath(f"{total_timesteps}_{rank}.npy")
|
| 290 |
+
|
| 291 |
+
needs_final_save = True
|
| 292 |
+
if observable and final_state_file_path.exists(): # T_h was saved
|
| 293 |
+
needs_final_save = False
|
| 294 |
+
elif not observable and final_T_state_file_path.exists() and total_timesteps % timesteps_per_frame == 0 : # T was saved by loop
|
| 295 |
+
needs_final_save = False
|
| 296 |
+
elif not observable and final_T_state_file_path.exists(): # T was saved by a previous final write
|
| 297 |
+
needs_final_save = False
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
if needs_final_save and total_timesteps > 0:
|
| 301 |
+
if not observable: # Save as T_0.npy
|
| 302 |
+
self.write_state(current_state, total_timesteps)
|
| 303 |
+
# If observable, it should have been saved as T_h. If it wasn't (e.g. T=0), this won't run.
|
| 304 |
+
|
| 305 |
+
if rank == 0:
|
| 306 |
+
print(f"Timestep: {total_timesteps}/{total_timesteps} (Evolution complete)")
|
| 307 |
+
cp.get_default_memory_pool().free_all_blocks()
|
| 308 |
+
self.final_state = current_state
|
| 309 |
+
|
| 310 |
+
downsampling_factor = 2**5
|
| 311 |
+
# Pass ux_input and uy_input to the constructor
|
| 312 |
+
qlbm_obj = QLBMAdvecDiffD2Q5_new(ux=ux_input, uy=uy_input)
|
| 313 |
+
|
| 314 |
+
initial_state = cudaq.get_state(alloc_kernel, num_qubits_total)
|
| 315 |
+
rank_slice_init = to_cupy_array(initial_state)
|
| 316 |
+
|
| 317 |
+
xv_init = np.arange(current_N)
|
| 318 |
+
yv_init = np.arange(current_N)
|
| 319 |
+
|
| 320 |
+
print(f'Start initializing state with {distribution_type} distribution (ux={ux_input}, uy={uy_input})...')
|
| 321 |
+
initial_grid_2d = initial_state_func_eval(*np.meshgrid(xv_init, yv_init))
|
| 322 |
+
sub_sv_init_flat = initial_grid_2d.flatten().astype(np.complex128)
|
| 323 |
+
|
| 324 |
+
full_initial_sv_host = np.zeros(N_sub_per_rank, dtype=np.complex128)
|
| 325 |
+
num_computational_states = current_N * current_N
|
| 326 |
+
|
| 327 |
+
if len(sub_sv_init_flat) == num_computational_states:
|
| 328 |
+
full_initial_sv_host[:num_computational_states] = sub_sv_init_flat
|
| 329 |
+
else:
|
| 330 |
+
print(f"Warning: Mismatch in expected initial state size {num_computational_states} and calculated {len(sub_sv_init_flat)}")
|
| 331 |
+
full_initial_sv_host[:len(sub_sv_init_flat)] = sub_sv_init_flat
|
| 332 |
+
|
| 333 |
+
print(f'Rank {rank}: Initial state (N*N part) prepared. Size: {len(sub_sv_init_flat)}. Total N_sub_per_rank: {N_sub_per_rank}')
|
| 334 |
+
cp.cuda.runtime.memcpy(rank_slice_init.data.ptr, full_initial_sv_host.ctypes.data, full_initial_sv_host.nbytes, cp.cuda.runtime.memcpyHostToDevice)
|
| 335 |
+
print(f'Rank {rank}: Initial state copied to device.')
|
| 336 |
+
|
| 337 |
+
print("Starting QLBM evolution...")
|
| 338 |
+
qlbm_obj.run_evolution(initial_state, T)
|
| 339 |
+
print("QLBM evolution complete.")
|
| 340 |
+
|
| 341 |
+
print("Generating animation...")
|
| 342 |
+
downsampled_N = current_N // downsampling_factor
|
| 343 |
+
if downsampled_N == 0:
|
| 344 |
+
print("Error: Downsampled grid size is zero. Check num_reg_qubits and downsampling_factor.")
|
| 345 |
+
downsampled_N = 1
|
| 346 |
+
|
| 347 |
+
plotted_timesteps_str = [] # To store frame names (can include '_h')
|
| 348 |
+
if timesteps_per_frame > 0:
|
| 349 |
+
# Add regular frames saved by the loop
|
| 350 |
+
for t_step in range(timesteps_per_frame, T + 1, timesteps_per_frame):
|
| 351 |
+
if intermediate_folder_path.joinpath(f"{t_step}_0.npy").exists():
|
| 352 |
+
plotted_timesteps_str.append(str(t_step))
|
| 353 |
+
|
| 354 |
+
# Ensure the very last timestep (T or T_h) is considered for plotting
|
| 355 |
+
final_T_path = intermediate_folder_path.joinpath(f"{T}_0.npy")
|
| 356 |
+
final_Th_path = intermediate_folder_path.joinpath(f"{T}_h_0.npy")
|
| 357 |
+
|
| 358 |
+
if T > 0:
|
| 359 |
+
if final_Th_path.exists(): # Prefer T_h if it exists
|
| 360 |
+
if str(T)+"_h" not in plotted_timesteps_str:
|
| 361 |
+
plotted_timesteps_str.append(str(T)+"_h")
|
| 362 |
+
elif final_T_path.exists(): # Else, use T if it exists
|
| 363 |
+
if str(T) not in plotted_timesteps_str:
|
| 364 |
+
plotted_timesteps_str.append(str(T))
|
| 365 |
+
|
| 366 |
+
# Remove duplicates and sort (handle numeric and '_h' strings appropriately for sorting if needed, though string sort is often fine here)
|
| 367 |
+
plotted_timesteps_str = sorted(list(set(plotted_timesteps_str)), key=lambda k: (int(str(k).replace('_h','')), str(k)))
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
if not plotted_timesteps_str and T > 0 : # Fallback if T is small and not caught by loop
|
| 371 |
+
if final_Th_path.exists(): plotted_timesteps_str = [str(T)+"_h"]
|
| 372 |
+
elif final_T_path.exists(): plotted_timesteps_str = [str(T)]
|
| 373 |
+
elif not plotted_timesteps_str and T == 0:
|
| 374 |
+
print("Warning: T=0, no frames to plot for animation.")
|
| 375 |
+
return None
|
| 376 |
+
|
| 377 |
+
data = []
|
| 378 |
+
actual_timesteps_for_title = []
|
| 379 |
+
|
| 380 |
+
for i_str in plotted_timesteps_str:
|
| 381 |
+
try:
|
| 382 |
+
file_path = intermediate_folder_path / f"{i_str}_0.npy"
|
| 383 |
+
sol = np.load(file_path)
|
| 384 |
+
if sol.size == downsampled_N * downsampled_N:
|
| 385 |
+
Z = np.reshape(sol, (downsampled_N, downsampled_N))
|
| 386 |
+
data.append(Z)
|
| 387 |
+
actual_timesteps_for_title.append(str(i_str).replace('_h',''))
|
| 388 |
+
else:
|
| 389 |
+
print(f"Warning: File {file_path} has unexpected size {sol.size}. Expected {downsampled_N*downsampled_N}. Skipping this frame.")
|
| 390 |
+
continue
|
| 391 |
+
except FileNotFoundError:
|
| 392 |
+
print(f"Warning: File not found for timestep {i_str} ({file_path}). Skipping this frame.")
|
| 393 |
+
continue
|
| 394 |
+
except Exception as e:
|
| 395 |
+
print(f"Error loading or reshaping file for timestep {i_str}: {e}. Skipping this frame.")
|
| 396 |
+
continue
|
| 397 |
+
|
| 398 |
+
if not data:
|
| 399 |
+
print("Error: No data available to create animation. Check .npy file generation and paths.")
|
| 400 |
+
return None
|
| 401 |
+
|
| 402 |
+
fig = plt.figure(figsize=(10, 8))
|
| 403 |
+
ax = fig.add_subplot(111, projection='3d')
|
| 404 |
+
|
| 405 |
+
x_plot = np.linspace(-10, 10, downsampled_N)
|
| 406 |
+
y_plot = np.linspace(-10, 10, downsampled_N)
|
| 407 |
+
X_plot, Y_plot = np.meshgrid(x_plot, y_plot)
|
| 408 |
+
|
| 409 |
+
norm_factor = np.linalg.norm(data[0]) if data[0].size > 0 else 1.0
|
| 410 |
+
if norm_factor == 0:
|
| 411 |
+
norm_factor = 1.0
|
| 412 |
+
|
| 413 |
+
def update_frame(frame_idx):
|
| 414 |
+
ax.clear()
|
| 415 |
+
Z = data[frame_idx]
|
| 416 |
+
surf = ax.plot_surface(X_plot, Y_plot, Z / norm_factor, cmap='viridis', linewidth=0, antialiased=False)
|
| 417 |
+
current_timestep_for_title = actual_timesteps_for_title[frame_idx] if frame_idx < len(actual_timesteps_for_title) else "Unknown"
|
| 418 |
+
ax.set_title(f'Quantum Simulation Evolution (Timestep: {current_timestep_for_title})')
|
| 419 |
+
ax.set_xlabel('x')
|
| 420 |
+
ax.set_ylabel('y')
|
| 421 |
+
ax.set_zlim(0, 0.6)
|
| 422 |
+
return surf,
|
| 423 |
+
|
| 424 |
+
gif_animation_fps = 10
|
| 425 |
+
ani = FuncAnimation(fig, update_frame, frames=len(data), blit=False)
|
| 426 |
+
|
| 427 |
+
results_base_dir = "Results"
|
| 428 |
+
gif_frames_for_naming = int(simulation_fps * T)
|
| 429 |
+
if gif_frames_for_naming == 0: gif_frames_for_naming = 1
|
| 430 |
+
|
| 431 |
+
# Sanitize distribution_type for filename
|
| 432 |
+
dist_name_part = distribution_type.replace(' ','').replace('(Original)','').replace('(','').replace(')','')
|
| 433 |
+
|
| 434 |
+
specific_folder_name = (f"d2q5_nq{current_N}x{current_N}_T{T}_fr{gif_frames_for_naming}_"
|
| 435 |
+
f"dist{dist_name_part}_ux{ux_input:.2f}_uy{uy_input:.2f}")
|
| 436 |
+
final_output_dir = Path(results_base_dir) / specific_folder_name
|
| 437 |
+
os.makedirs(final_output_dir, exist_ok=True)
|
| 438 |
+
gif_path_to_return = final_output_dir / "animation.gif"
|
| 439 |
+
|
| 440 |
+
try:
|
| 441 |
+
ani.save(str(gif_path_to_return), writer='pillow', fps=gif_animation_fps)
|
| 442 |
+
print(f"Animation saved to {gif_path_to_return}")
|
| 443 |
+
except Exception as e:
|
| 444 |
+
print(f"Error saving animation: {e}")
|
| 445 |
+
plt.close(fig)
|
| 446 |
+
return None
|
| 447 |
+
plt.close(fig)
|
| 448 |
+
return str(gif_path_to_return)
|
| 449 |
+
|
| 450 |
+
# --- Gradio Interface Definition ---
|
| 451 |
+
|
| 452 |
+
def qlbm_gradio_interface(num_reg_qubits_input: int, timescale_input: int, distribution_type_param: str, ux_param: float, uy_param: float):
|
| 453 |
+
num_reg_qubits_val = int(num_reg_qubits_input)
|
| 454 |
+
timescale_val = int(timescale_input)
|
| 455 |
+
ux_val = float(ux_param)
|
| 456 |
+
uy_val = float(uy_param)
|
| 457 |
+
|
| 458 |
+
print(f"Gradio Interface: num_reg_qubits={num_reg_qubits_val}, T={timescale_val}, Distribution={distribution_type_param}, ux={ux_val}, uy={uy_val}")
|
| 459 |
+
|
| 460 |
+
gif_path_output = simulate_qlbm_and_animate(
|
| 461 |
+
num_reg_qubits=num_reg_qubits_val,
|
| 462 |
+
T=timescale_val,
|
| 463 |
+
distribution_type=distribution_type_param,
|
| 464 |
+
ux_input=ux_val,
|
| 465 |
+
uy_input=uy_val
|
| 466 |
+
)
|
| 467 |
+
|
| 468 |
+
if gif_path_output is None:
|
| 469 |
+
gr.Warning("Animation generation failed. Please check console for errors.")
|
| 470 |
+
return None
|
| 471 |
+
return gif_path_output
|
| 472 |
+
|
| 473 |
+
with gr.Blocks(theme=gr.themes.Soft(), title="QLBM Simulation") as qlbm_demo:
|
| 474 |
+
gr.Markdown(
|
| 475 |
+
"""
|
| 476 |
+
# ⚛️ Quantum Lattice Boltzmann Method (QLBM) Simulator
|
| 477 |
+
Welcome to the Quantum Lattice Boltzmann Method (QLBM) simulator!
|
| 478 |
+
|
| 479 |
+
**What is QLBM?**
|
| 480 |
+
The Lattice Boltzmann Method (LBM) is a computational fluid dynamics technique for simulating fluid flow. QLBM is its quantum counterpart, leveraging quantum algorithms to potentially offer advantages for certain types of simulations. It's used here to model advection-diffusion phenomena, which describe how substances are transported due to bulk motion (advection) and spread out due to random motion (diffusion).
|
| 481 |
+
|
| 482 |
+
**How this Simulator Works:**
|
| 483 |
+
This simulator implements a D2Q5 model (2 dimensions, 5 velocity directions) on a quantum computer simulator (using CUDA-Q).
|
| 484 |
+
- You can control the grid size (via Number of Register Qubits, where grid size is $2^N \\times 2^N$).
|
| 485 |
+
- You can set the total simulation time (Timescale T).
|
| 486 |
+
- You can choose the initial distribution of the substance on the grid.
|
| 487 |
+
- You can set the advection velocities `ux` (x-direction) and `uy` (y-direction).
|
| 488 |
+
The simulation will then evolve this initial state over time, and an animation of the substance's density will be generated.
|
| 489 |
+
|
| 490 |
+
**Note:** Simulations with higher qubit counts and longer timescales can be computationally intensive and may take a significant amount of time to complete.
|
| 491 |
+
The $N$ in $2^N \\times 2^N$ grid refers to `num_reg_qubits`. Advection velocities `ux` and `uy` should generally be kept small (e.g., < 0.3) for model stability.
|
| 492 |
+
"""
|
| 493 |
+
)
|
| 494 |
+
|
| 495 |
+
with gr.Row():
|
| 496 |
+
with gr.Column(scale=1):
|
| 497 |
+
gr.Markdown("## Simulation Parameters")
|
| 498 |
+
num_reg_qubits_slider = gr.Slider(
|
| 499 |
+
minimum=2,
|
| 500 |
+
maximum=11,
|
| 501 |
+
value=8,
|
| 502 |
+
step=1,
|
| 503 |
+
label="Number of Register Qubits (num_reg_qubits)",
|
| 504 |
+
info="Determines the N for grid size (2^N x 2^N). Max 11 (Note: >8 can be very slow)."
|
| 505 |
+
)
|
| 506 |
+
timescale_slider = gr.Slider(
|
| 507 |
+
minimum=10,
|
| 508 |
+
maximum=2000,
|
| 509 |
+
value=100,
|
| 510 |
+
step=10,
|
| 511 |
+
label="Timescale (T)",
|
| 512 |
+
info="Total number of timesteps. Max 2000 (Note: higher values increase run time significantly)."
|
| 513 |
+
)
|
| 514 |
+
distribution_options = ["Sine Wave (Original)", "Gaussian", "Random"]
|
| 515 |
+
distribution_type_input = gr.Radio(
|
| 516 |
+
choices=distribution_options,
|
| 517 |
+
value="Sine Wave (Original)",
|
| 518 |
+
label="Initial Distribution Type",
|
| 519 |
+
info="Select the initial pattern of the substance on the grid."
|
| 520 |
+
)
|
| 521 |
+
ux_slider = gr.Slider(
|
| 522 |
+
minimum=-0.4,
|
| 523 |
+
maximum=0.4,
|
| 524 |
+
value=0.2,
|
| 525 |
+
step=0.01,
|
| 526 |
+
label="Advection Velocity ux",
|
| 527 |
+
info="x-component of the background advection velocity."
|
| 528 |
+
)
|
| 529 |
+
uy_slider = gr.Slider(
|
| 530 |
+
minimum=-0.4,
|
| 531 |
+
maximum=0.4,
|
| 532 |
+
value=0.15,
|
| 533 |
+
step=0.01,
|
| 534 |
+
label="Advection Velocity uy",
|
| 535 |
+
info="y-component of the background advection velocity."
|
| 536 |
+
)
|
| 537 |
+
|
| 538 |
+
run_qlbm_btn = gr.Button("Run QLBM Simulation", variant="primary")
|
| 539 |
+
|
| 540 |
+
with gr.Column(scale=3):
|
| 541 |
+
qlbm_plot_output = gr.Image(label="QLBM Simulation Animation", type="filepath")
|
| 542 |
+
|
| 543 |
+
qlbm_inputs_list = [num_reg_qubits_slider, timescale_slider, distribution_type_input, ux_slider, uy_slider]
|
| 544 |
+
|
| 545 |
+
run_qlbm_btn.click(
|
| 546 |
+
fn=qlbm_gradio_interface,
|
| 547 |
+
inputs=qlbm_inputs_list,
|
| 548 |
+
outputs=qlbm_plot_output
|
| 549 |
+
)
|
| 550 |
+
|
| 551 |
+
gr.Examples(
|
| 552 |
+
examples=[
|
| 553 |
+
[8, 100, "Sine Wave (Original)", 0.2, 0.15],
|
| 554 |
+
[6, 50, "Gaussian", 0.1, 0.05],
|
| 555 |
+
[4, 30, "Random", -0.05, 0.1],
|
| 556 |
+
[7, 70, "Sine Wave (Original)", 0.0, 0.0] # Example with no advection
|
| 557 |
+
],
|
| 558 |
+
inputs=qlbm_inputs_list,
|
| 559 |
+
outputs=qlbm_plot_output,
|
| 560 |
+
fn=qlbm_gradio_interface,
|
| 561 |
+
cache_examples=False
|
| 562 |
+
)
|
| 563 |
+
|
| 564 |
+
if __name__ == "__main__":
|
| 565 |
+
qlbm_demo.launch()
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio==3.50.2
|
| 2 |
+
matplotlib==3.8.0
|
| 3 |
+
numpy==1.26.0
|
| 4 |
+
cupy-cuda12x==12.2.0
|
| 5 |
+
cudaq==0.6.0
|