|
import streamlit as st |
|
import matplotlib.pyplot as plt |
|
import numpy as np |
|
|
|
|
|
def plot_parameter_space( |
|
alpha, |
|
beta, |
|
xmin=0, |
|
xmax=1, |
|
ymin=0, |
|
ymax=1, |
|
npts=500, |
|
max_iter=40, |
|
scheme="prism", |
|
draw_boundary=False, |
|
): |
|
X = np.linspace(xmax, xmin, npts, endpoint=False) |
|
X = np.flipud(X) |
|
Y = np.linspace(ymax, ymin, npts, endpoint=False) |
|
|
|
U, V = np.meshgrid(X, Y) |
|
Z = U + V * 1j |
|
|
|
exit_times = max_iter * np.ones(Z.shape, np.int32) |
|
mask = Z.imag < Z.real |
|
exit_times[np.logical_not(mask)] = 0 |
|
|
|
for k in range(max_iter): |
|
Z[mask] = ( |
|
Z[mask].imag / Z[mask].real |
|
+ ((Z[mask].imag - 1) / Z[mask].real + np.floor(1 / Z[mask].real)) * 1j |
|
) |
|
old_mask = mask |
|
mask = np.logical_and(Z.imag > 0, Z.imag < Z.real) |
|
exit_times[mask ^ old_mask] = k + 1 |
|
|
|
fig, ax = plt.subplots() |
|
ax.set_xlim((xmin, xmax)) |
|
ax.set_ylim((ymin, ymax)) |
|
exit_times[0, 0] = 0 |
|
im = ax.imshow( |
|
exit_times, |
|
cmap=scheme, |
|
aspect="equal", |
|
extent=(X.min(), X.max(), Y.min(), Y.max()), |
|
) |
|
|
|
ax.fill_between(X, X, 1.0, color=(1, 1, 1)) |
|
if draw_boundary: |
|
for k in range(1, 16): |
|
ax.plot([0, 1 / k], [1, 0], "k", lw=0.5) |
|
ax.plot([1 / k, 1 / k], [1, 0], "k", lw=0.5) |
|
ax.plot([1 / 2, 1], [0, 1 / k], "k", lw=0.5) |
|
|
|
|
|
ax.plot(alpha, beta, "ro", markersize=5, markeredgecolor="white") |
|
|
|
ax.set_title("Parameter Space") |
|
ax.set_xlabel("Alpha") |
|
ax.set_ylabel("Beta") |
|
|
|
return fig |
|
|
|
|
|
def itm_image(alpha, beta, interval): |
|
out = [] |
|
if interval[0] < 1 - alpha: |
|
out.append((interval[0] + alpha, min(interval[1], 1 - alpha) + alpha)) |
|
if interval[0] < 1 - beta and interval[1] > 1 - alpha: |
|
out.append( |
|
(max(interval[0], 1 - alpha) + beta, min(interval[1], 1 - beta) + beta) |
|
) |
|
if interval[1] > 1 - beta: |
|
out.append((max(interval[0], 1 - beta) + beta - 1, interval[1] + beta - 1)) |
|
return out |
|
|
|
|
|
def plot_images(alpha, beta, nimages=100): |
|
images = [(0, 1)] |
|
fig, ax = plt.subplots() |
|
for n in range(nimages): |
|
beg, end = zip(*images) |
|
ax.hlines([n / nimages] * len(beg), beg, end) |
|
images_next = [] |
|
for image in images: |
|
images_next.extend(itm_image(alpha, beta, image)) |
|
images = images_next |
|
ax.set_title(f"alpha={alpha:.2f}, beta={beta:.2f}") |
|
ax.set_xlim(0, 1) |
|
ax.set_ylim(0, 1) |
|
ax.set_xlabel("x") |
|
ax.set_ylabel("Iteration") |
|
return fig |
|
|
|
|
|
def main(): |
|
st.set_page_config(layout="wide") |
|
st.title("Interactive Visualization of Interval Translation Maps") |
|
|
|
st.sidebar.header("Controls") |
|
|
|
|
|
if "alpha" not in st.session_state: |
|
st.session_state.alpha = 0.6 |
|
if "beta" not in st.session_state: |
|
st.session_state.beta = 0.3 |
|
|
|
|
|
def update_alpha(increment): |
|
st.session_state.alpha = max(0, min(1, st.session_state.alpha + increment)) |
|
|
|
|
|
def update_beta(increment): |
|
st.session_state.beta = max(0, min(1, st.session_state.beta + increment)) |
|
|
|
|
|
st.sidebar.subheader("Alpha Control") |
|
col1, col2, col3, col4, col5 = st.sidebar.columns([1, 1, 2, 1, 1]) |
|
with col1: |
|
st.button("▼▼", on_click=update_alpha, args=(-0.1,), key="alpha_down_large") |
|
with col2: |
|
st.button("▼", on_click=update_alpha, args=(-0.01,), key="alpha_down") |
|
with col3: |
|
st.write(f"Alpha: {st.session_state.alpha:.2f}") |
|
with col4: |
|
st.button("▲", on_click=update_alpha, args=(0.01,), key="alpha_up") |
|
with col5: |
|
st.button("▲▲", on_click=update_alpha, args=(0.1,), key="alpha_up_large") |
|
|
|
|
|
st.sidebar.subheader("Beta Control") |
|
col1, col2, col3, col4, col5 = st.sidebar.columns([1, 1, 2, 1, 1]) |
|
with col1: |
|
st.button("▼▼", on_click=update_beta, args=(-0.1,), key="beta_down_large") |
|
with col2: |
|
st.button("▼", on_click=update_beta, args=(-0.01,), key="beta_down") |
|
with col3: |
|
st.write(f"Beta: {st.session_state.beta:.2f}") |
|
with col4: |
|
st.button("▲", on_click=update_beta, args=(0.01,), key="beta_up") |
|
with col5: |
|
st.button("▲▲", on_click=update_beta, args=(0.1,), key="beta_up_large") |
|
|
|
col1, col2 = st.columns(2) |
|
|
|
with col1: |
|
st.subheader("Parameter Space") |
|
fig_param = plot_parameter_space( |
|
st.session_state.alpha, st.session_state.beta, scheme="prism" |
|
) |
|
st.pyplot(fig_param) |
|
|
|
with col2: |
|
st.subheader("Image Plot") |
|
fig_images = plot_images(st.session_state.alpha, st.session_state.beta) |
|
st.pyplot(fig_images) |
|
|
|
st.caption( |
|
"Use the ▲/▲▲ and ▼/▼▼ buttons in the sidebar to adjust Alpha and Beta values. The red dot in the Parameter Space plot shows the current (Alpha, Beta) position." |
|
) |
|
|
|
st.markdown(""" |
|
## About this Visualization |
|
|
|
This interactive tool visualizes Interval Translation Maps (ITMs) and their parameter space. |
|
|
|
- The left plot shows the parameter space of ITMs. The red dot indicates the current (Alpha, Beta) values. |
|
- The right plot displays the images of the interval [0,1] under repeated application of the ITM. |
|
|
|
Use the up and down arrow buttons in the sidebar to adjust the alpha and beta parameters and observe how they affect both plots. |
|
- Single arrows (▲/▼) change values by 0.01 |
|
- Double arrows (▲▲/▼▼) change values by 0.1 |
|
""") |
|
|
|
|
|
if __name__ == "__main__": |
|
main() |