aciang commited on
Commit
fce8f29
·
verified ·
1 Parent(s): cb4a7d6

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +1 -3
  2. app.py +119 -80
  3. requirements.txt +5 -1
README.md CHANGED
@@ -1,5 +1,5 @@
1
  ---
2
- title: LanguageBridge — Math Fast Agent (SymPy, Robust)
3
  emoji: 🧮
4
  colorFrom: yellow
5
  colorTo: blue
@@ -8,5 +8,3 @@ sdk_version: "4.44.1"
8
  app_file: app.py
9
  pinned: false
10
  ---
11
-
12
- 純 SymPy 的數學推理小助手:支援一次貼上長式、因式分解、微分/積分,並能從多行敘述中自動擷取真正的方程行。**不依賴 torch**,可在 CPU Basic 上穩定運作。
 
1
  ---
2
+ title: LanguageBridge — Math Hybrid (Phi + SymPy)
3
  emoji: 🧮
4
  colorFrom: yellow
5
  colorTo: blue
 
8
  app_file: app.py
9
  pinned: false
10
  ---
 
 
app.py CHANGED
@@ -1,97 +1,136 @@
1
 
2
- import re
3
  import gradio as gr
4
  import sympy as sp
5
 
6
- TITLE = "LanguageBridge — Math Fast Agent (SymPy, Robust)"
7
 
8
- # ---- 全形轉半形表 ----
9
- FULL2HALF = str.maketrans({
10
- "+":"+","-":"-","*":"*","/":"/","^":"^","=":"=",",":",","(":"(",")":")",":":":",";":";","×":"*"
11
- })
12
 
13
- def _normalize(text: str) -> str:
14
- t = (text or "").translate(FULL2HALF)
15
- t = t.replace("\u3000"," ").strip()
16
- return t
17
 
18
- def _pick_equation_lines(text: str):
19
- # 挑出真正像方程式的行(含 '='),過濾純敘述行。
20
- eq_lines = []
21
- for ln in text.splitlines():
22
- s = ln.strip()
23
- if not s:
24
- continue
25
- if "=" in s and re.search(r"[A-Za-z0-9πe]|[+\-*/^()]", s):
26
- eq_lines.append(s)
27
- if not eq_lines:
28
- m = re.search(r"([^\n=]+=[^\n=]+)", text)
29
- if m:
30
- eq_lines.append(m.group(1).strip())
31
- return eq_lines
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- def solve_math(q: str):
34
- q = _normalize(q)
35
  if not q:
36
- return "請輸入算式或方程,例如:2x+3=11;或:sin(x)**2 + cos(x)**2;或:factor(x**2-9)"
37
 
38
- try:
39
- # 先嘗試方程/聯立模式
40
- eq_lines = _pick_equation_lines(q)
41
- if eq_lines:
42
- eqs, syms = [], set()
43
- for s in eq_lines:
44
- if "=" not in s:
45
- continue
46
- left, right = s.split("=", 1)
47
- eq = sp.Eq(sp.sympify(left), sp.sympify(right))
48
- eqs.append(eq)
49
- syms |= eq.free_symbols
50
- if hasattr(eq, "rhs"):
51
- syms |= eq.rhs.free_symbols
52
- if not syms:
53
- syms = {sp.symbols("x")}
54
- sol = sp.solve(eqs, list(syms), dict=True)
55
- if not sol:
56
- return "無解或需要更多條件。"
57
- lines = []
58
- for i, s in enumerate(sol, 1):
59
- items = [f"{k} = {sp.simplify(v)}" for k, v in s.items()]
60
- lines.append(f" {i}: " + ", ".join(items))
61
- return "\n".join(lines)
 
 
 
 
62
 
63
- # 無等號:表達式模式 → 給常見操作
64
  expr = sp.sympify(q)
