Skip to content

Commit 6bcf21a

Browse files
Addition of conversion function using re library
1 parent 57d3e4e commit 6bcf21a

File tree

8 files changed

+744
-149
lines changed

8 files changed

+744
-149
lines changed

app/evaluation.py

Lines changed: 180 additions & 116 deletions
Large diffs are not rendered by default.

app/evaluation_test_cases.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,38 +17,36 @@
1717
["", "", Params(), True],
1818
["", "x", Params(), False],
1919
["1+", "1", Params(), False],
20-
["x+1=0", "-2x-2=0", Params(), True],
20+
["x+1=0", "-2*x-2=0", Params(), True],
2121
["dy/dx", "diff(y, x)", Params(), True],
2222
["(x+y)/x", "1 + y/x", Params(), True],
23-
["∂y/∂x", "diff(y, x)", Params(), True],
24-
["∫f(x)dx", "int(f(x), x)", Params(), True],
25-
["∂^2y/∂x^2", "diff(diff(y, x), x)", Params(), True],
23+
["∫fdx", "int(f, x)", Params(), True],
2624
["dy/dx + 1", "diff(y, x) + 1", Params(), True],
27-
["∂y/∂x + 1", "diff(y, x) + 1", Params(), True],
2825
["dp/dt", "diff(p, t)", Params(), True],
2926
["dg/dm", "diff(y,x)", Params(), False],
30-
]
31-
test_cases2 = [
3227
["infty", "Infinity", Params(), True], #1
3328
["sqrt(-1)", "I", Params(), True],
3429
["sqrt(x**2)", "x", Params(), False],
3530
["1/(x-1)", "1/(1-x)", Params(), False],
3631
["x^2", "x**2", Params(), True],
3732
["x^^2", "x**2", Params(), False],
3833
["d^3y/dx^3", "diff(y, x, x, x)", Params(), True],
39-
["∫∫f(x)dxdy", "int(int(f(x), x), y)", Params(), True],
40-
["f(x)=x+1", "f(x)-x-1=0", Params(), True],
41-
["f(x) = x**2", "f(y) = y**2", Params(), False],#should this always be false?
34+
["∫∫fdxdy", "int(int(f, x), y)", Params(), True],
35+
["f=x+1", "f-x-1=0", Params(), True],
4236
["diff(y,x)+", "diff(y,x)+0", Params(), False],
4337
["d/dx(y", "diff(y, x)", Params(), False],
44-
["DiracDelta(x)", "0", Params(), False],
45-
["∫_{V_sys} ∂ρ/∂t dV", "int(partial(ρ)/partial(t), (V, V_sys))", Params(), True],
46-
["∮_{A_sys} ρu·n̂ dA", "Integral(rho * u.dot(n), (A, A_sys), circular=True)", Params(), True],
4738
["rho", "ρ", Params(), True],
4839
["Dx/Dt=-div(u)", "Dx/Dt+div(u)=0", Params(), True],
4940
["(1/rho)*Drho/Dt=-div(u)", "(1/ρ)*Dρ/Dt+div(u)=0", Params(), True],
50-
]
51-
test_cases3 = [
5241
["abs(x)", "sqrt(x**2)", Params(), False],
5342
["abs(x)", "sqrt(x**2)", Params(symbol_assumptions={"x": {"real": True},}), True],
54-
]
43+
]
44+
test_cases2 = [
45+
["nabla(f)=delf/delr*hat(r)+delf/deltheta*hat(theta)*delf/delz*hat(z)","grad(f)=delf/delr*hat(r)+delf/deltheta*hat(theta)*delf/delz*hat(z)",Params(),True],
46+
["∫_{V_sys} ∂ρ/∂t dV", "int_Vsys(partial(ρ)/partial(t), V)", Params(), True],
47+
["∫_{V_sys} ∂/∂t(ρ*u) dV", "int_Vsys(partial(ρ*u)/partial(t), V)", Params(), True],
48+
["∮_{A_sys} ρ*u·n dA", "Integral(rho * u.dot(n), (A, A_sys), circular=True)", Params(), True],
49+
["exp(a/b)","e^(a/b)",Params(),True],
50+
["exp(i*(omega*t-k*r))","cos(omega*t-k*r)+i*sin(omega*t-k*r)",Params(),True],
51+
["grad(p)=rho(g-a)","nabla(p)=ρ*(g-a)",Params(),True],
52+
]

