kevinkyi's picture
Upload app.py with huggingface_hub
ea97903 verified
raw
history blame
6.55 kB
import math, json
import gradio as gr
import pandas as pd
from typing import Dict, Any
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
LLM_ID = "HuggingFaceTB/SmolLM2-135M-Instruct"
tokenizer = AutoTokenizer.from_pretrained(LLM_ID)
llm = pipeline(
task="text-generation",
model=AutoModelForCausalLM.from_pretrained(LLM_ID),
tokenizer=tokenizer,
device_map="auto",
)
def projectile_calc(v0_mps: float, theta_deg: float, y0_m: float, g: float, weight_kg: float) -> Dict[str, Any]:
errors = []
if not (0 < v0_mps <= 500): errors.append("Initial speed must be in (0, 500] m/s.")
if not (-10 <= theta_deg <= 90): errors.append("Launch angle must be between -10° and 90°.")
if not (0 <= y0_m <= 1000): errors.append("Initial height must be in [0, 1000] m.")
if not (1 <= g <= 50): errors.append("Gravity must be in [1, 50] m/s².")
if not (0.05 <= weight_kg <= 50): errors.append("Ball weight must be in [0.05, 50] kg.")
if errors:
return {"ok": False, "errors": errors}
theta_rad = math.radians(theta_deg)
v0x = v0_mps * math.cos(theta_rad)
v0y = v0_mps * math.sin(theta_rad)
disc = v0y**2 + 2.0 * g * y0_m
t_flight = (v0y + math.sqrt(max(disc, 0.0))) / g
t_flight = max(t_flight, 0.0)
range_m = v0x * t_flight
t_apex = max(v0y / g, 0.0)
y_apex = y0_m + v0y * t_apex - 0.5 * g * t_apex**2
vy_final = -math.sqrt(max(v0y**2 + 2.0 * g * y0_m, 0.0))
v_impact = math.sqrt(v0x**2 + vy_final**2)
return {
"ok": True,
"inputs": {
"v0_mps": v0_mps,
"theta_deg": theta_deg,
"y0_m": y0_m,
"g_mps2": g,
"weight_kg": weight_kg,
},
"derived": {
"t_apex_s": t_apex,
},
"outputs": {
"time_of_flight_s": t_flight,
"range_m": range_m,
"max_height_m": y_apex,
"impact_speed_mps": v_impact,
},
"notes": {
"assumptions": ["No air resistance.", "Flat landing at y=0.", "Constant gravity g."],
}
}
SYSTEM_PROMPT = (
"You write a single concise sentence using ONLY numbers provided in the JSON. "
"Do not invent numbers or facts. Use units exactly as given; round to 2–3 sig figs if needed."
)
def llm_one_sentence(structured: Dict[str, Any]) -> str:
i = structured["inputs"]
d = structured["derived"]
o = structured["outputs"]
payload = {
"inputs": {
"v0_mps": round(i["v0_mps"], 3),
"theta_deg": round(i["theta_deg"], 3),
"y0_m": round(i["y0_m"], 3),
"g_mps2": round(i["g_mps2"], 3),
"weight_kg": round(i["weight_kg"], 3),
},
"derived": {
"t_apex_s": round(d["t_apex_s"], 4),
},
"outputs": {
"time_of_flight_s": round(o["time_of_flight_s"], 4),
"range_m": round(o["range_m"], 3),
"max_height_m": round(o["max_height_m"], 3),
"impact_speed_mps": round(o["impact_speed_mps"], 3),
}
}
instruction = (
"Produce EXACTLY one sentence of this form using only the JSON values:\n"
""If you threw a ball that weighed {weight_kg} kg at v0={v0_mps} m/s, "
"θ={theta_deg}°, from y0={y0_m} m under g={g_mps2} m/s², "
"it would stay in the air for {time_of_flight_s} s, reach {max_height_m} m, "
"travel {range_m} m, and impact at {impact_speed_mps} m/s.""
)
user_prompt = f"JSON:\n{json.dumps(payload, indent=2)}\n\nFollow the instruction exactly."
prompt = f"<|system|>\n{SYSTEM_PROMPT}\n<|user|>\n{instruction}\n\n{user_prompt}\n<|assistant|>"
out = llm(prompt, max_new_tokens=120, do_sample=False, temperature=0.0, return_full_text=False)
return out[0]["generated_text"].strip()
def run_once(v0_mps, theta_deg, y0_m, g_mps2, weight_kg):
rec = projectile_calc(float(v0_mps), float(theta_deg), float(y0_m), float(g_mps2), float(weight_kg))
if not rec.get("ok", False):
errs = rec.get("errors", ["Invalid input."])
df = pd.DataFrame([{"Error": "; ".join(errs)}])
return df, "Please adjust inputs."
i, d, o = rec["inputs"], rec["derived"], rec["outputs"]
df = pd.DataFrame([{
"v0_mps [m/s]": round(i["v0_mps"], 3),
"theta_deg [deg]": round(i["theta_deg"], 3),
"y0_m [m]": round(i["y0_m"], 3),
"g_mps2 [m/s²]": round(i["g_mps2"], 3),
"weight_kg [kg]": round(i["weight_kg"], 3),
"t_apex_s [s]": round(d["t_apex_s"], 4),
"max_height_m [m]": round(o["max_height_m"], 3),
"time_of_flight_s [s]": round(o["time_of_flight_s"], 4),
"range_m [m]": round(o["range_m"], 3),
"impact_speed_mps [m/s]": round(o["impact_speed_mps"], 3),
}])
sentence = llm_one_sentence(rec)
return df, sentence
with gr.Blocks(title="Projectile Motion — Deterministic + One-Sentence LLM") as demo:
gr.Markdown("# Projectile Motion — Deterministic Calculator + One-Sentence Explanation")
gr.Markdown("Deterministic projectile motion (no air resistance). See all inputs and derived values, plus a single, grounded sentence.")
with gr.Row():
v0 = gr.Slider(1, 200, value=30.0, step=0.5, label="Initial speed v₀ [m/s]")
theta = gr.Slider(-10, 90, value=45.0, step=0.5, label="Launch angle θ [deg]")
y0 = gr.Slider(0, 20, value=1.5, step=0.1, label="Initial height y₀ [m]")
g = gr.Slider(5, 20, value=9.81, step=0.01, label="Gravity g [m/s²]")
w = gr.Slider(0.05, 10.0, value=0.43, step=0.01, label="Ball weight [kg]")
run_btn = gr.Button("Compute")
results_df = gr.Dataframe(
headers=[
"v0_mps [m/s]", "theta_deg [deg]", "y0_m [m]", "g_mps2 [m/s²]", "weight_kg [kg]",
"t_apex_s [s]", "max_height_m [m]", "time_of_flight_s [s]", "range_m [m]", "impact_speed_mps [m/s]"
],
label="All values (inputs + derived)",
interactive=False
)
explain_md = gr.Markdown(label="One-sentence explanation")
run_btn.click(run_once, inputs=[v0, theta, y0, g, w], outputs=[results_df, explain_md])
gr.Examples(
examples=[
[30.0, 45.0, 1.5, 9.81, 0.43],
[20.0, 30.0, 0.0, 9.81, 0.145],
[40.0, 60.0, 0.0, 9.81, 0.45],
],
inputs=[v0, theta, y0, g, w],
label="Representative cases",
examples_per_page=3,
cache_examples=False,
)
if __name__ == "__main__":
demo.launch()