65
- out = []
66
- try:
67
- out.append(f"簡化:{sp.simplify(expr)}")
68
- except Exception:
69
- pass
70
- try:
71
- fac = sp.factor(expr)
72
- if fac != expr:
73
- out.append(f"因式分解:{fac}")
74
- except Exception:
75
- pass
76
- try:
77
- x = list(expr.free_symbols)[0] if expr.free_symbols else sp.symbols("x")
78
- out.append(f"對 {x} 微分:{sp.diff(expr, x)}")
79
- out.append(f"對 {x} 積分:{sp.integrate(expr, x)}")
80
- except Exception:
81
- pass
82
- return "\n".join(out) if out else f"結果:{expr}"
83
-
84
  except Exception as e:
85
- return f"(SymPy 解析失敗) {e}\n(提示)若要讓 LLM 協助長敘述理解,請改用混合版或安裝 torch/transformers。"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
  with gr.Blocks(title=TITLE) as demo:
88
- gr.Markdown(f"## {TITLE}\n貼上算式(可多行;含敘述亦可,系統會自動擷取方程式):")
89
- q = gr.Textbox(lines=6, label="題目 / 算式(可含聯立方程或敘述)")
90
- out = gr.Textbox(lines=12, label="輸出")
91
- btn = gr.Button("送出 🚀")
92
- # 使用 concurrency_limit 代替舊的 concurrency_count,避免 Spaces 日誌警告/報錯
93
- btn.click(fn=solve_math, inputs=q, outputs=out, concurrency_limit=2)
94
 
95
- # 在本地執行才 launch;Spaces 會自動匯入 app.py
96
  if __name__ == "__main__":
97
- demo.launch(share=True, max_threads=2)
 
1
 
2
+ import os, re, time
3
  import gradio as gr
4
  import sympy as sp
5
 
6
+ TITLE = "LanguageBridge — Math Hybrid (Phi + SymPy)"
7
 
8
+ DEFAULT_MODEL_ID = os.environ.get("MODEL_ID", "microsoft/phi-2")
9
+ MAX_NEW_TOKENS = int(os.environ.get("MAX_NEW_TOKENS", "96"))
 
 
10
 
11
+ _llm = None
12
+ _tok = None
 
 
13
 
14
+ def _try_load_llm():
15
+ global _llm, _tok
16
+ if _llm is not None:
17
+ return True
18
+ try:
19
+ import torch
20
+ from transformers import AutoModelForCausalLM, AutoTokenizer
21
+ _tok = AutoTokenizer.from_pretrained(DEFAULT_MODEL_ID, use_fast=True)
22
+ _llm = AutoModelForCausalLM.from_pretrained(
23
+ DEFAULT_MODEL_ID,
24
+ torch_dtype=torch.float32,
25
+ device_map="auto"
26
+ )
27
+ return True
28
+ except Exception:
29
+ _llm = None
30
+ _tok = None
31
+ return False
32
+
33
+ def _llm_parse_to_math(text):
34
+ # Rewrite user text to algebraic expression(s); fallback to original text
35
+ if not _try_load_llm():
36
+ return text
37
+ import torch
38
+ from transformers import AutoTokenizer
39
+ prompt = (
40
+ "Rewrite the following math question as a pure algebraic expression "
41
+ "or semicolon-separated equations suitable for SymPy. "
42
+ "Return only the expression/equations without explanations.\n\n"
43
+ f"Question:\n{text}\n\nExpression:\n"
44
+ )
45
+ ids = _tok(prompt, return_tensors="pt").input_ids.to(_llm.device)
46
+ gen = _llm.generate(
47
+ ids,
48
+ max_new_tokens=MAX_NEW_TOKENS,
49
+ do_sample=False,
50
+ pad_token_id=_tok.eos_token_id
51
+ )
52
+ out = _tok.decode(gen[0], skip_special_tokens=True)
53
+ if "Expression:" in out:
54
+ out = out.split("Expression:", 1)[-1].strip()
55
+ out = out.replace("^", "**").strip()
56
+ return out or text
57
 
58
+ def _solve_with_sympy(q):
59
+ q = (q or "").strip()
60
  if not q:
