kevinkyi commited on
Commit
ea97903
·
verified ·
1 Parent(s): 4e50d59

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +162 -0
app.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math, json
2
+ import gradio as gr
3
+ import pandas as pd
4
+ from typing import Dict, Any
5
+ from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
6
+
7
+ LLM_ID = "HuggingFaceTB/SmolLM2-135M-Instruct"
8
+ tokenizer = AutoTokenizer.from_pretrained(LLM_ID)
9
+ llm = pipeline(
10
+ task="text-generation",
11
+ model=AutoModelForCausalLM.from_pretrained(LLM_ID),
12
+ tokenizer=tokenizer,
13
+ device_map="auto",
14
+ )
15
+
16
+ def projectile_calc(v0_mps: float, theta_deg: float, y0_m: float, g: float, weight_kg: float) -> Dict[str, Any]:
17
+ errors = []
18
+ if not (0 < v0_mps <= 500): errors.append("Initial speed must be in (0, 500] m/s.")
19
+ if not (-10 <= theta_deg <= 90): errors.append("Launch angle must be between -10° and 90°.")
20
+ if not (0 <= y0_m <= 1000): errors.append("Initial height must be in [0, 1000] m.")
21
+ if not (1 <= g <= 50): errors.append("Gravity must be in [1, 50] m/s².")
22
+ if not (0.05 <= weight_kg <= 50): errors.append("Ball weight must be in [0.05, 50] kg.")
23
+ if errors:
24
+ return {"ok": False, "errors": errors}
25
+
26
+ theta_rad = math.radians(theta_deg)
27
+ v0x = v0_mps * math.cos(theta_rad)
28
+ v0y = v0_mps * math.sin(theta_rad)
29
+
30
+ disc = v0y**2 + 2.0 * g * y0_m
31
+ t_flight = (v0y + math.sqrt(max(disc, 0.0))) / g
32
+ t_flight = max(t_flight, 0.0)
33
+
34
+ range_m = v0x * t_flight
35
+ t_apex = max(v0y / g, 0.0)
36
+ y_apex = y0_m + v0y * t_apex - 0.5 * g * t_apex**2
37
+ vy_final = -math.sqrt(max(v0y**2 + 2.0 * g * y0_m, 0.0))
38
+ v_impact = math.sqrt(v0x**2 + vy_final**2)
39
+
40
+ return {
41
+ "ok": True,
42
+ "inputs": {
43
+ "v0_mps": v0_mps,
44
+ "theta_deg": theta_deg,
45
+ "y0_m": y0_m,
46
+ "g_mps2": g,
47
+ "weight_kg": weight_kg,
48
+ },
49
+ "derived": {
50
+ "t_apex_s": t_apex,
51
+ },
52
+ "outputs": {
53
+ "time_of_flight_s": t_flight,
54
+ "range_m": range_m,
55
+ "max_height_m": y_apex,
56
+ "impact_speed_mps": v_impact,
57
+ },
58
+ "notes": {
59
+ "assumptions": ["No air resistance.", "Flat landing at y=0.", "Constant gravity g."],
60
+ }
61
+ }
62
+
63
+ SYSTEM_PROMPT = (
64
+ "You write a single concise sentence using ONLY numbers provided in the JSON. "
65
+ "Do not invent numbers or facts. Use units exactly as given; round to 2–3 sig figs if needed."
66
+ )
67
+
68
+ def llm_one_sentence(structured: Dict[str, Any]) -> str:
69
+ i = structured["inputs"]
70
+ d = structured["derived"]
71
+ o = structured["outputs"]
72
+ payload = {
73
+ "inputs": {
74
+ "v0_mps": round(i["v0_mps"], 3),
75
+ "theta_deg": round(i["theta_deg"], 3),
76
+ "y0_m": round(i["y0_m"], 3),
77
+ "g_mps2": round(i["g_mps2"], 3),
78
+ "weight_kg": round(i["weight_kg"], 3),
79
+ },
80
+ "derived": {
81
+ "t_apex_s": round(d["t_apex_s"], 4),
82
+ },
83
+ "outputs": {
84
+ "time_of_flight_s": round(o["time_of_flight_s"], 4),
85
+ "range_m": round(o["range_m"], 3),
86
+ "max_height_m": round(o["max_height_m"], 3),
87
+ "impact_speed_mps": round(o["impact_speed_mps"], 3),
88
+ }
89
+ }
90
+ instruction = (
91
+ "Produce EXACTLY one sentence of this form using only the JSON values:\n"
92
+ ""If you threw a ball that weighed {weight_kg} kg at v0={v0_mps} m/s, "
93
+ "θ={theta_deg}°, from y0={y0_m} m under g={g_mps2} m/s², "
94
+ "it would stay in the air for {time_of_flight_s} s, reach {max_height_m} m, "
95
+ "travel {range_m} m, and impact at {impact_speed_mps} m/s.""
96
+ )
97
+ user_prompt = f"JSON:\n{json.dumps(payload, indent=2)}\n\nFollow the instruction exactly."
98
+ prompt = f"<|system|>\n{SYSTEM_PROMPT}\n<|user|>\n{instruction}\n\n{user_prompt}\n<|assistant|>"
99
+ out = llm(prompt, max_new_tokens=120, do_sample=False, temperature=0.0, return_full_text=False)
100
+ return out[0]["generated_text"].strip()
101
+
102
+ def run_once(v0_mps, theta_deg, y0_m, g_mps2, weight_kg):
103
+ rec = projectile_calc(float(v0_mps), float(theta_deg), float(y0_m), float(g_mps2), float(weight_kg))
104
+ if not rec.get("ok", False):
105
+ errs = rec.get("errors", ["Invalid input."])
106
+ df = pd.DataFrame([{"Error": "; ".join(errs)}])
107
+ return df, "Please adjust inputs."
108
+ i, d, o = rec["inputs"], rec["derived"], rec["outputs"]
109
+ df = pd.DataFrame([{
110
+ "v0_mps [m/s]": round(i["v0_mps"], 3),
111
+ "theta_deg [deg]": round(i["theta_deg"], 3),
112
+ "y0_m [m]": round(i["y0_m"], 3),
113
+ "g_mps2 [m/s²]": round(i["g_mps2"], 3),
114
+ "weight_kg [kg]": round(i["weight_kg"], 3),
115
+ "t_apex_s [s]": round(d["t_apex_s"], 4),
116
+ "max_height_m [m]": round(o["max_height_m"], 3),
117
+ "time_of_flight_s [s]": round(o["time_of_flight_s"], 4),
118
+ "range_m [m]": round(o["range_m"], 3),
119
+ "impact_speed_mps [m/s]": round(o["impact_speed_mps"], 3),
120
+ }])
121
+ sentence = llm_one_sentence(rec)
122
+ return df, sentence
123
+
124
+ with gr.Blocks(title="Projectile Motion — Deterministic + One-Sentence LLM") as demo:
125
+ gr.Markdown("# Projectile Motion — Deterministic Calculator + One-Sentence Explanation")
126
+ gr.Markdown("Deterministic projectile motion (no air resistance). See all inputs and derived values, plus a single, grounded sentence.")
127
+
128
+ with gr.Row():
129
+ v0 = gr.Slider(1, 200, value=30.0, step=0.5, label="Initial speed v₀ [m/s]")
130
+ theta = gr.Slider(-10, 90, value=45.0, step=0.5, label="Launch angle θ [deg]")
131
+ y0 = gr.Slider(0, 20, value=1.5, step=0.1, label="Initial height y₀ [m]")
132
+ g = gr.Slider(5, 20, value=9.81, step=0.01, label="Gravity g [m/s²]")
133
+ w = gr.Slider(0.05, 10.0, value=0.43, step=0.01, label="Ball weight [kg]")
134
+
135
+ run_btn = gr.Button("Compute")
136
+
137
+ results_df = gr.Dataframe(
138
+ headers=[
139
+ "v0_mps [m/s]", "theta_deg [deg]", "y0_m [m]", "g_mps2 [m/s²]", "weight_kg [kg]",
140
+ "t_apex_s [s]", "max_height_m [m]", "time_of_flight_s [s]", "range_m [m]", "impact_speed_mps [m/s]"
141
+ ],
142
+ label="All values (inputs + derived)",
143
+ interactive=False
144
+ )
145
+ explain_md = gr.Markdown(label="One-sentence explanation")
146
+
147
+ run_btn.click(run_once, inputs=[v0, theta, y0, g, w], outputs=[results_df, explain_md])
148
+
149
+ gr.Examples(
150
+ examples=[
151
+ [30.0, 45.0, 1.5, 9.81, 0.43],
152
+ [20.0, 30.0, 0.0, 9.81, 0.145],
153
+ [40.0, 60.0, 0.0, 9.81, 0.45],
154
+ ],
155
+ inputs=[v0, theta, y0, g, w],
156
+ label="Representative cases",
157
+ examples_per_page=3,
158
+ cache_examples=False,
159
+ )
160
+
161
+ if __name__ == "__main__":
162
+ demo.launch()