File size: 5,101 Bytes
5ac2f2d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8a63926
5ac2f2d
 
 
 
8a63926
 
 
 
 
5ac2f2d
 
 
 
 
 
 
 
 
 
8a63926
 
 
 
5ac2f2d
 
 
 
8a63926
5ac2f2d
8a63926
 
5ac2f2d
 
 
 
 
 
 
 
8a63926
 
 
5ac2f2d
 
 
8a63926
 
5ac2f2d
 
8a63926
5ac2f2d
8a63926
 
 
 
5ac2f2d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8a63926
 
5ac2f2d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8a63926
5ac2f2d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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
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
import json
import gradio as gr
import pandas as pd
from solver import (
    generate_random_instance,
    solve_vrp,
    plot_solution,
    parse_uploaded_csv,
    make_template_dataframe,
)

TITLE = "Ride-Sharing Optimizer (Capacitated VRP) β€” Gradio Demo"
DESC = """
This demo assigns **stops (riders)** to **drivers (vehicles)** with a simple, fast heuristic:
**Sweep clustering** β†’ **Greedy routing** β†’ **2-opt improvement**.  
You can **generate a sample** dataset or **upload a CSV** with columns:
`id,x,y,demand,tw_start,tw_end,service`.

- **Capacity** = max riders per vehicle (sum of `demand` per route).
- **Time windows** are *soft* (violations are reported in metrics).
- Distances are Euclidean on the X-Y plane for clarity.

πŸ’‘ Tip: Start with the generator, then switch to your CSV.
"""

FOOTER = "Made with ❀️ using Gradio. No native dependencies; runs quickly on Spaces."


# -----------------------------
# Functions
# -----------------------------
def run_generator(n_clients, n_vehicles, capacity, spread, demand_min, demand_max, seed):
    df = generate_random_instance(
        n_clients=n_clients,
        n_vehicles=n_vehicles,
        capacity=capacity,
        spread=spread,
        demand_min=demand_min,
        demand_max=demand_max,
        seed=seed,
    )
    depot = (0.0, 0.0)
    sol = solve_vrp(df, depot=depot, n_vehicles=n_vehicles, capacity=capacity, speed=1.0)

    # βœ… plot_solution now returns a PIL image
    img = plot_solution(df, sol, depot=depot)

    route_table = sol["assignments_table"]
    metrics = json.dumps(sol["metrics"], indent=2)

    return img, route_table, metrics, df


def run_csv(file, n_vehicles, capacity):
    if file is None:
        raise gr.Error("Please upload a CSV first.")
    try:
        df = parse_uploaded_csv(file)
    except Exception as e:
        raise gr.Error(f"CSV parsing error: {e}")

    depot = (0.0, 0.0)
    sol = solve_vrp(df, depot=depot, n_vehicles=n_vehicles, capacity=capacity, speed=1.0)

    img = plot_solution(df, sol, depot=depot)

    route_table = sol["assignments_table"]
    metrics = json.dumps(sol["metrics"], indent=2)
    return img, route_table, metrics


def download_template():
    return make_template_dataframe()


# -----------------------------
# UI Layout
# -----------------------------
with gr.Blocks(title=TITLE) as demo:
    gr.Markdown(f"# {TITLE}")
    gr.Markdown(DESC)

    with gr.Tab("πŸ”€ Generate sample"):
        with gr.Row():
            with gr.Column():
                n_clients = gr.Slider(5, 200, value=30, step=1, label="Number of riders (clients)")
                n_vehicles = gr.Slider(1, 20, value=4, step=1, label="Number of drivers (vehicles)")
                capacity = gr.Slider(1, 50, value=10, step=1, label="Vehicle capacity (sum of demand)")
                spread = gr.Slider(10, 200, value=50, step=1, label="Spatial spread (larger = wider map)")
                demand_min = gr.Slider(1, 5, value=1, step=1, label="Min demand per stop")
                demand_max = gr.Slider(1, 10, value=3, step=1, label="Max demand per stop")
                seed = gr.Slider(0, 9999, value=42, step=1, label="Random seed")
                run_btn = gr.Button("πŸš— Generate & Optimize", variant="primary")
            with gr.Column():
                img = gr.Image(type="pil", label="Route Visualization", interactive=False)

        with gr.Row():
            route_df = gr.Dataframe(label="Route assignments (per stop)", wrap=True)
            metrics = gr.Code(label="Metrics (JSON)")
        with gr.Accordion("Show generated dataset", open=False):
            data_out = gr.Dataframe(label="Generated input data")

        run_btn.click(
            fn=run_generator,
            inputs=[n_clients, n_vehicles, capacity, spread, demand_min, demand_max, seed],
            outputs=[img, route_df, metrics, data_out],
        )

    with gr.Tab("πŸ“„ Upload CSV"):
        with gr.Row():
            with gr.Column():
                file = gr.File(label="Upload CSV (id,x,y,demand,tw_start,tw_end,service)")
                dl_tmp = gr.Button("Get CSV Template")
                n_vehicles2 = gr.Slider(1, 50, value=5, step=1, label="Number of drivers (vehicles)")
                capacity2 = gr.Slider(1, 200, value=15, step=1, label="Vehicle capacity (sum of demand)")
                run_btn2 = gr.Button("πŸ“ˆ Optimize uploaded data", variant="primary")
            with gr.Column():
                img2 = gr.Image(type="pil", label="Route Visualization", interactive=False)

        with gr.Row():
            route_df2 = gr.Dataframe(label="Route assignments (per stop)")
            metrics2 = gr.Code(label="Metrics (JSON)")

        run_btn2.click(
            fn=run_csv,
            inputs=[file, n_vehicles2, capacity2],
            outputs=[img2, route_df2, metrics2],
        )

        def _tmpl():
            return gr.File.update(value=None), download_template()

        dl_tmp.click(fn=_tmpl, outputs=[file, route_df2], inputs=None)

    gr.Markdown(f"---\n{FOOTER}")

if __name__ == "__main__":
    demo.launch()