61
+ return "Please enter an expression or equations. Example: 2*x + 5 = 11; or: sin(x)**2 + cos(x)**2"
62
 
63
+ if "=" in q:
64
+ parts = []
65
+ for seg in q.split(";"):
66
+ parts.extend([s for s in seg.split("\n")])
67
+ eqs = []
68
+ syms = set()
69
+ for s in [p.strip() for p in parts if p.strip()]:
70
+ if "=" not in s:
71
+ expr = sp.sympify(s)
72
+ eqs.append(sp.Eq(expr, 0))
73
+ syms |= expr.free_symbols
74
+ continue
75
+ left, right = s.split("=", 1)
76
+ L = sp.sympify(left)
77
+ R = sp.sympify(right)
78
+ eqs.append(sp.Eq(L, R))
79
+ syms |= L.free_symbols
80
+ syms |= R.free_symbols
81
+ if not syms:
82
+ syms = {sp.symbols("x")}
83
+ sol = sp.solve(eqs, list(syms), dict=True)
84
+ if not sol:
85
+ return "No solution or more conditions are required."
86
+ lines = []
87
+ for i, d in enumerate(sol, 1):
88
+ pretty = ", ".join([f"{k} = {sp.simplify(v)}" for k, v in d.items()])
89
+ lines.append(f"Solution {i}: {pretty}")
90
+ return "\n".join(lines)
91
 
92
+ try:
93
  expr = sp.sympify(q)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  except Exception as e:
95
+ return f"SymPy parse failed: {e}"
96
+
97
+ tips = []
98
+ try:
99
+ tips.append(f"Simplify: {sp.simplify(expr)}")
100
+ except Exception:
101
+ pass
102
+ try:
103
+ fac = sp.factor(expr)
104
+ if fac != expr:
105
+ tips.append(f"Factor: {fac}")
106
+ except Exception:
107
+ pass
108
+ try:
109
+ x = list(expr.free_symbols)[0] if expr.free_symbols else sp.symbols("x")
110
+ tips.append(f"d/d{x}: {sp.diff(expr, x)}")
111
+ tips.append(f"integrate wrt {x}: {sp.integrate(expr, x)}")
112
+ except Exception:
113
+ pass
114
+ return "\n".join(tips) if tips else f"Result: {expr}"
115
+
116
+ def hybrid_solve(user_text, use_llm):
117
+ text = (user_text or "").strip()
118
+ if not text:
119
+ return "Please enter an expression or problem statement."
120
+ if use_llm:
121
+ normalized = _llm_parse_to_math(text)
122
+ header = f"LLM normalized -> {normalized}\n---\n"
123
+ return header + _solve_with_sympy(normalized)
124
+ else:
125
+ return _solve_with_sympy(text)
126
 
127
  with gr.Blocks(title=TITLE) as demo:
128
+ gr.Markdown(f"## {TITLE}\nPaste text or math: LLM helps rewrite -> SymPy solves (optional).")
129
+ q = gr.Textbox(lines=6, label="Problem / Expression (semicolon or newline for systems)")
130
+ use_llm = gr.Checkbox(value=False, label="Use Phi-2 to normalize text first (optional)")
131
+ out = gr.Textbox(lines=12, label="Output")
132
+ btn = gr.Button("Solve", variant="primary")
133
+ btn.click(hybrid_solve, inputs=[q, use_llm], outputs=out, concurrency_limit=1)
134
 
 
135
  if __name__ == "__main__":
136
+ demo.launch()
requirements.txt CHANGED
@@ -1,3 +1,7 @@
1
  gradio==4.44.1
 
2
  sympy>=1.12
3
- huggingface_hub>=0.24.0
 
 
 
 
1
  gradio==4.44.1
2
+ huggingface_hub==0.23.5
3
  sympy>=1.12
4
+ torch==2.4.1
5
+ transformers==4.44.2
6
+ accelerate==0.34.2
7
+ safetensors>=0.4.4