app/evaluation_tests.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from .evaluation import Params, evaluation_function
55
except ImportError:
66
from evaluation import Params, evaluation_function
7-
from evaluation_test_cases import test_cases, test_cases2,test_cases3
7+
from evaluation_test_cases import test_cases, test_cases2
88

99

1010
class TestEvaluationFunction(unittest.TestCase):
@@ -15,8 +15,8 @@ class TestEvaluationFunction(unittest.TestCase):
1515
def test_multiple_cases(self):
1616
passed = 0
1717
failed = 0
18-
case = [test_cases, test_cases2, test_cases3]
19-
for i, (response, answer, params, expected) in enumerate(case[2], 1): #change here test_cases <-> test_cases2
18+
case = [test_cases, test_cases2]
19+
for i, (response, answer, params, expected) in enumerate(case[1], 1): #change here test_cases <-> test_cases2
2020
with self.subTest(test_case=i):
2121
result = evaluation_function(response, answer, params)
2222
is_correct = result.get("is_correct")

app/llm_conversion.py

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import os
2+
from dotenv import load_dotenv
3+
from langchain_openai import ChatOpenAI
4+
5+
from parameter import Params
6+
7+
def convert_diff(expr: str, params: Params) -> str:
8+
load_dotenv()
9+
llm = ChatOpenAI(
10+
model=os.environ['OPENAI_MODEL'],
11+
api_key=os.environ["OPENAI_API_KEY"],
12+
)
13+
prompt = fr"""
14+
Follow these steps carefully:
15+
A response is provided below. Convert it into an expression under the following rules.
16+
17+
When the following notations are found in the response, they must be replaced with the notation after the right arrow (->) after the square brackets.
18+
19+
---
20+
21+
- [dy/dx, d/dx(y), diff(y,x)] -> d_y_x
22+
**Pay special attention: diff(y,x) must always be converted to dydx, but ONLY if the variable names are y and x.**
23+
For example:
24+
✅ diff(g,m) → d_g_m
25+
❌ diff(g,m) → d_y_x (**WRONG**)
26+
27+
- [d^2y/dx^2, d**2y/dx**2, diff(y,x,x)] -> d2_y_x
28+
- [d^3y/dx^3, d**3y/dx**3, diff(y,x,x,x)] -> d3_y_x
29+
- [∂y/∂x, ∂/∂x(y), partial(y)/partial(x)] -> del_y_x
30+
**Note: diff(y,x) is NOT equivalent to del_y_x.**
31+
**Note: it must be in the form of del_[]_[] where the variable names are in the square brackets, and nothing else.**
32+
- [∂**2y/dx**2, diff(y,x,x), partial**2(y)/partial(x)**2] -> del2_y_x
33+
- [Dy/Dx, D/Dx(y)] -> (del_y_t+dot(v,grad(y)))
34+
Note that the first variable (in this case "y") always appears after "del_" and "grad(" whereas the second variable (in this case "x") is ignored.
35+
**Note: "diff(y,x)","dy/dx","d_y_x" and "∂y/∂x","del_y_x" are NOT equivalent to "Dy/Dx" or "D/Dx(y)" or "(del_y_t+dot(v,grad(y)))".**
36+
Example: "Drho/Dt+divg(u)" must be converted to "(del_rho_t+dot(v,grad(rho)))+divg(u)" but "∂rho/∂t+divg(u)" must be converted to "del_rho_t+divg(u)".
37+
38+
39+
---
40+
Important:
41+
**Even if the notation is SymPy-compatible, it still must be replaced.**
42+
Variable names used in the notations above are "x" and "y", but they can be any variable names depending on the input, except for the differentiation operator characters given below. Variable names can be more than one letter, such as "rho" or "phi".
43+
**Input variable names must NEVER be changed. Keep the original variable names exactly as they appear.**
44+
Example: dp/dt or diff(p,t) must be converted to d_p_t, not d_y_x or any other forms using any variables other than p and t.
45+
Differentiation operator characters are: "d", "D", "del", "∂", and "partial". The "**{{real number}}" such as "**2" or "**3" is also a part of the differentiation operator characters and not part of variable names.
46+
**Variable names are always case-sensitive. If the variable is capitalized, it must be capitalized in the response.**
47+
For example, if dp/dt or diff(p,t) is given, it must be converted to d_p_t, not d_P_t or any other forms.
48+
The differentiation operators "d" and "D" are different; they are also case-sensitive.
49+
Do not remove or add any characters except as required by the replacement rules.
50+
51+
All the notations in the same square brackets are equivalent.
52+
**Notations from different groups or not listed above are NOT equivalent.**
53+
54+
---
55+
56+
This is the response: {expr}
57+
Now convert it to the required expression.
58+
Output only the expression, without any additional text or explanation.
59+
Remove all spaces. If there are any spaces remaining, you must remove them.
60+
"""
61+
llm_response = llm.invoke(prompt)
62+
return llm_response
63+
64+
def convert_integral(expr: str, params: Params) -> str:
65+
load_dotenv()
66+
llm = ChatOpenAI(
67+
model=os.environ['OPENAI_MODEL'],
68+
api_key=os.environ["OPENAI_API_KEY"],
69+
)
70+
prompt = fr"""
71+
Follow these steps carefully:
72+
A response is provided below. Convert it into an expression under the following rules.
73+
74+
When the following notations are found in the response, they must be replaced with the notation after the right arrow (->) after the square brackets.
75+
---
76+
Single integrals:
77+
- [∫fdx, int(f,x), integrate(f,x), Integral(f,x)] -> intg(f,x)
78+
- [∮fdx, int(f,x,circular=True), integrate(f,x,circular=True), Integral(f,x,circular=True), oint(f,x)] -> ointg(f,x)
79+
- [∫ₐᵇfdx, ∫_a^bfdx, int_a^bfdx, int(f,(x,a,b)), integrate(f,(x,a,b)), Integral(f,(x,a,b))] -> intg(f,(x,a,b))
80+
The above is a definite integral case.
81+
- [∫ₐfdx, ∫_a^fdx, int_a^fdx, int(f,(x,a)), integrate(f,(x,a)), Integral(f,(x,a))] -> intg(f,x)
82+
Example: ∫_{{V_sys}} t dV must be converted to intg(t,V).
83+
As in the above example, if there is a lower limit and not an upper limit, it must be converted to intg(t,x), ignoring the lower limit.
84+
- [Integral(f,(x,a),circular=True), oint(f,(x,a)), int(f,(x,a),circular=True), ∮ₐfdx, ∮_{{a}}fdx] -> ointg(f,x)
85+
For the first three notations in the above group, the first argument of the second argument of the functions (Integral, oint, int) is the integration variable.
86+
Do not assume that the integration variable is always x.
87+
It must be "ointg", not "ointment"!
88+
Multiple integrals:
89+
- [∫∫fdxdy, int(int(f,x),y), integrate(f,x,y), Integral(f,x,y)] -> intg(intg(f,x),y)
90+
- [∫∫∫fdxdydz, int(int(int(f,x),y),z), integrate(f,x,y,z), Integral(f,x,y,z)] -> intg(intg(intg(f,x),y),z)
91+
Note that if there is an extra set of parentheses around "x,y,z", this is a definite case so it must be converted to intg(f,(x,y,z)).
92+
All the integrals may have limits like for single integrals, for example: int_a^b(int_c^d(int_e^f(g,x)),y),z) must be converted to intg(intg(intg(g,(x,e,f)),(y,c,d)),(z,a,b)).
93+
94+
For all integrals, all the variables succeeding the letter 'd' **and not 'd_'** is the integration variable (what the function is integrated with respect to).
95+
Example: ∫fdx means f is integrated with respect to x, and ∫∫fdxdy means f is integrated with respect to x and then y. Therefore the outputs must be intg(f,x) and intg(intg(f,x),y) respectively.
96+
The integrand f can be any expression, including a function, a variable, or a constant, and it can be very long.
97+
Example: ∫_{{V_sys}} del_rho_t+2 dV must be converted to intg(del_rho_t+2,V).
98+
99+
---
100+
Important:
101+
**Even if the notation is SymPy-compatible, it still must be replaced.**
102+
Variable names used in the notations above are "f", "x", "y", "a" and "b", but they can be any variable names depending on the input, except for the differentiation operator characters given below. Variable names can be more than one letter, such as "rho" or "phi".
103+
**Input variable names must NEVER be changed. Keep the original variable names exactly as they appear.**
104+
Example: ∫pdt or int(p,t) must be converted to intg(p,t), not intg(y,x) or any other forms using any variables other than p and t.
105+
**Variable names are always case-sensitive. If the variable is capitalized, it must be capitalized in the response.**
106+
For example, if ∫pdt or int(p,t) is given, it must be converted to intg(p,t), not intg(P,t) or any other forms.
107+
Do not remove or add any characters except as required by the replacement rules.
108+
109+
All the notations in the same square brackets are equivalent.
110+
**Notations from different groups or not listed above are NOT equivalent.**
111+
112+
---
113+
114+
This is the response: {expr}
115+
Now convert it to the required expression.
116+
Output only the expression, without any additional text or explanation.
117+
Remove all spaces. If there are any spaces remaining, you must remove them.
118+
"""
119+
llm_response = llm.invoke(prompt)
120+
return llm_response
121+
122+
def convert_other(expr: str, params: Params) -> str:
123+
load_dotenv()
124+
llm = ChatOpenAI(
125+
model=os.environ['OPENAI_MODEL'],
126+
api_key=os.environ["OPENAI_API_KEY"],
127+
)
128+
prompt = fr"""
129+
Follow these steps carefully:
130+
A response is provided below. Convert it into an expression under the following rules.
131+
132+
When the following notations are found in the response, they must be replaced with the notation after the right arrow (->) after the square brackets.
133+
---
134+
135+
- [∇f, gradient(f), grad(f)] -> grad(f)
136+
- [∇·f, div(f), divergence(f)] -> divg(f)
137+
Example: div(u) should be converted into divg(u).
138+
- [∇×f, curl(f), rot(f)] -> rot(f)
139+
140+
- [Infinity, infinity, ∞, oo, Inf, inf, Infty, infty] -> oo
141+
- [a·b, a⋅b, a.b, dot(a, b), a.dot(b)] -> dot(a,b)
142+
*Note: a.b is only equivalent to dot(a,b) if a and b are variables, not constants like 0, 1, π, etc.*
143+
- [a×b, cross(a, b), a.cross(b)] -> cross(a,b)
144+
- [\vec{{a}}, vector(a), a.vector(), Matrix(a)] -> vec(a)
145+
- [â, \hat{{a}}, unit(a), normalize(a), a_hat] -> hat(a)
146+
- [exp(x), e**x, e**x, exponential(x)] -> exp(x)
147+
These can be within an integral, for example:
148+
"Integral(rho * u.dot(n), (A, A_sys), circular=True)" must be converted into "Integral(rho * dot(u,n), (A, A_sys), circular=True)".
149+
150+
---
151+
Important:
152+
**Even if the notation is SymPy-compatible, it still must be replaced.**
153+
Variable names used in the notations above are "f", "x", "a" and "b", but they can be any variable names depending on the input, except for the differentiation operator characters given below. Variable names can be more than one letter, such as "rho" or "phi".
154+
**Input variable names must NEVER be changed. Keep the original variable names exactly as they appear.**
155+
Example: curl(p) must be converted to rot(p), not rot(f) or any other forms using any variables other than p.
156+
**Variable names are always case-sensitive. If the variable is capitalized, it must be capitalized in the response.**
157+
Example: curl(p) must be converted to rot(p), not rot(P) or any other forms.
158+
Do not remove or add any characters except as required by the replacement rules.
159+
160+
All the notations in the same square brackets are equivalent.
161+
**Notations from different groups or not listed above are NOT equivalent.**
162+
163+
---
164+
165+
This is the response: {expr}
166+
Now convert it to the required expression.
167+
Output only the expression, without any additional text or explanation.
168+
Remove all spaces. If there are any spaces remaining, you must remove them.
169+
"""
170+
llm_response = llm.invoke(prompt)
171+
return llm_response

