CoolFace
Apppublic

Aarondard5/constrained_multiOT

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
streamlit_app.py211 linesDownload Raw Back to root
1import os2import sys3import warnings4warnings.simplefilter('ignore')5 6import streamlit as st7 8st.set_page_config(page_title="MultiOT Network", layout="wide")9 10import numpy as np11import pandas as pd12import networkx as nx13import matplotlib14matplotlib.use('Agg')15import matplotlib.pyplot as plt16 17CODE_DIR = os.path.dirname(os.path.abspath(__file__))18sys.path.insert(0, CODE_DIR)19 20import dynamics_capacity as dyn_e_constr21import initialization as init22import generate_planar as gpl23import tools as tl24import analysis_tools as atl25 26# ── Fixed parameters (match notebook) ────────────────────────────────27N         = 40028seedG     = 1129seedF     = 230weigth    = 10.31N_real    = 1           # single realization for speed32tol       = 1e-233coupling  = 'l2'34dt        = 0.0935tot_time  = 50036alpha     = 237seed_dyn  = seedG + seedF38 39OUT_DIR = os.path.join(CODE_DIR, '..', 'data', 'output', 'planar')40os.makedirs(OUT_DIR, exist_ok=True)41 42# ── Build graph once ─────────────────────────────────────────────────43@st.cache_resource44def build_graph():45    G = gpl.planar_graph(N, L_min=0.0, L_max=None, domain=(0, 0, 1, 1), seed=seedG)46    label   = f'planar_{N}_0.0_1.5'47    f_edges = os.path.join(OUT_DIR, label + str(seedG) + '.csv')48    init.from_nx_graphs2df([G], outfile=f_edges)49    graph, nnode, nedge, nodes, nodes_inter, nodeName2Id, _ = init.file2graph(f_edges, sep=',')50    f_coord = os.path.join(OUT_DIR, label + str(seedG) + 'coord.csv')51    init.from_nx_graph2coord([G], nodes, outfile=f_coord,52                              mapping=nodeName2Id, nodes_inter=nodes_inter)53    # mode '1' = import from file; input_path arg is unused in this mode54    init.coord_generation('1', OUT_DIR, f_coord, graph, nnode, sep=',')55    length0 = init.eucledian_bias('eucl', graph, length_inter0=0.1, haversine_on=False)56    nx.set_edge_attributes(graph,57                           {e: length0[i] for i, e in enumerate(graph.edges())},58                           'length0')59    return G, graph, nnode, nedge, nodes, nodes_inter, nodeName2Id, length060 61G, graph, nnode, nedge, nodes, nodes_inter, nodeName2Id, length0 = build_graph()62 63# ── Forcing (cached per p) ────────────────────────────────────────────64@st.cache_data65def get_forcing(p_val):66    label    = f'planar_{N}_{p_val}_1.5'67    f_path   = os.path.join(OUT_DIR, label + f'_forcing{seedF}.csv')68    raw      = tl.forcing_generate(G, p=p_val, weigth=weigth,69                                   pos=None, pos_center_of_mass=None, seed=seedF)70    pd.DataFrame(raw, columns=['source', 'sink', 'weight']).to_csv(71        f_path, sep=' ', header=True, index=False)72    forcing, _, _ = init.forcing_importing_from_file(73        f_path, nodes, nodes_inter, nodeName2Id,74        sep=' ', header=0, source='source', sink='sink')75    return forcing76 77# ── Dynamics (cached per beta, p, ce_pct) ────────────────────────────78@st.cache_data79def run_dynamics(beta_val, p_val, ce_pct):80    forcing = get_forcing(p_val)81 82    pflux_map_b = {0: beta_val, 'inter': 1.5, 'inter-super': 1.5}83    etypes  = nx.get_edge_attributes(graph, 'etype')84    pflux_b = np.array([pflux_map_b[etypes[e]] for e in graph.edges()])85 86    # unconstrained87    tdens_u, _, _, _, _, flux_u, _ = dyn_e_constr.dyn(88        graph, nodes, pflux_b, nedge, length0, forcing,89        tol, removed_e_id=0, plot_cost=False, N_real=N_real,90        verbose=0, seed=seed_dyn, tdens0=None, coupling=coupling,91        constraint=False, capacity=False,92        time_step=dt, alpha=alpha, tot_time=tot_time93    )94 95    # constrained: c_e = ce_pct-th percentile of unconstrained tdens96    c_e = float(np.percentile(tdens_u, max(1, ce_pct)))97    tdens_c, _, _, _, _, flux_c, _ = dyn_e_constr.dyn(98        graph, nodes, pflux_b, nedge, length0, forcing,99        tol, removed_e_id=0, plot_cost=False, N_real=N_real,100        verbose=0, seed=seed_dyn, tdens0=None, coupling=coupling,101        constraint=True, capacity=c_e,102        time_step=dt, alpha=alpha, tot_time=tot_time103    )104 105    return flux_u, flux_c, c_e106 107# ── G_plot (cached, never changes) ───────────────────────────────────108@st.cache_resource109def build_gplot():110    colors_map = {0: 'steelblue', 1: 'r', 2: 'g', 3: 'magenta',111                  'inter': 'black', 'inter-super': 'black'}112    G_p    = nx.Graph(graph)113    etypes = nx.get_edge_attributes(graph, 'etype')114    for e in graph.edges:115        G_p[e[0]][e[1]]['color'] = colors_map[etypes[e]]116    return G_p117 118G_plot   = build_gplot()119pos_plot = nx.get_node_attributes(G_plot, 'pos')120colors_e = list(nx.get_edge_attributes(G_plot, 'color').values())121 122# ═══════════════════════════════════════════════════════════════════════123# UI124# ═══════════════════════════════════════════════════════════════════════125st.title("Multi-commodity Optimal Transport — Interactive Network")126 127with st.sidebar:128    st.header("Parameters")129    beta_val = st.slider("β  (branching exponent)", 0.1, 1.9, 1.5, step=0.1, format="%.1f")130    p_val    = st.slider("p  (destination spread)",  0.0, 1.0, 0.0, step=0.1, format="%.1f")131    st.markdown("---")132    ce_pct   = st.slider("Edge capacity, c_e", 1, 100, 75, step=1)133    st.markdown("---")134    st.caption("**Source** ▲ green  |  **Sink** ◆ magenta")135    run_btn = st.button("Run", type="primary", use_container_width=True)136 137# ── Only compute when Run is clicked; reuse session state otherwise ───138if run_btn:139    with st.spinner("Running dynamics — this may take a few minutes …"):140        flux_u, flux_c, c_e = run_dynamics(141            round(beta_val, 1), round(p_val, 1), ce_pct)142    st.session_state['flux_u']   = flux_u143    st.session_state['flux_c']   = flux_c144    st.session_state['c_e']      = c_e145    st.session_state['p_cached'] = round(p_val, 1)146 147if 'flux_u' not in st.session_state:148    st.info("Set parameters in the sidebar and click **Run** to compute.")149    st.stop()150 151flux_u = st.session_state['flux_u']152flux_c = st.session_state['flux_c']153c_e    = st.session_state['c_e']154 155forcing      = get_forcing(st.session_state['p_cached'])156M            = forcing.shape[0]157flux_norm1_u = np.linalg.norm(flux_u, axis=1, ord=1)158flux_norm1_c = np.linalg.norm(flux_c, axis=1, ord=1)159 160# Commodity slider — default to OD pair with second-longest path161avgL_u        = atl.calculate_path_length(flux_u, length0)162default_comm  = int(np.argsort(-avgL_u)[1])163commodity_idx = st.slider(164    f"Commodity (OD pair)  —  {M} total", 0, M - 1, default_comm)165 166source = int(np.where(forcing[commodity_idx] > 0)[0][0])167sink   = int(np.where(forcing[commodity_idx] < 0)[0][0])168 169# ── Draw helper ───────────────────────────────────────────────────────170def draw_net(ax, flux, flux_norm1, title, info=None):171    w1, w0 = 0.01, 1.0172    # background: overall flux widths173    widths = [w1 * flux_norm1[i] for i in range(nedge)]174    nx.draw_networkx(G_plot, pos_plot, node_size=2, with_labels=False,175                     edge_color=colors_e, width=widths, ax=ax)176    # green overlay: selected OD pair177    widths_od = [0.1 + w0 * flux[i, commodity_idx] for i in range(nedge)]178    nx.draw_networkx(G_plot, pos_plot, node_size=0, with_labels=False,179                     edge_color='green', width=widths_od, ax=ax)180    # source: green triangle181    nx.draw_networkx_nodes(G_plot, pos_plot, node_shape='^',182                           nodelist=[source], node_size=300,183                           node_color=['green'], edgecolors='black', ax=ax)184    # sink: magenta diamond185    nx.draw_networkx_nodes(G_plot, pos_plot, node_shape='D',186                           nodelist=[sink], node_size=300,187                           node_color=['magenta'], edgecolors='black', ax=ax)188    ax.set_title(title, fontsize=13, pad=8)189    if info:190        ax.text(0.02, 0.97, info, transform=ax.transAxes, fontsize=9,191                va='top', bbox=dict(boxstyle='round', facecolor='white', alpha=0.7))192 193fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 7))194draw_net(ax1, flux_u, flux_norm1_u,195         f"Unconstrained   β={beta_val:.1f}   p={p_val:.1f}")196draw_net(ax2, flux_c, flux_norm1_c,197         f"Constrained   β={beta_val:.1f}   p={p_val:.1f}   c_e={ce_pct}th pct",198         info=f"c_e = {c_e:.5f}")199 200plt.tight_layout()201st.pyplot(fig)202plt.close(fig)203 204st.caption(205    f"Commodity **{commodity_idx}** │ "206    f"Source node **{source}** ▲ │ "207    f"Sink node **{sink}** ◆ │ "208    f"Total commodities: **{M}**"209)210 211