app/miscellaneous_testing.py

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,72 @@
11
from parameter import extract_variable_names, create_sympy_parsing_params
2-
from evaluation import Params, evaluation_function
2+
from evaluation import Params, evaluation_function, is_equivalent_sympy, extract_symbols, contains_special_math
33
from sympy.parsing.sympy_parser import parse_expr
4-
from sympy import solve, Eq, simplify, Symbol
5-
6-
# parsing_params = create_sympy_parsing_params(Params(symbol_assumptions={"x": {"real": True}}), "x+2+z", "x*y*z")
7-
# local_dict = parsing_params["symbol_dict"]
8-
# print(local_dict)
9-
lhs, rhs = "2*x+1", "0"
10-
eq = Eq(parse_expr(lhs), parse_expr(rhs))
11-
print(solve(eq))
12-
lhs, rhs = "x", "-1/2"
13-
eq = Eq(parse_expr(lhs), parse_expr(rhs))
14-
print(solve(eq))
4+
from sympy import solve, Eq, simplify, Symbol, integrate, diff
5+
from llm_conversion import convert_diff, convert_integral, convert_other
6+
from re_conversion import convert_diff_re, convert_other_re, convert_integral_re
7+
import re
8+
9+
# print(extract_derivative_symbols("d^2y/dx^2 + dy/dx + y = 0"))
10+
# print(is_equivalent_sympy("a.dot(b)", "c.dot(d)",Params()))
11+
# print(convert_to_sympy("diff(y,x)", Params()).content.strip())
12+
# print(evaluation_function("∮_{A_sys} rho*u·n dA", "Integral(rho * u.dot(n), (A, A_sys), circular=True)", Params()))
13+
print(convert_other_re("div(u)", Params()))
14+
# print(convert_integral_re(convert_other_re("Integral(rho * u.dot(n), (A, A_sys), circular=True)", Params()), Params()))
15+
# checker = contains_special_math()
16+
# print(checker.contains_integral("∮_{A_sys} rho*u·n dA"))
17+
# print(checker.contains_diff("∮_{A_sys} rho*u·n dA"))
18+
# print(checker.contains_other("∮_{A_sys} rho*u·n dA"))
19+
# test_cases = [
20+
# # --- 閉曲線積分(∮型) ---
21+
# ("∮_{a} f dx", "ointg(f,x)"),
22+
# ("∮_{a{a+b}} h dx", "ointg(h,x)"),
23+
# ("∮_(z^2) g dx", "ointg(g,x)"),
24+
# ("∮_{A_sys} rho*dot(u,n) dA", "ointg(rho*dot(u,n),A)"),
25+
26+
# # --- oint((f,x)) 型 ---
27+
# ("oint((f,x))", "ointg(f,x)"),
28+
# ("oint({f,x})", "ointg(f,x)"),
29+
# ("oint([f,x])", "ointg(f,x)"),
30+
31+
# # --- int(f,(x,a),circular=True) ---
32+
# ("int(f,(x,a),circular=True)", "ointg(f,x)"),
33+
# ("int(g,[x,a],circular=True)", "ointg(g,x)"),
34+
35+
# # --- 定積分 ∫ₐᵇ ---
36+
# ("∫_a^b f dx", "intg(f,(x,a,b))"),
37+
# ("int(f,(x,a,b))", "intg(f,(x,a,b))"),
38+
39+
# # --- 片側積分 ∫ₐ ---
40+
# ("∫_a f dx", "intg(f,x)"),
41+
# ("int((f,x))", "intg(f,x)"),
42+
# ("int({f,x})", "intg(f,x)"),
43+
44+
# # --- 通常積分 ---
45+
# ("∫ f dx", "intg(f,x)"),
46+
# ("int(f,x)", "intg(f,x)"),
47+
# ("integrate(f,x)", "intg(f,x)"),
48+
# ("Integral(f,x)", "intg(f,x)"),
49+
50+
# # --- ネスト ---
51+
# ("int(int(f,x),y)", "intg(intg(f,x),y)"),
52+
# ("int(int(int(h,z),y),x)", "intg(intg(intg(h,z),y),x)"),
53+
# ("oint(oint(f,x),y)", "ointg(ointg(f,x),y)"),
54+
# ]
55+
56+
# from pprint import pprint
57+
# def run_tests():
58+
# failed = []
59+
# print("🧪 Running test cases...\n")
60+
# for expr, expected in test_cases:
61+
# output = convert_integral_re(expr,Params())
62+
# if output != expected:
63+
# print(f"❌ FAIL:")
64+
# print(f" Input: {expr}")
65+
# print(f" Expected: {expected}")
66+
# print(f" Got: {output}\n")
67+
# failed.append((expr, expected, output))
68+
# else:
69+
# print(f"✅ PASS:")
70+
# print(f" Input: {expr}")
71+
# print(f" Output: {output}\n")
72+
# run_tests()

app/parameter.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from sympy import Symbol, Function
22
import re
3+
from typing import Any, TypedDict
34

45
# default_params = {
56
# "symbol_assumptions": {}
@@ -11,6 +12,8 @@
1112
# Return a copy of the default parameters.
1213
# """
1314
# return default_params.copy()
15+
class Params(TypedDict):
16+
pass
1417

1518
def extract_variable_names(*expressions: str) -> set:
1619
"""

0 commit comments

Comments
 (0)